diff --git a/packages/cli/src/nonInteractiveCli.test.ts b/packages/cli/src/nonInteractiveCli.test.ts index 938eb4e7..3e4ce037 100644 --- a/packages/cli/src/nonInteractiveCli.test.ts +++ b/packages/cli/src/nonInteractiveCli.test.ts @@ -70,6 +70,7 @@ describe('runNonInteractive', () => { getIdeMode: vi.fn().mockReturnValue(false), getFullContext: vi.fn().mockReturnValue(false), getContentGeneratorConfig: vi.fn().mockReturnValue({}), + getDebugMode: vi.fn().mockReturnValue(false), } as unknown as Config; }); diff --git a/packages/cli/src/nonInteractiveCli.ts b/packages/cli/src/nonInteractiveCli.ts index 8e573134..8b056a28 100644 --- a/packages/cli/src/nonInteractiveCli.ts +++ b/packages/cli/src/nonInteractiveCli.ts @@ -17,28 +17,37 @@ import { import { Content, Part, FunctionCall } from '@google/genai'; import { parseAndFormatApiError } from './ui/utils/errorParsing.js'; +import { ConsolePatcher } from './ui/utils/ConsolePatcher.js'; export async function runNonInteractive( config: Config, input: string, prompt_id: string, ): Promise { - await config.initialize(); - // Handle EPIPE errors when the output is piped to a command that closes early. - process.stdout.on('error', (err: NodeJS.ErrnoException) => { - if (err.code === 'EPIPE') { - // Exit gracefully if the pipe is closed. - process.exit(0); - } + const consolePatcher = new ConsolePatcher({ + stderr: true, + debugMode: config.getDebugMode(), }); - const geminiClient = config.getGeminiClient(); - const toolRegistry: ToolRegistry = await config.getToolRegistry(); - - const abortController = new AbortController(); - let currentMessages: Content[] = [{ role: 'user', parts: [{ text: input }] }]; - let turnCount = 0; try { + await config.initialize(); + consolePatcher.patch(); + // Handle EPIPE errors when the output is piped to a command that closes early. + process.stdout.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EPIPE') { + // Exit gracefully if the pipe is closed. + process.exit(0); + } + }); + + const geminiClient = config.getGeminiClient(); + const toolRegistry: ToolRegistry = await config.getToolRegistry(); + + const abortController = new AbortController(); + let currentMessages: Content[] = [ + { role: 'user', parts: [{ text: input }] }, + ]; + let turnCount = 0; while (true) { turnCount++; if ( @@ -133,6 +142,7 @@ export async function runNonInteractive( ); process.exit(1); } finally { + consolePatcher.cleanup(); if (isTelemetrySdkInitialized()) { await shutdownTelemetry(); } diff --git a/packages/cli/src/ui/utils/ConsolePatcher.ts b/packages/cli/src/ui/utils/ConsolePatcher.ts index 10be3bc7..a429698d 100644 --- a/packages/cli/src/ui/utils/ConsolePatcher.ts +++ b/packages/cli/src/ui/utils/ConsolePatcher.ts @@ -8,8 +8,9 @@ import util from 'util'; import { ConsoleMessageItem } from '../types.js'; interface ConsolePatcherParams { - onNewMessage: (message: Omit) => void; + onNewMessage?: (message: Omit) => void; debugMode: boolean; + stderr?: boolean; } export class ConsolePatcher { @@ -46,16 +47,22 @@ export class ConsolePatcher { originalMethod: (...args: unknown[]) => void, ) => (...args: unknown[]) => { - if (this.params.debugMode) { - originalMethod.apply(console, args); - } + if (this.params.stderr) { + if (type !== 'debug' || this.params.debugMode) { + this.originalConsoleError(this.formatArgs(args)); + } + } else { + if (this.params.debugMode) { + originalMethod.apply(console, args); + } - if (type !== 'debug' || this.params.debugMode) { - this.params.onNewMessage({ - type, - content: this.formatArgs(args), - count: 1, - }); + if (type !== 'debug' || this.params.debugMode) { + this.params.onNewMessage?.({ + type, + content: this.formatArgs(args), + count: 1, + }); + } } }; }