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>
198 lines
7.4 KiB
YAML
198 lines
7.4 KiB
YAML
version: '3'
|
|
|
|
# Skills library Taskfile.
|
|
#
|
|
# Primary entry point for project + host installation. Consumer projects
|
|
# do NOT depend on this file directly — they ship their own
|
|
# `skills:install` target that delegates here via `install.sh`. This
|
|
# Taskfile is invoked by maintainers of the skills repo itself, plus by
|
|
# install.sh during the post-clone wire step.
|
|
|
|
vars:
|
|
REPO_URL: 'https://git.d-ma.be/mathias/skills.git'
|
|
CHECKOUT_DIR: '{{.HOME}}/.local/share/skills'
|
|
CLAUDE_GLOBAL_DIR: '{{.HOME}}/.claude/skills'
|
|
CRUSH_DIR: '{{.HOME}}/.config/crush/skills'
|
|
ANTIGRAVITY_DIR: '{{.HOME}}/.gemini/antigravity/skills'
|
|
VIBE_DIR: '{{.HOME}}/.vibe/skills'
|
|
# Anything at the repo root that is NOT a skill directory. Both
|
|
# Taskfile + install.sh share this exclusion regex.
|
|
NON_SKILL_ENTRIES: '^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$'
|
|
|
|
tasks:
|
|
|
|
default:
|
|
cmds:
|
|
- task: list
|
|
|
|
list:
|
|
desc: 'Print every skill in this repo'
|
|
cmds:
|
|
- 'ls -1 {{.TASKFILE_DIR}} | grep -Ev "^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$" || true'
|
|
silent: true
|
|
|
|
install:
|
|
desc: 'Wire skills into every supported harness (Claude Code, Crush, Antigravity, Mistral Vibe).'
|
|
cmds:
|
|
- task: install:claude:global
|
|
- task: install:claude:repo
|
|
- task: install:crush
|
|
- task: install:antigravity
|
|
- task: install:vibe
|
|
|
|
install:claude:global:
|
|
desc: 'Per-skill symlinks under ~/.claude/skills/<name> — visible in every Claude Code project on this host.'
|
|
cmds:
|
|
- mkdir -p {{.CLAUDE_GLOBAL_DIR}}
|
|
- |
|
|
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
|
target="{{.TASKFILE_DIR}}/${skill}"
|
|
link="{{.CLAUDE_GLOBAL_DIR}}/${skill}"
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
continue
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
echo "linked $link → $target"
|
|
done
|
|
|
|
install:claude:repo:
|
|
desc: 'Per-skill symlinks under $(pwd)/.claude/skills/<name> when invoked inside a git repo.'
|
|
cmds:
|
|
- |
|
|
if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
echo "not in a git repo — skipping per-repo wire"; exit 0
|
|
fi
|
|
repo=$(git rev-parse --show-toplevel)
|
|
if [ "$repo" = "{{.TASKFILE_DIR}}" ]; then
|
|
echo "running from skills repo itself — skipping per-repo wire"; exit 0
|
|
fi
|
|
target_dir="$repo/.claude/skills"
|
|
mkdir -p "$target_dir"
|
|
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
|
target="{{.TASKFILE_DIR}}/${skill}"
|
|
link="${target_dir}/${skill}"
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
continue
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
echo "linked $link → $target"
|
|
done
|
|
|
|
install:crush:
|
|
desc: 'Per-skill symlinks under ~/.config/crush/skills/<name> — visible in every Crush session on this host.'
|
|
cmds:
|
|
- mkdir -p {{.CRUSH_DIR}}
|
|
- |
|
|
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
|
target="{{.TASKFILE_DIR}}/${skill}"
|
|
link="{{.CRUSH_DIR}}/${skill}"
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
continue
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
echo "linked $link → $target"
|
|
done
|
|
|
|
install:antigravity:
|
|
desc: 'Per-skill symlinks under ~/.gemini/antigravity/skills/<name>. SKILL.md frontmatter (name + description) is already antigravity-compatible.'
|
|
cmds:
|
|
- mkdir -p {{.ANTIGRAVITY_DIR}}
|
|
- |
|
|
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
|
target="{{.TASKFILE_DIR}}/${skill}"
|
|
link="{{.ANTIGRAVITY_DIR}}/${skill}"
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
continue
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
echo "linked $link → $target"
|
|
done
|
|
|
|
install:vibe:
|
|
desc: 'Per-skill file symlinks under ~/.vibe/skills/<name>.md → <skill>/SKILL.md for Mistral Vibe (flat .md layout, not a directory).'
|
|
cmds:
|
|
- mkdir -p {{.VIBE_DIR}}
|
|
- |
|
|
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
|
target="{{.TASKFILE_DIR}}/${skill}/SKILL.md"
|
|
[ -f "$target" ] || continue
|
|
link="{{.VIBE_DIR}}/${skill}.md"
|
|
if [ -L "$link" ] || [ -e "$link" ]; then
|
|
current=$(readlink "$link" 2>/dev/null || true)
|
|
if [ "$current" = "$target" ]; then
|
|
continue
|
|
fi
|
|
rm -rf "$link"
|
|
fi
|
|
ln -s "$target" "$link"
|
|
echo "linked $link → $target"
|
|
done
|
|
|
|
update:
|
|
desc: 'git pull the canonical checkout then re-run install.'
|
|
cmds:
|
|
- |
|
|
if [ ! -d "{{.CHECKOUT_DIR}}/.git" ]; then
|
|
echo "no canonical checkout at {{.CHECKOUT_DIR}} — run install.sh first"; exit 1
|
|
fi
|
|
git -C "{{.CHECKOUT_DIR}}" pull --ff-only
|
|
- task: install
|
|
|
|
release:
|
|
desc: 'Tag HEAD per Bump-Type in the latest commit footer. Normally CI handles this — run locally only when CI is unavailable.'
|
|
cmds:
|
|
- |
|
|
latest=$(git -C "{{.TASKFILE_DIR}}" describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
|
|
major=$(echo "$latest" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/')
|
|
minor=$(echo "$latest" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/')
|
|
patch=$(echo "$latest" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\3/')
|
|
bump=$(git -C "{{.TASKFILE_DIR}}" log -1 --pretty=%B | grep -iE "^Bump-Type:" | head -1 | awk '{print tolower($2)}')
|
|
case "$bump" in
|
|
major) major=$((major + 1)); minor=0; patch=0 ;;
|
|
minor) minor=$((minor + 1)); patch=0 ;;
|
|
*) patch=$((patch + 1)) ;;
|
|
esac
|
|
next="v${major}.${minor}.${patch}"
|
|
echo "tagging $next (previous $latest, bump=${bump:-patch})"
|
|
git -C "{{.TASKFILE_DIR}}" tag -a "$next" -m "release $next"
|
|
echo "now: git push origin $next"
|
|
|
|
check:
|
|
desc: 'Sanity check — every skill dir has a SKILL.md, no stray files at root.'
|
|
cmds:
|
|
- |
|
|
missing=0
|
|
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$"); do
|
|
dir="{{.TASKFILE_DIR}}/${skill}"
|
|
if [ ! -d "$dir" ]; then
|
|
echo "ERROR: $skill is not a directory" >&2
|
|
missing=$((missing + 1))
|
|
continue
|
|
fi
|
|
if [ ! -f "$dir/SKILL.md" ]; then
|
|
echo "ERROR: $dir missing SKILL.md" >&2
|
|
missing=$((missing + 1))
|
|
fi
|
|
done
|
|
if [ $missing -gt 0 ]; then
|
|
echo "$missing skill(s) failed structure check" >&2
|
|
exit 1
|
|
fi
|
|
echo "all skills have SKILL.md"
|