How to write a SKILL.md

Architectural floor plans drawn and annotated so another person can build from them
Image: Orphanage, Swanley, Kent: floor plans · CC0

Installing other people’s skills gets you to the point where you notice the gap. The thing you keep correcting is specific to your codebase, and nobody has written it down because nobody else has your codebase.

So you write it yourself. The format takes about ten minutes to learn. Making a skill that actually fires when it should is the part worth getting right, and it comes down to a handful of decisions.

The minimum viable skill

A skill is a folder with one required file:

release-checklist/
└── SKILL.md

And SKILL.md is Markdown with a YAML frontmatter block on top:

---
name: release-checklist
description: >
  The release process for this service, including the staging gate and
  the migration ordering rule. Use when cutting a release, preparing a
  deploy, or writing release notes.
---

## Order of operations

1. Migrations run on staging first, always. A migration that has not run on
   staging does not go to production, no matter how small it looks.
2. ...

That is a complete, working skill. There is no build step, no registration, no manifest to update. Drop the folder in ~/.claude/skills/ for yourself or .claude/skills/ for the repository and it is live — the installation guide covers both paths in detail.

Frontmatter: only two fields matter

Of everything you can put in frontmatter, exactly two change the agent’s behaviour.

---
name: release-checklist        # required — the identifier
description: >                 # required — the trigger
  ...
license: MIT                   # optional, but publish with one
version: 1.2.0                 # optional
compatibility: [claude-code, cursor]  # optional
tags: [deployment, release]    # optional
---

name should be a short, kebab-case identifier that matches the folder name. Keep them identical; a mismatch is confusing to every human who reads the repository later.

description is where skills live or die, so it gets its own section.

The rest is metadata for humans and for catalogs like this one. It costs you one line each and it is what makes your skill legible to someone deciding whether to trust it. A declared licence in particular: more than a fifth of the skills we index declare none at all, which leaves anyone who finds yours no clear footing to adapt or redistribute it.

YAML will bite you

Two failure modes account for nearly every malformed skill we crawl:

# BROKEN — the unquoted colon-space reads as a nested mapping
description: Deployment rules. Triggers: deploys, rollbacks

# FIXED — fold the value into a block
description: >
  Deployment rules. Triggers: deploys, rollbacks

# ALSO FIXED — quote it
description: 'Deployment rules. Triggers: deploys, rollbacks'

The other is indentation: YAML is whitespace-sensitive and a single tab character invalidates the block silently. When frontmatter fails to parse, the skill usually loses both its name and its description, which is the same as not being installed.

The description is the whole ballgame

Here is the mechanic that explains every piece of advice that follows.

At rest, the agent holds only each installed skill’s name and description — a few dozen tokens apiece. When you make a request, it matches that request against those descriptions and pulls in the body of whichever ones look relevant. The body could be perfect and it will never be read if the description does not match.

So a description is not a summary of the topic. It is a list of the situations in which this skill should apply, written so a matcher can recognise them.

# Never fires — describes a subject, not a situation
description: Helps with database stuff.

# Fires sometimes — better, still abstract
description: Database conventions for this project.

# Fires reliably — names the situations explicitly
description: >
  Database conventions for this service: migration ordering, the
  soft-delete policy, and why we do not use cascading deletes. Use when
  writing or reviewing a migration, adding a table, or changing a model.

Three rules that follow from this:

  1. Name the triggers, in the words a person would use. “Use when writing a migration” beats “for schema management”. You are matching against how requests are phrased, not how documentation is titled.
  2. Say what is inside. The description doubles as a table of contents that helps the agent judge whether the body is worth loading.
  3. State the boundaries when they matter. “Python projects only.” “Not for one-off scripts.” An over-broad description that fires constantly is as much of a problem as one that never fires, because it crowds out the skill that should have won.

Writing the body

The body is read by a model that already knows how to program. That changes what is worth writing.

Skip the general education. It does not need an explanation of what a database migration is. It needs to know that your migrations run on staging first.

Write the decisions, not the topic. The useful content is what someone with good general knowledge would get wrong about your specific situation:

  • the library you migrated away from and must not reintroduce
  • the test helper that already exists so nothing hand-rolls it again
  • the ordering constraint that is not visible in the code
  • the thing that looks like dead code and is not

Be concrete and be short. Examples outperform prose. A ten-line snippet of the pattern you want, next to the anti-pattern you keep seeing, does more than three paragraphs describing either.

Say why, briefly. A rule with a reason survives contact with a novel situation the rule did not anticipate; a bare prohibition does not.

Be decisive. “You could either use X or Y” hands the decision back. If your team has a convention, state it as the convention.

A body between roughly 50 and 200 lines covers most skills well. If yours is growing past that, split it:

api-conventions/
├── SKILL.md            ← the decisions, always loaded when relevant
├── error-codes.md      ← the long reference table
└── examples/
    └── handler.ts      ← a canonical implementation to copy

Reference the extra files from the body by relative path. They get read when needed rather than every time the skill fires.

Scope: one skill per job

The instinct is to write one big skill covering your whole stack. Resist it.

A skill named backend whose description mentions APIs, databases, testing, deployment, and logging matches everything loosely. It fires on requests it has nothing useful to say about, and it competes for selection with the four narrow skills that would each have been precisely right.

Split by the decision being made, not by the technology:

Instead of oneWrite several
pythonpytest-conventions, dependency-policy, async-patterns
frontendcomponent-structure, state-management-rules, design-tokens
devopsrelease-checklist, terraform-conventions, incident-runbook

Narrow skills are also far easier to maintain, because when a convention changes you know exactly which file is now wrong.

Test it before you commit it

Two checks, in order.

Does it load? Ask the agent what skills it has available. If yours is absent, the problem is structural — SKILL.md nested too deep, or frontmatter that failed to parse.

Does it fire on a realistic request? Not on “use the release checklist skill”, which proves nothing. Phrase a request the way you would on a normal Tuesday — “ship the current branch to staging” — and see whether the behaviour changes.

Then try the inverse: a request that should not trigger it. A skill that fires on everything is a description that needs narrowing.

Iterate on the description first. It is the highest-leverage line in the file, and most “my skill does not work” problems never reach the body at all.

Publishing it

If the skill is useful beyond your team, put it on GitHub. The conventions that make one discoverable and installable:

  • A licence. MIT and Apache-2.0 cover roughly 94% of the licensed skills we index, and either is a fine default. Skills with no licence get skipped by anyone with a policy.
  • A README with a one-line pitch and the install command, since that is what people read before SKILL.md.
  • Topics on the repository — claude-skills, agent-skills, claude-code-skills. This is how crawlers, including ours, find new work.
  • skills/ as the directory if you are shipping several, with one folder each.

A skill you wrote for yourself and then published is worth more than one written speculatively for an audience. Start from the correction you are tired of making.

Where to look for prior art

Reading a few well-made skills is the fastest way to calibrate length and tone. The catalog indexes public ones with licence, popularity, and last-commit date on every listing:

Read three, then write yours. The format is small enough that imitation gets you most of the way there.

Frequently asked questions

What fields does SKILL.md frontmatter need?
Only two are load-bearing: name and description. Everything else — license, version, compatibility, tags — is optional metadata that helps humans and catalogs understand the skill, but the agent selects on the description alone.
How long should a SKILL.md be?
Short enough that you would read it yourself. Most effective skills sit between 50 and 200 lines. Past that, split supporting material into separate files in the same folder and reference them from the body, so the detail is available without being loaded every time.
Why does my skill never activate?
Almost always the description. The agent holds only each skill name and description at rest and matches your request against them, so a description that names no concrete situations gives it nothing to match on. Rewrite it as a list of triggering situations rather than a summary of the topic.
Should I write one big skill or several small ones?
Several small ones, scoped to a job each. A skill covering an entire language or framework matches everything vaguely and nothing precisely, and it competes with your other skills. One skill per recurring decision is easier to trigger and easier to maintain.
Can a SKILL.md tell the agent to run scripts?
Yes. Bundle the script alongside SKILL.md and reference it by relative path in the body. Keep the script in the repository rather than piping a remote one into a shell, since anyone installing your skill is trusting it with their working tree.
agent-skillsskill-mdauthoringhow-to

Docs skills to try

All docs skills →

A documentation page — inline-start nav, scrollable article body, inline-end table of contents. Use when the brief mentions "docs", "documentation", "guide", "API reference", or "tutorial".

89k nexu-io Apache-2.0

Records decisions and documentation. Use when making architectural decisions, changing public APIs, shipping features, or when you need to record context that future engineers and agents will need to understand the codebase.

88k addyosmani MIT

Grounds every implementation decision in official documentation. Use when you want authoritative, source-cited code free from outdated patterns. Use when building with any framework or library where correctness matters.

88k addyosmani MIT

Extract business domain knowledge from a codebase and generate an interactive domain flow graph. Works standalone (lightweight scan) or derives from an existing /understand knowledge graph.

80k Egonex-AI MIT

Analyze a Figma file via the Figma REST API and generate an interactive design knowledge graph (pages, screens, components, component sets, instances, design tokens) with a kind:"design" dashboard.

80k Egonex-AI MIT

Analyze a Karpathy-pattern LLM wiki knowledge base and generate an interactive knowledge graph with entity extraction, implicit relationships, and topic clustering.

80k Egonex-AI MIT