bmad-method 6.10.1-next.8 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +30 -57
- package/README.md +49 -82
- package/README_CN.md +10 -10
- package/README_VN.md +11 -11
- package/bmad-modules.yaml +43 -39
- package/package.json +7 -3
- package/removals.txt +17 -0
- package/src/bmm-skills/{1-analysis → agents}/bmad-agent-analyst/SKILL.md +1 -1
- package/src/bmm-skills/{1-analysis → agents}/bmad-agent-analyst/customize.toml +22 -7
- package/src/bmm-skills/{3-solutioning → agents}/bmad-agent-architect/SKILL.md +1 -1
- package/src/bmm-skills/{3-solutioning → agents}/bmad-agent-architect/customize.toml +2 -2
- package/src/bmm-skills/{4-implementation → agents}/bmad-agent-dev/SKILL.md +1 -1
- package/src/bmm-skills/{4-implementation → agents}/bmad-agent-dev/customize.toml +7 -14
- package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-pm/SKILL.md +1 -1
- package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-pm/customize.toml +2 -2
- package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-ux-designer/SKILL.md +1 -1
- package/src/bmm-skills/module-help.csv +14 -26
- package/src/bmm-skills/module.yaml +5 -15
- package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/SKILL.md +3 -3
- package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/customize.toml +4 -2
- package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/SKILL.md +1 -1
- package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
- package/src/bmm-skills/plan/bmad-generate-project-context/SKILL.md +10 -0
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/SKILL.md +3 -1
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/prd-template.md +1 -1
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/customize.toml +5 -3
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/references/validate.md +3 -3
- package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/SKILL.md +1 -1
- package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/bmad-manifest.json +1 -1
- package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/verdict.md +1 -1
- package/src/bmm-skills/{1-analysis → plan}/bmad-product-brief/SKILL.md +1 -1
- package/src/bmm-skills/{1-analysis → plan}/bmad-product-brief/customize.toml +5 -3
- package/src/bmm-skills/plan/bmad-project-context/SKILL.md +110 -0
- package/src/bmm-skills/plan/bmad-project-context/customize.toml +24 -0
- package/src/bmm-skills/plan/bmad-project-context/references/best-practices.md +65 -0
- package/src/bmm-skills/plan/bmad-project-context/references/template.md +55 -0
- package/src/{core-skills → bmm-skills/plan}/bmad-spec/SKILL.md +18 -3
- package/src/{core-skills → bmm-skills/plan}/bmad-spec/assets/spec-template.md +1 -1
- package/src/bmm-skills/plan/bmad-spec/assets/stories-schema.md +44 -0
- package/src/{core-skills → bmm-skills/plan}/bmad-spec/customize.toml +3 -4
- package/src/bmm-skills/plan/bmad-sprint-planning/SKILL.md +62 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/fix-sprint-status.md +30 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/generate-tracking.md +25 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/readiness-gate.md +20 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/status-view.md +14 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/validate.md +10 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/sprint_plan.py +697 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
- package/src/bmm-skills/{4-implementation → plan}/bmad-sprint-planning/sprint-status-template.yaml +9 -7
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/SKILL.md +1 -1
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/color-themes.md +1 -1
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/customize.toml +4 -2
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/creative-tools.md +1 -1
- package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/validate.md +1 -1
- package/src/bmm-skills/ship/bmad-build/SKILL.md +13 -0
- package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/compile-epic-context.md +1 -1
- package/src/bmm-skills/ship/bmad-build/customize.toml +164 -0
- package/src/bmm-skills/ship/bmad-build/references/deletion-check.md +14 -0
- package/src/bmm-skills/ship/bmad-build/review-prompts/edge-case-hunter.md +88 -0
- package/src/bmm-skills/ship/bmad-build/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-01-clarify-and-route.md +20 -19
- package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-02-plan.md +5 -9
- package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-03-implement.md +10 -6
- package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-04-review.md +9 -15
- package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-05-present.md +10 -13
- package/src/bmm-skills/ship/bmad-build/step-oneshot.md +77 -0
- package/src/bmm-skills/ship/bmad-build/sync-sprint-status.md +19 -0
- package/src/bmm-skills/ship/bmad-build/workflow.md +84 -0
- package/src/bmm-skills/ship/bmad-build-auto/SKILL.md +13 -0
- package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/compile-epic-context.md +1 -1
- package/src/bmm-skills/ship/bmad-build-auto/customize.toml +121 -0
- package/src/{core-skills/bmad-review-edge-case-hunter/SKILL.md → bmm-skills/ship/bmad-build-auto/review-prompts/edge-case-hunter.md} +21 -6
- package/src/{core-skills/bmad-review-verification-gap/SKILL.md → bmm-skills/ship/bmad-build-auto/review-prompts/verification-gap.md} +15 -8
- package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/spec-template.md +2 -1
- package/src/bmm-skills/ship/bmad-build-auto/step-01-clarify-and-route.md +83 -0
- package/src/bmm-skills/ship/bmad-build-auto/step-02-plan.md +27 -0
- package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/step-03-implement.md +6 -4
- package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/step-04-review.md +26 -25
- package/src/bmm-skills/ship/bmad-build-auto/workflow.md +104 -0
- package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/SKILL.md +1 -1
- package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-05-wrapup.md +1 -1
- package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/SKILL.md +1 -1
- package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/customize.toml +37 -17
- package/src/bmm-skills/ship/bmad-code-review/references/deletion-check.md +14 -0
- package/src/bmm-skills/ship/bmad-code-review/review-prompts/edge-case-hunter.md +88 -0
- package/src/bmm-skills/ship/bmad-code-review/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-01-gather-context.md +7 -4
- package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-02-review.md +1 -1
- package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-04-present.md +1 -1
- package/src/bmm-skills/{4-implementation → ship}/bmad-correct-course/SKILL.md +7 -8
- package/src/bmm-skills/{4-implementation → ship}/bmad-qa-generate-e2e-tests/SKILL.md +2 -2
- package/src/bmm-skills/ship/bmad-retrospective/SKILL.md +94 -0
- package/src/bmm-skills/{4-implementation → ship}/bmad-retrospective/customize.toml +2 -2
- package/src/bmm-skills/ship/bmad-retrospective/references/acceptance-verdict.md +55 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/aggregate-views.md +17 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/evidence-gathering.md +30 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/retro-document.md +84 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/team-discussion.md +22 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/git_evidence.py +304 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/sprint_status.py +746 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_git_evidence.py +750 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_sprint_status.py +1579 -0
- package/src/bmm-skills/v6-shims/README.md +28 -0
- package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/SKILL.md +2 -2
- package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/SKILL.md +4 -4
- package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/SKILL.md +5 -3
- package/src/bmm-skills/v6-shims/bmad-dev-auto/SKILL.md +19 -0
- package/src/bmm-skills/{4-implementation → v6-shims}/bmad-dev-story/SKILL.md +5 -3
- package/src/bmm-skills/{4-implementation → v6-shims}/bmad-dev-story/customize.toml +3 -0
- package/src/bmm-skills/v6-shims/bmad-document-project/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +14 -0
- package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/SKILL.md +4 -4
- package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-quick-dev/SKILL.md +19 -0
- package/src/bmm-skills/v6-shims/bmad-sprint-status/SKILL.md +26 -0
- package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +14 -0
- package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/SKILL.md +4 -4
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +26 -103
- package/src/core-skills/bmad-advanced-elicitation/customize.toml +54 -0
- package/src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
- package/src/core-skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
- package/src/core-skills/bmad-brainstorming/SKILL.md +3 -3
- package/src/core-skills/bmad-brainstorming/assets/brain-selector.html +2 -0
- package/src/core-skills/bmad-brainstorming/references/mode-autonomous.md +1 -1
- package/src/core-skills/bmad-brainstorming/scripts/brain.py +36 -6
- package/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +22 -0
- package/src/core-skills/bmad-customize/SKILL.md +2 -2
- package/src/core-skills/bmad-deep-recon/SKILL.md +82 -0
- package/src/core-skills/bmad-deep-recon/assets/research.template.md +18 -0
- package/src/core-skills/bmad-deep-recon/customize.toml +212 -0
- package/src/core-skills/bmad-deep-recon/references/draft.md +8 -0
- package/src/core-skills/bmad-deep-recon/references/finalize.md +11 -0
- package/src/core-skills/bmad-deep-recon/references/html-briefing.md +16 -0
- package/src/core-skills/bmad-deep-recon/references/lifecycle.md +11 -0
- package/src/core-skills/bmad-deep-recon/references/process.md +10 -0
- package/src/core-skills/bmad-deep-recon/references/run.md +73 -0
- package/src/core-skills/bmad-deep-recon/references/selection.md +13 -0
- package/src/core-skills/bmad-deep-recon/references/synthesis.md +16 -0
- package/src/core-skills/bmad-deep-recon/references/verification.md +29 -0
- package/src/core-skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
- package/src/core-skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
- package/src/core-skills/bmad-deep-recon/types/academic-lit.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/competitive.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/domain.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/market.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/technical.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/user-voice.md +19 -0
- package/src/core-skills/bmad-forge-idea/SKILL.md +2 -2
- package/src/core-skills/bmad-forge-idea/scripts/resolve_personas.py +7 -2
- package/src/core-skills/bmad-help/SKILL.md +3 -3
- package/src/core-skills/bmad-party-mode/SKILL.md +2 -2
- package/src/core-skills/bmad-party-mode/customize.toml +1 -1
- package/src/core-skills/bmad-party-mode/scripts/resolve_party.py +14 -4
- package/src/core-skills/bmad-review/SKILL.md +49 -0
- package/src/core-skills/bmad-review/customize.toml +141 -0
- package/src/core-skills/bmad-review/references/editorial-common.md +56 -0
- package/src/core-skills/bmad-review/references/lens-adversarial.md +19 -0
- package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +54 -0
- package/src/core-skills/bmad-review/references/lens-prose.md +7 -0
- package/src/core-skills/bmad-review/references/lens-structure.md +9 -0
- package/src/core-skills/bmad-review/references/lens-verification-gap.md +92 -0
- package/src/core-skills/bmad-review/references/structure-models.md +44 -0
- package/src/core-skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
- package/src/core-skills/bmad-review/scripts/word_metrics.py +102 -0
- package/src/core-skills/module-help.csv +4 -8
- package/src/core-skills/module.yaml +5 -0
- package/src/core-skills/v6-shims/README.md +25 -0
- package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-editorial-review/customize.toml +31 -0
- package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +6 -0
- package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
- package/src/scripts/config_utils.py +119 -0
- package/src/scripts/render_skill.py +401 -0
- package/src/scripts/resolve_config.py +32 -136
- package/src/scripts/resolve_customization.py +43 -184
- package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
- package/src/scripts/tests/test_config_utils.py +85 -0
- package/src/scripts/tests/test_resolve_config.py +89 -0
- package/src/scripts/tests/test_resolve_customization.py +27 -0
- package/tools/installer/cli-utils.js +6 -2
- package/tools/installer/core/installer.js +31 -9
- package/tools/installer/core/manifest-generator.js +1 -1
- package/tools/installer/core/uv-check.js +122 -24
- package/tools/installer/ide/_config-driven.js +1 -1
- package/tools/installer/ide/platform-codes.yaml +7 -0
- package/tools/installer/ide/shared/path-utils.js +2 -2
- package/tools/installer/install-messages.yaml +3 -2
- package/tools/installer/modules/custom-module-manager.js +12 -6
- package/tools/installer/modules/external-manager.js +12 -8
- package/tools/installer/modules/git-env.js +47 -0
- package/tools/installer/modules/official-modules.js +1 -1
- package/tools/installer/prompts.js +41 -102
- package/tools/installer/ui.js +99 -22
- package/tools/skill-validator.md +11 -1
- package/tools/validate-published-implementation-model.mjs +68 -0
- package/tools/validate-skills.js +33 -0
- package/web-bundles/prd-coach/prd-template.md +1 -1
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +0 -76
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/customize.toml +0 -81
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/explain-concept.md +0 -20
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/mermaid-gen.md +0 -20
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/validate-doc.md +0 -19
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/write-document.md +0 -20
- package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +0 -62
- package/src/bmm-skills/1-analysis/bmad-document-project/checklist.md +0 -245
- package/src/bmm-skills/1-analysis/bmad-document-project/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/bmad-document-project/documentation-requirements.csv +0 -12
- package/src/bmm-skills/1-analysis/bmad-document-project/instructions.md +0 -128
- package/src/bmm-skills/1-analysis/bmad-document-project/templates/deep-dive-template.md +0 -345
- package/src/bmm-skills/1-analysis/bmad-document-project/templates/index-template.md +0 -169
- package/src/bmm-skills/1-analysis/bmad-document-project/templates/project-overview-template.md +0 -103
- package/src/bmm-skills/1-analysis/bmad-document-project/templates/project-scan-report-schema.json +0 -160
- package/src/bmm-skills/1-analysis/bmad-document-project/templates/source-tree-template.md +0 -135
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-instructions.md +0 -300
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-workflow.md +0 -34
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-instructions.md +0 -1108
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-workflow.md +0 -34
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +0 -96
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-01-init.md +0 -137
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-02-domain-analysis.md +0 -229
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-03-competitive-landscape.md +0 -238
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-04-regulatory-focus.md +0 -206
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-05-technical-trends.md +0 -234
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +0 -450
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/research.template.md +0 -29
- package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +0 -96
- package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/research/bmad-market-research/research.template.md +0 -29
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-01-init.md +0 -184
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-02-customer-behavior.md +0 -239
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-03-customer-pain-points.md +0 -251
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-04-customer-decisions.md +0 -261
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-05-competitive-analysis.md +0 -173
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +0 -484
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +0 -96
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/research.template.md +0 -29
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-01-init.md +0 -137
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-02-technical-overview.md +0 -239
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-03-integration-patterns.md +0 -248
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-04-architectural-patterns.md +0 -202
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-05-implementation-research.md +0 -233
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +0 -493
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +0 -91
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/customize.toml +0 -41
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +0 -179
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +0 -168
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +0 -169
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-04-ux-alignment.md +0 -129
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-05-epic-quality-review.md +0 -241
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +0 -132
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/templates/readiness-report-template.md +0 -4
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +0 -81
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/customize.toml +0 -41
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/project-context-template.md +0 -21
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-01-discover.md +0 -186
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-02-generate.md +0 -321
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-03-complete.md +0 -284
- package/src/bmm-skills/4-implementation/bmad-dev-auto/SKILL.md +0 -103
- package/src/bmm-skills/4-implementation/bmad-dev-auto/customize.toml +0 -108
- package/src/bmm-skills/4-implementation/bmad-dev-auto/step-01-clarify-and-route.md +0 -66
- package/src/bmm-skills/4-implementation/bmad-dev-auto/step-02-plan.md +0 -31
- package/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +0 -115
- package/src/bmm-skills/4-implementation/bmad-quick-dev/customize.toml +0 -83
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +0 -82
- package/src/bmm-skills/4-implementation/bmad-quick-dev/sync-sprint-status.md +0 -19
- package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +0 -1527
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md +0 -319
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/checklist.md +0 -34
- package/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md +0 -311
- package/src/core-skills/bmad-brainstorming/analysis/catalog-analysis.md +0 -239
- package/src/core-skills/bmad-brainstorming/analysis/method-matrix.csv +0 -109
- package/src/core-skills/bmad-editorial-review-prose/SKILL.md +0 -86
- package/src/core-skills/bmad-editorial-review-structure/SKILL.md +0 -179
- package/src/core-skills/bmad-index-docs/SKILL.md +0 -66
- package/src/core-skills/bmad-review-adversarial-general/SKILL.md +0 -37
- package/src/core-skills/bmad-shard-doc/SKILL.md +0 -105
- /package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-ux-designer/customize.toml +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/assets/spine-template.md +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/references/headless.md +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/references/reviewer-gate.md +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/scripts/lint_spine.py +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/scripts/tests/test_lint_spine.py +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/customize.toml +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-02-design-epics.md +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-03-create-stories.md +0 -0
- /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/templates/epics-template.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/headless-schemas.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/prd-validation-checklist.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/validation-report-template.html +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/references/headless.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/agents/artifact-analyzer.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/agents/web-researcher.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/assets/prfaq-template.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/customize.toml +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/customer-faq.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/internal-faq.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/press-release.md +0 -0
- /package/src/bmm-skills/{1-analysis → plan}/bmad-product-brief/assets/brief-template.md +0 -0
- /package/src/{core-skills → bmm-skills/plan}/bmad-spec/assets/headless-schemas.md +0 -0
- /package/src/bmm-skills/{4-implementation → plan}/bmad-sprint-planning/customize.toml +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-directions.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-example-editorial.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-example-mobile.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-example-shadcn.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/excalidraw-wireframe.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/experience-example-mobile.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/experience-example-shadcn.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/headless-schemas.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/key-screens.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/validation-report-template.html +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/design-md-spec.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/headless.md +0 -0
- /package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/spec-template.md +0 -0
- /package/src/{core-skills/bmad-review-edge-case-hunter → bmm-skills/ship/bmad-build-auto}/references/deletion-check.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/customize.toml +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/generate-trail.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-01-orientation.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-02-walkthrough.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-03-detail-pass.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-04-testing.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-03-triage.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-correct-course/checklist.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-correct-course/customize.toml +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-qa-generate-e2e-tests/checklist.md +0 -0
- /package/src/bmm-skills/{4-implementation → ship}/bmad-qa-generate-e2e-tests/customize.toml +0 -0
- /package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/customize.toml +0 -0
- /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/customize.toml +0 -0
- /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/checklist.md +0 -0
- /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/customize.toml +0 -0
- /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/discover-inputs.md +0 -0
- /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/template.md +0 -0
- /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-dev-story/checklist.md +0 -0
- /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/customize.toml +0 -0
- /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-sprint-status/customize.toml +0 -0
- /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/customize.toml +0 -0
- /package/src/core-skills/bmad-advanced-elicitation/{methods.csv → assets/methods.csv} +0 -0
- /package/src/core-skills/bmad-party-mode/scripts/tests/{test-resolve_party.py → test_resolve_party.py} +0 -0
|
@@ -1,177 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
1. {project-root}/_bmad/custom/{name}.user.toml (personal, gitignored)
|
|
7
|
-
2. {project-root}/_bmad/custom/{name}.toml (team/org, committed)
|
|
8
|
-
3. {skill-root}/customize.toml (skill defaults)
|
|
9
|
-
|
|
10
|
-
Skill name is derived from the basename of the skill directory.
|
|
11
|
-
|
|
12
|
-
Outputs merged JSON to stdout. Errors go to stderr.
|
|
13
|
-
|
|
14
|
-
Uses only the Python stdlib (`tomllib`) — no third-party dependencies.
|
|
15
|
-
BMad is standardizing on `uv run` to invoke scripts (uv provisions a suitable
|
|
16
|
-
interpreter for you); a plain `python3` on PATH still works during the
|
|
17
|
-
transition. Either runner needs Python 3.11+ for `tomllib`.
|
|
18
|
-
|
|
19
|
-
uv run resolve_customization.py --skill /abs/path/to/skill-dir
|
|
20
|
-
uv run resolve_customization.py --skill ... --key agent
|
|
21
|
-
uv run resolve_customization.py --skill ... --key agent.menu
|
|
22
|
-
|
|
23
|
-
Merge rules (purely structural — no field-name special-casing):
|
|
24
|
-
- Scalars (string, int, bool, float): override wins
|
|
25
|
-
- Tables: deep merge (recursively apply these rules)
|
|
26
|
-
- Arrays of tables where every item shares the *same* identifier
|
|
27
|
-
field (every item has `code`, or every item has `id`):
|
|
28
|
-
merge by that key (matching keys replace, new keys append)
|
|
29
|
-
- All other arrays — including arrays where only some items have
|
|
30
|
-
`code` or `id`, or where items mix the two keys:
|
|
31
|
-
append (base items followed by override items)
|
|
32
|
-
|
|
33
|
-
No removal mechanism — overrides cannot delete base items. To suppress
|
|
34
|
-
a default, fork the skill or override the item by code with a no-op
|
|
35
|
-
description/prompt.
|
|
36
|
-
"""
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# ///
|
|
5
|
+
"""Resolve a skill's default, team, and user TOML customization layers."""
|
|
37
6
|
|
|
38
7
|
import argparse
|
|
39
8
|
import json
|
|
40
9
|
import sys
|
|
41
10
|
from pathlib import Path
|
|
42
11
|
|
|
12
|
+
# Installed scripts are consumer files, not a location for interpreter caches.
|
|
13
|
+
sys.dont_write_bytecode = True
|
|
14
|
+
|
|
43
15
|
try:
|
|
44
|
-
import
|
|
45
|
-
except
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
sys.exit(3)
|
|
16
|
+
from config_utils import ConfigError, load_customization
|
|
17
|
+
except ModuleNotFoundError as error:
|
|
18
|
+
if error.name != "tomllib":
|
|
19
|
+
raise
|
|
20
|
+
sys.stderr.write("error: Python 3.11+ is required (stdlib `tomllib` not found).\n")
|
|
21
|
+
raise SystemExit(3) from None
|
|
52
22
|
|
|
53
23
|
|
|
54
24
|
_MISSING = object()
|
|
55
|
-
_KEYED_MERGE_FIELDS = ("code", "id")
|
|
56
25
|
|
|
57
26
|
|
|
58
|
-
def find_project_root(start: Path):
|
|
27
|
+
def find_project_root(start: Path) -> Path | None:
|
|
59
28
|
current = start.resolve()
|
|
60
29
|
while True:
|
|
61
30
|
if (current / "_bmad").exists() or (current / ".git").exists():
|
|
62
31
|
return current
|
|
63
|
-
parent
|
|
64
|
-
if parent == current:
|
|
32
|
+
if current.parent == current:
|
|
65
33
|
return None
|
|
66
|
-
current = parent
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def load_toml(file_path: Path, required: bool = False) -> dict:
|
|
70
|
-
if not file_path.exists():
|
|
71
|
-
if required:
|
|
72
|
-
sys.stderr.write(f"error: required customization file not found: {file_path}\n")
|
|
73
|
-
sys.exit(1)
|
|
74
|
-
return {}
|
|
75
|
-
try:
|
|
76
|
-
with file_path.open("rb") as f:
|
|
77
|
-
parsed = tomllib.load(f)
|
|
78
|
-
if not isinstance(parsed, dict):
|
|
79
|
-
if required:
|
|
80
|
-
sys.stderr.write(f"error: {file_path} did not parse to a table\n")
|
|
81
|
-
sys.exit(1)
|
|
82
|
-
return {}
|
|
83
|
-
return parsed
|
|
84
|
-
except tomllib.TOMLDecodeError as error:
|
|
85
|
-
level = "error" if required else "warning"
|
|
86
|
-
sys.stderr.write(f"{level}: failed to parse {file_path}: {error}\n")
|
|
87
|
-
if required:
|
|
88
|
-
sys.exit(1)
|
|
89
|
-
return {}
|
|
90
|
-
except OSError as error:
|
|
91
|
-
level = "error" if required else "warning"
|
|
92
|
-
sys.stderr.write(f"{level}: failed to read {file_path}: {error}\n")
|
|
93
|
-
if required:
|
|
94
|
-
sys.exit(1)
|
|
95
|
-
return {}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _detect_keyed_merge_field(items):
|
|
99
|
-
"""Return 'code' or 'id' if every table item carries that *same* field.
|
|
100
|
-
|
|
101
|
-
All items must share the same identifier (all `code`, or all `id`).
|
|
102
|
-
Mixed arrays — where some items use `code` and others use `id` —
|
|
103
|
-
return None and fall through to append semantics. This is intentional:
|
|
104
|
-
mixing identifier keys within one array is a schema smell, and
|
|
105
|
-
append-fallback is safer than guessing which key should merge.
|
|
106
|
-
"""
|
|
107
|
-
if not items or not all(isinstance(item, dict) for item in items):
|
|
108
|
-
return None
|
|
109
|
-
for candidate in _KEYED_MERGE_FIELDS:
|
|
110
|
-
if all(item.get(candidate) is not None for item in items):
|
|
111
|
-
return candidate
|
|
112
|
-
return None
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def _merge_by_key(base, override, key_name):
|
|
116
|
-
result = []
|
|
117
|
-
index_by_key = {}
|
|
118
|
-
|
|
119
|
-
for item in base:
|
|
120
|
-
if not isinstance(item, dict):
|
|
121
|
-
continue
|
|
122
|
-
if item.get(key_name) is not None:
|
|
123
|
-
index_by_key[item[key_name]] = len(result)
|
|
124
|
-
result.append(dict(item))
|
|
125
|
-
|
|
126
|
-
for item in override:
|
|
127
|
-
if not isinstance(item, dict):
|
|
128
|
-
result.append(item)
|
|
129
|
-
continue
|
|
130
|
-
key = item.get(key_name)
|
|
131
|
-
if key is not None and key in index_by_key:
|
|
132
|
-
result[index_by_key[key]] = dict(item)
|
|
133
|
-
else:
|
|
134
|
-
if key is not None:
|
|
135
|
-
index_by_key[key] = len(result)
|
|
136
|
-
result.append(dict(item))
|
|
137
|
-
|
|
138
|
-
return result
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _merge_arrays(base, override):
|
|
142
|
-
"""Shape-aware array merge. Base + override combined tables may opt into
|
|
143
|
-
keyed merge if every item has `code` or `id`. Otherwise: append."""
|
|
144
|
-
base_arr = base if isinstance(base, list) else []
|
|
145
|
-
override_arr = override if isinstance(override, list) else []
|
|
146
|
-
keyed_field = _detect_keyed_merge_field(base_arr + override_arr)
|
|
147
|
-
if keyed_field:
|
|
148
|
-
return _merge_by_key(base_arr, override_arr, keyed_field)
|
|
149
|
-
return base_arr + override_arr
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def deep_merge(base, override):
|
|
153
|
-
"""Recursively merge override into base using structural rules.
|
|
154
|
-
- Table + table: deep merge
|
|
155
|
-
- Array + array: shape-aware (keyed merge if all items have code/id, else append)
|
|
156
|
-
- Anything else: override wins
|
|
157
|
-
"""
|
|
158
|
-
if isinstance(base, dict) and isinstance(override, dict):
|
|
159
|
-
result = dict(base)
|
|
160
|
-
for key, over_val in override.items():
|
|
161
|
-
if key in result:
|
|
162
|
-
result[key] = deep_merge(result[key], over_val)
|
|
163
|
-
else:
|
|
164
|
-
result[key] = over_val
|
|
165
|
-
return result
|
|
166
|
-
if isinstance(base, list) and isinstance(override, list):
|
|
167
|
-
return _merge_arrays(base, override)
|
|
168
|
-
return override
|
|
34
|
+
current = current.parent
|
|
169
35
|
|
|
170
36
|
|
|
171
37
|
def extract_key(data, dotted_key: str):
|
|
172
|
-
parts = dotted_key.split(".")
|
|
173
38
|
current = data
|
|
174
|
-
for part in
|
|
39
|
+
for part in dotted_key.split("."):
|
|
175
40
|
if isinstance(current, dict) and part in current:
|
|
176
41
|
current = current[part]
|
|
177
42
|
else:
|
|
@@ -179,62 +44,56 @@ def extract_key(data, dotted_key: str):
|
|
|
179
44
|
return current
|
|
180
45
|
|
|
181
46
|
|
|
182
|
-
def write_json_stdout(output):
|
|
183
|
-
"""Write JSON as UTF-8 so Windows cp1252 stdout can carry emoji icons."""
|
|
47
|
+
def write_json_stdout(output) -> None:
|
|
184
48
|
reconfigure = getattr(sys.stdout, "reconfigure", None)
|
|
185
49
|
if reconfigure is not None:
|
|
186
50
|
reconfigure(encoding="utf-8")
|
|
187
51
|
sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n")
|
|
188
52
|
|
|
189
53
|
|
|
190
|
-
def main():
|
|
54
|
+
def main() -> int:
|
|
191
55
|
parser = argparse.ArgumentParser(
|
|
192
|
-
description="Resolve customization
|
|
193
|
-
|
|
56
|
+
description="Resolve skill customization using three-layer TOML merge."
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--skill", "-s", required=True, help="Absolute path to the skill directory"
|
|
194
60
|
)
|
|
195
61
|
parser.add_argument(
|
|
196
|
-
"--
|
|
197
|
-
|
|
62
|
+
"--project-root",
|
|
63
|
+
"-p",
|
|
64
|
+
help="Explicit project root containing _bmad/ (recommended)",
|
|
198
65
|
)
|
|
199
66
|
parser.add_argument(
|
|
200
|
-
"--key",
|
|
67
|
+
"--key",
|
|
68
|
+
"-k",
|
|
69
|
+
action="append",
|
|
70
|
+
default=[],
|
|
201
71
|
help="Dotted field path to resolve (repeatable). Omit for full dump.",
|
|
202
72
|
)
|
|
203
73
|
args = parser.parse_args()
|
|
204
74
|
|
|
205
75
|
skill_dir = Path(args.skill).resolve()
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
team = {}
|
|
218
|
-
user = {}
|
|
219
|
-
if project_root:
|
|
220
|
-
custom_dir = project_root / "_bmad" / "custom"
|
|
221
|
-
team = load_toml(custom_dir / f"{skill_name}.toml")
|
|
222
|
-
user = load_toml(custom_dir / f"{skill_name}.user.toml")
|
|
223
|
-
|
|
224
|
-
merged = deep_merge(defaults, team)
|
|
225
|
-
merged = deep_merge(merged, user)
|
|
76
|
+
project_root = (
|
|
77
|
+
Path(args.project_root).resolve()
|
|
78
|
+
if args.project_root
|
|
79
|
+
else find_project_root(skill_dir) or find_project_root(Path.cwd())
|
|
80
|
+
)
|
|
81
|
+
try:
|
|
82
|
+
merged = load_customization(project_root, skill_dir)
|
|
83
|
+
except ConfigError as error:
|
|
84
|
+
sys.stderr.write(f"error: {error}\n")
|
|
85
|
+
return 1
|
|
226
86
|
|
|
87
|
+
output = merged
|
|
227
88
|
if args.key:
|
|
228
89
|
output = {}
|
|
229
90
|
for key in args.key:
|
|
230
91
|
value = extract_key(merged, key)
|
|
231
92
|
if value is not _MISSING:
|
|
232
93
|
output[key] = value
|
|
233
|
-
else:
|
|
234
|
-
output = merged
|
|
235
|
-
|
|
236
94
|
write_json_stdout(output)
|
|
95
|
+
return 0
|
|
237
96
|
|
|
238
97
|
|
|
239
98
|
if __name__ == "__main__":
|
|
240
|
-
main()
|
|
99
|
+
raise SystemExit(main())
|
|
Binary file
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import tempfile
|
|
3
|
+
import unittest
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
8
|
+
|
|
9
|
+
from config_utils import ( # noqa: E402
|
|
10
|
+
ConfigError,
|
|
11
|
+
load_central_config,
|
|
12
|
+
load_customization,
|
|
13
|
+
load_toml,
|
|
14
|
+
structural_merge,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfigUtilsTests(unittest.TestCase):
|
|
19
|
+
def test_structural_merge_recurses_appends_and_replaces_keyed_tables(self):
|
|
20
|
+
base = {
|
|
21
|
+
"nested": {"keep": True, "replace": "old"},
|
|
22
|
+
"plain": ["base"],
|
|
23
|
+
"items": [{"id": "one", "value": "old"}],
|
|
24
|
+
}
|
|
25
|
+
override = {
|
|
26
|
+
"nested": {"replace": "new"},
|
|
27
|
+
"plain": ["override"],
|
|
28
|
+
"items": [
|
|
29
|
+
{"id": "one", "value": "new"},
|
|
30
|
+
{"id": "two", "value": "added"},
|
|
31
|
+
],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
merged = structural_merge(base, override)
|
|
35
|
+
|
|
36
|
+
self.assertEqual(merged["nested"], {"keep": True, "replace": "new"})
|
|
37
|
+
self.assertEqual(merged["plain"], ["base", "override"])
|
|
38
|
+
self.assertEqual(
|
|
39
|
+
merged["items"],
|
|
40
|
+
[
|
|
41
|
+
{"id": "one", "value": "new"},
|
|
42
|
+
{"id": "two", "value": "added"},
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def test_non_string_keyed_identifier_is_rejected(self):
|
|
47
|
+
with self.assertRaisesRegex(ConfigError, "identifier `id` must be a string"):
|
|
48
|
+
structural_merge([{"id": "valid"}], [{"id": 42}])
|
|
49
|
+
|
|
50
|
+
def test_present_malformed_optional_layer_is_rejected(self):
|
|
51
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
52
|
+
path = Path(temp_dir) / "optional.toml"
|
|
53
|
+
path.write_text("[broken\n", encoding="utf-8")
|
|
54
|
+
|
|
55
|
+
with self.assertRaisesRegex(ConfigError, "failed to parse"):
|
|
56
|
+
load_toml(path)
|
|
57
|
+
|
|
58
|
+
def test_missing_optional_layer_is_empty(self):
|
|
59
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
60
|
+
path = Path(temp_dir) / "optional.toml"
|
|
61
|
+
|
|
62
|
+
self.assertEqual(load_toml(path), {})
|
|
63
|
+
|
|
64
|
+
def test_filesystem_layer_precedence(self):
|
|
65
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
66
|
+
root = Path(temp_dir)
|
|
67
|
+
bmad = root / "_bmad"
|
|
68
|
+
custom = bmad / "custom"
|
|
69
|
+
skill = bmad / "bmm" / "sample-skill"
|
|
70
|
+
custom.mkdir(parents=True)
|
|
71
|
+
skill.mkdir(parents=True)
|
|
72
|
+
(bmad / "config.toml").write_text('[value]\norder = "base-team"\n', encoding="utf-8")
|
|
73
|
+
(bmad / "config.user.toml").write_text('[value]\norder = "base-user"\n', encoding="utf-8")
|
|
74
|
+
(custom / "config.toml").write_text('[value]\norder = "custom-team"\n', encoding="utf-8")
|
|
75
|
+
(custom / "config.user.toml").write_text('[value]\norder = "custom-user"\n', encoding="utf-8")
|
|
76
|
+
(skill / "customize.toml").write_text('[value]\norder = "default"\n', encoding="utf-8")
|
|
77
|
+
(custom / "sample-skill.toml").write_text('[value]\norder = "team"\n', encoding="utf-8")
|
|
78
|
+
(custom / "sample-skill.user.toml").write_text('[value]\norder = "user"\n', encoding="utf-8")
|
|
79
|
+
|
|
80
|
+
self.assertEqual(load_central_config(root)["value"]["order"], "custom-user")
|
|
81
|
+
self.assertEqual(load_customization(root, skill)["value"]["order"], "user")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
unittest.main()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import tempfile
|
|
6
|
+
import unittest
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
SCRIPT = Path(__file__).resolve().parents[1] / "resolve_config.py"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ResolveConfigCliTests(unittest.TestCase):
|
|
14
|
+
def test_missing_tomllib_exits_with_actionable_version_error(self):
|
|
15
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
16
|
+
scripts = Path(temp_dir)
|
|
17
|
+
shutil.copy2(SCRIPT, scripts / SCRIPT.name)
|
|
18
|
+
shutil.copy2(SCRIPT.parent / "config_utils.py", scripts / "config_utils.py")
|
|
19
|
+
(scripts / "tomllib.py").write_text(
|
|
20
|
+
'raise ModuleNotFoundError("No module named tomllib", name="tomllib")\n',
|
|
21
|
+
encoding="utf-8",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
result = subprocess.run(
|
|
25
|
+
[sys.executable, str(scripts / SCRIPT.name), "--help"],
|
|
26
|
+
text=True,
|
|
27
|
+
stdout=subprocess.PIPE,
|
|
28
|
+
stderr=subprocess.PIPE,
|
|
29
|
+
check=False,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
self.assertEqual(result.returncode, 3)
|
|
33
|
+
self.assertEqual(
|
|
34
|
+
result.stderr,
|
|
35
|
+
"error: Python 3.11+ is required (stdlib `tomllib` not found).\n",
|
|
36
|
+
)
|
|
37
|
+
self.assertNotIn("Traceback", result.stderr)
|
|
38
|
+
|
|
39
|
+
def test_full_and_repeated_key_output_follow_layer_precedence(self):
|
|
40
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
41
|
+
root = Path(temp_dir)
|
|
42
|
+
custom = root / "_bmad" / "custom"
|
|
43
|
+
custom.mkdir(parents=True)
|
|
44
|
+
(root / "_bmad" / "config.toml").write_text(
|
|
45
|
+
'[core]\nname = "base"\nkeep = "yes"\n', encoding="utf-8"
|
|
46
|
+
)
|
|
47
|
+
(root / "_bmad" / "config.user.toml").write_text(
|
|
48
|
+
'[core]\nname = "base-user"\n', encoding="utf-8"
|
|
49
|
+
)
|
|
50
|
+
(custom / "config.toml").write_text(
|
|
51
|
+
'[core]\nname = "team"\n', encoding="utf-8"
|
|
52
|
+
)
|
|
53
|
+
(custom / "config.user.toml").write_text(
|
|
54
|
+
'[core]\nname = "user"\n', encoding="utf-8"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
full = self._run(root)
|
|
58
|
+
self.assertEqual(full.returncode, 0, msg=full.stderr)
|
|
59
|
+
self.assertEqual(json.loads(full.stdout)["core"], {"name": "user", "keep": "yes"})
|
|
60
|
+
|
|
61
|
+
keyed = self._run(root, "--key", "core.name", "--key", "missing")
|
|
62
|
+
self.assertEqual(keyed.returncode, 0, msg=keyed.stderr)
|
|
63
|
+
self.assertEqual(json.loads(keyed.stdout), {"core.name": "user"})
|
|
64
|
+
|
|
65
|
+
def test_malformed_present_layer_fails(self):
|
|
66
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
67
|
+
root = Path(temp_dir)
|
|
68
|
+
custom = root / "_bmad" / "custom"
|
|
69
|
+
custom.mkdir(parents=True)
|
|
70
|
+
(root / "_bmad" / "config.toml").write_text("[core]\nvalid = true\n", encoding="utf-8")
|
|
71
|
+
(custom / "config.toml").write_text("[broken\n", encoding="utf-8")
|
|
72
|
+
|
|
73
|
+
result = self._run(root)
|
|
74
|
+
self.assertNotEqual(result.returncode, 0)
|
|
75
|
+
self.assertIn("failed to parse", result.stderr)
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _run(root: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
|
79
|
+
return subprocess.run(
|
|
80
|
+
[sys.executable, str(SCRIPT), "--project-root", str(root), *args],
|
|
81
|
+
text=True,
|
|
82
|
+
stdout=subprocess.PIPE,
|
|
83
|
+
stderr=subprocess.PIPE,
|
|
84
|
+
check=False,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
unittest.main()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
import shutil
|
|
3
4
|
import subprocess
|
|
4
5
|
import sys
|
|
5
6
|
import tempfile
|
|
@@ -11,6 +12,31 @@ SCRIPT = Path(__file__).resolve().parents[1] / "resolve_customization.py"
|
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class ResolveCustomizationStdoutTests(unittest.TestCase):
|
|
15
|
+
def test_missing_tomllib_exits_with_actionable_version_error(self):
|
|
16
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
17
|
+
scripts = Path(temp_dir)
|
|
18
|
+
shutil.copy2(SCRIPT, scripts / SCRIPT.name)
|
|
19
|
+
shutil.copy2(SCRIPT.parent / "config_utils.py", scripts / "config_utils.py")
|
|
20
|
+
(scripts / "tomllib.py").write_text(
|
|
21
|
+
'raise ModuleNotFoundError("No module named tomllib", name="tomllib")\n',
|
|
22
|
+
encoding="utf-8",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
result = subprocess.run(
|
|
26
|
+
[sys.executable, str(scripts / SCRIPT.name), "--help"],
|
|
27
|
+
text=True,
|
|
28
|
+
stdout=subprocess.PIPE,
|
|
29
|
+
stderr=subprocess.PIPE,
|
|
30
|
+
check=False,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
self.assertEqual(result.returncode, 3)
|
|
34
|
+
self.assertEqual(
|
|
35
|
+
result.stderr,
|
|
36
|
+
"error: Python 3.11+ is required (stdlib `tomllib` not found).\n",
|
|
37
|
+
)
|
|
38
|
+
self.assertNotIn("Traceback", result.stderr)
|
|
39
|
+
|
|
14
40
|
def test_writes_emoji_json_when_stdout_encoding_is_cp1252(self):
|
|
15
41
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
16
42
|
skill_dir = Path(temp_dir) / "emoji-agent"
|
|
@@ -33,6 +59,7 @@ class ResolveCustomizationStdoutTests(unittest.TestCase):
|
|
|
33
59
|
],
|
|
34
60
|
stdout=subprocess.PIPE,
|
|
35
61
|
stderr=subprocess.PIPE,
|
|
62
|
+
cwd=temp_dir,
|
|
36
63
|
env=env,
|
|
37
64
|
check=False,
|
|
38
65
|
)
|
|
@@ -29,9 +29,13 @@ const CLIUtils = {
|
|
|
29
29
|
|
|
30
30
|
const logoLines = termWidth >= 95 ? logoWide : logoNarrow;
|
|
31
31
|
const logo = logoLines.map((line) => color.blue(line)).join('\n');
|
|
32
|
-
|
|
32
|
+
// The wordmark supplies "BMad Method", so the lines below read as its
|
|
33
|
+
// tagline, then its positioning, then the company credit.
|
|
34
|
+
const tagline = color.white(' Agile Ai Driven Development');
|
|
35
|
+
const slogan = color.dim(' the agile way to do AiDD');
|
|
36
|
+
const credit = color.dim(' Build More, Architect Dreams · © BMad Code');
|
|
33
37
|
|
|
34
|
-
await prompts.box(`${logo}\n${tagline}`, '', {
|
|
38
|
+
await prompts.box(`${logo}\n${tagline}\n${slogan}\n${credit}`, '', {
|
|
35
39
|
contentAlign: 'center',
|
|
36
40
|
rounded: true,
|
|
37
41
|
formatBorder: color.blue,
|
|
@@ -663,8 +663,7 @@ class Installer {
|
|
|
663
663
|
* Excludes dev-only tests and Python caches so they don't ship to users.
|
|
664
664
|
* Wipes the destination first so files removed or renamed in source
|
|
665
665
|
* don't linger and get recorded as installed. Also seeds
|
|
666
|
-
*
|
|
667
|
-
* stay out of version control.
|
|
666
|
+
* gitignore files for personal overrides and generated render snapshots.
|
|
668
667
|
*/
|
|
669
668
|
async _installSharedScripts(paths) {
|
|
670
669
|
const srcScriptsDir = path.join(paths.srcDir, 'src', 'scripts');
|
|
@@ -687,6 +686,14 @@ class Installer {
|
|
|
687
686
|
await fs.writeFile(customGitignore, '*.user.toml\n', 'utf8');
|
|
688
687
|
this.installedFiles.add(customGitignore);
|
|
689
688
|
}
|
|
689
|
+
|
|
690
|
+
const renderDir = path.join(paths.bmadDir, 'render');
|
|
691
|
+
const renderGitignore = path.join(renderDir, '.gitignore');
|
|
692
|
+
if (!(await fs.pathExists(renderGitignore))) {
|
|
693
|
+
await fs.ensureDir(renderDir);
|
|
694
|
+
await fs.writeFile(renderGitignore, '*\n!.gitignore\n', 'utf8');
|
|
695
|
+
}
|
|
696
|
+
this.installedFiles.add(renderGitignore);
|
|
690
697
|
}
|
|
691
698
|
|
|
692
699
|
async _trackFilesRecursive(dir) {
|
|
@@ -870,8 +877,9 @@ class Installer {
|
|
|
870
877
|
const fullPath = path.join(dir, entry.name);
|
|
871
878
|
|
|
872
879
|
if (entry.isDirectory()) {
|
|
873
|
-
|
|
874
|
-
|
|
880
|
+
const relativeDir = path.relative(bmadDir, fullPath);
|
|
881
|
+
// Render snapshots are generated state, not user-authored customization.
|
|
882
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || relativeDir === 'render') {
|
|
875
883
|
continue;
|
|
876
884
|
}
|
|
877
885
|
await scanDirectory(fullPath);
|
|
@@ -959,7 +967,7 @@ class Installer {
|
|
|
959
967
|
|
|
960
968
|
// Get all installed module directories
|
|
961
969
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
962
|
-
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
|
|
970
|
+
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom', 'render']);
|
|
963
971
|
const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
|
|
964
972
|
|
|
965
973
|
// Generate config.yaml for each installed module
|
|
@@ -1056,7 +1064,7 @@ class Installer {
|
|
|
1056
1064
|
|
|
1057
1065
|
// Get all installed module directories
|
|
1058
1066
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
1059
|
-
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
|
|
1067
|
+
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom', 'render']);
|
|
1060
1068
|
const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
|
|
1061
1069
|
|
|
1062
1070
|
// Add core module to scan (it's installed at root level as _config, but we check src/core-skills)
|
|
@@ -1237,9 +1245,23 @@ class Installer {
|
|
|
1237
1245
|
' Get started:',
|
|
1238
1246
|
` 1. Launch your AI agent from your project folder`,
|
|
1239
1247
|
` 2. Not sure what to do? Invoke the ${color.cyan('bmad-help')} skill and ask it what to do!`,
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1248
|
+
);
|
|
1249
|
+
|
|
1250
|
+
// Repeat the uv warning here when it applies. The pre-install probe fires
|
|
1251
|
+
// before every prompt in the run, so by now it is far up the scrollback —
|
|
1252
|
+
// and this box is titled "BMAD is ready to use!", which is only true if
|
|
1253
|
+
// the rendered skills can actually start.
|
|
1254
|
+
const { detectUv } = require('./uv-check');
|
|
1255
|
+
if (!detectUv()) {
|
|
1256
|
+
lines.push(
|
|
1257
|
+
'',
|
|
1258
|
+
` ${color.yellow('⚠ uv is not installed.')} ${color.cyan('bmad-build')} and ${color.cyan('bmad-build-auto')} render through`,
|
|
1259
|
+
` ${color.cyan('uv run')} and will halt on activation until you set it up — ask your agent to`,
|
|
1260
|
+
` "install and set up uv for me", or see https://docs.astral.sh/uv/`,
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
lines.push(
|
|
1243
1265
|
'',
|
|
1244
1266
|
` Blog, Docs and Guides: ${color.blue('https://bmadcode.com/')}`,
|
|
1245
1267
|
` Community: ${color.blue('https://discord.gg/gk8jAdXWmj')}`,
|
|
@@ -753,7 +753,7 @@ class ManifestGenerator {
|
|
|
753
753
|
|
|
754
754
|
for (const entry of entries) {
|
|
755
755
|
// Skip if not a directory or is a special directory
|
|
756
|
-
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === '_config') {
|
|
756
|
+
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === '_config' || entry.name === 'render') {
|
|
757
757
|
continue;
|
|
758
758
|
}
|
|
759
759
|
|