Conversation
…d 9;5u output
When a Kitty-capable terminal (iTerm2, Kitty, WezTerm) is used, the CLI
enables the Kitty keyboard protocol at startup via ESC[>1u. On exit, the
protocol must be disabled with ESC[<u to restore the terminal's default
key encoding. Failing to do so leaves the terminal in Kitty mode: any
subsequent Ctrl+C press is encoded as ESC[99;5u, and since the shell does
not understand this sequence, it echoes the trailing '9;5u' as garbled
text.
Root cause: kittyProtocolDetector registered cleanup handlers for 'exit'
and 'SIGTERM', but omitted SIGINT. A process terminated via SIGINT (e.g.
kill -INT <pid>, a parent process sending SIGINT, or certain process
managers) would exit without disabling the protocol.
Fix:
1. Add process.on('SIGINT', disableProtocol) alongside the existing
'exit' and 'SIGTERM' handlers in kittyProtocolDetector.ts.
2. Export a new disableKittyProtocol() function for explicit call sites.
3. Call disableKittyProtocol() in the registerCleanup callback in
gemini.tsx before instance.unmount(), so the disable sequence is
written while stdout is fully operational regardless of exit path.
Fixes #3528
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
wenshao
left a comment
There was a problem hiding this comment.
[Critical] packages/cli/src/gemini.test.tsx:676 mocks ./ui/utils/kittyProtocolDetector.js with only detectAndEnableKittyProtocol, but gemini.tsx now imports the additional named export disableKittyProtocol. When the startInteractiveUI tests resolve that full-module mock or exercise cleanup, the mocked module is missing the newly imported export.
Suggested fix:
vi.mock('./ui/utils/kittyProtocolDetector.js', () => ({
detectAndEnableKittyProtocol: vi.fn(() => Promise.resolve(true)),
disableKittyProtocol: vi.fn(),
}));— gpt-5.5 via Qwen Code /review
|
Fixed in 692138e. Added |
1 similar comment
|
Fixed in 692138e. Added |
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅ — gpt-5.5 via Qwen Code /review
…d 9;5u output (QwenLM#3544) * fix(cli): disable Kitty keyboard protocol on SIGINT to prevent garbled 9;5u output When a Kitty-capable terminal (iTerm2, Kitty, WezTerm) is used, the CLI enables the Kitty keyboard protocol at startup via ESC[>1u. On exit, the protocol must be disabled with ESC[<u to restore the terminal's default key encoding. Failing to do so leaves the terminal in Kitty mode: any subsequent Ctrl+C press is encoded as ESC[99;5u, and since the shell does not understand this sequence, it echoes the trailing '9;5u' as garbled text. Root cause: kittyProtocolDetector registered cleanup handlers for 'exit' and 'SIGTERM', but omitted SIGINT. A process terminated via SIGINT (e.g. kill -INT <pid>, a parent process sending SIGINT, or certain process managers) would exit without disabling the protocol. Fix: 1. Add process.on('SIGINT', disableProtocol) alongside the existing 'exit' and 'SIGTERM' handlers in kittyProtocolDetector.ts. 2. Export a new disableKittyProtocol() function for explicit call sites. 3. Call disableKittyProtocol() in the registerCleanup callback in gemini.tsx before instance.unmount(), so the disable sequence is written while stdout is fully operational regardless of exit path. Fixes QwenLM#3528 * fix(test): add disableKittyProtocol to kittyProtocolDetector mock

TLDR
Fixes a bug (#3528) where exiting the CLI in a Kitty-capable terminal (iTerm2, Kitty, WezTerm) leaves the terminal in Kitty keyboard protocol mode. Subsequent Ctrl+C presses in the shell output garbled text like
9;5u9;5u9;5u.The root cause is that
kittyProtocolDetector.tsregistered cleanup on'exit'and'SIGTERM'but omittedSIGINT. Any termination via SIGINT skipped theESC[<udisable sequence entirely.Screenshots / Video Demo
Before fix (GLOBAL v0.15.0, unpatched — SIGINT path):
After fix (patched — SIGINT path):
Dive Deeper
How the bug manifests
When a Kitty-capable terminal is in use, the CLI sends
ESC[>1uat startup to enable the Kitty keyboard protocol. In this mode the terminal encodes Ctrl+C asESC[99;5uinstead of the traditional0x03.On a normal double-Ctrl+C exit the CLI calls
runExitCleanup()which goes through Node.js'sprocess.exit(), which fires the'exit'event, which callsdisableProtocol()and writesESC[<u— the terminal is restored. ✅However when the process is terminated via SIGINT (e.g.
kill -INT <pid>, a process manager, or certain shell integrations), Node.js delivers SIGINT, the process exits, but the'exit'event may not reliably emit the ESC[<u because the original code registered noSIGINThandler at all — the process just dies. The terminal stays in Kitty mode. Now in the shell, pressing Ctrl+C sendsESC[99;5u; the shell echoes the unrecognised suffix9;5uas literal text.Changes
packages/cli/src/ui/utils/kittyProtocolDetector.tsprocess.on('SIGINT', disableProtocol)alongside the existing'exit'and'SIGTERM'handlers so that SIGINT-based terminations also restore the terminal.disableKittyProtocol()function to allow explicit call sites.packages/cli/src/gemini.tsxdisableKittyProtocol()in theregisterCleanupcallback beforeinstance.unmount(). This ensures the disable escape sequence is written while stdout is still fully operational, regardless of exit path (defensive belt-and-suspenders over the signal handlers).Reviewer Test Plan
Automated E2E (recommended, no Kitty terminal required)
The test script uses
@lydell/node-ptyto simulate a Kitty-capable terminal by injecting the protocol detection responses, then sends SIGINT to the child process and checks whetherESC[<uwas emitted before the process died.Expected output after fix:
Manual verification (requires iTerm2 ≥ 3.5, Kitty, or WezTerm)
Reproducing the bug (unpatched
qwen):qwenand wait for the interactive prompt.kill -INT $(pgrep -f 'cli.js' | head -1)9;5u9;5u9;5uechoed after each keypress.Verifying the fix:
npm run build && npm run bundlenode dist/cli.jsand wait for the interactive prompt.kill -INT $(pgrep -f 'cli.js' | head -1)9;5uoutput.Manual test result (macOS arm64, iTerm2 3.5.10):
qwenv0.15.0Testing Matrix
Linked issues / bugs
Fixes #3528