-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathposttooluse_writeedit.py
More file actions
102 lines (81 loc) · 2.61 KB
/
Copy pathposttooluse_writeedit.py
File metadata and controls
102 lines (81 loc) · 2.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env python3
"""PostToolUse hook for Write | Edit — formatter dispatch.
After a successful Write or Edit, format the touched file in place:
- `.py` -> `uv run ruff check --fix` + `uv run ruff format`
- `.ts .tsx .js .jsx .json` -> `npx --no-install prettier -w` (from frontend/)
- `.css .html .md` -> `npx --no-install prettier -w` (from frontend/)
Silent on failure — the write already landed; formatting is best-effort. Exit code
is always 0 so Claude Code never rolls back the edit over a missing formatter.
"""
from __future__ import annotations
import contextlib
import json
import os
import subprocess
import sys
from pathlib import Path
PY_EXTENSIONS = {".py"}
PRETTIER_EXTENSIONS = {
".ts",
".tsx",
".js",
".jsx",
".json",
".css",
".html",
".md",
}
def project_dir() -> Path:
env = os.environ.get("CLAUDE_PROJECT_DIR")
if env:
return Path(env)
return Path(__file__).resolve().parent.parent.parent
def run_quiet(cmd: list[str], cwd: Path) -> None:
with contextlib.suppress(FileNotFoundError, subprocess.TimeoutExpired):
subprocess.run(
cmd,
cwd=cwd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
timeout=60,
)
def format_python(path: Path, base: Path) -> None:
run_quiet(["uv", "run", "ruff", "check", "--fix", str(path)], cwd=base)
run_quiet(["uv", "run", "ruff", "format", str(path)], cwd=base)
def format_prettier(path: Path, base: Path) -> None:
frontend = base / "frontend"
if not frontend.is_dir():
return
# --no-install so a checkout without prettier installed silently no-ops
# instead of triggering an npx download.
run_quiet(
["npx", "--no-install", "prettier", "-w", str(path)],
cwd=frontend,
)
def main() -> None:
try:
payload = json.load(sys.stdin)
except json.JSONDecodeError:
return
if not isinstance(payload, dict):
return
tool_input = payload.get("tool_input")
if not isinstance(tool_input, dict):
return
file_path = tool_input.get("file_path", "")
if not isinstance(file_path, str) or not file_path:
return
path = Path(file_path)
if not path.is_absolute():
path = project_dir() / path
if not path.exists():
return
suffix = path.suffix.lower()
base = project_dir()
if suffix in PY_EXTENSIONS:
format_python(path, base)
elif suffix in PRETTIER_EXTENSIONS:
format_prettier(path, base)
if __name__ == "__main__":
main()