Files
EvoScientist-Multi/EvoScientist/tools/skill_manager.py
T

160 lines
6.4 KiB
Python

"""Skill management tool (LangChain @tool wrapper)."""
from typing import Literal
from langchain_core.tools import tool
@tool(parse_docstring=True)
def skill_manager(
action: Literal["install", "list", "uninstall", "info", "browse"],
source: str = "",
name: str = "",
tag: str = "",
include_system: bool = False,
) -> str:
"""Manage user-installable skills: install from GitHub or local path, list available skills, browse remote skills, get details, or uninstall.
Actions and required parameters:
action="install" (requires source):
Install a skill. The source can be:
- GitHub shorthand: "owner/repo@skill-name" (e.g. "anthropics/skills@peft")
- GitHub URL: "https://github.com/owner/repo/tree/main/skill-name"
- Local path: "./my-skill" or "/path/to/skill"
Nested skills are auto-resolved — if the skill is not at the repo root, subdirectories are searched automatically.
action="list":
List installed skills. By default only shows user-installed skills.
Set include_system=True to also show built-in system skills.
Built-in skills evolve over time, so use action="list" to see the current set.
action="browse" (optional tag):
Browse available skills from the EvoSkills repository (EvoScientist/EvoSkills).
Set tag to filter by category (e.g. tag="core", tag="writing", tag="experiments").
Returns skill names, descriptions, tags, and install sources you can pass to action="install".
action="info" (requires name):
Get details (description, source, path, tags) about a specific skill by name.
Searches both user and system skills.
action="uninstall" (requires name):
Remove a user-installed skill by name. System skills cannot be uninstalled.
Args:
action: The operation to perform — "install", "list", "browse", "info", or "uninstall"
source: Required for install — GitHub shorthand, GitHub URL, or local directory path
name: Required for info and uninstall — the skill name (for example, one returned by action="list")
tag: Optional for browse — filter by tag (e.g. "core", "writing", "experiments", "research")
include_system: Only for list — set True to include built-in system skills in the output
Returns:
Result message
"""
from .skills_manager import (
fetch_remote_skill_index,
get_skill_info,
install_skill,
list_skills,
uninstall_skill,
)
if action == "install":
if not source:
return (
"Error: 'source' is required for install action. "
"Provide a GitHub shorthand (e.g. source='owner/repo@skill-name'), "
"a GitHub URL, or a local directory path."
)
result = install_skill(source)
if result["success"]:
return (
f"Successfully installed skill: {result['name']}\n"
f"Description: {result.get('description', '(none)')}\n"
f"Path: {result['path']}\n\n"
f"Read its SKILL.md for full instructions."
)
else:
return f"Failed to install skill: {result['error']}"
elif action == "list":
skills = list_skills(include_system=include_system)
if not skills:
if include_system:
return "No skills found."
return "No user skills installed. Use action='install' to add skills, or set include_system=True to see built-in skills."
user_skills = [s for s in skills if s.source in ("workspace", "global")]
system_skills = [s for s in skills if s.source == "builtin"]
lines = []
if user_skills:
lines.append(f"User Skills ({len(user_skills)}):")
for skill in user_skills:
tags_str = f" [{', '.join(skill.tags)}]" if skill.tags else ""
lines.append(f" - {skill.name}: {skill.description}{tags_str}")
if system_skills:
if lines:
lines.append("")
lines.append(f"System Skills ({len(system_skills)}):")
for skill in system_skills:
tags_str = f" [{', '.join(skill.tags)}]" if skill.tags else ""
lines.append(f" - {skill.name}: {skill.description}{tags_str}")
return "\n".join(lines)
elif action == "browse":
try:
index = fetch_remote_skill_index()
except Exception as e:
return f"Failed to fetch skill index: {e}"
if tag:
tag_lower = tag.lower()
index = [
s for s in index if tag_lower in [t.lower() for t in s.get("tags", [])]
]
if not index:
return f"No skills found{' with tag: ' + tag if tag else ''}."
lines = [f"Available Skills ({len(index)}):"]
for s in index:
tags_str = " · ".join(s.get("tags", []))
lines.append(f" - {s['name']}: {s['description']}")
if tags_str:
lines.append(f" Tags: {tags_str}")
lines.append(
f" Install: skill_manager(action='install', source='{s['install_source']}')"
)
return "\n".join(lines)
elif action == "uninstall":
if not name:
return (
"Error: 'name' is required for uninstall action. "
"Use action='list' first to see installed skill names."
)
result = uninstall_skill(name)
if result["success"]:
return f"Successfully uninstalled skill: {name}"
else:
return f"Failed to uninstall skill: {result['error']}"
elif action == "info":
if not name:
return (
"Error: 'name' is required for info action. "
"Use action='list' with include_system=True to see all available skill names."
)
info = get_skill_info(name)
if not info:
return (
f"Skill not found: {name}. "
f"Use action='list' with include_system=True to see all available skills."
)
tags_str = f"\nTags: {', '.join(info.tags)}" if info.tags else ""
return (
f"Name: {info.name}\n"
f"Description: {info.description}\n"
f"Source: {info.source}\n"
f"Path: {info.path}{tags_str}"
)
else:
return f"Unknown action: {action}. Use 'install', 'list', 'browse', 'uninstall', or 'info'."