Skills: Stop Retyping Instructions

A skill is a folder with a file in it, invoked by name.

.claude/skills/
  hdd/
    SKILL.md

.claude/skills/ is Claude Code's spelling of this, and this chapter uses it throughout. Other agents have the same idea under other names and paths — Cursor rules, Copilot instructions files — and the judgement half of this chapter (when a skill is worth writing, when it is the wrong answer, how to tell one has gone stale) transfers unchanged. The paths and the /name invocation do not. If your team is on something else, read the commands as illustrations and the reasoning as the content.

Type /hdd and your agent reads that file and follows it. That is the entire mechanism, and knowing it takes about ten seconds — the rest of this chapter is about when writing one is worth it, and the one way they go badly wrong.

Look at a real one

Install a board and you get nine of them without asking:

$ yurtle-kanban init --theme software
Skills:  9 Claude Code skills installed to .claude/skills/

$ ls .claude/skills/
blocked  done  feature  handoff  release  review  status  sync  work

Open one — but not from the nine above. The HDD skill ships with a different theme, and this is the kind of detail that only shows up when you actually run it:

$ yurtle-kanban init --theme hdd
Skills:  8 Claude Code skills installed to .claude/skills/

$ ls .claude/skills/
experiment  hdd  hypothesis  idea  literature  release  status  sync

Here is the top of hdd/SKILL.md:

---
name: hdd
description: Hypothesis-Driven Development overview and workflow
disable-model-invocation: true
allowed-tools: Read, Grep, Glob, Bash
argument-hint: "[phase]"
---

# Hypothesis-Driven Development (HDD)
...

Four of those five frontmatter lines are doing real work, and they are the part worth copying:

field what it buys
description how the agent decides this skill is relevant at all
allowed-tools the skill cannot reach for anything not listed — a read-only skill stays read-only
disable-model-invocation the agent may not fire this on its own; a human types /hdd
argument-hint tells the caller what /hdd <thing> expects

allowed-tools and disable-model-invocation are the two people skip, and they are the two that decide how much damage a wrong skill can do. A skill that only needs to read files should say so, and then it cannot do anything else.

The thing that makes skills different from documentation

A skill is executed, not read.

That sounds obvious and it has a sharp consequence: a skill whose commands have drifted from reality is worse than no skill at all, because a human skims a wrong command and frowns, while an agent runs it.

I did not have to invent an example. The HDD skill above — which is ours, and which is well built — tells you to run this:

yurtle-kanban create --board research hypothesis "Testable claim"
$ yurtle-kanban create --board research hypothesis "Adding examples improves answers"
Usage: yurtle-kanban create [OPTIONS] ITEM_TYPE TITLE
Error: No such option '--board'.

There is no --board flag. create --help does not mention the word.

The exact punctuation of that error comes from click, the argument-parsing library, not from yurtle-kanban — which does not pin a version of it. On a different machine the same mistake printed Error: No such option: --board. Same defect, different bytes. If you are going to trust console output over prose, it is worth knowing which parts of it are the tool talking and which are its dependencies.

And the same file's setup instruction has the second half of the problem. Back in the software project from the top of this chapter — the one with nine skills, which is what a reader following this skill would actually have:

$ yurtle-kanban board-add research --preset hdd --path research/
Added board 'research'        # rc 0 — this part works

$ ls .claude/skills/
blocked  done  feature  handoff  release  review  status  sync  work

The skill's own Related Skills table lists /idea, /literature, /hypothesis, /experiment. board-add installs none of them. Skills come from init — and only from the hdd theme, which is where we had to go to read this file in the first place. So a reader who follows the skill's own setup line in a software project gets a research board with none of the commands the file then tells them to use.

⚠ If you ran init --theme hdd earlier in this chapter you already have all four, which is exactly why the defect is easy to miss from the inside: whether the file is broken depends on which project you read it in, and the file does not say.

Both are filed. I am showing you our own broken skill rather than a hypothetical one because the failure is not exotic — it is what happens to any instruction nothing executes, and a skill is exactly a file of instructions that nothing executes until it matters.

This is not a small class of mistake

Earlier the same day I wrote this, I corrected an install command on our own site — by reading a README instead of running what I was about to print. The README had the same bug, so the correction inherited it and shipped.

Same shape, one layer up: an instruction written from a source rather than from a run. The only reliable defence is that somebody, at some point, executes the thing.

When writing one is worth it

The third time you paste the same instructions.

Not the first — the first time, pasting is correct and writing a skill is premature abstraction. Not the second, which might be coincidence. The third is a pattern, and the paste is now costing you more than the file would.

The other reliable trigger is when the instructions have a detail you keep getting wrong. If you have twice told an agent to run the tests and twice forgotten to mention the feature flag, that flag belongs in a file, not in your memory.

When a skill is the wrong answer

Three cases, and they are common enough to name:

The workflow is not stable yet. If you are still changing what you ask for each time, a skill freezes a decision you have not made. Paste until it settles.

It runs once. A migration, a one-off audit, a cleanup. Writing a skill for it costs more than doing it, and you will never invoke it again.

The instruction is genuinely different every time. "Review this PR" looks repeatable and is not — what you want reviewed depends on the PR. A skill here produces a checklist that gets followed literally and misses the thing that mattered, which is worse than the freehand version.

The test: could you write down what this does without knowing which case you are about to apply it to? If not, it is a prompt, not a skill.

Write one

Pick a workflow you actually repeat — not the example above. Then:

Create a skill at .claude/skills/<name>/SKILL.md for this workflow: <describe it>.
Give it a description, set allowed-tools to only what it needs, and set
disable-model-invocation to true so it only runs when I ask for it.

Then invoke it by name and watch what it does.

Run every command in it before you trust it, including the ones you are sure about. That is the whole lesson of this chapter, and the skill I showed you above is what happens when nobody does.

Now make something execute it

Everything above is advice. This part is the only thing that actually keeps a skill honest, and it is thirty lines.

A skill is a file of shell commands wrapped in prose. Nothing runs it until an agent does, in front of you, on a day when you need it to work. So run it yourself, on a schedule, in a place where failing is cheap:

#!/usr/bin/env bash
# skill-check <SKILL.md> — run every command a skill file contains, report rc.
set -u
TOOL="${TOOL:-yurtle-kanban}"          # the command your skill files invoke
n=0; bad=0
while IFS= read -r line; do
  cmd="$(printf '%s' "$line" | sed 's/^[[:space:]]*//')"
  case "$cmd" in "$TOOL"*) ;; *) continue ;; esac
  n=$((n+1))
  out="$(eval "$cmd" 2>&1)"; rc=$?
  printf '  rc %-3s %s\n' "$rc" "$cmd"
  [ "$rc" -eq 0 ] || { bad=$((bad+1))
    printf '         %s\n' "$(printf '%s' "$out" | grep -m1 -i error)"; }
done < "$1"
printf '\n  %d commands, %d failed\n' "$n" "$bad"

Read what that loop does before you run it: it executes every matching command in the file, for real. That is the point — you cannot smoke-test an instruction without running it — but it means the blast radius is whatever the file contains. Ten Minutes to a Board warns that migrate replaces a board rather than merging into it; if a command like that is in the skill you are checking, this loop will run it.

So run it the way you would run any unreviewed script: in a throwaway directory, on a board you do not care about, never in the project you are working in. Read the file first. If you would not paste those commands into your terminal by hand, do not point this at them.

Point it at the HDD skill from the top of this chapter — the one that ships with the tool, written by people who meant well. Run it in a fresh project: board-add returns rc 0 the first time and rc 1 ("board already exists") on every run after, so a second run in the same directory reports three failures rather than two.

$ skill-check .claude/skills/hdd/SKILL.md

  rc 0   yurtle-kanban board-add research --preset hdd --path research/
  rc 2   yurtle-kanban create --board research idea "Research question"
         Error: No such option '--board'.
  rc 2   yurtle-kanban create --board research hypothesis "Testable claim"
         Error: No such option '--board'.
  rc 0   yurtle-kanban board research

  4 commands, 2 failed

Half the file is wrong, and it took under a second to find out. No test framework, no CI, no fixtures — a loop, a case, and an exit code.

The reason to write this before you need it is that the failure mode of a stale skill is not an error message. It is an agent following the file confidently, hitting rc 2 on step two of five, and improvising the rest. You get a plausible-looking result built on a step that did not happen, and nothing in the transcript flags it — because from the agent's side, recovering from a failed command is the job.

Why a naive extractor is good enough here, and where it stops

The loop above matches lines starting with your tool's name. It will miss commands inside fenced blocks that are indented, multi-line commands with a trailing backslash, and anything built up in a variable.

That is fine, and worth being explicit about rather than quietly over-claiming: it is a smoke test, not a parser. Its job is to catch the flag that was renamed and the subcommand that moved — which is what actually rots — and it does that at a cost of thirty lines you will never maintain.

If it grows past a page, you have built a test suite. Build a test suite instead.

A skill you cannot check is a skill you are trusting on the strength of having written it carefully once. That is the same bet the --board flag above lost.

Where to go next

  • Many Agents, One Rule — what changes when it is not one agent, and the rule that does not bend
  • The skills you already havels .claude/skills/ after any init. Reading a few is faster than reading more about them