From 7b3260e7577e0567876dd8dd75a7611c3da2ab98 Mon Sep 17 00:00:00 2001 From: IronRookieCoder Date: Mon, 18 May 2026 17:13:17 +0800 Subject: [PATCH] fix(ink): restore Git Bash raw input handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Bug 详情 Windows Git Bash 终端下,csc 4.1.x 的 REPL 输入异常:回车需要按两次,方向键无法选择历史对话,输入 / 无法正常显示或触发命令。 ## 根因 Windows raw mode 风险判断将 Git for Windows 的 MINGW64/MINGW32 与 MSYS2/Cygwin/mintty 环境一并禁用 raw mode,导致 Git Bash 走 cooked input fallback,Ink 无法按预期解析 Enter、方向键和 slash 输入。 ## 修复方案 将 Git for Windows 的 MINGW64/MINGW32 从 raw mode 不安全名单中排除,恢复 Git Bash 的 raw mode 输入处理,同时保留 MSYS/UCRT/CLANG/Cygwin 和显式禁用开关的保护路径。 ## 变更要点 - 调整 Ink raw mode 风险检测,Git Bash 保持 raw mode - 同步启动早期输入捕获的 Git Bash raw mode 判断 - 增加 Git Bash raw mode 回归测试,并保留 MSYS2/Cygwin 场景覆盖 ## 自测 - bun test packages/@ant/ink/src/core/__tests__/raw-mode-support.test.ts src/utils/__tests__/earlyInput.test.ts - git diff --check - bun run typecheck(失败,失败点为仓库既有 Zod 类型、缺失声明、MACRO.COMMIT、AppState 等错误,不涉及本次修改文件) --- .../core/__tests__/raw-mode-support.test.ts | 20 +++++++++++++++++- .../@ant/ink/src/core/raw-mode-support.ts | 21 +++++++++++++------ src/utils/earlyInput.ts | 14 +++++++------ 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/packages/@ant/ink/src/core/__tests__/raw-mode-support.test.ts b/packages/@ant/ink/src/core/__tests__/raw-mode-support.test.ts index d143573fa..c65e71ded 100644 --- a/packages/@ant/ink/src/core/__tests__/raw-mode-support.test.ts +++ b/packages/@ant/ink/src/core/__tests__/raw-mode-support.test.ts @@ -18,11 +18,29 @@ describe('isWindowsRawModeUnsafe', () => { ).toBe(false); }); + test('does not disable raw mode for Git Bash terminals', () => { + expect( + isWindowsRawModeUnsafe({ + MSYSTEM: 'MINGW64', + TERM_PROGRAM: 'mintty', + SHELL: '/usr/bin/bash', + }, 'win32'), + ).toBe(false); + expect( + isWindowsRawModeUnsafe({ + MSYSTEM: 'MINGW32', + TERM: 'xterm-256color', + }, 'win32'), + ).toBe(false); + }); + test('disables raw mode for MSYS2 and Cygwin terminals', () => { expect(isWindowsRawModeUnsafe({ MSYSTEM: 'UCRT64' }, 'win32')).toBe(true); + expect(isWindowsRawModeUnsafe({ MSYSTEM: 'CLANG64' }, 'win32')).toBe(true); + expect(isWindowsRawModeUnsafe({ MSYSTEM: 'MSYS' }, 'win32')).toBe(true); expect(isWindowsRawModeUnsafe({ TERM: 'cygwin' }, 'win32')).toBe(true); expect(isWindowsRawModeUnsafe({ TERM_PROGRAM: 'mintty' }, 'win32')).toBe( - true, + false, ); expect(isWindowsRawModeUnsafe({ SHELL: '/usr/bin/bash' }, 'win32')).toBe( false, diff --git a/packages/@ant/ink/src/core/raw-mode-support.ts b/packages/@ant/ink/src/core/raw-mode-support.ts index 054b8dfca..91e390567 100644 --- a/packages/@ant/ink/src/core/raw-mode-support.ts +++ b/packages/@ant/ink/src/core/raw-mode-support.ts @@ -15,18 +15,27 @@ export function isWindowsRawModeUnsafe( } const term = env.TERM?.toLowerCase() ?? ''; - const termProgram = env.TERM_PROGRAM?.toLowerCase() ?? ''; const msystem = env.MSYSTEM?.toLowerCase() ?? ''; const shell = env.SHELL?.toLowerCase() ?? ''; + // Git for Windows reports MSYSTEM=MINGW64/MINGW32 and often runs under + // mintty, but it needs raw mode for Ink key parsing. Disabling raw mode + // there makes Enter, arrows, and slash commands behave like cooked shell + // input. Keep the startup-crash workaround scoped to MSYS2/Cygwin-like + // environments and leave Git Bash on the normal raw-mode path. + const isGitForWindowsMingw = + msystem === 'mingw64' || msystem === 'mingw32'; + if (isGitForWindowsMingw) { + return false; + } + return ( term === 'cygwin' || - term.includes('mintty') || - termProgram.includes('mintty') || - msystem.length > 0 || + msystem === 'msys' || + msystem.startsWith('ucrt') || + msystem.startsWith('clang') || shell.includes('msys') || - shell.includes('cygwin') || - shell.includes('mingw') + shell.includes('cygwin') ); } diff --git a/src/utils/earlyInput.ts b/src/utils/earlyInput.ts index 2c535c416..08a988c94 100644 --- a/src/utils/earlyInput.ts +++ b/src/utils/earlyInput.ts @@ -31,18 +31,20 @@ function supportsEarlyInputRawMode(): boolean { } const term = process.env.TERM?.toLowerCase() ?? '' - const termProgram = process.env.TERM_PROGRAM?.toLowerCase() ?? '' const msystem = process.env.MSYSTEM?.toLowerCase() ?? '' const shell = process.env.SHELL?.toLowerCase() ?? '' + if (msystem === 'mingw64' || msystem === 'mingw32') { + return true + } + return !( term === 'cygwin' || - term.includes('mintty') || - termProgram.includes('mintty') || - msystem.length > 0 || + msystem === 'msys' || + msystem.startsWith('ucrt') || + msystem.startsWith('clang') || shell.includes('msys') || - shell.includes('cygwin') || - shell.includes('mingw') + shell.includes('cygwin') ) }