Claude Code Hooks, Subagents, and Piping: Advanced Automation for Teams
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. Hooks, subagents, and piping let you build Claude into your workflow so that it works with your tools rather than alongside them.
Hooks: Making Claude Anticipatory
A hook is a shell command, script, 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.
flowchart TD
SS[SessionStart] --> Work[Developer works]
Work --> PTU[PreToolUse\nruns before each tool]
PTU --> Tool[Tool executes\nBash Edit Write etc]
Tool --> PTUS[PostToolUse\nruns after each tool]
PTUS --> Work
Work --> SE[SessionEnd]
Configuration
Hooks live in .claude/settings.json (project-wide) or ~/.claude/settings.json (personal):
{
"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"
}
]
}
]
}
}
Hooks receive a JSON payload on stdin with the tool name, input, current working directory, and session ID — enabling conditional behaviour.
Hook: Auto-run tests after every edit
The most valuable hook for development teams. Every time Claude edits a file, tests run automatically. Claude sees the results immediately and can fix breaking changes without you prompting it.
#!/bin/bash
# .claude/hooks/on-edit.sh
input=$(cat)
CWD=$(echo "$input" | jq -r '.cwd // "."')
cd "$CWD"
# Run fast unit tests only, cap output
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
Hook: Block destructive Bash commands
A safety net that blocks commands matching a danger pattern and shows Claude why:
#!/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|truncate.*--"
if echo "$cmd" | grep -qiE "$DANGEROUS"; then
jq -n --arg cmd "$cmd" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Blocked: destructive command pattern detected. Run manually after review."
}
}'
exit 0
fi
exit 0
Hook: Auto-format on save
Format files in the appropriate language every time Claude edits them:
#!/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
Subagents: Parallel Context Windows
A subagent is a separate Claude instance that runs alongside your main session. It has its own context window, so it can investigate a large codebase without polluting your 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 middleware chain
SA->>SA: Reads files 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
When to use subagents:
- Investigating a large codebase to answer a specific question before main work begins
- Running a security or code review scan in parallel with active development
- Fetching external documentation or API specs without polluting your main session
How to invoke:
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
Key rule: Keep subagent tasks independent. If a subagent needs to share state with the main session (edit the same file, for example), race conditions can corrupt work. Use subagents for read-only investigation, not write operations.
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\"}"
Writing Slack updates from git diffs
One of the highest-value daily uses of piping:
#!/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"
Combining All Three
The most powerful pattern combines hooks, subagents, and piping:
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 subagent to diagnose failure]
SA --> SA2[Subagent reads test output + edited file]
SA2 --> Fix[Subagent suggests fix]
Fix --> Main[Main session applies fix]
Main --> Edit
The key insight: hooks make Claude notice things without being asked. Subagents investigate without bloating main context. Piping exports Claude’s output into other tools. Together they turn Claude from a chatbot into a workflow participant.
Getting Started Checklist
- Add the auto-format hook first — low risk, immediate value
- Add the destructive command block — safety net with no false positive risk
- Try the test-on-edit hook in a project with fast tests
- Use piping for one daily task (log analysis or git diff summary)
- Try one subagent investigation before implementing a feature
Each of these can be added independently. Start with one and see the difference before adding more.
