From fa888a9e3891e4986a560a51fec5a179b621449c Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 19 Feb 2026 20:09:08 -0800 Subject: [PATCH 1/3] Add markdown rendering utility for bold, italic, and inline code --- src/markdown.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/markdown.ts diff --git a/src/markdown.ts b/src/markdown.ts new file mode 100644 index 0000000..c9eb259 --- /dev/null +++ b/src/markdown.ts @@ -0,0 +1,21 @@ +export function renderMarkdown(text: string): string { + // Extract code spans first so their contents aren't processed as bold/italic + const codeSpans: string[] = [] + let result = text.replace(/`([^`]+)`/g, (_, code) => { + codeSpans.push(code) + return `\x00CODE${codeSpans.length - 1}\x00` + }) + + // Bold: **text** + result = result.replace(/\*\*(.+?)\*\*/g, "\x1b[1m$1\x1b[22m") + + // Italic: *text* + result = result.replace(/\*(.+?)\*/g, "\x1b[3m$1\x1b[23m") + + // Restore code spans as light blue + result = result.replace(/\x00CODE(\d+)\x00/g, (_, i) => { + return `\x1b[94m${codeSpans[parseInt(i)]}\x1b[39m` + }) + + return result +} From 5beb35dbe239b6baabbb1d62d638064f58fd1ff4 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 20 Feb 2026 07:47:31 -0800 Subject: [PATCH 2/3] Change inline code color to light purple and add markdown test script --- package.json | 3 +++ src/markdown.ts | 2 +- src/test-markdown.ts | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/test-markdown.ts diff --git a/package.json b/package.json index 18207ff..a28a82d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "dependencies": { "commander": "^13.1.0" }, + "scripts": { + "test:markdown": "bun src/test-markdown.ts" + }, "devDependencies": { "@types/bun": "^1.3.9" } diff --git a/src/markdown.ts b/src/markdown.ts index c9eb259..d53c4fb 100644 --- a/src/markdown.ts +++ b/src/markdown.ts @@ -14,7 +14,7 @@ export function renderMarkdown(text: string): string { // Restore code spans as light blue result = result.replace(/\x00CODE(\d+)\x00/g, (_, i) => { - return `\x1b[94m${codeSpans[parseInt(i)]}\x1b[39m` + return `\x1b[38;5;147m${codeSpans[parseInt(i)]}\x1b[39m` }) return result diff --git a/src/test-markdown.ts b/src/test-markdown.ts new file mode 100644 index 0000000..b505ff6 --- /dev/null +++ b/src/test-markdown.ts @@ -0,0 +1,18 @@ +import { renderMarkdown } from "./markdown" + +const lines = [ + "This is **bold** text", + "This is *italic* text", + "This is `inline code` text", + "Mix of **bold**, *italic*, and `code` in one line", + "**bold with `code` inside** bold", + "No markdown here", + "Multiple **bold one** and **bold two**", + "Nested-ish: **bold and *italic* together**", +] + +for (const line of lines) { + console.log(` ${line}`) + console.log(` ${renderMarkdown(line)}`) + console.log() +} From f6bc2aecb7d2272e45362cdf22010cc9d268f0cf Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 20 Feb 2026 07:58:27 -0800 Subject: [PATCH 3/3] render markdown output for claude command results; add blank line before lists in markdown renderer --- src/cli.ts | 5 +++-- src/markdown.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index ec30cd3..bd3d402 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -9,6 +9,7 @@ import * as git from "./git.ts" import * as vm from "./vm.ts" import * as state from "./state.ts" import { spinner } from "./spinner.ts" +import { renderMarkdown } from "./markdown.ts" const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json() @@ -178,7 +179,7 @@ program const output = await vm.claude(worktreeAbs, { prompt, print: opts.print }) if (output) { spin.stop() - process.stdout.write(output + "\n") + process.stdout.write(renderMarkdown(output) + "\n") } else { spin.succeed("Done") } @@ -310,7 +311,7 @@ program const output = await vm.claude(session.worktree, { prompt, print: opts.print }) if (output) { spin.stop() - process.stdout.write(output + "\n") + process.stdout.write(renderMarkdown(output) + "\n") } else { spin.succeed("Done") } diff --git a/src/markdown.ts b/src/markdown.ts index d53c4fb..a0a73f2 100644 --- a/src/markdown.ts +++ b/src/markdown.ts @@ -17,5 +17,14 @@ export function renderMarkdown(text: string): string { return `\x1b[38;5;147m${codeSpans[parseInt(i)]}\x1b[39m` }) + // Breathe: add blank line before list starts when preceded by non-empty text + const lines = result.split("\n") + for (let i = lines.length - 1; i > 0; i--) { + if (/^[\s]*[-*] /.test(lines[i]) && lines[i - 1].trim() !== "" && !/^[\s]*[-*] /.test(lines[i - 1])) { + lines.splice(i, 0, "") + } + } + result = lines.join("\n") + return result }