feat(coding-agent/modes): added debug transcript export command to dump visible TUI conversation

- Added transcript export option to debug menu that writes visible TUI conversation to a temporary text file.
- Implemented handleDebugTranscriptCommand in CommandController to render chat messages, strip ANSI codes, and write to temp directory.
- Updated InteractiveModeContext interface and InteractiveMode to expose the new debug transcript command handler.
This commit is contained in:
can1357
2026-03-01 08:53:49 +01:00
parent e7c9ff4d4f
commit 0fbd76d3fa
5 changed files with 37 additions and 1 deletions
+11
View File
@@ -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<void> {
await this.ctx.handleDebugTranscriptCommand();
}
async #handleOpenArtifacts(): Promise<void> {
const sessionFile = this.ctx.sessionManager.getSessionFile();
if (!sessionFile) {
@@ -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<void> {
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<void> {
const tmpFile = path.join(os.tmpdir(), `${Snowflake.next()}.html`);
const cleanupTempFile = async () => {
@@ -947,6 +947,10 @@ export class InteractiveMode implements InteractiveModeContext {
return this.#commandController.handleDumpCommand();
}
handleDebugTranscriptCommand(): Promise<void> {
return this.#commandController.handleDebugTranscriptCommand();
}
handleShareCommand(): Promise<void> {
return this.#commandController.handleShareCommand();
}
+1
View File
@@ -154,6 +154,7 @@ export interface InteractiveModeContext {
handleChangelogCommand(showFull?: boolean): Promise<void>;
handleHotkeysCommand(): void;
handleDumpCommand(): Promise<void>;
handleDebugTranscriptCommand(): Promise<void>;
handleClearCommand(): Promise<void>;
handleForkCommand(): Promise<void>;
handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void>;
@@ -394,7 +394,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
},
{
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("");