Working with Subagents and Hooks in Claude Code

Working with Subagents and Hooks in Claude Code

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

🧠
Orchestrator Agent
Plans tasks, delegates to subagents, aggregates results
🔍 Research Subagent
Web searches, reads documentation, fetches context
💻 Coder Subagent
Writes code, runs tests, fixes bugs
📝 Reviewer Subagent
Code review, security analysis, documentation

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?
PreToolUseBefore any tool callValidate inputs, enforce policies, log intent✅ Yes
PostToolUseAfter any tool callAudit outputs, transform results, trigger actions❌ No
NotificationStatus updatesSlack/email alerts, progress tracking❌ No
StopSession endsCleanup, 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

Fan-Out / Fan-In
Orchestrator spawns N subagents in parallel, collects all results, then aggregates.
Pipeline
Subagent A's output becomes Subagent B's input, forming a sequential processing chain.
Specialist Routing
Orchestrator routes to different specialist subagents based on task type or content.
Recursive Delegation
Subagents can spawn their own subagents for further task decomposition (up to configured limits).

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.

Post a Comment

Previous Post Next Post