Subagents and Hooks
Subagents and hooks turn Claude Code from an interactive tool into a system. Subagents isolate work so your main context stays clean. Hooks enforce checks deterministically, whether or not anyone remembers to ask.
What a subagent is
A subagent is a separate Claude instance with its own context window, system prompt, tool permissions, and optionally its own model. The main session hands it a task. It works independently and returns a summary.
The benefit is context economics. A subagent can read thirty files and run a dozen searches, and your main session receives only the conclusion. Claude Code also ships built-in helpers, such as an Explore agent for read-only codebase search, and will delegate to them on its own.
Defining a custom subagent
Subagents are markdown files with YAML frontmatter in .claude/agents/ (project) or ~/.claude/agents/ (personal). The /agents command walks you through creating one.
---
name: code-reviewer
description: Senior code reviewer. Use proactively after any non-trivial
change, before commits, and whenever the user asks for a review.
tools: Read, Grep, Glob, Bash
model: opus
---
You are a senior engineer reviewing a change you did not write.
1. Run `git diff` to see the change. Read surrounding code for context.
2. Check correctness first, then error handling, then tests, then readability.
3. Verify that tests assert behavior, not just that code runs.
4. Report findings as Blocking, Should fix, or Nit, with file and line.
5. If the change is good, say so briefly. Do not invent problems.
Four design decisions matter:
- Description. Like skills, it decides when Claude delegates. "Use proactively" phrasing increases automatic use.
- Tools. Grant only what the job needs. A reviewer needs read access and maybe Bash for running tests. It does not need Edit.
- Model. Use the strongest model for judgment-heavy work like review and security. Use a faster model for mechanical work like searching or summarizing logs.
- Fresh eyes. A reviewer subagent has not seen the reasoning that produced the code. That is exactly why it catches what the author missed.
Invoking subagents
Claude delegates automatically when a description matches. You can also be explicit: "Have the code-reviewer agent review the last commit" or "Use subagents to investigate the three failing services in parallel." Several subagents can run at the same time.
The specialist debate
Some experienced engineers argue against many custom subagents. Their point: a python-tests subagent hides testing knowledge from the main agent, which then cannot reason about a change as a whole. They prefer putting knowledge in CLAUDE.md and letting the main agent spawn general-purpose copies of itself when it wants to parallelize.
Both views are right in different places. My rule:
- Custom subagent when the job needs a distinct perspective or restricted permissions: security review, code review, accessibility audit, performance profiling.
- General delegation for everything else: research, parallel exploration, bulk edits. Let the main agent decide how to split the work.
What a hook is
A hook is a shell command that Claude Code runs automatically at a lifecycle event. Hooks are deterministic. A rule in CLAUDE.md is followed most of the time; a hook runs every time.
| Event | Fires when | Common use |
|---|---|---|
SessionStart |
A session starts or resumes | Inject today's date, branch, open tickets |
UserPromptSubmit |
You send a prompt | Add context, block prompts containing secrets |
PreToolUse |
Before a tool runs | Block dangerous commands or protected files |
PostToolUse |
After a tool succeeds | Format, lint, typecheck edited files |
Notification |
Claude needs your attention | Desktop or chat notification |
Stop |
Claude finishes responding | Run tests, verify build |
SubagentStop |
A subagent finishes | Validate subagent output |
PreCompact |
Before compaction | Save a snapshot of state |
SessionEnd |
Session ends | Log usage, clean up |
How hooks receive data
A hook receives a JSON object on standard input describing the event: the tool name, its input (such as the file path being edited), and session details. Its exit code tells Claude Code what to do next. Exit code 0 means proceed. Exit code 2 means block, and whatever the hook prints to standard error is fed back to Claude so it can correct course.
That second behavior is powerful. A typecheck hook that exits 2 with the compiler errors makes Claude fix its own type errors before moving on.
A formatter and typecheck hook
In .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-and-check.sh"
}
]
}
]
}
}
And the script:
#!/usr/bin/env bash
# .claude/hooks/format-and-check.sh
set -euo pipefail
file=$(jq -r '.tool_input.file_path // empty')
[[ -z "$file" ]] && exit 0
case "$file" in
*.ts|*.tsx)
npx prettier --write "$file" >/dev/null 2>&1 || true
if ! out=$(npx tsc --noEmit -p . 2>&1); then
echo "$out" | head -40 >&2
exit 2
fi
;;
esac
exit 0
Note the head -40. Hook output that goes back to Claude consumes context, so trim it.
A guard hook
This PreToolUse hook blocks edits to protected paths:
#!/usr/bin/env bash
# .claude/hooks/protect-paths.sh
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
*.env*|*/migrations/*|*/generated/*)
echo "Blocked: $file is protected. Ask the user first." >&2
exit 2 ;;
esac
exit 0
Register it with "matcher": "Edit|Write" under PreToolUse.
Cost and debugging
Hooks that run on every edit add latency and, if they print a lot, context. Run the expensive checks, such as the full test suite, on Stop rather than on every PostToolUse. Run cheap ones, such as formatting a single file, per edit.
When a hook misbehaves, start Claude with claude --debug to see each hook's execution and output.
Pitfall: Hooks run with your user's full permissions. Treat hook scripts from plugins or other repositories as code you are about to execute, because you are.
What to do this week
- Add a
PostToolUseformatter hook for your main language. - Create a
code-reviewersubagent with read-only tools and run it on your next change. - Add one
PreToolUseguard for files that should never be edited without asking.
Key takeaways
- Subagents work in their own context window and return only a summary.
- Create custom subagents for distinct perspectives or restricted permissions, such as code and security review.
- Hooks run every time; exit code 2 blocks the action and feeds stderr back to Claude.
- Run cheap checks per edit and expensive ones on Stop, and keep hook output short.