Home / Claude Code subagents + hooks
Claude Code subagents, hooks and slash commands: a working setup
CLAUDE.md tells Claude Code what to do. Subagents, hooks and slash commands are how you make it actually happen: focused reviewers with limited tools, shell scripts that enforce your rules instead of hoping the model remembers them, and one-word commands for your workflow.
Subagents: .claude/agents/*.md
A subagent is a Markdown file with YAML frontmatter: name, a description that tells Claude when to delegate (write “Use PROACTIVELY after…” to get it used automatically), and an optional tools list. Each subagent runs in its own fresh context, so it does not clutter the main conversation, and it does not see that conversation either: pass it the exact files and goal.
- Least privilege: a reviewer or security auditor gets
Read, Grep, Globonly. If it cannot edit, it cannot “fix” things you did not ask for. - One job each: planner, code-reviewer, test-writer, debugger, security-auditor, docs-writer covers most teams.
- Output contracts: “Report MUST / SHOULD / NIT with path:line” beats “review the code”.
Run /agents in Claude Code to list, create and edit them.
Hooks: rules the model cannot skip
Hooks live in .claude/settings.json under "hooks". Each event (PreToolUse, PostToolUse, Stop, and others) has a matcher on the tool name (Bash, Edit|Write) and a command. The hook gets the tool call as JSON on stdin. Exit code 2 blocks the action and sends your stderr message back to Claude; exit 0 allows it.
- PreToolUse on Bash: block
rm -rf,git push --force,git reset --hard,curl | sh, pushes to main. - PreToolUse on Read|Edit|Write: refuse
.env,*.pemand lockfiles, while still allowing.env.example. - PostToolUse on Edit|Write: run your formatter on the edited file. Never block here.
- Stop: run tests when code changed; exit 2 keeps Claude working. Check
stop_hook_activeto avoid loops.
Hooks run with your user's permissions. Read every script before enabling it and keep them in version control. Pair them with permissions.deny entries in the same file for a second layer.
Slash commands: .claude/commands/*.md
Each file becomes /filename. Use $ARGUMENTS for what you type after the command, and frontmatter such as description, argument-hint and allowed-tools. Good commands chain your subagents: /feature = plan → implement → test-writer → tests → code-reviewer → summary.
Free snippet: copy it
#!/usr/bin/env bash
# PreToolUse (matcher: Bash). Exit 2 = block; stderr is shown to Claude.
cmd=$(jq -r '.tool_input.command // ""')
deny=(
'rm -rf /' 'rm -rf ~' 'rm -rf \*' 'rm -rf \.' 'git push --force' 'git push -f' 'git reset --hard'
'git clean -fd' 'DROP DATABASE' 'DROP TABLE' 'TRUNCATE ' 'supabase db reset' 'prisma migrate reset'
'alembic downgrade base' 'chmod -R 777' 'mkfs' 'dd if=' ':(){'
)
for p in "${deny[@]}"; do
if printf '%s' "$cmd" | grep -qiF -- "${p//\\/}"; then
echo "Blocked by .claude/hooks/block-dangerous-bash.sh: '$p' is destructive. Explain why it's needed and ask the human to run it." >&2
exit 2
fi
done
if printf '%s' "$cmd" | grep -qE '(curl|wget)[^|]*\|\s*(ba|z)?sh'; then
echo "Blocked: piping a download into a shell. Download, inspect, then ask." >&2; exit 2
fi
if printf '%s' "$cmd" | grep -qE 'git push[^&;|]*\b(main|master)\b'; then
echo "Blocked: pushing to main/master. Push a branch and open a PR." >&2; exit 2
fi
exit 0
FAQ
Where do Claude Code subagents live?
Project subagents go in .claude/agents/ as Markdown files with YAML frontmatter (name, description, optional tools). Personal ones go in ~/.claude/agents/. Use /agents to manage them.
How do I block dangerous commands in Claude Code?
Add a PreToolUse hook with matcher Bash in .claude/settings.json. The script reads the tool call JSON from stdin and exits with code 2 to block it; its stderr is shown to Claude. Add matching permissions.deny rules as a second layer.
What does exit code 2 mean in a Claude Code hook?
Exit 2 blocks the action (for PreToolUse) or tells Claude to keep working (for Stop), and stderr is fed back to Claude. Exit 0 allows. Other codes are non-blocking errors shown to you.
How do I make Claude Code run tests before it finishes?
Use a Stop hook that runs your test command when code changed and exits 2 on failure. Check the stop_hook_active field in the input and exit 0 when it is true, so the hook cannot loop forever.
Do subagents see the main conversation?
No. Each subagent starts with a fresh context window and only gets the task you hand it, so include file paths and the goal in the delegation.
Want the whole drawer?
The Claude Code Kit ($24) ships a fill-in CLAUDE.md, 6 least-privilege subagents, 10 slash commands, a settings.json with permissions and hook wiring, and 4 tested hooks (dangerous Bash, secret files, lockfiles, format, tests-before-stop).