claude-examples

Hooks

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.

By Kalle · intermediate · Updated 2026-04-26

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:

  1. PostToolUse fires after a tool call returns.
  2. matcher picks which tools we care about — file-editing ones.
  3. command receives the tool’s JSON payload on stdin. We pull out file_path with jq and pipe it to prettier --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/ or node_modules/.
  • Run tsc --noEmit after 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 formatting PostToolUse