""" Claude API agent with tool use. All JARVIS capabilities are registered as tools that Claude can invoke. """ import os import json import anthropic from capabilities import ( screen_vision, calendar_access, email_access, notes_manager, task_manager, file_system, terminal_control, browser_control, git_manager, ) client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) SYSTEM_PROMPT = """You are JARVIS (Just A Rather Very Intelligent System), a Windows-based AI assistant. You have access to the following capabilities: screen vision, Google Calendar, Gmail, Google Tasks, Google Keep notes, file system management, PowerShell terminal, Chrome browser automation, and Git. Be concise, direct, and helpful. When using tools, always explain what you found.""" TOOLS = [ { "name": "capture_screen", "description": "Take a screenshot and identify active windows and open applications", "input_schema": {"type": "object", "properties": {}, "required": []} }, { "name": "get_calendar_events", "description": "Get today's events or upcoming events from Google Calendar", "input_schema": { "type": "object", "properties": { "days_ahead": {"type": "integer", "description": "Number of days ahead to fetch (default 0 = today only)", "default": 0} }, "required": [] } }, { "name": "get_emails", "description": "Get unread emails from Gmail or search by query", "input_schema": { "type": "object", "properties": { "query": {"type": "string", "description": "Gmail search query (e.g. 'from:boss@company.com' or 'subject:invoice')"}, "count": {"type": "integer", "description": "Max number of emails to return", "default": 10} }, "required": [] } }, { "name": "get_tasks", "description": "Get pending tasks from Google Tasks", "input_schema": {"type": "object", "properties": {}, "required": []} }, { "name": "create_task", "description": "Create a new task in Google Tasks", "input_schema": { "type": "object", "properties": { "title": {"type": "string", "description": "Task title"}, "notes": {"type": "string", "description": "Optional task notes"}, "due": {"type": "string", "description": "Optional due date in RFC 3339 format"} }, "required": ["title"] } }, { "name": "create_note", "description": "Create a note in Google Keep", "input_schema": { "type": "object", "properties": { "title": {"type": "string", "description": "Note title"}, "content": {"type": "string", "description": "Note body text"} }, "required": ["title", "content"] } }, { "name": "run_powershell", "description": "Execute a PowerShell command and return the output", "input_schema": { "type": "object", "properties": { "command": {"type": "string", "description": "PowerShell command to run"} }, "required": ["command"] } }, { "name": "get_git_status", "description": "Get git status of a project directory", "input_schema": { "type": "object", "properties": { "path": {"type": "string", "description": "Absolute path to the git repository"} }, "required": ["path"] } }, { "name": "open_browser", "description": "Open Chrome and navigate to a URL or perform a Google search", "input_schema": { "type": "object", "properties": { "url": {"type": "string", "description": "URL to navigate to"}, "search": {"type": "string", "description": "Search query (alternative to url)"} }, "required": [] } }, { "name": "create_project_folder", "description": "Create a new project folder with a README", "input_schema": { "type": "object", "properties": { "name": {"type": "string", "description": "Project name"}, "base_path": {"type": "string", "description": "Base directory path", "default": "C:/Projects"} }, "required": ["name"] } } ] def _dispatch_tool(name: str, inputs: dict) -> str: """Route a Claude tool_use call to the appropriate capability module.""" try: if name == "capture_screen": windows = screen_vision.list_open_windows() active = screen_vision.get_active_window_title() return json.dumps({"active_window": active, "open_windows": windows[:10]}) elif name == "get_calendar_events": days = inputs.get("days_ahead", 0) if days == 0: events = calendar_access.get_todays_events() else: events = calendar_access.get_upcoming_events(days=days) return json.dumps(events) elif name == "get_emails": query = inputs.get("query") count = inputs.get("count", 10) if query: emails = email_access.search_emails(query) else: emails = email_access.get_unread_emails(count=count) return json.dumps(emails) elif name == "get_tasks": return json.dumps(task_manager.get_pending_tasks()) elif name == "create_task": result = task_manager.create_task( title=inputs["title"], notes=inputs.get("notes"), due=inputs.get("due") ) return json.dumps({"created": True, "id": result.get("id")}) elif name == "create_note": note_id = notes_manager.create_note(inputs["title"], inputs["content"]) return json.dumps({"created": True, "id": note_id}) elif name == "run_powershell": result = terminal_control.run_powershell(inputs["command"]) return json.dumps(result) elif name == "get_git_status": status = git_manager.get_git_status(inputs["path"]) return json.dumps({"status": status}) elif name == "open_browser": if inputs.get("url"): browser_control.navigate_to(inputs["url"]) return json.dumps({"opened": inputs["url"]}) elif inputs.get("search"): browser_control.search_web(inputs["search"]) return json.dumps({"searching": inputs["search"]}) elif name == "create_project_folder": path = file_system.create_project( inputs["name"], inputs.get("base_path", "C:/Projects") ) return json.dumps({"created": path}) return json.dumps({"error": f"Unknown tool: {name}"}) except Exception as e: return json.dumps({"error": str(e)}) async def get_response(user_text: str) -> str: """Send user text to Claude with tool use enabled. Returns final text response.""" messages = [{"role": "user", "content": user_text}] while True: response = client.messages.create( model="claude-opus-4-5", max_tokens=1024, system=SYSTEM_PROMPT, tools=TOOLS, messages=messages ) # If Claude wants to use a tool, dispatch and continue if response.stop_reason == "tool_use": tool_results = [] for block in response.content: if block.type == "tool_use": result = _dispatch_tool(block.name, block.input) tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": result }) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results}) else: # Final text response for block in response.content: if hasattr(block, "text"): return block.text return "I couldn't generate a response."