From 354732b5788f04d58f6d6f319e734670f8f3669e Mon Sep 17 00:00:00 2001 From: CJ Date: Sun, 30 Nov 2025 21:47:48 -0500 Subject: [PATCH 1/2] Fix Windows compatibility and deprecated -p flag - Set shell: true in spawn() to support Windows .cmd executables - Replace deprecated -p/--prompt flag with positional prompt - Add -y flag for non-interactive YOLO mode On Windows, gemini is installed as gemini.cmd which requires shell: true to execute properly. The -p flag is deprecated and causes conflicts when used with positional prompts in newer Gemini CLI versions. Tested on Windows 11 with Gemini CLI v0.18.4 --- src/utils/commandExecutor.ts | 2 +- src/utils/geminiExecutor.ts | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/utils/commandExecutor.ts b/src/utils/commandExecutor.ts index b9b6f1b..04c21d3 100644 --- a/src/utils/commandExecutor.ts +++ b/src/utils/commandExecutor.ts @@ -12,7 +12,7 @@ export async function executeCommand( const childProcess = spawn(command, args, { env: process.env, - shell: false, + shell: true, stdio: ["ignore", "pipe", "pipe"], }); diff --git a/src/utils/geminiExecutor.ts b/src/utils/geminiExecutor.ts index f7e79d3..33ca5a4 100644 --- a/src/utils/geminiExecutor.ts +++ b/src/utils/geminiExecutor.ts @@ -87,16 +87,12 @@ ${prompt_processed} prompt_processed = changeModeInstructions; } - const args = []; + const args = ['-y']; // YOLO mode for non-interactive if (model) { args.push(CLI.FLAGS.MODEL, model); } if (sandbox) { args.push(CLI.FLAGS.SANDBOX); } - - // Ensure @ symbols work cross-platform by wrapping in quotes if needed - const finalPrompt = prompt_processed.includes('@') && !prompt_processed.startsWith('"') - ? `"${prompt_processed}"` - : prompt_processed; - - args.push(CLI.FLAGS.PROMPT, finalPrompt); + + // Use positional prompt (not -p flag which is deprecated) + args.push(prompt_processed); try { return await executeCommand(CLI.COMMANDS.GEMINI, args, onProgress); @@ -105,18 +101,14 @@ ${prompt_processed} if (errorMessage.includes(ERROR_MESSAGES.QUOTA_EXCEEDED) && model !== MODELS.FLASH) { Logger.warn(`${ERROR_MESSAGES.QUOTA_EXCEEDED}. Falling back to ${MODELS.FLASH}.`); await sendStatusMessage(STATUS_MESSAGES.FLASH_RETRY); - const fallbackArgs = []; + const fallbackArgs = ['-y']; // YOLO mode fallbackArgs.push(CLI.FLAGS.MODEL, MODELS.FLASH); if (sandbox) { fallbackArgs.push(CLI.FLAGS.SANDBOX); } - - // Same @ symbol handling for fallback - const fallbackPrompt = prompt_processed.includes('@') && !prompt_processed.startsWith('"') - ? `"${prompt_processed}"` - : prompt_processed; - - fallbackArgs.push(CLI.FLAGS.PROMPT, fallbackPrompt); + + // Use positional prompt + fallbackArgs.push(prompt_processed); try { const result = await executeCommand(CLI.COMMANDS.GEMINI, fallbackArgs, onProgress); Logger.warn(`Successfully executed with ${MODELS.FLASH} fallback.`); From 74aa07832d69d7141a88a4fef48e44ea63eddb85 Mon Sep 17 00:00:00 2001 From: CJ Date: Sun, 30 Nov 2025 22:00:31 -0500 Subject: [PATCH 2/2] Add workingDirectory parameter for cross-drive access on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add workingDirectory parameter to ask-gemini tool - Pass cwd to spawn() to control Gemini's workspace directory - Enables accessing files on any Windows drive (C:/, D:/, E:/, etc.) This solves the Windows multi-drive limitation where Gemini workspace is locked to the launch directory. Users can now specify which drive root to use as the working directory, giving full read access to that drive. Example: workingDirectory: "C:/" → access C: drive files workingDirectory: "D:/" → access D: drive files --- src/tools/ask-gemini.tool.ts | 10 ++++++---- src/utils/commandExecutor.ts | 4 +++- src/utils/geminiExecutor.ts | 7 ++++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/tools/ask-gemini.tool.ts b/src/tools/ask-gemini.tool.ts index bc76b3a..de9d77b 100644 --- a/src/tools/ask-gemini.tool.ts +++ b/src/tools/ask-gemini.tool.ts @@ -13,6 +13,7 @@ const askGeminiArgsSchema = z.object({ changeMode: z.boolean().default(false).describe("Enable structured change mode - formats prompts to prevent tool errors and returns structured edit suggestions that Claude can apply directly"), chunkIndex: z.union([z.number(), z.string()]).optional().describe("Which chunk to return (1-based)"), chunkCacheKey: z.string().optional().describe("Optional cache key for continuation"), + workingDirectory: z.string().optional().describe("Working directory to run Gemini from. Use drive root (e.g., 'C:/' or 'D:/') to access files on that drive."), }); export const askGeminiTool: UnifiedTool = { @@ -24,8 +25,8 @@ export const askGeminiTool: UnifiedTool = { }, category: 'gemini', execute: async (args, onProgress) => { - const { prompt, model, sandbox, changeMode, chunkIndex, chunkCacheKey } = args; if (!prompt?.trim()) { throw new Error(ERROR_MESSAGES.NO_PROMPT_PROVIDED); } - + const { prompt, model, sandbox, changeMode, chunkIndex, chunkCacheKey, workingDirectory } = args; if (!prompt?.trim()) { throw new Error(ERROR_MESSAGES.NO_PROMPT_PROVIDED); } + if (changeMode && chunkIndex && chunkCacheKey) { return processChangeModeOutput( '', // empty for cache... @@ -34,13 +35,14 @@ export const askGeminiTool: UnifiedTool = { prompt as string ); } - + const result = await executeGeminiCLI( prompt as string, model as string | undefined, !!sandbox, !!changeMode, - onProgress + onProgress, + workingDirectory as string | undefined ); if (changeMode) { diff --git a/src/utils/commandExecutor.ts b/src/utils/commandExecutor.ts index 04c21d3..861efba 100644 --- a/src/utils/commandExecutor.ts +++ b/src/utils/commandExecutor.ts @@ -4,7 +4,8 @@ import { Logger } from "./logger.js"; export async function executeCommand( command: string, args: string[], - onProgress?: (newOutput: string) => void + onProgress?: (newOutput: string) => void, + cwd?: string ): Promise { return new Promise((resolve, reject) => { const startTime = Date.now(); @@ -14,6 +15,7 @@ export async function executeCommand( env: process.env, shell: true, stdio: ["ignore", "pipe", "pipe"], + cwd: cwd, }); let stdout = ""; diff --git a/src/utils/geminiExecutor.ts b/src/utils/geminiExecutor.ts index 33ca5a4..50dc964 100644 --- a/src/utils/geminiExecutor.ts +++ b/src/utils/geminiExecutor.ts @@ -17,7 +17,8 @@ export async function executeGeminiCLI( model?: string, sandbox?: boolean, changeMode?: boolean, - onProgress?: (newOutput: string) => void + onProgress?: (newOutput: string) => void, + cwd?: string ): Promise { let prompt_processed = prompt; @@ -95,7 +96,7 @@ ${prompt_processed} args.push(prompt_processed); try { - return await executeCommand(CLI.COMMANDS.GEMINI, args, onProgress); + return await executeCommand(CLI.COMMANDS.GEMINI, args, onProgress, cwd); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes(ERROR_MESSAGES.QUOTA_EXCEEDED) && model !== MODELS.FLASH) { @@ -110,7 +111,7 @@ ${prompt_processed} // Use positional prompt fallbackArgs.push(prompt_processed); try { - const result = await executeCommand(CLI.COMMANDS.GEMINI, fallbackArgs, onProgress); + const result = await executeCommand(CLI.COMMANDS.GEMINI, fallbackArgs, onProgress, cwd); Logger.warn(`Successfully executed with ${MODELS.FLASH} fallback.`); await sendStatusMessage(STATUS_MESSAGES.FLASH_SUCCESS); return result;