Your own slash commands for Claude Code, productive in 30 minutes
How to equip Claude Code with your own /review, /test, /deploy commands. One markdown file per command, done. With frontmatter, arguments, file references and bash execution.
If you use Claude Code for longer than two weeks, you will end up typing the same prompts several times a day. "Review this file", "Write me a test for", "Deploy to staging". That is exactly what custom slash commands are for. One Markdown file per command, no plugin system, no build step. I will show you how it works and where the pitfalls are.
1. Understand what this is actually about
A slash command in Claude Code is nothing more than a Markdown file with YAML frontmatter and a prompt. When you type /review, Claude Code looks in the project under .claude/commands/review.md and in your home dir under ~/.claude/commands/review.md. If it finds the file, it reads it, replaces placeholders like $ARGUMENTS with whatever you typed after the command, and fires that off as a prompt.
That means a command is only as good as the prompt you put in it. No magic. But that is exactly what makes it so useful, because you version your best prompts instead of reinventing them over and over.
2. Create your first command
Go into your project and make the directory:
mkdir -p .claude/commands
Now put a file review.md in there. Minimal content:
---
description: Code-Review für den aktuellen File
---
Schau dir den letzten geaenderten File im Working Directory an.
Pruef auf:
- Logik-Fehler oder Edge-Cases die fehlen
- Schlechte Variablen-Namen
- Tests die fehlen sollten
Gib mir maximal 5 konkrete Punkte zurück. Keine Lobhudelei.
Save it. Restart Claude Code or just type /help, then /review (project) should show up in the list. The (project) means it comes from your repo, not from your user directory. If you now type /review, the prompt fires.
3. Feed in arguments
Static is boring. You want to say "review FILE X". For that there are two placeholders: $ARGUMENTS swallows everything you type after the command as a single string. $1, $2, $3 swallow individual tokens.
Example fix-issue.md:
---
description: Fix GitHub Issue per Nummer
argument-hint: [issue-number]
---
Fix Issue #$ARGUMENTS nach unseren Coding-Standards.
Schreib Tests dafür, dann commit auf einem feature-Branch.
Call: /fix-issue 247 and $ARGUMENTS becomes 247.
The argument-hint field is not cosmetic, it shows Claude Code to you when you type only /fix-issue and hit Tab. Always worth it.
4. Pull in files with @-syntax
If your command should analyze a file, you can embed the file content directly into the prompt. Syntax: @path/to/file. When you combine that with $1, it gets really useful.
security-check.md:
---
description: Security-Audit für einen File
argument-hint: [file-path]
allowed-tools: Read
---
Lies @$1 und pruef auf:
- Hard-coded Secrets oder API-Keys
- SQL-Injection-Patterns
- Unsanitized User-Input
Wenn du was findest, gib genaue Zeilen-Nummern aus.
Call: /security-check src/auth.ts. Claude reads the file and fires the prompt with the file content as context.
5. Run bash commands inline
This is the underrated trick. With !`command` you can run a shell command inside your command and embed the output into the prompt. That means dynamic context without Claude having to call tools first.
commit-message.md:
---
description: Commit-Message basierend auf staged changes
allowed-tools: Bash(git:*)
---
Hier sind die staged changes:
!`git diff --cached`
Hier ist der current branch:
!`git branch --show-current`
Schreib mir eine Commit-Message im Conventional-Commits-Format.
Eine Zeile Subject, max 72 Zeichen, dann Leerzeile, dann Body.
Important: the allowed-tools: Bash(git:*) field explicitly permits only git commands. Without this whitelisting, Claude asks for confirmation on every bash call. If you write Bash(*) everything goes, but that is dangerous, so better stay granular.
6. Personal vs project commands
Project commands live in .claude/commands/ in the repo. You commit those. Advantage: your team uses the same commands. Disadvantage: your /dailylog command that is really only interesting for you spills into every project.
Solution: personal commands. Those live in ~/.claude/commands/ and are available in every project. In /help they are marked as (user).
My setup: workflow commands like /review, /test, /deploy as project commands (team sharing). Personal ones like /note, /commit-msg, /explain-this as user commands.
7. Namespacing when it gets to be more
Past 15 commands the flat list gets messy. Solution: subdirectories. You create a command in .claude/commands/git/review.md, it shows up in /help as /review (project:git). With /git/review you access it explicitly.
.claude/commands/
├── git/
│ ├── review.md
│ └── commit.md
├── ci/
│ └── deploy.md
└── docs/
└── api.md
Start with this early if you plan to have more than 5-10 commands. Renaming later is effort and you break muscle memory.
8. Make commands actually useful
The difference between a gimmick and a daily driver is whether the command does something you would otherwise do by hand. Three patterns that hit hard for me:
Status snapshot. /status packs git status, npm test output, and the last 5 commits into one prompt and asks Claude what is next. Saves me 10 minutes of orientation in the morning.
Code to docs. /document $1 reads a file, generates JSDoc comments and writes them back. Boring, but once you have the command, you never do it by hand again.
Pre-commit sanity. /prepush runs git diff origin/main...HEAD, asks Claude whether everything belongs together sensibly or whether there are accidentally three features in the same PR.
The logic is always: what annoys you daily, takes 30 seconds to 3 minutes, and is uniform enough for a prompt. That is exactly what becomes a command.
9. The two pitfalls
Fallback chaos. When your command name collides with a built-in (e.g. /help), the built-in wins. When a project command and a user command have the same name, the project command wins. That means: before you drop /test in ~/.claude/commands/, check that the repo does not already define a /test.
Forgetting prompt engineering. Your command is a prompt. If the prompt is vague, the answer will be vague. Write acceptance criteria into it ("max 5 points", "no praise", "concrete line numbers"). Otherwise you get the usual Claude wall of text.
10. What is next
Once you have written your first three to five commands, you will notice that some of them really should be hooks. Hooks run automatically on events (PostToolUse, PreCompact), slash commands you fire manually. Both have their place.
If you want to learn more about hooks, look at the playbook Hooks gegen Halluzinationen. If you want to build slash commands for sub-agents, Erster Sub-Agent in 30 min is the follow-up step. And if you want to share your setup, consider turning it into a plugin, which is basically just a directory of commands plus a bit of manifest file.
My tip: create a single command today, /review.md, with a prompt that has already shown up twice in your chat history. That is small effort, immediate payoff, and you understand the whole system afterward.
Source
Specs verified against the /anthropics/claude-code repo, as of 2026-04-28:
plugins/plugin-dev/skills/command-development/SKILL.md(locations, frontmatter, arguments)plugins/plugin-dev/skills/command-development/README.md(file format, $ARGUMENTS, @-syntax, !-bash)plugins/plugin-dev/skills/command-development/references/plugin-features-reference.md(namespacing)
Official docs: https://docs.claude.com/en/docs/claude-code/slash-commands