Claude Code brings a powerful multi-agent architecture into your development workflow. By combining subagents — independent Claude instances that work on delegated tasks — with hooks — event-driven lifecycle callbacks — you can build sophisticated automation pipelines that go far beyond simple prompt-response interactions.
What Are Subagents?
A subagent is a Claude instance spawned by an orchestrator agent to execute a specific, bounded task. The orchestrator defines the task, provides context, assigns tools, and waits for the result. Subagents enable parallelism, specialization, and separation of concerns in complex AI workflows.
Architecture Overview
Spawning a Subagent
In Claude Code, you launch a subagent using the Task tool. The subagent runs with its own context, tools, and instructions.
// Orchestrator instructs Claude Code to spawn a subagent
{
"tool": "Task",
"input": {
"description": "Analyze the codebase for security vulnerabilities",
"prompt": "You are a security-focused code reviewer. Read all .cs files in the /src directory and identify potential SQL injection, XSS, and authentication bypass vulnerabilities. Return a structured JSON report.",
"subagent_model": "claude-opus-4-5",
"allowed_tools": ["Read", "Grep", "Bash"]
}
}
What Are Hooks?
Hooks are shell commands that Claude Code executes at specific points in its lifecycle. They let you intercept, modify, or react to Claude's actions — adding logging, validation, notifications, or custom business logic without modifying the underlying agent.
Available Hook Events
| Hook Event | Trigger | Use Cases | Can Block? |
|---|---|---|---|
| PreToolUse | Before any tool call | Validate inputs, enforce policies, log intent | ✅ Yes |
| PostToolUse | After any tool call | Audit outputs, transform results, trigger actions | ❌ No |
| Notification | Status updates | Slack/email alerts, progress tracking | ❌ No |
| Stop | Session ends | Cleanup, reporting, final validation | ❌ No |
Configuring Hooks in settings.json
Hooks are configured in Claude Code's settings file. Each hook specifies the event, a command to run, and optional filters for specific tools.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/validate_bash.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "echo 'File written: $CLAUDE_TOOL_INPUT_PATH' >> ~/.claude/audit.log"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "node ~/.claude/hooks/notify_slack.js '$CLAUDE_NOTIFICATION_MESSAGE'"
}
]
}
]
}
}
Hook Environment Variables
Claude Code passes context to your hook commands via environment variables:
CLAUDE_TOOL_NAME # The tool being called (e.g., "Bash", "Write", "Read")
CLAUDE_TOOL_INPUT # Full JSON input to the tool
CLAUDE_TOOL_INPUT_PATH # For file tools: the file path
CLAUDE_TOOL_INPUT_COMMAND # For Bash: the command being run
CLAUDE_TOOL_RESULT # [PostToolUse] The tool's output
CLAUDE_NOTIFICATION_MESSAGE # [Notification] Status message text
CLAUDE_SESSION_ID # Unique session identifier
Practical Example: Security Gate Hook
This hook blocks any Bash command that contains dangerous patterns like rm -rf, sudo, or access to sensitive directories.
#!/usr/bin/env python3
# ~/.claude/hooks/validate_bash.py
import json, os, sys
BLOCKED_PATTERNS = ["rm -rf /", "sudo rm", "dd if=", "> /dev/sd", "chmod 777 /"]
SENSITIVE_DIRS = ["/etc/", "/root/", "/var/lib/"]
tool_input = json.loads(os.environ.get("CLAUDE_TOOL_INPUT", "{}"))
command = tool_input.get("command", "")
for pattern in BLOCKED_PATTERNS:
if pattern in command:
print(json.dumps({
"decision": "block",
"reason": f"Dangerous pattern detected: '{pattern}'"
}))
sys.exit(0)
for sensitive_dir in SENSITIVE_DIRS:
if sensitive_dir in command:
print(json.dumps({
"decision": "block",
"reason": f"Access to sensitive directory: '{sensitive_dir}'"
}))
sys.exit(0)
# Allow the command
print(json.dumps({"decision": "allow"}))
Subagent Communication Patterns
Common Patterns
Best Practices
- Minimize subagent context: Give each subagent only the information it needs — this reduces token costs and improves focus.
- Use hooks for observability: Log all tool calls in PostToolUse to build a complete audit trail of what Claude did.
- Validate in PreToolUse: Use PreToolUse hooks as a security gate for any tools that can cause side effects (Bash, Write, HTTP).
- Handle subagent failures: Always check the subagent's return status and have fallback strategies for task failure.
- Limit tool access: Grant each subagent only the tools required for its specific task.
Conclusion
Subagents and hooks transform Claude Code from a simple coding assistant into a full orchestration platform for complex multi-step tasks. By decomposing work across specialized subagents and adding observability and control via lifecycle hooks, you can build AI-powered workflows that are secure, auditable, and highly capable.