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/<name>/SKILL.md
for skills, plugins.<id> 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)
This commit is contained in:
2026-05-05 21:21:19 +03:00
parent 2f7943f06f
commit 8c547d73af
8 changed files with 206 additions and 47 deletions

View File

@@ -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/<name>/SKILL.md` exists?
- `kind: plugin` → key under `plugins.<id>` 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/<name>/`, installed via `bash scripts/install.sh <name>` (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 <id>@<marketplace>`. 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.

View File

@@ -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

View File

@@ -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/<name>/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.