From 4716712a924e9371c35adf39998a0e566ab88876 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 17 Apr 2026 03:04:47 -0400 Subject: [PATCH] refactor: redirect reasoning output to stderr --- index.ts | 22 +++++++----------- src/render/index.ts | 54 +++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/index.ts b/index.ts index 28f306c..6a9fecd 100644 --- a/index.ts +++ b/index.ts @@ -78,25 +78,19 @@ async function main() { }); const start = Date.now(); - - const streamRender = new StreamRenderer().renderStream(fullStream); - - const [width] = process.stdout.getWindowSize(); - const glow = Bun.spawn({ - cmd: ["glow", "-w", `${width}`, "-"], - stdin: textStream, - stdout: "inherit", - }); - - await streamRender; - + await new StreamRenderer().renderStream(fullStream); const stop = Date.now(); - await glow.exited; - convo.messages.push({ role: "assistant", content: await text }); await convoFile.write(JSON.stringify(convo)); + const [width] = process.stdout.getWindowSize(); + await Bun.spawn({ + cmd: ["glow", "-w", `${width}`, "-"], + stdin: textStream, + stdout: "inherit", + }).exited; + const { inputTokens, outputTokens } = await totalUsage; const context = (inputTokens ?? 0) / 1000; const output = (outputTokens ?? 0) / 1000; diff --git a/src/render/index.ts b/src/render/index.ts index 774020b..581a3a8 100644 --- a/src/render/index.ts +++ b/src/render/index.ts @@ -1,7 +1,8 @@ import os from "node:os"; +import type { WriteStream } from "node:tty"; import type { AsyncIterableStream, TextStreamPart, ToolSet } from "ai"; import styles from "ansi-styles"; -import { supportsColor } from "chalk"; +import { supportsColorStderr } from "chalk"; const home = os.homedir(); const reasoningWriter = Bun.file(`${home}/.aicl_reasoning.md`).writer(); @@ -15,18 +16,21 @@ export default class StreamRenderer { process.stdout.addListener("drain", () => { this.memOk = true; }); + process.stderr.addListener("drain", () => { + this.memOk = true; + }); } - private write(text: string) { - this.memOk = process.stdout.write(text); + private write(text: string, pipe: WriteStream = process.stdout) { + this.memOk = pipe.write(text); } - private moveCursor(x: number, y: number) { - this.memOk = process.stdout.moveCursor(x, y); + private moveCursor(y: number, pipe: WriteStream = process.stdout) { + this.memOk = pipe.moveCursor(-9999, y); } - private clearScreenDown() { - this.memOk = process.stdout.clearScreenDown(); + private clearScreenDown(pipe: WriteStream = process.stdout) { + this.memOk = pipe.clearScreenDown(); } private resetCounts() { @@ -34,8 +38,8 @@ export default class StreamRenderer { this.charsSinceNL = 0; } - private checkContainment(text: string) { - const [columns, rows] = process.stdout.getWindowSize(); + private checkContainment(text: string, pipe: WriteStream = process.stdout) { + const [columns, rows] = pipe.getWindowSize(); this.charsSinceNL += text.length; if (text.includes("\n")) { this.lineCount += text.match(/\n/g)?.length ?? 1; @@ -46,10 +50,9 @@ export default class StreamRenderer { } if (this.lineCount >= Math.floor(rows * 0.75)) { - this.moveCursor(0, -this.lineCount); - this.clearScreenDown(); - this.lineCount = 0; - this.charsSinceNL = 0; + this.moveCursor(-this.lineCount, pipe); + this.clearScreenDown(pipe); + this.resetCounts(); } } @@ -58,18 +61,21 @@ export default class StreamRenderer { for await (const part of stream) { if (part.type === "reasoning-start") { - if (supportsColor) - this.write(`${styles.dim.open}${styles.italic.open}`); - this.write("\n"); + if (supportsColorStderr) + this.write(`${styles.dim.open}${styles.italic.open}`, process.stderr); + this.write("\n", process.stderr); } else if (part.type === "reasoning-delta") { - this.write(part.text); + this.write(part.text, process.stderr); reasoningWriter.write(part.text); - this.checkContainment(part.text); + this.checkContainment(part.text, process.stderr); } else if (part.type === "reasoning-end") { - if (supportsColor) - this.write(`${styles.italic.close}${styles.dim.close}`); - this.moveCursor(0, -this.lineCount - 1); - this.clearScreenDown(); + if (supportsColorStderr) + this.write( + `${styles.italic.close}${styles.dim.close}`, + process.stderr, + ); + this.moveCursor(-this.lineCount - 1, process.stderr); + this.clearScreenDown(process.stderr); this.resetCounts(); reasoningWriter.end(); } else if (part.type === "text-start") { @@ -78,8 +84,8 @@ export default class StreamRenderer { this.write(part.text); this.checkContainment(part.text); } else if (part.type === "text-end") { - this.moveCursor(0, -this.lineCount - 1); - this.clearScreenDown(); + this.moveCursor(-this.lineCount - 1); + this.clearScreenDown(process.stderr); this.resetCounts(); }