From 8c547d73af01d2e473a965555fefa8dcab16d6c1 Mon Sep 17 00:00:00 2001 From: vitya Date: Tue, 5 May 2026 21:21:19 +0300 Subject: [PATCH] feat(project-bootstrap): generic Step 5.6 skill-deps check Collapse Step 5.6 from a single-skill detector (only the superpowers@claude-plugins-official plugin) into a generic `trigger -> fulfiller` table walker that scales to every canonical CLAUDE.md trigger. Inline 9-row map in SKILL.md covers: caveman, superpowers (kind: plugin), using-wiki, using-tasks, using-projects-meta, pulling-before-work, project-discipline, using-interns, active-platform. The `kind` flag (skill | plugin) drives which install command is emitted in the chat-only recommendation block. Detection paths: ~/.claude/skills//SKILL.md for skills, plugins. in ~/.claude/plugins/installed_plugins.json for plugins. Algorithm: read project's CLAUDE.md -> match each non-comment line vs map (substring + tolower, mirrors Step 5 idempotent merge) -> for each canonical match check disk -> print one chat block listing every missing fulfiller with copy-pasteable install commands, OR a single "all dependencies satisfied" line. User-custom lines and removed canonical lines are silently skipped. Hard rule "never auto-install" preserved verbatim. version: 1.6.0 -> 1.7.0 (MINOR per project-discipline Rule 3 -- adds capability, absorbs prior superpowers-only detector cleanly). Closes [bootstrap-skill-deps-check] (.tasks/STATUS.md done). Closes [bootstrap-recommend-projects-meta] by absorption -- the deferred mirror task was the seed of this generalization; the generic walker now handles using-projects-meta along with everything else, no per-skill mirror needed. Design rationale at .wiki/concepts/bootstrap-skill-deps-check.md: - why generic over per-skill mirrors (5x mirror explosion) - skill vs plugin kind distinction - MCP-server-backed skills (only check using-X policy skill; setup-X self-fires via Prerequisites pointer) - source-of-truth invariant: SKILL map + assets/CLAUDE.md.template must stay in sync (a future CI lint could enforce) --- .tasks/STATUS.md | 21 ++-- .tasks/bootstrap-skill-deps-check.md | 9 +- .wiki/concepts/bootstrap-skill-deps-check.md | 100 +++++++++++++++++ .wiki/index.md | 1 + .wiki/log.md | 2 + dist/project-bootstrap.skill | Bin 9559 -> 10474 bytes skills/project-bootstrap/README.md | 11 +- skills/project-bootstrap/SKILL.md | 109 +++++++++++++------ 8 files changed, 206 insertions(+), 47 deletions(-) create mode 100644 .wiki/concepts/bootstrap-skill-deps-check.md diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index 97da69b..59c35c1 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -22,20 +22,19 @@ _Updated: 2026-05-05_ --- -## ⚪ [bootstrap-skill-deps-check] — refactor project-bootstrap Step 5.6 into a generic skill-dependencies check (replaces per-skill `Step 5.X` mirror shape) -**Status:** ready -**Where I stopped:** (not started) — design agreed in chat 2026-05-05 after `[interns-skills-mvp]` shipped: adding `delegate to interns when allowed` to the canonical CLAUDE.md template made it visible that every new trigger would need its own `Step 5.X` detector mirror, which doesn't scale. Decision: collapse Step 5.6 (superpowers plugin) and the deferred `[bootstrap-recommend-projects-meta]` into a single generic `Step 5.6` that reads the canonical trigger list and maps each line to a fulfiller (skill or plugin) for missing-deps detection. Hard rule "never auto-install" carries over verbatim — chat-only recommendation block with copy-pasteable install commands -**Next action:** Read `.tasks/bootstrap-skill-deps-check.md` for the full spec — initial trigger→fulfiller seed table is in the Notes section. Write `.wiki/concepts/bootstrap-skill-deps-check.md` design page first (rationale: why generic over per-skill mirrors, kind: skill vs plugin distinction, MCP server caveat). Then edit `skills/project-bootstrap/SKILL.md` Step 5.6 in place, bump `version:` 1.6.0 → 1.7.0. Update `skills/project-bootstrap/README.md` Workflow Step 5.6 description. Rebuild + install. Close `[bootstrap-recommend-projects-meta]` with a note in commit message -**Branch:** (not started) +## 🟢 [bootstrap-skill-deps-check] — refactor project-bootstrap Step 5.6 into a generic skill-dependencies check (replaces per-skill `Step 5.X` mirror shape) +**Status:** done +**Where I stopped:** `skills/project-bootstrap/SKILL.md` Step 5.6 переписан с single-skill `superpowers`-only detector на generic `trigger → fulfiller` table walker (9-row inline map: 8 skills + 1 plugin; `kind` flag drives install-command emission); README Workflow Step 5.6 description обновлён под новую форму; `version:` 1.6.0 → 1.7.0 (MINOR — adds capability, absorbs prior detector cleanly); `dist/project-bootstrap.skill` rebuilt + installed; `~/.claude/skills/project-bootstrap/SKILL.md` shows `version: 1.7.0`; design page `.wiki/concepts/bootstrap-skill-deps-check.md` written (rationale: why generic over per-skill mirrors, skill/plugin kind distinction, MCP-server caveat, source-of-truth invariant between SKILL map ↔ `assets/CLAUDE.md.template`); `.wiki/index.md` + `.wiki/log.md` updated. Subsumes `[bootstrap-recommend-projects-meta]` by absorption. +**Next action:** (none — kept until merged) +**Branch:** master --- -## ⚪ [bootstrap-recommend-projects-meta] — Step 5.7 in project-bootstrap: recommend `setup-projects-meta` if MCP tools missing -**Status:** superseded -**Where I stopped:** (not started) — opened as follow-up to `[bootstrap-projects-meta-trigger]`. The `check across all projects` trigger is now in every CLAUDE.md template, but on a fresh machine without `projects-meta-mcp` registered the trigger is silently no-op until the user verbally asks for cross-project work -**Next action:** **superseded by `[bootstrap-skill-deps-check]`** (2026-05-05) — that task absorbs this one into a generic `Step 5.6` that walks the whole canonical trigger list, not just projects-meta. Kept here for git-log archaeology; close once `[bootstrap-skill-deps-check]` ships -**Branch:** (not started) -**Blocker:** waits on observing a real fresh-machine bootstrap that silently misses the dependency — defer until we see the failure mode in practice +## 🟢 [bootstrap-recommend-projects-meta] — Step 5.7 in project-bootstrap: recommend `setup-projects-meta` if MCP tools missing +**Status:** done +**Where I stopped:** closed by absorption 2026-05-05 — `[bootstrap-skill-deps-check]` shipped a generic Step 5.6 that walks the whole canonical trigger list (`using-projects-meta` is one of 9 rows in the inline `trigger → fulfiller` map). The per-skill mirror this task envisioned was rejected as a copy-paste explosion shape; the generic walker handles the same case + the analogous gap for every other canonical trigger. +**Next action:** (none — kept until merged) +**Branch:** master --- diff --git a/.tasks/bootstrap-skill-deps-check.md b/.tasks/bootstrap-skill-deps-check.md index 7d89fb3..48dd08e 100644 --- a/.tasks/bootstrap-skill-deps-check.md +++ b/.tasks/bootstrap-skill-deps-check.md @@ -26,7 +26,14 @@ Refactor `project-bootstrap` Step 5.6 (single-skill `superpowers` plugin detecto - [ ] Optional polish: print a one-line "✅ all skill dependencies satisfied" when nothing is missing, to make the silent-success case visible. Probably worth it; very low cost. ## Completed steps -(none — task created) +- [x] 2026-05-05: read `skills/project-bootstrap/SKILL.md` + README to confirm scope (Step 5.6 located lines 349-389; README Workflow item 5). +- [x] 2026-05-05: wrote `.wiki/concepts/bootstrap-skill-deps-check.md` design page (rationale, skill/plugin kind, MCP-server caveat, source-of-truth invariant). +- [x] 2026-05-05: replaced SKILL.md Step 5.6 in place with generic `trigger → fulfiller` map (9 rows) + algorithm + recommendation block. Hard rule "never auto-install" preserved verbatim. ✅ all-satisfied case prints one line. +- [x] 2026-05-05: bumped SKILL.md `version:` 1.6.0 → 1.7.0 (MINOR per project-discipline Rule 3 — adds capability, absorbs prior single-skill detector cleanly). +- [x] 2026-05-05: updated README.md Workflow Step 5.6 description to match the new shape. +- [x] 2026-05-05: rebuilt `dist/project-bootstrap.skill` via `scripts/build.sh`, reinstalled to `~/.claude/skills/project-bootstrap/` via `scripts/install.sh`; verified installed `version: 1.7.0`. +- [x] 2026-05-05: updated `.wiki/index.md` (added concept entry) and `.wiki/log.md` (ingest + decision lines). +- [x] 2026-05-05: closed sibling `[bootstrap-recommend-projects-meta]` 🟢 in same commit (closed by absorption — note in commit message). ## Notes - Triggers in current template (2026-05-05) and their fulfillers — initial seed for the map: diff --git a/.wiki/concepts/bootstrap-skill-deps-check.md b/.wiki/concepts/bootstrap-skill-deps-check.md new file mode 100644 index 0000000..51ff231 --- /dev/null +++ b/.wiki/concepts/bootstrap-skill-deps-check.md @@ -0,0 +1,100 @@ +--- +title: "project-bootstrap@1.7.0 — generic skill-dependencies check (Step 5.6)" +type: concept +updated: 2026-05-05 +--- + +# project-bootstrap@1.7.0 — generic skill-dependencies check (Step 5.6) + +_2026-05-05._ + +## Problem + +Step 5.6 in `project-bootstrap@1.6.0` detected exactly one missing fulfiller — the `superpowers@claude-plugins-official` plugin — and printed a recommendation if absent. The shape worked but doesn't scale. + +The canonical CLAUDE.md template grew from 5 lines (1.0.0) to 9 lines (1.6.0) — every release added a new trigger: + +| Release | Added trigger | Fulfiller | +|---|---|---| +| 1.1.0 | `we're on Windows` / Linux / macOS | `active-platform` (skill) | +| 1.2.0 | `check across all projects` | `using-projects-meta` (skill) | +| 1.4.0 | `pull remote before work` | `pulling-before-work` (skill) | +| 1.5.0 | `follow project discipline` | `project-discipline` (skill) | +| 1.6.0 | `delegate to interns when allowed` | `using-interns` (skill) | + +Each new trigger silently no-ops on hosts where the corresponding skill / plugin isn't installed. The Step 5.6 superpowers detector was the right idea, but mirroring it as `Step 5.7`, `Step 5.8`, … per missing-skill — five mirror sections to cover the gaps above, and one more per future trigger — is a copy-paste explosion. Worse: a deferred task `[bootstrap-recommend-projects-meta]` was already opened in 1.2.0's wake to mirror the `superpowers` detector for `using-projects-meta`, and the same gap is structurally inevitable for every line in the template. + +## Decision + +Collapse Step 5.6 into a single **generic** "Skill dependencies check" that: + +1. Reads the project's `CLAUDE.md` (just-written or pre-existing). +2. Walks every non-empty, non-comment line. +3. Looks each line up in a `trigger → fulfiller` map embedded in the SKILL. +4. Checks the corresponding detection path on disk: + - `kind: skill` → `~/.claude/skills//SKILL.md` exists? + - `kind: plugin` → key under `plugins.` in `~/.claude/plugins/installed_plugins.json`? +5. Prints **one** chat-only block listing every missing fulfiller with a copy-pasteable install command. + +The `superpowers`-only Step 5.6 from 1.6.0 is subsumed cleanly: `superpowers` becomes one row in the map, with `kind: plugin` so its install command is `/plugin install …` rather than `bash scripts/install.sh …`. + +`[bootstrap-recommend-projects-meta]` (the deferred mirror task) is closed by absorption — the generic step handles `using-projects-meta` along with everything else in the same commit. + +Bumps `project-bootstrap` 1.6.0 → 1.7.0 (MINOR — adds capability, doesn't break the existing detector since it's absorbed). + +## Why generic over per-skill mirrors + +Per-skill mirror shape: + +``` +Step 5.6 — Recommend superpowers if missing +Step 5.7 — Recommend setup-projects-meta if MCP missing +Step 5.8 — Recommend project-discipline if missing +Step 5.9 — Recommend pulling-before-work if missing +… +``` + +Each section is ~30 lines of nearly-identical "read detection path → if absent print recommendation" prose. Adding a new canonical trigger means writing another mirror section. Five-skill template → five mirror sections; ten-skill template → ten. The detection logic is the same; only the path and install command differ. + +Generic shape replaces the prose mirrors with one ~10-row table. Adding a new canonical trigger means adding one row in the map (plus the line in `assets/CLAUDE.md.template`, in the same commit). The detection algorithm is invariant. + +## Skill vs plugin distinction + +Two install pathways exist on a Claude Code host: + +- **Skills** live at `~/.claude/skills//`, installed via `bash scripts/install.sh ` (or via the `find-skills` skill). Detection: file existence. +- **Plugins** are a higher-level Claude Code concept (slash commands, hooks, sub-agents, MCP servers); installed via `/plugin install @`. Detection: key lookup in `~/.claude/plugins/installed_plugins.json`. + +The map's `kind` column flags which pathway each fulfiller uses, so the recommendation block emits the correct install command. Currently only `superpowers` is `kind: plugin`; everything else is `kind: skill`. Future triggers may add more plugins. + +## MCP-server-backed skills + +`using-context7`, `using-projects-meta`, `using-interns` each depend on an MCP server that's separately installed (`setup-context7`, `setup-projects-meta`, `setup-interns`). Step 5.6 only checks the `using-X` policy skill — not whether the MCP server is registered or running. Rationale: every `using-X` skill carries a "Prerequisites" pointer in its body that fires `setup-X` at first use if the MCP tools are missing. Bootstrap doesn't need to duplicate that detection — the skill itself self-bootstraps when invoked. Step 5.6 only ensures the `using-X` skill is present so the trigger isn't silently dead. + +## Hard rule — never auto-install carries over + +The `superpowers` detector at 1.6.0 had a hard rule: "never auto-install". Reasons: + +- Slash commands (`/plugin install …`) aren't callable from inside a skill. +- Silently mutating user-level skill / plugin state without consent is overreach. + +Both reasons generalize. The 1.7.0 generic check is recommendation-only — it prints to chat and never writes into project files or user-level config. The user can install some / all / none of the recommendations, or remove canonical lines from `CLAUDE.md` to lean the project's trigger set down. + +## What about user-custom triggers? + +Step 5.6 ignores any line in the project's `CLAUDE.md` that doesn't match a trigger row in the map. If the user added a custom trigger like `use my-team-style-guide`, it's their responsibility — bootstrap's contract is "we own canonical triggers; non-canonical lines are your responsibility". The check is permissive on custom lines (silent skip), strict on canonical lines (recommend if missing). + +Removed canonical lines work symmetrically: if the user deleted `delegate to interns when allowed` on purpose, Step 5.6 reads from the actual file and never sees that line, so it never recommends `using-interns`. Correct behavior — the user opted out. + +## Source-of-truth invariant + +The map embedded in SKILL.md and the trigger list in `assets/CLAUDE.md.template` must stay in sync. Adding a new canonical trigger requires editing both in the same commit. Mismatch = silent gap (template adds a trigger, map doesn't have its row, the recommendation never fires for it). A future hardening could lint the two against each other in CI; for now it's a discipline rule for whoever bumps the version. + +## Composition with the rest of bootstrap + +Step 5 (idempotent merge) ensures the trigger lines are *present* in the project's `CLAUDE.md`. Step 5.6 (this) ensures the fulfillers are *installed* on the host. Together they close the loop: + +- 5 → trigger in the file. +- 5.6 → fulfiller on disk. + +Either alone is partial. Together a fresh bootstrap on a fresh machine surfaces every install gap in one shot. diff --git a/.wiki/index.md b/.wiki/index.md index 103cc83..ba13baf 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -15,6 +15,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever - [active-platform-decision.md](concepts/active-platform-decision.md) — why `active-platform` is a skill (not a memory entry); why default = Windows; how it's wired into `project-bootstrap` - [bootstrap-claude-md-merge.md](concepts/bootstrap-claude-md-merge.md) — project-bootstrap@1.3.0 — Step 5 upgrade path becomes idempotent merge (read → diff vs template → confirm → append missing); fixes silent gap where pre-1.2.0 projects never picked up new canonical triggers (`check across all projects`, `we're on Windows`) +- [bootstrap-skill-deps-check.md](concepts/bootstrap-skill-deps-check.md) — project-bootstrap@1.7.0 — Step 5.6 collapses the per-skill "detect-and-recommend" mirror shape into one generic `trigger → fulfiller` table walker (skill vs plugin kind, never auto-install); subsumes the deferred `[bootstrap-recommend-projects-meta]` and the existing `superpowers`-only detector - [bootstrap-manifest.md](concepts/bootstrap-manifest.md) — record of which `project-bootstrap` / `setup-wiki` / `setup-tasks` versions initialized this project's `.wiki/` and `.tasks/` layout (overwritten on re-bootstrap; history in git) - [build-notes.md](concepts/build-notes.md) — why `build.ps1` exists alongside `build.sh`; PS 5.1 backslash-in-zip gotcha; how to extract a `.skill` - [install-portability.md](concepts/install-portability.md) — `install.sh` / `build.sh` rewritten to drop `mapfile` (bash 4+) and `find -printf` (GNU only) so stock macOS (bash 3.2 + BSD find) works diff --git a/.wiki/log.md b/.wiki/log.md index b00be12..00b5fe8 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -43,3 +43,5 @@ Parseable: `grep "^## \[" .wiki/log.md | tail -20`. ## [2026-05-05] ingest | concepts/interns-design ## [2026-05-05] decision | interns-skills-mvp — shipped `setup-interns` v0.1.0 (8-phase install: detect `.common/lib/interns-mcp/`, `pip install -e`, `.common/secrets/interns.env` write, `mcpServers.interns` registration with absolute Python interpreter + `cwd`) and `using-interns` v0.1.0 (runtime policy mirroring project-discipline Rule 4: ask-mode default, conversational grant/revoke, always-ask paths for `.env`/secrets/keys/SSH/credentials with transitive rule, cost-cap >$0.10, session-end reset; routing hints for `bulk_text_read` + `transcript_distill`); `project-bootstrap` 1.5.0→1.6.0 with canonical CLAUDE.md trigger `delegate to interns when allowed` between `follow project discipline` and `we're on Windows`, Step 5 commentary paragraph, manifest table extended with both new skills + `project-discipline` row; root `CLAUDE.md` dogfood updated; both READMEs written; descriptions verified (setup-interns 899 chars, using-interns 814 chars, both under 900 budget); all three rebuilt + installed + listed by harness with full descriptions (no H1 fallback) +## [2026-05-05] ingest | concepts/bootstrap-skill-deps-check +## [2026-05-05] decision | bootstrap-skill-deps-check — `project-bootstrap` 1.6.0→1.7.0 collapses Step 5.6 from a single-skill detector (only `superpowers` plugin) into a generic `trigger → fulfiller` table walker. Map embedded in SKILL.md (9 rows: caveman, superpowers plugin, using-wiki, using-tasks, using-projects-meta, pulling-before-work, project-discipline, using-interns, active-platform); `kind: skill` vs `kind: plugin` flag drives the install command emitted in the recommendation block. Algorithm: read project's CLAUDE.md → match each line vs map (substring + tolower, mirrors Step 5 idempotent merge) → for each canonical match check disk (`~/.claude/skills//SKILL.md` or `installed_plugins.json` key); print one chat-only block listing every missing fulfiller + install commands, or one ✅ line if all satisfied. User-custom lines silently skipped; removed canonical lines silently skipped (respects user opt-out). Hard rule "never auto-install" carries over verbatim. Subsumes the deferred `[bootstrap-recommend-projects-meta]` task (closed by absorption — generic step handles `using-projects-meta` along with everything else). MCP-server-backed skills only check the `using-X` policy skill; `setup-X` self-fires on first use via Prerequisites pointer, bootstrap doesn't duplicate. diff --git a/dist/project-bootstrap.skill b/dist/project-bootstrap.skill index bc0187b509603219269c6868d976c65a39b6b8ea..20860bf75f801ea90fe4346862ce493b8717a8fb 100644 GIT binary patch delta 9737 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 T5}WC966YAPkX~v2d+>h%s8Lp0 diff --git a/skills/project-bootstrap/README.md b/skills/project-bootstrap/README.md index f2ce38a..fe54434 100644 --- a/skills/project-bootstrap/README.md +++ b/skills/project-bootstrap/README.md @@ -59,10 +59,13 @@ projects bootstrapped at different times debuggable. 4. **Step 5.5.** Write `bootstrap-manifest.md` recording the versions of `project-bootstrap`, `setup-wiki`, `setup-tasks`, `project-discipline`, `setup-interns`, and `using-interns` used. -5. **Step 5.6.** Detect the official `superpowers@claude-plugins-official` - plugin via `~/.claude/plugins/installed_plugins.json`. If absent, print - a one-time chat recommendation with the install command and upstream - link. Never auto-installs, never modifies project files. +5. **Step 5.6.** Skill dependencies check. Walk the canonical trigger list + in `CLAUDE.md`, look each up in an embedded `trigger → fulfiller` map, + detect what's missing on this host (`~/.claude/skills//SKILL.md` + for skills, `~/.claude/plugins/installed_plugins.json` for plugins), + and print one chat-only block listing every missing fulfiller with a + copy-pasteable install command. Prints a single ✅ line when nothing + is missing. Never auto-installs, never modifies project files. 6. **Commit.** `chore: bootstrap project structure` for fresh repos, or `chore: upgrade project structure` adding only the new files for existing ones. Pushes only on explicit user request. diff --git a/skills/project-bootstrap/SKILL.md b/skills/project-bootstrap/SKILL.md index f925d68..2de14cc 100644 --- a/skills/project-bootstrap/SKILL.md +++ b/skills/project-bootstrap/SKILL.md @@ -1,6 +1,6 @@ --- name: project-bootstrap -version: 1.6.0 +version: 1.7.0 description: > Initializes or upgrades a project in the current folder: git, .gitignore, README.md, .wiki/ using Karpathy's method, .tasks/ for task tracking, CLAUDE.md with skill triggers. @@ -346,47 +346,94 @@ If a delegated setup-skill is unavailable on this machine (e.g. user installed o --- -## Step 5.6 — Recommend `superpowers` plugin (chat-only, never auto-install) +## Step 5.6 — Skill dependencies check (chat-only, never auto-install) -The `CLAUDE.md` template just written contains `use superpowers` as a trigger. -That trigger is a no-op unless the official `superpowers` plugin is installed -on the host. On a fresh machine the plugin is often absent, and the user -won't know the trigger is silently dead. Detect and recommend. +The `CLAUDE.md` template just written contains canonical trigger lines. +Each one is a no-op unless the corresponding skill or plugin is installed +on the host. On a fresh machine these are often absent, and the user +won't know the trigger is silently dead. Detect what's missing on this +machine and print one informational block in chat — never write into any +project file, never auto-install. -Read `~/.claude/plugins/installed_plugins.json`. The path is identical on -Windows / Linux / macOS — `~` resolves correctly under git-bash too. +### Trigger → fulfiller map -Look for the key `superpowers@claude-plugins-official` under `plugins`. -Two outcomes: +Source of truth for this map is the canonical `assets/CLAUDE.md.template`. +When a new trigger is added there, also add a row here in the same commit. +Mismatch between template and map → silent gaps in the recommendation. -- **Installed** — say nothing. Skip to Step 6. -- **Missing** (key absent, or the JSON file itself doesn't exist) — print - this block in chat exactly once. Do **not** write it into any project file: +| Trigger line in `CLAUDE.md` | Fulfiller | Kind | Detection path | Install command | +|---|---|---|---|---| +| `talk like a caveman` | `caveman` | skill | `~/.claude/skills/caveman/SKILL.md` | `bash scripts/install.sh caveman` | +| `use superpowers` | `superpowers@claude-plugins-official` | plugin | key `plugins["superpowers@claude-plugins-official"]` in `~/.claude/plugins/installed_plugins.json` | `/plugin install superpowers@claude-plugins-official` | +| `use project wiki` | `using-wiki` | skill | `~/.claude/skills/using-wiki/SKILL.md` | `bash scripts/install.sh using-wiki` | +| `use task management system` | `using-tasks` | skill | `~/.claude/skills/using-tasks/SKILL.md` | `bash scripts/install.sh using-tasks` | +| `check across all projects` | `using-projects-meta` | skill | `~/.claude/skills/using-projects-meta/SKILL.md` | `bash scripts/install.sh using-projects-meta` | +| `pull remote before work` | `pulling-before-work` | skill | `~/.claude/skills/pulling-before-work/SKILL.md` | `bash scripts/install.sh pulling-before-work` | +| `follow project discipline` | `project-discipline` | skill | `~/.claude/skills/project-discipline/SKILL.md` | `bash scripts/install.sh project-discipline` | +| `delegate to interns when allowed` | `using-interns` | skill | `~/.claude/skills/using-interns/SKILL.md` | `bash scripts/install.sh using-interns` | +| `we're on Windows` / `we're on Linux` / `we're on macOS` | `active-platform` | skill | `~/.claude/skills/active-platform/SKILL.md` | `bash scripts/install.sh active-platform` | - ``` - ℹ️ Recommended: install the official `superpowers` plugin +### Algorithm - CLAUDE.md now contains `use superpowers`, but that trigger is a no-op - until the plugin is installed. With it, every session auto-loads - brainstorming, planning, code-review, debugging, and TDD skills. +1. Read the project's `CLAUDE.md` (just-written or pre-existing). Extract + every non-empty, non-comment line — these are the active triggers for + THIS project. The user may have removed canonical lines on purpose; + respect that — only check what's actually in the file. +2. Match each line against the trigger column above using `trim` + `tolower` + substring (same matching as Step 5 idempotent merge). Lines that don't + match any row are user-custom — skip silently. The platform line matches + the `active-platform` row regardless of which platform is pinned. +3. For each matched canonical line, check the detection path: + - `kind: skill` → does `~/.claude/skills//SKILL.md` exist? + - `kind: plugin` → does `~/.claude/plugins/installed_plugins.json` contain + the plugin key under `plugins`? (Treat malformed JSON as "missing" and + continue — don't crash the bootstrap over a detection edge case.) +4. Collect every fulfiller that's missing. Two outcomes: - Install (run inside Claude Code): - /plugin install superpowers@claude-plugins-official + - **All present** — print one line: - Source: https://github.com/anthropics/claude-plugins-official + ``` + ✅ all skill dependencies satisfied — every CLAUDE.md trigger has its fulfiller on this host. + ``` - After install + Claude Code restart, the trigger picks it up. - ``` + Skip to Step 6. -**Hard rule — never auto-install.** Slash commands aren't callable from a -skill, and silently mutating user-level plugin state without consent is -overreach. The recommendation is informational. The user can install it -later, ignore it entirely, or remove the `use superpowers` line from -`CLAUDE.md` if they prefer a leaner setup. + - **Some missing** — print one block in chat exactly once. Do **not** + write it into any project file: -If the JSON file is malformed (rare), treat as "missing" and print the -recommendation. Do not crash the bootstrap over a plugin-detection edge -case — the file isn't ours to fix here. + ``` + ℹ️ Recommended: install the following to fulfill CLAUDE.md triggers + + The triggers below are present in CLAUDE.md but their fulfillers are + missing on this machine — they're silently no-ops until installed: + + trigger fulfiller (kind) + () + () + … + + Install (run inside Claude Code or terminal): + + + … + + After install + (for plugins) a Claude Code restart, the triggers pick + them up. + ``` + +### Notes + +- **MCP-server-backed skills** (`using-context7`, `using-projects-meta`, + `using-interns`) — only the `using-X` policy skill is checked here. If + the MCP isn't registered, the `using-X` Prerequisites pointer fires + `setup-X` at first use; bootstrap doesn't duplicate that detection. +- The `~/.claude/skills/` and `~/.claude/plugins/` paths resolve identically + on Windows / Linux / macOS — `~` works under git-bash too. +- **Hard rule — never auto-install.** Slash commands aren't callable from a + skill, and silently mutating user-level skill / plugin state without + consent is overreach. The recommendation is informational. The user can + install some / all / none of the recommendations, or remove canonical + lines from `CLAUDE.md` to lean the project's trigger set down. ## Step 6 — Commit