Initial import: 10 Radix skills across 3 categories

Kategoriserer danske regnskabs-/ERP-skills, ERP-data-afstemnings-
skill og to devops-workflows (gitea-issue-agent, kanban-workflows)
som Radix-medlemmers AI-agenter kan dele.

Struktur:
  skills/<kategori>/<skill-navn>/SKILL.md
  skills/<kategori>/<skill-navn>/{references,templates,scripts}/

Indekseret af index.json (genereret af scripts/rebuild_index.py).
Kør `python3 scripts/rebuild_index.py --check` i CI for at fange
synkroniseringsfejl.
This commit is contained in:
2026-06-15 14:41:04 +02:00
commit 236053cd7f
30 changed files with 4460 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""
Rebuild index.json from SKILL.md frontmatter in this repository.
Usage:
python3 scripts/rebuild_index.py
python3 scripts/rebuild_index.py --check # exit 1 if index.json is out of date
The index is the canonical machine-readable manifest of all skills in this
repository. Regenerate it whenever a SKILL.md is added, removed, or has its
frontmatter changed. Commit index.json together with the SKILL.md change.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
SKILLS_ROOT = REPO_ROOT / "skills"
INDEX_PATH = REPO_ROOT / "index.json"
FRONTMATTER_RE = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
KEY_RE = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*):\s*(.+?)(?=\n[A-Za-z_]|\Z)", re.MULTILINE | re.DOTALL)
def _grab(frontmatter: str, key: str) -> str:
m = re.search(rf"^{key}:\s*(.+?)(?=\n[A-Za-z_]|\Z)", frontmatter, re.MULTILINE | re.DOTALL)
if not m:
return ""
val = m.group(1).strip()
# strip YAML block indicators and quotes
val = val.strip(">").strip("|").strip()
if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
val = val[1:-1]
return val.strip()
def _grab_tags(frontmatter: str) -> list[str]:
m = re.search(r"tags:\s*\[(.*?)\]", frontmatter, re.DOTALL)
if not m:
return []
return [t.strip().strip('"').strip("'") for t in m.group(1).split(",") if t.strip()]
def build_index() -> dict:
skills: list[dict] = []
for skill_md in sorted(SKILLS_ROOT.rglob("SKILL.md")):
rel = skill_md.relative_to(SKILLS_ROOT)
parts = rel.parts
category = parts[0] if len(parts) > 1 else "root"
name = parts[-2] if len(parts) > 1 else "root"
text = skill_md.read_text(encoding="utf-8")
fm_match = FRONTMATTER_RE.match(text)
frontmatter = fm_match.group(1) if fm_match else ""
desc = _grab(frontmatter, "description").replace("\n", " ").strip()
version = _grab(frontmatter, "version")
author = _grab(frontmatter, "author")
tags = _grab_tags(frontmatter)
all_files = sorted(
str(p.relative_to(SKILLS_ROOT))
for p in skill_md.parent.rglob("*")
if p.is_file()
)
skills.append(
{
"category": category,
"name": name,
"path": str(rel),
"description": desc,
"version": version,
"author": author,
"tags": tags,
"files": all_files,
}
)
return {
"repository": "Radix-Skills",
"version": 1,
"description": (
"Delt AI-skill-bibliotek for Radix — bruges af alle medarbejderes "
"AI-agenter (Hermes, Codex m.fl.) til dansk regnskab/ERP/data/devops."
),
"categories": sorted({s["category"] for s in skills}),
"skill_count": len(skills),
"skills": skills,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true", help="Exit 1 if index.json is stale")
args = parser.parse_args()
new_index = build_index()
new_json = json.dumps(new_index, ensure_ascii=False, indent=2) + "\n"
if args.check:
if not INDEX_PATH.exists():
print("index.json does not exist", file=sys.stderr)
return 1
current = INDEX_PATH.read_text(encoding="utf-8")
if current != new_json:
print("index.json is out of date — run scripts/rebuild_index.py", file=sys.stderr)
return 1
return 0
INDEX_PATH.write_text(new_json, encoding="utf-8")
print(
f"Wrote {INDEX_PATH.relative_to(REPO_ROOT)} with {new_index['skill_count']} skills "
f"across {len(new_index['categories'])} categories: {', '.join(new_index['categories'])}"
)
return 0
if __name__ == "__main__":
sys.exit(main())