// src/cli/commands/registry.ts // Registry of available CLI commands type CommandHandler = (args: string[]) => Promise | string; interface Command { desc: string; handler: CommandHandler; args?: string; // Optional argument syntax hint (e.g., "[conversation_id]", "") hidden?: boolean; // Hidden commands don't show in autocomplete but still work order?: number; // Lower numbers appear first in autocomplete (default: 100) noArgs?: boolean; // If true, reject any arguments passed to this command } export const commands: Record = { // === Page 1: Most commonly used (order 10-19) === "/agents": { desc: "Browse agents (pinned, Letta Code, all)", order: 10, noArgs: true, handler: () => { // Handled specially in App.tsx to open agent browser return "Opening agent browser..."; }, }, "/model": { desc: "Switch model", order: 11, noArgs: true, handler: () => { return "Opening model selector..."; }, }, "/init": { desc: "Initialize (or re-init) your agent's memory", order: 12, noArgs: true, handler: () => { // Handled specially in App.tsx to send initialization prompt return "Initializing memory..."; }, }, "/doctor": { desc: "Audit and refine your memory structure", order: 12.1, noArgs: true, handler: () => { // Handled specially in App.tsx to send doctor prompt return "Running memory doctor..."; }, }, "/remember": { desc: "Remember something from the conversation (/remember [instructions])", order: 13, handler: () => { // Handled specially in App.tsx to trigger memory update return "Processing memory request..."; }, }, "/reflect": { desc: "Launch a background reflection agent to update memory", order: 50, noArgs: true, handler: () => { // Handled specially in App.tsx return "Launching reflection agent..."; }, }, "/skills": { desc: "Browse available skills", order: 28, noArgs: true, handler: () => { // Handled specially in App.tsx to open skills browser overlay return "Opening skills browser..."; }, }, "/skill-creator": { desc: "Enter skill creation mode (/skill-creator [description])", order: 28.5, handler: () => { // Handled specially in App.tsx to trigger skill-creation workflow return "Starting skill creation..."; }, }, "/memory": { desc: "View your agent's memory", order: 15, noArgs: true, handler: () => { // Handled specially in App.tsx to open memory viewer return "Opening memory viewer..."; }, }, "/palace": { desc: "Open the Memory Palace in your browser", order: 16, noArgs: true, handler: () => { // Handled specially in App.tsx - opens browser directly return "Opening Memory Palace..."; }, }, "/sleeptime": { desc: "Configure reflection reminder trigger settings", order: 15.5, noArgs: true, handler: () => { // Handled specially in App.tsx to open sleeptime settings return "Opening sleeptime settings..."; }, }, "/compaction": { desc: "Configure compaction mode settings", order: 15.6, noArgs: true, handler: () => { // Handled specially in App.tsx to open compaction settings return "Opening compaction settings..."; }, }, "/memfs": { desc: "Manage filesystem-backed memory (/memfs [enable|disable|sync|reset])", args: "[enable|disable|sync|reset]", order: 27.5, // Advanced feature, near /toolset handler: () => { // Handled specially in App.tsx return "Managing memory filesystem..."; }, }, "/search": { desc: "Search messages across all agents (/search [query])", order: 15.1, handler: () => { // Handled specially in App.tsx to show message search return "Opening message search..."; }, }, "/connect": { desc: "Connect your LLM API keys (OpenAI, Anthropic, etc.)", order: 17, noArgs: true, handler: () => { // Handled specially in App.tsx - opens ProviderSelector return "Opening provider connection..."; }, }, // "/remote": { // desc: "Connect to Letta Cloud (device connect mode)", // args: "[--env-name ]", // order: 17.5, // handler: () => { // // Handled specially in App.tsx // return "Starting listener..."; // }, // }, "/clear": { desc: "Clear in-context messages", order: 18, noArgs: true, handler: () => { // Handled specially in App.tsx to reset agent messages return "Clearing in-context messages..."; }, }, // === Page 2: Agent management (order 20-29) === "/new": { desc: "Start a new conversation (keep agent memory)", order: 20, noArgs: true, handler: () => { // Handled specially in App.tsx to create new conversation return "Starting new conversation..."; }, }, "/pin": { desc: "Pin current agent globally, or use -l for local only", order: 22, handler: () => { // Handled specially in App.tsx return "Pinning agent..."; }, }, "/unpin": { desc: "Unpin current agent globally, or use -l for local only", order: 23, handler: () => { // Handled specially in App.tsx return "Unpinning agent..."; }, }, "/rename": { desc: "Rename agent or conversation (/rename agent|convo )", order: 24, handler: () => { // Handled specially in App.tsx to access agent ID and client return "Renaming..."; }, }, "/description": { desc: "Update the current agent's description (/description )", order: 25, handler: () => { // Handled specially in App.tsx to access agent ID and client return "Updating description..."; }, }, "/export": { desc: "Export AgentFile (.af)", order: 26, noArgs: true, handler: () => { // Handled specially in App.tsx to access agent ID and client return "Exporting agent file..."; }, }, "/toolset": { desc: "Switch toolset (replaces /link and /unlink)", order: 27, noArgs: true, handler: () => { // Handled specially in App.tsx to access agent ID and client return "Opening toolset selector..."; }, }, "/ade": { desc: "Open agent in ADE (browser)", order: 28, noArgs: true, handler: () => { // Handled specially in App.tsx to access agent ID and open browser return "Opening ADE..."; }, }, // === Page 3: Advanced features (order 30-39) === "/system": { desc: "Switch system prompt", order: 30, noArgs: true, handler: () => { // Handled specially in App.tsx to open system prompt selector return "Opening system prompt selector..."; }, }, "/subagents": { desc: "Manage custom subagents", order: 31, noArgs: true, handler: () => { // Handled specially in App.tsx to open SubagentManager component return "Opening subagent manager..."; }, }, "/mcp": { desc: "Manage MCP servers (add, connect with OAuth)", order: 32, handler: () => { // Handled specially in App.tsx to show MCP server selector return "Opening MCP server manager..."; }, }, "/usage": { desc: "Show session usage statistics and balance", order: 33, noArgs: true, handler: () => { // Handled specially in App.tsx to display usage stats return "Fetching usage statistics..."; }, }, "/context": { desc: "Show context window usage", order: 33.5, noArgs: true, handler: () => { // Handled specially in App.tsx to display context usage return "Fetching context usage..."; }, }, "/recompile": { desc: "Recompile current agent + conversation (warning: this will evict the cache and increase costs)", order: 33.6, noArgs: true, handler: () => { // Handled specially in App.tsx return "Recompiling agent and conversation..."; }, }, "/feedback": { desc: "Send feedback to the Letta team", order: 34, handler: () => { // Handled specially in App.tsx to send feedback request return "Sending feedback..."; }, }, "/help": { desc: "Show available commands", order: 35, hidden: true, // Redundant with improved autocomplete, but still works if typed noArgs: true, handler: () => { // Handled specially in App.tsx to open help dialog return "Opening help..."; }, }, "/hooks": { desc: "Manage hooks configuration", order: 36, noArgs: true, handler: () => { // Handled specially in App.tsx to open hooks manager return "Opening hooks manager..."; }, }, "/statusline": { desc: "Configure status line (help|show|set|clear|test|enable|disable)", args: "[subcommand]", order: 36.5, handler: () => { // Handled specially in App.tsx return "Managing status line..."; }, }, "/reasoning-tab": { desc: "Toggle Tab shortcut for reasoning tiers (/reasoning-tab on|off|status)", args: "[on|off|status]", order: 36.6, handler: () => { // Handled specially in App.tsx return "Managing reasoning Tab shortcut..."; }, }, "/terminal": { desc: "Setup terminal shortcuts [--revert]", order: 37, handler: async (args: string[]) => { if (args.includes("help")) { return [ "/terminal help", "", "Setup terminal keyboard shortcuts.", "", "USAGE", " /terminal — install Shift+Enter keybinding", " /terminal --revert — remove keybinding", " /terminal help — show this help", ].join("\n"); } const { detectTerminalType, getKeybindingsPath, installKeybinding, removeKeybinding, } = await import("../utils/terminalKeybindingInstaller"); const { updateSettings } = await import("../../settings"); const isRevert = args.includes("--revert") || args.includes("--remove"); const terminal = detectTerminalType(); if (!terminal) { return "Not running in a VS Code-like terminal. Shift+Enter keybinding is not needed."; } const terminalName = { vscode: "VS Code", cursor: "Cursor", windsurf: "Windsurf", }[terminal]; const keybindingsPath = getKeybindingsPath(terminal); if (!keybindingsPath) { return `Could not determine keybindings.json path for ${terminalName}`; } if (isRevert) { const result = removeKeybinding(keybindingsPath); if (!result.success) { return `Failed to remove keybinding: ${result.error}`; } await updateSettings({ shiftEnterKeybindingInstalled: false }); return `Removed Shift+Enter keybinding from ${terminalName}`; } const result = installKeybinding(keybindingsPath); if (!result.success) { return `Failed to install keybinding: ${result.error}`; } if (result.alreadyExists) { return `Shift+Enter keybinding already exists in ${terminalName}`; } await updateSettings({ shiftEnterKeybindingInstalled: true }); return `Installed Shift+Enter keybinding for ${terminalName}\nLocation: ${keybindingsPath}`; }, }, "/install-github-app": { desc: "Setup Letta Code GitHub Action in this repo", order: 38, noArgs: true, handler: () => { // Handled specially in App.tsx return "Opening GitHub App installer..."; }, }, // === Session management (order 40-49) === "/plan": { desc: "Enter plan mode", order: 40, noArgs: true, handler: () => { // Handled specially in App.tsx return "Entering plan mode..."; }, }, "/disconnect": { desc: "Disconnect an existing account (/disconnect codex|claude|zai)", order: 41, handler: () => { // Handled specially in App.tsx return "Disconnecting..."; }, }, "/bg": { desc: "Show background shell processes", order: 42, noArgs: true, handler: () => { // Handled specially in App.tsx to show background processes return "Showing background processes..."; }, }, "/exit": { desc: "Exit this session", order: 43, noArgs: true, handler: () => { // Handled specially in App.tsx return "Exiting..."; }, }, "/logout": { desc: "Clear credentials and exit", order: 44, noArgs: true, handler: () => { // Handled specially in App.tsx to access settings manager return "Clearing credentials..."; }, }, // === Ralph Wiggum mode (order 45-46) === "/ralph": { desc: 'Start Ralph Wiggum loop (/ralph [prompt] [--completion-promise "X"] [--max-iterations N])', order: 45, handler: () => { // Handled specially in App.tsx return "Activating ralph mode..."; }, }, "/yolo-ralph": { desc: "Start Ralph loop with bypass permissions (yolo + ralph)", order: 46, handler: () => { // Handled specially in App.tsx return "Activating yolo-ralph mode..."; }, }, // === Hidden commands (not shown in autocomplete) === "/stream": { desc: "Toggle token streaming on/off", hidden: true, noArgs: true, handler: () => { // Handled specially in App.tsx for live toggling return "Toggling token streaming..."; }, }, "/compact": { desc: "Summarize conversation history (compaction) with optional mode", args: "[all|sliding_window|self_compact_all|self_compact_sliding_window]", handler: () => { // Handled specially in App.tsx to access client and agent ID return "Compacting conversation..."; }, }, "/link": { desc: "Attach all Letta Code tools to agent (deprecated, use /toolset instead)", hidden: true, noArgs: true, handler: () => { // Handled specially in App.tsx to access agent ID and client return "Linking tools..."; }, }, "/unlink": { desc: "Remove all Letta Code tools from agent (deprecated, use /toolset instead)", hidden: true, noArgs: true, handler: () => { // Handled specially in App.tsx to access agent ID and client return "Unlinking tools..."; }, }, "/resume": { desc: "Resume a previous conversation", args: "[conversation_id]", order: 19, handler: () => { // Handled specially in App.tsx to show conversation selector or switch directly return "Opening conversation selector..."; }, }, "/pinned": { desc: "Browse pinned agents", hidden: true, // Alias for /agents (opens to Pinned tab) noArgs: true, handler: () => { return "Opening agent browser..."; }, }, "/profiles": { desc: "Browse pinned agents", hidden: true, // Alias for /agents (opens to Pinned tab) noArgs: true, handler: () => { return "Opening agent browser..."; }, }, "/download": { desc: "Export AgentFile (.af)", hidden: true, // Legacy alias for /export noArgs: true, handler: () => { return "Exporting agent file..."; }, }, }; /** * Execute a command and return the result */ export async function executeCommand( input: string, ): Promise<{ success: boolean; output: string; notFound?: boolean }> { const [command, ...args] = input.trim().split(/\s+/); if (!command) { return { success: false, output: "No command found", }; } const handler = commands[command]; if (!handler) { return { success: false, output: `Unknown command: ${command}`, notFound: true, }; } if (handler.noArgs && args.length > 0) { return { success: false, output: `${command} does not accept arguments.`, }; } try { const output = await handler.handler(args); return { success: true, output }; } catch (error) { return { success: false, output: `Error executing ${command}: ${error instanceof Error ? error.message : String(error)}`, }; } }