From ec3a5b528fdbb909f5bb7f8d0aa9fa762e40451e Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 31 Jan 2026 21:47:16 -0500 Subject: [PATCH] fix(ssh): skip adding prompt to args when using stdin for SSH When sendPromptViaStdin or sendPromptViaStdinRaw is set, the prompt will be sent via stdin as stream-json data. The ChildProcessSpawner was incorrectly ALSO adding the prompt to the command line args, causing shell escaping issues with zsh on remote hosts. This fix skips adding the prompt to args when it will be sent via stdin, preventing: - "zsh:35: parse error near 'do'" from multi-line prompts - "zsh:3: no matches found: **Summary:**" from glob-like patterns The prompt still gets sent correctly via stdin in the isStreamJsonMode block at line 389+. --- .../spawners/ChildProcessSpawner.ts | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/process-manager/spawners/ChildProcessSpawner.ts b/src/main/process-manager/spawners/ChildProcessSpawner.ts index c9550c8c..c557b1e5 100644 --- a/src/main/process-manager/spawners/ChildProcessSpawner.ts +++ b/src/main/process-manager/spawners/ChildProcessSpawner.ts @@ -63,19 +63,26 @@ export class ChildProcessSpawner { contextWindow, customEnvVars, noPromptSeparator, + sendPromptViaStdin, + sendPromptViaStdinRaw, } = config; const isWindows = process.platform === 'win32'; const hasImages = images && images.length > 0; const capabilities = getAgentCapabilities(toolType); + // Check if prompt will be sent via stdin instead of command line + // This is critical for SSH remote execution to avoid shell escaping issues + const promptViaStdin = sendPromptViaStdin || sendPromptViaStdinRaw; + // Build final args based on batch mode and images let finalArgs: string[]; let tempImageFiles: string[] = []; if (hasImages && prompt && capabilities.supportsStreamJsonInput) { // For agents that support stream-json input (like Claude Code) - finalArgs = [...args, '--input-format', 'stream-json']; + // When using stdin, --input-format stream-json should already be in args from the caller + finalArgs = promptViaStdin ? [...args] : [...args, '--input-format', 'stream-json']; } else if (hasImages && prompt && imageArgs) { // For agents that use file-based image args (like Codex, OpenCode) finalArgs = [...args]; @@ -88,20 +95,25 @@ export class ChildProcessSpawner { } } // Add the prompt using promptArgs if available, otherwise as positional arg - if (promptArgs) { - finalArgs = [...finalArgs, ...promptArgs(prompt)]; - } else if (noPromptSeparator) { - finalArgs = [...finalArgs, prompt]; - } else { - finalArgs = [...finalArgs, '--', prompt]; + // SKIP this when prompt is sent via stdin to avoid shell escaping issues + if (!promptViaStdin) { + if (promptArgs) { + finalArgs = [...finalArgs, ...promptArgs(prompt)]; + } else if (noPromptSeparator) { + finalArgs = [...finalArgs, prompt]; + } else { + finalArgs = [...finalArgs, '--', prompt]; + } } logger.debug('[ProcessManager] Using file-based image args', 'ProcessManager', { sessionId, imageCount: images.length, tempFiles: tempImageFiles, + promptViaStdin, }); - } else if (prompt) { + } else if (prompt && !promptViaStdin) { // Regular batch mode - prompt as CLI arg + // SKIP this when prompt is sent via stdin to avoid shell escaping issues if (promptArgs) { finalArgs = [...args, ...promptArgs(prompt)]; } else if (noPromptSeparator) {