# Claude Code Hooks — Walkthrough

## Before We Begin

<!-- hint:card type="concept" title="Automation runs in response to events" -->

**Warm-up:** Think about automation you use today — Git hooks, CI pipelines, formatters that run on save. What do they have in common? What "events" trigger them, and what happens after?

**Checkpoint:** The user should recognize that automation is often event-driven: something happens (a commit, a push, a file save), and a script runs. Hooks extend this pattern to the AI coding session.

---

## Step 1: What Are Hooks?

Hooks are shell commands that Claude Code runs automatically when certain events occur.

<!-- hint:diagram mermaid-type="flowchart" topic="Hook flow: event fires → script runs → continues" -->

**Task:** Look at your project's `.claude/settings.json` (or the hooks docs). See if any hooks are already configured.

**Question:** Why might you want a script to run *after* the agent uses a tool, rather than manually running it yourself?

**Checkpoint:** The user should understand that hooks automate repetitive workflows — e.g., linting after every edit, so you never forget. They run transparently in the background of the session.

---

## Step 2: Lifecycle Events

Claude Code fires hooks at three lifecycle points: PostToolUse, UserPromptSubmit, and Stop.

**Task:** Review the table of events. For each event, name one concrete use case from your own workflow.

**Question:** PostToolUse runs after *every* tool call — Edit, Write, Bash, etc. What's the trade-off? When might that be too frequent?

**Checkpoint:** The user should see that PostToolUse is powerful but can be noisy — you might want to filter by tool name (e.g., only run on Edit/Write) or add a timeout to avoid slowdown.

---

## Step 3: Writing a PostToolUse Hook

PostToolUse receives `TOOL_NAME`, `TOOL_INPUT`, and `TOOL_OUTPUT` as environment variables.

<!-- hint:terminal -->

**Task:** Write a minimal PostToolUse hook that logs the tool name to a file (e.g., `.local/hook-log.txt`) when the tool is Edit or Write. Configure it in `.claude/settings.json`.

**Question:** How would you extract the file path from `TOOL_INPUT` inside a shell script? (Hint: it's JSON.)

**Checkpoint:** The user should use `jq` or similar to parse `TOOL_INPUT` and get `file_path`. They should configure the hook with a timeout.

---

## Step 4: Writing a UserPromptSubmit Hook

UserPromptSubmit fires when the user sends a message, before the agent responds.

<!-- hint:card type="concept" title="UserPromptSubmit runs before the agent thinks" -->

**Task:** Write a UserPromptSubmit hook that writes the first 100 characters of the user's prompt to a log file. Use the `USER_PROMPT` environment variable.

**Question:** What could you use UserPromptSubmit for? Think beyond logging — validation, command detection, rate limiting.

**Checkpoint:** The user should identify uses like: detecting `/teach` and routing to a specific handler; validating prompt length; blocking unsafe prompts.

---

## Step 5: Writing a Stop Hook

Stop fires when the agent finishes a response. It receives `STOP_REASON` and `TOKEN_USAGE`.

<!-- hint:terminal -->

**Task:** Write a Stop hook that appends the session's token usage to a file. Parse `TOKEN_USAGE` (JSON) and log input/output counts.

**Question:** Why run something at "Stop" rather than at "PostToolUse"? What kinds of things only make sense at the end of a turn?

**Checkpoint:** The user should recognize that session-level metrics (duration, token usage, cleanup) only make sense when the turn is complete. PostToolUse would fire many times per turn; Stop fires once.

---

## Step 6: Testing Hooks

Hooks block the agent while they run. Slow or failing hooks hurt the experience.

<!-- hint:terminal -->

**Task:** Run your PostToolUse hook script manually with fake env vars: `TOOL_NAME=Edit TOOL_INPUT='{"file_path":"test.txt"}' ./scripts/on-tool-use.sh`. Verify it behaves correctly.

**Question:** What happens if your hook script exits with code 1? Should it? How does the agent interpret non-zero exit?

**Checkpoint:** The user should test hooks in isolation before wiring them up. They should consider: hooks should fail gracefully — a lint failure shouldn't crash the session. Exit codes can signal the agent; document the contract.

---

## Step 7: Error Handling and Best Practices

Hooks run synchronously. They can slow down or break the workflow if misused.

<!-- hint:card type="concept" title="Keep hooks fast; fail gracefully" -->

**Task:** Add a `timeout` to each hook in your config. Ensure your scripts handle missing or malformed env vars (e.g., empty `TOOL_INPUT`).

**Question:** The content says "Log, don't print" — why? What happens to stdout when a hook runs?

**Checkpoint:** The user should understand that stdout from hooks can be fed back to the agent, which may confuse it. Write to files instead. Use `timeout` to prevent runaway hooks.
