fix(warp): Windows Git Bash/MSYS2 — probe tty, preserve paths, degrade without jq - #88
Open
jacek4yang wants to merge 1 commit into
Open
jacek4yang wants to merge 1 commit into
jacek4yang wants to merge 1 commit into
Conversation
jacek4yang
force-pushed
the
fix/windows-msys2-tty-jq
branch
from
September 15, 2026 14:27
e1d63c4 to
7c7cfc1
Compare
Author
|
Squashed to a single commit and trimmed to a 44-line script diff (3 files) plus tests. Verified on Windows 11 + Git Bash/MSYS2: 74 passed / 0 failed (was 55 passed / 2 failed on |
…q fallback
Three independent failures on Windows Git Bash/MSYS2, all reproduced locally:
1. Every hook leaks an error to stderr, which Claude Code surfaces to the user:
emit-terminal-sequence.sh: line 82: /dev/tty: No such device or address
Shell redirections apply left to right, so `> /dev/tty 2>/dev/null` opens the
device while stderr is still inherited and the failure escapes before
2>/dev/null takes effect. A MSYS2 /dev/tty node also exists but returns ENXIO
on a detached process, so checking for the node is not enough.
2. cwd and transcript_path are silently rewritten. Git Bash converts path-like
arguments before jq sees them: "/Users/alice/my-project" became
"D:/.../Users/alice/my-project" and "/tmp/transcript.jsonl" became
"C:/Users/.../Temp/transcript.jsonl". This is also why test-hooks.sh reports
55 passed / 2 failed on Windows today.
3. A missing jq surfaces as "jq: command not found" on stderr.
Fixes:
- Add _tty_write(), which redirects stderr before opening /dev/tty
(`printf '%s' "$1" 2>/dev/null > /dev/tty`). Probing and writing in one call
keeps the write safe if the tty disappears in between.
- Set MSYS2_ARG_CONV_EXCL='*' on the jq calls so path-like arguments survive.
jq treats them as opaque strings; ignored on Linux/macOS.
- Guard jq in build_payload (sourced, so `return` not `exit`) and in
emit_terminal_sequence, both degrading silently.
Tests assert externally visible behavior: exact payload path values, exit
status, and zero stderr for all seven hooks and all three version branches when
detached. Mutation-checked: removing MSYS2_ARG_CONV_EXCL fails 4 tests,
reverting _tty_write to the unsafe order fails 10, removing the jq guard fails 1.
Run against unmodified main, 16 of 17 new assertions fail.
Windows 11 + Git Bash: 74 passed / 0 failed (was 55/2), 0 bytes stderr.
jacek4yang
force-pushed
the
fix/windows-msys2-tty-jq
branch
from
September 15, 2026 14:28
7c7cfc1 to
3c1fb3d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Windows 11 + Git Bash/MSYS2 the Warp hooks break in three ways, all reproduced locally:
cwdandtranscript_pathare silently rewritten in the payload. Git Bash converts path-like arguments beforejqsees them, so/Users/alice/my-projectbecameD:/.../Users/alice/my-projectand/tmp/transcript.jsonlbecameC:/Users/.../Temp/transcript.jsonl.jqsurfaces asjq: command not foundon stderr.Failure 2 is also why
tests/test-hooks.shreports 55 passed / 2 failed on Windows today (noted as "unrelated" in #57).Fix
Three small changes, 44 lines of script across 3 files:
_tty_write()(emit-terminal-sequence.sh) — writes to/dev/ttywith stderr redirected before the open:printf '%s' "$1" 2>/dev/null > /dev/tty. Redirections apply left to right, so the old> /dev/tty 2>/dev/nullopened the device while stderr was still inherited and leaked the error. Probing and writing in one call also means the write stays safe if the tty goes away in between. A MSYS2/dev/ttynode exists but returns ENXIO on a detached process, so node existence alone is not a usable check.MSYS2_ARG_CONV_EXCL='*'(build-payload.sh) — stops Git Bash rewriting path-like arguments.jqtreats them as opaque strings. No-op on Linux/macOS.build_payloadreturns an empty body (callers read that as "no notification") andemit_terminal_sequencereturns early.build-payload.shis sourced, so it returns rather than exits.Linux/macOS behavior is unchanged.
Verification
Windows 11, Git Bash/MSYS2 (MINGW64_NT-10.0-26200, bash 3.6.10):
bash tests/test-hooks.shjqremoved fromPATH--arg/--argjsonbash -non all scriptsTests use the existing
assert_eq/assert_json_fieldhelpers and assert externally visible behavior: exact payload field values, exit status, and zero stderr.Mutation testing (each retained test detects its regression)
MSYS2_ARG_CONV_EXCL→ 4 failures (incl. the 2 pre-existing ones)_tty_writeto> /dev/tty 2>/dev/null→ 10 failuresRunning the new tests against unmodified
mainfails 16 of 17, so none are vacuous. One assertion is a deliberate control: it checks that the old unguarded pattern does leak on a host with no usable tty, so the zero-stderr assertions cannot pass for the wrong reason.Existing PRs
_is_windows()(a platform guess rather than probing whether the terminal is usable), and does not address the MSYS2 path rewriting that breakscwd/transcript_path.2>/dev/null > /dev/tty. This PR makes the same ordering fix plus capability probing and path preservation.emit_terminal_sequencesites; this PR routes them through a shared helper and also coverslegacy/warp-notify.sh.hooks.jsonbash invocation, complementary and non-overlapping.Happy to drop this in favor of any of the above if a maintainer prefers to land one first; the MSYS2 path preservation (
MSYS2_ARG_CONV_EXCL) is the piece not covered elsewhere.