diff --git a/.changeset/fix-windows-status-line-quotes.md b/.changeset/fix-windows-status-line-quotes.md new file mode 100644 index 0000000000..f389bad8cd --- /dev/null +++ b/.changeset/fix-windows-status-line-quotes.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix status line commands with quoted arguments on Windows. diff --git a/apps/kimi-code/src/tui/utils/status-line-command.ts b/apps/kimi-code/src/tui/utils/status-line-command.ts index 6482a4ac7c..cd3fd4ab7d 100644 --- a/apps/kimi-code/src/tui/utils/status-line-command.ts +++ b/apps/kimi-code/src/tui/utils/status-line-command.ts @@ -43,13 +43,20 @@ export function runStatusLineCommand( const isWin = process.platform === 'win32'; let child; try { - child = spawn(isWin ? (process.env['ComSpec'] ?? 'cmd.exe') : 'sh', isWin ? ['/d', '/s', '/c', command] : ['-c', command], { - stdio: ['pipe', 'pipe', 'ignore'], - env: { ...process.env, KIMI_CODE_STATUS_LINE: '1' }, - // Own process group on POSIX so a timeout can drop the whole tree, - // not just the shell wrapper. - detached: !isWin, - }); + child = spawn( + isWin ? (process.env['ComSpec'] ?? 'cmd.exe') : 'sh', + isWin ? ['/d', '/s', '/c', `"${command}"`] : ['-c', command], + { + stdio: ['pipe', 'pipe', 'ignore'], + env: { ...process.env, KIMI_CODE_STATUS_LINE: '1' }, + // Own process group on POSIX so a timeout can drop the whole tree, + // not just the shell wrapper. + detached: !isWin, + // Preserve the nested quotes in cmd /s /c "" instead of + // letting libuv escape them as literal backslashes. + windowsVerbatimArguments: isWin, + }, + ); } catch { finish(null); return; diff --git a/apps/kimi-code/test/tui/components/chrome/footer-status-line.test.ts b/apps/kimi-code/test/tui/components/chrome/footer-status-line.test.ts index a6be39adfb..3190da0806 100644 --- a/apps/kimi-code/test/tui/components/chrome/footer-status-line.test.ts +++ b/apps/kimi-code/test/tui/components/chrome/footer-status-line.test.ts @@ -134,6 +134,15 @@ describe('FooterComponent status_line items', () => { }); describe('runStatusLineCommand', () => { + it.runIf(process.platform === 'win32')( + 'preserves double quotes in Windows commands', + async () => { + const command = `"${process.execPath}" -e "process.stdout.write('quoted-ok')"`; + + expect(await runStatusLineCommand(command, payload, 5_000)).toBe('quoted-ok'); + }, + ); + it('passes the payload as JSON on stdin and returns the first stdout line', async () => { const line = await runStatusLineCommand('cat', payload);