diff --git a/.claude/hooks/pretooluse-bom-guard.sh b/.claude/hooks/pretooluse-bom-guard.sh index c94d3d508..b8430b2a1 100755 --- a/.claude/hooks/pretooluse-bom-guard.sh +++ b/.claude/hooks/pretooluse-bom-guard.sh @@ -81,10 +81,14 @@ Strip it, then re-run this command: python3 - <<'EOF' import subprocess - fs = subprocess.run(['git','diff','--name-only','origin/main...HEAD','--','*.cs'], - capture_output=True, text=True).stdout.split() - for f in fs: - b = open(f,'rb').read() + def g(*a): return subprocess.run(['git','diff','--name-only',*a,'--','*.cs'], + capture_output=True, text=True).stdout.split() + # same detection set as the guard: branch diff + staged + dirty (a brand-new staged + # file is exactly what fires the deny and is absent from origin/main...HEAD) + fs = set(g('origin/main...HEAD')) | set(g('--cached')) | set(g()) + for f in sorted(fs): + try: b = open(f,'rb').read() + except OSError: continue if b[:3] == b'\xef\xbb\xbf': open(f,'wb').write(b[3:]); print('stripped', f) EOF diff --git a/docs/contributing.md b/docs/contributing.md index 0c72f3006..30a1b0c38 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -117,7 +117,7 @@ talks exclusively to the REST API. Every former Blazor route now 302-redirects t `io.open(..., encoding='utf-8-sig')` *writes one back*, and sed/perl round-trips keep it. This cost two sessions a red CI job on 2026-07-17 alone (PR #405, 6 files; PR #402, 19), which is what the guard is for. To check by hand: - `for f in $(git diff --name-only origin/main...HEAD -- '*.cs'); do head -c3 "$f" | xxd -p | grep -q '^efbbbf' && echo "BOM: $f"; done` + `for f in $(git diff --name-only origin/main...HEAD --cached -- '*.cs'; git diff --name-only -- '*.cs') ; do head -c3 "$f" | xxd -p | grep -q '^efbbbf' && echo "BOM: $f"; done` (covers branch + staged + dirty — a brand-new staged file is absent from `origin/main...HEAD`) (note `dotnet format --include` needs **bash** — `mapfile` is bash-only, and under zsh the file list comes out empty, which looks exactly like the tool silently doing nothing). (A one-time repo-wide normalization is a separate, unmade decision; the touched-file rule is the standing one.)