diff --git a/packages/coding-agent/src/debug/index.ts b/packages/coding-agent/src/debug/index.ts index e528aa73b..323a24599 100644 --- a/packages/coding-agent/src/debug/index.ts +++ b/packages/coding-agent/src/debug/index.ts @@ -27,6 +27,11 @@ const DEBUG_MENU_ITEMS: SelectItem[] = [ { value: "memory", label: "Report: memory issue", description: "Heap snapshot + bundle" }, { value: "logs", label: "View: recent logs", description: "Show last 50 log entries" }, { value: "system", label: "View: system info", description: "Show environment details" }, + { + value: "transcript", + label: "Export: TUI transcript", + description: "Write visible TUI conversation to a temp txt", + }, { value: "clear-cache", label: "Clear: artifact cache", description: "Remove old session artifacts" }, ]; @@ -95,6 +100,9 @@ export class DebugSelectorComponent extends Container { case "system": await this.#handleViewSystemInfo(); break; + case "transcript": + await this.#handleTranscriptExport(); + break; case "clear-cache": await this.#handleClearCache(); break; @@ -323,6 +331,9 @@ export class DebugSelectorComponent extends Container { this.ctx.ui.requestRender(); } + async #handleTranscriptExport(): Promise { + await this.ctx.handleDebugTranscriptCommand(); + } async #handleOpenArtifacts(): Promise { const sessionFile = this.ctx.sessionManager.getSessionFile(); if (!sessionFile) { diff --git a/packages/coding-agent/src/modes/controllers/command-controller.ts b/packages/coding-agent/src/modes/controllers/command-controller.ts index cc3413c45..d34a6a7e8 100644 --- a/packages/coding-agent/src/modes/controllers/command-controller.ts +++ b/packages/coding-agent/src/modes/controllers/command-controller.ts @@ -28,6 +28,7 @@ import type { AuthStorage } from "../../session/auth-storage"; import { createCompactionSummaryMessage } from "../../session/messages"; import { outputMeta } from "../../tools/output-meta"; import { resolveToCwd } from "../../tools/path-utils"; +import { replaceTabs } from "../../tools/render-utils"; import { getChangelogPath, parseChangelog } from "../../utils/changelog"; import { openPath } from "../../utils/open"; @@ -70,6 +71,25 @@ export class CommandController { } } + async handleDebugTranscriptCommand(): Promise { + try { + const width = Math.max(1, this.ctx.ui.terminal.columns); + const renderedLines = this.ctx.chatContainer.render(width).map(line => replaceTabs(Bun.stripANSI(line))); + const rendered = renderedLines.join("\n").trimEnd(); + if (!rendered) { + this.ctx.showError("No messages to dump yet."); + return; + } + const tmpPath = path.join(os.tmpdir(), `${Snowflake.next()}-tmp.txt`); + await Bun.write(tmpPath, `${rendered}\n`); + this.ctx.showStatus(`Debug transcript written to:\n${tmpPath}`); + } catch (error: unknown) { + this.ctx.showError( + `Failed to write debug transcript: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + } + async handleShareCommand(): Promise { const tmpFile = path.join(os.tmpdir(), `${Snowflake.next()}.html`); const cleanupTempFile = async () => { diff --git a/packages/coding-agent/src/modes/interactive-mode.ts b/packages/coding-agent/src/modes/interactive-mode.ts index 7c96e5964..39f60767c 100644 --- a/packages/coding-agent/src/modes/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive-mode.ts @@ -947,6 +947,10 @@ export class InteractiveMode implements InteractiveModeContext { return this.#commandController.handleDumpCommand(); } + handleDebugTranscriptCommand(): Promise { + return this.#commandController.handleDebugTranscriptCommand(); + } + handleShareCommand(): Promise { return this.#commandController.handleShareCommand(); } diff --git a/packages/coding-agent/src/modes/types.ts b/packages/coding-agent/src/modes/types.ts index 86a2ef614..8c565082a 100644 --- a/packages/coding-agent/src/modes/types.ts +++ b/packages/coding-agent/src/modes/types.ts @@ -154,6 +154,7 @@ export interface InteractiveModeContext { handleChangelogCommand(showFull?: boolean): Promise; handleHotkeysCommand(): void; handleDumpCommand(): Promise; + handleDebugTranscriptCommand(): Promise; handleClearCommand(): Promise; handleForkCommand(): Promise; handleBashCommand(command: string, excludeFromContext?: boolean): Promise; diff --git a/packages/coding-agent/src/slash-commands/builtin-registry.ts b/packages/coding-agent/src/slash-commands/builtin-registry.ts index a2219182f..ff87fa130 100644 --- a/packages/coding-agent/src/slash-commands/builtin-registry.ts +++ b/packages/coding-agent/src/slash-commands/builtin-registry.ts @@ -394,7 +394,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray = [ }, { name: "debug", - description: "Write debug log (TUI state and messages)", + description: "Open debug tools selector", handle: (_command, runtime) => { runtime.ctx.showDebugSelector(); runtime.ctx.editor.setText("");