# How to Configure Hooks in Claude Code

> Hooks run a shell command on Claude Code events. Note $CLAUDE_FILE_PATHS no longer exists — read the file path from the JSON on stdin instead.

- Canonical: https://guides-ai.pages.dev/guides/claude-code-hooks/
- Plate 03.03 · Topic: Claude Code commands (https://guides-ai.pages.dev/topics/commands/)
- Published: 10 Jun 2026 · Updated: 06 Sept 2026 · 4 min read
- Source site: guides-ai — https://guides-ai.pages.dev/

Hooks run a shell command automatically on Claude Code events — format after every edit, block
edits to a protected path, log what ran. They live in your settings files and run as ordinary
code, so they do what a prompt can only ask for.

**Read this first if you're porting an old hook:** `$CLAUDE_FILE_PATHS` no longer exists. Hooks
receive a JSON object on **stdin**, and the file path lives at `.tool_input.file_path`. The
environment variable that does exist is `${CLAUDE_PROJECT_DIR}`.

## 1. Pick an event

`PreToolUse` (before a tool runs, and able to block it), `PostToolUse` (after — format, lint,
test), `UserPromptSubmit`, `SessionStart`, `SessionEnd` and `Stop` cover almost everything. The
matcher filters by tool name.

## 2. Add it to settings.json

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/format.sh" }
        ]
      }
    ]
  }
}
```

What it does: runs one script after every Edit or Write. Put it in `.claude/settings.json` for
the project or `~/.claude/settings.json` for yourself — see
[which settings file wins](/guides/claude-code-settings-json/).

## 3. Read the path from stdin

```bash
#!/bin/bash
# .claude/hooks/format.sh
file=$(jq -r '.tool_input.file_path')
[ -f "$file" ] && npx prettier --write "$file"
```

What it does: parses the hook's stdin JSON with `jq` and formats the one file that changed.
This is the modern replacement for the old `$CLAUDE_FILE_PATHS` one-liner.

## 4. Confirm it loaded

```text
/hooks
```

What it does: opens a read-only browser of every configured hook, its matcher, its handler type
and which settings file it came from. **No restart is needed** — direct edits to hooks in
settings files are picked up by the file watcher.

## 5. Block something

A `PreToolUse` hook that exits with **code 2** blocks the action and shows stderr as the reason.
Any other non-zero code is a non-blocking error and the action proceeds.

## If it fails

If `/hooks` doesn't list it, the JSON is malformed — `claude doctor` names what it dropped. If
it lists but never fires, the matcher doesn't match the tool name. Hooks run without a
controlling terminal, so anything interactive hangs until it times out.

Start with one harmless formatting hook before you write a blocking one: a bad matcher gets in
your way all day. Hooks are real [guardrails](/glossary/#guardrails) rather than instructions,
which is what separates them from [permissions](/guides/claude-code-permissions/) prompts. They
run unattended in [headless CI](/guides/claude-code-headless-ci/) too, so read one carefully.

Source: [Claude Code hooks reference](https://code.claude.com/docs/en/hooks).
