from __future__ import annotations import re from pathlib import Path from supply_agent.types import SkillInfo class SkillLoader: """Load skills from SKILL.md files (Cursor-compatible format).""" SKILL_FILENAME = "SKILL.md" def __init__(self, skills_dir: Path | str) -> None: self.skills_dir = Path(skills_dir) def discover(self) -> list[Path]: """Find all SKILL.md files in the skills directory.""" if not self.skills_dir.exists(): return [] return sorted(self.skills_dir.rglob(self.SKILL_FILENAME)) def load(self, skill_path: Path) -> SkillInfo: """Load a single skill from a SKILL.md file.""" content = skill_path.read_text(encoding="utf-8") name, description = self._parse_frontmatter(content, skill_path) body = self._strip_frontmatter(content) return SkillInfo( name=name, description=description, content=body.strip(), path=str(skill_path), ) def load_all(self) -> list[SkillInfo]: """Load all discovered skills.""" return [self.load(p) for p in self.discover()] def _parse_frontmatter(self, content: str, path: Path) -> tuple[str, str]: """Extract name and description from YAML frontmatter or heading.""" name = path.parent.name description = "" fm_match = re.match(r"^---\s*\n(.*?)\n---", content, re.DOTALL) if fm_match: fm = fm_match.group(1) for line in fm.splitlines(): if line.startswith("name:"): name = line.split(":", 1)[1].strip().strip('"').strip("'") elif line.startswith("description:"): description = line.split(":", 1)[1].strip().strip('"').strip("'") return name, description # Fallback: use directory name; extract description from first paragraph if present return name, description def _strip_frontmatter(self, content: str) -> str: return re.sub(r"^---\s*\n.*?\n---\s*\n?", "", content, count=1, flags=re.DOTALL)