Content
1#!/usr/bin/env node
2// Hook type: PostToolUse
3// Trigger: Write, Edit (file tool calls)
4//
5// Reads the tool result from stdin, extracts the file path,
6// and runs Prettier on it if a Prettier config is present.
7
8import { execSync } from "child_process"
9import { existsSync } from "fs"
10import { join } from "path"
11import { readFileSync } from "fs"
12
13const input = JSON.parse(readFileSync("/dev/stdin", "utf8"))
14
15// Only act on Write / Edit tool calls
16const toolName = input?.tool_name ?? ""
17if (!["Write", "Edit"].includes(toolName)) process.exit(0)
18
19const filePath = input?.tool_input?.file_path ?? input?.tool_input?.path
20if (!filePath) process.exit(0)
21
22// Detect Prettier config in project root (walk up from file)
23function findPrettierConfig(start: string): boolean {
24 const configs = [
25 ".prettierrc",
26 ".prettierrc.json",
27 ".prettierrc.js",
28 ".prettierrc.cjs",
29 "prettier.config.js",
30 "prettier.config.cjs",
31 "prettier.config.mjs",
32 ]
33 let dir = start
34 while (dir !== "/") {
35 if (configs.some((c) => existsSync(join(dir, c)))) return true
36 dir = join(dir, "..")
37 }
38 return false
39}
40
41const dir = filePath.replace(/\/[^\/]+$/, "")
42if (!findPrettierConfig(dir)) process.exit(0)
43
44try {
45 execSync(`npx prettier --write "${filePath}"`, { stdio: "inherit" })
46} catch {
47 // Non-fatal: Prettier may not support this file type
48}
49