Store recurring workflows as a Markdown file
A slash command in Claude Code is simply a Markdown file with frontmatter that bundles a recurring prompt, fixed arguments, and a tightly scoped tool access. Instead of retyping the same deploy or review prompt every time, the team invokes it through a short slash command, consistent and version controlled inside the project repository.
Table of Contents
- 1. What a slash command technically is
- 2. Structure: a Markdown file plus frontmatter
- 3. Project wide and personal slash commands
- 4. Using arguments and placeholders correctly
- 5. Combining bash execution with allowed tools
- 6. Namespacing for many team commands
- 7. Practical examples: deploy, review and tests
- 8. Debugging and common failure sources
- 9. Slash commands compared with alternatives
- 10. Summary
- 11. FAQ
1. What a slash command technically is
A slash command in Claude Code is essentially nothing more than a Markdown file at a fixed location. Type a command like /review-pr into the Claude Code CLI and Claude Code looks up a matching file, loads its content as a preformulated prompt, and runs it in the current context. There is no separate scripting language, no build step, and no hidden configuration database, just a plain text file that gets versioned in the Git repository like the rest of the code.
The practical benefit shows up immediately in daily development: recurring tasks such as a structured code review, a release readiness check, or a database migration dry run no longer need to be recast as a prompt each time. A slash command encapsulates the wording once, and the whole team then invokes the same, tested prompt afterward. That reduces variance between developers and makes Claude Code workflows traceable, because the actually executed prompt stays readable in the repository instead of existing only in one developer's terminal history.
2. Structure: a Markdown file plus frontmatter
Every slash command consists of two parts: an optional YAML frontmatter block at the top of the file and the actual prompt text below it. The frontmatter controls metadata such as a short description shown in autocompletion, plus an explicit list of allowed tools through the allowed-tools field. If the frontmatter is missing entirely, the whole file is interpreted as the prompt, which is entirely sufficient for simple commands.
The filename itself determines the invocation: review.md becomes /review, subdirectories become namespaces separated by a colon. This one to one mapping between the file system and command names makes the system predictable, anyone who knows a project's directory structure automatically knows its available slash commands too. The example below shows a minimal command for a structured summary of open pull requests.
# Directory layout for project-wide slash commands
mkdir -p .claude/commands
# Create a minimal slash command: /pr-summary
cat > .claude/commands/pr-summary.md << 'EOF'
---
description: Summarize all open pull requests with review status
allowed-tools: Bash(gh pr list:*), Bash(gh pr view:*)
---
List all open pull requests via `gh pr list --json number,title,author,reviewDecision`.
Group them by review status (approved, changes requested, pending) and flag any PR
open longer than 5 days as stale.
EOF
# Invoke it inside Claude Code
# /pr-summary
3. Project wide and personal slash commands
Claude Code distinguishes two storage locations for slash commands, and that distinction is more than mere organization. Commands under .claude/commands/ in the project directory get versioned with the repository and are automatically available to every team member as soon as they check out the project. Commands under ~/.claude/commands/ in the home directory are personal, cross project, and never shared with anyone else, ideal for individual habits such as a personal format for commit messages.
In practice a clear separation is worthwhile: anything that requires project knowledge, such as the exact deploy sequence or the project's test framework, belongs under .claude/commands/ and therefore in the repository. Anything project independent, such as a personal slash command for formatting notes or summarizing arbitrary text, belongs in the home directory. Anyone who uses both levels consistently avoids personal commands accidentally ending up in the team repository and causing confusion there.
# Project-wide: versioned with the repository, shared with the whole team
ls .claude/commands/
# pr-summary.md fix-issue.md release-check.md
# Personal: never shared, lives only on this machine
mkdir -p ~/.claude/commands
cat > ~/.claude/commands/summarize.md << 'EOF'
---
description: Summarize the given text into three concise bullet points
---
Summarize the following text into exactly three bullet points, no preamble:
$ARGUMENTS
EOF
4. Using arguments and placeholders correctly
Static prompts cover many cases, but true reusability only emerges with arguments. Claude Code provides the placeholder $ARGUMENTS for this, which gets replaced at invocation time with whatever was typed after the slash command name. When a developer runs /fix-issue 342, Claude Code substitutes 342 for $ARGUMENTS in the prompt text, and the command can then insert that value into a gh issue view call, for instance.
For finer grained control, positional placeholders $1, $2, and so on are available whenever multiple space separated arguments are passed. A slash command for creating a hotfix branch can interpret $1 as the ticket number and $2 as a short description this way, instead of manually parsing both values out of a single combined string. This separation makes more complex commands significantly more robust against typos and saves additional parsing logic inside the prompt text itself.
mkdir -p .claude/commands
# /fix-issue 342 -- fetches issue and drafts a fix plan
cat > .claude/commands/fix-issue.md << 'EOF'
---
description: Fetch a GitHub issue and draft a fix plan before touching code
argument-hint: <issue-number>
allowed-tools: Bash(gh issue view:*)
---
Fetch issue #$ARGUMENTS with `gh issue view $ARGUMENTS --json title,body,labels`.
Read the referenced files, then propose a fix plan with affected files and test
strategy before making any code changes. Wait for explicit approval.
EOF
# /new-hotfix TICKET-88 "fix null pointer in checkout"
cat > .claude/commands/new-hotfix.md << 'EOF'
---
description: Create a hotfix branch following the team naming convention
argument-hint: <ticket> <short-description>
allowed-tools: Bash(git checkout:*), Bash(git branch:*)
---
Create and check out a branch named hotfix/$1-$2 from the current default branch.
Ticket reference: $1. Description: $2.
EOF
5. Combining bash execution with allowed tools
A particularly powerful feature of slash commands is the ! prefix in front of a bash line inside the prompt text. If a line starts with !, Claude Code runs the command behind it before the actual prompt processing and injects its output directly into the context. A slash command can gather the current git status, the latest commits, or the content of a log file automatically this way, without Claude itself needing to first stage a separate tool call.
For this feature to not become a security risk, the frontmatter field allowed-tools must precisely define which bash commands are permitted. Instead of blanket allowing every bash invocation, one specifies patterns such as Bash(git status:*) or Bash(npm test:*), so that a slash command meant for testing can never accidentally run a destructive rm -rf command. This combination of automatic context enrichment and tightly scoped tool access makes bash backed commands both productive and safe enough for team use.
mkdir -p .claude/commands
# /release-check -- gathers repo state before a release, no code changes allowed
cat > .claude/commands/release-check.md << 'EOF'
---
description: Gather pre-release repo state and flag risks
allowed-tools: Bash(git log:*), Bash(git status:*), Bash(npm test:*)
---
Current branch and status:
!`git status --short --branch`
Commits since last tag:
!`git log $(git describe --tags --abbrev=0)..HEAD --oneline`
Test suite result:
!`npm test -- --silent`
Based on the above, list open risks before this release goes out. Do not modify
any files, this command is read-only.
EOF
6. Namespacing for many team commands
As soon as a team maintains more than a handful of slash commands, a flat layout in a single directory quickly becomes hard to navigate. Claude Code solves this through subdirectories that automatically become a namespace separated by a colon: a file under .claude/commands/db/migrate.md becomes /db:migrate, a file under .claude/commands/frontend/build.md becomes /frontend:build. This structure often mirrors a project's team split and makes it immediately clear which area a slash command belongs to.
For larger projects a convention based on area of responsibility rather than technology is worthwhile: backend/, frontend/, ops/, and docs/ as the top level, with concrete commands underneath. Anyone creating a new command should briefly check whether an existing namespace fits before opening a new one, because too many flat top level namespaces cancel out the organizational benefit again. The description inside the frontmatter remains the most important orientation aid, it shows up in autocompletion and should explain in one sentence what the slash command does and what it does not.
7. Practical examples: deploy, review and tests
In daily practice three categories of slash commands come up especially often. The first is the review command, which takes a structured look at a diff: security gaps, missing tests, style violations, each with a clear priority. The second category is deploy and release commands, which map out a fixed sequence of checks, build, and sign off, as shown in the previous section. The third category is test commands that, for instance, target only the test files affected by the latest changes instead of running the entire suite for every small fix.
A fourth, often underrated use case is onboarding: a slash command named /explain-module that accepts any directory as an argument and returns a structured explanation of the architecture, the most important classes, and test coverage. New team members use exactly this command noticeably more often in the first weeks than experienced developers, which makes it a measurable lever for faster ramp up, without anyone needing to personally invest time in a code walkthrough.
8. Debugging and common failure sources
The most common mistake with custom slash commands is an overly broad allowed-tools pattern. Anyone using Bash(*) instead of a specific pattern like Bash(npm test:*) effectively defeats the safety feature and allows the command to run any arbitrary bash command, which becomes especially risky for commands with embedded ! prefixed calls. A second common mistake is a missing argument-hint field: without this hint in autocompletion, team members often do not know in which order and shape arguments are expected.
A third mistake concerns expectations around $ARGUMENTS versus $1/$2: if the prompt text uses $1 but the invocation contains only a single word with no space as a separator, $2 stays empty and the command behaves unexpectedly. The simplest approach is to first test a new slash command with clearly recognizable test values like test-arg-1 and test-arg-2, to immediately see whether the placeholders are substituted correctly and in the right order before the command goes into productive team use.
# Safe way to test a new slash command before rolling it out to the team
# /new-hotfix test-arg-1 test-arg-2
# Expected: branch name becomes hotfix/test-arg-1-test-arg-2
# If $2 stays empty, the call was likely missing a space separator, e.g.:
# /new-hotfix "test-arg-1 test-arg-2" -- WRONG, treated as a single $1
git branch --list "hotfix/test-arg-1*" # verify the branch name matches
9. Slash commands compared with alternatives
Not every recurring task necessarily belongs in a slash command. Depending on complexity and audience, more suitable alternatives exist, and their strengths and weaknesses can be compared directly.
| Approach | Strength | Weakness | Use case |
|---|---|---|---|
| Slash command | Versioned, team wide, takes arguments | Only usable inside Claude Code | Recurring prompt workflows |
| CLAUDE.md rule | Always active, no invocation needed | Cannot be parameterized | Permanent project conventions |
| Bash script | Deterministic, no LLM needed | No language understanding | Fixed steps needing no interpretation |
| Ad hoc prompt | Available instantly, no setup | Not reusable, inconsistent | One off edge cases |
The rule of thumb is: as soon as a prompt gets typed nearly identically for the second or third time, investing in a slash command pays off. Fixed, purely deterministic steps with no room for interpretation belong more in a classic bash script, while permanent behavior rules that should always apply are better placed in CLAUDE.md instead of a command that must be explicitly invoked.
Mironsoft
Claude Code setup, workflow automation and Magento/Hyva development with AI
Building Claude Code workflows for your team?
We set up custom slash commands, hooks, and team conventions for Claude Code, tailored to your deploy sequence, your test framework, and your code review standards.
Command library
Building project wide slash commands for review, deploy and tests
Security model
Scoping allowed-tools patterns cleanly instead of blanket permissions
Team onboarding
Getting new developers productive faster with explanatory commands
10. Summary
Custom slash commands in Claude Code are Markdown files with optional frontmatter that bundle recurring prompts, arguments, and a tightly scoped tool access. Project wide commands under .claude/commands/ get versioned with the repository and are available to the whole team, personal commands under ~/.claude/commands/ stay individual. Placeholders like $ARGUMENTS, $1, and $2 make commands parameterizable, the ! prefix allows automatic bash execution before prompt processing.
Security comes from precise allowed-tools patterns instead of blanket grants, structure from sensible namespacing by area of responsibility rather than technology. Anyone who versions review, deploy, and test workflows as slash commands reduces variance across the team and makes Claude Code usage traceable, instead of leaving it dependent on individual developers' personal prompting habits.
Custom Slash Commands in Claude Code — The Essentials at a Glance
Structure
A Markdown file under .claude/commands/ with optional YAML frontmatter for description and tool access.
Storage locations
Project wide under .claude/commands/, versioned; personal under ~/.claude/commands/ in the home directory.
Arguments
$ARGUMENTS for the entire invocation, $1/$2 for individual, space separated values.
Security
Precise allowed-tools patterns like Bash(npm test:*) instead of a blanket Bash(*) grant.