feat(skills): author root SKILL.md for reference-curator suite + 7 sub-skills
Finish the migration for the dirs the bulk pass couldn't auto-handle:
- 90-reference-curator/SKILL.md: hand-authored suite orchestrator (pipeline overview,
7-stage table, /reference-curator run modes, install) — the single loadable entry.
- 90-reference-curator/0{1..7}-*/SKILL.md: generated from each sub-skill's desktop/SKILL.md.
- scripts/migrate_skill_root.py: generalized discovery to find nested suite sub-skills
(rglob desktop/code SKILL.md), so the migrator now handles suites too.
81-mac-optimizer, 91-multi-agent-guide, 94-dintel-bootstrap need NO root SKILL.md: they
are Claude Code plugins whose skill correctly lives at skills/<name>/SKILL.md (validated).
Adding a root SKILL.md there would violate plugin structure.
All SKILL.md repo-wide validate: flat-root=65, suite-sub=7, plugin-skills=3, 0 failures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,21 @@ def find_source(d):
|
||||
return None
|
||||
|
||||
|
||||
def discover_skill_dirs():
|
||||
"""Top-level skill dirs PLUS any nested dir that has a desktop/ or code/ SKILL.md
|
||||
(e.g. the sub-skills of a suite like 90-reference-curator). Plugins keep their skill
|
||||
at skills/<name>/SKILL.md with no desktop/ or code/, so they are correctly left alone."""
|
||||
found = {}
|
||||
for d in SKILLS.iterdir(): # top-level: report SKIP/MANUAL/CREATE
|
||||
if is_skill_dir(d):
|
||||
found[d] = True
|
||||
for src in list(SKILLS.rglob("desktop/SKILL.md")) + list(SKILLS.rglob("code/SKILL.md")):
|
||||
d = src.parent.parent # nested sub-skills (suite members)
|
||||
if is_skill_dir(d):
|
||||
found[d] = True
|
||||
return sorted(found)
|
||||
|
||||
|
||||
def set_name(frontmatter, name):
|
||||
"""Replace the single-line `name:` value (or prepend one) with `name`."""
|
||||
if re.search(r"^name:.*$", frontmatter, re.M):
|
||||
@@ -80,10 +95,8 @@ def main(argv=None):
|
||||
rows = [] # (dir, status, detail)
|
||||
created = skipped = manual = warned = 0
|
||||
|
||||
for d in sorted(SKILLS.iterdir()):
|
||||
if not is_skill_dir(d):
|
||||
continue
|
||||
name = d.name
|
||||
for d in discover_skill_dirs():
|
||||
name = str(d.relative_to(SKILLS))
|
||||
if (d / "SKILL.md").exists():
|
||||
rows.append((name, "SKIP", "already has root SKILL.md"))
|
||||
skipped += 1
|
||||
@@ -93,7 +106,7 @@ def main(argv=None):
|
||||
rows.append((name, "MANUAL", "no desktop/ or code/ SKILL.md source (commands/README only)"))
|
||||
manual += 1
|
||||
continue
|
||||
text, issues = build_root_skill(src.read_text(encoding="utf-8"), name)
|
||||
text, issues = build_root_skill(src.read_text(encoding="utf-8"), d.name)
|
||||
rel = src.relative_to(d)
|
||||
if text is None:
|
||||
rows.append((name, "MANUAL", f"{rel}: {issues[0]}"))
|
||||
|
||||
Reference in New Issue
Block a user