Fix Windows statusLine hook never firing under Git Bash#68
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d0526c859
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| value = value.replace("\\", "/") | ||
| return subprocess.list2cmdline([value]) |
There was a problem hiding this comment.
Quote Windows commands for the shell
When Claude Code invokes this through Git Bash, subprocess.list2cmdline is still Windows argv quoting, not shell quoting: it leaves Bash metacharacters unescaped, so a valid path like C:\Users\R&D\.claude\usage-statusline.py becomes C:/Users/R&D/... and Bash treats & as a control operator before the hook runs. The statusLine docs say the command field runs in a shell, so after making the command Bash-compatible with forward slashes this also needs shell-safe quoting for Windows paths with characters such as &, $, or parentheses.
Useful? React with 👍 / 👎.
| if "usage-statusline-forwarder" in command: | ||
| new_command = _forwarder_command() | ||
| elif "usage-statusline" in command: | ||
| new_command = _statusline_command() |
There was a problem hiding this comment.
Migrate companion hook commands too
For Windows users who already enabled Resume or Terse mode before upgrading, the existing hooks.SessionStart/UserPromptSubmit commands were written by the old _shell_arg with backslash paths, but this migration only rewrites statusLine. Claude hooks are shell commands as well, so those opt-in hooks continue to fail under Git Bash until the user manually disables and re-enables them; self-heal should also rewrite usage-owned usage_session_resume, usage_terse_mode, and reminder hook commands.
Useful? React with 👍 / 👎.
Claude Code runs Windows statusLine commands through Git Bash when it is installed, where backslashes are escape characters — so the backslash-separated python.exe/script paths setup_hook.py wrote there silently failed to launch, and usage-status.json was never produced. _shell_arg now emits forward-slash paths on Windows (valid for both Git Bash and PowerShell). Existing installs are migrated automatically by self-heal and --setup. doctor.py's new "status command" line flags any remaining un-migrated backslash command. Also fixes a latent bug this exposed in _resume_restore_context: it compared the resume hook's installed command against str(Path) (backslash form) to classify whether the command still points at the source or target script, so on Windows that classification silently degraded to "other" once the command itself switched to forward slashes. Compares against as_posix() now.
8d0526c to
25e9d26
Compare
Summary
python.exe/script pathssetup_hook.pywrote into~/.claude/settings.json'sstatusLine.commandsilently failed to launch —~/.claude/usage-status.jsonwas never produced, and the app's Claude usage card had nothing to show._shell_argnow emits forward-slash paths on Windows (valid for both Git Bash and PowerShell). Existing installs are migrated automatically on the next self-heal pass or--setuprun.doctor.pygained astatus command:line that flags any remaining un-migrated backslash command, to make this diagnosable without reading source._resume_restore_context(session_hooks.py): it classified the installed resume-hook command by comparing againststr(Path)(backslash form), so on Windows that classification silently degraded toregistered=otheronce the command itself switched to forward slashes. Now compares against.as_posix().CHANGELOG.md/CHANGELOG.zh-TW.mdentries added under Unreleased.Test plan
ruff check— cleanmypy .— cleanpytest -v— 699 passed, 2 skipped (pre-existing, environment-specific)monkeypatchonly — no real~/.claude/accesstest_setup_hook_resume.py,test_setup_hook_terse.py,test_setup_hook_coexistence.py) that compared against the old backslash path format and broke once the fix legitimately changed the emitted command format🤖 Generated with Claude Code