Module 6 — Build your own slash commands: skills from A to Z
Previous modules walked through the shipped commands. This module teaches you to add your own: skills. Since v2.1.199, custom commands and skills are the same thing. A .claude/skills/deploy/SKILL.md file and a .claude/commands/deploy.md file both create /deploy; the old format still works, the new one brings a folder of supporting files, a richer frontmatter, and the option for Claude to decide on its own to invoke the skill. Unlike CLAUDE.md, a skill body only loads when the skill is invoked: a long reference topic costs almost nothing until it is used.
Anatomy of a skill
A skill is a folder containing at least one SKILL.md file. The folder name becomes the command to type: .claude/skills/deploy/SKILL.md creates /deploy. The folder can carry other files (scripts, specs, examples) that Claude will read on demand. SKILL.md has two parts: a YAML frontmatter between --- that describes the skill (Claude uses it to decide whether to load the skill on its own), and Markdown content that is the prompt handed to Claude when the skill is invoked. The frontmatter is only read if --- is the very first line; otherwise Claude Code treats the whole file as the body.
---
description: Summarizes uncommitted changes and flags what looks risky. Use when the user asks "what changed?" or wants a commit message.
---
## Current changes
!`git diff HEAD`
## Instructions
Summarize in two or three bullets, then list the risks: missing error handling, hardcoded values, tests to update. If the diff is empty, say so.
The line !`git diff HEAD` is a dynamic injection: Claude Code runs the shell command before sending, and its output replaces the placeholder. The model receives the live diff, not the command.
Scopes: where to store a skill
Seven locations coexist:
| Location | Path | Loaded in |
|---|---|---|
| Enterprise | .claude/skills/<name>/SKILL.md in managed settings | Machines where the organization deploys it |
| Personal | ~/.claude/skills/<name>/SKILL.md | Every project on the machine |
| Project | .claude/skills/<name>/SKILL.md | Every session in the repo |
| Nested | <subfolder>/.claude/skills/<name>/SKILL.md | Sessions started in/under <subfolder>, otherwise when Claude touches a file inside that path |
| Added directory | .claude/skills/<name>/SKILL.md in a --add-dir folder | This session only |
| Plugin | <plugin>/skills/<name>/SKILL.md | Where the plugin is active, under /<plugin>:<name> |
| claude.ai account | Skills enabled on the claude.ai side | Cowork and cloud sessions (module 15) |
Precedence on name clashes: enterprise > personal > project. A .claude/commands/ file yields to a skill with the same name. A plugin skill is prefixed. The name synced is reserved. A repo skill applies in -p even inside an unapproved folder: reread allowed-tools before any claude in the repo.
Full frontmatter reference
All fields are optional; only description is recommended. Booleans: yes/no/on/off/1/0 in addition to true/false (v2.1.218+).
| Field | Effect |
|---|---|
name | Display name. Defaults to the folder name. Does not change the command to type, except inside a plugin where name replaces the last segment. |
description | What the skill does and when to use it. Cap combined with when_to_use: 1,536 characters. |
when_to_use | Additional context: trigger phrases, examples. |
argument-hint | Autocomplete hint: [file] [format]. |
arguments | Named list for $name substitution. String or YAML list. |
disable-model-invocation | true prevents Claude from loading the skill on its own (and from firing it via a scheduled task). |
user-invocable | false hides from the / menu: only Claude-side invocation works. |
allowed-tools | Tools authorized without confirmation during the invocation turn. Grant cleared on the next message. |
disallowed-tools | Tools removed from the pool during the turn. Cannot remove EndConversation if other tools remain. |
model | Model for the duration of the turn; same values as /model, or inherit. |
effort | Effort for the turn: low, medium, high, xhigh, max. |
context | fork runs the skill in an isolated subagent. |
agent | Subagent type when context: fork: Explore, Plan, general-purpose, or a subagent from .claude/agents/. |
background | With context: fork, false blocks the turn. Defaults to true. v2.1.218+. |
hooks | Hooks registered at invocation, active for the session. |
paths | Glob patterns limiting auto-invocation to matching files. |
shell | bash (default) or powershell for injected commands. |
metadata | Free YAML map for your own tooling. |
license, compatibility | Fields of the Agent Skills standard, accepted with no effect. |
Outside Claude Code (claude.ai upload, Skills API, package_skill.py), only name, description, license, compatibility, metadata and allowed-tools are accepted; the others are Claude Code extensions.
Arguments: $ARGUMENTS, $0/$1, $name
A skill receives whatever follows its name on the line. Four placeholder forms: $ARGUMENTS (the whole thing as a single string — without a placeholder, Claude Code appends it at the end of the skill as ARGUMENTS: <value>), $ARGUMENTS[N] (zero-based indexed access), $N (equivalent shortcut, $0, $1), and $name (named argument declared in arguments: [issue, branch], $issue takes the first).
Multi-word values must be quoted (/migrate "SearchBar" JavaScript TypeScript). An indexed placeholder without an argument stays unchanged; a named one without a value becomes an empty string. An argument containing $1 or $ARGUMENTS is inserted literally without re-expansion. For a literal $, escape it: \$1.00.
Dynamic injection and file references
Two forms inject external content before sending to Claude. Inline line !`command`: recognized only if ! is at the start of a line or right after a space; the output replaces the placeholder and is not re-scanned. Multi-line block ```! for several commands. @file references attach a file to the context on invocation, useful to share a convention snippet between several skills.
Two variables are substituted in the body and in the Bash rules of allowed-tools: ${CLAUDE_SKILL_DIR} (the skill's folder) and ${CLAUDE_PROJECT_DIR} (project root, v2.1.196+). The classic trick is to write a script inside the skill folder and pre-approve its execution: allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/gen.sh *) then, in the body, call ${CLAUDE_SKILL_DIR}/scripts/gen.sh. If an injected command fails, the whole invocation is aborted — Claude sees nothing. An exit code 1 from the search commands listed in "Output limits" is treated as normal; append || true on other commands expected to exit non-zero.
Subagent, permissions and visibility
context: fork runs the skill inside an isolated subagent. The body becomes the prompt; the conversation history is not transmitted. Three reasons: long background task, reduced tool palette (agent: Explore in read-only), or large output to contain. The fork runs in the background by default; background: false (v2.1.218+) blocks the turn. A background fork writes outside of checkpoints: /rewind will not undo its edits, you have to go through git.
allowed-tools authorizes tools without confirmation during the invocation turn (grant cleared on the next message); disallowed-tools blocks; disable-model-invocation: true prevents Claude from loading the skill on its own (useful for anything with a side effect); user-invocable: false does the opposite (hidden from the menu, only Claude-side invocation works). The skillOverrides file applies visibility from the outside: "on", "name-only", "user-invocable-only", "off". The /skills menu writes it on its own.
Reloading and iterating
Claude Code watches skill directories and accounts for additions, edits and deletions without restarting, except in bare mode. /reload-skills forces a rescan. The best testing discipline remains A/B comparison; the official skill-creator plugin automates the loop and writes an evals.json in the skill's folder.
Claude Code loads the skill list on every turn with a budget of 1% of the model window. Beyond that, descriptions are trimmed starting with the least invoked. Placing the key sentence first, capping description + when_to_use at 1,536 characters, and pushing a secondary skill to "name-only" in skillOverrides keeps the list useful. /skill-doctor (v2.1.252+) shows the cost of each skill.
Running example: six skills for Kiosque
Six skills to write in Kiosque's .claude/skills/. None requires tooling beyond git, gh, pytest, ruff and make.
1. /commit — conventional message from the staged diff
---
description: Writes a conventional commit message from the staged changes and offers to commit. Use after `git add` or when the user asks to "commit".
argument-hint: "[scope]"
disable-model-invocation: true
allowed-tools: Bash(git diff --staged*) Bash(git status*) Bash(git commit *)
---
## Staged diff
!`git diff --staged`
## Status
!`git status --short`
## Instructions
Write a **conventional** message (`feat:`, `fix:`, `refactor:`, `test:`, `chore:`, `docs:`) in French, scope = $ARGUMENTS if provided, otherwise inferred from the file paths.
- Title: 72 characters max, present tense, imperative.
- Optional body: only if the "why" is not obvious.
- Refuse and flag if the diff contains `.env` or `secrets/`.
Then run `git commit -m "<message>"`.
2. /review-code [file] — read-only review
---
description: Reviews a file (or the current diff) looking for bugs, missing tests and convention violations. Read-only.
argument-hint: "[path]"
allowed-tools: Read Grep Glob Bash(git diff*)
disallowed-tools: Edit Write Bash(git commit*) Bash(git push*)
---
Target: $ARGUMENTS (if empty, take `git diff HEAD`).
## Reference diff
!`git diff HEAD -- $ARGUMENTS`
## Instructions
Review the code **read-only** — edit nothing. Look for:
1. **Bugs**: inverted condition, wrong type, off-by-one, missing error handling.
2. **Security**: SQL injection, cleartext secret, missing input validation.
3. **Kiosque conventions** (see `.claude/rules/api.md`): function names in English, comments in French, `raise HTTPException` rather than returning an error.
4. **Missing tests**: new branch not covered, error case not tested.
Return a report in four sections (Bugs / Security / Conventions / Tests), each item with file, line, sentence.
The command name is /review-code because /review is already a built-in (exact alias of /code-review since v2.1.223). Chaining works too: /review-code /commit paiements triggers a review then a commit.
3. /new-endpoint <resource> — route + schema + test
---
description: Generates a FastAPI CRUD endpoint (route + Pydantic schema + pytest test) for a new resource.
argument-hint: "<resource-singular>"
allowed-tools: Read Grep Glob Edit Write
---
Target resource: **$ARGUMENTS**.
@.claude/rules/api.md
Existing routes:
!`ls app/routes/`
## Instructions
Create: `app/schemas/$0.py` (Pydantic `Create`, `Read`, `Update`), `app/routes/$0.py` (`GET /$0s`, `GET /$0s/{id}`, `POST`, `PATCH`, `DELETE`; SQLAlchemy via `Depends(get_db)`), `tests/test_$0.py` (one happy-path test per endpoint, 404 on `GET /$0s/{id}`, 422 on `POST`). Register the router in `app/main.py` and run `pytest tests/test_$0.py`.
4. /targeted-tests — pytest on tests touched by the diff
---
description: Runs only the pytest tests that touch files in the current diff. Use before every commit for fast feedback.
allowed-tools: Bash(git diff*) Bash(pytest*)
---
## Modified files
!`git diff --name-only origin/main...HEAD`
## Instructions
Take the modified `tests/` files, add `tests/test_<module>.py` for every modified `app/<module>.py`, then run `pytest -x -q <files>`. If nothing is targeted, `pytest -x -q -k <keyword>` to cover indirect tests. Report failures (test, line, assertion). Do not re-run the whole suite — `make test` stays under the user's keyboard.
5. /api-doc — updates docs/api.md from the routes
---
description: Regenerates `docs/api.md` from the FastAPI routes under `app/routes/`. Use after any endpoint addition or change.
allowed-tools: Read Grep Glob Edit Write Bash(rg *)
paths: app/routes/**
---
Routes:
!`rg -n "@(?:router|app)\.(get|post|patch|put|delete)" app/routes/`
## Instructions
For each endpoint: method, path, tag, summary (docstring), input and output Pydantic schemas, status codes. Write `docs/api.md`: one table per tag `| Method | Path | Input | Output | Statuses |`, then one H3 section per endpoint with a French description (reuse the docstring when it exists). End with "Generated by `/api-doc` — do not edit by hand".
6. /release-notes <tag> — forked subagent
---
description: Writes markdown release notes for a Git tag from the commits since the previous tag. Runs in a separate subagent.
argument-hint: "<tag>"
context: fork
agent: Explore
background: true
allowed-tools: Bash(git log*) Bash(git tag*) Bash(git show*) Read Grep
---
Target tag: **$ARGUMENTS**.
Previous tag:
!`git describe --tags --abbrev=0 $ARGUMENTS^ 2>/dev/null || echo "(no earlier tag)"`
Log:
!`git log --pretty=format:"%h %s" $(git describe --tags --abbrev=0 $ARGUMENTS^ 2>/dev/null)..$ARGUMENTS`
## Instructions
Group into four sections: **New features**, **Fixes**, **Internal improvements**, **Documentation**. Ignore merge commits and `chore(deps)` commits. One sentence per item, clear for a non-technical user ("The payment module now accepts a second provider"), not the raw commit message. Return the markdown in the conversation; do not create a file.
Since v2.1.199, a single line chains two skills: /review-code /commit paiements loads both skills and passes paiements as the argument, useful when the review must not block anything.
Summary
- A skill is a
.claude/skills/<name>/SKILL.mdfolder: the folder name is the command, the frontmatter describes the skill, the body is the prompt. - Seven scopes, precedence enterprise > personal > project; a
.claude/commands/<name>.mdfile still works but yields to a skill with the same name. - Arguments:
$ARGUMENTS,$0/$1,$name(viaarguments:). Shell injection:!`cmd`or```!block. References:@file. Variables${CLAUDE_SKILL_DIR}and${CLAUDE_PROJECT_DIR}are substituted in the body and inallowed-tools. context: forkruns the skill in an isolated subagent, in the background by default (background: falseto block the turn).allowed-toolspre-approves for the turn;disable-model-invocationprevents Claude from loading it on its own;user-invocable: falsehides from the menu;skillOverridesapplies visibility in settings./reload-skillsforces a hot rescan;/skill-doctor(v2.1.252+) shows the cost of each skill.
Next module: Permissions, execution modes, sandbox and settings files — set the rules that let Claude work on its own without opening a hole.