Engineering Guides 3 of 4
Claude Code Skills: How They Work, How to Write One, and How to Check Claude Uses It
Claude picks a skill from its name and description. How to write the two lines it reads, and check it chose yours.
How this is checked 83 claims · 62 verified at the source · last checked 26 Oct 2026
- What would prove it wrong
- The guide is wrong if Claude chooses skills on something other than the name and description it lists, for instance by reading skill bodies before choosing, so that a vague name and description cost nothing. Two things would show it: a vague skill like the one in row 6 being chosen as often as the sharp ones on a fresh run, or Claude Code's docs changing how the listing works. Either would move the writing advice from "required" to "helpful" and the check would need re-running.
Re-checked 26 Oct 20261 holding1 untestedThe re-check
- Evidence
- 83 claims: 62 verified at the primary source and 21 from runs made for the piece.
Claude picks a skill from its name and description. How to write the two lines it reads, and check it chose yours.
A Claude Code skill is a folder of instructions that Claude loads when a task calls for it. It follows an open format that the Claude app, ChatGPT and Codex also read: the SKILL.md file travels between them, though how you install it, when it runs on its own and which fields count all differ.
I wrote one that writes commit messages in a house format and gave it the name and description Anthropic’s guide to writing skills tells you to avoid: helper, and “Does stuff with files”. Typed as /helper, it worked.
Asked in plain words for a commit message, Claude passed it over 12 times out of 12 (four phrasings, three runs each, Claude Opus 5.5, one repository) and wrote decent messages of its own, with nothing in the reply to say a skill had been skipped.

The instructions were fine. Claude never chose them.
The point of a skill is that you stop having to remember it, so one that runs only when you type its name has done half its job. This guide takes helper from that first draft to a version Claude chose on all 21 commit-message requests in my runs, and ends with the check that tells you which kind you have. (If a well-written skill of yours has stopped being chosen, The Skill That Never Fired covers why.)
The short version: Claude chooses a skill mainly from two lines, its name and its description. Give it a name that says the job and a description that says what it does and when to use it, in the words you would type. Then check the choice: in Claude Code, a
Skillcall shows that Claude picked the skill, and a reply in the right format doesn’t. Section 3 shows how to write a skill, and section 6 how to check it.
What are agent skills?
Agent skills is the name the format goes by outside Claude Code. Anthropic created it, describing a skill as “a directory containing a SKILL.md file” whose instructions, scripts and resources “give agents additional capabilities,” and in December 2025 it “published Agent Skills as an open standard for cross-platform portability.”
Claude Code’s skills “follow the Agent Skills open standard, which works across multiple AI tools,” and OpenAI’s skills for ChatGPT and Codex “build on the open agent skills standard.”
Inside Claude Code, a skill is easy to confuse with four other things:
- CLAUDE.md is read at the start of every session, so it suits facts the agent always needs, while a skill’s body “loads only when it’s used,” which suits a procedure.
- A custom slash command is now the same thing: “Custom commands have been merged into skills.”
- A subagent “runs in its own context window”, while an invoked skill’s content “enters the conversation as a single message and stays there across later turns.”
- MCP is “an open-source standard for connecting AI applications to external systems”; a skill can tell the agent how to use what MCP connects.
Claude Code’s docs give the test for when to write one: “Create a skill when you keep pasting the same instructions, checklist, or multi-step procedure into chat, or when a section of CLAUDE.md has grown into a procedure rather than a fact.” A house commit format is that kind of procedure.
How Claude chooses a skill
At the start of a session, in the words of Anthropic’s engineering post on skills, “the agent pre-loads the name and description of every installed skill into its system prompt.” The rest of the file waits: “If Claude thinks the skill is relevant to the current task, it will load the skill by reading its full SKILL.md into context.”
So the choice rests mainly on two lines. My test sessions loaded 41 skills: helper and 40 others that come with Claude Code or with my account, none of them about commits.
Asked for a commit message, Claude had the name helper and the words “Does stuff with files” to go on. The format, the ticket rule and the Refs: line were all in the body, which the skill loads only once Claude picks it.

Claude picks a skill from its name and description, and the rest loads only after it has picked.
There are two exceptions. A skill set to run only by name (section 3) stays out of the listing, and once the listing outgrows its budget, by default 1% of the context window, Claude Code drops descriptions “starting with the skills you invoke least.”
How to write a Claude Code skill
A skill is a folder named after the skill, with a SKILL.md inside. Here is the version I recommend: helper’s instructions with two lines added, under a name that says the job and a description adapted from Anthropic’s example.
---
name: writing-commit-messages
description: Writes commit messages in the house format from the staged diff. Use when the user asks for a commit message or the message for changes about to be committed. Not for code review.
---
Write the commit message for the staged changes:
1. Read the staged diff (git diff --staged) and the branch name (git branch --show-current).
2. First line: type(scope): summary, at most 60 characters. Types: feat, fix, docs, refactor, test, chore.
3. A blank line, then one or two sentences on why the change was made.
4. A last line: Refs: followed by the ticket ID from the branch name (PAY-142 from feat/PAY-142-retry-timeout). If the branch name has no ticket ID, leave the Refs: line out and never invent one.
Reply with the message only. Do not run git commit.
Try it
Make the folder in your repository and save the file there as .claude/skills/writing-commit-messages/SKILL.md:
mkdir -p .claude/skills/writing-commit-messages
Then stage a change, start Claude Code in the repository and ask for a commit message in your own words. The message should open with a type(scope): line and, if your branch name carries a ticket, end with Refs: and that ticket; section 6 shows how to confirm Claude chose the skill rather than writing the message itself.
In my runs this version was chosen on all 21 commit-message requests, took none of 9 code-review requests, and on a branch with no ticket left the Refs: line out all six times. I wrote its description after seeing the first two runs, so some of those requests are ones it was written around; test it on yours.
What changed from Anthropic’s example
Anthropic’s Git Commit Helper example, which the runs also tested, is the starting point. The name now says the job.
The description is in the third person, as Anthropic’s page asks (“Always write in third person”), where the example opens with “Generate”; it drops “or reviewing staged changes”, a job this body doesn’t do, and adds “Not for code review”. The body says where to look and what to do when a branch has no ticket.
On a branch with no ticket, the version with Anthropic’s description, in the third person, and the original body left a placeholder such as Refs: <TICKET-ID> in all six runs; the recommended version left the line out.
The description
The description is where you say when the skill applies, since a name has no room for it. Claude Code’s skills docs define the field as “What the skill does and when to use it,” and the examples on Anthropic’s best-practice page follow one pattern: what the skill does, then “Use when” and the requests it is for.
Put in the words you would type when you want the skill. Keep it under 200 characters, the Claude app’s limit. Claude Code also takes a when_to_use field for trigger phrases, which the Claude app rejects (section 4).
The name
The name is the folder’s name and the command you type: lowercase letters, numbers and hyphens, up to 64 characters, and the standard says it “Must match the parent directory name”.
In my runs a name that says the job carried the choice even under the vague description, including on requests that never said “commit”. In the two-skill test in the earlier essay, “A good description rescued names that carried no signal,” and “A good name had already done the same for descriptions that carried none.” Routing broke down only when neither said what set the skill apart, and section 6 shows the same here.
Every tool reads the description, and it is the one portable field that can say when, so write it first.
The body
The body is the procedure. Keep it to steps Claude can follow, because “Once a skill loads, its content stays in context across turns, so every line is a recurring token cost.” Longer reference material can go in other files in the folder, which the body names and Claude opens “only as needed.”
Skills with side effects
One frontmatter field decides whether Claude may choose the skill at all. This skill is designed only to draft a message, so an unwanted invocation is low-risk; its “Do not run git commit” is an instruction, and tool permissions remain the separate safeguard.
A skill that commits, deploys or sends something is different. Claude Code’s docs say to set disable-model-invocation: true “for workflows with side effects or that you want to control timing, like /commit, /deploy, or /send-slack-message. You don’t want Claude deciding to deploy because your code looks ready.”
With that set, the description leaves the listing and Claude can’t run the skill on its own; stopping the action itself is a job for the permissions covered in the second guide in this series.
Where skills live in Claude Code, the Claude app, ChatGPT and Codex

Claude Code
Claude Code reads skills from two places most people need. ~/.claude/skills/<skill-name>/SKILL.md is yours and loads in all your projects on this machine. .claude/skills/<skill-name>/SKILL.md lives in a repository, and the docs’ advice is “Commit it so your team gets it too.” When two skills share a name, “Enterprise over personal, and personal over project,” so a personal helper would replace the team’s.
The Claude app
In the Claude app, skills sit under Customize > Skills; you upload the folder as a ZIP, and Claude “reviews available skills, loads relevant ones, and applies their instructions.” Anthropic’s help centre lists skills on the Free, Pro, Max, Team and Enterprise plans with code execution on, though the developer overview still lists only the paid plans.
Uploads accept six frontmatter fields, name, description, license, compatibility, metadata and allowed-tools; anything else, disable-model-invocation and when_to_use included, “fails with a hard error instead of ignoring the field.”
ChatGPT
In ChatGPT, according to OpenAI’s help page, “Skills are available to eligible ChatGPT Business, Enterprise, Healthcare, and Edu users, subject to workspace settings and product availability.” They sit under Plugins, on a Skills tab, and you call one by typing @.
Codex
Codex looks for skills in .agents/skills in the repository and ~/.agents/skills in your home folder, and you mention one with $. To stop Codex choosing a skill on its own, set allow_implicit_invocation: false under policy: in the skill’s agents/openai.yaml; then “explicit $skill invocation still works.”
Read a skill before you install it
A skill someone else wrote goes into the same listing as yours, and whenever Claude picks it, its instructions join the conversation. Skills, Anthropic’s post notes, “can also include code for Claude to execute as tools at its discretion.”
The post’s advice is to install skills “only from trusted sources” and, for anything less, to start “by reading the contents of the files bundled in the skill,” including instructions “that instruct Claude to connect to potentially untrusted external network sources.”
Read the SKILL.md as closely as the scripts: when Snyk scanned 3,984 skills from two public registries in February, its reviewers confirmed 76 with “malicious payloads in their markdown instructions to AI agents” (vendor research).
Which skills to trust, and how to pin and remove them, is the subject of Skills Are Package Management for Your AI.
How to check Claude chose your skill
Claude Code’s docs draw the line this guide has been following: “Seeing a skill trigger tells you Claude found it, not that it did what you intended. To know a skill is working, measure separately whether Claude invokes it on the prompts it should, and whether the output matches what you expect when it does.”
Calling a skill by name shows you the output. Three steps check the choice.
Step 1: Check that Claude Code can read the frontmatter
If it can’t, Claude Code “loads the skill body with empty metadata, so /skill-name still works but Claude can’t match against your description.” The validator, in Claude Code 2.1.233 or later, finds that. Here it is on a description with a stray quotation mark inside:
claude plugin validate .claude/skills
✘ Found 1 error:
❯ frontmatter: YAML frontmatter failed to parse: YAML Parse error: Unexpected token. At runtime this skill loads with empty metadata (all frontmatter fields silently dropped).
Step 2: Read the Skill call
When Claude picks a skill in your session, it calls a tool named Skill, and a headless run prints that call. Scott Spence measured skill activation this way in February, running prompts “through claude -p with —output-format stream-json” because he only needed to know “did it call Skill() or not?”
This command prints the name of the skill Claude called. On helper with Anthropic’s description it printed the line below; on the vague helper, nothing. It needs jq, and it ran as shown on Claude Code 2.1.292; the stream’s layout can change between versions.
claude -p "Write a commit message for the staged changes." \
--output-format stream-json --verbose \
| jq -c 'select(.type=="assistant") | .message.content[]?
| select(.type=="tool_use" and .name=="Skill") | .input'
{"skill":"helper"}
Each claude -p run is a new session, which matters, because “leftover context from authoring the skill will mask gaps in the written instructions.”
Typing /helper can’t stand in for this: Claude Code runs a skill you name directly, and in my runs the stream then showed no Skill call at all. An empty result can also mean the run failed, so look at the reply before you rewrite anything.
Step 3: Vary the words
On Sonnet 4.5 with four Svelte skills, Spence found activation “keyword-dependent.” With no extra setup, prompts that used a skill’s keyword activated it nearly every time, generic phrasing 20 to 40% of the time, and indirect phrasing almost never.
So run the check on three or four phrasings you would really use, at least one without the skill’s own words, and on a near-miss request it should leave alone, two or three times each. Then read the replies for the other half of the docs’ test: when Claude chooses the skill, the output should follow it.
What the check found
Reading the Skill call across phrasings is the check that caught helper. I ran five versions in Claude Code 2.1.292 with Opus 5.5: the same body under four frontmatters, each quoted from Anthropic’s best-practice page or built on its naming convention, plus the version above with its two added lines. I wrote each routing plan down before its runs. These were the requests:
Commit requests (three times each)
Write a commit message for the staged changes.
Draft the commit message for what I've staged.
I'm ready to commit. What should the message say?
Summarise my staged diff as a commit message.
Without the word "commit" (three times each)
Prepare the message for the changes I'm about to save.
I'm about to push this. Write the summary line and a short reason for the history.
Describe this change for the project history in the team's usual format.
Review requests the skill should leave alone (three times each)
Review my staged changes for bugs and potential regressions.
Check the staged diff for anything risky before I push.
What did I change in the staged files, and is anything missing a test?
Unrelated (twice each)
What does the fetch function in client.py do?
Add a docstring to the fetch function in client.py.

Every planned phrasing gave the same result on every repeat. The version whose name and description both said nothing about commits was never chosen, and on these phrasings either a telling name or a telling description was enough, whether or not the request said “commit”.
No version took a review request, including the two whose descriptions still said “reviewing staged changes”: the first went to Claude Code’s own code-review skill every time, and Claude answered the other two itself, so this shows the clause losing to a neighbour that owns the job, not what happens without one.
On the branch with a ticket, the commit message in every reply from a chosen skill passed a format check I wrote down before scoring it (a type(scope): first line of 60 characters or fewer, a blank line, a body, and Refs: PAY-142 as the message’s last line), 84 of 84.
But 14 replies added text outside the message, twelve of them notes about the change, against the skill’s “Reply with the message only,” so 70 of 84 followed every instruction checked. Being chosen didn’t guarantee the skill was followed in full.
The replies are why the call is the thing to read. Without the skill, 11 of the 12 messages for the commit requests carried the ticket number and none used the house format, so the vague skill looked as if it had nearly worked.
On the request without “commit” that mentioned “the team’s usual format” the opposite happened: Claude went looking for the team’s conventions, opened helper’s SKILL.md as an ordinary file and copied the format exactly, all three times, and once more on another phrasing, with no Skill call.
A reply in the right format can’t tell you whether Claude chose the skill.
This is one skill, one model and twelve phrasings, with 40 other skills loaded. Two exploratory phrasings an AI reviewer ran outside the plan saw the description version miss once, so test your own setup. Beyond that one contest with code-review, it says nothing about a crowded listing where two skills could both answer.
Read your result
If Claude chose the skill on every phrasing and left the near-miss request alone, the choice passes.
If it missed some, put the words from the misses into the description, or into when_to_use in Claude Code, and run them again, each two or three times.
If a sharp description still misses, or another skill fires instead, look outside the skill as well: a neighbour claiming the same job, a listing that has dropped descriptions, or a skill of the same name shadowing yours, which the Skill call won’t reveal because it prints only the name. The Skill That Never Fired covers those, and its runnable pack grades a skill against the neighbour it could lose to.
Anthropic’s skill-creator plugin automates part of this: it “generates should-trigger and should-not-trigger prompts, measures the hit rate, and proposes description edits when the skill activates on the wrong requests.” In the Claude app there is no stream to read; the help centre’s advice is to “Review Claude’s thinking to confirm it’s loading the skill.”
The checklist
This week
☐ Your skill’s name says its job, and its description says what it does and when to use it, in the words you would type, with the main use first.
☐ claude plugin validate passes on the folder.
☐ The Skill call appears on three or four phrasings you really use, one of them without the skill’s keyword, each run more than once, and stays away from a near-miss request the skill should leave alone.
☐ When Claude chooses the skill, the reply follows it; you have checked the output as well as the call.
Next
☐ Any skill that commits, deploys or sends has disable-model-invocation: true, and your permissions cover the action itself.
☐ Every skill you didn’t write has been read line by line, the SKILL.md as well as the scripts.
☐ The team’s skills are committed in .claude/skills/, with names a personal skill won’t shadow.
Once it works
☐ The check runs again after each change to a description, a change of model, and each Claude Code update.
☐ Skills headed for the Claude app use only its six frontmatter fields.
The whole job
Write the description for the moment Claude chooses, then watch it choose. This week, run the check on the skill you rely on most, with one phrasing that leaves out its keyword and one request it should ignore, and rewrite the description until the call appears, the way {"skill":"helper"} did, or make it a skill you call by name. Until you have watched Claude choose it, a skill is a hypothesis.
Further reading: if you read three of the originals in full, make them Claude Code’s skills docs, Anthropic’s skill-authoring best practices and Anthropic’s engineering post on skills. Every other source is linked where it is used. The sources run from October 2025 to October 2026, and each quotation was matched word for word against a saved copy of its page.
The newsletter · Free
Subscribe to get the next structural lens in your inbox.
Several essays a week, plus Notes through the week.
Signup completes on Substack. Unsubscribe from any email.
If an essay was worth one, buy me a coffee.
Claude and ChatGPT can search this archive and its claim ledgers once you add the connector.