Skip to content

Course · Claude Code · Skills

Write a repeated procedure as a SKILL.md the agent can run

A skill is a folder with a SKILL.md file inside it: YAML frontmatter that says when the skill applies, and a markdown body that says what to do. Claude Code keeps a listing of skill names and descriptions in context - descriptions can shorten or drop under a budget - and loads a body only when the skill is invoked, so a procedure you repeat is cheap to keep on the shelf. This lesson writes one from scratch.

The procedure is the branch wrap-up in a small TypeScript payments service (the example this course returns to; the facts you need are here). Its CLAUDE.md - the instruction file Claude Code reads from the repo - sets three testing rules: run tests with npm test, never commit without passing tests, every bug fix must include a regression test. Its package.json supplies lint (eslint src/ --ext .ts) and build (tsc, in strict mode). Those four moves - tests, regression check, lint, build - are the ritual you would otherwise narrate at the end of every task.

Make the folder first, inside the repo, so the skill is a property of the project rather than of your machine:

mkdir -p .claude/skills/finish-pr

The name matters twice. The folder name is the skill’s name, and it is also the command name - typing /finish-pr later runs this skill directly. Where the folder lives decides who gets it: .claude/skills/ in the repo means everyone who clones the payments service has it, carried by git like any other project file; ~/.claude/skills/ would make it personal, available in all your projects but yours alone. The full locations table is in the skills documentation. This ritual belongs to this codebase, so it belongs in the repo.

Every SKILL.md is the same two parts, and they do completely different jobs. The frontmatter between the --- markers is the label on the outside of the box: the agent reads it to decide whether to open the skill at all. The body is what is inside - instructions that load only once the skill fires. Here is the whole file for the ritual - create it at .claude/skills/finish-pr/SKILL.md. It is a proposal for you to write, not a file the companion repo ships:

---
name: finish-pr
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.
---
Run these four steps in order before this branch is opened as a PR.
Stop and report if any step fails - do not continue past a failure.
1. Run the test suite with `npm test`. All tests must pass - CLAUDE.md
is explicit: never commit without passing tests.
2. If this branch fixes a bug, name the test that proves it. If you
cannot name one, stop and report - every bug fix must include a
regression test.
3. Run `npm run lint`. If it reports issues, fix them and run it again.
4. Run `npm run build` and confirm it compiles. TypeScript is strict
here, so this is the check that catches code no test exercises.
When all four pass, summarize what each step did and flag anything the
reviewer should look at.

The description names the situation - a change is done, a branch is ready to wrap up - not the mechanism. You would never type finish a pull request in those words, but you say I think this is ready constantly, and the agent matches your phrasing against this field. Leave description out entirely and Claude Code falls back to the first non-empty line of the body, which is almost never a trigger; the trigger lesson tunes the field deliberately.

That is the entire contract: one directory, one file, frontmatter for when, body for what. The anatomy below maps the parts of a SKILL.md and shows which keys the other agent tools honor. Claude Code treats name as optional and defaults it to the folder name; several of the other tools in that anatomy require it, so the file states it outright and stays portable across all of them.

SKILL.md
------
description

The trigger. Eligible skill descriptions are commonly indexed up front; the model matches your intent against them to decide when to pull the body in. Indexing can be bounded or lazy, so verify the active tool. Write this as a "use when…" hint, not a summary.

Capped at 1024 characters where a limit is documented (OpenCode, Copilot).

  • Claude Coderecommended · drives auto-invocation · omitted falls back to the first body line
  • Codexrequired · drives auto-invocation
  • OpenCoderequired · 1 - 1024 chars
  • Cursorrequired · drives auto-select
  • Copilotrequired · ≤1024 chars

The body is standing instructions, and once a skill fires its content stays in the conversation for the rest of the session - every line is a recurring cost, so state what to do and skip the why. Two lines in this body are doing more than they appear to:

  • Stop and report if any step fails. A human running a wrap-up has an instinct for obviously don’t open a PR over a red suite; the agent has only what is written. The guardrail is one line to write and impossible to improvise - an instruction the agent follows, not a gate that enforces it.
  • Name the test that proves it. Step two is not a command - it is the repo’s own rule, every bug fix must include a regression test, turned into a demand the agent must answer before it can claim the branch is done. A skill can carry judgment, not only commands.

Notice what the four steps are: npm test, a regression-test check, npm run lint, npm run build. They map one to one onto the repo’s real surface - the two CLAUDE.md gates and the two package scripts beyond test. If your own project’s wrap-up has more moves than this one, they go in the same list; the skill is the container, the steps come from your repo.

You do not have to wait for recognition to find out whether the steps work - invoke the skill directly by typing the folder name with a slash, in a Claude Code session running in the repo (the getting-started chapter covers starting one). An illustrative first run, shaped like the four steps above; the counts are invented, and yours depend on what your working copy carries (the companion repo ships tests/payments.test.ts and tests/ledger.test.ts; any spec you added is counted too):

> /finish-pr
1. npm test
✓ tests/payments.test.ts (3)
✓ tests/ledger.test.ts (3)
Test Files 2 passed (2)
Tests 6 passed (6)
2. No bug fix claimed on this branch - nothing to name.
3. npm run lint - clean
4. npm run build - compiled without errors
All four steps pass. Nothing flagged for the reviewer.

Two failures are likely at this point, and both are inspectable. If the skill behaves as if it has no description, check the frontmatter: the --- fences must open the file - anything saved above the first fence, a pasted filename included, becomes body text and hides the description - and a mis-indented description makes the YAML parse as body. The fallback is then the first non-empty body line - a procedure sentence rather than a trigger, and recognition will suffer for it.

If the run stops at step one on a failing test, the skill is working, not broken: it reports which test failed and goes no further. Before blaming the branch, run npm test on a clean checkout of main and compare. A red suite that predates the branch is exactly the fact you want surfaced before a pull request, not after it.

That is the procedure you used to narrate at every wrap-up, now one command - and a thing the agent can reach for on its own. But by hand is the fallback, not the goal. A skill beats a plain command because you should not have to remember to invoke it: the agent should notice the branch is ready and run the ritual itself. That recognition lives in one frontmatter field - so make it fire at the right time, and only the right time.