Skip to content

Latest commit

 

History

History
162 lines (142 loc) · 4.31 KB

File metadata and controls

162 lines (142 loc) · 4.31 KB

Hooks Configuration

Hooks run commands at specific points in Claude Code's lifecycle.

Hook Structure

{
  "hooks": {
    "EVENT_NAME": [
      {
        "matcher": "ToolName|OtherTool",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here",
            "timeout": 60,
            "statusMessage": "Running..."
          }
        ]
      }
    ]
  }
}

Hook Events

Event Matcher Purpose
PermissionRequest Tool name Run before permission prompt
PreToolUse Tool name Run before tool, can block
PostToolUse Tool name Run after successful tool
PostToolUseFailure Tool name Run after tool fails
Notification Notification type Run on notifications
Stop - Run when Claude stops (including clear, resume, compact)
PreCompact "manual"/"auto" Before compaction
PostCompact "manual"/"auto" After compaction (receives summary)
UserPromptSubmit - When user submits
SessionStart - When session starts

Common tool matchers: Bash, Write, Edit, Read, Glob, Grep

Hook Types

1. Command Hook - Runs a shell command:

{ "type": "command", "command": "prettier --write $FILE", "timeout": 30 }

2. Prompt Hook - Evaluates a condition with LLM:

{ "type": "prompt", "prompt": "Is this safe? $ARGUMENTS" }

Only available for tool events: PreToolUse, PostToolUse, PermissionRequest.

3. Agent Hook - Runs an agent with tools:

{ "type": "agent", "prompt": "Verify tests pass: $ARGUMENTS" }

Only available for tool events: PreToolUse, PostToolUse, PermissionRequest.

Hook Input (stdin JSON)

{
  "session_id": "abc123",
  "tool_name": "Write",
  "tool_input": { "file_path": "/path/to/file.txt", "content": "..." },
  "tool_response": { "success": true }  // PostToolUse only
}

Hook JSON Output

Hooks can return JSON to control behavior:

{
  "systemMessage": "Warning shown to user in UI",
  "continue": false,
  "stopReason": "Message shown when blocking",
  "suppressOutput": false,
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Context injected back to model"
  }
}

Fields:

  • systemMessage - Display a message to the user (all hooks)
  • continue - Set to false to block/stop (default: true)
  • stopReason - Message shown when continue is false
  • suppressOutput - Hide stdout from transcript (default: false)
  • decision - "block" for PostToolUse/Stop/UserPromptSubmit hooks (deprecated for PreToolUse, use hookSpecificOutput.permissionDecision instead)
  • reason - Explanation for decision
  • hookSpecificOutput - Event-specific output (must include hookEventName):
    • additionalContext - Text injected into model context
    • permissionDecision - "allow", "deny", or "ask" (PreToolUse only)
    • permissionDecisionReason - Reason for the permission decision (PreToolUse only)
    • updatedInput - Modified tool input (PreToolUse only)

Common Patterns

Auto-format after writes:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true"
      }]
    }]
  }
}

Log all bash commands:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.command' >> ~/.claude/bash-log.txt"
      }]
    }]
  }
}

Stop hook that displays message to user:

Command must output JSON with systemMessage field:

# Example command that outputs: {"systemMessage": "Session complete!"}
echo '{"systemMessage": "Session complete!"}'

Run tests after code changes:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path // .tool_response.filePath' | grep -E '\\.(ts|js)$' && npm test || true"
      }]
    }]
  }
}