Skill Tracer
Skills › Scientific Agent Skills › autoskill › match_skills.py

match_skills.py

43 lines · skills/autoskill/scripts/match_skills.py at 5f2bc03

1import math
2from pathlib import Path
3
4import yaml
5
6
7def _parse_frontmatter(content: str) -> dict:
8 if not content.startswith("---\n"):
9 return {}
10 _, _, rest = content.partition("---\n")
11 block, _, _ = rest.partition("\n---")
12 parsed = yaml.safe_load(block)
13 return parsed if isinstance(parsed, dict) else {}
16def load_skill_descriptions(skills_dir):
17 skills_dir = Path(skills_dir)
18 skills = []
19 for skill_md in sorted(skills_dir.glob("*/SKILL.md")):
20 fm = _parse_frontmatter(skill_md.read_text())
21 if all(isinstance(fm.get(key), str) and fm[key].strip() for key in ("name", "description")):
22 skills.append({"name": fm["name"], "description": fm["description"]})
23 return skills
26def _cosine(a, b):
27 dot = sum(x * y for x, y in zip(a, b))
28 na = math.sqrt(sum(x * x for x in a))
29 nb = math.sqrt(sum(y * y for y in b))
30 if na == 0 or nb == 0:
31 return 0.0
32 return dot / (na * nb)
35def top_k_matches(query, skills, embedder, k):
36 q = embedder(query)
37 scored = [
38 {"name": s["name"], "description": s["description"],
39 "score": _cosine(q, embedder(s["description"]))}
40 for s in skills
41 ]
42 scored.sort(key=lambda r: r["score"], reverse=True)
43 return scored[:k]