A skill is a folder with an instruction manual: the manual (the description) stays resident in context so Claude can "find its way", while the body loads only when invoked. This lesson is based on official docs fetched 2026-08-05 (Claude Code 2.1.222 waterline).
Why this lesson comes today
D4 covered CLAUDE.md: fully resident, occupying context on every single request, so it should hold only short always-apply rules. But real work has another class of content — deploy checklists, API style guides, debugging runbooks — not needed every turn, yet needed in full when it is. That's exactly where Skills sit: Officialthe official docs explicitly advise that when you keep pasting the same set of instructions into conversations, or a section of CLAUDE.md has grown from "facts" into "procedure", it's time to extract it into a skill; a skill's body loads only when used, so long reference material costs almost no context while idle.
This lesson is also the foundation for the back half of the course: D8 Hooks will contrast skills with "deterministic execution vs. model-interpreted execution"; D10 Subagents uses the context: fork mentioned here; and the packaging-and-distribution story of D14 Plugins ships skills as its main cargo. Skip this lesson and all three that follow are missing a piece of the puzzle.
Core concepts, explained
SKILL.md: frontmatter + body
OfficialEach skill is a directory whose entry file is always named SKILL.md, in two parts: the YAML frontmatter fenced by --- tells Claude "when to use me", and the markdown body after it holds the instructions Claude executes when it's invoked. The directory name is the command name you type (.claude/skills/deploy-staging/ → /deploy-staging). All frontmatter fields are optional; the docs recommend only description as a must-write:
| Common field | What it does |
|---|---|
description | What the skill does and when to use it. Claude relies on it to decide whether to auto-load; if omitted, the body's first paragraph is used instead |
when_to_use | Extra trigger scenarios (trigger phrases, example requests), appended after the description; together the two are truncated at 1,536 characters in the listing |
disable-model-invocation | Set true and only you can trigger it (good for side-effectful actions like deploy or commit) |
user-invocable | Set false to hide it from the / menu; only Claude can use it (good for pure background knowledge) |
allowed-tools | Tools usable without approval during the turn that invokes the skill; the grant clears when you send your next message |
context: fork + agent | Run this skill in an isolated subagent (details in D10) |
paths | Glob patterns; auto-activates only when operations touch matching files |
OfficialIn the body you can receive arguments with $ARGUMENTS / $0 and reference the skill's own scripts with ${CLAUDE_SKILL_DIR}; there's also "dynamic context injection": !`command` executes before Claude sees the content, its output replacing the placeholder — so Claude receives real data, not the command itself. The docs advise keeping SKILL.md under 500 lines, splitting detailed material into companion files in the directory for Claude to read when needed.
Triggering: the description is your only ad slot
OfficialAt session start, only the name and description of every model-invocable skill enter context; Claude matches your request against those descriptions and loads the full text only on a hit. You can also type /skill-name to skip matching and force-load. Which means: how well the description is written directly decides whether the skill gets remembered.
So how do you write a description that hits? Direction from the official troubleshooting guide: Officialinclude the keywords a user would naturally say, and put the most central use cases first (overly long descriptions get truncated); when a skill won't trigger, first confirm it appears in the answer to "What skills are available?", then rephrase your ask closer to the description, or just invoke it with /skill-name. Our takeIf you usually talk to Claude Code in Chinese, write your Chinese trigger phrases into the description too — matching is semantic, but putting the words you'd actually say into the description verbatim gives the steadiest hit rate.
One sneaky pitfall: Officialwhen the frontmatter YAML is broken, Claude Code loads the body with "empty metadata" — manual /skill-name invocation still works fine, but Claude has no description to match against, so auto-triggering fails completely. Start with --debug to see the parse error. So "works manually, never triggers automatically" is almost always a description missing keywords, or broken YAML.
Where to put it: three levels and name collisions
OfficialWhere a skill lives decides who can use it:
| Level | Path | Scope |
|---|---|---|
| Personal | ~/.claude/skills/<skill-name>/SKILL.md | All of your projects |
| Project | .claude/skills/<skill-name>/SKILL.md | This project only (commit it to the repo to share with the team) |
| Plugin | <plugin>/skills/<skill-name>/SKILL.md | Wherever the plugin is enabled |
| Enterprise | Delivered via managed settings | Every user in the organization |
OfficialOn a name collision, levels override: enterprise > personal > project, and a same-named skill at any level overrides the built-in bundled skill (drop a code-review in your project and it replaces the bundled /code-review). Plugin skills use the plugin-name:skill-name namespace, so they never collide. The skills directory is watched live: edits to SKILL.md take effect on the spot, no session restart needed (except when creating a brand-new top-level skills directory). This "project level commits with the repo, plugin level packages for distribution" path is the doorway into D14 Plugins.
Versus CLAUDE.md: resident vs. on-demand (continuing D4)
OfficialThe official division of labor: short rules that "every session must know" (build commands, coding conventions, never-do rules) go in CLAUDE.md, kept under 200 lines; occasionally-needed reference material and triggerable workflows become skills. Two details worth remembering: first, once a skill's full text is invoked it stays in the conversation as a message until the session ends, so keep the body disciplined too — it's not a dumping ground; second, with disable-model-invocation: true not even the description enters context, making manually-triggered-only skills zero idle cost. Our takeRule of thumb: content that's "facts and rules" goes in CLAUDE.md, "procedures and reference" goes in a skill; the moment a CLAUDE.md section sprouts steps 1, 2, 3, that's its signal to move out.
Slash commands are skills
OfficialCustom commands have been folded into the skill system: .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md both create /deploy, with identical behavior; old commands files keep working. Skills are the superset, adding three things: a whole directory of companion files, frontmatter controlling "who can invoke", and being auto-loaded by Claude on description match. The built-in bundled skills (/code-review, /debug, /loop, etc.) run on the same machinery. By default both you and Claude can invoke any skill; two switches turn that into "you only" (disable-model-invocation: true, for actions like deploy where you control the timing) or "Claude only" (user-invocable: false, for background knowledge that isn't an action).
Hands-on: doable today
Goal: build a minimal "summarize uncommitted changes" skill and verify three things — auto-trigger, manual invocation, on-demand loading. About 20 minutes; you'll need a git project.
- Create the skill directory (personal level, available in all projects):
Expected: directory created. Note: ifmkdir -p ~/.claude/skills/summarize-changes~/.claude/skills/is brand-new and a Claude Code session is already open, restart the session so it watches the new directory; edits after that all take effect immediately. - Save the following as
~/.claude/skills/summarize-changes/SKILL.md(structure from the official getting-started example, with the description worded to match how you'd actually ask):
The--- name: summarize-changes description: Summarizes the current uncommitted git changes and flags anything risky. Use when the user asks "what did I change", wants this round of changes reviewed, needs a commit message, or asks you to review the diff. --- ## Current changes !`git diff HEAD` ## Instructions Summarize the changes above in two or three bullet points, then list the risks you notice, such as missing error handling, hard-coded values, or tests that need updating. If the diff is empty, just say there are no uncommitted changes.!`git diff HEAD`is dynamic context injection: when the skill is invoked, the command runs first and the real diff is spliced into the instructions before they reach Claude. - In any git project, casually change a file, start
claude, and test auto-triggering first — with a natural question close to the description:
Expected: the UI shows the skill loading, and Claude gives a two-or-three-point summary plus a risk list based on the real diff — not guesswork.What did I just change? Summarize this round of changes for me and point out any risks. - Now test manual invocation: type
/summarize-changes. Expected: matching is skipped, and you get the same structured output directly. - Verify on-demand loading: ask Claude "What skills are available?" to confirm the skill is registered; then recall
/contextfrom D1 — the Skills line shows only the cost of the description list; the full text only entered context when it was invoked just now. - One step further: add a
disable-model-invocation: trueline to the frontmatter, save (no restart needed), and ask the step-3 question again. Expected: no more auto-trigger (the description has been removed from context), but/summarize-changesstill works. Delete the line to restore things once you've seen the effect.
Finally, put this capability to work on something real — have Claude Code distill one of your recurring procedures into a project-level skill:
Turn my pre-release checklist into a project-level skill:
- Create .claude/skills/release-check/SKILL.md
- Write a description that spells out the trigger scenarios, including the keywords I actually say (release, ship, go live)
- List the check steps in the body, each one verifiable
- Set disable-model-invocation: true so only I can trigger it manually with /release-check
When it's written, show me the file contents and explain what each frontmatter field does.
How to know you've learned it
- Without notes, you can describe SKILL.md's two-part structure, and the role the description plays "at session start" versus "at trigger time".
- You can draw (or talk through) this lesson's first diagram: the difference between the auto-match path and the
/skill-namepath. - You can explain the context-cost difference between the same content living in CLAUDE.md versus as a skill, and what each is suited to hold.
- All 6 hands-on steps completed: you've seen auto-trigger, manual invocation, and the effect of
disable-model-invocationwith your own eyes. - Self-check: a skill runs fine via
/name, but Claude never uses it on its own. Name the two most likely causes and their fixes. (Hint: one is in the description's content, one is in the frontmatter's syntax or switches.)