From a3a432e3cf5d2de084cecb684229b14ccd4969ac Mon Sep 17 00:00:00 2001 From: Tommaso Sciortino Date: Thu, 31 Jul 2025 17:27:07 -0700 Subject: [PATCH] Fix bug executing commands in windows whose flags contain spaces (#5317) --- .../services/shellExecutionService.test.ts | 30 ++++++++++++------- .../src/services/shellExecutionService.ts | 11 ++++--- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/core/src/services/shellExecutionService.test.ts b/packages/core/src/services/shellExecutionService.test.ts index 4d1655a2..cfce08d2 100644 --- a/packages/core/src/services/shellExecutionService.test.ts +++ b/packages/core/src/services/shellExecutionService.test.ts @@ -91,9 +91,9 @@ describe('ShellExecutionService', () => { }); expect(mockSpawn).toHaveBeenCalledWith( - 'bash', - ['-c', 'ls -l'], - expect.any(Object), + 'ls -l', + [], + expect.objectContaining({ shell: 'bash' }), ); expect(result.exitCode).toBe(0); expect(result.signal).toBeNull(); @@ -334,23 +334,31 @@ describe('ShellExecutionService', () => { describe('Platform-Specific Behavior', () => { it('should use cmd.exe on Windows', async () => { mockPlatform.mockReturnValue('win32'); - await simulateExecution('dir', (cp) => cp.emit('exit', 0, null)); + await simulateExecution('dir "foo bar"', (cp) => + cp.emit('exit', 0, null), + ); expect(mockSpawn).toHaveBeenCalledWith( - 'cmd.exe', - ['/c', 'dir'], - expect.objectContaining({ detached: false }), + 'dir "foo bar"', + [], + expect.objectContaining({ + shell: true, + detached: false, + }), ); }); it('should use bash and detached process group on Linux', async () => { mockPlatform.mockReturnValue('linux'); - await simulateExecution('ls', (cp) => cp.emit('exit', 0, null)); + await simulateExecution('ls "foo bar"', (cp) => cp.emit('exit', 0, null)); expect(mockSpawn).toHaveBeenCalledWith( - 'bash', - ['-c', 'ls'], - expect.objectContaining({ detached: true }), + 'ls "foo bar"', + [], + expect.objectContaining({ + shell: 'bash', + detached: true, + }), ); }); }); diff --git a/packages/core/src/services/shellExecutionService.ts b/packages/core/src/services/shellExecutionService.ts index 0f0002cd..d1126a7d 100644 --- a/packages/core/src/services/shellExecutionService.ts +++ b/packages/core/src/services/shellExecutionService.ts @@ -89,13 +89,16 @@ export class ShellExecutionService { abortSignal: AbortSignal, ): ShellExecutionHandle { const isWindows = os.platform() === 'win32'; - const shell = isWindows ? 'cmd.exe' : 'bash'; - const shellArgs = [isWindows ? '/c' : '-c', commandToExecute]; - const child = spawn(shell, shellArgs, { + const child = spawn(commandToExecute, [], { cwd, stdio: ['ignore', 'pipe', 'pipe'], - detached: !isWindows, // Use process groups on non-Windows for robust killing + // Use bash unless in Windows (since it doesn't support bash). + // For windows, just use the default. + shell: isWindows ? true : 'bash', + // Use process groups on non-Windows for robust killing. + // Windows process termination is handled by `taskkill /t`. + detached: !isWindows, env: { ...process.env, GEMINI_CLI: '1',