GenAIHub
← Back to Technical Section

Hooks Guide β€” Claude Code

Run shell commands automatically on Claude Code lifecycle events

πŸͺ Hooks Guide

Wire shell commands to Claude Code lifecycle events β€” enforce policies, run linters, send notifications, and automate governance without changing your agent's prompts.

Official Docs β†’

1. Hook Event Types

Claude Code fires hooks at four key lifecycle points:

PreToolUse

Fires before a tool is called. Can block the tool call by exiting with code 2. Use for security validation, dry-run checks, approval gates.

PostToolUse

Fires after a tool completes. Use for logging, metrics, post-processing, notifications.

Notification

Fires when Claude sends a notification (needs input, task complete, etc.). Use for desktop alerts, Slack pings, sound effects.

Stop

Fires when Claude finishes or is interrupted. Use for cleanup, summaries, post-session reporting.

2. Configuring Hooks

Hooks are configured in settings.json (global: ~/.claude/settings.json, project: .claude/settings.json):

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash /path/to/validate-command.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $CLAUDE_FILE_PATHS"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude needs input\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

3. Hook Input & Output

Claude Code passes a JSON object via stdin to every hook:

# PreToolUse stdin example
{
  "session_id": "abc123",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build"
  }
}

# Hook can write JSON to stdout to modify behavior:
# exit 0  β†’ allow (optionally provide context to Claude)
# exit 2  β†’ block the tool call (output shown to Claude as error)

4. Practical Examples

βœ…

Block dangerous bash commands

PreToolUse β†’ Bash: scan input for rm -rf /, DROP TABLE, etc.

βœ…

Auto-format on file write

PostToolUse β†’ Write: run Prettier/ESLint on saved files automatically

βœ…

Audit log all tool calls

PostToolUse β†’ *: append JSON to audit.log for compliance

βœ…

Desktop notification when idle

Notification event β†’ trigger OS notification when Claude needs input

5. Best Practices

  • β†’ Keep hooks fast β€” hook scripts block the agent. Aim for <100ms; offload slow work to background processes.
  • β†’ Use exit codes correctly β€” only exit 2 to block; use exit 0 for warnings that Claude should see but not be blocked by.
  • β†’ Prefer project-scoped hooks β€” commit .claude/settings.json so the whole team benefits from the same guardrails.
  • β†’ Test hooks independently β€” pass synthetic JSON via stdin to validate behavior before enabling in live sessions.