Claude Code Hooks, Commands, Skills, and Subagents: The Complete Guide
Most teams use Claude Code reactively — they type a prompt, Claude responds, they type another. That is fine, but it leaves significant value on the table. Claude Code has four automation layers that let you turn it from a reactive assistant into an active workflow participant.
Consider the core problem each layer solves: you tell Claude never to touch a file, it touches it anyway. You ask it to skip secrets, it commits an API key. CLAUDE.md instructions are hopes — they are advisory, and Claude applies judgement. Hooks turn hopes into guarantees. And when you need parallel deep expertise instead of a single opinion, subagents let you spin up a team.
| Layer | What it does | When to reach for it |
|---|---|---|
| Hooks | Shell or HTTP calls that fire on lifecycle events | “This must happen every time, without exception” |
| Custom Commands | Reusable slash commands for repeatable prompts | “I type the same prompt repeatedly” |
| Skills | Context-aware instructions Claude loads automatically | “Claude should always do X when working on Y” |
| Subagents | Separate Claude instances for isolated, parallel work | “This task is noisy and the main session only needs a summary” |
This post covers how to create each one and when to use them.
Hooks: Deterministic Side Effects
A hook is a shell command or HTTP call that fires automatically when Claude Code reaches a specific lifecycle point. Unlike CLAUDE.md instructions (which are advisory), hooks are deterministic — they always run, regardless of what Claude decides to do.
flowchart TD
SS[SessionStart] --> Work[Developer works]
Work --> PTU[PreToolUse\nruns before each tool]
PTU -->|exit 2 = block| Block[Action blocked]
PTU --> Tool[Tool executes\nBash, Edit, Write, etc.]
Tool --> PTUS[PostToolUse\nruns after tool succeeds]
Tool --> PTUF[PostToolUseFailure\nruns after tool fails]
PTUS --> Work
PTUF --> Work
Work --> SE[SessionEnd]
Hook lifecycle events
All hooks for the same event run in parallel. If multiple hook entries contain the same command, Claude Code automatically deduplicates — it runs only once.
| Event | When it fires | Common use |
|---|---|---|
SessionStart | Once at startup | Load context, warm caches, load .envrc |
UserPromptSubmit | When you submit a prompt, before Claude processes it | Input sanitisation, logging |
PreToolUse | Before every tool call (can block the tool) | Block dangerous commands, dry-run checks |
PermissionRequest | When Claude needs your approval | Custom approval workflows |
PostToolUse | After every successful tool call | Run tests, format, log |
PostToolUseFailure | After every failed tool call | Alert on errors, cleanup |
Notification | When Claude Code sends a notification | Relay to Slack, mobile push |
SubagentStart | When a subagent is created | Log subagent activity |
SubagentStop | When a subagent completes | Aggregate results |
Stop | When Claude finishes generating its response | Notify user, trigger follow-up |
TeammateIdle | When an agent team member becomes idle | Reassign work |
TaskCompleted | When a task is marked completed | Trigger downstream steps |
ConfigChange | When a config file is modified during the session | Reload settings |
WorktreeCreate | When a Git worktree is created | Initialise worktree-specific config |
WorktreeRemove | When a worktree is removed | Cleanup |
PreCompact | Before Claude performs context compaction | Save current state |
SessionEnd | Once on exit | Summarise session, commit artifacts |
Creating hooks interactively: type /hooks in any session to add, edit, or delete hooks through a menu — no JSON editing required.
Hook types
There are four handler types:
{ "type": "command", "command": ".claude/hooks/script.sh" }
{ "type": "http", "url": "https://internal.api/hook", "method": "POST" }
{ "type": "prompt", "prompt": "Summarise what just changed" }
{ "type": "agent", "agent": "code-reviewer" }
Command hooks are the most common — a shell script that receives JSON on stdin and returns JSON on stdout.
HTTP hooks call a URL instead of a local script — useful for sending events to an audit log, a Slack webhook, or an internal policy service.
Prompt hooks feed the event data back to Claude as a follow-up prompt — rarely needed but useful for logging natural-language summaries.
Agent hooks invoke a named subagent (see Subagents below) in response to a lifecycle event.
How to create hooks
Hooks live in .claude/settings.json (project-wide) or ~/.claude/settings.json (your personal config):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/on-edit.sh"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/pre-bash.sh"
}
]
}
]
}
}
The matcher field filters by tool name: "Edit", "Bash", "Write", "*" (all tools). Each hook script receives a JSON payload on stdin:
{
"tool_name": "Edit",
"tool_input": { "path": "src/api.ts", "old_string": "...", "new_string": "..." },
"cwd": "/Users/dev/my-project",
"session_id": "abc123",
"duration_ms": 120
}
Exit codes matter in PreToolUse:
- Exit
0— allow the action - Exit
2— block the action (Claude sees the reason you print to stdout) - Any other non-zero — hook error, action proceeds anyway
When to use hooks
Hooks are for enforcement and side effects that must happen without exception:
- Auto-running tests after every edit
- Blocking destructive commands before they execute
- Enforcing formatting standards before code is saved
- Logging every tool call to an audit trail
- Sending notifications when long tasks finish
If you find yourself writing “always run X after Y” in your CLAUDE.md, it probably belongs in a hook instead — hooks guarantee execution, CLAUDE.md only requests it.
Example: Scan for secrets before any file is written
This is the most important hook to add first. It runs before Claude writes any file and blocks the action if secrets or oversized files are detected — before they can be committed:
#!/bin/bash
# .claude/hooks/pre-write-scan.sh
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.path // ""')
content=$(echo "$input" | jq -r '.tool_input.content // ""')
# Block if secrets detected in content
SECRET_PATTERNS='(api[_-]?key|secret|password|token|credential)\s*[=:]\s*["\x27][A-Za-z0-9+/=]{8,}'
if echo "$content" | grep -qiE "$SECRET_PATTERNS"; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Secret pattern detected in file content. Remove credentials before writing."
}
}'
exit 0
fi
# Block writes to files above 1MB (likely a mistake)
if [ -n "$content" ] && [ "$(echo "$content" | wc -c)" -gt 1048576 ]; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "File content exceeds 1MB. Review before writing."
}
}'
exit 0
fi
exit 0
Wire it up for all Write and Edit operations:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [{ "type": "command", "command": ".claude/hooks/pre-write-scan.sh" }]
},
{
"matcher": "Edit",
"hooks": [{ "type": "command", "command": ".claude/hooks/pre-write-scan.sh" }]
}
]
}
}
Example: Shield critical files from all edits
A shell script that physically prevents Claude from touching specific files — regardless of what it was asked to do:
#!/bin/bash
# .claude/hooks/shield-files.sh
input=$(cat)
tool=$(echo "$input" | jq -r '.tool_name // ""')
file=$(echo "$input" | jq -r '.tool_input.path // ""')
# Files Claude must never touch
SHIELDED_FILES=(
".env"
".env.production"
"config/secrets.yml"
"infra/terraform.tfvars"
"k8s/secrets/"
)
for shield in "${SHIELDED_FILES[@]}"; do
if [[ "$file" == *"$shield"* ]]; then
jq -n --arg f "$file" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: ("Shielded file: " + $f + " is protected from AI edits. Edit manually.")
}
}'
exit 0
fi
done
exit 0
Example: Auto-run tests after every edit
Every time Claude edits a file, tests run automatically. Claude sees the results immediately and can fix breaking changes without prompting.
#!/bin/bash
# .claude/hooks/on-edit.sh
input=$(cat)
CWD=$(echo "$input" | jq -r '.cwd // "."')
cd "$CWD"
if [ -f "package.json" ]; then
npm test --silent 2>&1 | tail -20
elif [ -f "go.mod" ]; then
go test ./... 2>&1 | tail -20
elif [ -f "requirements.txt" ]; then
python -m pytest --tb=short -q 2>&1 | tail -20
fi
exit 0
Example: Block destructive Bash commands
#!/bin/bash
# .claude/hooks/pre-bash.sh
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command // ""')
DANGEROUS="rm -rf|kubectl delete|terraform destroy|DROP TABLE"
if echo "$cmd" | grep -qiE "$DANGEROUS"; then
jq -n --arg cmd "$cmd" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Blocked: destructive command. Run manually after review."
}
}'
exit 0
fi
exit 0
Example: Auto-format on save
#!/bin/bash
# .claude/hooks/format.sh
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.path // ""')
[ -z "$file" ] && exit 0
case "$file" in
*.ts|*.tsx|*.js|*.jsx) npx prettier --write "$file" 2>/dev/null ;;
*.py) black "$file" 2>/dev/null ;;
*.go) gofmt -w "$file" 2>/dev/null ;;
*.tf) terraform fmt "$file" 2>/dev/null ;;
esac
exit 0
Example: Sanitise tool output before Claude reads it
PostToolUse hooks can replace a tool’s output using hookSpecificOutput.updatedToolOutput before Claude processes it. Use this to strip secrets from command output:
#!/bin/bash
# .claude/hooks/sanitise-output.sh
input=$(cat)
tool=$(echo "$input" | jq -r '.tool_name // ""')
if [ "$tool" = "Bash" ]; then
raw_output=$(echo "$input" | jq -r '.tool_result // ""')
cleaned=$(echo "$raw_output" | sed 's/[A-Za-z0-9_-]\{20,\}/[REDACTED]/g')
jq -n --arg out "$cleaned" '{
hookSpecificOutput: {
updatedToolOutput: $out
}
}'
exit 0
fi
exit 0
Example: Desktop notifications for long-running tools
Hooks can emit desktop notifications using terminalSequence — no controlling terminal required:
#!/bin/bash
# .claude/hooks/notify.sh
input=$(cat)
tool=$(echo "$input" | jq -r '.tool_name // ""')
duration=$(echo "$input" | jq -r '.duration_ms // 0')
if [ "$duration" -gt 10000 ]; then
jq -n --arg tool "$tool" --arg ms "$duration" '{
hookSpecificOutput: {
terminalSequence: "",
desktopNotification: {
title: "Claude Code",
body: ($tool + " completed in " + $ms + "ms")
}
}
}'
fi
exit 0
Example: HTTP hook to audit log
Send every tool call to an internal audit service:
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "http",
"url": "https://audit.internal/claude-tool-log",
"method": "POST",
"headers": {
"Authorization": "Bearer ${AUDIT_TOKEN}"
}
}
]
}
]
}
}
Custom Slash Commands: Reusable Prompts
Note: Custom commands (
.claude/commands/) are the legacy approach and are now superseded by Skills. Commands still work for backward compatibility, but for new work, use Skills instead — they support auto-discovery, supporting files (scripts/,references/,assets/), and more frontmatter options. Migrate a command to a skill when it becomes complex enough to warrant supporting files or automatic invocation.
Custom slash commands let you save prompt templates as files and invoke them with /command-name [arguments]. They are the simplest form of automation in Claude Code — no code, just a markdown file.
How to create custom commands
Create a markdown file in .claude/commands/ (project) or ~/.claude/commands/ (personal):
your-project/
└── .claude/
└── commands/
├── security-audit.md
├── write-tests.md
└── pr-summary.md
The file content becomes the prompt. Use $ARGUMENTS to pass arguments from the command line:
<!-- .claude/commands/security-audit.md -->
Review $ARGUMENTS for security vulnerabilities.
Trace data flows end-to-end across related files — not just the file provided.
Check for:
- Injection risks: SQL, command injection, path traversal
- Authentication and authorisation gaps
- Sensitive data exposure in logs or responses
- Business logic flaws specific to this application
- Insecure Direct Object References
Rate each finding: critical / high / medium / low
Include: file path, line number, description, and why it is exploitable.
Do not make any code changes — report only.
Then run it:
/project:security-audit src/api/payments/
$ARGUMENTS is replaced by everything you type after the command name.
More command examples
Write tests for a component:
<!-- .claude/commands/write-tests.md -->
Write comprehensive tests for $ARGUMENTS.
Cover:
- Happy path with realistic inputs
- Edge cases: empty input, boundary values, nulls
- Error cases: invalid input, missing dependencies
Use the same test framework as the existing tests in this project.
Do not modify the source file.
Generate a PR description:
<!-- .claude/commands/pr-summary.md -->
Generate a pull request description for the current branch.
Run: git diff main...HEAD --stat
Then: git log main...HEAD --oneline
Write:
1. A one-line title (imperative, under 70 chars)
2. A 3-bullet summary of what changed and why
3. A testing checklist
Format as GitHub Markdown.
Explain a complex function:
<!-- .claude/commands/explain.md -->
Explain $ARGUMENTS in plain English.
Assume the reader is a senior engineer unfamiliar with this specific codebase.
Cover: what it does, inputs, outputs, side effects, and any non-obvious behaviour.
Keep it under 200 words.
When to use custom commands
Commands are for repeatable prompt workflows you’d otherwise type from scratch each time:
- Security audit templates you run before every release
- Test generation prompts tailored to your framework
- PR description formats specific to your team’s style
- Onboarding prompts that explain specific parts of a codebase
- Incident review templates
The rule of thumb: if you’ve typed the same opening paragraph in a chat prompt more than twice, it belongs in a command file.
Commands vs CLAUDE.md instructions: Commands are explicit — you invoke them deliberately. CLAUDE.md instructions are ambient — they apply to every session. Use commands for tasks you run occasionally; use CLAUDE.md for behaviour you always want.
Skills: Context-Aware Automatic Instructions
Skills are the evolution of custom commands. A skill is a markdown file that Claude loads automatically when it is relevant to the current task — you do not need to invoke it manually. Claude reads skill descriptions at startup and pulls the full content when it determines the skill applies.
Skills are based on the Agent Skills open standard — designed to work across different AI tools, not just Claude Code. Claude Code adds extra features on top: better invocation control, sub-agent support, and dynamic context injection. A skill you write today is compatible with any tool that implements the same standard.
Skills vs custom commands
| Custom Commands | Skills | |
|---|---|---|
| How invoked | You type /command-name explicitly | Claude invokes automatically, or you type /skill-name |
| When to use | Occasional, deliberate workflows | Recurring patterns Claude should handle without prompting |
| Frontmatter | None | Supported: model, tools, invocation, behaviour flags |
| Location | .claude/commands/*.md | .claude/skills/<name>/SKILL.md |
| Status | Legacy (still works) | Recommended |
Progressive Disclosure: How Skills Stay Cheap
Skills use a “phone app” model for context efficiency. At session start, Claude loads only the name and description (~100 tokens) for every skill — like seeing app icons on your home screen. The full instructions only load when a skill is actually invoked.
This means you can have 20 skills defined without paying the context cost of 20 full instruction sets. A skill for database migration sits idle (just its 100-token description) until you actually ask Claude to write a migration, at which point its full body loads.
This is why the description frontmatter field is so important — it is the always-visible trigger. Write it precisely: “Invoked when the user asks to write, add, or generate tests.” A vague description means Claude either invokes the skill when it shouldn’t, or misses opportunities to invoke it when it should.
How to create a skill
Skills live in a named subdirectory under .claude/skills/, and can include supporting files alongside SKILL.md:
your-project/
└── .claude/
└── skills/
├── test-writer/
│ ├── SKILL.md ← required: instructions
│ ├── scripts/ ← optional: bash/python scripts Claude can run
│ │ └── run-tests.sh
│ ├── references/ ← optional: docs Claude can read
│ │ └── vitest-api.md
│ └── assets/ ← optional: templates, configs, fixtures
│ └── test-template.ts
├── security-reviewer/
│ └── SKILL.md
└── migration-generator/
├── SKILL.md
└── assets/
└── migration-template.sql
The scripts/, references/, and assets/ subdirectories mirror how a human expert thinks: they bring the tool (script), the manual (reference), and the starting material (asset) to the job. Claude can reference these files in skill instructions:
<!-- SKILL.md body -->
1. Run `scripts/run-tests.sh` to execute the test suite
2. Consult `references/vitest-api.md` for API details on complex matchers
3. Use `assets/test-template.ts` as the starting point for new test files
Each SKILL.md has a frontmatter block followed by the instruction body:
---
name: test-writer
description: Writes tests for TypeScript components using Vitest. Invoked when the user asks to write, add, or generate tests.
user-invocable: true
allowed-tools:
- Read
- Edit
- Bash
---
When writing tests for this project:
1. Use Vitest as the test framework (`import { describe, it, expect } from 'vitest'`)
2. Co-locate test files next to source: `component.ts` → `component.test.ts`
3. Cover: happy path, edge cases (null, empty, boundary), error conditions
4. Mock external dependencies using `vi.mock()` — never make real network calls in tests
5. Run `bun test` to verify tests pass before finishing
For React components, use `@testing-library/react` for rendering. For API handlers, test the handler function directly without starting a server.
Skill frontmatter options
---
name: security-reviewer # slug used for /skill-name invocation
description: > # shown at startup (~100 tokens) — be specific
Reviews code for security vulnerabilities. Auto-invokes when
the user asks to audit, review security, or check for vulnerabilities.
user-invocable: true # can be called with /security-reviewer
disable-model-invocation: false # true = skip LLM, run tools directly (for side-effecting skills)
allowed-tools: # restrict which tools this skill can use
- Read
- Bash
- Grep
model: claude-opus-4-7 # override the model for this skill
agent: true # run as a subagent (own context window)
---
The description field is the most important — Claude reads all skill descriptions at session start and uses them to decide which skills to invoke automatically. Write the description as a clear trigger statement: “Invoked when the user asks to…”.
Skill invocation modes
Automatic invocation: Claude reads the description and decides the skill is relevant. If you ask “write tests for this component”, Claude will auto-invoke the test-writer skill without you typing anything.
Manual invocation: Type /skill-name to invoke it directly:
/security-reviewer src/api/checkout.ts
Disabled auto-invocation: Set user-invocable: true and omit the trigger language from the description if you only want manual invocation.
More skill examples
Migration generator for a specific ORM:
---
name: migration-generator
description: Generates database migrations using Drizzle ORM. Auto-invokes when the user asks to add a column, create a table, or write a migration.
user-invocable: true
allowed-tools:
- Read
- Write
- Bash
---
When generating a migration for this project:
1. Read `src/db/schema.ts` first to understand the current schema structure
2. Create migration files in `src/db/migrations/` using Drizzle's format
3. Name files: `NNNN_description.ts` where NNNN is the next sequential number
4. Always include both `up()` and `down()` functions
5. For large tables (check row count with `SELECT COUNT(*) FROM table`), add a comment noting the table size and whether the migration will lock
Run `bun db:migrate --dry-run` to validate before finishing.
Kubernetes deployment helper:
---
name: k8s-deploy
description: Helps with Kubernetes deployments and kubectl operations. Auto-invokes when the user mentions kubectl, deployments, pods, or Kubernetes.
user-invocable: true
allowed-tools:
- Bash
- Read
---
For Kubernetes operations in this project:
- Always use the `staging` namespace for tests: `kubectl -n staging`
- Production namespace is `prod` — require explicit confirmation before any prod changes
- Deployment manifests live in `k8s/` — read them before suggesting changes
- After deploying, verify with `kubectl rollout status deployment/<name>`
- If a rollout fails, run `kubectl rollout undo deployment/<name>` and report what happened
Never delete pods directly. Use `kubectl rollout restart` for restarts.
Code style enforcer:
---
name: style-guide
description: Enforces this project's code style rules. Auto-invokes when writing or editing TypeScript files.
disable-model-invocation: false
allowed-tools:
- Read
- Edit
---
This project's TypeScript conventions:
- Use named exports only — no default exports
- Type everything explicitly — no implicit `any`
- Error handling: use `Result<T, E>` from `neverthrow` — no try/catch in business logic
- Async: always `async/await` — no raw Promise chains
- File names: `kebab-case.ts`
- Test files: co-located `component.test.ts`
When editing existing files, fix violations you encounter but do not refactor unrelated code.
When to use skills
Skills are for recurring behaviours Claude should apply automatically without you saying so every time:
- Project-specific conventions (your ORM, your test framework, your naming patterns)
- Workflows that follow the same steps every time (generate migration, verify, run dry-run)
- Tool restrictions for certain task types (security reviewer should only Read, not Edit)
- Specialised expertise that applies to a domain of tasks in this codebase
The decision rule: If you find yourself typing the same 3-10 line explanation at the start of a task (“when writing tests, use Vitest, co-locate files, and mock external deps”), that belongs in a skill. Claude will load it automatically next time.
Subagents: Parallel Context Windows
A subagent is a separate Claude instance with its own context window. It runs in parallel with your main session, investigates independently, and reports back a summary. This keeps noisy exploration out of your main conversation context.
Every subagent starts with a blank slate. It receives only its own system prompt (from the agent markdown file) and basic environment info like the working directory. It does not inherit the main session’s conversation history, CLAUDE.md, or tool permissions. This is by design — the blank slate is what keeps subagents focused and prevents them from carrying the baggage of a long main conversation.
sequenceDiagram
participant Dev as Developer
participant Main as Main Session
participant SA as Subagent
Dev->>Main: Implement the new rate limiter
Main->>SA: Spawn: read src/middleware/ and summarise the existing chain
SA->>SA: Reads files, explores codebase independently
SA-->>Main: Summary: 3 middleware layers, auth runs before rate limit
Main->>Main: Uses summary to implement correctly
Main-->>Dev: Rate limiter implemented, sits after auth middleware
How to create a subagent
The interactive path — open the agents menu inside Claude Code:
/agents
Select “Create New Agent”, choose project-level or user-level, then select “Generate with Claude (recommended)”. Provide a brief description of what the agent should do. Choose the tools it needs, model, and other config. Claude generates the file automatically.
The manual path — define subagents as markdown files in .claude/agents/ (project-level) or ~/.claude/agents/ (user-level):
your-project/
└── .claude/
└── agents/
├── security-reviewer.md
├── codebase-explorer.md
└── doc-writer.md
Each agent file uses frontmatter for configuration and a body that becomes the system prompt:
---
name: security-reviewer
description: Reviews code for security vulnerabilities. Use when auditing auth, payment, or API code.
model: claude-opus-4-7
tools:
- Read
- Bash
- Grep
- Glob
isolation: worktree
---
You are a security-focused code reviewer. Your job is to read code and report vulnerabilities.
Rules:
- Read files thoroughly before drawing conclusions
- Trace data flows end-to-end across imports
- Report findings with: severity (critical/high/medium/low), file path, line number, description, and why it is exploitable in this specific codebase
- Do NOT make any code changes — report only
- If you find something critical, say so clearly at the top of your response
Subagent frontmatter options
---
name: codebase-explorer # slug used to invoke this agent
description: > # shown to Claude when it decides which agent to spawn
Fast read-only exploration agent. Use for "find where X is defined"
or "which files reference Y" tasks.
model: claude-haiku-4-5 # use a cheaper/faster model for simple tasks
tools: # restrict available tools
- Read
- Bash
- Grep
- Glob
isolation: worktree # give this agent its own git worktree (required if it writes files)
hooks: # agent-specific hooks
PostToolUse:
- matcher: "*"
hooks:
- type: command
command: ".claude/hooks/agent-log.sh"
---
Built-in agent types you can reference without creating a file:
Explore— fast read-only Haiku agent for locating codePlan— context-gathering agent that returns a structured plangeneral-purpose— default Claude instance
Subagent Persistent Memory
Subagents can accumulate knowledge across sessions via the memory frontmatter field. When enabled, the agent reads and writes to its own memory files — storing patterns it discovers, recurring issues, design decisions, anything worth remembering for next time.
---
name: test-analysis-agent
description: Analyses test cases and tracks recurring failures
model: claude-sonnet-4-6
tools:
- Read
- Bash
memory: project
---
You are a testing specialist. Before analysing, check your memory
for known flaky tests and recurring failure patterns. After each
session, save new findings to memory so future runs benefit.
Memory scope options:
| Value | What it means |
|---|---|
user | Shared across all projects — personal learning |
project | Scoped to this project only |
local | Single-machine only, not synced |
When memory is enabled, Claude automatically adds Read/Write/Edit to the agent’s available tools and provides the first 200 lines of MEMORY.md in every session. Guide the agent explicitly: “Check your memory before starting” and “Save important findings to memory when done.”
Foreground vs Background Execution
Subagents can run in two modes depending on how much visibility you want:
| Foreground | Background | |
|---|---|---|
| Main session | Waits until agent finishes | Continues while agent runs |
| Permissions | Shown to you as they arise | All requested upfront before start |
| Visibility | Full — you see everything | Minimal — summary at the end |
| Best for | Tasks needing oversight | Long-running, fire-and-forget tasks |
For background execution, Claude asks for all required permissions before the subagent starts. After you approve, it runs automatically without further interruption.
Invoking subagents
From a prompt:
Use a subagent to investigate the payment service.
Read only: src/services/payment/, src/models/order.ts
Task: summarise the data flow from order creation to payment confirmation.
Report back a concise summary — do not make any changes.
By name (if you have defined a .claude/agents/ file):
Spawn the security-reviewer agent on src/api/checkout.ts
From Claude Code CLI (for scripting):
claude --agent security-reviewer -p "Audit src/api/checkout.ts" --output-format json
Worktree isolation for parallel writes
If a subagent needs to edit files, add isolation: worktree to prevent conflicts with the main session or other parallel subagents:
---
name: refactor-agent
description: Refactors a module independently
model: claude-sonnet-4-6
tools:
- Read
- Edit
- Bash
isolation: worktree
---
You are a refactoring agent. You receive a module path and a refactoring goal.
Apply the refactoring, run the tests, and report what you changed.
Each agent with isolation: worktree gets its own git branch and working tree. Changes are isolated until you merge them. This is the safe default for any subagent that writes files.
Example: Parallel specialist team for code review
This is the divide-and-conquer pattern that turns Claude Code from a single agent into a team. Instead of one review from one perspective, you spin up three specialists in parallel:
Spawn three subagents in parallel to review src/api/payments/:
1. Use the security-reviewer agent — audit for injection vulnerabilities,
auth gaps, and data exposure. Report findings with severity ratings.
2. Use the performance-reviewer agent — identify N+1 queries, missing indexes,
synchronous operations that should be async, and inefficient data structures.
3. Use the style-reviewer agent — check naming consistency, error handling patterns,
test coverage, and adherence to our API conventions.
When all three finish, compile a consolidated report with findings grouped by severity.
Define each specialist in .claude/agents/:
---
name: security-reviewer
description: Security-focused reviewer. Audits auth, injection, data exposure.
model: claude-opus-4-8
tools: [Read, Bash, Grep]
---
You are a security specialist. Review for: injection risks, auth gaps, sensitive data
in logs/responses, IDOR, insecure dependencies. Rate each finding critical/high/medium/low.
Report file:line references. Do NOT make any code changes.
---
name: performance-reviewer
description: Performance-focused reviewer. Finds N+1s, slow queries, blocking calls.
model: claude-sonnet-4-6
tools: [Read, Bash, Grep]
---
You are a performance specialist. Review for: N+1 queries, missing database indexes,
synchronous I/O that should be async, large in-memory operations, inefficient algorithms.
Estimate impact (latency, memory). Do NOT make any code changes.
---
name: style-reviewer
description: Code style and convention reviewer. Checks naming, tests, API consistency.
model: claude-haiku-4-5
tools: [Read, Grep]
---
You are a code style specialist. Review for: naming consistency, test coverage gaps,
error handling patterns, API consistency with existing endpoints.
Do NOT make any code changes.
You get three deep reviews in the time of one. Each agent has a fresh context — not biased toward code it just wrote — and specialised focus that produces sharper findings than a general review.
When to use subagents
Subagents are for self-contained tasks where the main session only needs a summary, not the full investigation:
- Exploring a large codebase to answer a specific question before you start implementing
- Running a security or code review scan while you continue other work
- Fetching and summarising external documentation or API specs
- Running parallel implementations of the same feature (different approaches, compare results)
- Any task that would fill your context window with output you do not need long-term
When not to use subagents: If the task requires tight coordination with your main session (shared state, sequential steps), run it in the main session instead. Subagent overhead is not worth it for small tasks.
Example subagents for common workflows
Dependency auditor:
---
name: dep-auditor
description: Audits project dependencies for vulnerabilities and outdated packages.
model: claude-sonnet-4-6
tools:
- Bash
- Read
---
Run the following and report findings:
1. `npm audit --json` — parse and summarise CVEs by severity
2. `npm outdated` — list major-version outdated packages
3. Read `package.json` — flag any packages that are known to be deprecated
Return a structured report: critical issues first, then high, then informational.
Do not make any changes.
Architecture explorer:
---
name: arch-explorer
description: Reads the codebase and produces an architecture summary. Use before starting large features.
model: claude-haiku-4-5
tools:
- Read
- Glob
- Bash
---
You are a read-only codebase explorer. Your goal is to produce a concise architecture summary.
Read:
1. Top-level directory structure
2. Package.json / go.mod / requirements.txt — understand the tech stack
3. Main entry points
4. Key data models
Return: tech stack, main modules, data flow from request to response, anything non-obvious.
Keep the summary under 400 words. Do not make any changes.
Piping: Claude in Your Shell Workflows
The -p flag makes Claude Code headless — single prompt in, stdout out. This enables Claude to participate in shell pipelines like any other Unix tool.
Basic piping
# Analyse log output
docker logs myapp --tail 200 | claude -p "what errors are here and what is causing them?"
# Summarise git history
git log --oneline -20 | claude -p "summarise what changed this week in plain English"
# Review a diff before committing
git diff --staged | claude -p "review this diff for bugs or issues before I commit"
Chaining Claude calls
# Generate migration, then review it
claude -p "generate a SQL migration to add a soft-delete column to the users table" \
> migration.sql
claude -p "review this migration for safety — will it lock the table? Is rollback safe?" \
< migration.sql
Integrating with CI
# Post a natural-language summary of a deploy to Slack
deploy_output=$(./deploy.sh 2>&1)
summary=$(echo "$deploy_output" | claude -p "summarise this deploy output in 2 sentences for a Slack message")
curl -X POST "$SLACK_WEBHOOK" -d "{\"text\": \"$summary\"}"
Daily standup from git log
#!/bin/bash
# scripts/slack-standup.sh
diff=$(git log --since="yesterday" --oneline --all)
update=$(echo "$diff" | claude -p "write a casual 3-bullet Slack standup update from these commits. First person, past tense, no jargon.")
echo "$update"
When to Use Which: Decision Framework
flowchart TD
Q1{Must this happen\nevery time?} -->|Yes| Hook[Hook]
Q1 -->|No| Q2{Is it a repeatable\nprompt you type often?}
Q2 -->|Yes, you invoke it| Cmd[Custom Command]
Q2 -->|Yes, Claude should auto-detect| Skill[Skill]
Q2 -->|No| Q3{Is the task noisy,\nlong, or parallel?}
Q3 -->|Yes| SA[Subagent]
Q3 -->|No| Main[Main session prompt]
In plain terms:
- Hook — “Run
prettieron every file Claude edits. No exceptions.” - Custom command — “I write the same security audit prompt every sprint. Save it as
/security-audit.” - Skill — “Every time Claude writes TypeScript in this project, it should follow our specific conventions — but I don’t want to type them every time.”
- Subagent — “Before I implement this feature, have Claude read the entire legacy module and give me a summary. I don’t need all that detail in my main session.”
- Piping — “Pipe
docker logsinto Claude to classify errors without opening an interactive session.”
They are not mutually exclusive. The most powerful patterns combine them:
flowchart TD
Edit[Claude edits a file] --> Hook[PostToolUse hook fires]
Hook --> Tests[Run test suite]
Tests --> Fail{Tests pass?}
Fail -->|Yes| Continue[Claude continues working]
Fail -->|No| SA[Spawn arch-explorer subagent]
SA --> SA2[Subagent reads test output + edited file]
SA2 --> Fix[Subagent suggests fix]
Fix --> Main[Main session applies fix]
Main --> Edit
Getting Started Checklist
Start here — in this order. Each step adds immediate value before you move to the next:
- Add the secrets scanner hook — the most important safety net; blocks accidental credential commits before they happen
- Add the file shield hook — protect
.envand other sensitive files from AI edits entirely - Add destructive command block — prevents
rm -rf,terraform destroy, andkubectl deleteaccidents - Add auto-format hook — low risk, immediate value, no false positives
- Create one custom command — save a prompt you type repeatedly (security audit template, PR description format)
- Try the test-on-edit hook — only in a project with fast tests (<10s total)
- Create one skill — start with your project’s test conventions or deployment workflow
- Try one subagent investigation — before implementing a feature, not during; get the summary, then implement
- Try a parallel specialist review — spin up security + performance + style reviewers on your next PR
- Add
isolation: worktreeto any subagent that writes files
You do not need all of these at once. The first three hooks add immediate safety with no downside. Add the rest when you feel the friction each one solves.
