From 6ca446bdeda359339b20aaf024d5cce4234bfe9a Mon Sep 17 00:00:00 2001 From: Taylor Mullen Date: Tue, 20 May 2025 00:21:01 -0700 Subject: fix(cli): Prevent truncation of first character in shell commands - The shell command processor was incorrectly truncating the first character of the command (e.g., 'ls' became 's') due to an erroneous `slice(1)` operation, likely introduced during a previous merge. This change removes the slice, ensuring the full command is processed. - Introduces unit tests for the shellCommandProcessor hook. - Fixes a minor grammatical issue in the display of GEMINI.md file count. --- packages/cli/src/ui/hooks/shellCommandProcessor.ts | 64 +++++++++++++--------- 1 file changed, 37 insertions(+), 27 deletions(-) (limited to 'packages/cli/src/ui/hooks/shellCommandProcessor.ts') diff --git a/packages/cli/src/ui/hooks/shellCommandProcessor.ts b/packages/cli/src/ui/hooks/shellCommandProcessor.ts index 5e43712b..35935e7d 100644 --- a/packages/cli/src/ui/hooks/shellCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/shellCommandProcessor.ts @@ -4,7 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { exec as _exec } from 'child_process'; +import { exec as defaultExec } from 'child_process'; +import type { exec as ExecType } from 'child_process'; import { useCallback } from 'react'; import { Config } from '@gemini-code/server'; import { type PartListUnion } from '@google/genai'; @@ -13,6 +14,7 @@ import crypto from 'crypto'; import path from 'path'; import os from 'os'; import fs from 'fs'; + /** * Hook to process shell commands (e.g., !ls, $pwd). * Executes the command in the target directory and adds output/errors to history. @@ -22,6 +24,7 @@ export const useShellCommandProcessor = ( onExec: (command: Promise) => void, onDebugMessage: (message: string) => void, config: Config, + executeCommand: typeof ExecType = defaultExec, // Injectable for testing ) => { /** * Checks if the query is a shell command, executes it, and adds results to history. @@ -33,7 +36,7 @@ export const useShellCommandProcessor = ( return false; } - let commandToExecute = rawQuery.trim().slice(1).trimStart(); + let commandToExecute = rawQuery.trim().trimStart(); // wrap command to write pwd to temporary file const pwdFileName = `shell_pwd_${crypto.randomBytes(6).toString('hex')}.tmp`; @@ -48,7 +51,7 @@ export const useShellCommandProcessor = ( userMessageTimestamp, ); - if (!commandToExecute) { + if (rawQuery.trim() === '') { addItemToHistory( { type: 'error', text: 'Empty shell command.' }, userMessageTimestamp, @@ -65,37 +68,44 @@ export const useShellCommandProcessor = ( }; const execPromise = new Promise((resolve) => { - _exec(commandToExecute, execOptions, (error, stdout, stderr) => { - if (error) { - addItemToHistory( - { type: 'error', text: error.message }, - userMessageTimestamp, - ); - } else { - let output = ''; - if (stdout) output += stdout; - if (stderr) output += (output ? '\n' : '') + stderr; // Include stderr as info + executeCommand( + commandToExecute, + execOptions, + (error, stdout, stderr) => { + if (error) { + addItemToHistory( + { type: 'error', text: error.message }, + userMessageTimestamp, + ); + } else { + let output = ''; + if (stdout) output += stdout; + if (stderr) output += (output ? '\n' : '') + stderr; // Include stderr as info - addItemToHistory( - { type: 'info', text: output || '(Command produced no output)' }, - userMessageTimestamp, - ); - } - if (fs.existsSync(pwdFilePath)) { - const pwd = fs.readFileSync(pwdFilePath, 'utf8').trim(); - if (pwd !== targetDir) { addItemToHistory( { type: 'info', - text: `WARNING: shell mode is stateless; \`cd ${pwd}\` will not apply to next command`, + text: output || '(Command produced no output)', }, userMessageTimestamp, ); } - fs.unlinkSync(pwdFilePath); - } - resolve(); - }); + if (fs.existsSync(pwdFilePath)) { + const pwd = fs.readFileSync(pwdFilePath, 'utf8').trim(); + if (pwd !== targetDir) { + addItemToHistory( + { + type: 'info', + text: `WARNING: shell mode is stateless; \`cd ${pwd}\` will not apply to next command`, + }, + userMessageTimestamp, + ); + } + fs.unlinkSync(pwdFilePath); + } + resolve(); + }, + ); }); try { @@ -106,7 +116,7 @@ export const useShellCommandProcessor = ( return true; // Command was initiated }, - [config, onDebugMessage, addItemToHistory, onExec], + [config, onDebugMessage, addItemToHistory, onExec, executeCommand], ); return { handleShellCommand }; -- cgit v1.2.3