diff --git a/src/core-skills/bmad-module/README.md b/src/core-skills/bmad-module/README.md index 605b24063..928bf3f5c 100644 --- a/src/core-skills/bmad-module/README.md +++ b/src/core-skills/bmad-module/README.md @@ -1,18 +1,12 @@ # bmad-module -The core BMAD skill for installing, updating, removing, and listing community -BMAD modules. Modules are standalone GitHub repos that conform to the BMAD -Module Manifest Spec (see `docs/spec.md` in `bmad-marketplace`). +The core BMAD skill for installing, updating, removing, and listing community BMAD modules. Modules are standalone GitHub repos that conform to the BMAD Module Manifest Spec (see `docs/spec.md` in `bmad-marketplace`). ## How it fits -- **Authors** publish a single repo with `.claude-plugin/plugin.json` that - works in both Claude Code's plugin marketplace and BMAD-METHOD. -- **Users** install via this skill — no CLI required. Modules land in - `_bmad//` alongside the official modules. -- **BMAD-METHOD** treats community-installed modules as a new `source: 'community'` - row in `manifest.yaml`; re-running `bmad install` preserves them (with the - paired `manifest-generator.js` patch). +- **Authors** publish a single repo with `.claude-plugin/plugin.json` that works in both Claude Code's plugin marketplace and BMAD-METHOD. +- **Users** install via this skill — no CLI required. Modules land in `_bmad//` alongside the official modules. +- **BMAD-METHOD** treats community-installed modules as a new `source: 'community'` row in `manifest.yaml`; re-running `bmad install` preserves them (with the paired `manifest-generator.js` patch). ## Verbs @@ -27,45 +21,24 @@ bmad-module list [--json] ## Behavior notes -- **Source of truth** for what was installed is `_bmad/_config/files-manifest.csv` - (per-file hashes) and `_bmad/_config/skill-manifest.csv` (one row per - shipped skill). `manifest.yaml` carries the source/version/sha tuple. -- **`update`** refuses to overwrite locally-modified files (hash mismatch - against the recorded hash). Move overrides into `_bmad/custom//` - and retry. -- **`remove`** without `--purge` preserves `_bmad/custom//` so a - re-install picks the customizations back up. `--purge` deletes them. -- **Hooks / MCP / LSP / Claude subagents** declared in the module manifest - are _copied_ but NOT auto-activated by this skill. Use Claude Code's - plugin manager to wire them up. +- **Source of truth** for what was installed is `_bmad/_config/files-manifest.csv` (per-file hashes) and `_bmad/_config/skill-manifest.csv` (one row per shipped skill). `manifest.yaml` carries the source/version/sha tuple. +- **`update`** refuses to overwrite locally-modified files (hash mismatch against the recorded hash). Move overrides into `_bmad/custom//` and retry. +- **`remove`** without `--purge` preserves `_bmad/custom//` so a re-install picks the customizations back up. `--purge` deletes them. +- **Hooks / MCP / LSP / Claude subagents** declared in the module manifest are _copied_ but NOT auto-activated by this skill. Use Claude Code's plugin manager to wire them up. ## Implementation -The skill itself is a thin verb router (`SKILL.md`). `scripts/bmad-module.mjs` -is a zero-import launcher that guards the import graph (a missing/corrupt -runtime file becomes a documented exit code, not a raw stack trace); the verb -dispatcher lives in `scripts/cli.mjs` and all filesystem work happens in the -`lib/` modules. These carry **no registry dependencies** — important because -the installer copies the skill into `_bmad/` without `node_modules` and never -runs `npm install` there: +The skill itself is a thin verb router (`SKILL.md`). `scripts/bmad-module.mjs` is a zero-import launcher that guards the import graph (a missing/corrupt runtime file becomes a documented exit code, not a raw stack trace); the verb dispatcher lives in `scripts/cli.mjs` and all filesystem work happens in the `lib/` modules. These carry **no registry dependencies** — important because the installer copies the skill into `_bmad/` without `node_modules` and never runs `npm install` there: -- `manifest.yaml` is read/written with a **vendored copy of the real `yaml` - library** (`lib/vendor/yaml.mjs`, regenerated by `lib/vendor/build-vendor.mjs`) - so it stays byte-identical to BMAD core's writer. -- `semver` validity/range checks use a small `node:`-only helper - (`lib/semver-lite.mjs`). +- `manifest.yaml` is read/written with a **vendored copy of the real `yaml` library** (`lib/vendor/yaml.mjs`, regenerated by `lib/vendor/build-vendor.mjs`) so it stays byte-identical to BMAD core's writer. +- `semver` validity/range checks use a small `node:`-only helper (`lib/semver-lite.mjs`). -They re-use no BMAD-METHOD internal modules — the same code runs during -development inside `bmad-marketplace` and after the skill is PR'd into -BMAD-METHOD core. +They re-use no BMAD-METHOD internal modules — the same code runs during development inside `bmad-marketplace` and after the skill is PR'd into BMAD-METHOD core. ## Exit codes -See `SKILL.md` for the full table. The script's stderr always names the -condition; the codes are stable so tooling can branch. +See `SKILL.md` for the full table. The script's stderr always names the condition; the codes are stable so tooling can branch. ## Tests -Integration tests live in `tests/integration.test.sh` and run end-to-end on -a fresh BMAD install. Fixtures for negative cases (collisions, path -traversal, reserved codes) are under `tests/fixtures/`. +Integration tests live in `tests/integration.test.sh` and run end-to-end on a fresh BMAD install. Fixtures for negative cases (collisions, path traversal, reserved codes) are under `tests/fixtures/`. diff --git a/src/core-skills/bmad-module/SKILL.md b/src/core-skills/bmad-module/SKILL.md index f40b0f641..083866fc4 100644 --- a/src/core-skills/bmad-module/SKILL.md +++ b/src/core-skills/bmad-module/SKILL.md @@ -5,27 +5,14 @@ description: Install, update, remove, or list community BMAD modules. Use when t # bmad-module -Manage community BMAD modules — installable packages of skills, agents, and -supporting assets that ship as standalone GitHub repos. Modules land in -`_bmad//` alongside official modules and are tracked in the -existing manifests. The same artifact is also loadable as a Claude Code -plugin via its `.claude-plugin/plugin.json` manifest. +Manage community BMAD modules — installable packages of skills, agents, and supporting assets that ship as standalone GitHub repos. Modules land in `_bmad//` alongside official modules and are tracked in the existing manifests. The same artifact is also loadable as a Claude Code plugin via its `.claude-plugin/plugin.json` manifest. ## CRITICAL RULES -- NEVER write directly to files under `_bmad/`. All filesystem changes go - through the Node script at `scripts/bmad-module.mjs` — it handles staging, - atomic swaps, manifest updates, and rollback on failure. -- HALT and report cleanly if `_bmad/` is not present in the current working - directory (exit code 10 from the script). -- DO NOT execute hooks, MCP server commands, or any code shipped inside the - module during install. The install copies files; activation is a separate - step the user opts into via Claude Code's plugin manager. -- If the script exits non-zero, report the exit code and stderr verbatim and - stop. Do NOT retry, do NOT try a different verb. The one exception is exit - code 5 (the skill's own bundled runtime files are missing/corrupt): that's a - fixable setup/packaging problem, not a module rejection — relay the script's - "reinstall the skill" guidance instead of reporting a failed install. +- NEVER write directly to files under `_bmad/`. All filesystem changes go through the Node script at `scripts/bmad-module.mjs` — it handles staging, atomic swaps, manifest updates, and rollback on failure. +- HALT and report cleanly if `_bmad/` is not present in the current working directory (exit code 10 from the script). +- DO NOT execute hooks, MCP server commands, or any code shipped inside the module during install. The install copies files; activation is a separate step the user opts into via Claude Code's plugin manager. +- If the script exits non-zero, report the exit code and stderr verbatim and stop. Do NOT retry, do NOT try a different verb. The one exception is exit code 5 (the skill's own bundled runtime files are missing/corrupt): that's a fixable setup/packaging problem, not a module rejection — relay the script's "reinstall the skill" guidance instead of reporting a failed install. ## EXECUTION @@ -40,35 +27,27 @@ The user's request maps to exactly one of: | `remove` | "remove module X", "uninstall X", "delete X module" | | `list` | "list modules", "what modules are installed", "show installed modules" | -If the verb is ambiguous (e.g. the user says "manage modules"), ASK which -verb they want before continuing. +If the verb is ambiguous (e.g. the user says "manage modules"), ASK which verb they want before continuing. ### Step 2 — Parse the args -- **install:** the user supplies `` — `owner/repo` (GitHub short), - a full git URL (`https://…` or `git@…`), or a local path. Optional flags: - `--ref `, `--channel `, `--dry-run`. -- **update:** the user supplies `` (the `_bmad//` folder name) - or asks for "all"; in that case use `--all`. Optional `--ref`. -- **remove:** the user supplies ``. Use `--purge` only if they - explicitly say "also remove customizations" or "purge". +- **install:** the user supplies `` — `owner/repo` (GitHub short), a full git URL (`https://…` or `git@…`), or a local path. Optional flags: `--ref `, `--channel `, `--dry-run`. +- **update:** the user supplies `` (the `_bmad//` folder name) or asks for "all"; in that case use `--all`. Optional `--ref`. +- **remove:** the user supplies ``. Use `--purge` only if they explicitly say "also remove customizations" or "purge". - **list:** no args. Use `--json` if the user asks for machine-readable. If anything is missing or ambiguous, ASK before invoking. ### Step 3 — Confirm before destructive verbs -For `install`, `update`, and `remove`, summarize what will happen and confirm -once with the user: +For `install`, `update`, and `remove`, summarize what will happen and confirm once with the user: > About to **install** `acme/acme-devlog` (will create `_bmad/devlog/`). > Proceed? [y/N] -For `install` you may run a dry-run first (`--dry-run`) and show the file -plan; that counts as the summary — still confirm before the real run. +For `install` you may run a dry-run first (`--dry-run`) and show the file plan; that counts as the summary — still confirm before the real run. -Skip the confirmation step only if the user has already pre-authorized in -this turn (e.g. "go ahead and install acme-md-lint without asking"). +Skip the confirmation step only if the user has already pre-authorized in this turn (e.g. "go ahead and install acme-md-lint without asking"). ### Step 4 — Invoke the Node script @@ -78,21 +57,15 @@ Run from the project root (the dir containing `_bmad/`): node /scripts/bmad-module.mjs [args...] ``` -`` is wherever the skill files live in the current install. After -this skill ships into BMAD-METHOD that's `_bmad/core/skills/bmad-module/`; -during development it's this repo's `src/core-skills/bmad-module/`. +`` is wherever the skill files live in the current install. After this skill ships into BMAD-METHOD that's `_bmad/core/skills/bmad-module/`; during development it's this repo's `src/core-skills/bmad-module/`. -Stream stdout and stderr verbatim. Do NOT silence or rewrite them — the -script's own messages are designed for end-user consumption. +Stream stdout and stderr verbatim. Do NOT silence or rewrite them — the script's own messages are designed for end-user consumption. ### Step 5 — Report -On exit 0: paraphrase the script's final line(s) and note any next-step hint -(e.g. "next: run the `bmad-devlog-setup` skill to finish setup"). +On exit 0: paraphrase the script's final line(s) and note any next-step hint (e.g. "next: run the `bmad-devlog-setup` skill to finish setup"). -On non-zero exit: print the exit code, the stderr message, and stop. Do not -suggest workarounds beyond what the script's message itself suggests -(e.g. "use `update` instead", "move changes into `_bmad/custom//`"). +On non-zero exit: print the exit code, the stderr message, and stop. Do not suggest workarounds beyond what the script's message itself suggests (e.g. "use `update` instead", "move changes into `_bmad/custom//`"). ## EXIT CODES @@ -114,18 +87,12 @@ suggest workarounds beyond what the script's message itself suggests ## EXAMPLES -User: "Install the devlog module from acme/acme-devlog" -→ Confirm, then run: -`node …/scripts/bmad-module.mjs install acme/acme-devlog` +User: "Install the devlog module from acme/acme-devlog" → Confirm, then run: `node …/scripts/bmad-module.mjs install acme/acme-devlog` -User: "Try installing examples/minimal/acme-md-lint first as a dry-run" -→ Run with `--dry-run`, show the plan, then ask whether to proceed for real. +User: "Try installing examples/minimal/acme-md-lint first as a dry-run" → Run with `--dry-run`, show the plan, then ask whether to proceed for real. -User: "What modules do I have installed?" -→ Run `… list`. No confirmation needed (read-only). +User: "What modules do I have installed?" → Run `… list`. No confirmation needed (read-only). -User: "Update the devlog module to v0.5.0" -→ Confirm, then run `… update devlog --ref v0.5.0`. +User: "Update the devlog module to v0.5.0" → Confirm, then run `… update devlog --ref v0.5.0`. -User: "Remove the mdlint module and wipe its customizations too" -→ Confirm, then run `… remove mdlint --purge`. +User: "Remove the mdlint module and wipe its customizations too" → Confirm, then run `… remove mdlint --purge`. diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/README.md b/src/core-skills/bmad-module/scripts/lib/vendor/README.md index 4647eb58b..534c25e79 100644 --- a/src/core-skills/bmad-module/scripts/lib/vendor/README.md +++ b/src/core-skills/bmad-module/scripts/lib/vendor/README.md @@ -1,24 +1,18 @@ # `lib/vendor/` — vendored runtime dependencies -This directory holds **self-contained, generated** copies of third-party -libraries the skill needs at runtime. They are committed on purpose. +This directory holds **self-contained, generated** copies of third-party libraries the skill needs at runtime. They are committed on purpose. ## Why vendor at all? -The `bmad-module` skill is **copied into a user's project** at -`_bmad/core/skills/bmad-module/` by `npx bmad-method install`. The installer: +The `bmad-module` skill is **copied into a user's project** at `_bmad/core/skills/bmad-module/` by `npx bmad-method install`. The installer: - strips `node_modules` while copying (`tools/installer/core/installer.js`), - ships **no** `package.json` under the skill, and - never runs `npm install` inside `_bmad/`. -So a bare `import 'yaml'` cannot resolve at runtime — it throws -`ERR_MODULE_NOT_FOUND` before any of the skill's exit codes can fire. Every -other script BMAD installs is zero-third-party-dependency; vendoring keeps this -skill self-sufficient the same way, without setup. +So a bare `import 'yaml'` cannot resolve at runtime — it throws `ERR_MODULE_NOT_FOUND` before any of the skill's exit codes can fire. Every other script BMAD installs is zero-third-party-dependency; vendoring keeps this skill self-sufficient the same way, without setup. -Files here are imported by **relative path** (`./vendor/yaml.mjs`), which -resolves regardless of cwd, install location, or `node_modules` presence. +Files here are imported by **relative path** (`./vendor/yaml.mjs`), which resolves regardless of cwd, install location, or `node_modules` presence. ## What's vendored — and what's NOT @@ -27,39 +21,25 @@ resolves regardless of cwd, install location, or `node_modules` presence. | `yaml` (parse/stringify `_bmad/_config/manifest.yaml`) | **vendored, real library** | `vendor/yaml.mjs` | | `semver` (`valid` + `validRange` on `plugin.json`) | **dropped** — hand-rolled, `node:` only | `../semver-lite.mjs` | -`manifest.yaml` is **co-owned** with BMAD core, which reads/writes it with the -same `yaml` package and the same `{indent:2, lineWidth:0}` options -(`tools/installer/core/manifest.js`). Hand-rolling a YAML emitter risks -diverging from that on the user's live install state, so we ship the **real** -library and verify byte-identical output in `build-vendor.mjs`. `semver` is -only input-validation of an author's manifest, so it is safe to hand-roll. +`manifest.yaml` is **co-owned** with BMAD core, which reads/writes it with the same `yaml` package and the same `{indent:2, lineWidth:0}` options (`tools/installer/core/manifest.js`). Hand-rolling a YAML emitter risks diverging from that on the user's live install state, so we ship the **real** library and verify byte-identical output in `build-vendor.mjs`. `semver` is only input-validation of an author's manifest, so it is safe to hand-roll. ## `yaml.mjs` -- **GENERATED — do not edit by hand.** An esbuild single-file bundle of the - `yaml` npm package (eemeli/yaml), tree-shaken to just `parse` + `stringify`. +- **GENERATED — do not edit by hand.** An esbuild single-file bundle of the `yaml` npm package (eemeli/yaml), tree-shaken to just `parse` + `stringify`. - The exact pinned version and build provenance are in the file's header. - Upstream license is retained inline (`legalComments: 'inline'`). ### Regenerating -After bumping `yaml` (or esbuild) in the repo's **root** `package.json` + -`npm install`: +After bumping `yaml` (or esbuild) in the repo's **root** `package.json` + `npm install`: ```bash npm run vendor:build # regenerate this yaml.mjs npm run vendor:check # verify it's in sync (what CI runs) ``` -The build is **deterministic** for a given `yaml` + `esbuild` version (both -pinned in the lockfile) and self-checks a parse→stringify round-trip. +The build is **deterministic** for a given `yaml` + `esbuild` version (both pinned in the lockfile) and self-checks a parse→stringify round-trip. -**You don't have to remember to do this.** `vendor:check` is wired into -`npm test` (husky pre-commit) and `npm run quality` (the `validate` job in -`.github/workflows/quality.yaml`). If the committed bundle drifts from the -installed `yaml`/`esbuild` version, those gates fail with a message telling you -to run `npm run vendor:build` — so a bump can't land with a stale bundle, and -manifest writes stay byte-identical between BMAD core and this skill. +**You don't have to remember to do this.** `vendor:check` is wired into `npm test` (husky pre-commit) and `npm run quality` (the `validate` job in `.github/workflows/quality.yaml`). If the committed bundle drifts from the installed `yaml`/`esbuild` version, those gates fail with a message telling you to run `npm run vendor:build` — so a bump can't land with a stale bundle, and manifest writes stay byte-identical between BMAD core and this skill. -Lint/format intentionally ignore this directory (see `eslint.config.mjs` -global ignores and `.prettierignore`) — it is generated, not authored. +Lint/format intentionally ignore this directory (see `eslint.config.mjs` global ignores and `.prettierignore`) — it is generated, not authored.