Skip to main content

What Are Hooks?

Hooks are shell commands that execute automatically in response to agent lifecycle events. They enable:
  • Session initialization - Set up context at start
  • Tool interception - Validate or modify tool calls
  • Event reactions - Respond to specific actions

Hook Types

Session Hooks

Execute when a session starts or ends:
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "squads status",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Tool Hooks

Execute before or after specific tool calls:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Bash command: $TOOL_INPUT'"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint --fix"
          }
        ]
      }
    ]
  }
}

Configuration

Location

Hooks are configured in .claude/settings.json:
project/
└── .claude/
    └── settings.json    # Hook definitions

Schema

{
  "hooks": {
    "<EventType>": [
      {
        "matcher": "<ToolPattern>",  // Optional: filter by tool
        "hooks": [
          {
            "type": "command",
            "command": "<shell command>",
            "timeout": 30,           // Optional: seconds
            "workingDirectory": "."  // Optional: cwd
          }
        ]
      }
    ]
  }
}

Event Types

EventTriggerCommon Uses
SessionStartAgent session beginsLoad context, show status
SessionEndAgent session endsSave state, cleanup
PreToolUseBefore any tool executesValidation, logging
PostToolUseAfter any tool executesFormatting, side effects
NotificationAgent sends notificationAlerts, integrations

Examples

Auto-Format on Write

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$TOOL_OUTPUT_PATH\"",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Squad Status on Session Start

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "squads status",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Git Commit Validation

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(git commit:*)",
        "hooks": [
          {
            "type": "command",
            "command": "npm test",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Notify on Completion

{
  "hooks": {
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Agent completed\" with title \"Squads\"'"
          }
        ]
      }
    ]
  }
}

Environment Variables

Hooks have access to context via environment variables:
VariableDescription
TOOL_NAMEName of the tool being called
TOOL_INPUTInput parameters (JSON)
TOOL_OUTPUTOutput result (post-hooks only)
TOOL_OUTPUT_PATHFile path (for file operations)
SESSION_IDCurrent session identifier

Best Practices

  • Keep hooks fast (< 10 seconds typical)
  • Use timeouts to prevent hangs
  • Log hook failures for debugging
  • Test hooks in isolation before deploying
  • Use matchers to target specific tools
Common pitfalls:
  • Infinite loops (hook triggers itself)
  • Missing error handling
  • Blocking hooks that slow agent work
  • Hardcoded paths that break across machines

Debugging Hooks

Check Hook Execution

# Enable verbose logging
export CLAUDE_VERBOSE=1
claude

Test Commands Manually

# Run the hook command directly
squads status

Validate JSON Syntax

# Check settings.json is valid
cat .claude/settings.json | jq .