CC5: Skills & Slash Commands
Build your own /deploy, /triage, /review. Custom commands and skills are now the same thing — here's how to use the merged system, including shell-injected context, forked execution, and the bundled skills that ship with Claude Code.
Learning Objectives
- Explain why custom commands and skills merged — and when to still use the simple-command shape.
- Drop a SKILL.md anywhere in the 4-scope hierarchy and have Claude pick it up live.
- Use all six argument substitution variables (
$ARGUMENTS,$N,$name,${CLAUDE_SESSION_ID},${CLAUDE_EFFORT},${CLAUDE_SKILL_DIR}). - Inject live data with
!`cmd`backtick syntax (and disable it via policy). - Pick when to use
context: forkfor isolation vs inline execution. - Reach for the right bundled skill (
/simplify,/batch,/debug,/loop,/claude-api) instead of writing one.
Commands and Skills, Unified
Imagine your kitchen used to have two different ways to write recipes: index cards (one-pagers) and binders (multi-page playbooks with checklists, prep notes, photos). Both produced dishes. Both used the same ingredients. The split forced you to choose: simple? Use a card. Complex? Use a binder. And every binder needed its own giant filing cabinet.
Then you got smarter cards: stick a card in a folder, optionally add prep notes, photos, scripts. Same place, same name, same way to find it — just more capacity when you need it. Index cards aren't deprecated; they live as folders-with-only-a-card-inside, which is fine.
That's the merge. Old .claude/commands/foo.md still works, but it's now a degenerate skill: a folder named foo with just a SKILL.md inside. New work gets the upgrade for free, old work keeps running.
A skill is a directory containing a SKILL.md file. The directory name becomes the slash command (my-skill/ → /my-skill). The file uses YAML frontmatter for config and Markdown for the prompt body. Skills can also include supporting files (templates, scripts, examples) that the body references.
Old-style files at .claude/commands/name.md still work and use the same frontmatter. Skills add four things commands can't do: automatic invocation (Claude triggers based on description), supporting files, hooks scoped to skill lifecycle, and execution in a forked subagent context.
The 4 Scopes — Where Skills Live
| Scope | Path | Visible to |
|---|---|---|
| Enterprise | managed-settings/skills/ | Everyone in the org — admin-deployed |
| Personal | ~/.claude/skills/<name>/SKILL.md | You, all your projects |
| Project | .claude/skills/<name>/SKILL.md | This repo, all teammates (checked into git) |
| Plugin | <plugin>/skills/<name>/SKILL.md | Wherever the plugin is enabled (namespaced as plugin:skill) |
Conflict resolution: Enterprise overrides Personal overrides Project. Plugin skills use plugin-name:skill-name namespacing — they cannot conflict with non-plugin skills. If a skill and a custom command share a name at the same scope, the skill wins.
Live change detection: add, edit, or remove a skill inside ~/.claude/skills/, project .claude/skills/, or any directory passed via --add-dir — the change takes effect during the current session. No restart needed. Creating a brand-new top-level skills directory does require a restart so it can be watched.
Monorepo bonus: nested discovery
When you work on a file in packages/frontend/, Claude Code also scans packages/frontend/.claude/skills/. Each sub-package can ship its own skills without polluting the root.
Build Your First Skill in 60 Seconds
Concrete example: a skill that runs the test suite, parses failures, and prints a focused report. We'll call it /test-triage.
mkdir -p .claude/skills/test-triage
cat > .claude/skills/test-triage/SKILL.md <<'EOF'
---
description: Run the test suite, identify failures, propose fixes
allowed-tools: Bash(pnpm test*), Bash(pnpm typecheck*), Read, Edit
---
Run `pnpm test` and capture the output.
For each failure:
1. Identify the test file and the failing assertion.
2. Read the source under test.
3. Decide whether the test is wrong or the source is wrong.
4. Propose the smallest change that makes the test pass without breaking others.
Report:
- Total: X tests, Y failing
- For each failure: file, root cause, proposed change
- Don't apply changes — just report.
EOF
Now in any session inside that repo:
> /test-triage
Claude runs the suite, reads the source, and reports. The allowed-tools entry pre-approves Bash for pnpm test and pnpm typecheck only — nothing else.
You wrote a 12-line skill that turns a multi-step diagnostic flow into one slash command. Five teammates run /test-triage in their sessions and get the same playbook. That's the bargain: skills convert "the way you debug" from tribal knowledge into checked-in artifacts.
SKILL.md Frontmatter — The Knobs
15 frontmatter fields. You'll use 4-5 most of the time. The full list:
| Field | What it does |
|---|---|
name | Display name (defaults to dir name); lowercase + hyphens |
description | Recommended. When Claude should auto-invoke. Front-load the use case — combined with when_to_use, capped at 1,536 chars in the listing |
when_to_use | Trigger phrases / example requests, appended to description |
argument-hint | Autocomplete hint, e.g. [issue-number] |
arguments | Named positional args for $name substitution: arguments: [issue, branch] |
disable-model-invocation | true → only YOU can invoke. Use for /deploy, /commit |
user-invocable | false → hides from / menu; only Claude can invoke (background knowledge) |
allowed-tools | Pre-approves tools while skill is active — doesn't restrict, just skips per-use prompt |
model | Override (sonnet/opus/haiku/full ID/inherit); resets after the turn |
effort | Thinking budget for the turn: low / medium / high / xhigh / max |
context | fork → runs in a forked subagent context, no main-conversation access |
agent | Which subagent type executes the fork: Explore, Plan, general-purpose, or custom |
hooks | Hooks scoped to this skill's lifecycle (CC7) |
paths | Glob patterns — auto-load only when working with matching files |
shell | bash (default) or powershell (Windows; needs CLAUDE_CODE_USE_POWERSHELL_TOOL=1) |
allowed-tools
allowed-tools grants pre-approval, not restriction. Listing Read there does NOT prevent Claude from using Edit — it just skips the per-use prompt for Read. To actually block tools while a skill runs, add deny rules in permissions (CC4). Most users misread this on first encounter.
Argument Substitution — Six Variables
Variables interpolate before the skill body is sent to Claude. Positional args use shell-style quoting ("hello world" = one arg, not two).
| Variable | Resolves to |
|---|---|
$ARGUMENTS | Full argument string. If absent from body, appended as ARGUMENTS: <value>. |
$ARGUMENTS[N] / $N | Positional arg by 1-based index ($1, $2…) |
$name | Named arg from arguments frontmatter list, mapped by position |
${CLAUDE_SESSION_ID} | Current session ID — use for log paths, correlation IDs |
${CLAUDE_EFFORT} | Active effort level — adapt instructions ("at low, skip exhaustive checks") |
${CLAUDE_SKILL_DIR} | Directory containing this SKILL.md — use to invoke bundled scripts |
Example: a fix-issue skill
---
name: fix-issue
description: Fix a GitHub issue by number
arguments: [issue, branch]
disable-model-invocation: true
allowed-tools: Bash(gh issue view*), Read, Edit, Bash(git*)
---
Fix GitHub issue $issue on branch $branch.
1. Run `gh issue view $issue` and read the description.
2. Identify the affected files.
3. Implement the fix on $branch.
4. Run tests.
5. Open a PR linked to issue #$issue.
If $branch doesn't exist, create it from main.
Invoke with /fix-issue 1234 fix/login-bug — $issue becomes 1234, $branch becomes fix/login-bug.
Shell Injection — !`cmd` Runs Before Claude Sees Anything
Skills can run shell commands at render time using backtick syntax. The output replaces the placeholder, and the rendered prompt is what reaches Claude. This is preprocessing — not Claude executing the command.
---
name: pr-summary
description: Summarize the current pull request using live GitHub data
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---
## PR diff
!`gh pr diff`
## Reviewer comments
!`gh pr view --comments`
## Multi-line block
```!
node --version
npm --version
git status --short
```
Summarize the changes and flag anything that needs reviewer attention.
When this runs:
- Each
!`cmd`executes; stdout is captured. - Output replaces the placeholder in the rendered text.
- Claude receives the rendered prompt with actual PR data inlined — no tool round-trips needed.
For managed environments, set "disableSkillShellExecution": true in settings. Each !`cmd` is replaced with [shell command execution disabled by policy]. Affects user/project/plugin skills; bundled and managed skills are exempt. Most useful in managed-policy settings where users cannot override.
Inline shell injection saves tool round-trips. A /pr-summary skill without it would call Bash 3 times during the conversation. With injection, the data is already there when Claude starts thinking. Faster, cheaper, fewer permission prompts.
Skills That Run in a Fork — context: fork
Add context: fork and the skill runs in a forked subagent context: it gets the system prompt, no main-conversation access, and its tool calls don't pollute your transcript — only the final result returns. Pair with agent: to choose which subagent type executes (CC6 covers the full subagent system).
---
name: deep-research
description: Research a topic across the codebase
context: fork
agent: Explore
---
Research $ARGUMENTS thoroughly:
1. Use Glob and Grep to find relevant files
2. Read the most relevant ones
3. Summarize findings with specific file references
4. Identify follow-up questions worth pursuing
When to use fork: the skill produces verbose intermediate output (file reads, search results) you don't need in main context. When NOT: reference content like API conventions; those should run inline so Claude can use them alongside your conversation.
context: fork only makes sense for skills with a task to execute. If the skill is "use these conventions" with no task, the forked subagent receives the conventions but no instruction — it returns nothing useful. Reference content should be plain skills (no fork) or preloaded into a custom subagent.
Bundled Skills — Use Before You Build
Claude Code ships with prompt-based skills in every session. Reach for these before writing your own:
| Skill | Use when |
|---|---|
/simplify | Refactor recently-changed code for clarity / consistency / maintainability |
/batch | Run the same task across many files / branches / PRs |
/debug | Structured debugging playbook (reproduce, isolate, fix, verify) |
/loop | Background monitoring — rerun a check on a cadence and report drift |
/claude-api | Help build agents on top of the Claude Messages API |
Plus a few built-in commands also available through the Skill tool: /init, /review, /security-review. Block all skills with a Skill deny rule, restrict to specific ones with Skill(name) in permissions.allow. To trigger extended thinking from a skill, include the literal word "ultrathink" anywhere in the skill content.
Once invoked, the skill body enters the conversation as one message and stays there. Claude does NOT re-read the file on later turns — write standing instructions, not one-shots. After auto-compaction, the most recent invocation of each skill is re-attached, keeping the first 5,000 tokens of each up to a combined 25,000-token budget. Older invocations get dropped first.
Hands-On Lab — Build a /new-resource Generator for the API
Working in the PublicRecords API. You'll author a project-scoped slash command that scaffolds Controller + Service + Repository + MockMvc test for a new UCC entity, given just an entity name. About 10 minutes.
Step 1 — Create the command file
Slash commands live as Markdown files in .claude/commands/. The filename (minus .md) becomes the command name — new-resource.md = /new-resource. The file body becomes the prompt sent to Claude when you invoke the command. Frontmatter at the top (description, argument-hint) controls the picker UI. Arguments are referenced as $1, $2, etc.
From the project root, make the commands directory:
cd /path/to/PublicRecordsAPI && mkdir -p .claude/commandsCreate the command file with this content:
---
description: Scaffold Controller + Service + Repository + test for a UCC entity
argument-hint: <EntityName>
---
Generate four Java files for a new resource named `$1` in the
`com.publicrecords.api.$1lower` package, where `$1lower` is `$1` lowercased.
Follow the existing layout in `com.publicrecords.api.filing` exactly:
1. **`$1.java`** — JPA entity. Always include:
- `id` (Long, `@GeneratedValue` IDENTITY, primary key)
- `state` (String, 2-char, `@NotBlank`, `@Size(min=2,max=2)`)
- `createdAt` (Instant, `@Column(nullable=false)`)
- 2–3 entity-specific fields appropriate for US public records
(e.g., for `Amendment`: `originalFilingId`, `amendmentType`,
`amendedAt`).
2. **`$1Repository.java`** — extends `JpaRepository<$1, Long>`.
Add a `findByState(String state)` derived query.
3. **`$1Service.java`** — `@Service` with `findAll`, `findById`,
`create($1 entity)`. Constructor-inject the repository (no `@Autowired`).
4. **`$1Controller.java`** — `@RestController` with `@RequestMapping("/$1lower")`.
- `GET /` → service.findAll()
- `GET /{id}` → service.findById, returning `ResponseEntity<$1>`
with `.notFound()` on miss
- `POST /` → service.create with `@Valid @RequestBody`
Every public method gets a 1-line JavaDoc.
5. **`$1ControllerTest.java`** under
`src/test/java/com/publicrecords/api/$1lower/` — one MockMvc test
asserting `GET /$1lower` returns 200.
After generating, run `mvn compile` and report any errors.
Step 2 — Verify Claude sees the command
Restart Claude Code and type / — /new-resource should appear in the picker. Type /help new-resource to confirm the description and argument-hint are loaded.
Step 3 — Generate a real resource
> /new-resource Amendment
Claude reads the existing filing/ package as reference, generates the five files under amendment/, then runs mvn compile. Watch the file ribbon — you should see five Edit/Write calls in sequence. If mvn compile fails, ask Claude to fix the errors and re-compile.
Step 4 — Verify the structure matches the existing pattern
$ tree src/main/java/com/publicrecords/api/amendment src/test/java/com/publicrecords/api/amendment
$ mvn test
$ curl http://localhost:8080/amendments # in another terminal after mvn spring-boot:run
Expected: five new files in the right packages, all tests pass, and /amendments returns [] (no seed data, but the endpoint responds 200).
Step 5 — Generate a second resource and confirm consistency
> /new-resource Termination
The command should produce a structurally identical package — same field naming, same controller method signatures, same test shape. That consistency is the whole point of a slash command: the next teammate doesn't have to remember conventions because the command bakes them in.
Step 6 — Convert it to a Skill (optional, for autoinvocation)
Slash commands are explicit (/new-resource X). A Skill with the same description fires whenever Claude infers it's needed. Convert:
$ mkdir -p .claude/skills/new-resource
$ mv .claude/commands/new-resource.md .claude/skills/new-resource/SKILL.md
Now ask Claude in plain language: "We need a new entity for UCC continuation filings." The skill should fire without you typing the command. (If it doesn't, your description field isn't specific enough — Claude won't auto-invoke a skill it isn't confident about.)
A reusable scaffolding command (or skill) under .claude/ that produces structurally consistent CRUD packages on demand. Two real packages (amendment/, termination/) generated, compiled, and tested. The command is committed with the repo, so every teammate gets the same conventions for free. CC6 builds a subagent that audits these new endpoints for missing PII protections.
Knowledge Check
1. You have .claude/commands/deploy.md in your repo. A teammate adds .claude/skills/deploy/SKILL.md. What happens when you type /deploy?
2. You add allowed-tools: Read to a skill's frontmatter. Claude calls Edit mid-skill. What happens?
allowed-tools only pre-approves Read.allowed-tools grants pre-approval, not restriction. Add Edit to permissions.deny if you actually want to block it.allowed-tools is pre-approval; it doesn't block other tools. To actually deny, use the permissions.deny array.3. Your skill needs the GitHub PR diff inlined into the prompt without a tool round-trip. What's the right syntax?
{{ gh pr diff }}!`gh pr diff` — inline shell injection$(gh pr diff)!`cmd` runs at render time and inlines stdout. For multi-line, use a fenced ```! block.!`cmd` is the syntax. The command runs before Claude sees the prompt.4. A skill should run a thorough codebase search but you don't want all the file contents flooding your main context. What do you set?
disable-model-invocation: trueallowed-tools: Readcontext: fork with agent: Explore~/.claude/skills/context: fork. The Explore subagent type is the right execution environment for read-only research.5. You want to manually clean up old branches via /cleanup-branches — not have Claude trigger it automatically. Which frontmatter field?
user-invocable: truedisable-model-invocation: truecontext: forkauto-invoke: falsedisable-model-invocation means only YOU can trigger via /cleanup-branches. Right move for anything with side effects (deploys, deletes, sends).disable-model-invocation: true — it stops Claude from auto-triggering, leaving manual /cleanup-branches as the only path.Module Summary
- Commands and skills merged. New work uses skill format; old
.claude/commands/*.mdstill works. - 4 scopes: Enterprise > Personal > Project. Plugins use
plugin:skillnamespace. - SKILL.md frontmatter has 15 fields; you'll use
description,allowed-tools,disable-model-invocation,context,argumentsmost often. - Substitution:
$ARGUMENTS,$N,$name,${CLAUDE_SESSION_ID},${CLAUDE_EFFORT},${CLAUDE_SKILL_DIR}. - Inline shell injection:
!`cmd`for one-liners,```! ... ```for blocks. Disable viadisableSkillShellExecution. context: fork+agent:for isolated research; inline for reference content.- Bundled:
/simplify,/batch,/debug,/loop,/claude-api. Plus/init,/review,/security-review. - Trigger extended thinking by including "ultrathink" anywhere in skill content.