[Sync] llm stuff
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""PostToolUse hook: format an edited file with the right formatter.
|
||||
|
||||
Reads the hook JSON from stdin, extracts the edited file path, and runs a
|
||||
Reads the hook JSON from stdin, works out which file(s) were just edited, and runs a
|
||||
language-appropriate formatter -- but, to avoid imposing a style on a project
|
||||
that never asked for it, every formatter here only runs when the project opts in
|
||||
via a config file discoverable by walking up from the edited file: stylua, ruff,
|
||||
@@ -9,6 +9,15 @@ prettier and taplo on their own configs, gofmt and rustfmt on go.mod / Cargo.tom
|
||||
shfmt and fish_indent on .editorconfig. No marker, no formatting -- a repo that
|
||||
hand-formats its shell keeps its own layout.
|
||||
|
||||
Two entry points, both wired in settings.json > hooks > PostToolUse:
|
||||
* Edit|Write -- the edited path is `tool_input.file_path`.
|
||||
* Bash -- under `defaultMode: auto` Claude is steered to edit through the shell
|
||||
(sed -i, heredocs, python), which the Edit|Write matcher never sees.
|
||||
There is no per-file event for that (`FileChanged` only watches literal
|
||||
filenames), so this scans `tool_input.command` for path-like tokens with a
|
||||
handled extension and formats the ones modified in the last few seconds --
|
||||
the mtime gate keeps a parallel session's dirty files out of it.
|
||||
|
||||
Best-effort: always exits 0 so a format hiccup never blocks an edit. jq is not
|
||||
guaranteed on the host, hence python for the stdin JSON parse.
|
||||
"""
|
||||
@@ -19,6 +28,7 @@ import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def find_up(start, names):
|
||||
@@ -178,14 +188,13 @@ HANDLERS = {
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except Exception:
|
||||
return
|
||||
ti = data.get("tool_input") or {}
|
||||
tr = data.get("tool_response") or {}
|
||||
path = ti.get("file_path") or tr.get("filePath") or ""
|
||||
RECENT_SECS = 30 # a Bash edit is "ours" if the file changed this recently
|
||||
|
||||
_EXT_ALT = "|".join(re.escape(e[1:]) for e in HANDLERS)
|
||||
_PATHISH = re.compile(r"[\w.~/@+-]+\.(?:" + _EXT_ALT + r")\b")
|
||||
|
||||
|
||||
def format_one(path):
|
||||
if not path or not os.path.isfile(path):
|
||||
return
|
||||
# Resolve symlinks before walking for markers: deployed dotfiles get edited via
|
||||
@@ -204,5 +213,42 @@ def main():
|
||||
pass
|
||||
|
||||
|
||||
def recent_paths_in_command(command, cwd):
|
||||
"""Files a shell command plausibly just wrote: path-like tokens with a handled
|
||||
extension, resolved against the tool's cwd, that exist and changed within RECENT_SECS.
|
||||
Unchanged mentions (the source of a `cp`, a file merely grepped) fail the mtime gate."""
|
||||
now = time.time()
|
||||
seen, out = set(), []
|
||||
for tok in _PATHISH.findall(command or ""):
|
||||
p = os.path.expanduser(tok)
|
||||
if not os.path.isabs(p):
|
||||
p = os.path.join(cwd, p)
|
||||
p = os.path.normpath(p)
|
||||
if p in seen:
|
||||
continue
|
||||
seen.add(p)
|
||||
try:
|
||||
if os.path.isfile(p) and now - os.path.getmtime(p) <= RECENT_SECS:
|
||||
out.append(p)
|
||||
except OSError:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except Exception:
|
||||
return
|
||||
ti = data.get("tool_input") or {}
|
||||
tr = data.get("tool_response") or {}
|
||||
if data.get("tool_name") == "Bash":
|
||||
cwd = data.get("cwd") or os.getcwd()
|
||||
for p in recent_paths_in_command(ti.get("command"), cwd):
|
||||
format_one(p)
|
||||
return
|
||||
format_one(ti.get("file_path") or tr.get("filePath") or "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user