"""Hook management tools for Hermes. Provides tools for listing, creating, deleting, enabling, and disabling hooks. Hooks are YAML files in ~/.hermes/hooks/ that run actions on file/command events. """ import json import os from tools.registry import registry # Lazy import to avoid circular imports def _get_hooks_module(): from tools import hooks as _hooks return _hooks def _hooks_list(action: str = "list", hook_path: str = None) -> str: """Tool wrapper for hook operations.""" hooks = _get_hooks_module() if action == "list": result = hooks.list_hooks() if not result: return json.dumps({"hooks": {}, "message": "No hooks configured"}) return json.dumps({"hooks": result}, ensure_ascii=False) elif action == "create": if not hook_path: return json.dumps({"error": "hook_path required for create"}) parts = hook_path.split("/", 1) if len(parts) != 2: return json.dumps({"error": "hook_path must be in format '/' (e.g. 'post-write/pytest-on-py')"}) subdir, name = parts if subdir not in ("pre-write", "post-write", "pre-command", "post-command"): return json.dumps({"error": f"Invalid subdir '{subdir}'. Must be one of: pre-write, post-write, pre-command, post-command"}) success, msg = hooks.create_hook( name=name, subdir=subdir, trigger_event="file_write", pattern="*.py", actions=[{"type": "command", "command": "pytest {file}", "timeout": 60, "block_on_failure": True}], description=f"Auto-created hook: {name}", ) return json.dumps({"success": success, "message": msg}, ensure_ascii=False) elif action == "delete": if not hook_path: return json.dumps({"error": "hook_path required for delete"}) success, msg = hooks.delete_hook(hook_path) return json.dumps({"success": success, "message": msg}, ensure_ascii=False) elif action == "enable": if not hook_path: return json.dumps({"error": "hook_path required for enable"}) success, msg = hooks.enable_hook(hook_path) return json.dumps({"success": success, "message": msg}, ensure_ascii=False) elif action == "disable": if not hook_path: return json.dumps({"error": "hook_path required for disable"}) success, msg = hooks.disable_hook(hook_path) return json.dumps({"success": success, "message": msg}, ensure_ascii=False) elif action == "dry_run": if not hook_path: return json.dumps({"error": "hook_path required for dry_run"}) # Dry run a specific hook hooks_dir = hooks._get_hooks_dir() full_path = hooks_dir / hook_path if not full_path.exists(): return json.dumps({"error": f"Hook not found: {hook_path}"}) hook = hooks._load_hook_file(full_path) if not hook: return json.dumps({"error": f"Failed to load hook: {hook_path}"}) # Simulate what would happen return json.dumps({ "hook": hook.name, "description": hook.description, "trigger_event": hook.trigger_event, "trigger_pattern": hook.trigger_pattern, "actions": [{"type": a.type, "command": a.command} for a in hook.actions], "dry_run": True, "note": "Commands would be shown but not executed", }, ensure_ascii=False) else: return json.dumps({"error": f"Unknown action: {action}"}) # Schema for hook tool _hooks_schema = { "name": "hooks", "description": """Manage Hermes hooks — trigger-based automation for file and command events. Hooks run actions (commands, notifications) when files are written or commands are executed. **Hook directories:** - `pre-write/` — before file write (can block the write) - `post-write/` — after file write - `pre-command/` — before terminal command (can block the command) - `post-command/` — after terminal command **Actions:** - `command` — run a shell command - `notification` — log or send notification - `log` — log a message **Examples:** - List all hooks: `{"action": "list"}` - Create a Python test hook: `{"action": "create", "hook_path": "post-write/pytest-on-py"}` - Delete a hook: `{"action": "delete", "hook_path": "post-write/pytest-on-py"}` - Dry run a hook: `{"action": "dry_run", "hook_path": "post-write/pytest-on-py"}`""", "parameters": { "type": "object", "properties": { "action": { "type": "string", "enum": ["list", "create", "delete", "enable", "disable", "dry_run"], "description": "Action to perform", }, "hook_path": { "type": "string", "description": "Hook path for create/delete/enable/disable/dry_run. Format: '/' e.g. 'post-write/pytest-on-py'", }, }, "required": ["action"], }, } def _check_hooks_available() -> bool: """Check if hooks module is available.""" return True registry.register( name="hooks", toolset="file", schema=_hooks_schema, handler=lambda args, **kw: _hooks_list( action=args.get("action", "list"), hook_path=args.get("hook_path"), ), check_fn=_check_hooks_available, requires_env=[], is_async=False, description="Manage Hermes hooks for file/command automation", emoji="🪝", )