Claude Code Skills so schreiben, dass sie nur bei Bedarf laden
Mit einer sauber strukturierten SKILL.md lassen sich Claude Code Skills definieren, die nur eingeblendet werden, wenn sie tatsächlich gebraucht werden. So bleibt wertvoller Kontext unangetastet.

Öffne deine CLAUDE.md und scrolle. Irgendwo hinter den Build-Befehlen gibt es einen Abschnitt namens "Cutting a release": wie man den letzten Tag findet, welche Commit-Typen man auslässt, die Reihenfolge der Überschriften, der Ton. Vierzig Zeilen, die man vielleicht zweimal im Monat braucht. Sie liegen in jedem Zug jeder Sitzung im Kontextfenster, auch an dem Nachmittag, an dem du einen CSS-Bug fixst.
So funktioniert CLAUDE.md von der Konstruktion her. Claude Code liest sie zu Beginn jeder Sitzung, und dieselbe Seite empfiehlt, pro Datei unter 200 Zeilen zu bleiben und mehrstufige Prozeduren stattdessen in einen Skill auszulagern. Der Körper eines Skills bleibt auf der Festplatte, bis Claude entscheidet, dass die Aufgabe ihn braucht. Das einzige, was vorab im Kontext liegt, sind ein Name und eine Absatzbeschreibung.
Verlagere diese Prozedur in einen Claude-Code-Skill: eine SKILL.md, ein gebündeltes Skript und eine Stilreferenz, die nur geladen wird, wenn die Release-Notes tatsächlich geschrieben werden.
Anthropics Agent-Skills-Übersicht gliedert einen Skill in drei Ebenen, die zu unterschiedlichen Zeitpunkten geladen werden:
Skripte bringen einen zusätzlichen Rabatt. Wenn Claude ein gebündeltes Skript ausführt, gelangt nur dessen Ausgabe in den Kontext, nicht der Code. Die deterministische Arbeit (Commits auflisten, gruppieren) gehört also in ein Skript, das Urteilsvermögen (welche Commits für einen Nutzer relevant sind) ins Markdown.
Ein Vorbehalt, bevor du alles aus der CLAUDE.md auslagerst. Sobald ein Skill aufgerufen wurde, bleibt sein Inhalt für den Rest der Sitzung in der Konversation, und Claude Code liest die Datei in späteren Zügen nicht erneut. Die Ersparnis liegt also in den Sitzungen, die Releases nie berühren. In der Release-Sitzung selbst liegt der Körper vom Moment des Aufrufs an im Kontext.
Nur die Karteikarte liegt immer auf dem Tisch. Der Ordner und die Schublade öffnen sich, wenn die Aufgabe sie braucht.
Projekt-Skills liegen in.claude/skills/<name>/SKILL.md, persönliche in ~/.claude/skills/<name>/SKILL.md (Skills-Dokumentation). Nutze den Projekt-Speicherort, damit der Skill mit dem Repo ausgeliefert wird:
.claude/skills/release-notes/ ├── SKILL.md ├── reference/ │ └── style.md └── scripts/ └── commits.sh Enter fullscreen mode Exit fullscreen mode Beginne mit dem Skript. Es findet den letzten Tag (oder nimmt einen als Argument), listet die Nicht-Merge-Commits seitdem auf und versieht jede Zeile mit ihrem Conventional-Commits-Typ, damit das Modell die Präfixe nicht selbst parsen muss:
#!/usr/bin/env bash # Usage: commits.sh [from-tag] # Prints "<type>\t<hash> <subject>" for each commit # since the tag, grouped by type. set -euo pipefail from = " ${ 1:-} " if [ -z " $from " ]; then from = " $( git describe --tags --abbrev = 0 2>/dev/null \ || true ) " fi if [ -n " $from " ]; then echo "# commits since $from " range = " $from..HEAD" else echo "# no tag found, listing all commits" range = "HEAD" fi git log " $range " --no-merges --pretty = 'format:%h %s' | awk ' { type = "other" } $2 ~ /^feat(\(.*\))?!?:/ { type = "feat" } $2 ~ /^fix(\(.*\))?!?:/ { type = "fix" } { print type "\t" $0 } ' | sort -s -k1,1 Enter fullscreen mode Exit fullscreen mode git describe --tags --abbrev=0 gibt nur den nächstgelegenen Tag aus, der von HEAD aus erreichbar ist, einschließlich Lightweight-Tags. Das || true verhindert, dass set -e das Skript in einem Repo ohne Tags abbricht.
Jetzt die Stilreferenz. Das sind die vierzig Zeilen, die früher in der CLAUDE.md standen, reduziert auf das, was das Modell nicht erraten kann:
# Release notes style ## Headings Use these, in this order, and skip empty ones: ### Features, ### Fixes, ### Other ## Bullets - Start with a past-tense verb: "Added", "Fixed". - Describe the effect for the user, not the code. - End with the short hash in parentheses: (a1b2c3d). - A `!` right before the colon marks a breaking change. List it first under its heading, prefixed **Breaking:**. ## Leave out - Refactors with no visible effect. - Dependency bumps, unless they fix a security issue. Enter fullscreen mode Exit fullscreen mode Dann die SKILL.md, die beides zusammenführt:
--- name: release-notes description: >- Drafts user-facing release notes from the git commits since the last tag, grouped into Features, Fixes and Other. Use when the user asks for release notes, a changelog entry, what shipped since a version, or a summary of changes for a release. Not for single commit messages or PR descriptions. --- # Release notes 1.
Hole die Commits, indem du Folgendes ausführst: `bash "${CLAUDE_SKILL_DIR}/scripts/commits.sh" [tag]` Übergib einen Tag nur, wenn der Nutzer einen genannt hat. 2. Lies [ reference/style.md ]( reference/style.md ), bevor du irgendetwas schreibst.
3. Ein Bullet pro nutzersichtbarer Änderung. Führe Commits, die dieselbe Änderung beschreiben, zusammen.
Lasse chore-, ci-, test- und docs-Commits weg, außer der Nutzer verlangt danach. 4. Antworte ausschließlich mit Markdown.
Bearbeite CHANGELOG.md nur, wenn der Nutzer dich darum bittet. Enter fullscreen mode Exit fullscreen mode Das >- ist einfaches YAML: Es faltet die eingerückten Zeilen zu einem String zusammen, sodass eine lange Beschreibung lesbar bleibt und ein versehentlicher Doppelpunkt darin das Parsen nicht zerstört.
${CLAUDE_SKILL_DIR} ist eine Substitution, die Claude Code mit dem eigenen Verzeichnis des Skills füllt, sodass der Skriptpfad aus jedem Arbeitsverzeichnis funktioniert. Der Name darf nur Kleinbuchstaben, Zahlen und Bindestriche enthalten, und auf Anthropics anderen Oberflächen darf er weder "anthropic" noch "claude" enthalten. Die Referenzdatei ist direkt aus der SKILL.md verlinkt, genau eine Ebene tief, weil der Best-Practices-Leitfaden warnt, dass Claude Dateien, die über eine Kette von Referenzen erreicht werden, unter Umständen nur teilweise liest.
Alternativ könntest du das Skript mit der Inline-Shell-Syntax der Skills-Dokumentation einbetten (ein Ausrufezeichen gefolgt von einem Backtick-Befehl), die ausgeführt wird, bevor Claude den Skill sieht, und deren Ausgabe eingefügt wird. Die Dokumentation weist darauf hin, dass ein fehlgeschlagener Befehl den gesamten Aufruf abbricht, und das Tag-Argument ist hier optional, daher ist es die einfachere Wahl, Claude das Skript ausführen zu lassen.
Claude Code überwacht Skill-Verzeichnisse und erkennt neue oder bearbeitete Skills auch während einer Sitzung. Existierte.claude/skills/ beim Sitzungsstart noch nicht, beobachtet Claude Code es noch nicht: Führe /reload-skills aus, um den Skill zu übernehmen, und danach bei jeder weiteren Bearbeitung in dieser Sitzung erneut, oder starte eine neue Sitzung.
Starte eine frische Sitzung in einem Repo mit mindestens einem Tag. Frage zunächst "What skills are available?", eine der Prüfungen aus dem Troubleshooting-Leitfaden. release-notes sollte in der Liste auftauchen. Wenn es erscheint, aber nie ausgelöst wird, wirf einen Blick auf das YAML: Schlägt das Frontmatter beim Parsen fehl, lädt der Skill trotzdem mit leerer Metadaten, sodass /release-notes funktioniert, aber Claude keine Beschreibung zum Abgleich hat. Führe claude --debug aus, um den Parse-Fehler zu sehen. Eine einfache Möglichkeit, ihn zu verursachen: eine einzeilige, nicht in Anführungszeichen gesetzte Beschreibung mit einem Doppelpunkt gefolgt von einem Leerzeichen.
Führe dann Prompts auf beiden Seiten der Grenze aus:
Die "Nein"-Zeilen sind die nützlichen. Sie teilen Vokabular mit dem Skill (Commits, git, Tags) und sollten weiterhin ohne ihn bearbeitet werden. Wenn der Commit-Message-Prompt den Skill hereinzieht, ist die Beschreibung zu breit gefasst.
Um zu sehen, was der Skill verändert, beschaffe dir eine Basislinie. Schalte ihn in der.claude/settings.local.json mit skillOverrides aus, führe dieselben Prompts in einer neuen Sitzung aus und entferne dann den Override:
{ "skillOverrides": { "release-notes": "off" } } Enter fullscreen mode Exit fullscreen mode Zu den Kosten: Die Skills-Zeile in /context zeigt die Größe des Skill-Verzeichnisses nach Anwendung des Budgets an und entspricht damit dem, was das Modell erhält, und /skill-doctor (Claude Code v2.1.252 oder neuer) meldet die Kontextkosten und die Aufrufanzahl jedes Skills. Fünf Prompts von Hand finden die offensichtlichen Fehlauslösungen. Um mehr als fünf zu testen, erzeugt das skill-creator-Plugin der Dokumentation should-trigger- und should-not-trigger-Prompts, misst die Trefferquote und schlägt Beschreibungsänderungen vor. Installiere es mit /plugin install skill-creator@claude-plugins-official.
Die Beschreibung ist das Einzige, was an der Tür geprüft wird. Der Körper erhält nie ein Stimmrecht darüber, ob er geladen wird.
Alles in der SKILL.md unterhalb des Frontmatter bleibt unsichtbar, bis der Skill ausgewählt wird. Die Auswahl trifft Claude allein anhand der Beschreibung, die laut Anthropics Übersicht gegen deine Anfrage "abgeglichen" wird. Ein großartiger Körper hinter einer vagen Beschreibung läuft nie.
Sage was und wann. Der Best-Practices-Leitfaden verlangt beides: was der Skill tut und die Auslöser für seine Nutzung. "Hilft bei Releases" scheitert an beiden Hälften. "Erstellt Release-Notes aus Commits seit dem letzten Tag. Verwenden, wenn der Nutzer nach Release-Notes, einem Changelog-Eintrag... fragt" gibt dem Modell eine Aufgabe und eine Reihe von Phrasen zum Abgleich.
Schreibe in der dritten Person. Die Beschreibung wird in den Systemprompt injiziert, und der Leitfaden warnt, dass "Ich kann dir helfen" oder "Du kannst das verwenden" Entdeckungsprobleme verursachen kann. "Erstellt Release-Notes" liest sich aus jeder Perspektive korrekt.
Verwende die Wörter, die deine Nutzer tippen. Niemand fragt nach "einer Zusammenfassung des Release-Artefakts". Man fragt "was ist seit 1.4 erschienen" oder "schreib das Changelog". Nimm diese Phrasen auf. Der erste Troubleshooting-Schritt der Dokumentation für einen Skill, der nicht auslöst, lautet zu prüfen, ob die Beschreibung "Schlüsselwörter enthält, die Nutzer natürlich sagen würden".
Stelle den wichtigsten Anwendungsfall nach vorn. In Claude Code wird die kombinierte Beschreibung und der when_to_use-Text in der Auflistung standardmäßig bei 1.536 Zeichen abgeschnitten. Alles dahinter erreicht das Modell nie, daher sollte der wichtige Satz zuerst kommen.
Die gesamte Liste hat zudem ein Budget, standardmaessig 1% des Kontextfensters des Modells. Wenn viele Skills vorhanden sind und die Liste ueberlaeuft, entfernt Claude Code ganze Beschreibungen, beginnend mit den Skills, die am seltesten aufgerufen werden. Ein neuer Skill startet bei null Aufrufen und ist daher ein frueher Kandidat. Pruefe die Skills-Zeile in /context und setze Skills mit niedriger Prioritaet in skillOverrides auf "name-only", um Platz zu schaffen.
Benenne die Grenze. Die Zeile "Not for single commit messages or PR descriptions" ist eine Designentscheidung dieses Tutorials und keine Vorgabe aus der Dokumentation. Sie ist berechtigt, wenn eine benachbarte Anfrage wiederholt den Skill ausloest. Der dokumentierte Loesungsweg bei zu haeufigem Ausloesen besteht darin, die Beschreibung konkreter zu fassen, und eine explizite Ausschliessung ist eine Moeglichkeit dazu.
Bleibe innerhalb der portablen Grenzen. In der Claude API und auf claude.ai darf die Beschreibung nicht leer sein, darf hoechstens 1.024 Zeichen umfassen und keine XML-Tags enthalten. Die eigene Dokumentation von Claude Code erwaehnt nur die Grenze von 1.536 Zeichen fuer die Liste, aber unter 1.024 Zeichen bleibt der Skill auf diese Oberflaechen portierbar.
Wenn du Release Notes nur auf ausdrueckliche Anfrage moechtest, fuege disable-model-invocation: true zum Frontmatter hinzu. Die Beschreibung verlaesst dann den Kontext vollstaendig, und der Skill wird nur ausgefuehrt, wenn du /release-notes eingibst. Das ist die guenstigste Option und die richtige fuer alles mit Nebeneffekten, das du von Hand ausloesen willst.
Wenn eine Regel jedes Mal gelten muss, etwa "niemals einen Tag pushen, ohne die Tests laufen zu lassen", ist ein Skill der falsche Ort. Die Dokumentation empfiehlt dafuer einen Hook, weil ein Hook bei seinem Ereignis laeuft, unabhaengig davon, ob Claude einem Skill folgt. CLAUDE.md bleibt der Ort fuer Fakten, die jede Sitzung braucht: Build-Befehle, Projektstruktur, Konventionen. Alles andere kann auf der Platte warten, bis eine Anfrage danach verlangt.
Eine Skill-Beschreibung ist ein Prompt mit einer einzigen Aufgabe: fuer die richtige Anfrage ausgewaehlt zu werden, und genau um diese Art von Prompt geht es in der Prompt Engineering Pocket Guide: konkret sein, die Woerter waehlen, mit denen das Modell uebereinstimmt, und auch die Fehltreffer ebenso wie die Treffer testen. Wenn du eine CLAUDE.md kaufst, die zu einem Handbuch herangewachsen ist, gelten dieselben Gewohnheiten.
Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.
Original (Englisch) anzeigen
Open your CLAUDE.md and scroll. Somewhere past the build commands there is a section called "Cutting a release": how to find the last tag, which commit types to drop, the heading order, the tone. Forty lines you need maybe twice a month. They sit in the context window for every turn of every session, including the afternoon you spend fixing a CSS bug.
That is how CLAUDE.md works by design. Claude Code reads it at the start of every session, and the same page tells you to target under 200 lines per file and to move "a multi-step procedure" into a skill instead. A skill's body stays on disk until Claude decides the task needs it. The only part in context up front is a name and a one-paragraph description.
Move that procedure into a Claude Code skill: a SKILL.md, a bundled script, and a style reference that loads only when the notes get written.
Anthropic's Agent Skills overview splits a skill into three levels, each loaded at a different time:
Scripts get an extra discount. When Claude runs a bundled script, only its output enters context, not the code. So the deterministic work (listing commits, grouping them) belongs in a script, and the judgment (which commits a user cares about) belongs in markdown.
One caveat before you move everything out of CLAUDE.md. Once a skill is invoked, its content stays in the conversation for the rest of the session, and Claude Code does not re-read the file on later turns. So the saving is in the sessions that never touch releases. In the release session itself, the body sits in context from the moment the skill fires.
Only the index card is always on the desk. The folder and the drawer open when the task needs them.
Project skills live in.claude/skills/<name>/SKILL.md, personal ones in ~/.claude/skills/<name>/SKILL.md ( skills docs ). Use the project location so the skill ships with the repo:
.claude/skills/release-notes/ ├── SKILL.md ├── reference/ │ └── style.md └── scripts/ └── commits.sh Enter fullscreen mode Exit fullscreen mode Start with the script. It finds the last tag (or takes one as an argument), lists non-merge commits since then, and tags each line with its Conventional Commits type so the model does not have to parse prefixes:
#!/usr/bin/env bash # Usage: commits.sh [from-tag] # Prints "<type>\t<hash> <subject>" for each commit # since the tag, grouped by type. set -euo pipefail from = " ${ 1:-} " if [ -z " $from " ]; then from = " $( git describe --tags --abbrev = 0 2>/dev/null \ || true ) " fi if [ -n " $from " ]; then echo "# commits since $from " range = " $from..HEAD" else echo "# no tag found, listing all commits" range = "HEAD" fi git log " $range " --no-merges --pretty = 'format:%h %s' | awk ' { type = "other" } $2 ~ /^feat(\(.*\))?!?:/ { type = "feat" } $2 ~ /^fix(\(.*\))?!?:/ { type = "fix" } { print type "\t" $0 } ' | sort -s -k1,1 Enter fullscreen mode Exit fullscreen mode git describe --tags --abbrev=0 prints only the closest tag reachable from HEAD, lightweight tags included. The || true keeps set -e from killing the script in a repo with no tags yet.
Now the style reference. This is the forty lines that used to live in CLAUDE.md, cut down to what the model cannot guess:
# Release notes style ## Headings Use these, in this order, and skip empty ones: ### Features, ### Fixes, ### Other ## Bullets - Start with a past-tense verb: "Added", "Fixed". - Describe the effect for the user, not the code. - End with the short hash in parentheses: (a1b2c3d). - A `!` right before the colon marks a breaking change. List it first under its heading, prefixed **Breaking:**. ## Leave out - Refactors with no visible effect. - Dependency bumps, unless they fix a security issue. Enter fullscreen mode Exit fullscreen mode Then SKILL.md, which ties the two together:
--- name: release-notes description: >- Drafts user-facing release notes from the git commits since the last tag, grouped into Features, Fixes and Other. Use when the user asks for release notes, a changelog entry, what shipped since a version, or a summary of changes for a release. Not for single commit messages or PR descriptions. --- # Release notes 1.
Get the commits by running: `bash "${CLAUDE_SKILL_DIR}/scripts/commits.sh" [tag]` Pass a tag only if the user named one. 2. Read [ reference/style.md ]( reference/style.md ) before writing anything.
3. One bullet per user-visible change. Merge commits that describe the same change.
Drop chore, ci, test and docs commits unless the user asks for them. 4. Reply with Markdown only.
Edit CHANGELOG.md only if the user asks you to. Enter fullscreen mode Exit fullscreen mode The >- is plain YAML: it folds the indented lines into one string, so a long description stays readable and a stray colon inside it cannot break the parse.
${CLAUDE_SKILL_DIR} is a substitution Claude Code fills in with the skill's own directory, so the script path works from any working directory. The name must be lowercase letters, numbers and hyphens, and on Anthropic's other surfaces it cannot contain "anthropic" or "claude". The reference file is linked directly from SKILL.md, one level deep, because the best-practices guide warns that Claude may only partially read files reached through a chain of references.
You could also inline the script with the skills docs' inline shell syntax (an exclamation mark followed by a backticked command), which runs before Claude sees the skill and pastes the output in. The docs note that a failing command aborts the whole invocation, and the tag argument here is optional, so letting Claude run the script is the simpler choice.
Claude Code watches skill directories and picks up new or edited skills mid-session. If.claude/skills/ did not exist when the session started, Claude Code is not watching it yet: run /reload-skills to pick the skill up, and run it again after each later edit in that session, or start a new session.
Start a fresh session in a repo that has at least one tag. First ask What skills are available?, one of the checks in the troubleshooting guide. release-notes should be in the list. If it shows up but never triggers, look at the YAML: when frontmatter fails to parse, the skill still loads with empty metadata, so /release-notes works but Claude has no description to match. Run claude --debug to see the parse error. One easy way to cause it: an unquoted one-line description containing a colon followed by a space.
Then run prompts on both sides of the line:
The "No" rows are the useful ones. They share vocabulary with the skill (commits, git, tags) and should still be handled without it. If the commit-message prompt pulls the skill in, the description is too broad.
To see what the skill changes, get a baseline. Turn it off in.claude/settings.local.json with skillOverrides, run the same prompts in a new session, then remove the override:
{ "skillOverrides": { "release-notes": "off" } } Enter fullscreen mode Exit fullscreen mode For cost, the Skills row in /context reports the size of the skill listing after the budget is applied, so it matches what the model receives, and /skill-doctor (Claude Code v2.1.252 or later) reports each skill's context cost and invocation count. Five prompts by hand catch the obvious misses. To test more than five, the docs' skill-creator plugin generates should-trigger and should-not-trigger prompts, measures the hit rate and proposes description edits. Install it with /plugin install skill-creator@claude-plugins-official.
The description is the only thing checked at the door. The body never gets a vote on whether it loads.
Everything in SKILL.md below the frontmatter is invisible until the skill is chosen. The choice is made from the description alone, which Anthropic's overview says Claude "matches your request against". A great body behind a vague description never runs.
Say what and when. The best-practices guide asks for both: what the skill does, and the triggers for using it. "Helps with releases" fails both halves. "Drafts release notes from commits since the last tag. Use when the user asks for release notes, a changelog entry..." gives the model a task and a set of phrases to match.
Write in third person. The description is injected into the system prompt, and the guide warns that "I can help you" or "You can use this" can cause discovery problems. "Drafts release notes" reads correctly from any point of view.
Use the words your users type. Nobody asks for "a release artifact summary". They ask "what shipped since 1.4" or "write the changelog". Put those phrases in. The docs' first troubleshooting step for a skill that does not trigger is to check that the description "includes keywords users would naturally say".
Put the key use case first. In Claude Code the combined description and when_to_use text is cut at 1,536 characters in the listing by default. Anything past that never reaches the model, so front-load the sentence that matters.
The whole listing also has a budget, 1% of the model's context window by default. If you have many skills and the listing overflows it, Claude Code drops whole descriptions, starting with the skills you invoke least. A new skill starts at zero invocations, so it is an early candidate. Check the Skills row in /context, and set low-priority skills to "name-only" in skillOverrides to free room.
Name the boundary. The "Not for single commit messages or PR descriptions" line is a design choice of this tutorial rather than a rule from the docs. It earns its place when a nearby request keeps triggering the skill. The documented fix for over-triggering is to make the description more specific, and an explicit exclusion is one way to do that.
Stay inside the portable limits. On the Claude API and claude.ai, the description must be non-empty, at most 1,024 characters and free of XML tags. Claude Code's own docs only mention the 1,536-character listing cut, but staying under 1,024 keeps the skill portable to those surfaces.
If you only ever want release notes when you ask for them, add disable-model-invocation: true to the frontmatter. The description then leaves context entirely, and the skill runs only when you type /release-notes. That is the cheapest option, and the right one for anything with side effects you want to trigger by hand.
If a rule has to hold every time, such as "never push a tag without running the tests", a skill is the wrong home. The docs recommend a hook for that, because a hook runs on its event whether or not Claude is following any skill. CLAUDE.md stays the place for facts every session needs: build commands, layout, conventions. Everything else can wait on disk until a request asks for it.
A skill description is a prompt with one job, getting picked for the right request, and that is the kind of prompt the Prompt Engineering Pocket Guide spends its time on: being specific, choosing the words the model will match, and testing the misses as well as the hits. If you are trimming a CLAUDE.md that has grown into a manual, the same habits apply.