""" Tool registration functions that create ToolMetadata and register all available tools. """ import os from typing import Dict, Any, Callable from .registry import ToolRegistry, ToolMetadata from .base_tools import ( file_tools, system_tools, browser_tools, ai_tools, memory_tools, skill_tools, task_tools ) from .config_tools import config_tools def create_tool_registry() -> ToolRegistry: """Create and populate a tool registry with all available tools.""" registry = ToolRegistry() # Register file tools _register_file_tools(registry, file_tools) # Register system tools _register_system_tools(registry, system_tools) # Register browser tools _register_browser_tools(registry, browser_tools) # Register AI tools _register_ai_tools(registry, ai_tools) # Register memory tools _register_memory_tools(registry, memory_tools) # Register skill tools _register_skill_tools(registry, skill_tools) # Register task tools _register_task_tools(registry, task_tools) # Register config tools _register_config_tools(registry, config_tools) return registry def _register_file_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register file operation tools.""" # read_file registry.register_tool( 'read_file', tools['read_file'], ToolMetadata( name='read_file', description='Read a text file with line numbers and pagination', parameters={ 'path': {'type': 'string', 'required': True, 'description': 'File path to read'}, 'offset': {'type': 'integer', 'required': False, 'default': 1, 'description': 'Line number to start from (1-indexed)'}, 'limit': {'type': 'integer', 'required': False, 'default': 500, 'description': 'Maximum number of lines to read'} }, permissions=['file_read'], examples=[ {'path': 'config.txt'}, {'path': 'logs/app.log', 'offset': 100, 'limit': 50} ], security_notes='Cannot read files outside user work directory or system protected directories' ) ) # write_file registry.register_tool( 'write_file', tools['write_file'], ToolMetadata( name='write_file', description='Write content to a file, completely replacing existing content', parameters={ 'path': {'type': 'string', 'required': True, 'description': 'Path to the file to write'}, 'content': {'type': 'string', 'required': True, 'description': 'Complete content to write to the file'} }, permissions=['file_write'], examples=[ {'path': 'output.txt', 'content': 'Hello World!'} ], security_notes='Cannot write to system protected directories or outside user work directory' ) ) # search_files registry.register_tool( 'search_files', tools['search_files'], ToolMetadata( name='search_files', description='Search file contents or find files by name using ripgrep', parameters={ 'pattern': {'type': 'string', 'required': True, 'description': 'Regex pattern for content search, or glob pattern for file search'}, 'target': {'type': 'string', 'required': False, 'default': 'content', 'description': "'content' searches inside files, 'files' searches for files by name"}, 'path': {'type': 'string', 'required': False, 'default': '.', 'description': 'Directory or file to search in'}, 'file_glob': {'type': 'string', 'required': False, 'description': 'Filter files by pattern in grep mode (e.g., "*.py")'}, 'limit': {'type': 'integer', 'required': False, 'default': 50, 'description': 'Maximum number of results to return'} }, permissions=['file_read'], examples=[ {'pattern': 'TODO', 'target': 'content', 'path': './src'}, {'pattern': '*.py', 'target': 'files', 'path': './src'} ], security_notes='Search is limited to user accessible directories' ) ) # patch registry.register_tool( 'patch', tools['patch'], ToolMetadata( name='patch', description='Targeted find-and-replace edits in files with fuzzy matching', parameters={ 'mode': {'type': 'string', 'required': False, 'default': 'replace', 'description': "Edit mode: 'replace' for targeted find-and-replace, 'patch' for V4A multi-file patches"}, 'path': {'type': 'string', 'required': False, 'description': 'File path to edit (required for replace mode)'}, 'old_string': {'type': 'string', 'required': False, 'description': 'Text to find in the file (required for replace mode)'}, 'new_string': {'type': 'string', 'required': False, 'description': 'Replacement text (required for replace mode)'}, 'replace_all': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Replace all occurrences instead of requiring a unique match'} }, permissions=['file_write'], examples=[ {'mode': 'replace', 'path': 'config.txt', 'old_string': 'old_value', 'new_string': 'new_value'} ], security_notes='Edits are limited to user accessible files and require proper permissions' ) ) def _register_system_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register system operation tools.""" # terminal registry.register_tool( 'terminal', tools['terminal'], ToolMetadata( name='terminal', description='Execute shell commands on a Linux environment', parameters={ 'command': {'type': 'string', 'required': True, 'description': 'The command to execute on the VM'}, 'background': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Run the command in the background'}, 'timeout': {'type': 'integer', 'required': False, 'default': 180, 'description': 'Max seconds to wait'}, 'workdir': {'type': 'string', 'required': False, 'description': 'Working directory for this command'}, 'pty': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Run in pseudo-terminal (PTY) mode'}, 'notify_on_complete': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Auto-notify when background process completes'} }, permissions=['system_execute'], examples=[ {'command': 'ls -la'}, {'command': 'python script.py', 'background': True, 'notify_on_complete': True} ], security_notes='Commands are executed with user privileges. Dangerous commands may be restricted.' ) ) # process registry.register_tool( 'process', tools['process'], ToolMetadata( name='process', description='Manage background processes started with terminal(background=true)', parameters={ 'action': {'type': 'string', 'required': True, 'description': "Action: 'list', 'poll', 'log', 'wait', 'kill', 'write', 'submit', 'close'"}, 'session_id': {'type': 'string', 'required': False, 'description': 'Process session ID (required for all actions except list)'}, 'data': {'type': 'string', 'required': False, 'description': 'Text to send to process stdin (for write and submit actions)'}, 'timeout': {'type': 'integer', 'required': False, 'description': 'Max seconds to block for wait action'}, 'offset': {'type': 'integer', 'required': False, 'description': 'Line offset for log action'}, 'limit': {'type': 'integer', 'required': False, 'description': 'Max lines to return for log action'} }, permissions=['system_manage'], examples=[ {'action': 'list'}, {'action': 'poll', 'session_id': 'abc123'} ], security_notes='Can only manage processes started by the current user session' ) ) # execute_code registry.register_tool( 'execute_code', tools['execute_code'], ToolMetadata( name='execute_code', description='Run a Python script that can call Hermes tools programmatically', parameters={ 'code': {'type': 'string', 'required': True, 'description': 'Python code to execute'} }, permissions=['code_execute'], examples=[ {'code': 'print("Hello from Python!")'} ], security_notes='Code execution is sandboxed but still requires caution. Limited to 5-minute timeout.' ) ) def _register_browser_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register browser automation tools.""" browser_permissions = ['browser_access'] # Common browser tool metadata browser_examples = [{'url': 'https://example.com'}] browser_security_notes = 'Browser access is limited to HTTP/HTTPS URLs. Local file access may be restricted.' browser_tools_list = [ ('browser_navigate', 'Navigate to a URL in the browser'), ('browser_snapshot', 'Get a text-based snapshot of the current page'), ('browser_click', 'Click on an element identified by its ref ID'), ('browser_type', 'Type text into an input field identified by its ref ID'), ('browser_press', 'Press a keyboard key'), ('browser_scroll', 'Scroll the page in a direction'), ('browser_console', 'Get browser console output and JavaScript errors'), ('browser_get_images', 'Get a list of all images on the current page'), ('browser_vision', 'Take a screenshot and analyze it with vision AI'), ('browser_back', 'Navigate back to the previous page') ] for tool_name, description in browser_tools_list: registry.register_tool( tool_name, tools[tool_name], ToolMetadata( name=tool_name, description=description, parameters={}, # Parameters vary by tool, simplified for now permissions=browser_permissions, examples=browser_examples, security_notes=browser_security_notes ) ) def _register_ai_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register AI-powered tools.""" # vision_analyze registry.register_tool( 'vision_analyze', tools['vision_analyze'], ToolMetadata( name='vision_analyze', description='Analyze images using AI vision with comprehensive description and Q&A', parameters={ 'image_url': {'type': 'string', 'required': True, 'description': 'Image URL or local file path to analyze'}, 'question': {'type': 'string', 'required': True, 'description': 'Specific question about the image content'} }, permissions=['ai_vision'], examples=[ {'image_url': 'https://example.com/image.jpg', 'question': 'What objects are in this image?'} ], security_notes='Image analysis may have privacy implications. Local files must be in accessible directories.' ) ) # text_to_speech registry.register_tool( 'text_to_speech', tools['text_to_speech'], ToolMetadata( name='text_to_speech', description='Convert text to speech audio with user-configured voice', parameters={ 'text': {'type': 'string', 'required': True, 'description': 'Text to convert to speech (under 4000 characters)'}, 'output_path': {'type': 'string', 'required': False, 'description': 'Optional custom file path to save the audio'} }, permissions=['ai_tts'], examples=[ {'text': 'Hello, this is a test message.'} ], security_notes='Audio files are saved to user-accessible directories only' ) ) def _register_memory_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register memory management tools.""" # memory registry.register_tool( 'memory', tools['memory'], ToolMetadata( name='memory', description='Save durable information to persistent memory across sessions', parameters={ 'action': {'type': 'string', 'required': True, 'description': "Action: 'add', 'replace', or 'remove'"}, 'target': {'type': 'string', 'required': True, 'description': "Target: 'memory' for personal notes, 'user' for user profile"}, 'content': {'type': 'string', 'required': False, 'description': 'Content to add/replace (required for add/replace)'}, 'old_text': {'type': 'string', 'required': False, 'description': 'Text to identify entry for replace/remove'} }, permissions=['memory_manage'], examples=[ {'action': 'add', 'target': 'memory', 'content': 'User prefers dark mode'} ], security_notes='Memory is isolated per user. Sensitive information should be handled carefully.' ) ) # session_search registry.register_tool( 'session_search', tools['session_search'], ToolMetadata( name='session_search', description='Search long-term memory of past conversations or browse recent sessions', parameters={ 'query': {'type': 'string', 'required': False, 'description': 'Search query keywords or phrases'}, 'limit': {'type': 'integer', 'required': False, 'default': 3, 'description': 'Max sessions to summarize'} }, permissions=['memory_read'], examples=[ {'query': 'database setup'}, {} # Browse recent sessions ], security_notes='Only searches sessions belonging to the current user' ) ) def _register_skill_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register skill management tools.""" # skill_view registry.register_tool( 'skill_view', tools['skill_view'], ToolMetadata( name='skill_view', description='Load a skill\'s full content or access its linked files', parameters={ 'name': {'type': 'string', 'required': True, 'description': 'The skill name'}, 'file_path': {'type': 'string', 'required': False, 'description': 'Path to a linked file within the skill'} }, permissions=['skill_read'], examples=[ {'name': 'module-development-spec'}, {'name': 'bricks-framework', 'file_path': 'templates/base.ui'} ], security_notes='Skills are loaded from the configured skills_path directory' ) ) # skills_list registry.register_tool( 'skills_list', tools['skills_list'], ToolMetadata( name='skills_list', description='List available skills with name and description', parameters={ 'category': {'type': 'string', 'required': False, 'description': 'Optional category filter'} }, permissions=['skill_read'], examples=[ {}, {'category': 'software-development'} ], security_notes='Lists only skills accessible to the current user' ) ) # skill_manage registry.register_tool( 'skill_manage', tools['skill_manage'], ToolMetadata( name='skill_manage', description='Manage skills (create, update, delete) with procedural memory', parameters={ 'action': {'type': 'string', 'required': True, 'description': "Action: 'create', 'patch', 'edit', 'delete', 'write_file', 'remove_file'"}, 'name': {'type': 'string', 'required': True, 'description': 'Skill name'}, 'content': {'type': 'string', 'required': False, 'description': 'Full SKILL.md content (for create/edit)'} }, permissions=['skill_manage'], examples=[ {'action': 'list'}, {'action': 'create', 'name': 'my-skill', 'content': '# My Skill\n...'} ], security_notes='Skill management affects the shared skills repository. Use with caution.' ) ) def _register_task_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register task management tools.""" # todo registry.register_tool( 'todo', tools['todo'], ToolMetadata( name='todo', description='Manage task list for the current session with priority ordering', parameters={ 'todos': {'type': 'array', 'required': False, 'description': 'Task items to write (omit to read current list)'}, 'merge': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Update existing items by id, add new ones'} }, permissions=['task_manage'], examples=[ {}, {'todos': [{'id': 'task1', 'content': 'Do something', 'status': 'pending'}]} ], security_notes='Task lists are session-specific and not persisted across sessions' ) ) # delegate_task registry.register_tool( 'delegate_task', tools['delegate_task'], ToolMetadata( name='delegate_task', description='Spawn subagents to work on tasks in isolated contexts', parameters={ 'goal': {'type': 'string', 'required': False, 'description': 'Single task goal'}, 'tasks': {'type': 'array', 'required': False, 'description': 'Batch tasks to run in parallel (limit 3)'}, 'context': {'type': 'string', 'required': False, 'description': 'Background information for subagent'} }, permissions=['task_delegate'], examples=[ {'goal': 'Debug this error', 'context': 'Error message: ...'}, {'tasks': [{'goal': 'Task A'}, {'goal': 'Task B'}]} ], security_notes='Subagents have no memory of parent conversation and cannot call clarify' ) ) # clarify registry.register_tool( 'clarify', tools['clarify'], ToolMetadata( name='clarify', description='Ask user for clarification, feedback, or decision before proceeding', parameters={ 'question': {'type': 'string', 'required': True, 'description': 'Question to present to the user'}, 'choices': {'type': 'array', 'required': False, 'description': 'Up to 4 answer choices for multiple choice'} }, permissions=['user_interact'], examples=[ {'question': 'Which approach should I take?', 'choices': ['Option A', 'Option B']} ], security_notes='Used for interactive decision making with the user' ) ) # cronjob registry.register_tool( 'cronjob', tools['cronjob'], ToolMetadata( name='cronjob', description='Manage scheduled cron jobs with compressed tool interface', parameters={ 'action': {'type': 'string', 'required': True, 'description': "Action: 'create', 'list', 'update', 'pause', 'resume', 'remove', 'run'"}, 'prompt': {'type': 'string', 'required': False, 'description': 'Self-contained prompt for create action'}, 'schedule': {'type': 'string', 'required': False, 'description': 'Schedule string like \'30m\', \'every 2h\', or cron format'} }, permissions=['schedule_manage'], examples=[ {'action': 'list'}, {'action': 'create', 'prompt': 'Check system status', 'schedule': 'every 1h'} ], security_notes='Cron jobs run autonomously with no user present. Prompts must be self-contained.' ) ) def _register_config_tools(registry: ToolRegistry, tools: Dict[str, Callable]): """Register configuration tools.""" # get_app_config registry.register_tool( 'get_app_config', tools['get_app_config'], ToolMetadata( name='get_app_config', description='Read application configuration file and extract skills_path', parameters={ 'config_path': {'type': 'string', 'required': False, 'description': 'Optional custom config file path'} }, permissions=['config_read'], examples=[ {}, {'config_path': './custom/config.json'} ], security_notes='Reads configuration files from accessible directories only' ) )