The complete guide to Claude Code. Opus 4.7, Sonnet 4.6, Haiku 4.5. 1M token context window. 27 hook events. 43 production-tested chapters across 6 topical Parts (Foundation, Workflow, Extension, Context Engineering, Advanced, Reference). Three install tiers. CC 2.1.121+ compatible.
Short answer: slash commands still exist, but they are now just skills with user-invocable: true. If you’re writing a new one, read Skills Authoring. If you have legacy commands/*.md files from before Claude Code 2.1.88, read the migration section below.
Before CC 2.1.88, user-invocable slash commands lived in two directories:
~/.claude/commands/ # Global
.claude/commands/ # Project-scoped
Each was a single Markdown file with YAML frontmatter. In 2.1.88, Anthropic merged commands into skills — the commands/ directories were deprecated and any /<name> invocation that used to be a command is now a skill with user-invocable: true.
There is no longer a separate command type. Skills are the one primitive.
/skill-name
/skill-name argument text here
The first form runs the skill with empty arguments. The second passes everything after the name as $ARGUMENTS (see Skills Authoring → Arguments).
Skills installed via a plugin are addressed with the plugin prefix:
/plugin-name:skill-name
Example: the superpowers plugin exposes /superpowers:brainstorming, /superpowers:writing-plans, etc.
Several built-in commands are now rewired through the Skill tool:
| Slash | What it does |
|---|---|
/init |
Initialize a new CLAUDE.md for the project |
/review |
Review a pull request |
/security-review |
Security review of pending changes |
Plus the classic built-ins (not implemented as skills): /help, /clear, /cost, /config, /doctor, /model, /resume, /compact, /release-notes, and others. These are hard-wired into the CLI.
The short form — full details in Skills Authoring:
---
name: my-command
description: "Short action-verb description. Use when user says 'my command' or types /my-command."
user-invocable: true
argument-hint: "[optional argument description]"
---
# My Command
$ARGUMENTS
## Workflow
Steps to execute using the argument text above.
Key fields:
user-invocable: true — required to show in the / menuargument-hint — text displayed in the UI when the user types /my-command<space>$ARGUMENTS — expanded to whatever the user typed after the command nameTo prevent accidental automatic invocation (e.g., for deploy commands that must be explicit):
disable-model-invocation: true
This keeps the skill user-invocable via slash but prevents Claude from auto-triggering it based on conversation context.
commands/ FilesIf you have files in ~/.claude/commands/ or .claude/commands/ from before 2.1.88, migrate them to the skills layout:
~/.claude/commands/
deploy.md
ship-it.md
weekly-review.md
Each file had frontmatter and a body. The filename (minus .md) was the slash name.
~/.claude/skills/
deploy/
SKILL.md
ship-it/
SKILL.md
weekly-review/
SKILL.md
The directory name becomes the slash name. SKILL.md replaces the old command file.
# For each legacy command file:
OLD="deploy"
mkdir -p ~/.claude/skills/$OLD
mv ~/.claude/commands/$OLD.md ~/.claude/skills/$OLD/SKILL.md
Then edit SKILL.md and ensure the frontmatter has:
---
name: deploy # optional; directory name is used if omitted
description: "Deploy to production. Use when user says /deploy or 'deploy to prod'."
user-invocable: true
---
Important frontmatter hygiene during migration:
allowed_tools → allowed-tools (hyphenated, not underscored — the old form is silently ignored)["Read", "Bash"] → Read, Bashuser-invocable: true if the old command was invocable (most were, by default)description to include “Use when…” trigger textThen remove the empty commands/ directory (if nothing else remains in it):
rmdir ~/.claude/commands 2>/dev/null || true
| Problem | Cause | Fix |
|---|---|---|
Slash doesn’t appear in / menu |
Missing user-invocable: true |
Add the field |
| Tool restrictions ignored | Used allowed_tools (underscore) |
Rename to allowed-tools |
| Arguments not expanded | Legacy command used `` | Replace with $ARGUMENTS |
| Tool list parsed as empty | Used JSON array format | Use comma-separated string |
| Slash fires when unwanted | Model auto-invokes from context | Add disable-model-invocation: true |