Files
SneakyCode/app/tools/registry.py
Phillip Tarrant 3f9012e6c2 feat: implement tweaks plan - modals, smart shell, spinner, /models, debug log, skills
Phase 1: Permission modal dialog, session resume modal, HistoryInput with
up/down arrow cycling, remove "You:" echo from chat log, LLM client cleanup
on unmount.

Phase 2: Smart shell auto-approve using allowed/denied command lists from
ToolsConfig, animated thinking spinner with live token count in status bar.

Phase 3: /models slash command (list + switch), CLI directory positional
argument, JSONL debug logger with rotation.

Phase 4: Skills system with SkillsManager, load_skill LLM tool, /skills
listing, skill invocation via slash commands, system prompt integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:46:44 -05:00

100 lines
2.9 KiB
Python

"""Tool registration and schema export."""
from __future__ import annotations
import logging
from pathlib import Path
from typing import TYPE_CHECKING, Any
from app.models.config import AppConfig
from app.tools.base import BaseTool
if TYPE_CHECKING:
from app.services.skills import SkillsManager
logger = logging.getLogger(__name__)
class ToolRegistry:
"""Registry of available tools, keyed by name."""
def __init__(self) -> None:
self._tools: dict[str, BaseTool] = {}
def register(self, tool: BaseTool) -> None:
"""Register a tool instance. Raises ValueError on duplicate name."""
if tool.name in self._tools:
raise ValueError(f"Duplicate tool name: '{tool.name}'")
self._tools[tool.name] = tool
logger.debug("Registered tool: %s", tool.name)
def get(self, name: str) -> BaseTool | None:
"""Look up a tool by name."""
return self._tools.get(name)
def get_all(self) -> dict[str, BaseTool]:
"""Return all registered tools."""
return dict(self._tools)
def get_openai_tools_schema(self) -> list[dict[str, Any]]:
"""Return OpenAI function-calling schemas for all registered tools."""
return [tool.get_openai_schema() for tool in self._tools.values()]
def create_default_registry(
workspace_root: Path,
config: AppConfig,
skills_manager: SkillsManager | None = None,
) -> ToolRegistry:
"""Create a ToolRegistry populated with all built-in tools."""
# Read tools
from app.tools.filesystem import ListDirTool, ReadFileTool
# Write tools
from app.tools.filesystem import DeleteFileTool, MakeDirTool, WriteFileTool
# Edit tools
from app.tools.edit import PatchApplyTool, StrReplaceTool
# Shell tools
from app.tools.shell import RunCommandTool
# Control flow
from app.tools.finish import FinishTool
# Search tools
from app.tools.search import FindFilesTool, GrepFilesTool
registry = ToolRegistry()
# Read
registry.register(ReadFileTool(workspace_root, config))
registry.register(ListDirTool(workspace_root, config))
# Search
registry.register(GrepFilesTool(workspace_root, config))
registry.register(FindFilesTool(workspace_root, config))
# Write
registry.register(WriteFileTool(workspace_root, config))
registry.register(MakeDirTool(workspace_root, config))
registry.register(DeleteFileTool(workspace_root, config))
# Edit
registry.register(StrReplaceTool(workspace_root, config))
registry.register(PatchApplyTool(workspace_root, config))
# Shell
registry.register(RunCommandTool(workspace_root, config))
# Control flow
registry.register(FinishTool(workspace_root, config))
# Skills (conditional)
if skills_manager is not None:
from app.tools.skills import LoadSkillTool
registry.register(LoadSkillTool(workspace_root, config, skills_manager))
return registry