---
title: "Claude Code Skills: How They Work, How to Write One, and How to Check Claude Uses It"
description: "Claude picks a skill from its name and description. How to write the two lines it reads, and check it chose yours."
author: "Harry Floyd"
publication: "The Durability Curve"
canonical: "https://durabilitycurve.com/blog/claude-code-skills/"
date: "2026-10-26"
series: "ENGINEERING GUIDES"
claims: "https://durabilitycurve.com/claims/claude-code-skills/"
format: "markdown mirror of the canonical HTML page; figures are named, not embedded"
---

# 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.*

By Harry Floyd · 2026-10-26 · canonical: https://durabilitycurve.com/blog/claude-code-skills/

*Claude picks a skill from its name and description. How to write the two lines it reads, and check it chose yours.*

[Figure: 41 skills loaded, drawn as a row of glass cards fading into the distance. The front card is helper, "Does stuff with files". A request for a commit message travels down the row, and the readout says helper was chosen 0 of 12 times.]

**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](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices) 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](https://durabilitycurve.com/blog/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 `Skill` call 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](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-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.

[Figure: What Claude reads before it chooses a skill (each skill's name and description) and after (the chosen skill's body). For helper, the two lines were "helper" and "Does stuff with files".]

**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.

```markdown
---
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`:

```bash
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](https://code.claude.com/docs/en/skills) 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](https://agentskills.io/specification) 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](https://durabilitycurve.com/blog/skill-that-never-fired/), "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](https://durabilitycurve.com/blog/ai-agent-permissions/).

## Where skills live in Claude Code, the Claude app, ChatGPT and Codex

[Figure: Where skills live: Claude Code reads ~/.claude/skills and .claude/skills; the Claude app takes a ZIP; ChatGPT lists them under Plugins for Business, Enterprise, Healthcare and Edu; Codex reads .agents/skills. Each tool can pick a skill itself, and each lets you ask for one by name.]

### 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](https://support.claude.com/en/articles/12512180-use-skills-in-claude) 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](https://help.openai.com/en/articles/20001066-skills-in-chatgpt), "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](https://snyk.io/blog/toxicskills-malicious-ai-agent-skills-clawhub/) 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](https://durabilitycurve.com/blog/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:

```bash
claude plugin validate .claude/skills
```

```text
✘ 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](https://scottspence.com/posts/measuring-claude-code-skill-activation-with-sandboxed-evals) 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.

```bash
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'
```

```text
{"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:

```text
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.
```

[Figure: Five versions of one commit skill, Claude Code 2.1.292 and Opus 5.5. The vague helper was never chosen on a commit request (0 of 21). A telling name, Anthropic's description, the two together, and the recommended version were each chosen on all 21. No version took a review request or an unrelated one.]

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](https://durabilitycurve.com/blog/skill-that-never-fired/) covers those, and [its runnable pack](https://durabilitycurve.com/tools/skill-routing-eval/) 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](https://code.claude.com/docs/en/skills), [Anthropic's skill-authoring best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices) and [Anthropic's engineering post on skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-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.*
