# Claude Code Hooks

> **Level: 🌿 Intermediate**

## What Are Hooks?

Hooks are shell commands that Claude Code executes automatically in response to lifecycle events. They let you automate workflows without manual intervention — run linters after edits, track usage statistics, validate changes before commits, and more.

## Lifecycle Events

| Event | When It Fires | Use Cases |
|---|---|---|
| **PostToolUse** | After any tool is used (Edit, Write, Bash, etc.) | Linting, formatting, usage tracking |
| **UserPromptSubmit** | When the user sends a message | Input validation, command detection |
| **Stop** | When the agent finishes a response | Session tracking, cleanup, notifications |

## Configuration

Hooks are defined in `hooks.json` (for plugins) or in `.claude/settings.json` (for user hooks):

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "command": "./scripts/on-tool-use.sh",
        "timeout": 5000
      }
    ],
    "UserPromptSubmit": [
      {
        "command": "./scripts/on-prompt.sh",
        "timeout": 3000
      }
    ],
    "Stop": [
      {
        "command": "./scripts/on-stop.sh",
        "timeout": 5000
      }
    ]
  }
}
```

## Environment Variables

Hook scripts receive context via environment variables:

### PostToolUse
- `TOOL_NAME` — name of the tool that was used (e.g., "Edit", "Bash")
- `TOOL_INPUT` — JSON string of the tool's input parameters
- `TOOL_OUTPUT` — JSON string of the tool's output

### UserPromptSubmit
- `USER_PROMPT` — the text the user submitted

### Stop
- `STOP_REASON` — why the agent stopped (e.g., "end_turn", "max_tokens")
- `TOKEN_USAGE` — JSON with input/output token counts

## Example: Auto-Format on Edit

```bash
#!/bin/bash
# scripts/auto-format.sh — runs after PostToolUse
if [ "$TOOL_NAME" = "Edit" ] || [ "$TOOL_NAME" = "Write" ]; then
  FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
  if [ -n "$FILE" ] && [ -f "$FILE" ]; then
    npx prettier --write "$FILE" 2>/dev/null
  fi
fi
```

## Example: Track Tool Usage

```bash
#!/bin/bash
# scripts/track-usage.sh — counts tool usage per category
DATA_FILE=".local/usage.json"
CATEGORY=$(echo "$TOOL_NAME" | tr '[:upper:]' '[:lower:]')

if [ ! -f "$DATA_FILE" ]; then
  echo '{}' > "$DATA_FILE"
fi

jq --arg cat "$CATEGORY" '.[$cat] = ((.[$cat] // 0) + 1)' "$DATA_FILE" > "${DATA_FILE}.tmp"
mv "${DATA_FILE}.tmp" "$DATA_FILE"
```

## Best Practices

1. **Keep hooks fast** — they block the agent while running. Use the `timeout` field.
2. **Fail gracefully** — a hook error shouldn't break the user's workflow.
3. **Use exit codes** — exit 0 for success, non-zero to signal the agent.
4. **Log, don't print** — write to files, not stdout (stdout goes back to the agent).
5. **Test independently** — run your hook scripts manually before configuring them.
