feat(catalog): sovereign catalog — provenance + de-superpowers (idea 12)
- author: ours / adapted-from frontmatter on all 38 skills (scripts/add-provenance.py, idempotent) - versions added to vendor copies (caveman x5, find-skills) + active-platform - README(en/ru): new Sovereignty/Adapted-from section, drop wiki-maintainer & superpowers refs - project-bootstrap v1.12.0 -> v1.13.0: remove 'use superpowers' trigger from template + trigger table; recommend-dont-menu wording no longer references superpowers:brainstorming - dist rebuilt to 38 (pruned stale update-claude-skills.skill) - lint: 0 violations, 0 warnings
This commit is contained in:
115
scripts/add-provenance.py
Normal file
115
scripts/add-provenance.py
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add provenance frontmatter (author/adapted-from) + missing versions to all skills/.
|
||||
|
||||
Idempotent: only inserts lines that are absent. Preserves all existing content.
|
||||
Usage: python scripts/add-provenance.py (run from repo root)
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
||||
SKILLS = os.path.join(ROOT, "skills")
|
||||
|
||||
# skill -> provenance line(s) to guarantee present in frontmatter
|
||||
PROVENANCE = {
|
||||
# ours — authored in this workshop/ecosystem
|
||||
"active-platform": ["author: ours", "version: 0.1.0"],
|
||||
"browser-cdp": ["author: ours"],
|
||||
"delegate-task": ["author: ours"],
|
||||
"inter-session-peer-discipline": ["author: ours"],
|
||||
"meta-host-routing": ["author: ours"],
|
||||
"private-dev-public-publish": ["author: ours"],
|
||||
"project-bootstrap": ["author: ours"],
|
||||
"project-discipline": ["author: ours"],
|
||||
"pulling-before-work": ["author: ours"],
|
||||
"ralph-loop-execution": ["author: ours"],
|
||||
"recommend-dont-menu": ["author: ours"],
|
||||
"session-handoff": ["author: ours"],
|
||||
"session-inbox-monitor": ["author: ours"],
|
||||
"setup-agents-task-runner": ["author: ours"],
|
||||
"setup-context7": ["author: ours"],
|
||||
"setup-interns": ["author: ours"],
|
||||
"setup-projects-meta": ["author: ours"],
|
||||
"setup-tasks": ["author: ours"],
|
||||
"setup-wiki": ["author: ours"],
|
||||
"task-format": ["author: ours"],
|
||||
"task-loop": ["author: ours"],
|
||||
"tdd-criteria": ["author: ours"],
|
||||
"update-skills": ["author: ours"],
|
||||
"using-context7": ["author: ours"],
|
||||
"using-interns": ["author: ours"],
|
||||
"using-markitdown": ["author: ours"],
|
||||
"using-projects-meta": ["author: ours"],
|
||||
"using-system-snapshot": ["author: ours"],
|
||||
"using-tasks": ["author: ours"],
|
||||
"using-vds-ops": ["author: ours"],
|
||||
"using-wiki": ["author: ours"],
|
||||
"using-wiki-graph": ["author: ours"],
|
||||
# adapted — vendored copies, sovereignly maintained; pins to backfill (see README "Adapted from")
|
||||
"caveman": ["adapted-from: JuliusBrussee/caveman (MIT) — vendored copy, upstream pin TBD", "version: 0.1.0"],
|
||||
"caveman-commit": ["adapted-from: JuliusBrussee/caveman (MIT) — vendored copy, upstream pin TBD", "version: 0.1.0"],
|
||||
"caveman-compress": ["adapted-from: JuliusBrussee/caveman (MIT) — vendored copy, upstream pin TBD"],
|
||||
"caveman-help": ["adapted-from: JuliusBrussee/caveman (MIT) — vendored copy, upstream pin TBD", "version: 0.1.0"],
|
||||
"caveman-review": ["adapted-from: JuliusBrussee/caveman (MIT) — vendored copy, upstream pin TBD", "version: 0.1.0"],
|
||||
"find-skills": ["adapted-from: vercel-labs/skills (MIT) — vendored copy, upstream pin TBD", "version: 0.1.0"],
|
||||
}
|
||||
|
||||
FRONTMATTER_RE = re.compile(r"^(---\s*\n)(.*?)(\n---\s*\n)", re.DOTALL)
|
||||
|
||||
changed = []
|
||||
errors = []
|
||||
for name, lines in sorted(PROVENANCE.items()):
|
||||
path = os.path.join(SKILLS, name, "SKILL.md")
|
||||
if not os.path.exists(path):
|
||||
errors.append(f"MISSING: {path}")
|
||||
continue
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
m = FRONTMATTER_RE.match(text)
|
||||
if not m:
|
||||
errors.append(f"{name}: no frontmatter")
|
||||
continue
|
||||
fm = m.group(2)
|
||||
new_lines = []
|
||||
inserted = False
|
||||
for line in fm.splitlines():
|
||||
# insert provenance after the name: line (keep name-first convention)
|
||||
if not inserted and line.startswith("name:"):
|
||||
new_lines.append(line)
|
||||
for prov in lines:
|
||||
key = prov.split(":", 1)[0]
|
||||
# skip if this key already present anywhere in frontmatter
|
||||
if not any(existing.startswith(key + ":") for existing in fm.splitlines()):
|
||||
new_lines.append(prov)
|
||||
inserted = True
|
||||
continue
|
||||
# drop old lines that we're replacing (e.g. duplicate version)
|
||||
new_lines.append(line)
|
||||
# dedupe: remove lines for keys that now appear twice
|
||||
seen = {}
|
||||
final = []
|
||||
for line in new_lines:
|
||||
key = line.split(":", 1)[0]
|
||||
if key in ("name",):
|
||||
final.append(line)
|
||||
continue
|
||||
if key in seen and seen[key] >= 1:
|
||||
continue # keep first occurrence
|
||||
seen[key] = seen.get(key, 0) + 1
|
||||
final.append(line)
|
||||
new_fm = "\n".join(final)
|
||||
if new_fm != fm:
|
||||
new_text = text[: m.start(2)] + new_fm + text[m.end(2):]
|
||||
with open(path, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(new_text)
|
||||
changed.append(name)
|
||||
else:
|
||||
print(f" = {name}: no change")
|
||||
|
||||
print(f"\nChanged ({len(changed)}): {', '.join(changed)}")
|
||||
if errors:
|
||||
print(f"\nERRORS ({len(errors)}):")
|
||||
for e in errors:
|
||||
print(" ", e)
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user