Claude Code skills: the missing manual
Skills are the most misunderstood extension point in Claude Code, partly because they quietly absorbed slash commands. What a skill actually is, when a procedure deserves one instead of bloating CLAUDE.md, the four frontmatter features that do the real work, and the pitfalls the reference docs will not warn you about.
A skill in Claude Code is a markdown file that teaches the agent a procedure. Put instructions in .claude/skills/deploy/SKILL.md and two things become true: you can run it by typing /deploy, and Claude can load it by itself when the task matches. That second part is what makes skills more than macros, and it is the part most write-ups skip.
We run a blog pipeline, a multi-agent workflow, and a release process through skills daily, so this is the practical manual: when a procedure deserves a skill, the small set of frontmatter that does the real work, and the failure modes you only find by running them.
One piece of recent history that explains most confusion: custom slash commands and skills are now the same feature. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy. Old command files keep working; skills are the current form, with a directory for supporting files and control over who invokes them. If you learned "commands" in 2025, skills are that, grown up.
When a procedure deserves a skill
The cleanest test comes from watching CLAUDE.md files rot: facts belong in CLAUDE.md, procedures belong in skills. "The dev bundle id is com.example.dev" is a fact; the agent needs it every session, so it earns its permanent context cost. "How we publish a blog post" is a nine-step procedure; it is only relevant when publishing, and a skill's body loads into context only when invoked. Long reference material costs nearly nothing until the moment it is needed, which is exactly the economics CLAUDE.md cannot offer. We covered keeping CLAUDE.md small before; skills are where the trimmed material should go.
The other trigger is repetition. The third time you paste the same checklist into chat, you are maintaining a skill with no version control. Move it into the repo, and it becomes shared infrastructure: project skills in .claude/skills/ ship to every teammate on git pull. Personal skills live in ~/.claude/skills/ and follow you across projects; plugins can carry them too.
The minimal skill
---
description: Publish a drafted blog post: build, verify output, deploy, ping search engines. Use when the user asks to publish a post.
---
1. Run `node scripts/build-blog.mjs` and check the post count went up.
2. Verify the new HTML exists and the index includes it.
3. Commit, then deploy with `cd server && fly deploy`.
4. Confirm the live URL returns 200 and the sitemap includes the slug.
5. Ping IndexNow with `node scripts/indexnow-ping.mjs /blog/<slug>`.
The directory name becomes the command; the description is not decoration, it is the routing layer. Claude reads it to decide when to load the skill on its own, so write it like a matching rule: what the skill does, then the phrases that should trigger it. A vague description means the skill exists but never fires automatically, which is the most common way skills quietly fail.
The four features that do the real work
Everything in the frontmatter table is optional; these four earn their keep in practice.
Dynamic context injection. A line like !`git diff HEAD` in the body runs the command and inlines its output before Claude reads the skill. Your instructions arrive already grounded in the current state of the world: the actual diff, the failing test list, today's date. This is the difference between "check the diff" (a request the model may fumble) and handing it the diff.
disable-model-invocation: true. Some procedures should never run just because the conversation drifted near them: deploys, releases, anything that costs money. This flag makes a skill strictly manual; it runs when a human types /name and never otherwise. The inverse exists too: user-invocable: false hides background knowledge from the / menu while letting Claude load it when relevant.
allowed-tools. A skill can pre-approve the exact tools its procedure needs, scoped to the turn that invoked it. The permission philosophy from our best-practices post applies: instead of loosening global settings so a workflow can run, grant precisely inside the skill and let everything else keep prompting.
context: fork. Marks the skill to run in its own subagent context instead of the main conversation, keeping a heavy procedure's noise out of your session while it works in the background. For long checklists that would otherwise flood your context window, this changes what is practical to automate.
Two more get honorable mention: paths activates a skill only when Claude touches matching files (monorepo packages can carry their own skills), and skills can even register hooks when invoked.
What this looks like in a real setup
Our daily driver examples, since first-hand beats hypothetical:
- A publish pipeline. The blog procedure above is real; one invocation runs build, verify, deploy, and search pings, with the verification steps written as commands whose exit codes settle arguments. Before it was a skill, every publish depended on remembering nine steps in order.
- A multi-agent pipeline. Our repo's
/pipelineskill encodes a state machine (product manager to architect to engineer to QA to reviewer), with routing rules in the skill body. The skill is the orchestrator's memory; the agents themselves stay stateless. - Recorded launch recipes. Claude Code's own bundled skills lean the same way:
/run-skill-generatorfigures out how to build and launch your app once, then commits the recipe as a project skill so every future session and agent follows it instead of rediscovering it. That pattern (spend the discovery once, commit the procedure) is the whole skills thesis in one feature.
Pitfalls, from use rather than docs
- A loaded skill stays in context. Once invoked, the body persists across turns for the rest of the session, so every line is a recurring token cost. Keep bodies terse; push long reference material into supporting files in the skill directory that the instructions tell Claude to read when needed.
- The description has a budget. The listing Claude routes from truncates at 1,536 characters of description; front-load the use case, because the tail may never be read.
- The directory name is the command. For personal and project skills, frontmatter
nameonly changes the display label. If you want/ship, the directory must beship. - Auto-invocation is probabilistic; typing is not. Claude loading the right skill at the right moment depends on your description matching the conversation. For procedures where "usually fires" is not good enough, type the slash command; for procedures where firing unasked would be bad, set
disable-model-invocationand remove the ambiguity. - Name collisions resolve by rank. Personal beats project, and a local skill overrides a bundled one with the same name, which is occasionally what you want and occasionally a mystery to debug. If
/somethingbehaves strangely, check whether a personal skill is shadowing the project's.
Where to start
Take the procedure you have explained to Claude most often this month. Make it a directory, write the steps as numbered imperatives, add a description with trigger phrases, and inject any state it needs with ! commands. That single skill will teach you more than the reference docs, and the second one takes five minutes.
The pattern behind all of it is the same one that runs through our whole workflow: move knowledge out of chat and into files the agent can reliably re-read. Chat is steering; files are state. Skills are just the version of that rule that ships with a slash command attached.