# Claude Code Hooks — Quick Reference

## 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 |

## Environment Variables by Event

### PostToolUse
| Variable | Description |
|---|---|
| `TOOL_NAME` | Name of the tool (e.g., "Edit", "Write", "Bash") |
| `TOOL_INPUT` | JSON string of the tool's input parameters |
| `TOOL_OUTPUT` | JSON string of the tool's output |

### UserPromptSubmit
| Variable | Description |
|---|---|
| `USER_PROMPT` | The text the user submitted |

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

## Configuration Syntax

Hooks are defined in `.claude/settings.json` or `hooks.json`:

```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
      }
    ]
  }
}
```

## Parsing Tool Input (PostToolUse)

```bash
# Get file path from Edit/Write tool input
FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')

# Check if it's a code file
if [ -n "$FILE" ] && [[ "$FILE" == *.js ]]; then
  npx eslint "$FILE" >> .local/lint.log 2>&1
fi
```

## Best Practices

| Practice | Reason |
|---|---|
| **Keep hooks fast** | They block the agent; use `timeout` |
| **Fail gracefully** | A hook error shouldn't break the user's workflow |
| **Log, don't print** | Stdout can be fed back to the agent |
| **Test independently** | Run scripts manually with fake env vars first |
| **Use exit codes** | Exit 0 for success; document non-zero semantics |

## Testing Hooks Manually

```bash
# Simulate PostToolUse
TOOL_NAME=Edit TOOL_INPUT='{"file_path":"src/app.js"}' TOOL_OUTPUT='{}' ./scripts/on-tool-use.sh

# Simulate UserPromptSubmit
USER_PROMPT="Review this PR" ./scripts/on-prompt.sh

# Simulate Stop
STOP_REASON=end_turn TOKEN_USAGE='{"input":1000,"output":500}' ./scripts/on-stop.sh
```
