# Gemeinsame Funktionen für die Skill-Skripte. Wird per "source" eingebunden. ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SKILLS_DIR="$ROOT/skills" DIST_DIR="$ROOT/dist" NAME_RE='^[a-z0-9]+(-[a-z0-9]+)*$' # Alle Skill-Namen (Ordner unter skills/), sortiert. skill_names() { local d for d in "$SKILLS_DIR"/*/; do [[ -d "$d" ]] && basename "$d" done | LC_ALL=C sort } # Gibt pro Verstoß gegen die Namensregeln eine Zeile aus. name_errors() { local n="$1" [[ "$n" =~ $NAME_RE ]] || echo "Name '$n' ist kein kebab-case (nur a-z, 0-9 und einzelne Bindestriche)" (( ${#n} <= 64 )) || echo "Name '$n' ist länger als 64 Zeichen" case "$n" in *claude*|*anthropic*) echo "Name '$n' darf nicht 'claude' oder 'anthropic' enthalten" ;; esac return 0 } # Frontmatter-Zeilen (ohne die ---) einer SKILL.md; Exit-Code != 0, wenn keins da ist. frontmatter() { tr -d '\r' < "$1" | awk ' NR == 1 { if ($0 != "---") exit 2; next } $0 == "---" { found = 1; exit } { print } END { if (!found) exit 3 }' } # Rohwert eines Frontmatter-Schlüssels (getrimmt, Anführungszeichen noch enthalten). fm_raw() { local line v line="$(grep -m1 -E "^$2[[:space:]]*:" <<< "$1" || true)" [[ -z "$line" ]] && return 1 v="${line#*:}" v="${v#"${v%%[![:space:]]*}"}" v="${v%"${v##*[![:space:]]}"}" printf '%s' "$v" } # Wert eines Frontmatter-Schlüssels ohne umschließende Anführungszeichen. fm_value() { local v v="$(fm_raw "$1" "$2")" || return 1 if [[ "$v" =~ ^\"(.*)\"$ ]] || [[ "$v" =~ ^\'(.*)\'$ ]]; then v="${BASH_REMATCH[1]}" fi printf '%s' "$v" } # Erste Überschrift "## ..." aus CHANGELOG.md. changelog_heading() { [[ -f "$1/CHANGELOG.md" ]] || return 1 tr -d '\r' < "$1/CHANGELOG.md" | grep -m1 '^## ' || true } # Aktuelle Version eines Skills (oberster CHANGELOG-Eintrag) oder leer. skill_version() { local h h="$(changelog_heading "$1")" || return 0 if [[ "$h" =~ ^##\ ([0-9]+\.[0-9]+\.[0-9]+)\ \([0-9]{4}-[0-9]{2}-[0-9]{2}\)[[:space:]]*$ ]]; then printf '%s' "${BASH_REMATCH[1]}" fi }