#!/usr/bin/env bash # Build .skill archives from skills// into dist/.skill # Usage: build.sh [--prune] [name...] # no args = build all skills/* into dist/.skill # --prune = after build, remove dist/.skill files whose # is NOT in skills/* (analogue of install.sh --prune). # Prune always scans the full dist/, ignores name filter. # # Uses `zip` if available; otherwise delegates to scripts/build.ps1 # (so the script works on Linux, macOS, and Windows-with-git-bash without # requiring `zip` on Windows). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" SRC="$ROOT/skills" DIST="$ROOT/dist" prune=0 positional=() for arg in "$@"; do case "$arg" in --prune) prune=1 ;; *) positional+=("$arg") ;; esac done if command -v zip >/dev/null 2>&1; then mkdir -p "$DIST" if [ "${#positional[@]}" -eq 0 ]; then # Portable across bash 3.2 (stock macOS) and bash 4+ (Linux, git-bash): # avoid `mapfile` (bash 4+) and `find -printf` (GNU find only). names=() for d in "$SRC"/*/; do [ -d "$d" ] || continue names+=("$(basename "$d")") done IFS=$'\n' names=($(printf '%s\n' "${names[@]}" | sort)) unset IFS else names=("${positional[@]}") fi for name in "${names[@]}"; do src_dir="$SRC/$name" if [ ! -d "$src_dir" ]; then echo "skip: $name (not found in skills/)" >&2 continue fi if [ ! -f "$src_dir/SKILL.md" ]; then echo "skip: $name (missing SKILL.md)" >&2 continue fi out="$DIST/$name.skill" rm -f "$out" ( cd "$SRC" && zip -qr "$out" "$name" ) echo "built: dist/$name.skill" done elif command -v powershell.exe >/dev/null 2>&1; then # Windows fallback: delegate to build.ps1 (proper ZIP via .NET API). # PS array binding via -File is fragile (commas don't always split into [string[]]), # so call build.ps1 once per skill and let the bash loop do the work. # --prune is NOT forwarded — bash handles prune at the end of this script # against the same dist/, regardless of which build path ran. ps1="$SCRIPT_DIR/build.ps1" ps1_win="$(cygpath -w "$ps1" 2>/dev/null || echo "$ps1")" if [ "${#positional[@]}" -eq 0 ]; then powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$ps1_win" else for name in "${positional[@]}"; do powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$ps1_win" -Names "$name" done fi else echo "error: need either 'zip' (Linux/macOS) or PowerShell (Windows) to build .skill archives" >&2 exit 1 fi if [ "$prune" -eq 1 ]; then for f in "$DIST"/*.skill; do [ -f "$f" ] || continue name="$(basename "$f" .skill)" if [ ! -d "$SRC/$name" ]; then echo "pruning: $name (not in skills/) → $f" rm -f "$f" fi done fi