diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index a4f47dc4..38f95246 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -112,6 +112,10 @@ function toPrintOutputMode(appMode: AppMode): Exclude { return appMode === "json" ? "json" : "text"; } +function isPlainRuntimeMetadataCommand(parsed: Args): boolean { + return !parsed.print && parsed.mode === undefined && (parsed.help === true || parsed.listModels !== undefined); +} + async function prepareInitialMessage( parsed: Args, autoResizeImages: boolean, @@ -480,11 +484,6 @@ export async function main(args: string[], options?: MainOptions) { } } time("parseArgs"); - let appMode = resolveAppMode(parsed, process.stdin.isTTY, process.stdout.isTTY); - const shouldTakeOverStdout = appMode !== "interactive"; - if (shouldTakeOverStdout) { - takeOverStdout(); - } if (parsed.version) { console.log(VERSION); @@ -505,6 +504,12 @@ export async function main(args: string[], options?: MainOptions) { process.exit(0); } + let appMode = resolveAppMode(parsed, process.stdin.isTTY, process.stdout.isTTY); + const shouldTakeOverStdout = appMode !== "interactive" && !isPlainRuntimeMetadataCommand(parsed); + if (shouldTakeOverStdout) { + takeOverStdout(); + } + if (parsed.mode === "rpc" && parsed.fileArgs.length > 0) { console.error(chalk.red("Error: @file arguments are not supported in RPC mode")); process.exit(1); diff --git a/packages/coding-agent/test/stdout-cleanliness.test.ts b/packages/coding-agent/test/stdout-cleanliness.test.ts index 057db06a..f1b31eb3 100644 --- a/packages/coding-agent/test/stdout-cleanliness.test.ts +++ b/packages/coding-agent/test/stdout-cleanliness.test.ts @@ -80,6 +80,22 @@ async function runCli(args: string[]): Promise<{ stdout: string; stderr: string; } describe("stdout cleanliness in non-interactive modes", () => { + it("prints --version to stdout when stdout is redirected", async () => { + const result = await runCli(["--version"]); + + expect(result.code).toBe(0); + expect(result.stdout.trim()).toMatch(/^\d+\.\d+\.\d+/); + expect(result.stderr).toBe(""); + }); + + it("prints plain --help to stdout when stdout is redirected", async () => { + const result = await runCli(["--help"]); + + expect(result.code).toBe(0); + expect(result.stdout).toContain("Usage:"); + expect(result.stderr).not.toContain("Usage:"); + }); + it("keeps stdout empty for --mode json --help while routing trusted startup chatter to stderr", async () => { const result = await runCli(["--mode", "json", "--help", "--approve"]);