# -*- coding: utf-8 -*- """Skill metadata parsing helpers for SKILL.md files.""" from dataclasses import dataclass, field from pathlib import Path from typing import List import yaml @dataclass(frozen=True) class SkillMetadata: """Parsed metadata for a skill package.""" skill_name: str path: Path source: str name: str description: str version: str = "" tools: List[str] = field(default_factory=list) def parse_skill_metadata(skill_dir: Path, source: str) -> SkillMetadata: """Parse SKILL.md frontmatter with a forgiving schema.""" skill_name = skill_dir.name skill_file = skill_dir / "SKILL.md" if not skill_file.exists(): return SkillMetadata( skill_name=skill_name, path=skill_dir, source=source, name=skill_name, description="", ) raw = skill_file.read_text(encoding="utf-8").strip() frontmatter = {} body = raw if raw.startswith("---"): parts = raw.split("---", 2) if len(parts) >= 3: try: frontmatter = yaml.safe_load(parts[1].strip()) or {} except yaml.YAMLError: frontmatter = {} body = parts[2].strip() if not isinstance(frontmatter, dict): frontmatter = {} description = str(frontmatter.get("description") or "").strip() if not description and body: description = body.splitlines()[0].strip().lstrip("#").strip() return SkillMetadata( skill_name=skill_name, path=skill_dir, source=source, name=str(frontmatter.get("name") or skill_name).strip() or skill_name, description=description, version=str(frontmatter.get("version") or "").strip(), tools=_string_list(frontmatter.get("tools")), ) def _string_list(value) -> List[str]: if isinstance(value, str): item = value.strip() return [item] if item else [] if not isinstance(value, list): return [] seen: List[str] = [] for item in value: if not isinstance(item, str): continue normalized = item.strip() if normalized and normalized not in seen: seen.append(normalized) return seen