# skills Engineering skills library — markdown `SKILL.md` files that plug into any AI-coding harness. For **Claude Code** this repo is a plugin, installed from the marketplace it also hosts; that path carries the hooks and MCP servers too. For **Crush, Pi and OpenCode** it is a plain library wired by `install.sh` as one directory symlink per harness. See [`SKILLS_INDEX.md`](./SKILLS_INDEX.md#wiring-into-tools). ## What is a skill? A skill is a single directory containing one or more markdown files. The canonical entrypoint is `/SKILL.md`. Additional reference material lives alongside in the same directory (e.g. `clean-code/references/code-smells.md`). Harnesses load skills on demand — keep `SKILL.md` short and triggerable; push depth into references. See [`SKILLS_INDEX.md`](./SKILLS_INDEX.md) for the full catalogue with descriptions and "use when" triggers. ## Install ### Claude Code — the plugin ``` /plugin marketplace add https://git.d-ma.be/mathias/skills.git /plugin install skills@mathias ``` The plugin ships skills, the `secret-guard` + `pre-task-ritual` hooks, and the brain/gitea/infra MCP servers as one versioned unit. Skills are namespaced (`skills:tdd`). Updates land when `.claude-plugin/plugin.json`'s `version` moves — CI bumps it on every release. ### Crush, Pi, OpenCode — `install.sh` ```bash curl -fsSL https://git.d-ma.be/mathias/skills/raw/branch/main/install.sh | bash ``` Idempotent. It clones or fast-forwards the checkout, then populates one **managed directory** and gives each harness a single link to it: | Path | Target | |---|---| | `~/.agents/skills/` | a real directory, one symlink per skill (Pi reads this path directly) | | `~/.agents/skills/` | `$SKILLS_CHECKOUT_DIR/skills/`, or another configured source | | `~/.config/crush/skills` | `~/.agents/skills` | | `~/.config/opencode/skills` | `~/.agents/skills` | Total links are N + one per harness — not N x harnesses, which is what #23 was actually about. ### More than one source of skills `~/.agents/skills` used to be a single link straight at this checkout. That encoded an assumption nobody had stated — that this checkout is the only source of skills on the host — and on flamingo the only way to install this library was to displace Anthropic's official set, which lived in Crush's directory. Real capability loss dressed up as a tidy-up (#27). Point `SKILLS_EXTRA_SOURCES` at any number of extra directories, colon separated, each holding `/SKILL.md`: ``` SKILLS_EXTRA_SOURCES=~/.config/crush/skills.anthropic bash install.sh ``` - The checkout is always first and always wins a name collision — but the collision is **logged**, never resolved in silence. Quietly picking one is how stale content wins. - Removing a skill from a source removes its link on the next run. - Anything in the managed directory that is not a symlink is reported and left alone. install.sh does not delete content it did not create. - The source list is recorded at `~/.agents/skills-sources` so `bin/doctor` can tell a link into a known source from a link into a forgotten one. ### Where the checkout must live `SKILLS_CHECKOUT_DIR` defaults to `~/.local/share/skills`, and that default is load-bearing: it is **host-local, outside any synced or shared directory**. On this fleet `~/dev` is an iCloud folder shared by flamingo and iguana, and it spans two path roots — `/Users/mathias` on the Macs, `/home/mathias` on koala. A checkout there is a git working tree with two concurrent writers, and any absolute symlink stored there is broken on one side by construction. That is what produced 243 dangling links across 12 repos (#23). Do not "fix" the default to point into `~/dev`. ### Per-host env-var overrides | Env var | Default | |---|---| | `SKILLS_REPO_URL` | `https://git.d-ma.be/mathias/skills.git` | | `SKILLS_REF` | `main` (set to e.g. `v0.4.0` to pin a release) | | `SKILLS_CHECKOUT_DIR` | `$HOME/.local/share/skills` | | `SKILLS_SKIP_FETCH` | unset (`1` wires an existing checkout without touching HEAD) | | `AGENTS_SKILLS_DIR` | `$HOME/.agents/skills` | | `CRUSH_SKILLS_DIR` | `$HOME/.config/crush/skills` | | `OPENCODE_SKILLS_DIR` | `$HOME/.config/opencode/skills` | | `ANTIGRAVITY_SKILLS_JSON` | `$HOME/.gemini/config/skills.json` (registers `AGENTS_SKILLS_DIR` as a path entry; needs `jq`) | | `SKILLS_EXTRA_SOURCES` | unset (colon-separated extra source directories) | The Mistral Vibe target was removed — unused. Antigravity's was removed for the same reason and re-added once it came back into use. ## Checking a host ```bash task doctor # or bin/doctor ``` Reports dangling links, links into a source nobody configured, skills in the checkout with no link, stray entries, leftover per-skill Claude symlinks, and a Claude Code plugin sitting on an older generation than the host already has. Non-zero exit on drift. It also runs at session start via the plugin, so a drifting host says so rather than waiting to be discovered. All of that compares copies already on the machine — deliberately, since it runs in a session hook where latency shows. That leaves one case invisible: a host where the checkout, the marketplace clone and the installed plugin all agree, and all three are a release behind origin. ```bash task doctor -- --online # or bin/doctor --online ``` One `git ls-remote` against origin, opt-in. Belongs in a scheduled per-host job, not the session hook. An unreachable remote is reported as its own fault rather than as staleness — "I could not check" and "you are behind" are different answers and only one of them is about the host. ## Versioning Every commit to `main` is auto-tagged as a semver release by the `.gitea/workflows/release.yml` action. Bump direction follows the commit footer: - `Bump-Type: major` → `X.0.0` - `Bump-Type: minor` → `0.Y.0` - *anything else* → `0.0.Z` (patch, the default) Consumer projects can pin a version by setting `SKILLS_REF=v0.1.0` before `task skills:install`; default is `main` for always-current. ## Contributing a skill 1. Add a directory `skills//`. 2. Create `skills//SKILL.md` with this frontmatter — all four keys are gated by `task check`, and the index table is generated from the last three: ```yaml --- name: surfaces: [claude-code, library] # any of claude-code | library | chat summary: "" use_when: "" description: --- ``` Then: one-paragraph purpose, triggers ("use when …"), mechanics. 3. Run `task index` and commit the regenerated `SKILLS_INDEX.md`. Do **not** hand-edit the table — `task check` regenerates it and fails on a diff. 4. Pressure-test it before shipping: run the scenario against a subagent that does *not* have the skill, write the skill against what that run actually did, then re-run with it. See `superpowers-writing-skills` and #19. 5. Commit directly to `main`. Conventional-commit prefix (`feat:` / `fix:` / `docs:`). Add `Bump-Type: minor` footer when adding a new skill or expanding scope; default patch covers edits. This repo is markdown-only and public — never include secrets, client names, or anything under NDA in a skill. ## Mode 2 routing candidates These notes used to sit in each SKILL.md as a `## Mode 2 Routing Note` section. They changed nothing about how the skill is invoked — several said so outright — and cost context on every load, so they live here instead. Nothing in the repo depends on this table; it is a record of which skills looked mechanical enough to route to a local model, should a routing layer ever exist. | Skill | Note | |---|---| | `cad-babysit` | Phases 2-4 (reading comments/logs, classifying fault vs outcome) are judgment-heavy and should stay Mode 1. Phase 1 (running `cad stuck` and grouping its output) is mechanical and a Mode 2 routing candidate once Plan 6 ships the routing pod. | | `code-review` | This skill is invoked identically in Mode 1 (cloud Claude) and Mode 2 (client-local with supervisor routing). The routing layer (Plan 6) does not exist yet; treat this skill as Mode 1 only for now. Findings format and severity classification are unchanged across modes. | | `debug` | This skill produces high-volume mechanical output (hypothesis enumeration) and is a candidate for Mode 2 routing to a local model in the future. Until Plan 6 ships the routing pod, treat as Mode 1 only. The hypothesis format and discipline are identical regardless of which model generates them. | | `feature-spec` | Spec writing requires interpretation of intent and tradeoff reasoning — work where Claude generally outperforms a 9-13B local model. This skill is intentionally **not** a Mode 2 routing candidate. In Mode 2, the routing layer should leave `feature-spec` calls on Claude. | | `session-retrospective` | Reading long session logs and pulling themes is high-volume, mechanical pattern-recognition work — a strong Mode 2 routing candidate. Until Plan 6 ships the routing pod, treat as Mode 1 only. | | `tdd` | This skill is invoked identically whether the agent is running in Mode 1 (cloud Claude, no routing) or Mode 2 (client-local, supervisor routing layer). The routing pod (Plan 6) does not exist yet; until it does, treat this skill as Mode 1 only. The discipline does not change between modes — only the model behind the call. | | `trainer` | Trainer is the same shape as `session-retrospective` — long-context reading, mechanical filtering — and is a Mode 2 routing candidate. Until Plan 6, treat as Mode 1 only. |