Source code for atloop.tools.interaction.skill_tool
"""Skill tool for loading skill knowledge on-demand."""
from typing import Any, Dict, Optional
from atloop.tools.base import BaseTool, ToolResult
[docs]
class SkillTool(BaseTool):
"""
Tool for loading skill content on-demand.
When the LLM needs guidance from a skill (e.g., tool-usage, error-handling),
it uses this tool to load the full skill content. The skill content is returned
as stdout for the LLM to read and apply.
**Use cases:**
- Loading tool usage best practices (tool-usage skill)
- Loading error handling guidance (error-handling skill)
- Loading domain-specific knowledge (any custom skill)
**Note:** This tool requires skill_loader to be configured in the ToolRegistry.
"""
[docs]
def __init__(self, skill_loader=None):
"""
Initialize skill tool.
Args:
skill_loader: SkillLoader or EnhancedSkillLoader instance for loading skill content
"""
self.skill_loader = skill_loader
@property
def name(self) -> str:
"""Tool name."""
return "skill"
@property
def description(self) -> str:
"""Tool description."""
return "Load skill knowledge on-demand. Use when task matches a skill's description (e.g., tool-usage, error-handling). Returns the full skill content as guidance."
[docs]
def validate_args(self, args: Dict[str, Any]) -> tuple[bool, Optional[str]]:
"""Validate arguments."""
if "name" not in args:
return False, "Missing required argument: 'name' (skill name)"
if not isinstance(args.get("name"), str):
return False, "Argument 'name' must be a string"
return True, None
[docs]
def execute(self, args: Dict[str, Any]) -> ToolResult:
"""
Load and return skill content.
Args:
args: Must contain 'name' (str) - the skill name to load
Returns:
ToolResult with skill content in stdout, or error in stderr
"""
skill_name = args["name"]
if not self.skill_loader:
return ToolResult(
ok=False,
stdout="",
stderr="Skill loader not available. Cannot load skill content.",
meta={"skill_name": skill_name},
)
content = self.skill_loader.get_skill_content(skill_name)
if content is None or content == "":
available = ""
if hasattr(self.skill_loader, "skills") and self.skill_loader.skills:
available = f" Available skills: {', '.join(self.skill_loader.skills.keys())}"
return ToolResult(
ok=False,
stdout="",
stderr=f"Skill '{skill_name}' not found or has no content.{available}",
meta={"skill_name": skill_name},
)
return ToolResult(
ok=True,
stdout=content,
stderr="",
meta={"skill_name": skill_name, "content_length": len(content)},
)