Claude Code
Claude Code hooks: format on save, block risky edits, get notified
How do I run a command automatically when Claude Code edits a file?
Add a hooks block to .claude/settings.json (project) or ~/.claude/settings.json (user). A PostToolUse hook with matcher "Edit|Write" runs after every file edit; a PreToolUse hook that exits with code 2 blocks the action and sends its stderr back as the reason. Type /hooks in Claude Code to see what is configured.
Format every file Claude edits
.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}Block edits to protected files
Hooks receive the tool call as JSON on stdin. Exit 0 to allow, exit 2 to block with a reason on stderr. Make the script executable with chmod +x.
.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.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" }
]
}
]
}
}Other useful events
- Notification: fires when Claude is waiting for input or permission; pair it with a desktop notification command.
- SessionStart with matcher "compact": re-inject key context after the conversation is compacted.
- Hooks are deterministic: use them for rules that must always hold, and CLAUDE.md for guidance.
Sources
More on Claude Code
Other guides
Get the weekly agent stack update
New official MCP servers, spec changes and harness releases, checked against the source. One email a week, no fluff.
Reviewed Oct 6, 2026. Settings change often; the linked vendor docs are the source of truth.