Skip to main content

Mental Model

A slash command is not a function call. It is a prompt injection — markdown content that enters the conversation as a single message and stays there until the session ends or context compacts. Every architectural decision about commands flows from this fact.

The Full Lifecycle

When you type / in Claude Code, five stages execute in sequence.

1. Discovery

Claude Code scans all registered command locations and builds an autocomplete dropdown. The scan covers six sources simultaneously:

SourceLocationScopeAutocomplete Label
Project commands<repo>/.claude/commands/This repo(project)
Project skills<repo>/.claude/skills/*/SKILL.mdThis repo(project)
User commands~/.claude/commands/All projects(user)
User skills~/.claude/skills/*/SKILL.mdAll projects(user)
Plugin skillsInstalled pluginsPer config(plugin:name)
MCP promptsConnected MCP serversPer config/mcp__server__prompt

Typing characters after / filters the list. If a project command and user command share the same name, the project command wins. Plugin and MCP commands use namespaced prefixes to avoid collisions entirely.

2. Resolution

The filename minus .md becomes the command name. deploy.md creates /deploy. For skills in .claude/skills/name/SKILL.md, the directory name determines the command name unless a name field in frontmatter overrides it.

Name constraints: lowercase letters, numbers, hyphens only. Maximum 64 characters. No reserved words.

3. Argument Expansion

Three substitution mechanisms are available:

# $ARGUMENTS — everything after the command name
Fix the bug described in GitHub issue #$ARGUMENTS.
 
# Positional — $1, $2, etc.
Migrate the database schema from $1 to $2.
 
# Implicit append — if no placeholder exists, arguments go to the end

Usage: /fix-issue 123 replaces $ARGUMENTS with 123. /migrate v2 v3 replaces $1 with v2 and $2 with v3. If the file contains no $ARGUMENTS placeholder and you pass arguments, they get appended after the file content.

4. Context Injection

The rendered markdown enters the conversation as a single user message. This is the most important thing to understand about commands:

  • The content stays in context for the rest of the session. It is not re-read on later turns.
  • After /compact, the content gets summarized along with everything else. Specific variable names, edge cases, and nuanced constraints often do not survive compression.
  • The injection is a one-time event. If you edit the command file mid-session, the old content is already in context. The edit has no effect until the next session.

5. Execution

Claude reads the injected instructions and follows them using available tools. There is no special runtime. It is the same Claude, with the same tools, operating on new instructions in context. If allowed-tools frontmatter restricts tool access, those restrictions apply for the duration of the skill execution.

Command File Anatomy

A command file is markdown with optional YAML frontmatter:

---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
allowed-tools: Bash,Read,Grep
mode: false
---
 
# Deploy to Production
 
Run the full deployment pipeline for $ARGUMENTS (defaults to staging).
 
## Steps
 
1. Run `git status` — abort if uncommitted changes exist
2. Run `npm run build` — abort on failure
3. Deploy with `vercel --prod`

Frontmatter Fields

FieldTypeDefaultPurpose
namestringfilenameCommand name. Max 64 chars, lowercase/numbers/hyphens.
descriptionstringnoneHow Claude decides auto-invocation relevance. Max 1024 chars. Critical for skills, optional for commands.
disable-model-invocationbooleanfalseWhen true, only the user can trigger via /name. Required for destructive operations.
user-invocablebooleantrueWhen false, only Claude can invoke. For background knowledge skills only.
allowed-toolsstringall toolsComma-separated tool list. Supports patterns: Bash(git:*), Bash(npm run build:*).
modebooleanfalseWhen true, appears under "Mode Commands" in autocomplete. For behavior-changing commands.

The description field serves double duty. For skills that Claude auto-invokes, the description is the only signal Claude uses to decide relevance. For commands with disable-model-invocation: true, the description is informational — it shows in /help output and autocomplete tooltips.

Commands vs. Skills: The Invocation Axis

The core distinction is who triggers execution:

DimensionCommand (legacy)Skill
Location.claude/commands/name.md.claude/skills/name/SKILL.md
InvocationUser only (/name)User OR model (automatic)
StructureSingle markdown fileDirectory with supporting files
FrontmatterOptional, minimalFull YAML support
Supporting filesNoneScripts, templates, configs alongside SKILL.md

Both formats create the same /name slash command for user invocation. The commands directory is the legacy format. Skills are the current recommended approach for anything beyond simple prompt shortcuts.

Backward compatibility is maintained: .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md both create /deploy and work identically when the user types the command.

Decision framework:

  • Single markdown file, user-triggered only, no supporting assets: use a command
  • Needs templates, scripts, or reference docs alongside it: use a skill
  • Should auto-invoke based on task context: use a skill with a precise description
  • Destructive operation requiring human confirmation: use either, but set disable-model-invocation: true

Context Window Impact

This is the most under-appreciated aspect of commands.

At startup: Only skill names and descriptions load — roughly 100 tokens per skill. The full content of command files is NOT loaded until invocation. Twenty commands cost ~2,000 tokens of startup overhead total.

On invocation: The full file content enters context and stays there permanently. A 500-line command file consumes ~2,000 tokens on injection.

After compaction: /compact summarizes the conversation. Command instructions lose fidelity. The first 5 lines and last 5 lines of a command file are most reliably retained; middle content degrades first.

The competition: Claude Code's system prompt uses ~50 instruction slots out of roughly 150-200 that frontier models follow reliably. Your CLAUDE.md, loaded skills, and command content all compete for the remaining ~100-150 slots.

Rule of thumb: Keep command files under 200 lines. If you need more, split into multiple commands or instruct Claude to read external files on demand rather than inlining everything into the command body.

How Resolution Order Determines Precedence

When multiple sources define the same command name:

  1. Project commands override user commands
  2. Plugin commands use plugin:name prefix — no collision possible
  3. MCP prompts use mcp__server__prompt prefix — no collision possible
  4. Within the same scope, commands and skills with the same name conflict unpredictably — avoid this

Personal workflow customization lives in ~/.claude/commands/. Team-shared automation lives in <repo>/.claude/commands/. Project commands always win if names collide, giving teams control over the canonical behavior while individuals can extend with non-overlapping names.