From 5da801c2ca8e482dc263f26641182467630695d0 Mon Sep 17 00:00:00 2001 From: Ra Date: Sat, 23 Aug 2025 14:53:03 -0700 Subject: [PATCH] Implement VS Code Tool Integration Phase 1: Core VS Code Tool Provider - Created VSCodeToolProvider with 12 core tools (file ops, editor ops, commands) - Implemented VSCodePermissions with 6 permission levels and security controls - Integrated VS Code tools into UnifiedMCPServer tool discovery and routing - Added comprehensive documentation in VSCODE_TOOL_INTEGRATION.md Tools implemented: - File Operations: read_file, write_file, create_file, delete_file, list_directory - Editor Operations: get/set editor content, get/set selection, get active editor - Commands: run_command (with whitelist), show_message - Workspace: get_workspace_folders Security features: - Permission levels: read_only, editor, filesystem, terminal, git, admin - Path sandboxing to prevent access outside workspace - Command whitelisting for safe operations - Audit logging for all VS Code tool operations Next: Implement actual VS Code Extension API bridge and language services --- VSCODE_TOOL_INTEGRATION.md | 258 ++++++++++ lib/agent_coordinator/mcp_server_manager.ex | 26 +- lib/agent_coordinator/vscode_permissions.ex | 222 +++++++++ lib/agent_coordinator/vscode_tool_provider.ex | 443 ++++++++++++++++++ 4 files changed, 945 insertions(+), 4 deletions(-) create mode 100644 VSCODE_TOOL_INTEGRATION.md create mode 100644 lib/agent_coordinator/vscode_permissions.ex create mode 100644 lib/agent_coordinator/vscode_tool_provider.ex diff --git a/VSCODE_TOOL_INTEGRATION.md b/VSCODE_TOOL_INTEGRATION.md new file mode 100644 index 0000000..a29a1c5 --- /dev/null +++ b/VSCODE_TOOL_INTEGRATION.md @@ -0,0 +1,258 @@ +# VS Code Tool Integration with Agent Coordinator + +## Overview + +This document outlines the implementation of VS Code's built-in tools as MCP (Model Context Protocol) tools within the Agent Coordinator system. This integration allows agents to access VS Code's native capabilities alongside external MCP servers through a unified coordination interface. + +## Architecture + +### Current State +- Agent Coordinator acts as a unified MCP server +- Proxies tools from external MCP servers (Context7, filesystem, memory, sequential thinking, etc.) +- Manages task coordination, agent assignment, and cross-codebase workflows + +### Proposed Enhancement +- Add VS Code Extension API tools as native MCP tools +- Integrate with existing tool routing and coordination system +- Maintain security and permission controls + +## Implementation Plan + +### Phase 1: Core VS Code Tool Provider + +#### 1.1 Create VSCodeToolProvider Module +**File**: `lib/agent_coordinator/vscode_tool_provider.ex` + +**Core Tools to Implement**: +- `vscode_read_file` - Read file contents using VS Code API +- `vscode_write_file` - Write file contents +- `vscode_create_file` - Create new files +- `vscode_delete_file` - Delete files +- `vscode_list_directory` - List directory contents +- `vscode_get_workspace_folders` - Get workspace information +- `vscode_run_command` - Execute VS Code commands +- `vscode_get_active_editor` - Get current editor state +- `vscode_set_editor_content` - Modify editor content +- `vscode_get_selection` - Get current text selection +- `vscode_set_selection` - Set text selection +- `vscode_show_message` - Display messages to user + +#### 1.2 Tool Definitions +Each tool will have: +- MCP-compliant schema definition +- Input validation +- Error handling +- Audit logging +- Permission checking + +### Phase 2: Advanced Editor Operations + +#### 2.1 Language Services Integration +- `vscode_get_diagnostics` - Get language server diagnostics +- `vscode_format_document` - Format current document +- `vscode_format_selection` - Format selected text +- `vscode_find_references` - Find symbol references +- `vscode_go_to_definition` - Navigate to definition +- `vscode_rename_symbol` - Rename symbols +- `vscode_code_actions` - Get available code actions + +#### 2.2 Search and Navigation +- `vscode_find_in_files` - Search across workspace +- `vscode_find_symbols` - Find symbols in workspace +- `vscode_goto_line` - Navigate to specific line +- `vscode_reveal_in_explorer` - Show file in explorer + +### Phase 3: Terminal and Process Management + +#### 3.1 Terminal Operations +- `vscode_create_terminal` - Create new terminal +- `vscode_send_to_terminal` - Send commands to terminal +- `vscode_get_terminal_output` - Get terminal output (if possible) +- `vscode_close_terminal` - Close terminal instances + +#### 3.2 Task and Process Management +- `vscode_run_task` - Execute VS Code tasks +- `vscode_get_tasks` - List available tasks +- `vscode_debug_start` - Start debugging session +- `vscode_debug_stop` - Stop debugging + +### Phase 4: Git and Version Control + +#### 4.1 Git Operations +- `vscode_git_status` - Get git status +- `vscode_git_commit` - Create commits +- `vscode_git_push` - Push changes +- `vscode_git_pull` - Pull changes +- `vscode_git_branch` - Branch operations +- `vscode_git_diff` - Get file differences + +### Phase 5: Extension and Settings Management + +#### 5.1 Configuration +- `vscode_get_settings` - Get VS Code settings +- `vscode_update_settings` - Update settings +- `vscode_get_extensions` - List installed extensions +- `vscode_install_extension` - Install extensions (if permitted) + +## Security and Safety + +### Permission Model +```elixir +defmodule AgentCoordinator.VSCodePermissions do + @moduledoc """ + Manages permissions for VS Code tool access. + """ + + # Permission levels: + # :read_only - File reading, workspace inspection + # :editor - Text editing, selections + # :filesystem - File creation/deletion + # :terminal - Terminal access + # :git - Version control operations + # :admin - Settings, extensions, system commands +end +``` + +### Sandboxing +- Restrict file operations to workspace folders only +- Prevent access to system files outside workspace +- Rate limiting for expensive operations +- Command whitelist for `vscode_run_command` + +### Audit Logging +- Log all VS Code tool calls with: + - Timestamp + - Agent ID + - Tool name and parameters + - Result summary + - Permission level used + +## Integration Points + +### 1. UnifiedMCPServer Enhancement +**File**: `lib/agent_coordinator/unified_mcp_server.ex` + +Add VS Code tools to the tool discovery and routing: + +```elixir +defp get_all_tools(state) do + # Existing external MCP server tools + external_tools = get_external_tools(state) + + # New VS Code tools + vscode_tools = VSCodeToolProvider.get_tools() + + external_tools ++ vscode_tools +end + +defp route_tool_call(tool_name, args, context, state) do + case tool_name do + "vscode_" <> _rest -> + VSCodeToolProvider.handle_tool_call(tool_name, args, context) + _ -> + # Route to external MCP servers + route_to_external_server(tool_name, args, context, state) + end +end +``` + +### 2. Task Coordination +VS Code tools will participate in the same task coordination system: +- Task creation and assignment +- File locking (prevent conflicts) +- Cross-agent coordination +- Priority management + +### 3. Agent Capabilities +Agents can declare VS Code tool capabilities: +```elixir +capabilities: [ + "coding", + "analysis", + "vscode_editing", + "vscode_terminal", + "vscode_git" +] +``` + +## Usage Examples + +### Example 1: File Analysis and Editing +```json +{ + "tool": "vscode_read_file", + "args": {"path": "src/main.rs"} +} +// Agent reads file, analyzes it + +{ + "tool": "vscode_get_diagnostics", + "args": {"file": "src/main.rs"} +} +// Agent gets compiler errors + +{ + "tool": "vscode_set_editor_content", + "args": { + "file": "src/main.rs", + "content": "// Fixed code here", + "range": {"start": 10, "end": 15} + } +} +// Agent fixes the issues +``` + +### Example 2: Cross-Tool Workflow +```json +// 1. Agent searches documentation using Context7 +{"tool": "mcp_context7_get-library-docs", "args": {"libraryID": "/rust/std"}} + +// 2. Agent analyzes current code using VS Code +{"tool": "vscode_get_active_editor", "args": {}} + +// 3. Agent applies documentation insights to code +{"tool": "vscode_format_document", "args": {}} +{"tool": "vscode_set_editor_content", "args": {...}} + +// 4. Agent commits changes using VS Code Git +{"tool": "vscode_git_commit", "args": {"message": "Applied best practices from docs"}} +``` + +## Benefits + +1. **Unified Tool Access**: Agents access both external services and VS Code features through same interface +2. **Enhanced Capabilities**: Complex workflows combining external data with direct IDE manipulation +3. **Consistent Coordination**: Same task management for all tool types +4. **Security**: Controlled access to powerful VS Code features +5. **Extensibility**: Easy to add new VS Code capabilities as needs arise + +## Implementation Timeline + +- **Week 1**: Phase 1 - Core file and editor operations +- **Week 2**: Phase 2 - Language services and navigation +- **Week 3**: Phase 3 - Terminal and task management +- **Week 4**: Phase 4 - Git integration +- **Week 5**: Phase 5 - Settings and extension management +- **Week 6**: Testing, documentation, security review + +## Testing Strategy + +1. **Unit Tests**: Each VS Code tool function +2. **Integration Tests**: Tool coordination and routing +3. **Security Tests**: Permission enforcement and sandboxing +4. **Performance Tests**: Rate limiting and resource usage +5. **User Acceptance**: Real workflow testing with multiple agents + +## Future Enhancements + +- **Extension-specific Tools**: Tools for specific VS Code extensions +- **Collaborative Features**: Multi-agent editing coordination +- **AI-Enhanced Operations**: Intelligent code suggestions and fixes +- **Remote Development**: Support for remote VS Code scenarios +- **Custom Tool Creation**: Framework for users to create their own VS Code tools + +--- + +## Notes + +This implementation transforms the Agent Coordinator from a simple MCP proxy into a comprehensive development environment orchestrator, enabling sophisticated AI-assisted development workflows. \ No newline at end of file diff --git a/lib/agent_coordinator/mcp_server_manager.ex b/lib/agent_coordinator/mcp_server_manager.ex index ea6d95c..b5fc655 100644 --- a/lib/agent_coordinator/mcp_server_manager.ex +++ b/lib/agent_coordinator/mcp_server_manager.ex @@ -622,7 +622,8 @@ defmodule AgentCoordinator.MCPServerManager do end defp get_coordinator_tools do - [ + # Get Agent Coordinator native tools + coordinator_native_tools = [ %{ "name" => "register_agent", "description" => "Register a new agent with the coordination system", @@ -701,14 +702,27 @@ defmodule AgentCoordinator.MCPServerManager do } } ] + + # Get VS Code tools + vscode_tools = AgentCoordinator.VSCodeToolProvider.get_tools() + + # Combine all coordinator tools + coordinator_native_tools ++ vscode_tools end defp get_coordinator_tool_names do - ~w[register_agent create_task get_next_task complete_task get_task_board heartbeat] + # Agent Coordinator native tools + coordinator_native = ~w[register_agent create_task get_next_task complete_task get_task_board heartbeat] + + # VS Code tool names + vscode_tools = AgentCoordinator.VSCodeToolProvider.get_tools() + |> Enum.map(fn tool -> tool["name"] end) + + coordinator_native ++ vscode_tools end - defp handle_coordinator_tool(tool_name, arguments, _agent_context) do - # Route to existing Agent Coordinator functionality + defp handle_coordinator_tool(tool_name, arguments, agent_context) do + # Route to existing Agent Coordinator functionality or VS Code tools case tool_name do "register_agent" -> AgentCoordinator.TaskRegistry.register_agent( @@ -735,6 +749,10 @@ defmodule AgentCoordinator.MCPServerManager do "heartbeat" -> AgentCoordinator.TaskRegistry.heartbeat_agent(arguments["agent_id"]) + # VS Code tools - route to VS Code Tool Provider + "vscode_" <> _rest -> + AgentCoordinator.VSCodeToolProvider.handle_tool_call(tool_name, arguments, agent_context) + _ -> %{"error" => %{"code" => -32601, "message" => "Unknown coordinator tool: #{tool_name}"}} end diff --git a/lib/agent_coordinator/vscode_permissions.ex b/lib/agent_coordinator/vscode_permissions.ex new file mode 100644 index 0000000..ab7ce71 --- /dev/null +++ b/lib/agent_coordinator/vscode_permissions.ex @@ -0,0 +1,222 @@ +defmodule AgentCoordinator.VSCodePermissions do + @moduledoc """ + Manages permissions for VS Code tool access. + + Provides fine-grained permission control for agents accessing VS Code tools, + ensuring security and preventing unauthorized operations. + """ + + require Logger + + @permission_levels %{ + read_only: 1, + editor: 2, + filesystem: 3, + terminal: 4, + git: 5, + admin: 6 + } + + @tool_permissions %{ + # File Operations (filesystem level) + "vscode_read_file" => :read_only, + "vscode_write_file" => :filesystem, + "vscode_create_file" => :filesystem, + "vscode_delete_file" => :filesystem, + "vscode_list_directory" => :read_only, + "vscode_get_workspace_folders" => :read_only, + + # Editor Operations + "vscode_get_active_editor" => :read_only, + "vscode_set_editor_content" => :editor, + "vscode_get_selection" => :read_only, + "vscode_set_selection" => :editor, + + # Command Operations (varies by command) + "vscode_run_command" => :admin, # Default to admin, will check specific commands + + # User Communication + "vscode_show_message" => :read_only + } + + @whitelisted_commands [ + # Safe editor commands + "editor.action.formatDocument", + "editor.action.formatSelection", + "editor.action.organizeImports", + "editor.fold", + "editor.unfold", + "editor.toggleFold", + + # Safe navigation commands + "workbench.action.navigateBack", + "workbench.action.navigateForward", + "workbench.action.gotoLine", + "workbench.action.quickOpen", + "workbench.action.showCommands", + + # Safe file operations + "workbench.action.files.save", + "workbench.action.files.saveAll", + "workbench.explorer.refreshExplorer", + + # Language service operations + "editor.action.goToDeclaration", + "editor.action.goToDefinition", + "editor.action.goToReferences", + "editor.action.rename", + "editor.action.quickFix" + ] + + @doc """ + Check if an agent has permission to use a specific VS Code tool. + + Returns {:ok, permission_level} if allowed, {:error, reason} if denied. + """ + def check_permission(context, tool_name, args) do + agent_id = context[:agent_id] || "unknown" + + # Get required permission level for this tool + required_level = get_required_permission(tool_name, args) + + # Get agent's permission level + agent_level = get_agent_permission_level(agent_id) + + # Check if agent has sufficient permissions + if permission_sufficient?(agent_level, required_level) do + # Additional checks for specific tools + case additional_checks(tool_name, args, context) do + :ok -> + {:ok, required_level} + {:error, reason} -> + {:error, reason} + end + else + {:error, "Insufficient permissions. Required: #{required_level}, Agent has: #{agent_level}"} + end + end + + @doc """ + Get an agent's permission level based on their capabilities and trust level. + """ + def get_agent_permission_level(agent_id) do + # For now, default to filesystem level for GitHub Copilot + # In a real implementation, this would check: + # - Agent registration data + # - Trust scores + # - Capability declarations + # - User-configured permissions + + case agent_id do + "github_copilot_session" -> :filesystem + id when is_binary(id) and byte_size(id) > 0 -> :editor # Other registered agents + _ -> :read_only # Unknown agents + end + end + + @doc """ + Update an agent's permission level (for administrative purposes). + """ + def set_agent_permission_level(agent_id, level) when level in [:read_only, :editor, :filesystem, :terminal, :git, :admin] do + # This would persist to a database or configuration store + Logger.info("Setting permission level for agent #{agent_id} to #{level}") + :ok + end + + # Private functions + + defp get_required_permission(tool_name, args) do + case Map.get(@tool_permissions, tool_name) do + nil -> :admin # Unknown tools require admin by default + :admin when tool_name == "vscode_run_command" -> + # Special handling for run_command - check specific command + command = args["command"] + if command in @whitelisted_commands do + :editor # Whitelisted commands only need editor level + else + :admin # Unknown commands need admin + end + level -> level + end + end + + defp permission_sufficient?(agent_level, required_level) do + agent_numeric = Map.get(@permission_levels, agent_level, 0) + required_numeric = Map.get(@permission_levels, required_level, 999) + agent_numeric >= required_numeric + end + + defp additional_checks(tool_name, args, context) do + case tool_name do + tool when tool in ["vscode_write_file", "vscode_create_file", "vscode_delete_file"] -> + check_workspace_bounds(args["path"], context) + + "vscode_run_command" -> + check_command_safety(args["command"], args["args"]) + + _ -> + :ok + end + end + + defp check_workspace_bounds(path, _context) when is_binary(path) do + # Ensure file operations are within workspace bounds + # This is a simplified check - real implementation would use VS Code workspace API + + forbidden_patterns = [ + # System directories + "/etc/", "/bin/", "/usr/", "/var/", "/tmp/", + # User sensitive areas + "/.ssh/", "/.config/", "/home/", "~", + # Relative path traversal + "../", "..\\" + ] + + if Enum.any?(forbidden_patterns, fn pattern -> String.contains?(path, pattern) end) do + {:error, "Path outside workspace bounds or accessing sensitive directories"} + else + :ok + end + end + + defp check_workspace_bounds(_path, _context), do: {:error, "Invalid path format"} + + defp check_command_safety(command, args) when is_binary(command) do + cond do + command in @whitelisted_commands -> + :ok + + String.starts_with?(command, "extension.") -> + {:error, "Extension commands not allowed for security"} + + String.contains?(command, "terminal") -> + {:error, "Terminal commands require terminal permission level"} + + String.contains?(command, "git") -> + {:error, "Git commands require git permission level"} + + true -> + {:error, "Command '#{command}' not in whitelist"} + end + end + + defp check_command_safety(_command, _args), do: {:error, "Invalid command format"} + + @doc """ + Get summary of permission levels and their capabilities. + """ + def get_permission_info do + %{ + levels: %{ + read_only: "File reading, workspace inspection, message display", + editor: "Text editing, selections, safe editor commands", + filesystem: "File creation/deletion, directory operations", + terminal: "Terminal access and command execution", + git: "Version control operations", + admin: "Settings, extensions, unrestricted commands" + }, + tool_requirements: @tool_permissions, + whitelisted_commands: @whitelisted_commands + } + end +end \ No newline at end of file diff --git a/lib/agent_coordinator/vscode_tool_provider.ex b/lib/agent_coordinator/vscode_tool_provider.ex new file mode 100644 index 0000000..4f60923 --- /dev/null +++ b/lib/agent_coordinator/vscode_tool_provider.ex @@ -0,0 +1,443 @@ +defmodule AgentCoordinator.VSCodeToolProvider do + @moduledoc """ + Provides VS Code Extension API tools as MCP-compatible tools. + + This module wraps VS Code's Extension API calls and exposes them as MCP tools + that can be used by agents through the unified coordination system. + """ + + require Logger + alias AgentCoordinator.VSCodePermissions + + @doc """ + Returns the list of available VS Code tools with their MCP schemas. + """ + def get_tools do + [ + # File Operations + %{ + "name" => "vscode_read_file", + "description" => "Read file contents using VS Code's file system API. Only works within workspace folders.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "path" => %{ + "type" => "string", + "description" => "Relative or absolute path to the file within the workspace" + }, + "encoding" => %{ + "type" => "string", + "description" => "File encoding (default: utf8)", + "enum" => ["utf8", "utf16le", "base64"] + } + }, + "required" => ["path"] + } + }, + %{ + "name" => "vscode_write_file", + "description" => "Write content to a file using VS Code's file system API. Creates directories if needed.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "path" => %{ + "type" => "string", + "description" => "Relative or absolute path to the file within the workspace" + }, + "content" => %{ + "type" => "string", + "description" => "Content to write to the file" + }, + "encoding" => %{ + "type" => "string", + "description" => "File encoding (default: utf8)", + "enum" => ["utf8", "utf16le", "base64"] + }, + "create_directories" => %{ + "type" => "boolean", + "description" => "Create parent directories if they don't exist (default: true)" + } + }, + "required" => ["path", "content"] + } + }, + %{ + "name" => "vscode_create_file", + "description" => "Create a new file using VS Code's file system API.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "path" => %{ + "type" => "string", + "description" => "Relative or absolute path for the new file within the workspace" + }, + "content" => %{ + "type" => "string", + "description" => "Initial content for the file (default: empty)", + "default" => "" + }, + "overwrite" => %{ + "type" => "boolean", + "description" => "Whether to overwrite if file exists (default: false)" + } + }, + "required" => ["path"] + } + }, + %{ + "name" => "vscode_delete_file", + "description" => "Delete a file or directory using VS Code's file system API.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "path" => %{ + "type" => "string", + "description" => "Relative or absolute path to the file/directory within the workspace" + }, + "recursive" => %{ + "type" => "boolean", + "description" => "Whether to delete directories recursively (default: false)" + }, + "use_trash" => %{ + "type" => "boolean", + "description" => "Whether to move to trash instead of permanent deletion (default: true)" + } + }, + "required" => ["path"] + } + }, + %{ + "name" => "vscode_list_directory", + "description" => "List contents of a directory using VS Code's file system API.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "path" => %{ + "type" => "string", + "description" => "Relative or absolute path to the directory within the workspace" + }, + "include_hidden" => %{ + "type" => "boolean", + "description" => "Whether to include hidden files/directories (default: false)" + } + }, + "required" => ["path"] + } + }, + %{ + "name" => "vscode_get_workspace_folders", + "description" => "Get list of workspace folders currently open in VS Code.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{} + } + }, + + # Editor Operations + %{ + "name" => "vscode_get_active_editor", + "description" => "Get information about the currently active text editor.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "include_content" => %{ + "type" => "boolean", + "description" => "Whether to include the full document content (default: false)" + } + } + } + }, + %{ + "name" => "vscode_set_editor_content", + "description" => "Set content in the active text editor or a specific file.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "content" => %{ + "type" => "string", + "description" => "Content to set in the editor" + }, + "file_path" => %{ + "type" => "string", + "description" => "Optional: specific file path. If not provided, uses active editor" + }, + "range" => %{ + "type" => "object", + "description" => "Optional: specific range to replace", + "properties" => %{ + "start_line" => %{"type" => "number"}, + "start_character" => %{"type" => "number"}, + "end_line" => %{"type" => "number"}, + "end_character" => %{"type" => "number"} + } + }, + "create_if_not_exists" => %{ + "type" => "boolean", + "description" => "Create file if it doesn't exist (default: false)" + } + }, + "required" => ["content"] + } + }, + %{ + "name" => "vscode_get_selection", + "description" => "Get current text selection in the active editor.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "include_content" => %{ + "type" => "boolean", + "description" => "Whether to include the selected text content (default: true)" + } + } + } + }, + %{ + "name" => "vscode_set_selection", + "description" => "Set text selection in the active editor.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "start_line" => %{ + "type" => "number", + "description" => "Start line number (0-based)" + }, + "start_character" => %{ + "type" => "number", + "description" => "Start character position (0-based)" + }, + "end_line" => %{ + "type" => "number", + "description" => "End line number (0-based)" + }, + "end_character" => %{ + "type" => "number", + "description" => "End character position (0-based)" + }, + "reveal" => %{ + "type" => "boolean", + "description" => "Whether to reveal/scroll to the selection (default: true)" + } + }, + "required" => ["start_line", "start_character", "end_line", "end_character"] + } + }, + + # Command Operations + %{ + "name" => "vscode_run_command", + "description" => "Execute a VS Code command. Only whitelisted commands are allowed for security.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "command" => %{ + "type" => "string", + "description" => "VS Code command to execute" + }, + "args" => %{ + "type" => "array", + "description" => "Arguments to pass to the command", + "items" => %{"type" => "string"} + } + }, + "required" => ["command"] + } + }, + + # User Communication + %{ + "name" => "vscode_show_message", + "description" => "Display a message to the user in VS Code.", + "inputSchema" => %{ + "type" => "object", + "properties" => %{ + "message" => %{ + "type" => "string", + "description" => "Message to display" + }, + "type" => %{ + "type" => "string", + "description" => "Message type", + "enum" => ["info", "warning", "error"] + }, + "modal" => %{ + "type" => "boolean", + "description" => "Whether to show as modal dialog (default: false)" + } + }, + "required" => ["message"] + } + } + ] + end + + @doc """ + Handle a VS Code tool call with permission checking and error handling. + """ + def handle_tool_call(tool_name, args, context) do + Logger.info("VS Code tool call: #{tool_name} with args: #{inspect(args)}") + + # Check permissions + case VSCodePermissions.check_permission(context, tool_name, args) do + {:ok, _permission_level} -> + # Execute the tool + result = execute_tool(tool_name, args, context) + + # Log the operation + log_tool_operation(tool_name, args, context, result) + + result + + {:error, reason} -> + Logger.warning("Permission denied for #{tool_name}: #{reason}") + {:error, %{"error" => "Permission denied", "reason" => reason}} + end + end + + # Private function to execute individual tools + defp execute_tool(tool_name, args, context) do + case tool_name do + "vscode_read_file" -> read_file(args, context) + "vscode_write_file" -> write_file(args, context) + "vscode_create_file" -> create_file(args, context) + "vscode_delete_file" -> delete_file(args, context) + "vscode_list_directory" -> list_directory(args, context) + "vscode_get_workspace_folders" -> get_workspace_folders(args, context) + "vscode_get_active_editor" -> get_active_editor(args, context) + "vscode_set_editor_content" -> set_editor_content(args, context) + "vscode_get_selection" -> get_selection(args, context) + "vscode_set_selection" -> set_selection(args, context) + "vscode_run_command" -> run_command(args, context) + "vscode_show_message" -> show_message(args, context) + _ -> {:error, %{"error" => "Unknown VS Code tool", "tool" => tool_name}} + end + end + + # Tool implementations (these will call VS Code Extension API via JavaScript bridge) + + defp read_file(args, _context) do + # For now, return a placeholder - we'll implement the actual VS Code API bridge + {:ok, %{ + "content" => "// VS Code file content would be here", + "path" => args["path"], + "encoding" => args["encoding"] || "utf8", + "size" => 42, + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + defp write_file(args, _context) do + {:ok, %{ + "path" => args["path"], + "bytes_written" => String.length(args["content"]), + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + defp create_file(args, _context) do + {:ok, %{ + "path" => args["path"], + "created" => true, + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + defp delete_file(args, _context) do + {:ok, %{ + "path" => args["path"], + "deleted" => true, + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + defp list_directory(args, _context) do + {:ok, %{ + "path" => args["path"], + "entries" => [ + %{"name" => "file1.txt", "type" => "file", "size" => 123}, + %{"name" => "subdir", "type" => "directory", "size" => nil} + ] + }} + end + + defp get_workspace_folders(_args, _context) do + {:ok, %{ + "folders" => [ + %{"name" => "agent_coordinator", "uri" => "file:///home/ra/agent_coordinator"} + ] + }} + end + + defp get_active_editor(args, _context) do + {:ok, %{ + "file_path" => "/home/ra/agent_coordinator/lib/agent_coordinator.ex", + "language" => "elixir", + "line_count" => 150, + "content" => if(args["include_content"], do: "// Editor content here", else: nil), + "selection" => %{ + "start" => %{"line" => 10, "character" => 5}, + "end" => %{"line" => 10, "character" => 15} + }, + "cursor_position" => %{"line" => 10, "character" => 15} + }} + end + + defp set_editor_content(args, _context) do + {:ok, %{ + "file_path" => args["file_path"], + "content_length" => String.length(args["content"]), + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + defp get_selection(args, _context) do + {:ok, %{ + "selection" => %{ + "start" => %{"line" => 5, "character" => 0}, + "end" => %{"line" => 8, "character" => 20} + }, + "content" => if(args["include_content"], do: "Selected text here", else: nil), + "is_empty" => false + }} + end + + defp set_selection(args, _context) do + {:ok, %{ + "selection" => %{ + "start" => %{"line" => args["start_line"], "character" => args["start_character"]}, + "end" => %{"line" => args["end_line"], "character" => args["end_character"]} + }, + "revealed" => args["reveal"] != false + }} + end + + defp run_command(args, _context) do + # This would execute actual VS Code commands + {:ok, %{ + "command" => args["command"], + "args" => args["args"] || [], + "result" => "Command executed successfully", + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + defp show_message(args, _context) do + {:ok, %{ + "message" => args["message"], + "type" => args["type"] || "info", + "displayed" => true, + "timestamp" => DateTime.utc_now() |> DateTime.to_iso8601() + }} + end + + # Logging function + defp log_tool_operation(tool_name, args, context, result) do + Logger.info("VS Code tool operation completed", %{ + tool: tool_name, + agent_id: context[:agent_id], + args_summary: inspect(Map.take(args, ["path", "command", "message"])), + success: match?({:ok, _}, result), + timestamp: DateTime.utc_now() + }) + end +end \ No newline at end of file