Skip to content

Course · Claude Code · Skills

Write the description so the agent invokes the skill at the right moment

A skill you have to summon is only a slash command with a longer name. The point of packaging a procedure as a skill is that the agent notices, mid-conversation, that the moment has arrived and runs it unprompted. Whether that happens - and whether it happens at the right time, rather than at every mention of the word PR - comes down almost entirely to one frontmatter field: the description.

The skill this lesson tunes is a branch wrap-up for a small TypeScript payments service, the example this course returns to. It lives at .claude/skills/finish-pr/SKILL.md in the repo, and its body runs four steps in order and stops on the first failure: npm test, a check that any bug fix names its regression test, npm run lint, npm run build. The create lesson shows the whole file; you can also write your own from that description alone. Typing /finish-pr runs it by hand. The question here is how it runs without that.

Claude Code loads a listing of skill names and descriptions into context - the model’s working memory for the conversation - so the agent always knows what is on the shelf; a skill’s full body loads only when it is invoked. The description therefore does double duty: it is the menu the agent reads and the pattern it matches your request against. When your message looks like the situation a description names, the agent pulls in that skill’s instructions and follows them. When it does not, the skill stays closed and costs its menu line.

The shelf has rules of its own, and they shape the field. Every skill name is always in the listing, but descriptions are budgeted: a skill’s description and its optional when_to_use field together are truncated at 1,536 characters per skill by default. Separately, when the listing outgrows a budget that scales at about one percent of the context window, Claude Code drops descriptions starting with the skills you invoke least. Front-load the trigger - the tail of a long description is the first thing to go. The mechanics are on the skills page; the craft is in the field.

The instinct is to write a description that says what the skill is:

description: Finishes a pull request.

True, useless. It names the skill, not the moment you would want it - and you would never say finish a pull request in those words. Compare the field finish-pr carries, which names the situation and the phrasings you actually reach for:

description: >-
Run the standard wrap-up checks before opening a pull request on the
payments service. Use when the user says a change is done, is ready to
open or push a PR, or asks to finish up a branch.

The pattern: what it does, then when to use it, in your words. That second half is a list of the phrasings a wrap-up moment actually produces - the change is done, ready to open the PR, finish up the branch. If you want the lead sentence tight, the when can move to a separate when_to_use field; it is appended to the description in the listing and counts toward the same 1,536-character cap, so it is organization, not extra budget.

A skill you only ever run by hand might as well be a plain command. So test the trigger, not the steps: finish some small change, then say the kind of thing you would really say, without naming the skill. An illustrative run on a bug-fix branch - the wording and counts are constructed; the step order is the skill’s:

> I think the ledger fix is done - ready to open the PR
Skill(finish-pr)
⎿ Successfully loaded skill
1. npm test - 7 passed (7)
2. This branch fixes a bug. Regression test: "files ledger entries
under the account id" in tests/ledger.test.ts.
3. npm run lint - clean
4. npm run build - compiled without errors
All four pass. The reviewer should look at the new ledger test
first - it fails against the old route code.

The Skill(finish-pr) header with its Successfully loaded skill subline is the recognition firing: Claude loaded the skill itself, matched from the description alone, and ran all four steps without being told which skill to use. Note what step two did on a bug-fix branch - it named the test that proves the fix, because the body told it to stop otherwise.

If the skill does not fire when you expect it to, the fix is nearly always the description: it is missing the words you actually used. Add your real phrasing and try again. A skill that never appears at all has a configuration problem rather than a wording one, and the debugging steps are on the debug-your-config page.

A description tuned too broad fails the other way: the skill fires when you did not want it. Write use whenever working on a pull request and the agent runs the full ritual the moment you mention a PR - including the moment you ask a question about one. The cure is narrowing: pin the description to the genuine end-of-work moment (ready to open or push, finish up the branch) rather than to any appearance of the topic.

There is a stronger form of that worry. A procedure with real side effects - one that pushes, deploys, sends, or spends - should arguably not fire on the agent’s judgment at all. Claude Code has a switch for exactly that: disable-model-invocation: true takes the skill out of the listing entirely, so recognition is off and the ritual runs only when you type /finish-pr.

That is command behavior restored - fires when I say so - and drawing that line deliberately is what the next lesson is for. Skill, command, or subagent: which one does the job want?