diff --git a/ts/packages/agents/code/src/codeActionsSchema.ts b/ts/packages/agents/code/src/codeActionsSchema.ts index 176655ff2..b38f25ce4 100644 --- a/ts/packages/agents/code/src/codeActionsSchema.ts +++ b/ts/packages/agents/code/src/codeActionsSchema.ts @@ -41,13 +41,29 @@ export type ChangeColorThemeAction = { }; export type SplitDirection = "right" | "left" | "up" | "down"; +export type EditorPosition = "first" | "last" | "active"; -// Split to update the current editor window into a new editor pane to the left, right, above, or below +// ACTION: Split an editor window into multiple panes showing the same file or different files side-by-side. +// This creates a new editor pane (split view) for working with multiple files simultaneously. +// USE THIS for: "split editor", "split the editor with X", "duplicate this editor to the right", "split X" +// +// Examples: +// - "split editor to the right" → splits active editor +// - "split the first editor" → splits leftmost editor +// - "split app.tsx to the left" → finds editor showing app.tsx and splits it +// - "split the last editor down" → splits rightmost editor downward +// - "split the editor with utils.ts" → finds editor showing utils.ts and splits it export type SplitEditorAction = { actionName: "splitEditor"; parameters: { - // e.g., "right", "left", "up", "down", only if specified by the user + // Direction to split: "right", "left", "up", "down". Only include if user specifies direction. direction?: SplitDirection; + // Which editor to split by position. Use "first" for leftmost editor, "last" for rightmost, "active" for current editor, or a number (0-based index). + editorPosition?: EditorPosition | number; + // Which editor to split by file name. Extract the file name or pattern from user request. + // Examples: "app.tsx", "main.py", "utils", "codeActionHandler" + // Use this when user says "split X" or "split the editor with X" where X is a file name. + fileName?: string; }; }; diff --git a/ts/packages/agents/code/src/vscode/editorCodeActionsSchema.ts b/ts/packages/agents/code/src/vscode/editorCodeActionsSchema.ts index 28e1c55fa..38126e23a 100644 --- a/ts/packages/agents/code/src/vscode/editorCodeActionsSchema.ts +++ b/ts/packages/agents/code/src/vscode/editorCodeActionsSchema.ts @@ -160,11 +160,20 @@ export type EditorActionFixProblem = { }; }; -// Action to move the cursor in a file to a specified position. +// ACTION: Move the cursor to a specific position within a file (for navigation or editing preparation). +// This moves the cursor position, NOT split/duplicate the editor view. +// USE THIS for: "go to file X", "jump to line 50", "go to function foo", "move cursor to X" +// DO NOT USE for: "split editor", "split X", "duplicate editor" (use splitEditor action instead) +// +// Examples: +// - "go to line 50" → { target: { type: "onLine", line: 50 } } +// - "jump to function main" → { target: { type: "insideFunction", name: "main" } } +// - "go to app.tsx" → { target: { type: "inFile", filePath: "app.tsx" } } +// - "move cursor to the end of file" → { target: { type: "atEndOfFile" } } export type EditorActionMoveCursor = { actionName: "moveCursorInFile"; parameters: { - //Target position for the cursor. Supports symbolic locations, line-based positions, or file-relative positions. + // Target position for the cursor. Supports symbolic locations, line-based positions, or file-relative positions. target: CursorTarget; // Optional file where the cursor should be moved. Defaults to the active editor if not provided. file?: FileTarget; diff --git a/ts/packages/coda/src/handleVSCodeActions.ts b/ts/packages/coda/src/handleVSCodeActions.ts index 564063351..f8a6217f4 100644 --- a/ts/packages/coda/src/handleVSCodeActions.ts +++ b/ts/packages/coda/src/handleVSCodeActions.ts @@ -298,38 +298,186 @@ export async function handleBaseEditorActions( } case "splitEditor": { - if (actionData && actionData.direction) { - switch (actionData.direction) { + console.log( + `[splitEditor] Starting with actionData:`, + JSON.stringify(actionData), + ); + // Find the target editor to split + let targetEditor: vscode.TextEditor | undefined; + const editorPosition = actionData?.editorPosition; + const fileName = actionData?.fileName; + console.log( + `[splitEditor] editorPosition=${editorPosition}, fileName=${fileName}`, + ); + + if (fileName || editorPosition !== undefined) { + // Find target editor by fileName or editorPosition + // Use visibleTextEditors to get all currently visible editors + const allEditors = vscode.window.visibleTextEditors; + console.log( + `[splitEditor] Found ${allEditors.length} visible editors:`, + allEditors.map((e) => e.document.fileName), + ); + + if (fileName) { + // Search by file name (case-insensitive, partial match) + const pattern = fileName.toLowerCase(); + console.log( + `[splitEditor] Searching for pattern: ${pattern}`, + ); + + // First try visible editors + targetEditor = allEditors.find((editor) => + editor.document.fileName + .toLowerCase() + .includes(pattern), + ); + + // If not found in visible editors, search all open tabs + if (!targetEditor) { + console.log( + `[splitEditor] Not found in visible editors, searching all tabs...`, + ); + for (const tabGroup of vscode.window.tabGroups.all) { + for (const tab of tabGroup.tabs) { + const input = tab.input as any; + if (input?.uri) { + const filePath = + input.uri.fsPath || input.uri.path; + if ( + filePath.toLowerCase().includes(pattern) + ) { + console.log( + `[splitEditor] Found tab with matching file: ${filePath}`, + ); + // Open the document to make it an editor + const document = + await vscode.workspace.openTextDocument( + input.uri, + ); + targetEditor = + await vscode.window.showTextDocument( + document, + { + viewColumn: + tabGroup.viewColumn, + preserveFocus: false, + }, + ); + break; + } + } + } + if (targetEditor) break; + } + } + + if (!targetEditor) { + console.log( + `[splitEditor] No editor or tab found with pattern: ${pattern}`, + ); + actionResult.handled = false; + actionResult.message = `No editor found with file: ${fileName}`; + break; + } + console.log( + `[splitEditor] Found target editor: ${targetEditor.document.fileName}`, + ); + } else if (editorPosition !== undefined) { + // Search by position + if (typeof editorPosition === "number") { + targetEditor = allEditors[editorPosition]; + if (!targetEditor) { + actionResult.handled = false; + actionResult.message = `No editor at position: ${editorPosition}`; + break; + } + } else if (editorPosition === "first") { + // Sort by viewColumn to get leftmost editor + const sortedEditors = [...allEditors].sort( + (a, b) => (a.viewColumn || 0) - (b.viewColumn || 0), + ); + targetEditor = sortedEditors[0]; + } else if (editorPosition === "last") { + // Sort by viewColumn to get rightmost editor + const sortedEditors = [...allEditors].sort( + (a, b) => (a.viewColumn || 0) - (b.viewColumn || 0), + ); + targetEditor = sortedEditors[sortedEditors.length - 1]; + } else if (editorPosition === "active") { + targetEditor = vscode.window.activeTextEditor; + } + + if (!targetEditor) { + actionResult.handled = false; + actionResult.message = `No editor found at position: ${editorPosition}`; + break; + } + } + + // Focus the target editor temporarily (only if it's not already active) + if (targetEditor !== vscode.window.activeTextEditor) { + console.log( + `[splitEditor] Focusing target editor: ${targetEditor!.document.fileName}`, + ); + await vscode.window.showTextDocument( + targetEditor!.document, + { + viewColumn: + targetEditor!.viewColumn ?? + vscode.ViewColumn.One, + preserveFocus: false, + }, + ); + } + } + + // Execute the split command + const direction = actionData?.direction; + if (direction) { + switch (direction) { case "right": { - vscode.commands.executeCommand( + await vscode.commands.executeCommand( "workbench.action.splitEditorRight", ); break; } case "left": { - vscode.commands.executeCommand( + await vscode.commands.executeCommand( "workbench.action.splitEditorLeft", ); break; } case "up": { - vscode.commands.executeCommand( + await vscode.commands.executeCommand( "workbench.action.splitEditorUp", ); break; } case "down": { - vscode.commands.executeCommand( + await vscode.commands.executeCommand( "workbench.action.splitEditorDown", ); break; } } - actionResult.message = `Split editor ${actionData.direction}`; } else { - vscode.commands.executeCommand("workbench.action.splitEditor"); - actionResult.message = "Split editor"; + await vscode.commands.executeCommand( + "workbench.action.splitEditor", + ); } + + // Build result message + const targetInfo = fileName + ? ` (${fileName})` + : editorPosition !== undefined + ? ` (${editorPosition})` + : ""; + actionResult.message = + `Split editor${targetInfo} ${direction || ""}`.trim(); + console.log( + `[splitEditor] Completed successfully: ${actionResult.message}`, + ); break; } diff --git a/ts/packages/commandExecutor/README.md b/ts/packages/commandExecutor/README.md index 39adee334..a510b40f3 100644 --- a/ts/packages/commandExecutor/README.md +++ b/ts/packages/commandExecutor/README.md @@ -86,7 +86,7 @@ The server is configured in `.mcp.json`: #### execute_command -Execute user commands such as playing music, managing lists, or working with calendars. +Execute user commands including music playback, list management, calendar operations, and VSCode automation. **Parameters:** @@ -94,10 +94,32 @@ Execute user commands such as playing music, managing lists, or working with cal **Examples:** +**Music & Media:** + - "play sweet emotion by aerosmith" +- "play bohemian rhapsody by queen" + +**Lists & Tasks:** + - "add jelly beans to my grocery list" +- "what's on my shopping list" + +**Calendar:** + - "schedule a meeting for tomorrow at 2pm" +**VSCode Automation:** + +- "switch to monokai theme" +- "change theme to dark+" +- "open the explorer view" +- "create a new folder called components" +- "open file app.ts" +- "split editor to the right" +- "toggle zen mode" +- "open integrated terminal" +- "show output panel" + #### ping (debug mode) Test server connectivity. @@ -115,7 +137,9 @@ Command Executor MCP Server ↓ TypeAgent Dispatcher (WebSocket) ↓ -TypeAgent Agents (Music, Lists, Calendar, etc.) + ├─ TypeAgent Agents (Music, Lists, Calendar, etc.) + └─ Coda VSCode Extension (via WebSocket on port 8082) + └─ VSCode APIs (theme, editor, files, terminal, etc.) ``` The MCP server: diff --git a/ts/packages/commandExecutor/VSCODE_CAPABILITIES.md b/ts/packages/commandExecutor/VSCODE_CAPABILITIES.md new file mode 100644 index 000000000..ecb97378c --- /dev/null +++ b/ts/packages/commandExecutor/VSCODE_CAPABILITIES.md @@ -0,0 +1,349 @@ + + +# VSCode Capabilities Available Through Command Executor + +The Command Executor MCP server can control VSCode through the Coda extension. Below are the available capabilities organized by category. + +## How It Works + +``` +User → Claude Code → execute_command MCP tool → + → TypeAgent Dispatcher → + → Coda Extension (WebSocket on port 8082) → + → VSCode APIs +``` + +The Coda VSCode extension connects to TypeAgent's dispatcher and can execute various VSCode commands. Simply use natural language with the `execute_command` tool. + +## Available Commands + +### Theme & Appearance + +**Change Color Theme:** + +- "switch to monokai theme" +- "change theme to dark+" +- "change to light theme" +- "set theme to solarized dark" + +**Display Controls:** + +- "toggle full screen" +- "toggle zen mode" +- "zoom in" (zooms in 5 levels) +- "zoom out" (zooms out 5 levels) +- "reset zoom" + +### Editor Layout + +**Split Editor:** + +- "split editor to the right" (splits currently focused editor) +- "split editor to the left" +- "split editor up" +- "split editor down" +- "split the first editor to the right" (splits leftmost editor) +- "split the last editor" (splits rightmost editor) +- "split app.tsx to the right" (splits editor with app.tsx file) +- "split the typescript file" (splits editor with a .ts file) + +**Column Layout:** + +- "change editor to single column" +- "change editor to double columns" +- "change editor to three columns" +- "toggle editor layout" + +**Editor Management:** + +- "close editor" + +### File & Folder Operations + +**Open Files:** + +- "open file app.ts" +- "open main.py" +- "goto file index.html" + +**Navigate:** + +- "goto line 42" +- "goto file package.json" + +**Create Files:** + +- "create new file" (untitled) +- "create file hello.ts in src folder" + +**Create Folders:** + +- "create folder called components" +- "create folder utils in src" + +**Open Folders:** + +- "open folder src in explorer" +- "reveal packages folder" + +### Views & Panels + +**Show Views:** + +- "show explorer" +- "open explorer view" +- "show search" +- "show source control" +- "show output panel" + +**Special Views:** + +- "toggle search details" +- "replace in files" +- "open markdown preview" +- "open markdown preview to side" + +### Navigation & Commands + +**Command Palette:** + +- "show command palette" + +**Quick Open:** + +- "quick open file" + +**Settings:** + +- "open settings" +- "show user settings" +- "show keyboard shortcuts" + +### Terminal + +**Open Terminal:** + +- "open integrated terminal" +- "open terminal in src folder" +- "open terminal and run npm install" + +### Tasks & Build + +**Run Tasks:** + +- "build the project" +- "clean the project" +- "rebuild the project" +- "run build task in packages folder" + +### Window Management + +**New Windows:** + +- "open new window" + +## Usage Examples + +### Example 1: Change Theme + +**User to Claude Code:** + +``` +switch to monokai theme +``` + +**Claude Code calls:** + +```json +{ + "tool": "execute_command", + "arguments": { + "request": "switch to monokai theme" + } +} +``` + +**Result:** + +``` +Changed theme to Monokai +``` + +### Example 2: Open File and Split Editor + +**User to Claude Code:** + +``` +open app.ts and split the editor to the right +``` + +**Claude Code can:** + +1. Call execute_command with "open app.ts" +2. Call execute_command with "split editor to the right" + +Or TypeAgent might handle both in one call. + +### Example 3: Create Project Structure + +**User to Claude Code:** + +``` +create folders called src, tests, and docs +``` + +**Claude Code calls:** + +```json +{ + "tool": "execute_command", + "arguments": { + "request": "create folders called src, tests, and docs" + } +} +``` + +## Implementation Details + +### How Coda Extension Handles Commands + +The Coda extension listens for WebSocket messages from TypeAgent's Code Agent: + +```typescript +// Message format from TypeAgent +{ + id: "123", + method: "code/changeColorScheme", + params: { + theme: "Monokai" + } +} + +// Response from Coda +{ + id: "123", + result: "Changed theme to Monokai" +} +``` + +### Action Handlers + +Commands are routed through several handlers: + +1. **handleBaseEditorActions**: Theme, split, layout, new file +2. **handleGeneralKBActions**: Command palette, goto, settings +3. **handleDisplayKBActions**: Views, panels, zoom, full screen +4. **handleWorkbenchActions**: Files, folders, tasks, terminal +5. **handleDebugActions**: Debugging operations +6. **handleExtensionActions**: Extension management +7. **handleEditorCodeActions**: Code editing, refactoring + +See source files in `packages/coda/src/handle*.ts` for full details. + +### Available Action Names + +Here are the internal action names (useful for understanding the code): + +**Base Editor:** + +- `changeColorScheme` +- `splitEditor` +- `changeEditorLayout` +- `newFile` + +**Display:** + +- `toggleFullScreen` +- `toggleEditorLayout` +- `zoomIn`, `zoomOut`, `fontZoomReset` +- `showExplorer`, `showSearch`, `showSourceControl` +- `showOutputPanel` +- `toggleSearchDetails` +- `replaceInFiles` +- `openMarkdownPreview`, `openMarkdownPreviewToSide` +- `zenMode` +- `closeEditor` +- `openSettings` + +**General:** + +- `showCommandPalette` +- `gotoFileOrLineOrSymbol` +- `newWindowFromApp` +- `showUserSettings` +- `showKeyboardShortcuts` + +**Workbench:** + +- `workbenchOpenFile` +- `workbenchOpenFolder` +- `workbenchCreateFolderFromExplorer` +- `workbenchBuildRelatedTask` +- `openInIntegratedTerminal` + +## Prerequisites + +To use these VSCode capabilities: + +1. **TypeAgent Dispatcher** must be running: + + ```bash + pnpm run start:agent-server + ``` + +2. **Coda Extension** must be installed and activated in VSCode: + + - Published as `aisystems.copilot-coda` + - Auto-connects to dispatcher on port 8082 + +3. **Command Executor MCP Server** configured in `.mcp.json`: + ```json + { + "mcpServers": { + "command-executor": { + "command": "node", + "args": ["packages/commandExecutor/dist/server.js"] + } + } + } + ``` + +## Limitations + +1. **Natural Language Translation**: Commands must be clear enough for TypeAgent to translate to the correct action +2. **File/Folder Matching**: File and folder names are matched via search, so ambiguous names might require user selection +3. **Terminal Commands**: High-risk terminal commands are blocked for security +4. **Theme Names**: Theme names must match installed themes exactly + +## Testing Commands + +You can test these commands directly in Claude Code: + +``` +// Test theme changing +switch to monokai theme + +// Test editor layout +split editor to the right + +// Test file operations +open package.json + +// Test views +show explorer + +// Test terminal +open integrated terminal +``` + +## Extending Capabilities + +To add new VSCode capabilities: + +1. Add handler in `packages/coda/src/handle*.ts` +2. Register in `handleVSCodeActions` function +3. Update this documentation +4. No changes needed to MCP server (it forwards all commands to dispatcher) + +The beauty of this architecture is that new capabilities added to Coda are automatically available through the MCP server without any code changes to the command executor. diff --git a/ts/packages/commandExecutor/src/commandServer.ts b/ts/packages/commandExecutor/src/commandServer.ts index 4e0dca2c4..be2966bdf 100644 --- a/ts/packages/commandExecutor/src/commandServer.ts +++ b/ts/packages/commandExecutor/src/commandServer.ts @@ -352,7 +352,11 @@ export class CommandServer { { inputSchema: executeCommandRequestSchema(), description: - "Execute a user command such as playing music, managing lists, or working with calendars", + "Execute user commands including:\n" + + "- Music & media: play songs, control playback\n" + + "- Lists & tasks: manage shopping lists, todo lists\n" + + "- Calendar: schedule events, view calendar\n" + + "- VSCode automation: change theme (e.g. 'switch to monokai theme'), open files, create folders, run tasks, manage editor layout, open terminals, toggle settings", }, async (request: ExecuteCommandRequest) => this.executeCommand(request),