From df7aa6d01fc4f312f6ea3c023138a2ff611a4d46 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 14 Jul 2026 18:06:40 +0000 Subject: [PATCH] fix(cli): awaited config JSON stdout flush - Waited for the stdout write callback before the config command exits. - Covered JSON output larger than the 64 KiB pipe buffer. Fixes #5309 --- packages/coding-agent/CHANGELOG.md | 4 ++++ packages/coding-agent/src/cli/config-cli.ts | 18 ++++++++++++--- packages/coding-agent/test/config-cli.test.ts | 22 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e0b1b1608..955918ded 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -10,6 +10,10 @@ - Updated status event log to prioritize the most recent entries in the display window +### Fixed + +- Fixed `omp config list --json` truncating output at 64 KiB when stdout is piped ([#5309](https://github.com/can1357/oh-my-pi/issues/5309)) + ### Removed - Removed the unreliable Bing and Yahoo HTML-scraping web search providers diff --git a/packages/coding-agent/src/cli/config-cli.ts b/packages/coding-agent/src/cli/config-cli.ts index b9ecf3ca9..ee18c41ae 100644 --- a/packages/coding-agent/src/cli/config-cli.ts +++ b/packages/coding-agent/src/cli/config-cli.ts @@ -241,7 +241,7 @@ export async function runConfigCommand(cmd: ConfigCommandArgs): Promise { switch (cmd.action) { case "list": - handleList(cmd.flags); + await handleList(cmd.flags); break; case "get": handleGet(cmd.key, cmd.flags); @@ -261,7 +261,19 @@ export async function runConfigCommand(cmd: ConfigCommandArgs): Promise { } } -function handleList(flags: { json?: boolean }): void { +async function writeStdout(text: string): Promise { + const pending = Promise.withResolvers(); + process.stdout.write(text, error => { + if (error) { + pending.reject(error); + return; + } + pending.resolve(); + }); + await pending.promise; +} + +async function handleList(flags: { json?: boolean }): Promise { const defs = ALL_SETTING_PATHS.map(path => findSettingDef(path)).filter((def): def is CliSettingDef => !!def); if (flags.json) { @@ -273,7 +285,7 @@ function handleList(flags: { json?: boolean }): void { description: def.description, }; } - console.log(JSON.stringify(result, null, 2)); + await writeStdout(`${JSON.stringify(result, null, 2)}\n`); return; } diff --git a/packages/coding-agent/test/config-cli.test.ts b/packages/coding-agent/test/config-cli.test.ts index 2a4621266..c2b54ad6c 100644 --- a/packages/coding-agent/test/config-cli.test.ts +++ b/packages/coding-agent/test/config-cli.test.ts @@ -8,6 +8,7 @@ import { getConfigRootDir, setAgentDir, TempDir } from "@oh-my-pi/pi-utils"; let testAgentDir: TempDir | undefined; const originalAgentDir = process.env.PI_CODING_AGENT_DIR; const fallbackAgentDir = path.join(getConfigRootDir(), "agent"); +const cliEntry = path.join(import.meta.dir, "..", "src", "cli.ts"); beforeEach(() => { resetSettingsForTest(); @@ -166,4 +167,25 @@ describe("config CLI schema coverage", () => { expect(parsed.type).toBe("enum"); expect(parsed.value).toBe("max"); }); + it("fully flushes JSON larger than a pipe buffer", async () => { + if (!testAgentDir) throw new Error("Test agent directory was not initialized"); + const proc = Bun.spawn([process.execPath, cliEntry, "config", "list", "--json"], { + stdout: "pipe", + stderr: "pipe", + env: { + ...process.env, + NO_COLOR: "1", + PI_CODING_AGENT_DIR: testAgentDir.path(), + }, + }); + const stdout = new Response(proc.stdout).text(); + const stderr = new Response(proc.stderr).text(); + const [exitCode, output, error] = await Promise.all([proc.exited, stdout, stderr]); + + expect(exitCode).toBe(0); + expect(error).toBe(""); + expect(Buffer.byteLength(output)).toBeGreaterThan(65_536); + const parsed: unknown = JSON.parse(output); + expect(parsed).toMatchObject({ modelRoles: { type: "record" } }); + }); });