The skills-index page had drifted badly (claimed 66 skills; 330 are actually installed). Regenerate it from the live skill tree and add an automated regeneration script + related cron so it can never go stale again. - regenerate-skills-index.sh: full stock-take (inventory -> quartz build -> deploy) - systems/skills-index.md: rebuilt with all 330 skills across 26 categories - .gitignore: fix stray literal chars, ignore build artifacts
120 lines
4.1 KiB
Bash
Executable File
120 lines
4.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# regenerate-skills-index.sh
|
|
# Stock-take: regenerate systems/skills-index.md from the live skill tree,
|
|
# rebuild the Quartz site, and redeploy. Run on a schedule so the wiki never drifts.
|
|
set -euo pipefail
|
|
|
|
WIKI="/home/hermes/workspace/agent-estate-wiki"
|
|
QUARTZ="/home/hermes/workspace/quartz-temp"
|
|
SKILLS="/home/hermes/.hermes/skills"
|
|
|
|
echo "[$(date -Is)] Starting skills stock-take"
|
|
|
|
# 1. Regenerate skills-index.md from the live skill tree
|
|
python3 - "$SKILLS" "$WIKI/systems/skills-index.md" <<'PY'
|
|
import sys, re
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
from datetime import date
|
|
|
|
skills_root = Path(sys.argv[1])
|
|
out_path = Path(sys.argv[2])
|
|
skill_files = list(skills_root.rglob('SKILL.md'))
|
|
|
|
skills = defaultdict(list)
|
|
for f in skill_files:
|
|
rel = f.relative_to(skills_root)
|
|
parts = rel.parts
|
|
if len(parts) >= 3:
|
|
cat, name = parts[0], parts[1]
|
|
elif len(parts) == 2:
|
|
cat, name = 'uncategorized', parts[0]
|
|
else:
|
|
continue
|
|
desc = ''
|
|
try:
|
|
content = f.read_text(errors='ignore')
|
|
m = re.search(r'^description:\s*(.+)$', content[:2000], re.M)
|
|
if m:
|
|
desc = m.group(1).strip().strip('"').strip("'")[:120]
|
|
except Exception:
|
|
pass
|
|
skills[cat].append((name, desc))
|
|
|
|
total = sum(len(v) for v in skills.values())
|
|
|
|
L = []
|
|
L.append('---')
|
|
L.append('title: Installed Skills')
|
|
L.append('type: system')
|
|
L.append('status: active')
|
|
L.append('created: 2026-07-22')
|
|
L.append(f'updated: {date.today().isoformat()}')
|
|
L.append(f'verified_on: {date.today().isoformat()}')
|
|
L.append('confidence: high')
|
|
L.append('tags: [skills, hermes]')
|
|
L.append('sources: []')
|
|
L.append('---')
|
|
L.append('')
|
|
L.append('# Installed Skills')
|
|
L.append('')
|
|
L.append(f'**Total installed: {total} skills** in `~/.hermes/skills/`.')
|
|
L.append('')
|
|
L.append('This page is auto-regenerated from the live skill inventory. Do not hand-edit.')
|
|
L.append('')
|
|
L.append('## Stock-take notes')
|
|
L.append('- Category names come from the directory structure under `~/.hermes/skills/` (`category/skill/SKILL.md`).')
|
|
L.append('- Skills stored directly under `~/.hermes/skills/` (no subfolder) are grouped under `uncategorized`.')
|
|
L.append('- Deeper nested paths (e.g. `mlops/evaluation`) are grouped by their top-level category.')
|
|
L.append('')
|
|
|
|
priority = ['core-hermes','autonomous-ai-agents','hermes','devops','productivity','creative','software-development','research','email','mlops','github','apple','frontend','media','note-taking','web-development','smart-home','mcp','uncategorized']
|
|
def cat_order(cat):
|
|
try: return priority.index(cat)
|
|
except ValueError: return 100 + len(priority)
|
|
cats = sorted(skills.items(), key=lambda x: (cat_order(x[0]), -len(x[1])))
|
|
|
|
for cat, items in cats:
|
|
nice = cat.replace('-', ' ').title()
|
|
L.append(f'## {nice} ({len(items)})')
|
|
L.append('')
|
|
for name, desc in sorted(items):
|
|
L.append(f'- `{name}` — {desc}' if desc else f'- `{name}`')
|
|
L.append('')
|
|
|
|
L.append('## Related')
|
|
L.append('- [[systems/hermes-agent]]')
|
|
L.append('- [[systems/omniroute]]')
|
|
L.append('')
|
|
|
|
out_path.write_text('\n'.join(L))
|
|
print(f"Regenerated {out_path} with {total} skills across {len(skills)} categories")
|
|
PY
|
|
|
|
# 2. Sync content into Quartz
|
|
rm -rf "$QUARTZ/content" "$QUARTZ/public"
|
|
mkdir -p "$QUARTZ/content"
|
|
python3 - "$WIKI" "$QUARTZ/content" <<'PY'
|
|
import shutil, sys
|
|
from pathlib import Path
|
|
src = Path(sys.argv[1]); dst = Path(sys.argv[2])
|
|
skip = {'.git','public','node_modules','quartz-site','serve.py','.gitignore','package.json','package-lock.json','.codewiki-state.json'}
|
|
for item in src.iterdir():
|
|
if item.name in skip: continue
|
|
if item.is_dir():
|
|
shutil.copytree(item, dst / item.name, dirs_exist_ok=True)
|
|
elif item.suffix == '.md':
|
|
shutil.copy2(item, dst / item.name)
|
|
PY
|
|
|
|
# 3. Build Quartz
|
|
cd "$QUARTZ"
|
|
npx quartz build >/tmp/quartz-build.log 2>&1 || { echo "Quartz build failed"; tail -20 /tmp/quartz-build.log; exit 1; }
|
|
|
|
# 4. Deploy to public
|
|
rm -rf "$WIKI/public"
|
|
mkdir -p "$WIKI/public"
|
|
cp -a "$QUARTZ/public/." "$WIKI/public/"
|
|
|
|
echo "[$(date -Is)] Completed: $(find "$WIKI/public" -name '*.html' | wc -l) html files deployed"
|