Skip to content

Hooks System

Hooks enable you to execute custom scripts at specific points in Claude Code’s workflow—automating tasks like formatting, validation, notifications, and more.

Event Types

EventTrigger
PreToolUseBefore tool execution
PostToolUseAfter tool completion
StopWhen Claude finishes response
SubagentStopWhen subagent completes
NotificationOn desktop notification
UserPromptSubmitBefore processing user input
PermissionRequestWhen permission prompt appears
PreCompactBefore context compaction
SessionStartWhen session begins
SessionEndWhen session ends

Configuration

Hooks can be defined in:

  • ~/.claude/settings.json (user)
  • .claude/settings.json (project)
  • Subagent YAML frontmatter
  • Plugin manifests

Basic Structure

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint:fix \"$TOOL_INPUT_file_path\""
          }
        ]
      }
    ]
  }
}

Hook Types

Command-based Hooks

Execute shell commands:

{
  "type": "command",
  "command": "prettier --write \"$TOOL_INPUT_file_path\""
}

Prompt-based Hooks

Invoke LLM for decisions:

{
  "type": "prompt",
  "prompt": "Are you confident this response fully addresses the user's question?"
}

Exit Codes

CodeBehavior
0Success, continue
2Block tool/stop operation
OtherError logged, continue

Environment Variables

Available in hook commands:

VariableDescription
CLAUDE_PROJECT_DIRProject directory
TOOL_NAMECurrent tool name
TOOL_INPUT_*Tool input parameters
TOOL_OUTPUTTool output (PostToolUse)

Practical Examples

Auto-format on Edit

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "prettier --write \"$TOOL_INPUT_file_path\" 2>/dev/null || true"
      }]
    }]
  }
}

Block Dangerous Commands

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "if echo \"$TOOL_INPUT_command\" | grep -qE 'rm -rf|sudo|chmod 777'; then exit 2; fi"
      }]
    }]
  }
}

Session Initialization

{
  "hooks": {
    "SessionStart": [{
      "hooks": [{
        "type": "command",
        "command": "source ~/.nvm/nvm.sh && nvm use"
      }]
    }]
  }
}

Smart Stop Check

{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "prompt",
        "prompt": "Did you fully complete the task? If not, continue."
      }]
    }]
  }
}

Security Best Practices

  1. Validate inputs: Don’t trust $TOOL_INPUT_* blindly
  2. Use absolute paths: Avoid path injection
  3. Limit scope: Apply hooks only where needed
  4. Handle errors: Always handle command failures gracefully

Debugging

# View hook execution logs
claude --debug

# Test hook command manually
TOOL_INPUT_file_path="test.js" bash -c 'your-hook-command'

Next Steps