diff --git a/src/output.ts b/src/output.ts index b6d362c..307dbc9 100644 --- a/src/output.ts +++ b/src/output.ts @@ -131,6 +131,10 @@ function collapseWhitespace(value: string): string { return value.replace(/\s+/g, " ").trim(); } +function normalizeLineEndings(value: string): string { + return value.replace(/\r\n?/g, "\n"); +} + function truncate(value: string, maxChars: number): string { if (value.length <= maxChars) { return value; @@ -656,14 +660,18 @@ class TextOutputFormatter implements OutputFormatter { } private flushThoughtBuffer(): void { - const thought = truncate(collapseWhitespace(this.thoughtBuffer), MAX_THOUGHT_CHARS); + const thought = truncate(normalizeLineEndings(this.thoughtBuffer).trim(), MAX_THOUGHT_CHARS); this.thoughtBuffer = ""; if (!thought) { return; } this.beginSection("thought"); - this.writeLine(this.dim(`[thinking] ${thought}`)); + const [firstLine, ...restLines] = thought.split("\n"); + this.writeLine(this.dim(`[thinking] ${firstLine}`)); + for (const line of restLines) { + this.writeLine(this.dim(` ${line}`)); + } } private renderToolUpdate(update: ToolCall | ToolCallUpdate): void { diff --git a/test/output.test.ts b/test/output.test.ts index e108cf8..8331f29 100644 --- a/test/output.test.ts +++ b/test/output.test.ts @@ -68,6 +68,18 @@ test("text formatter batches thought chunks from ACP notifications", () => { assert.match(output, /\[done\] end_turn/); }); +test("text formatter preserves line breaks in thought chunks", () => { + const writer = new CaptureWriter(); + const formatter = createOutputFormatter("text", { stdout: writer }); + + formatter.onAcpMessage(thoughtChunk("Line one\n\nLine two") as never); + formatter.onAcpMessage(doneResult("end_turn") as never); + + const output = writer.toString(); + assert.match(output, /\[thinking\] Line one\n\s*\n\s*Line two/); + assert.doesNotMatch(output, /\[thinking\] Line one Line two/); +}); + test("text formatter renders tool call lifecycle from ACP updates", () => { const writer = new CaptureWriter(); const formatter = createOutputFormatter("text", { stdout: writer });