When a skill is warranted
Write a skill when you catch yourself giving Claude the same instructions more than once. If you have explained your commit message style, your triage rules, or your compliance checklist in three separate conversations this month, that is the signal. A one-off task does not need a skill; it needs a good prompt. A skill earns its place when the workflow repeats and the instructions are stable enough to write down once.
Scope one workflow, not five
The single most common mistake in a first skill is scope creep: one SKILL.md trying to cover triage, drafting, filing and scheduling all at once. Each of those is a real workflow with its own inputs, its own edge cases and its own trigger condition, and cramming them together makes the description too vague to match reliably and the body too long to stay under the token budget that keeps a skill cheap to have installed. Write one skill per workflow. If a task genuinely needs several, write several skills and let the instructions in one point to the next, the way a real pipeline is built out of named steps rather than one enormous step.
The frontmatter rules, exactly
Every SKILL.md opens with YAML frontmatter between two --- lines, and only two fields are required:
- name: maximum 64 characters, lowercase letters, numbers and hyphens only. No XML tags. It cannot contain "anthropic" or "claude", since those are reserved words.
- description: non-empty, maximum 1024 characters, no XML tags.
That is the entire required surface. Everything else, headings, lists, code blocks, is ordinary markdown in the body, with complete freedom over structure.
Writing a description that actually triggers
The description is not a summary for a human browsing a list. It is the field Claude matches your request against to decide whether to read the rest of the file, so it needs to do two jobs in one or two sentences: state what the skill does, and state when to use it.
Compare a weak version to a working one:
- Weak: "Helps with emails." Says a topic, not a trigger. Almost anything could match this or nothing will.
- Working: "Sort a full inbox into act, waiting, read or archive with a one-line reason and a priority. Use at the start of an email session or any time the inbox has more than about 15 unread." States the output shape and the exact moment to reach for it.
If you are not sure your description is specific enough, try to answer this in one sentence using only what is written there: what request should cause Claude to open this file, and what request should not? If you cannot answer cleanly, rewrite the description before touching anything else.
Structuring the body
There is no mandated template, but a body that reliably produces good output tends to answer the same questions in roughly this order:
- When to use this. A short reinforcement of the trigger from the description, plus the case it is explicitly not for.
- Inputs. What Claude needs before it can start, named specifically rather than implied.
- Steps. The procedure itself, as an ordered list where order matters. Concrete verbs, not general advice.
- Edge cases. The two or three situations that actually come up and would otherwise be improvised badly.
- Output format. The exact shape the result should take, so it can be consumed by whatever comes next.
- Quality bar. A short checklist for what "done well" looks like, so Claude has a way to self-check before handing the result back.
Keep the whole body well under the roughly 5,000-token guideline for what loads when the skill triggers. If a section is only needed for an uncommon case, a reference file, and a link to it from the main body, keeps that detail off the token cost of every ordinary run.
A full worked example
Here is a complete, minimal, genuinely usable skill: it turns a list of merged changes into one dated CHANGELOG.md entry, following the Keep a Changelog convention. Nothing about it is a toy; you could drop this into .claude/skills/changelog-entry/SKILL.md in a real repository today.
---
name: changelog-entry
description: Turn a list of merged changes into a single dated
CHANGELOG.md entry in Keep a Changelog style. Use when the user asks
to update the changelog, write release notes, or log what changed
since the last entry.
---
# Changelog entry
Turn a short list of merged changes into one clean CHANGELOG.md entry.
## When to use this
After a batch of PRs merge, or when asked to "update the changelog" or
"write release notes." Not for a single throwaway commit.
## Inputs
- A list of changes: PR titles, commit summaries, or plain bullets.
- The current top of CHANGELOG.md, so the new entry can be inserted
above it and match the format already there.
## Steps
1. Group changes into Added, Changed, Fixed, Removed. Drop empty
categories.
2. Rewrite each item as one short, user-facing sentence. Skip
internal-only noise (refactors, dependency bumps) unless it
changed behavior.
3. Date the entry with today's date, ISO format (YYYY-MM-DD).
4. Insert the new entry directly above the most recent existing
entry. Never reorder or edit past entries.
## Output format
```markdown
## [YYYY-MM-DD]
### Added
- ...
### Fixed
- ...
```
## Quality bar
- Every bullet is one sentence, plain language, no PR numbers or
hashes.
- Nothing is invented: only changes present in the input make the
entry.
- The file's existing entries are untouched.Notice what makes it hold together: the description names both the action and three concrete trigger phrases, the inputs are stated rather than assumed, the steps are ordered because order matters (categorize, then rewrite, then date, then insert), and the quality bar gives Claude something to check its own output against before calling the task done. That is the whole pattern; everything else in this guide is elaboration on those five moves.
Common mistakes that keep a skill silent
- A description that states a topic, not a trigger. Covered above, and still the most common failure by a wide margin.
- Bundling unrelated tasks into one skill. Splits the trigger condition across too many scenarios for the description to state cleanly.
- Frontmatter that breaks the format rules. A name over 64 characters, an uppercase letter, an underscore instead of a hyphen, or the word "claude" in the name will keep the skill from loading correctly.
- No stated output format. Without one, results vary run to run, and nothing downstream can rely on the shape of what comes back.
- Happy-path-only instructions. The first messy real-world case becomes the first time the skill improvises, usually inconsistently with how it would improvise the second time.
- Writing thousands of words nobody will read at trigger time. Long, unfocused instructions cost more tokens every time the skill fires and are genuinely harder for Claude to follow precisely. Move detail that is not needed on every run into a separate reference file instead.
Testing it, then placing it
Once the file is written, test the trigger directly: ask a question that should match the description and confirm Claude reaches for the skill rather than improvising. Then try a near-miss question that should not match, to check the description is not so broad it fires on the wrong requests. Iterate on the description first if either test fails; it is almost always the fix.
For where to put it: start personal, at ~/.claude/skills/, while you are iterating. Once it is stable and other people on a project would benefit, move it to .claude/skills/ inside the repository so it travels with the code. If you eventually want to version it, share it beyond one repository, or bundle it with related agents and hooks, package it as a plugin; see Claude Code's own guide to creating plugins for that step, and Anthropic's authoring best practices for a deeper treatment of the description-writing step above.
If you would rather start from something already proven than build from a blank file, the SkillCrate storefront has two complete, production-length SKILL.md files open to read for free, one for list building and one for inbox triage, structured exactly the way this guide describes.