Repository navigation
Conversation
Three defects, all in the path a Windows agent actually takes.
**Hook input rejected outright.** Some Windows hosts prepend a UTF-8 BOM
to the JSON they write to hook stdin. `serde_json` refuses it, so
`run_claude` and `run_gemini` died on the first byte:
Failed to parse hook input as JSON: expected value at line 1 column 1
Strip leading BOMs before parsing. On input without one this is a no-op.
**The hook did not recognize itself.** `is_claude_hook_command` compared
the binary name against `"rtk"`, but on Windows the command reads
`rtk.exe hook claude`, so the check failed and the hook was treated as an
ordinary command.
**Audit log written somewhere unwritable.** Both the reader and the
writer hardcoded `$HOME/.local/share/rtk`, falling back to `/tmp` -- and
`HOME` is often unset on Windows while `/tmp` does not exist. The two
also disagreed: only the reader honoured `RTK_AUDIT_DIR`.
Resolution now lives in one `hooks::audit_dir()` that both call, so they
cannot drift again. It uses `%LOCALAPPDATA%\rtk` on Windows and keeps the
historic `$XDG_DATA_HOME` / `$HOME/.local/share` on Unix -- deliberately
not `dirs::data_local_dir()`, which is `~/Library/Application Support` on
macOS and would silently relocate an existing user's log.
Also accepts `powershell`, `pwsh` and `cmd` as terminal tool names
alongside `bash`, since that is what Windows hosts report.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
BOM stripping landed for Gemini in #3751 (merged 2026-08-30) and for the Claude hook in #3008 (merged 2026-09-19), and #3552 (merged 2026-09-13) added rtk.exe matching through is_rtk_binary in src/hooks/mod.rs. What develop still lacks from this branch: a shared audit directory (RTK_AUDIT_DIR, XDG_DATA_HOME, %LOCALAPPDATA% on Windows) used by both the writer in hook_cmd.rs and the reader in hook_audit_cmd.rs, which still disagree; powershell, pwsh and cmd accepted as terminal tool names in detect_format and run_vibe_inner; and matching the raw leading token so uppercase RTK.EXE and unquoted C:...\rtk.exe paths are recognised, which would now go into the shared is_rtk_hook_command in src/hooks/mod.rs. Could you rebase onto current develop and narrow the PR to those three parts? It conflicts with develop in the hook files. Thanks for all the Windows testing behind this. |
|
Thank you for this work. BOM stripping on the Claude and Gemini hook inputs landed in #3751 (merged 2026-08-30), rtk.exe is recognised through the shared hook command check from #3552 (merged 2026-09-13), and Copilot on Windows already reaches the hook as Bash per #3212 (merged 2026-08-02); since #3758 (merged 2026-10-01) user paths must resolve through src/core/user_dirs.rs, so the audit_dir helper here would need to be redone on that model. Develop still does not match an unquoted C:...\rtk.exe path or an uppercase RTK.EXE in the hook command (src/hooks/mod.rs:21), and an issue or a narrow PR against is_rtk_hook_command would be welcome; the audit log writer and reader disagreeing on RTK_AUDIT_DIR is tracked in #1681. If I missed a part of this, please comment and we can reopen. |
Problem
Three defects, all on the path a Windows agent actually takes.
1. Hook input rejected outright
Some Windows hosts prepend a UTF-8 BOM to the JSON they write to hook stdin.
serde_jsonrefuses it, sorun_claudeandrun_geminidied on the first byte:strip_leading_bomalready exists in this file for exactly this reason (the Cursor hook), it just was not applied to the Claude and Gemini entry points. On input without a BOM it is a no-op.2. The hook did not recognize itself
is_claude_hook_commandcompared the binary name against"rtk". On Windows the command readsrtk.exe hook claude, so the check failed and the hook was treated as an ordinary command.Fixing the
.exesuffix alone was not enough.shell_splitapplies POSIX unquoting, which consumes the separators in a Windows path —C:\Users\me\...\rtk.exearrives asC:Usersme...rtk.exe, leaving nothing for the existingrsplit(['/', '\'])to find. The binary's name is now taken from the raw command text instead, so absolute Windows paths (quoted or not) match.3. Audit log written somewhere unwritable
Reader and writer both hardcoded
$HOME/.local/share/rtk, falling back to/tmp. On WindowsHOMEis frequently unset and/tmpdoes not exist. The two also disagreed — only the reader honouredRTK_AUDIT_DIR.Resolution now lives in one
hooks::audit_dir()that both call, so they cannot drift again.Also accepts
powershell,pwshandcmdas terminal tool names alongsidebash, since that is what Windows hosts report.Testing
New test
claude_hook_command_matches_windows_exe_and_backslash_pathcovers barertk.exe, uppercaseRTK.EXE, and a fullC:\...\rtk.exepath — the last of which failed until the raw-token fix above. Existingstrip_leading_bomandis_claude_hook_commandtests still pass unchanged.Full suite on
x86_64-pc-windows-gnu: 2616 passed.cargo fmt --checkandcargo clippy --all-targetsclean. The single unrelated failure on this toolchain is fixed in #3615.Not included
The Gemini hook wrapper is still generated as
#!/bin/bash. Making it a Windows batch file also requires renamingrtk-hook-gemini.shand teachinghook_checkthe new name, and I could not verify how the Gemini CLI invokes that file. Left for a separate, properly-verified PR rather than guessing.🤖 Generated with Claude Code