diff --git a/bun.lock b/bun.lock index 8ac1d04..46c9bf0 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,8 @@ "dependencies": { "@ai-sdk/openai-compatible": "^2.0.41", "ai": "^6.0.161", + "ansi-styles": "^6.2.3", + "chalk": "^5.6.2", }, "devDependencies": { "@biomejs/biome": "2.4.12", @@ -56,8 +58,12 @@ "ai": ["ai@6.0.161", "", { "dependencies": { "@ai-sdk/gateway": "3.0.98", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ufhmijmx2YyWTPAicGgtpLOB/xD7mG8zKs1pT1Trj+JL/3r1rS8fkMi/cHZoChSAQSGB4pgmcWVxDrVTUvK2IQ=="], + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], diff --git a/index.ts b/index.ts index 4add7c1..28f306c 100644 --- a/index.ts +++ b/index.ts @@ -2,6 +2,7 @@ import os from "node:os"; import { parseArgs } from "node:util"; import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { type ModelMessage, streamText } from "ai"; +import chalk from "chalk"; import StreamRenderer from "./src/render"; @@ -101,7 +102,9 @@ async function main() { const output = (outputTokens ?? 0) / 1000; const time = (stop - start) / 1000; process.stderr.write( - `\u001b[2mContext: ${context.toFixed(1)}k, Output: ${output.toFixed(1)}k, Time: ${time.toFixed(2)}s\u001b[22m\n`, + chalk.dim( + `Context: ${context.toFixed(1)}k, Output: ${output.toFixed(1)}k, Time: ${time.toFixed(2)}s\n`, + ), ); } diff --git a/package.json b/package.json index 63aacf3..827975e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ }, "dependencies": { "@ai-sdk/openai-compatible": "^2.0.41", - "ai": "^6.0.161" + "ai": "^6.0.161", + "ansi-styles": "^6.2.3", + "chalk": "^5.6.2" } } diff --git a/src/render/index.ts b/src/render/index.ts index 3b0e33b..774020b 100644 --- a/src/render/index.ts +++ b/src/render/index.ts @@ -1,5 +1,7 @@ import os from "node:os"; import type { AsyncIterableStream, TextStreamPart, ToolSet } from "ai"; +import styles from "ansi-styles"; +import { supportsColor } from "chalk"; const home = os.homedir(); const reasoningWriter = Bun.file(`${home}/.aicl_reasoning.md`).writer(); @@ -56,14 +58,17 @@ export default class StreamRenderer { for await (const part of stream) { if (part.type === "reasoning-start") { - this.write("\u001b[2m\u001b[3m\n"); + if (supportsColor) + this.write(`${styles.dim.open}${styles.italic.open}`); + this.write("\n"); } else if (part.type === "reasoning-delta") { this.write(part.text); reasoningWriter.write(part.text); this.checkContainment(part.text); } else if (part.type === "reasoning-end") { - this.write("\n\u001b[22m\u001b[23m"); - this.moveCursor(0, -this.lineCount - 2); + if (supportsColor) + this.write(`${styles.italic.close}${styles.dim.close}`); + this.moveCursor(0, -this.lineCount - 1); this.clearScreenDown(); this.resetCounts(); reasoningWriter.end();