diff --git a/README.md b/README.md index 0b9c3c7..b796123 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,37 @@ changes. CI tags every change as a `vX.Y.Z` release (see ## Wired harnesses -Phase 1 (current) wires **Claude Code** only: +| Harness | Target path | Notes | +|---|---|---| +| **Claude Code (global)** | `~/.claude/skills/` | Visible in every Claude Code session on the host. | +| **Claude Code (per-repo)** | `/.claude/skills/` | Created when `install` is invoked inside a git repo (not the skills repo itself). Gitignored globally via `**/.claude/skills/`. | +| **Crush** | `~/.config/crush/skills/` | Charmbracelet Crush — successor to `opencode-ai/opencode` (archived). | +| **Antigravity** | `~/.gemini/antigravity/skills/` | Google Antigravity (VS Code extension). Our `SKILL.md` frontmatter (`name` + `description`) is already in the format Antigravity expects, so symlinks suffice — no manifest translation. | -- Global: `~/.claude/skills/` → `~/.local/share/skills/` -- Per-repo: `/.claude/skills/` (when invoked from a git - repo) → same +### Not wired (and why) -Phase 2 will add: Crush, opencode, antigravity, gitea-resident agents -(cobalt-dingo, agentsquad). See `mathias/infra` issue `infra#62` -addendum for the roadmap. +- **opencode** (`opencode-ai/opencode`): archive notice; succeeded by Crush. + Its only "skill"-like surface — Custom Commands at `~/.config/opencode/commands/` — + is for user-facing prompt templates, not system-level instructions. + Misaligned with how we use skills. Skipped. +- **gitea-resident agents** (cobalt-dingo, agentsquad): they consume + skills via their containing project's `.claude/skills` directory + (populated by the per-repo wirer when run from the host), or via the + brain MCP. No special target needed. + +### Per-host env-var overrides + +Each target path can be overridden by setting the matching env var before +running `install.sh`: + +| Env var | Default | +|---|---| +| `SKILLS_REPO_URL` | `https://gitea.d-ma.be/mathias/skills.git` | +| `SKILLS_REF` | `main` (set to e.g. `v0.1.0` to pin a release) | +| `SKILLS_CHECKOUT_DIR` | `$HOME/.local/share/skills` | +| `CLAUDE_SKILLS_DIR` | `$HOME/.claude/skills` | +| `CRUSH_SKILLS_DIR` | `$HOME/.config/crush/skills` | +| `ANTIGRAVITY_SKILLS_DIR` | `$HOME/.gemini/antigravity/skills` | ## Versioning diff --git a/Taskfile.yml b/Taskfile.yml index 1f5ba08..ca2c871 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -12,6 +12,11 @@ vars: REPO_URL: 'https://gitea.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' + # 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: @@ -26,17 +31,19 @@ tasks: silent: true install: - desc: 'Wire skills into every harness detected on this host (Claude Code today, more in phase 2).' + desc: 'Wire skills into every supported harness (Claude Code, Crush, Antigravity).' cmds: - task: install:claude:global - task: install:claude:repo + - task: install:crush + - task: install:antigravity install:claude:global: desc: 'Per-skill symlinks under ~/.claude/skills/ — visible in every Claude Code project on this host.' cmds: - mkdir -p {{.CLAUDE_GLOBAL_DIR}} - | - for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$"); do + 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 @@ -63,7 +70,7 @@ tasks: fi target_dir="$repo/.claude/skills" mkdir -p "$target_dir" - for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$"); do + 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 @@ -77,6 +84,44 @@ tasks: echo "linked $link → $target" done + install:crush: + desc: 'Per-skill symlinks under ~/.config/crush/skills/ — 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/. 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 + update: desc: 'git pull the canonical checkout then re-run install.' cmds: diff --git a/install.sh b/install.sh index eaedfb2..97a5454 100755 --- a/install.sh +++ b/install.sh @@ -17,6 +17,8 @@ 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}" +CRUSH_DIR="${CRUSH_SKILLS_DIR:-$HOME/.config/crush/skills}" +ANTIGRAVITY_DIR="${ANTIGRAVITY_SKILLS_DIR:-$HOME/.gemini/antigravity/skills}" log() { printf '[skills] %s\n' "$*"; } @@ -69,6 +71,28 @@ wire_claude_global() { done < <(list_skills) } +wire_crush() { + # Crush reads skills from ~/.config/crush/skills//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//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. @@ -92,6 +116,8 @@ main() { ensure_checkout wire_claude_global wire_claude_repo + wire_crush + wire_antigravity log "done — $(list_skills | wc -l | tr -d ' ') skill(s) wired at ref=$REF" }