Claude Code hooks: three you can paste today
A hook is a shell command that Claude Code runs at a fixed point, such as after an edit or before a tool call. It happens every time, whether or not the model remembers.
Where a hook goes
~/.claude/settings.json: all your projects, only on your machine..claude/settings.json: one project, can be committed and shared..claude/settings.local.json: one project, yours only.
Every event is a key inside one hooks object, so add a new event next to the existing ones, not over them. Type /hooks in a session to see what is configured.
1. A notification when Claude needs you
The Notification event fires when Claude is waiting for input or permission. On Linux:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}
On Windows, swap the command for this one. It opens a dialog box, which can appear behind your terminal:
"command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
On macOS use osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'. The empty matcher fires on every notification type. Use permission_prompt to fire only when Claude is waiting for you to approve something, or idle_prompt when it finished and you have not typed for about a minute.
2. Format files after Claude edits them
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
The hook receives the event as JSON on stdin. jq pulls out the path of the edited file and Prettier rewrites it. The matcher limits it to the file-editing tools. You need jq installed. When the hook succeeds it prints nothing, so check the file, not the conversation. A PostToolUse hook runs after the edit, so it cannot undo anything.
3. Block edits to protected files
Save this as .claude/hooks/protect-files.sh:
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
for pattern in ".env" "package-lock.json" ".git/"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
On macOS and Linux run chmod +x .claude/hooks/protect-files.sh. Then register it in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
On Windows, paths come with backslashes, so normalise them to forward slashes before matching, as Anthropic's version of this script does.
How a hook answers
- Input: the event as JSON on stdin. For a Bash call,
tool_input.commandholds the command. For a file edit,tool_input.file_pathholds the path. - Exit 0: no objection. On
PreToolUsethat is not approval; the normal permission flow still runs. - Exit 2: block, and what you wrote to stderr goes back to Claude so it can change course.
- JSON on stdout with exit 0 gives finer control, such as a deny with a reason. Choose exit 2 or JSON for one hook, not both.
Hook, rule or CLAUDE.md?
A line in CLAUDE.md is a request. A permission rule is enforced by the program. A PreToolUse hook can do the same with your own logic, and its deny holds in every permission mode, including bypassPermissions, according to Anthropic's documentation.
Limits
- A hook is a shell command that runs with your permissions. Read a hook before you add it, the same as any script.
Stophooks fire whenever Claude finishes responding, not only when the whole task is done.- Command hooks talk only through stdout, stderr and exit codes. They cannot run
/commands or call tools. - If a hook does not fire, run
/hooksto confirm it sits under the right event, and check that the matcher matches the tool name exactly. Matchers are case-sensitive.
Check it works
- Add one hook at a time to
.claude/settings.local.jsonin a scratch project. - Run
/hooksand confirm it is listed under the right event. - Trigger it on purpose: ask Claude to edit a
.envfile, or to add a badly formatted line.
This follows Anthropic's hooks guide as we read it in October 2026. That page is the authority, and we are not affiliated with Anthropic. We wrote this from the documentation and have not tested every example on every system.
If you want a second pair of eyes on your CLAUDE.md and settings, the Tune-Up Kit is a checklist for exactly that.
See also: Work toward a goal with /goal.