release / tag (push) Failing after 1s
Ports the Vibe wiring from local-dev's wire-skills.sh so this repo's installer covers all four harnesses. Vibe uses a flat layout — ~/.vibe/skills/<name>.md symlinked to the skill's SKILL.md file (not the directory, unlike Claude/Crush/ Antigravity). Added to both `task install` and install.sh, idempotent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
149 lines
4.4 KiB
Bash
Executable File
149 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# install.sh — bootstrap installer for the skills library.
|
|
#
|
|
# Usage (one-liner, hosts without Task):
|
|
# curl -fsSL https://git.d-ma.be/mathias/skills/raw/branch/main/install.sh | bash
|
|
#
|
|
# Pinned to a specific tag:
|
|
# SKILLS_REF=v0.1.0 bash install.sh
|
|
#
|
|
# Idempotent: re-running pulls the latest changes for the configured ref
|
|
# (default: main) and re-wires symlinks. Existing correct links are
|
|
# left alone; mismatched ones are replaced.
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_URL="${SKILLS_REPO_URL:-https://git.d-ma.be/mathias/skills.git}"
|
|
REF="${SKILLS_REF:-main}"
|
|
CHECKOUT_DIR="${SKILLS_CHECKOUT_DIR:-$HOME/.local/share/skills}"
|
|
CLAUDE_GLOBAL_DIR="${CLAUDE_SKILLS_DIR:-$HOME/.claude/skills}"
|
|
CRUSH_DIR="${CRUSH_SKILLS_DIR:-$HOME/.config/crush/skills}"
|
|
ANTIGRAVITY_DIR="${ANTIGRAVITY_SKILLS_DIR:-$HOME/.gemini/antigravity/skills}"
|
|
VIBE_SKILLS_DIR="${VIBE_SKILLS_DIR:-$HOME/.vibe/skills}"
|
|
|
|
log() { printf '[skills] %s\n' "$*"; }
|
|
|
|
ensure_checkout() {
|
|
if [ -d "$CHECKOUT_DIR/.git" ]; then
|
|
log "updating $CHECKOUT_DIR"
|
|
git -C "$CHECKOUT_DIR" fetch --tags --quiet
|
|
git -C "$CHECKOUT_DIR" checkout --quiet "$REF"
|
|
if git -C "$CHECKOUT_DIR" symbolic-ref -q HEAD >/dev/null; then
|
|
# on a branch — fast-forward
|
|
git -C "$CHECKOUT_DIR" pull --ff-only --quiet
|
|
fi
|
|
else
|
|
log "cloning $REPO_URL into $CHECKOUT_DIR (ref=$REF)"
|
|
mkdir -p "$(dirname "$CHECKOUT_DIR")"
|
|
git clone --quiet "$REPO_URL" "$CHECKOUT_DIR"
|
|
git -C "$CHECKOUT_DIR" checkout --quiet "$REF"
|
|
fi
|
|
}
|
|
|
|
list_skills() {
|
|
# Every top-level entry that isn't a known meta file.
|
|
(cd "$CHECKOUT_DIR" && ls -1) | grep -Ev '^(Taskfile\.yml|install\.sh|README\.md|SKILLS_INDEX\.md|\.gitea|\.git)$' || true
|
|
}
|
|
|
|
link_skill() {
|
|
# link_skill <target_dir> <skill_name>
|
|
local target_dir="$1"
|
|
local skill="$2"
|
|
local target="$CHECKOUT_DIR/$skill"
|
|
local link="$target_dir/$skill"
|
|
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
local current
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
return
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
log "linked $link → $target"
|
|
}
|
|
|
|
wire_claude_global() {
|
|
mkdir -p "$CLAUDE_GLOBAL_DIR"
|
|
while IFS= read -r skill; do
|
|
[ -n "$skill" ] || continue
|
|
link_skill "$CLAUDE_GLOBAL_DIR" "$skill"
|
|
done < <(list_skills)
|
|
}
|
|
|
|
wire_crush() {
|
|
# Crush reads skills from ~/.config/crush/skills/<name>/SKILL.md.
|
|
# Pre-creating the dir is cheap even when Crush isn't installed.
|
|
mkdir -p "$CRUSH_DIR"
|
|
while IFS= read -r skill; do
|
|
[ -n "$skill" ] || continue
|
|
link_skill "$CRUSH_DIR" "$skill"
|
|
done < <(list_skills)
|
|
}
|
|
|
|
wire_antigravity() {
|
|
# Antigravity (Google's VS Code extension) reads global skills from
|
|
# ~/.gemini/antigravity/skills/<name>/SKILL.md. Our SKILL.md files
|
|
# already carry the required `name` + `description` YAML frontmatter,
|
|
# so symlinks are sufficient — no manifest translation step.
|
|
mkdir -p "$ANTIGRAVITY_DIR"
|
|
while IFS= read -r skill; do
|
|
[ -n "$skill" ] || continue
|
|
link_skill "$ANTIGRAVITY_DIR" "$skill"
|
|
done < <(list_skills)
|
|
}
|
|
|
|
wire_claude_repo() {
|
|
# Only wire per-repo when invoked from inside a git repo and it isn't
|
|
# the skills repo itself.
|
|
if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
return
|
|
fi
|
|
local repo
|
|
repo=$(git rev-parse --show-toplevel)
|
|
if [ "$repo" = "$CHECKOUT_DIR" ]; then
|
|
return
|
|
fi
|
|
local target_dir="$repo/.claude/skills"
|
|
mkdir -p "$target_dir"
|
|
while IFS= read -r skill; do
|
|
[ -n "$skill" ] || continue
|
|
link_skill "$target_dir" "$skill"
|
|
done < <(list_skills)
|
|
}
|
|
|
|
wire_vibe() {
|
|
# Mistral Vibe reads a flat ~/.vibe/skills/<name>.md — link the SKILL.md
|
|
# file directly (not the skill directory, as the other harnesses do).
|
|
mkdir -p "$VIBE_SKILLS_DIR"
|
|
while IFS= read -r skill; do
|
|
[ -n "$skill" ] || continue
|
|
local target="$CHECKOUT_DIR/$skill/SKILL.md"
|
|
[ -f "$target" ] || continue
|
|
local link="$VIBE_SKILLS_DIR/$skill.md"
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
local current
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
continue
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
log "linked $link → $target"
|
|
done < <(list_skills)
|
|
}
|
|
|
|
main() {
|
|
ensure_checkout
|
|
wire_claude_global
|
|
wire_claude_repo
|
|
wire_crush
|
|
wire_antigravity
|
|
wire_vibe
|
|
log "done — $(list_skills | wc -l | tr -d ' ') skill(s) wired at ref=$REF"
|
|
}
|
|
|
|
main "$@"
|