How do I run my formatter automatically after Claude edits a file?
A PostToolUse hook on Edit and Write runs prettier (or any formatter) so Claude's output matches your house style.
Hooks run outside of Claude — they’re shell commands the harness executes around tool calls. That makes them perfect for guardrails Claude shouldn’t be trusted to enforce on its own.
The pattern
.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I {} npx prettier --write --ignore-unknown {}"
}
]
}
]
}
}
Walking through it:
PostToolUsefires after a tool call returns.matcherpicks which tools we care about — file-editing ones.commandreceives the tool’s JSON payload on stdin. We pull outfile_pathwithjqand pipe it toprettier --write.
Now every time Claude touches a file, prettier rewrites it. Claude sees the new contents on the next read and adapts.
Why hooks beat asking
You could put “always run prettier after editing” in CLAUDE.md. Claude will usually remember. The harness, with a hook, always does. Hooks are the right place for any rule you want enforced 100% of the time:
- Format on save.
- Block edits to
dist/ornode_modules/. - Run
tsc --noEmitafter writes and feed errors back as context. - Notify you when Claude finishes a long task.
Debugging a hook
If a hook misbehaves, two things help:
claude --debug
That prints every hook invocation and exit code. And:
echo '{"tool_input":{"file_path":"src/foo.ts"}}' | <your hook command>
Run the hook command by hand with a fake payload, see what happens.
Sources
- Hooks reference — Configuration schema, JSON input/output, all hook events
- Hooks guide — Worked examples and patterns