In the previous post I described the full setup: 120 files, 18 agents, 22 rules, 11 hooks. One piece I glossed over was the 51 custom slash commands. They’re the most visible part of the workbench, what I actually type day to day. This post covers how they work. The follow-up posts will go through each category.

What a command file is

A command is a markdown file in .claude/commands/. When you type /deploy in Claude Code, it finds commands/deploy.md, reads it, and treats it as your instruction for the session. The file name is the command name. That’s the whole mechanism.

There’s no registration step, no config file to update, no restart required. Drop a markdown file in the directory and the command exists.

The simplest possible command is just instructions:

1
2
3
4
5
# /summarize

Summarize the following content in 3 bullet points. Be specific. No filler.

Content: $ARGUMENTS

$ARGUMENTS is the one special variable. It gets replaced with whatever you type after the command name. Run /summarize https://example.com/article and Claude reads the file with the URL substituted in.

If the command takes no input, you don’t need $ARGUMENTS at all. /morning runs a full system briefing with no arguments.

Frontmatter

Commands support optional YAML frontmatter with three fields:

1
2
3
4
5
---
allowed-tools: Bash(curl:*)
argument-hint: [description]
description: Start a Toggl timer
---

allowed-tools restricts which tools the command can use. The Toggl commands only need curl, so they’re locked to Bash(curl:*). This is a safety measure and also a signal to Claude about what’s in scope for the command.

description shows up in the command picker when you type /. It’s the one-line label next to the command name.

argument-hint shows a hint in the picker after the command name, like a function signature. /toggl:start [description] tells me what to pass without opening the file.

None of these are required. Most of my commands skip frontmatter entirely and rely on the instructions alone.

Directory structure and namespacing

Subdirectories create namespaced commands. A file at commands/toggl/start.md becomes /toggl:start, not /start. A file at commands/devops/deploy.md becomes /devops:deploy.

My structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.claude/commands/
  analysis/     # 5 commands
  code/         # 3 commands
  content/      # 6 commands
  devops/       # 11 commands
  docs/         # 3 commands
  media/        # 6 commands
  productivity/ # 3 commands
  search/       # 2 commands
  toggl/        # 8 commands
  comms/        # 1 command

With 51 commands, namespacing keeps the picker readable. /toggl:start, /toggl:stop, /toggl:current, and /toggl:summary are obviously related. /deploy and /devops:deploy would be ambiguous. The colon makes the grouping visible at the call site.

You can also put a README.md inside a subdirectory. It won’t become a command, it’s just documentation that lives next to the files it describes. The toggl/ directory has one explaining the API token setup.

The pattern inside a command

The command files that actually save time share a structure. Steps, not prose. Specific tool calls, not general requests. A confirmation gate before anything destructive.

Here’s the skeleton:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# /my-command

Brief description of what this does.

## Steps

1. Gather data (run in parallel if possible):
   - specific tool or bash command to run
   - another specific tool or bash command

2. Present findings. Ask for confirmation before proceeding.

3. Do the thing (only after user confirms):
   ```bash
   actual command here
  1. Verify the result.
1
2
3
4
5
6
7
8

The parallel execution instruction is real. Claude runs multiple checks at the same time when the file says to. The confirmation gate is real too. "Do NOT proceed without explicit user approval" is not decoration, it holds.

The tool references matter. Writing `mcp__trigger__list_deploys with environment: "prod"` is better than "check recent deployments." The vague version works until it doesn't. Specific tool names give consistent behavior across sessions. I show real examples of this in the [devops commands post](/posts/claude-code-devops-commands/).

## Reasoning strategies

Most commands include a strategy flag:

Usage: /deploy [–strategy ] Default strategy: standard.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

The strategy controls how Claude approaches the task. `reflexion` means draft, self-critique, fix, good for text output. `cot` (chain-of-thought) is better for diagnosis and debugging. `standard` is fine for anything operational.

You don't need this for simple commands. But for anything where output quality matters, baking in the right strategy means you get consistent results without remembering to ask for it each time. `/humanize` always uses `reflexion`. `/review-pr` always uses `self-refine`. I don't think about it anymore.

## Referencing rules instead of repeating them

One more pattern worth knowing: commands can reference other files instead of duplicating logic.

The `/humanize` command doesn't list all 40 banned words and constructions inline. It says:

```markdown
Apply humanizer rules from `.claude/rules/humanizer.md`.

Claude reads that file as part of running the command. If I update the rules, every command that references them picks up the change. This keeps the command files short and the rules in one place.

The same works for agent definitions. /review-pr tells Claude to spawn parallel Explore agents for large pull requests. The agents are defined separately in .claude/agents/. The command just calls for them by name.

What’s next

That’s the mechanics. File goes in directory, name becomes command, $ARGUMENTS carries user input, frontmatter adds optional constraints, and the file body is just instructions.

The interesting part is what you put in the files. Next up: the 11 devops commands that replaced my runbooks.