How to write a proper Claude Code skill
9m read time

How to write a proper Claude Code skill

Most Claude Code skills are a prompt with a fancy filename. A proper skill is a procedure that encodes distrust of the model itself. Here is how to write one, using a code-audit skill as the worked example.

You read the decision tree, you picked "skill" out of the seven ways to steer Claude Code, and now you have an empty SKILL.md open. What do you actually put in it?

Here is what most people put in it: a prompt. A paragraph or two of "audit this code and tell me what's wrong", saved under a directory with a nice name. It loads, the model reads it, the model probably does roughly what it says. Probably.

That is not a skill. That is a prompt with a fancy filename.

A proper skill is a procedure. And the best ones do something a prompt can never do: they encode distrust of the model that runs them. They name exactly how the agent is going to go wrong, and they build the guardrail into the steps so it can't.

Let me show you what I mean with a real one. Take a code-audit skill: point it at a path or a git range, it hunts for defects and writes a report. It is a good example precisely because the interesting part is not the part you'd expect.

What a skill actually is

Strip away the mystique first, because it matters that the container is boring.

A skill is a folder with a SKILL.md in it. That file has YAML frontmatter with two required fields, name and description, and then a markdown body with the actual instructions. That's it. That's the whole format. Anything heavier, a reference doc, a helper script, lives in a sibling file the skill points to, and only gets loaded when it's genuinely needed. I walked through the mechanics of installing and organising these in the superpowers plugin guide.

The container is deliberately dumb. All the value is in what the body tells the agent to do, and in how tightly it constrains the ways that can go wrong.

So the real question was never "what's the file format". It's "what separates a skill worth loading from a saved prompt". Three things, and code-audit shows all three.

A skill is a procedure, not a prompt

Here is the naive version of code-audit, the one most people would write:

Audit this code for bugs, security issues, and bad logic. Report what you find.

Drop that on a model and it will read some files, pattern-match against things that look like bugs, and hand you a confident list. Some of it real. Some of it hallucinated. You have no way to tell which, so you have to re-check all of it by hand, which is the work you were trying to avoid.

The actual code-audit skill is a procedure with three phases. It partitions the target into audit units small enough that one subagent can read every line. It spawns a finder per unit across four fixed dimensions, plus one cross-cutting finder for contradictions that are invisible to any single-unit reader. And then it does the move that separates a skill from a prompt:

No finding is asserted until a fresh, independent skeptic subagent has tried to refute it and failed.

Read that again, because it is the entire point. Every finding a finder produces gets handed to a new subagent whose only job is to kill it. That skeptic receives the claim and nothing else. Not the finder's reasoning, not its confidence, not its justification. Here is the skeptic's actual instruction, trimmed:

text
You are a skeptical senior engineer. A code audit produced this claim. Your job is to REFUTE it.
Claim: in [file:line], [dimension] defect: [failure scenario]. Offending code: [code].
Read the actual file(s) in full, including surrounding code, callers, and any guards
elsewhere that would prevent the failure. Then decide:
- REFUTED: the failure cannot actually happen. Default to REFUTED if you cannot confirm
  it end-to-end.
- CONFIRMED: you traced the failure scenario end-to-end and it happens. Quote the exact
  lines that prove it.

Notice the default. REFUTED unless proven, with the burden on the finding to survive rather than on the skeptic to demolish it. The skeptic reads the actual file in full, including the guards and callers elsewhere that might make the failure impossible, and it never sees the argument that made the finding sound convincing in the first place.

That design starts from an assumption most prompts never make: the model generating findings is not trustworthy. It will produce plausible-looking bugs that dissolve the moment someone actually checks. So the skill builds the check in. It sets the model's output against a fresh instance of the same model, stripped of the reasoning that made the claim feel true, and only the survivors reach you.

This is the difference. A prompt tells the agent what to do. A procedure tells it what to do and how it's going to be wrong while doing it.

The craft is naming how the agent goes wrong

Once you accept that a skill is guardrails against the model's own failure modes, the rest of the craft follows. Here's where it shows up.

The description is a trigger, not a summary. This is the one everyone gets backwards. The description field exists so a future agent, scanning a list of skills, can decide whether to load this one. It should describe when to use the skill, not what the skill does. Here is code-audit's real frontmatter:

yaml
---
name: code-audit
description: Use when asked to audit code — "audit this module", "logic audit",
  "fact-checked review", "/code-audit [path|ref]" — for a path, git ref/range, or the
  whole repo. Runs finder subagents across four dimensions, then adversarially verifies
  EVERY finding with independent skeptic subagents before asserting it. NOT for reviewing
  the current working diff (use /code-review) and NOT a fix-applying tool — it is strictly
  read-only.
---

It leads with the phrases someone would actually say and, crucially, spends its back half on when not to reach for it. It is not perfect. That middle clause, "runs finder subagents, then verifies", is exactly the workflow summary I just told you to cut, and you could. But the two ends earn their place: the quoted triggers up front so the skill gets found, and the loud NOTs at the back so it does not fire on the wrong job.

Why does this matter so much? Because if you summarise the workflow in the description, the agent reads the summary and thinks it now knows the procedure, so it never opens the body. A description that says "audits code by finding and verifying issues" quietly teaches the agent to skip the part where it learns how. Keep the process out of the description. The description gets you in the door. The body does the teaching.

The output contract is a recipe, not a wish. A finder in code-audit can't just say "this looks wrong". The skill forces every finding into a fixed shape: dimension, severity, exact file and line, the offending code quoted, and a failure_scenario that has to be a concrete input or state leading to a concrete wrong outcome. The skill spells it out: "This looks wrong" is not a scenario. An empty list is explicitly allowed and called respectable.

That rigidity is the point. "Report anything suspicious" is an open invitation to pad the list with vibes. A required, concrete shape gives the model nothing to negotiate with. Either you can name the input that breaks it, or you have no finding. The same principle runs through why the prompt is not the spec: loose instructions get loose output, and the fix is to make the shape non-optional.

Close the loopholes before the agent finds them. code-audit ends with a table of discipline rules, each marked "no exceptions", and a red-flags list titled "stop and correct yourself". A slice of that table:

RuleNo exceptions
One skeptic per finding for critical/high severityNever batch these
Never override a skeptic's REFUTED back to confirmedIf you disagree, spawn a second skeptic, majority wins
No finding skips verificationMove low-severity items to an Unconfirmed appendix, never assert unverified
Finder dies or returns garbageMark its unit "not audited", never fabricate coverage

Every one of those lines exists because it's a shortcut a model under pressure will reach for. Batching skeptics to save time. Quietly promoting a refuted finding back because it feels right. Papering over a crashed subagent so the report looks complete. Writing these into the skill isn't paranoia, it's memory: it's every way this exact procedure has gone wrong, written down so it can't go wrong the same way twice. A skill without a red-flags section is a skill that hasn't met its own failure modes yet.

Match the effort to the target. code-audit has a scaling rule: if the target is five files or fewer, skip the finder fan-out and do the find pass inline. But the verification never degrades, even a tiny target still gets fresh skeptic subagents. That's judgement encoded directly. Don't spin up eight subagents to audit a helper file, but never let the core guarantee slip just because the job looked small.

What to actually do

Next time you're staring at an empty SKILL.md, work in this order.

  • Write the description as a trigger. Start with "Use when", load it with the words someone would actually type or say, and state where the skill does not apply. Do not summarise the steps. If your description explains the workflow, delete that half.
  • Write the procedure, not the intention. If your body reads like something you'd type into the chat box, it's a prompt. Break it into phases with a clear job each. Name what gets read, what gets produced, and in what order.
  • Find the step where the model will lie to you, and build the check in. This is the actual craft. Where does the procedure depend on the model's own output being correct? That's where you add the independent verification, the skeptic, the second pass that can't see the reasoning that made the first pass feel right.
  • Make the output a contract. Fixed fields, concrete evidence required, "an empty answer is a valid answer" stated out loud. Give it nothing to pad with.
  • Write down every way it's gone wrong. A no-exceptions table and a red-flags list. Not hypothetically, from watching it actually fail. If you've never watched the skill fail, you don't yet know what to put there, which means you're not done.

A prompt asks the model to be good. A skill assumes it won't be, and holds the line anyway. That is the whole difference, and it's why the ones worth keeping all read less like instructions and more like a list of the ways their author has already been burned.

series: Claude Pro(14 of 14)