From d5e0cb463088716118220e2a66a4ac8fb12435e7 Mon Sep 17 00:00:00 2001 From: Hew Li Yang Date: Fri, 5 Dec 2025 18:47:57 +0800 Subject: [PATCH] wip: add /resume slash command --- packages/coding-agent/README.md | 10 +++ packages/coding-agent/src/tui/tui-renderer.ts | 64 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/packages/coding-agent/README.md b/packages/coding-agent/README.md index ec4809fc..630fc48b 100644 --- a/packages/coding-agent/README.md +++ b/packages/coding-agent/README.md @@ -502,6 +502,16 @@ This allows you to explore alternative conversation paths without losing your cu /branch ``` +### /resume + +Switch to a different session. Opens an interactive selector showing all available sessions. Select a session to load it and continue where you left off. + +This is equivalent to the `--resume` CLI flag but can be used mid-session. + +``` +/resume +``` + ### /login Login with OAuth to use subscription-based models (Claude Pro/Max): diff --git a/packages/coding-agent/src/tui/tui-renderer.ts b/packages/coding-agent/src/tui/tui-renderer.ts index 13e66778..6254ec45 100644 --- a/packages/coding-agent/src/tui/tui-renderer.ts +++ b/packages/coding-agent/src/tui/tui-renderer.ts @@ -42,6 +42,7 @@ import { FooterComponent } from "./footer.js"; import { ModelSelectorComponent } from "./model-selector.js"; import { OAuthSelectorComponent } from "./oauth-selector.js"; import { QueueModeSelectorComponent } from "./queue-mode-selector.js"; +import { SessionSelectorComponent } from "./session-selector.js"; import { ThemeSelectorComponent } from "./theme-selector.js"; import { ThinkingSelectorComponent } from "./thinking-selector.js"; import { ToolExecutionComponent } from "./tool-execution.js"; @@ -95,6 +96,9 @@ export class TuiRenderer { // User message selector (for branching) private userMessageSelector: UserMessageSelectorComponent | null = null; + // Session selector (for resume) + private sessionSelector: SessionSelectorComponent | null = null; + // OAuth selector private oauthSelector: any | null = null; @@ -214,6 +218,11 @@ export class TuiRenderer { description: "Toggle automatic context compaction", }; + const resumeCommand: SlashCommand = { + name: "resume", + description: "Resume a different session", + }; + // Load hide thinking block setting this.hideThinkingBlock = settingsManager.getHideThinkingBlock(); @@ -243,6 +252,7 @@ export class TuiRenderer { clearCommand, compactCommand, autocompactCommand, + resumeCommand, ...fileSlashCommands, ], process.cwd(), @@ -488,6 +498,13 @@ export class TuiRenderer { return; } + // Check for /resume command + if (text === "/resume") { + this.showSessionSelector(); + this.editor.setText(""); + return; + } + // Check for file-based slash commands text = expandSlashCommand(text, this.fileCommands); @@ -1468,6 +1485,53 @@ export class TuiRenderer { this.ui.setFocus(this.editor); } + private showSessionSelector(): void { + // Create session selector + this.sessionSelector = new SessionSelectorComponent( + this.sessionManager, + (sessionPath) => { + // Set the selected session as active + this.sessionManager.setSessionFile(sessionPath); + + // Reload the session + const loaded = loadSessionFromEntries(this.sessionManager.loadEntries()); + this.agent.replaceMessages(loaded.messages); + + // Clear and re-render the chat + this.chatContainer.clear(); + this.isFirstUserMessage = true; + this.renderInitialMessages(this.agent.state); + + // Show confirmation message + this.chatContainer.addChild(new Spacer(1)); + this.chatContainer.addChild(new Text(theme.fg("dim", "Resumed session"), 1, 0)); + + // Hide selector and show editor again + this.hideSessionSelector(); + this.ui.requestRender(); + }, + () => { + // Just hide the selector + this.hideSessionSelector(); + this.ui.requestRender(); + }, + ); + + // Replace editor with selector + this.editorContainer.clear(); + this.editorContainer.addChild(this.sessionSelector); + this.ui.setFocus(this.sessionSelector.getSessionList()); + this.ui.requestRender(); + } + + private hideSessionSelector(): void { + // Replace selector with editor in the container + this.editorContainer.clear(); + this.editorContainer.addChild(this.editor); + this.sessionSelector = null; + this.ui.setFocus(this.editor); + } + private async showOAuthSelector(mode: "login" | "logout"): Promise { // For logout mode, filter to only show logged-in providers let providersToShow: string[] = [];