Skip to content

Commit a83dcac

Browse files
committed
Add PostToolUse hook to format files with Prettier on write
Runs the workspace Prettier on each file Claude Code writes or edits inside the repo, using --ignore-unknown so it's a no-op on unsupported files and honors .prettierignore. Keeps the tree formatted without a separate pass; CI's prettier:check stays the source of truth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvxEaPhyEq2HCZ6XFjEAde
1 parent 960f89b commit a83dcac

3 files changed

Lines changed: 51 additions & 0 deletions

File tree

.claude/hooks/format-file.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
#
3+
# PostToolUse hook: formats a file with Prettier right after Claude Code writes
4+
# or edits it, so formatting never has to be fixed up later (or caught by CI).
5+
#
6+
# It runs the workspace's pinned Prettier with `--ignore-unknown`, so it is a
7+
# no-op on files Prettier can't format and it honors .prettierignore — meaning
8+
# it is safe to run on every write regardless of file type.
9+
set -euo pipefail
10+
11+
ROOT="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
12+
13+
# The hook payload arrives as JSON on stdin; the edited path is tool_input.file_path.
14+
FILE_PATH="$(node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);process.stdout.write(String((j.tool_input&&j.tool_input.file_path)||""))}catch{process.stdout.write("")}})' 2>/dev/null || true)"
15+
16+
# Nothing to do without a real, existing file.
17+
[ -n "$FILE_PATH" ] && [ -f "$FILE_PATH" ] || exit 0
18+
19+
# Only format files inside the repo (skip scratchpad/other locations).
20+
ABS_FILE="$(cd "$(dirname "$FILE_PATH")" 2>/dev/null && pwd)/$(basename "$FILE_PATH")" || exit 0
21+
case "$ABS_FILE" in
22+
"$ROOT"/*) ;;
23+
*) exit 0 ;;
24+
esac
25+
26+
# Use the workspace Prettier if dependencies are installed; otherwise skip.
27+
PRETTIER="$ROOT/node_modules/.bin/prettier"
28+
[ -x "$PRETTIER" ] || exit 0
29+
30+
# Never fail the tool call over formatting.
31+
"$PRETTIER" --ignore-unknown --write "$ABS_FILE" >/dev/null 2>&1 || true
32+
exit 0

.claude/settings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@
99
}
1010
]
1111
}
12+
],
13+
"PostToolUse": [
14+
{
15+
"matcher": "Write|Edit|MultiEdit",
16+
"hooks": [
17+
{
18+
"type": "command",
19+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format-file.sh"
20+
}
21+
]
22+
}
1223
]
1324
}
1425
}

CLAUDE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ ready to build, lint, and test the Node.js tooling packages.
1919

2020
See the "Environment & Bootstrap" section of `AGENTS.md` for the manual steps and
2121
for why native iOS/Android builds are not part of the default bootstrap.
22+
23+
## Formatting
24+
25+
A `PostToolUse` hook (`.claude/hooks/format-file.sh`) runs the workspace's
26+
Prettier on every file Claude Code writes or edits inside the repo. It uses
27+
`--ignore-unknown`, so it is a no-op on files Prettier can't format and it honors
28+
`.prettierignore` — keeping the tree formatted without a separate `prettier:write`
29+
pass. It's a convenience only; CI's `prettier:check` remains the source of truth.

0 commit comments

Comments
 (0)