#!/usr/bin/env bash # Erzeugt die Skill-Übersicht in README.md zwischen und # neu. Mit --check wird nur geprüft, ob sie aktuell ist (Exit-Code 1, wenn nicht). # Aufruf: scripts/update-index.sh [--check] set -euo pipefail source "$(dirname "$0")/lib.sh" REPO_WEB="https://git.gundel.dev/dg/claude-skills" readme="$ROOT/README.md" check=0 [[ "${1:-}" == "--check" ]] && check=1 if ! grep -q '' "$readme" || ! grep -q '' "$readme"; then echo "README.md enthält die Marker / nicht." >&2 exit 1 fi table="$(mktemp)" trap 'rm -f "$table"' EXIT { first=1 while IFS= read -r name; do [[ -z "$name" ]] && continue ver="$(skill_version "$SKILLS_DIR/$name")" desc="" if fm="$(frontmatter "$SKILLS_DIR/$name/SKILL.md" 2>/dev/null)"; then desc="$(fm_value "$fm" description || true)" fi deeplink="" if [[ -f "$SKILLS_DIR/$name/extension/manifest.json" ]]; then deeplink="$(python3 - "$SKILLS_DIR/$name/extension/manifest.json" "$name" <<'PY' import json, base64, sys, urllib.parse, re man = json.load(open(sys.argv[1], encoding="utf-8")) name = sys.argv[2] sk = {"wordcraft-mcp":"wordcraft","gridcraft-mcp":"gridcraft","pdfcraft-mcp":"pdfcraft","figcraft-mcp":"figcraft","vectorcraft-mcp":"vectorcraft","solidcraft-mcp":"solidcraft"}.get(name) if not sk: m = re.match(r"^(.+)-mcp$", name) sk = m.group(1) if m else name cmd = man["server"]["mcp_config"]["command"] args = [] for a in man["server"]["mcp_config"].get("args") or []: s = str(a) if s == "${user_config.author}": args.append("Claude") elif s == "${user_config.read_root}": args.append("${userHome}/Documents") elif s == "${user_config.write_root}": args.append("${userHome}/Documents/Claude") else: args.append(s) # Forward slashes; config = transport only (name= is separate). if len(cmd) >= 3 and cmd[1] == ":" and "\\" in cmd: cmd = cmd.replace("\\", "/") transport = {"command": cmd, "args": args} b64 = base64.urlsafe_b64encode(json.dumps(transport, separators=(",", ":")).encode()).decode().rstrip("=") print(f"cursor://anysphere.cursor-deeplink/mcp/install?name={urllib.parse.quote(sk)}&config={b64}") PY )" fi if [[ $first -eq 0 ]]; then printf '\n---\n\n' fi first=0 python3 -c 'import sys, urllib.parse name, ver, desc, repo, link = sys.argv[1:] def md(s): out = [] trans = {"\\": "\\\\", "*": "\\*", "_": "\\_", "`": "\\`", "[": "\\["} for ch in s: out.append(trans.get(ch, ch)) return "".join(out) lead, note = desc.strip(), "" cut = lead.find(" Verwenden") if cut > 0: note = lead[cut + 1:].strip() lead = lead[:cut].strip() if lead[:1].islower(): lead = lead[0].upper() + lead[1:] tag = urllib.parse.quote(f"{name}/v{ver}", safe="") base = f"{repo}/releases/download/{tag}" parts = [ f"[Doku](skills/{name}/README.md)", f"[Skill-ZIP]({base}/{name}-v{ver}.zip \"{name}-v{ver}.zip\")", ] if link: parts.append(f"[Erweiterung]({base}/{name}-v{ver}.mcpb \"{name}-v{ver}.mcpb\")") parts.append(f"[Cursor-Plugin]({base}/{name}-cursor-v{ver}.zip \"{name}-cursor-v{ver}.zip\")") parts.append(f"[Add to Cursor]({link})") lines = [f"### [{name}](skills/{name}/) · `v{ver}`", "", "> " + md(lead), ""] if note: lines += ["*" + md(note) + "*", ""] lines.append(" · ".join(parts)) sys.stdout.write("\n".join(lines)) ' "$name" "$ver" "$desc" "$REPO_WEB" "$deeplink" printf '\n' done < <(skill_names) } > "$table" new="$(tr -d '\r' < "$readme" | awk -v t="$table" ' // { print; while ((getline l < t) > 0) print l; skip = 1; next } // { skip = 0 } !skip { print }')" if [[ $check -eq 1 ]]; then if [[ "$new" != "$(tr -d '\r' < "$readme")" ]]; then echo "README.md ist nicht aktuell – scripts/update-index.sh ausführen und committen." >&2 exit 1 fi echo "README.md ist aktuell." else python3 -c 'import sys; open(sys.argv[1],"w",encoding="utf-8",newline="\n").write(sys.stdin.read())' "$readme" <<< "$new" echo "README.md aktualisiert." fi