export interface AssistantCommandResult { stdout: string; stderr: string; } /** * CLI test utility — run an assistant CLI command via the real program, * capturing stdout and stderr. * * Returns both stdout and stderr. For backward compatibility, the function * is also callable with just a string return (use `runAssistantCommand`). */ export async function runAssistantCommandFull( ...args: string[] ): Promise { const { buildCliProgram } = await import("../program.js"); const program = await buildCliProgram(); program.exitOverride(); const stderrChunks: string[] = []; program.configureOutput({ writeErr: (str: string) => stderrChunks.push(str), writeOut: () => {}, }); const stdoutChunks: string[] = []; const originalWrite = process.stdout.write; // Override must invoke the callback (when provided) so that `Writable` streams // piped into `process.stdout` (e.g. pino's CLI destination) can drain. // Without this, only the first write lands and subsequent writes hang in // backpressure. The second arg can be either an encoding string or the callback. process.stdout.write = (( chunk: string | Uint8Array, encoding?: unknown, cb?: (err?: Error | null) => void, ) => { stdoutChunks.push( typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk), ); const callback = typeof encoding === "function" ? encoding : cb; if (typeof callback === "function") { callback(); } return true; }) as typeof process.stdout.write; try { await program.parseAsync(["node", "assistant", ...args]); } catch { /* commander exit override throws */ } finally { process.stdout.write = originalWrite; } return { stdout: stdoutChunks.join(""), stderr: stderrChunks.join(""), }; } /** * CLI test utility — run an assistant CLI command via the real program, * capturing stdout (backward-compatible wrapper). */ export async function runAssistantCommand(...args: string[]): Promise { const result = await runAssistantCommandFull(...args); return result.stdout; }