From 70a99173f703e4b28dbb68240d15dab09529386a Mon Sep 17 00:00:00 2001 From: zerone0x Date: Sun, 1 Feb 2026 12:17:25 +0800 Subject: [PATCH] fix(tui): remove outer backtick wrapper in session transcript tool formatting Tool invocations in session transcripts were wrapped in an outer triple-backtick code block that also contained inner triple-backtick blocks for input/output. This created invalid nested markdown, breaking rendering when tool output itself contained triple backticks. Removes the outer code fence and uses bold markdown for the tool name header instead. Each input/output/error section now has its own independent code block. Fixes #11513 Co-Authored-By: Claude --- .../src/cli/cmd/tui/util/transcript.ts | 10 +++---- .../opencode/test/cli/tui/transcript.test.ts | 29 +++++++++++++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/util/transcript.ts b/packages/opencode/src/cli/cmd/tui/util/transcript.ts index 8f986c3379..420c9dde1b 100644 --- a/packages/opencode/src/cli/cmd/tui/util/transcript.ts +++ b/packages/opencode/src/cli/cmd/tui/util/transcript.ts @@ -80,17 +80,17 @@ export function formatPart(part: Part, options: TranscriptOptions): string { } if (part.type === "tool") { - let result = `\`\`\`\nTool: ${part.tool}\n` + let result = `**Tool: ${part.tool}**\n` if (options.toolDetails && part.state.input) { - result += `\n**Input:**\n\`\`\`json\n${JSON.stringify(part.state.input, null, 2)}\n\`\`\`` + result += `\n**Input:**\n\`\`\`json\n${JSON.stringify(part.state.input, null, 2)}\n\`\`\`\n` } if (options.toolDetails && part.state.status === "completed" && part.state.output) { - result += `\n**Output:**\n\`\`\`\n${part.state.output}\n\`\`\`` + result += `\n**Output:**\n\`\`\`\n${part.state.output}\n\`\`\`\n` } if (options.toolDetails && part.state.status === "error" && part.state.error) { - result += `\n**Error:**\n\`\`\`\n${part.state.error}\n\`\`\`` + result += `\n**Error:**\n\`\`\`\n${part.state.error}\n\`\`\`\n` } - result += `\n\`\`\`\n\n` + result += `\n` return result } diff --git a/packages/opencode/test/cli/tui/transcript.test.ts b/packages/opencode/test/cli/tui/transcript.test.ts index 2cb29e1a89..7a5fa6b8f1 100644 --- a/packages/opencode/test/cli/tui/transcript.test.ts +++ b/packages/opencode/test/cli/tui/transcript.test.ts @@ -119,13 +119,38 @@ describe("transcript", () => { }, } const result = formatPart(part, options) - expect(result).toContain("Tool: bash") + expect(result).toContain("**Tool: bash**") expect(result).toContain("**Input:**") expect(result).toContain('"command": "ls"') expect(result).toContain("**Output:**") expect(result).toContain("file1.txt") }) + test("formats tool output containing triple backticks without breaking markdown", () => { + const part: Part = { + id: "part_1", + sessionID: "ses_123", + messageID: "msg_123", + type: "tool", + callID: "call_1", + tool: "bash", + state: { + status: "completed", + input: { command: "echo '```hello```'" }, + output: "```hello```", + title: "Echo backticks", + metadata: {}, + time: { start: 1000, end: 1100 }, + }, + } + const result = formatPart(part, options) + // The tool header should not be inside a code block + expect(result).toStartWith("**Tool: bash**\n") + // Input and output should each be in their own code blocks + expect(result).toContain("**Input:**\n```json") + expect(result).toContain("**Output:**\n```\n```hello```\n```") + }) + test("formats tool part without details when disabled", () => { const part: Part = { id: "part_1", @@ -144,7 +169,7 @@ describe("transcript", () => { }, } const result = formatPart(part, { ...options, toolDetails: false }) - expect(result).toContain("Tool: bash") + expect(result).toContain("**Tool: bash**") expect(result).not.toContain("**Input:**") expect(result).not.toContain("**Output:**") })