Files
skills/install.sh
Mathias d6a71e370e
Some checks failed
release / tag (push) Has been cancelled
chore: bootstrap skills library — 19 skills + installer + CI auto-tag
Phase 1 of mathias/skills extraction (infra#62 Track D — homelab
next-step plan addendum). Imports ~/dev/.skills/ verbatim (19 skill
dirs + SKILLS_INDEX.md) and adds the installation surface:

- Taskfile.yml — install / update / list / release / check targets
- install.sh — bootstrap installer for hosts without Task. Idempotent
  symlink wirer; default checkout at ~/.local/share/skills/ on every
  host; SKILLS_REF env var pins a tag (default: main).
- .gitea/workflows/release.yml — auto-tag every push to main by
  Bump-Type footer (major/minor/patch, default patch). Skipped when
  commit contains [skip-release].
- README — usage, versioning, contribution flow, secret-hygiene rule.

Phase 1 wires Claude Code only (~/.claude/skills/<name> global +
<repo>/.claude/skills/<name> per-repo). Phase 2 adds Crush, opencode,
antigravity, and gitea-resident agents (cobalt-dingo, agentsquad)
once their skill conventions are researched.

Public repo, markdown-only — no secrets, no client names. Verified
via pre-push grep before initial push.

[skip-release]
2026-05-24 14:59:54 +02:00

99 lines
2.7 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://gitea.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://gitea.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}"
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_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)
}
main() {
ensure_checkout
wire_claude_global
wire_claude_repo
log "done — $(list_skills | wc -l | tr -d ' ') skill(s) wired at ref=$REF"
}
main "$@"