Replace the regex token substitution and the line-parsed bmad-if
directives in render_skill.py with Jinja2. Templates see config,
workflow, and snapshot(); undefined names, empty entry files, and
links to omitted sources halt; every value a render reaches and the
Jinja2 version key the generation. Migrate the five rendered skills'
sources to the new forms, keep their shipped output byte-identical,
and update the validator rule and the authoring docs.
The root package.json, lockfile, .nvmrc, prettier ignore file and
.npmignore are gone. docs-site has its own package.json and lockfile
with the Astro, ESLint and Prettier dependencies, and its scripts run
relative to that directory. tools/quality.py, both workflows and the
docs all call npm inside docs-site.
stamp_release.py stamps only the 29 skill manifests now; the version
lives nowhere else on this branch. The tests for package stamping go
with it.
* build: declare the Python toolchain in pyproject.toml
Pin the dev environment (pytest, pytest-xdist, pyyaml, ruamel.yaml, ruff)
with uv instead of a one-line --with list in package.json. The project is
marked non-packaged: the tree itself is what gets installed, and shipped
scripts keep their inline metadata. The validate CI job now runs on uv
alone, without Node, and gains a ruff check.
* style: apply ruff to tools and skill scripts
Mechanical output of ruff check --fix and ruff format under the new
config, plus four hand edits: explicit zip strictness in setup.py and
test_sprint_status.py, an f-string for the hex colour in brain.py, and a
noqa on the sys.path-dependent import in render_skill.py. No behaviour
change; 500 tests pass.
* build: declare Python 3.11 as the floor of every shipped script
Eighteen scripts declared 3.8 or 3.10 inline while ruff targeted 3.11,
so an autofix could introduce syntax those scripts did not advertise.
The lower floors were never reachable: every skill that carried one
also runs a 3.11 hub script, and uv fetches whatever interpreter a
script asks for. All inline metadata now says 3.11, and a test keeps
the inline floors equal to requires-python and the ruff target.
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
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.
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.
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.
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.
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.
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.
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 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.
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.
Replace tools/validate-skills.js with a stdlib Python 3.11 port and rewire
validate:skills and test:skills onto it. Observable behavior is preserved,
including the JS frontmatter quirks; in-skill file walks are sorted.
The checker's V4/V5 grammar (exec attrs, invoke-task, step metadata,
Load directives, quoted dot-paths, {_bmad} shorthand) matches nothing
in the flat tree; it verified only four script filenames. It now
resolves backticked slash-paths against the containing file's directory
and the skill root, flagging a missing file only when the path's first
directory exists — paths without one are prose, so the current tree
stays at zero false positives (276 refs verified, up from 135). Files
sitting directly under skills/ are reported as stray.
The checker still scanned src/, which the flattening removed, so it
crashed on any fresh checkout and failed the quality gate. It now scans
skills/; the obsolete core-skills/bmm-skills module mapping is replaced
by _bmad/scripts/ -> skills/bmad/scripts/, with the install-only and
install-generated skip lists unchanged.
Move the duplicated BMM routing guide into the bmad hub skill and replace
every module-manifest.md with a two-key TOML file parsed by stdlib tomllib.
Drop PyYAML from setup.py and from test:npx-skills.
Move core and BMM skills to skills/<id>/ so npx skills can install
the repo as-is. Copy the BMM module manifest into every skill, move
the shared Python runtime into skills/bmad/scripts/, drop src/, and
retarget repo tests at the new locations.
Port tools/validate-file-refs.js to tools/validate_file_refs.py (PEP 723,
pyyaml) preserving detection, resolution, CLI, and CI-annotation behavior.
CSV extraction is dropped: .csv leaves the scan set, and the CSV test and
fixtures are deleted rather than ported. The npm dependencies block (yaml,
csv-parse) goes away with its sole consumer; validate:refs and test:refs
now run via uv, and test:refs joins the quality chain. A new stdlib
unittest suite covers extraction, mapping, skip lists, UNRESOLVED vs
BROKEN, leak detection, exit codes, and the GitHub Actions output path.
The port also hardens what the JS left fragile: multi-document YAML is
scanned in full, non-UTF-8 bytes are replaced instead of crashing the
run, and directory symlinks are not traversed.
Core skills are independent tools, not a set to finish. Help no longer
reports uninstalled skills as missing, and the packager no longer
requires every skill id in the manifest body.
The model already knows who it is talking to and what language fits the
conversation; answers frozen at install time only mislead. Setup asks no
questions of its own now — the only questions come from installed module
manifests. user_name, communication_language, user_skill_level, and
document_output_language leave the config templates, setup.py, the setup
reference, and every live skill's prose. Classic-installer surface
(module.yaml, tools/installer, v6-shims, bmad-help) is unchanged; the
test suite keeps negative assertions that the keys stay out of team
config.
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.
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.
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.
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.
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.
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.
Replaces tools/validate-skills.js with tools/validate_skills.py, taken from the
flat-tree work on feat/npx-skills-distribution rather than written afresh, so
the two do not diverge again. Discovery is repointed from that branch's flat
skills/ tree to src/. Behaviour is otherwise unchanged: same rule IDs, same
JSON shape, same --strict and --json flags, and the tests come across as
unittest cases run through uv, matching how the other Python tests are wired.
The three step-structure rules go away with it. STEP-01 (filename format),
STEP-06 (step frontmatter) and STEP-07 (step count) all key off a literal
steps/ directory that only two of fifty skills have — everywhere else step
files sit at the skill root — so they validated almost nothing. The catalog's
first-pass list drops from 13 rules to 10 to match, and their rule sections
are removed.
CI is unaffected: quality.yaml already provisions uv and invokes the npm
script rather than the file.