"""JARVIS VS Code & Copilot Tools — open editors, run commands, assign tasks to Copilot.""" import subprocess import os import json from tools import tool def _run_osascript(script: str, timeout: int = 10) -> str: result = subprocess.run( ["osascript", "-e", script], capture_output=True, text=True, timeout=timeout, ) return result.stdout.strip() def _vscode_cli(*args, timeout: int = 15) -> str: """Run VS Code CLI command.""" code_paths = [ "/usr/local/bin/code", "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code", os.path.expanduser("~/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"), ] code_cmd = None for p in code_paths: if os.path.exists(p): code_cmd = p break if code_cmd is None: # Try PATH result = subprocess.run(["which", "code"], capture_output=True, text=True) if result.returncode == 0: code_cmd = result.stdout.strip() else: return "Error: VS Code CLI ('code') not found. Install from VS Code: Cmd+Shift+P → 'Shell Command: Install code command'" result = subprocess.run( [code_cmd] + list(args), capture_output=True, text=True, timeout=timeout, ) output = result.stdout.strip() if result.stderr.strip(): output += f"\n{result.stderr.strip()}" return output or "(done)" # ─── VS Code Operations ────────────────────────── @tool( name="vscode_open", description="Open a file or folder in VS Code", parameters={ "type": "object", "properties": { "path": {"type": "string", "description": "File or folder path to open"}, "new_window": {"type": "boolean", "description": "Open in new window (default false)"}, }, "required": ["path"], }, ) def vscode_open(path: str, new_window: bool = False) -> str: path = os.path.expanduser(path) args = ["--goto", path] if os.path.isfile(path) else [path] if new_window: args.insert(0, "--new-window") return _vscode_cli(*args) @tool( name="vscode_open_terminal", description="Open VS Code's integrated terminal in a specific folder", parameters={ "type": "object", "properties": { "folder": {"type": "string", "description": "Folder to open terminal in (defaults to current project)"}, }, }, ) def vscode_open_terminal(folder: str = "") -> str: folder = os.path.expanduser(folder) if folder else os.getcwd() # Open folder in VS Code, then use AppleScript to open integrated terminal _vscode_cli(folder) _run_osascript(''' tell application "Visual Studio Code" to activate delay 0.5 tell application "System Events" keystroke "`" using {control down} end tell ''') return f"VS Code terminal opened in {folder}" @tool( name="vscode_run_command", description="Execute a VS Code command (like from Command Palette). Use for any VS Code action.", parameters={ "type": "object", "properties": { "command": {"type": "string", "description": "VS Code command ID (e.g. 'workbench.action.togglePanel', 'editor.action.formatDocument')"}, }, "required": ["command"], }, ) def vscode_run_command(command: str) -> str: # Use AppleScript to trigger Command Palette and type the command _run_osascript(f''' tell application "Visual Studio Code" to activate delay 0.3 tell application "System Events" keystroke "p" using {{shift down, command down}} delay 0.3 keystroke ">{command}" delay 0.2 keystroke return end tell ''') return f"Executed VS Code command: {command}" @tool( name="copilot_chat", description="Send a task/prompt to GitHub Copilot Chat in VS Code. Opens Copilot Chat and types your message.", parameters={ "type": "object", "properties": { "message": {"type": "string", "description": "The task or question to send to Copilot"}, "agent": {"type": "string", "description": "Copilot agent to use: 'workspace' for @workspace, 'terminal' for @terminal, empty for default"}, }, "required": ["message"], }, ) def copilot_chat(message: str, agent: str = "") -> str: # Sanitize message for AppleScript safe_msg = message.replace('"', '\\"').replace("\\", "\\\\") agent_prefix = "" if agent == "workspace": agent_prefix = "@workspace " elif agent == "terminal": agent_prefix = "@terminal " full_msg = f"{agent_prefix}{safe_msg}" _run_osascript(f''' tell application "Visual Studio Code" to activate delay 0.3 tell application "System Events" -- Open Copilot Chat (Ctrl+Cmd+I) keystroke "i" using {{control down, command down}} delay 0.5 keystroke "{full_msg}" delay 0.2 keystroke return end tell ''', timeout=15) return f"Sent to Copilot Chat: {full_msg[:100]}" @tool( name="copilot_inline", description="Use Copilot inline edit (Cmd+I) on the currently open file in VS Code with a specific instruction", parameters={ "type": "object", "properties": { "instruction": {"type": "string", "description": "What to tell Copilot to do (e.g. 'add error handling', 'refactor this function')"}, }, "required": ["instruction"], }, ) def copilot_inline(instruction: str) -> str: safe_instr = instruction.replace('"', '\\"').replace("\\", "\\\\") _run_osascript(f''' tell application "Visual Studio Code" to activate delay 0.3 tell application "System Events" keystroke "i" using {{command down}} delay 0.5 keystroke "{safe_instr}" delay 0.2 keystroke return end tell ''', timeout=15) return f"Copilot inline edit triggered: {instruction[:80]}" @tool( name="vscode_list_extensions", description="List installed VS Code extensions", parameters={"type": "object", "properties": {}}, ) def vscode_list_extensions() -> str: output = _vscode_cli("--list-extensions") extensions = output.strip().split("\n") return f"Installed extensions ({len(extensions)}):\n" + "\n".join(f" • {e}" for e in extensions[:30]) @tool( name="vscode_diff", description="Open a diff view between two files in VS Code", parameters={ "type": "object", "properties": { "file1": {"type": "string", "description": "First file path"}, "file2": {"type": "string", "description": "Second file path"}, }, "required": ["file1", "file2"], }, ) def vscode_diff(file1: str, file2: str) -> str: f1 = os.path.expanduser(file1) f2 = os.path.expanduser(file2) return _vscode_cli("--diff", f1, f2)