feat!: distribute BMAD as a flat skills tree via npx skills and plugin marketplaces - #2768
Open
alexeyv wants to merge 90 commits into
Open
feat!: distribute BMAD as a flat skills tree via npx skills and plugin marketplaces#2768alexeyv wants to merge 90 commits into
alexeyv wants to merge 90 commits into
Conversation
3 tasks
alexeyv
force-pushed
the
feat/npx-skills-distribution
branch
from
August 21, 2026 02:54
69481ae to
9254832
Compare
This was referenced Aug 24, 2026
alexeyv
marked this pull request as ready for review
August 24, 2026 17:14
Contributor
|
Too many files changed for review (434 files, 100 file limit). Bypass the limit by tagging |
Contributor
|
@coderabbitai review |
alexeyv
force-pushed
the
feat/npx-skills-distribution
branch
from
August 28, 2026 15:47
9e77de7 to
63e4d3d
Compare
alexeyv
force-pushed
the
feat/npx-skills-distribution
branch
from
August 30, 2026 22:29
63e4d3d to
518c239
Compare
Walk core+bmm skill sources into a skills/<canonical-id>/ tree and fatten only dest bmad-help with the shared Python, module.yaml defaults, and a baked core+bmm help catalog.
Read top-down from main, drop PackagerError, and stop catching failures so a missing source or payload surfaces as a stack.
Read package steps top-down, assemble bmad-help.csv through the stdlib csv module so quoted descriptions stay intact, and assert bad, empty, or short catalogs fail without touching dest.
Materialize _bmad from dest help: scripts symlink or copy, shipped module.yaml defaults, empty custom/, baked catalog.
First-run setup copies authored team and user templates instead of walking module.yaml, and reads the three user answers from a sidecar so the skill route does not put them on the shell.
Setup is the only place that knows the template filenames; the packager copies help assets as-is.
Invoke setup from {skill-root}/scripts/setup.py so dest scripts
includes the pump and an npx update is seen. Require
resolve_config.py in the payload.
Move the setup interview and pump into references/setup.md so ordinary bmad-help loads do not pay that context.
A second bmad-help setup now fills new team-config and module-yaml keys, replaces the baked catalog, and repairs a wrong or stale scripts path. Existing custom/ and *.user.toml stay untouched.
Document shipped skill relationships, conditional routes, completion criteria, and artifact destinations for independently packaged skills. Refs: sc-1
Add the canonical bmad entrypoint, exclude bmad-help from flattened output, and retain transitional CSV packaging. Move the setup tests, allow the canonical root ID in validation, and normalize the existing install-only custom path for strict reference checks.
Validate module manifests and package versions before replacing output. Copy identical manifests and declared scripts into every owning skill.
Replace catalog-driven ordinary help with fresh host skill discovery and read-only manifest reasoning for partial and conflicting installations.
Add two deterministic runtime modes to the bmad skill. Update inspects every installed manifest copy, compares each against its declared source, and reports state per module without writing anything. Doctor repairs an existing _bmad: it asks only newly declared questions, refreshes the shared scripts as an exact copy, and makes each selected module's script tree match its declaration, choosing the unique highest orderable release when copies disagree and blocking the module otherwise.
The branch parked the npx setup payload in bmad-help and later moved it to the bmad skill, leaving SKILL.md routing setup and update to files that no longer exist. bmad-help is legacy-installer surface: this branch leaves it byte-identical to main and the packager already excludes it.
Point both marketplaces at plugins/bmad-method. That folder holds the host manifests and a skills/ symlink into the repo skills tree so an install copies the plugin directory and Claude dereferences the payload. Track the Codex catalog at .agents/plugins/marketplace.json.
Codex plugin add copies the plugin directory without dereferencing symlinks, so the nested plugin shipped no skills. Root the Codex plugin at the repo so the cache copy carries the real skills tree.
Every version declaration now reads 6.11.0-next, matching the module-manifest.toml files the update check compares against.
… mirror Make the bmad-skills mirror's main branch release-only: dev now mirrors this branch unstamped, and main is always current dev plus one stamp commit cut by following tools/release.md. - tools/stamp_release.py: stdlib-only stamper that validates an orderable SemVer argument (rejects -dev, which setup.py cannot order), rewrites the version line in every skills/*/module-manifest.toml textually to keep the manifests byte-identical, and sets the version in .claude-plugin/marketplace.json, plugins/bmad-method/.claude-plugin/ plugin.json, and .codex-plugin/plugin.json without reformatting them. Nothing is written unless every file validates; after writing it re-reads everything and fails naming the offending path if the manifests diverge or any file carries a different version. - tools/release.md: self-contained runbook — clean detached checkout of origin/dev, human-chosen version that differs from what main serves, stamp, review, commit, force-with-lease push of HEAD:main. - tools/tests/test_stamp_release.py: unit tests covering the happy path, invalid and -dev versions, missing version line/key, divergent manifests, idempotent re-stamp, and formatting preservation. - TEMP-RELEASE-PROCESS.md: the mirror is the testbed for both the npx install/update flow and the release process that feeds it. Records the dev/main branch model, the 0.0.0-next.N throwaway versions to stamp there, and a note to replace the file with the real release instructions before this branch merges to main. - AGENTS.md: replace the test-mirror section with a pointer to that file. - .npmignore: exclude the new tool files and the temporary runbook from the npm tarball.
The version's single source of truth is skills/*/module-manifest.toml. Plugin metadata (Claude marketplace and plugin.json, Codex plugin.json) is no longer stamped — whatever builds those artifacts reads the version from skills/bmad/module-manifest.toml. The textual single-"version"-key rewrite would have broken the moment the Claude marketplace gained a second plugin, and those lids are likely to leave this repo anyway. Tests updated: JSON-stamping cases replaced with a regression test that plugin metadata is left byte-identical; runbook expectation adjusted.
The tools/bmm split will put a second plugin into the Claude marketplace, so the stamper no longer hardcodes plugins[0] and the bmad-method path. It now stamps the version of every entry in the marketplace plugins array, discovers plugins/*/.claude-plugin/plugin.json by glob, and requires the marketplace entry count to match the plugin manifest count. Each textual rewrite is proven against the parsed JSON: the result must equal the original tree with only the intended version nodes changed, and a "version" key anywhere else in a stamped file fails the run before anything is written.
The stamper globbed for manifests, so a skill directory without module-manifest.toml was silently skipped and would ship unstamped with no version or update_source. Enumerate skill directories and fail naming the offender before anything is written.
Run the suite with pytest-xdist (-n auto): 17.7s to 5.9s wall measured locally. Quiet output when green (-q), short tracebacks and a summary of non-passing outcomes when not (-ra --tb=short). Write nothing to disk: -p no:cacheprovider stops .pytest_cache and PYTHONDONTWRITEBYTECODE=1 stops __pycache__; a warm single-directory bytecode cache measured no faster than cold, so the cache bought nothing. Parallelism exposed a race in test_recon_kit.py: two test classes shared the fixture path tests/_report.md and deleted it after use, so xdist workers could unlink it under each other. All four file-fixture tests there now write into per-test temporary directories.
Two marketplace entries share source "./" and list their skills explicitly, replacing the single plugin whose skills tree was a symlink to skills/ — with per-plugin subsets the symlink bought nothing. The plugins/bmad-method lid is deleted. bmad-tools has no skills yet, and an empty skills list makes Claude Code fall back to scanning skills/, so its source points at the empty plugins/tools directory instead. The stamper now validates the whole distribution shape before writing: every manifest carries exactly module, version, and update_source, with a known module and the one known source; the marketplace has exactly one entry per module; and each entry's skills list equals, as a set, the skills whose manifest carries that module — no orphans, no double-shipping, no dangling paths (a dangling path is load-bearing: Claude Code silently falls back to shipping every skill for one). The manifest byte-identity check is now per module, since manifests legitimately differ in the module line. Verified live: bmad-bmm installs with 29 skills, bmad-tools with 0, and a scratch stamp of the real tree passes.
…and toolbox modules The seven standalone support skills that help.md lists as belonging to no path and no stage — brainstorming, forge-idea, deep-recon, advanced-elicitation, review, party-mode, customize — now carry module = "toolbox"; the other 22 skills carry module = "method". The module key names only the distribution grouping; the _bmad/bmm runtime config path skills read is staged by setup.py independently of it. The Claude marketplace and the Codex plugin lid leave this repo: both plugin ecosystems are built by bmad-code-org/bmad-plugins from the stamped manifests bmad-skills main serves, sharing one skills tree per plugin. The stamper therefore stamps and validates only the manifests (exact key set, known module, the one known update source, per-module byte identity), and the runbook gains a rebuild-the-plugins step.
… from bmad-plugins
…oved README section
setup and doctor rewrite _bmad wholesale via replace_dir, so running them through a symlinked _bmad crashed mid-flight with a raw IsADirectoryError from the backup rename. Reads through the symlink are the point of symlinking and stay untouched; only the write paths now fail fast, naming the link target and the --project-root to use against the real installation. The doctor flow rejects in its missing-_bmad pre-flight so --list-config-questions --doctor fails before interrogating the user.
help.md now names https://docs.bmad-method.org/llms.txt as the place to look when it and the installed skills cannot answer a BMad question; the index also names the source repository as the final authority. The hub skill's constraints direct that fetch before conceding a limitation, restoring the remote-docs grounding the CSV-era bmad-help had via its _meta row.
parse_packaged_manifest required `knowledge` to equal one hardcoded literal naming the bmad skill's own help document, so no second module could ship a manifest that parses. The value is then discarded — it is not a field on ParsedManifest and nothing reads it — so the check bought nothing. The cost was disproportionate. discover_installed_copies parses every sibling manifest before doing any work, so one unacceptable value aborted `bmad update`, `setup`, and `doctor` for the whole project, including modules whose own manifests were fine. This is also what the format spec asks for: the packager rejects unknown or malformed keys, while `bmad` ignores what it does not recognize, so a manifest from a newer format cannot break an older hub. stamp_release.py keeps its equality check — it is the packager, and it only ever runs against this repo's manifests. `knowledge` stays required and non-empty. CIS points it at its published llms.txt, the same remote-docs grounding help.md already names for BMM.
The knowledge value named `reference/help.md`, but the bmad skill has no reference/ directory — the file is references/help.md. Nothing ever resolved the value, so the dangling pointer reached 30 manifests and the release stamper's constant unnoticed.
The hub bound every route to references/help.md and was told to treat no manifest key but `module` as routing, so a module shipped from another repository was visible and unroutable. Discovery now reads each sound manifest's free-form `knowledge` text and follows it, for the module the question concerns, to the document it names. help.md stops being privileged: it opens with the discovery model and carries its method/toolbox content as a labelled section — which is simply what those two modules' manifests point at.
setup.py drops build metadata when ordering versions, so 6.12.0+hotfix compares equal to 6.12.0 and a release stamped that way is invisible to every installed copy. The runbook warned against it; the stamper accepted it. Reject it before anything is written. The version rule is now "orderable and distinguishable", one notch stricter than setup.py's orderability - the contract test pins that gap and pins the premise it rests on.
`load_central_config` read `_bmad/config.user.toml` as layer 2 while setup.py listed the same file in LEGACY_LEFTOVERS — debris from the classic installer that doctor reports read-only and never writes. The mechanism that populated it (the removed JS installer's `module.yaml` `scope:` partition) is gone, so nothing creates the file and the resolver read a layer that could only ever be hand-written into a directory documented as installer-owned. Central config is now three layers, matching load_customization: config.toml -> custom/config.toml -> custom/config.user.toml. Tests keep writing config.user.toml and assert a key unique to it never reaches the merge, pinning the removal.
Sixteen skills read `_bmad/bmm/config.yaml` directly — a flattened legacy YAML projection that setup.py writes only for `core` and `bmm`. It coerces every value to a string, lets `[core]` silently win key collisions with `[modules.bmm]`, and is generated once at setup, so the `_bmad/custom/` override layers never reach it. Each now calls resolve_config.py with the exact keys it needs, matching the eight skills that already did. Nothing reads either YAML projection after this; setup.py still writes them. Values stay under `modules.bmm`, which is where config.template.toml writes them, though these skills declare `module = "method"`. Drops the fallbacks with them: the `config.user.yaml` hedge (nothing writes that file), the missing-key defaults, and "never block". The config step always follows a resolve_customization.py call, so a missing scripts directory has already surfaced by then.
Setup resolved config through the script already, but still wrote core/config.yaml and bmm/config.yaml on every run. Nothing reads them. Drop the writes and the code that existed only to build them: the legacy_config projection threaded through materialize_bmad/stage_bmad, without_manifest_answers, delete_path, stringify, and the whole YAML parse/render/fill path. ensure_file now merges TOML only. Existing files are left in place, like the other classic-installer traces, and both paths join LEGACY_LEFTOVERS so doctor reports them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sn1de7Zc9yWcJnJkE6QNis
Main renamed bmad-checkpoint-preview to bmad-walkthrough; apply the same rename under skills/, repoint the hub help doc, and drop the v6 shim main added since this branch removes the shim tree.
The Python rewrite of the file-ref checker ported the JS pattern verbatim, including the `\\\\` that only matched the escaped `C:\\Users` form. Main fixed the JS copy in #2794 before this branch deleted it; apply the same pattern here so the rebase does not revert the fix.
alexeyv
force-pushed
the
feat/npx-skills-distribution
branch
from
September 1, 2026 19:14
518c239 to
9f77c1b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every agent that understands skills already has an install door:
npx skills add, and for Claude Code and Codex, plugin marketplaces. BMAD's skills could not go through any of them — they lived nested undersrc/, depended on an installer-built_bmad/, and shipped through a bespoke npm installer that only some agents could use.This PR makes the repository itself the distributable: a flat
skills/tree that generic tooling can install, with a smallbmadhub skill handling project setup, update checks, and repair. The classic installer and its packaging pipeline are removed.What changed
Flat skills tree. All 29 skills now live at
skills/<canonical-id>/, each carrying amodule-manifest.toml(module membership, version,update_source, and aknowledgekey pointing at thebmadskill's help doc). No build step stands between the repo and an install.Two modules, one flat tree. The seven standalone support skills that belong to no path and no stage — brainstorming, forge-idea, deep-recon, advanced-elicitation, review, party-mode, customize — plus the
bmadhub carrymodule = "toolbox"; the other 21 carrymodule = "method". The key names only the distribution grouping; the_bmad/bmmruntime config path skills read is staged bysetup.pyindependently of it.The
bmadhub skill. Answers help from the module manifests (falling back to the remote docs site for questions the local manifests cannot answer) and owns three commands:bmad setup(materializes_bmad/— scripts, config, output dir — and fills new config keys on re-run without touchingcustom/or*.user.toml),bmad update(checks every installed skill's manifest against itsupdate_source, reporting plugin-shipped copies as plugin-managed), andbmad doctor(repairs an existing runtime). Deterministic parts are Python driven bysetup.py.Distribution split across two repos. This repo is the source of the skills tree only.
bmad-code-org/bmad-skillsserves it to installers —devmirrors this branch,mainis release-only and defines the version installed copies compare against. The Claude and Codex plugin ecosystems are built separately bybmad-code-org/bmad-pluginsfrom the stamped manifestsbmad-skillsmainserves, so no.claude-plugin,.codex-plugin, orplugins/directory remains here. All three doors —npx skills add, the Claude marketplace, the Codex plugin — were exercised end to end against that arrangement.Release process.
tools/release.mdis a hand-run runbook andtools/stamp_release.pya deterministic stdlib-Python stamper: the human supplies a SemVer version, the stamper writes it into every manifest and validates them (exact key set, known module, the one known update source, per-module byte identity), and the runbook turns that into one stamp commit force-pushed tomainplus a tag, then a plugin rebuild.devis never stamped.Removals. The classic installer (
tools/installer), the npx packager and publish scripts, the npm publish workflow, the v6 shim skills, and assorted dead tooling. Net effect: 430 files changed, +12,182 / −36,562 lines.Tooling ported to Python.
validate_skills,validate_file_refs, and the renderer snapshot tests are Python now; tests are colocated with the skills they cover and run innpm test.Repo housekeeping.
CLAUDE.mdis a tracked one-line import ofAGENTS.mdso both agent files stay identical;website/renamed todocs-site/; every version declaration aligned at6.11.0-next.Still draft because
README.md,docs/how-to/install-bmad.md, and translations) still document the removednpx bmad-method installflow and need a rewrite around the new doors.TEMP-RELEASE-PROCESS.mddescribes the rehearsal against the disposable mirror and must be replaced with the real process before merge.Replaces #2736, whose description predated most of this scope.