aimu.skills¶
Filesystem-discovered agent skills, plus runtime skill authoring. See how-to: build a personal assistant for the self-improvement pattern.
aimu.skills.AgentSkill
dataclass
¶
AgentSkill(name: str, description: str, path: Path, compatibility: str = '', license_info: str = '', metadata: dict = dict())
A single discovered Agent Skill from the filesystem.
script_tool_names ¶
Return {skill}__{script_stem} tool names for every .py / .sh in scripts/.
foo.py and foo.sh share the {skill}__foo name; .py sorts first and wins, so
the name is listed once (matching the skills-server registration).
aimu.skills.SkillManager ¶
Discovers and manages Agent Skills from the filesystem.
With no skill_dirs argument, scans the standard search paths at project and
user scope: .agents/skills/, .claude/skills/, ~/.agents/skills/,
~/.claude/skills/. Project-level paths win on name collision. Pass explicit
skill_dirs to override all defaults.
Discovery logs (at INFO) the number of skills found and the paths searched, so a
missing skill directory is easy to spot. Malformed SKILL.md files raise
:class:SkillLoadError rather than being silently skipped.
Usage::
manager = SkillManager() # auto-discover
manager = SkillManager(skill_dirs=["/path/to/skills"]) # explicit
print(manager.catalog_prompt())
body = manager.get_skill_body("pdf-processing")
refresh ¶
Invalidate the cache and re-discover skills, returning the new map.
Lets a skill authored at runtime (see :func:aimu.skills.write_skill) become
visible mid-run without constructing a fresh manager.
catalog_prompt ¶
Return an XML skill catalog suitable for injection into a system prompt.
Each entry lists the skill name, description, and any script-derived tool names
the model can call directly (without first calling activate_skill).
get_skill_body ¶
Return the full instructions body of a named skill.
Raises :class:SkillNotFoundError if the skill doesn't exist.
aimu.skills.SkillLoadError ¶
Bases: ValueError
Raised when a SKILL.md file is malformed and cannot be parsed.
aimu.skills.SkillNotFoundError ¶
Bases: KeyError
Raised when a requested skill name does not exist.
aimu.skills.build_skills_server ¶
Build an in-process FastMCP server from a SkillManager.
Registered tools
- activate_skill(name): returns the full SKILL.md body for the named skill
- {skill_name}__{script_stem}(args=""): runs a Python or shell script from a skill's scripts/ dir
The returned FastMCP instance can be passed directly to MCPClient(server=...).
Authoring¶
aimu.skills.write_skill ¶
write_skill(name: str, description: str, body: str, *, skills_dir: Union[str, Path], overwrite: bool = False, metadata: Optional[dict] = None, scripts: Optional[dict[str, str]] = None) -> Path
Write a new SKILL.md under skills_dir/<name>/ and return its path.
The file carries YAML frontmatter (name, description, optional metadata)
followed by the markdown body, matching the format
:class:~aimu.skills.manager.SkillManager discovers.
Validates that name is a slug (lowercase-with-hyphens, no path separators, which
also prevents traversal) and that description is non-empty. Refuses to overwrite an
existing skill unless overwrite=True. The written file is round-tripped through the
manager parser, so an authored skill is guaranteed discoverable (a parse failure raises
:class:~aimu.skills.manager.SkillLoadError).
scripts maps "<slug>.py" / "<slug>.sh" filenames to source, written into
scripts/ (each becomes a {skill}__{stem} tool). .sh files are marked executable.
aimu.skills.make_skill_authoring_tool ¶
Return an async @tool that authors a skill and refreshes manager.
The tool writes a new SKILL.md under skills_dir via :func:write_skill, then
calls :meth:SkillManager.refresh so the skill is discoverable in the same run. Both
manager and skills_dir are captured by closure (no module globals).
Note: after refresh, activate_skill (and any fresh-conversation catalog rebuild) will
surface the new skill, but a skill catalog already injected into an in-flight system
prompt is not retroactively updated. See :class:~aimu.aio.SkillAgent.