"""Runtime tools for listing and managing skills.""" from __future__ import annotations from dataclasses import dataclass import json from typing import Any from beaver.tools.base import BaseTool, ToolContext, ToolResult, ToolSpec def _result(tool_name: str, success: bool, **payload: Any) -> ToolResult: return ToolResult( success=success, tool_name=tool_name, content=json.dumps({"success": success, **payload}, ensure_ascii=False, indent=2), error=None if success else str(payload.get("error") or "failed"), ) @dataclass(slots=True) class SkillsListTool(BaseTool): @property def spec(self) -> ToolSpec: return ToolSpec( name="skills_list", description="List available skills with descriptions.", input_schema={"type": "object", "properties": {}}, toolset="skills", ) async def invoke(self, arguments: dict[str, Any], context: ToolContext) -> ToolResult: loader = context.get("skills_loader") if loader is None: return _result(self.spec.name, False, error="skills_loader is unavailable") skills = [ { "name": record.name, "description": record.description, "source": record.source, "version": record.version, "tool_hints": list(record.tool_hints), } for record in loader.list_skills(filter_unavailable=False) ] return _result(self.spec.name, True, skills=skills) @dataclass(slots=True) class SkillManageTool(BaseTool): @property def spec(self) -> ToolSpec: return ToolSpec( name="skill_manage", description="Create a new skill draft. Publishing still goes through the normal review/publish APIs.", input_schema={ "type": "object", "properties": { "action": {"type": "string", "enum": ["create_draft"]}, "name": {"type": "string"}, "description": {"type": "string"}, "content": {"type": "string"}, }, "required": ["action", "name", "content"], }, toolset="skills", ) async def invoke(self, arguments: dict[str, Any], context: ToolContext) -> ToolResult: if arguments.get("action") != "create_draft": return _result(self.spec.name, False, error="only create_draft is supported") draft_service = context.get("draft_service") if draft_service is None: return _result(self.spec.name, False, error="draft_service is unavailable") name = str(arguments.get("name") or "").strip() content = str(arguments.get("content") or "").strip() if not name or not content: return _result(self.spec.name, False, error="name and content are required") draft = draft_service.create_new_skill_draft( skill_name=name, proposed_content=content, proposed_frontmatter={"description": str(arguments.get("description") or name)}, created_by=context.user_id or "agent", reason="created by skill_manage tool", trigger_session_id=context.session_id, ) return _result(self.spec.name, True, draft=draft.to_dict())