From b7a16f8b3501b30a0834df413f0e3c7694911a4e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 1 Sep 2026 07:13:01 +0000 Subject: [PATCH] fix(coding-agent): remove unused interactive slash commands Remove /export, /import, /session, /changelog, /debug, /arminsayshi, and /dementedelves from built-in slash command handling. Co-authored-by: Ingo Wolf --- packages/coding-agent/CHANGELOG.md | 4 + .../coding-agent/src/core/slash-commands.ts | 4 - .../src/modes/interactive/interactive-mode.ts | 242 +----------------- 3 files changed, 7 insertions(+), 243 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 475d4f21..cf0558a1 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Removed + +- Removed interactive slash commands `/export`, `/import`, `/session`, `/changelog`, `/debug`, `/arminsayshi`, and `/dementedelves`. + ## [0.83.0] - 2026-09-01 ## [0.82.0] - 2026-08-16 diff --git a/packages/coding-agent/src/core/slash-commands.ts b/packages/coding-agent/src/core/slash-commands.ts index 7ea4a621..b5c2acdf 100644 --- a/packages/coding-agent/src/core/slash-commands.ts +++ b/packages/coding-agent/src/core/slash-commands.ts @@ -17,12 +17,8 @@ export interface BuiltinSlashCommand { export const BUILTIN_SLASH_COMMANDS: ReadonlyArray = [ { name: "settings", description: "Open settings menu" }, - { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" }, - { name: "import", description: "Import and resume a session from a JSONL file" }, { name: "copy", description: "Copy last agent message to clipboard" }, { name: "name", description: "Set session display name" }, - { name: "session", description: "Show session info and stats" }, - { name: "changelog", description: "Show changelog entries" }, { name: "hotkeys", description: "Show all keyboard shortcuts" }, { name: "fork", description: "Create a new fork from a previous user message" }, { name: "clone", description: "Duplicate the current session at the current position" }, diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 46515f23..3af1b1c1 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -33,13 +33,12 @@ import { Text, TruncatedText, TUI, - visibleWidth, } from "@southbag/code-tui"; import chalk from "chalk"; import { spawn } from "child_process"; -import { APP_NAME, APP_TITLE, getDebugLogPath, VERSION } from "../../config.ts"; +import { APP_NAME, APP_TITLE, VERSION } from "../../config.ts"; import { type AgentSession, type AgentSessionEvent, parseSkillBlock } from "../../core/agent-session.ts"; -import { type AgentSessionRuntime, SessionImportFileNotFoundError } from "../../core/agent-session-runtime.ts"; +import type { AgentSessionRuntime } from "../../core/agent-session-runtime.ts"; import type { AutocompleteProviderFactory, EditorFactory, @@ -68,7 +67,6 @@ import { parseGitUrl } from "../../utils/git.ts"; import { getCwdRelativePath } from "../../utils/paths.ts"; import { killTrackedDetachedChildren } from "../../utils/shell.ts"; import { ensureTool } from "../../utils/tools-manager.ts"; -import { ArminComponent } from "./components/armin.ts"; import { AssistantMessageComponent } from "./components/assistant-message.ts"; import { BashExecutionComponent } from "./components/bash-execution.ts"; import { BranchSummaryMessageComponent } from "./components/branch-summary-message.ts"; @@ -77,7 +75,6 @@ import { CountdownTimer } from "./components/countdown-timer.ts"; import { CustomEditor } from "./components/custom-editor.ts"; import { CustomMessageComponent } from "./components/custom-message.ts"; import { DynamicBorder } from "./components/dynamic-border.ts"; -import { EarendilAnnouncementComponent } from "./components/earendil-announcement.ts"; import { ExtensionEditorComponent } from "./components/extension-editor.ts"; import { ExtensionInputComponent } from "./components/extension-input.ts"; import { ExtensionSelectorComponent } from "./components/extension-selector.ts"; @@ -492,7 +489,7 @@ export class InteractiveMode { if (this.settingsManager.getCollapseChangelog()) { const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/); const latestVersion = versionMatch ? versionMatch[1] : this.version; - const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`; + const condensedText = `Updated to v${latestVersion}.`; this.chatContainer.addChild(new Text(condensedText, 1, 0)); } else { this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0)); @@ -2266,8 +2263,6 @@ export class InteractiveMode { this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ()); this.defaultEditor.onAction("app.thinking.cycle", () => this.cycleThinkingLevel()); - // Global debug handler on TUI (works regardless of focus) - this.ui.onDebug = () => this.handleDebugCommand(); this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion()); this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility()); this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor()); @@ -2325,16 +2320,6 @@ export class InteractiveMode { this.editor.setText(""); return; } - if (text === "/export" || text.startsWith("/export ")) { - await this.handleExportCommand(text); - this.editor.setText(""); - return; - } - if (text === "/import" || text.startsWith("/import ")) { - await this.handleImportCommand(text); - this.editor.setText(""); - return; - } if (text === "/copy") { await this.handleCopyCommand(); this.editor.setText(""); @@ -2345,16 +2330,6 @@ export class InteractiveMode { this.editor.setText(""); return; } - if (text === "/session") { - this.handleSessionCommand(); - this.editor.setText(""); - return; - } - if (text === "/changelog") { - this.handleChangelogCommand(); - this.editor.setText(""); - return; - } if (text === "/hotkeys") { this.handleHotkeysCommand(); this.editor.setText(""); @@ -2396,21 +2371,6 @@ export class InteractiveMode { await this.handleReloadCommand(); return; } - if (text === "/debug") { - this.handleDebugCommand(); - this.editor.setText(""); - return; - } - if (text === "/arminsayshi") { - this.handleArminSaysHi(); - this.editor.setText(""); - return; - } - if (text === "/dementedelves") { - this.handleDementedDelves(); - this.editor.setText(""); - return; - } if (text === "/resume") { this.showSessionSelector(); this.editor.setText(""); @@ -4016,99 +3976,6 @@ export class InteractiveMode { } } - private async handleExportCommand(text: string): Promise { - const outputPath = this.getPathCommandArgument(text, "/export"); - - try { - if (outputPath?.endsWith(".jsonl")) { - const filePath = this.session.exportToJsonl(outputPath); - this.showStatus(`Session exported to: ${filePath}`); - } else { - const filePath = await this.session.exportToHtml(outputPath); - this.showStatus(`Session exported to: ${filePath}`); - } - } catch (error: unknown) { - this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`); - } - } - - private getPathCommandArgument(text: string, command: "/export" | "/import"): string | undefined { - if (text === command) { - return undefined; - } - if (!text.startsWith(`${command} `)) { - return undefined; - } - - const argsString = text.slice(command.length + 1).trimStart(); - if (!argsString) { - return undefined; - } - - const firstChar = argsString[0]; - if (firstChar === '"' || firstChar === "'") { - const closingQuoteIndex = argsString.indexOf(firstChar, 1); - if (closingQuoteIndex < 0) { - return undefined; - } - return argsString.slice(1, closingQuoteIndex); - } - - const firstWhitespaceIndex = argsString.search(/\s/); - if (firstWhitespaceIndex < 0) { - return argsString; - } - return argsString.slice(0, firstWhitespaceIndex); - } - - private async handleImportCommand(text: string): Promise { - const inputPath = this.getPathCommandArgument(text, "/import"); - if (!inputPath) { - this.showError("Usage: /import "); - return; - } - - const confirmed = await this.showExtensionConfirm("Import session", `Replace current session with ${inputPath}?`); - if (!confirmed) { - this.showStatus("Import cancelled"); - return; - } - - try { - if (this.loadingAnimation) { - this.loadingAnimation.stop(); - this.loadingAnimation = undefined; - } - this.statusContainer.clear(); - const result = await this.runtimeHost.importFromJsonl(inputPath); - if (result.cancelled) { - this.showStatus("Import cancelled"); - return; - } - this.showStatus(`Session imported from: ${inputPath}`); - } catch (error: unknown) { - if (error instanceof MissingSessionCwdError) { - const selectedCwd = await this.promptForMissingSessionCwd(error); - if (!selectedCwd) { - this.showStatus("Import cancelled"); - return; - } - const result = await this.runtimeHost.importFromJsonl(inputPath, selectedCwd); - if (result.cancelled) { - this.showStatus("Import cancelled"); - return; - } - this.showStatus(`Session imported from: ${inputPath}`); - return; - } - if (error instanceof SessionImportFileNotFoundError) { - this.showError(`Failed to import session: ${error.message}`); - return; - } - await this.handleFatalRuntimeError("Failed to import session", error); - } - } - private async handleCopyCommand(): Promise { const text = this.session.getLastAssistantText(); if (!text) { @@ -4148,64 +4015,6 @@ export class InteractiveMode { this.ui.requestRender(); } - private handleSessionCommand(): void { - const stats = this.session.getSessionStats(); - const sessionName = this.sessionManager.getSessionName(); - - let info = `${theme.bold("Session Info")}\n\n`; - if (sessionName) { - info += `${theme.fg("dim", "Name:")} ${sessionName}\n`; - } - info += `${theme.fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n`; - info += `${theme.fg("dim", "ID:")} ${stats.sessionId}\n\n`; - info += `${theme.bold("Messages")}\n`; - info += `${theme.fg("dim", "User:")} ${stats.userMessages}\n`; - info += `${theme.fg("dim", "Assistant:")} ${stats.assistantMessages}\n`; - info += `${theme.fg("dim", "Tool Calls:")} ${stats.toolCalls}\n`; - info += `${theme.fg("dim", "Tool Results:")} ${stats.toolResults}\n`; - info += `${theme.fg("dim", "Total:")} ${stats.totalMessages}\n\n`; - info += `${theme.bold("Tokens")}\n`; - info += `${theme.fg("dim", "Input:")} ${stats.tokens.input.toLocaleString()}\n`; - info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`; - if (stats.tokens.cacheRead > 0) { - info += `${theme.fg("dim", "Cache Read:")} ${stats.tokens.cacheRead.toLocaleString()}\n`; - } - if (stats.tokens.cacheWrite > 0) { - info += `${theme.fg("dim", "Cache Write:")} ${stats.tokens.cacheWrite.toLocaleString()}\n`; - } - info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`; - - if (stats.cost > 0) { - info += `\n${theme.bold("Cost")}\n`; - info += `${theme.fg("dim", "Total:")} ${stats.cost.toFixed(4)}`; - } - - this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild(new Text(info, 1, 0)); - this.ui.requestRender(); - } - - private handleChangelogCommand(): void { - const changelogPath = getChangelogPath(); - const allEntries = parseChangelog(changelogPath); - - const changelogMarkdown = - allEntries.length > 0 - ? allEntries - .reverse() - .map((e) => normalizeChangelogLinks(e.content, e)) - .join("\n\n") - : "No changelog entries found."; - - this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild(new DynamicBorder()); - this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0)); - this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, this.getMarkdownThemeWithSettings())); - this.chatContainer.addChild(new DynamicBorder()); - this.ui.requestRender(); - } - /** * Get capitalized display string for an app keybinding action. */ @@ -4349,51 +4158,6 @@ export class InteractiveMode { } } - private handleDebugCommand(): void { - const width = this.ui.terminal.columns; - const height = this.ui.terminal.rows; - const allLines = this.ui.render(width); - - const debugLogPath = getDebugLogPath(); - const debugData = [ - `Debug output at ${new Date().toISOString()}`, - `Terminal: ${width}x${height}`, - `Total lines: ${allLines.length}`, - "", - "=== All rendered lines with visible widths ===", - ...allLines.map((line, idx) => { - const vw = visibleWidth(line); - const escaped = JSON.stringify(line); - return `[${idx}] (w=${vw}) ${escaped}`; - }), - "", - "=== Agent messages (JSONL) ===", - ...this.session.messages.map((msg) => JSON.stringify(msg)), - "", - ].join("\n"); - - fs.mkdirSync(path.dirname(debugLogPath), { recursive: true }); - fs.writeFileSync(debugLogPath, debugData); - - this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild( - new Text(`${theme.fg("accent", "✓ Debug log written")}\n${theme.fg("muted", debugLogPath)}`, 1, 1), - ); - this.ui.requestRender(); - } - - private handleArminSaysHi(): void { - this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild(new ArminComponent(this.ui)); - this.ui.requestRender(); - } - - private handleDementedDelves(): void { - this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild(new EarendilAnnouncementComponent()); - this.ui.requestRender(); - } - private async handleBashCommand(command: string, excludeFromContext = false): Promise { const extensionRunner = this.session.extensionRunner;