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.
Files changed (352) hide show
  1. package/.claude-plugin/marketplace.json +30 -57
  2. package/README.md +49 -82
  3. package/README_CN.md +10 -10
  4. package/README_VN.md +11 -11
  5. package/bmad-modules.yaml +43 -39
  6. package/package.json +7 -3
  7. package/removals.txt +17 -0
  8. package/src/bmm-skills/{1-analysis → agents}/bmad-agent-analyst/SKILL.md +1 -1
  9. package/src/bmm-skills/{1-analysis → agents}/bmad-agent-analyst/customize.toml +22 -7
  10. package/src/bmm-skills/{3-solutioning → agents}/bmad-agent-architect/SKILL.md +1 -1
  11. package/src/bmm-skills/{3-solutioning → agents}/bmad-agent-architect/customize.toml +2 -2
  12. package/src/bmm-skills/{4-implementation → agents}/bmad-agent-dev/SKILL.md +1 -1
  13. package/src/bmm-skills/{4-implementation → agents}/bmad-agent-dev/customize.toml +7 -14
  14. package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-pm/SKILL.md +1 -1
  15. package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-pm/customize.toml +2 -2
  16. package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-ux-designer/SKILL.md +1 -1
  17. package/src/bmm-skills/module-help.csv +14 -26
  18. package/src/bmm-skills/module.yaml +5 -15
  19. package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/SKILL.md +3 -3
  20. package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/customize.toml +4 -2
  21. package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/SKILL.md +1 -1
  22. package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
  23. package/src/bmm-skills/plan/bmad-generate-project-context/SKILL.md +10 -0
  24. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/SKILL.md +3 -1
  25. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/prd-template.md +1 -1
  26. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/customize.toml +5 -3
  27. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/references/validate.md +3 -3
  28. package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/SKILL.md +1 -1
  29. package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/bmad-manifest.json +1 -1
  30. package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/verdict.md +1 -1
  31. package/src/bmm-skills/{1-analysis → plan}/bmad-product-brief/SKILL.md +1 -1
  32. package/src/bmm-skills/{1-analysis → plan}/bmad-product-brief/customize.toml +5 -3
  33. package/src/bmm-skills/plan/bmad-project-context/SKILL.md +110 -0
  34. package/src/bmm-skills/plan/bmad-project-context/customize.toml +24 -0
  35. package/src/bmm-skills/plan/bmad-project-context/references/best-practices.md +65 -0
  36. package/src/bmm-skills/plan/bmad-project-context/references/template.md +55 -0
  37. package/src/{core-skills → bmm-skills/plan}/bmad-spec/SKILL.md +18 -3
  38. package/src/{core-skills → bmm-skills/plan}/bmad-spec/assets/spec-template.md +1 -1
  39. package/src/bmm-skills/plan/bmad-spec/assets/stories-schema.md +44 -0
  40. package/src/{core-skills → bmm-skills/plan}/bmad-spec/customize.toml +3 -4
  41. package/src/bmm-skills/plan/bmad-sprint-planning/SKILL.md +62 -0
  42. package/src/bmm-skills/plan/bmad-sprint-planning/references/fix-sprint-status.md +30 -0
  43. package/src/bmm-skills/plan/bmad-sprint-planning/references/generate-tracking.md +25 -0
  44. package/src/bmm-skills/plan/bmad-sprint-planning/references/readiness-gate.md +20 -0
  45. package/src/bmm-skills/plan/bmad-sprint-planning/references/status-view.md +14 -0
  46. package/src/bmm-skills/plan/bmad-sprint-planning/references/validate.md +10 -0
  47. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
  48. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/sprint_plan.py +697 -0
  49. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
  50. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
  51. package/src/bmm-skills/{4-implementation → plan}/bmad-sprint-planning/sprint-status-template.yaml +9 -7
  52. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/SKILL.md +1 -1
  53. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/color-themes.md +1 -1
  54. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/customize.toml +4 -2
  55. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/creative-tools.md +1 -1
  56. package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/validate.md +1 -1
  57. package/src/bmm-skills/ship/bmad-build/SKILL.md +13 -0
  58. package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/compile-epic-context.md +1 -1
  59. package/src/bmm-skills/ship/bmad-build/customize.toml +164 -0
  60. package/src/bmm-skills/ship/bmad-build/references/deletion-check.md +14 -0
  61. package/src/bmm-skills/ship/bmad-build/review-prompts/edge-case-hunter.md +88 -0
  62. package/src/bmm-skills/ship/bmad-build/review-prompts/verification-gap.md +113 -0
  63. package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-01-clarify-and-route.md +20 -19
  64. package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-02-plan.md +5 -9
  65. package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-03-implement.md +10 -6
  66. package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-04-review.md +9 -15
  67. package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/step-05-present.md +10 -13
  68. package/src/bmm-skills/ship/bmad-build/step-oneshot.md +77 -0
  69. package/src/bmm-skills/ship/bmad-build/sync-sprint-status.md +19 -0
  70. package/src/bmm-skills/ship/bmad-build/workflow.md +84 -0
  71. package/src/bmm-skills/ship/bmad-build-auto/SKILL.md +13 -0
  72. package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/compile-epic-context.md +1 -1
  73. package/src/bmm-skills/ship/bmad-build-auto/customize.toml +121 -0
  74. 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
  75. package/src/{core-skills/bmad-review-verification-gap/SKILL.md → bmm-skills/ship/bmad-build-auto/review-prompts/verification-gap.md} +15 -8
  76. package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/spec-template.md +2 -1
  77. package/src/bmm-skills/ship/bmad-build-auto/step-01-clarify-and-route.md +83 -0
  78. package/src/bmm-skills/ship/bmad-build-auto/step-02-plan.md +27 -0
  79. package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/step-03-implement.md +6 -4
  80. package/src/bmm-skills/{4-implementation/bmad-dev-auto → ship/bmad-build-auto}/step-04-review.md +26 -25
  81. package/src/bmm-skills/ship/bmad-build-auto/workflow.md +104 -0
  82. package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/SKILL.md +1 -1
  83. package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-05-wrapup.md +1 -1
  84. package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/SKILL.md +1 -1
  85. package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/customize.toml +37 -17
  86. package/src/bmm-skills/ship/bmad-code-review/references/deletion-check.md +14 -0
  87. package/src/bmm-skills/ship/bmad-code-review/review-prompts/edge-case-hunter.md +88 -0
  88. package/src/bmm-skills/ship/bmad-code-review/review-prompts/verification-gap.md +113 -0
  89. package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-01-gather-context.md +7 -4
  90. package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-02-review.md +1 -1
  91. package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-04-present.md +1 -1
  92. package/src/bmm-skills/{4-implementation → ship}/bmad-correct-course/SKILL.md +7 -8
  93. package/src/bmm-skills/{4-implementation → ship}/bmad-qa-generate-e2e-tests/SKILL.md +2 -2
  94. package/src/bmm-skills/ship/bmad-retrospective/SKILL.md +94 -0
  95. package/src/bmm-skills/{4-implementation → ship}/bmad-retrospective/customize.toml +2 -2
  96. package/src/bmm-skills/ship/bmad-retrospective/references/acceptance-verdict.md +55 -0
  97. package/src/bmm-skills/ship/bmad-retrospective/references/aggregate-views.md +17 -0
  98. package/src/bmm-skills/ship/bmad-retrospective/references/evidence-gathering.md +30 -0
  99. package/src/bmm-skills/ship/bmad-retrospective/references/retro-document.md +84 -0
  100. package/src/bmm-skills/ship/bmad-retrospective/references/team-discussion.md +22 -0
  101. package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
  102. package/src/bmm-skills/ship/bmad-retrospective/scripts/git_evidence.py +304 -0
  103. package/src/bmm-skills/ship/bmad-retrospective/scripts/sprint_status.py +746 -0
  104. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
  105. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
  106. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
  107. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_git_evidence.py +750 -0
  108. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_sprint_status.py +1579 -0
  109. package/src/bmm-skills/v6-shims/README.md +28 -0
  110. package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/SKILL.md +2 -2
  111. package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/SKILL.md +4 -4
  112. package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/SKILL.md +5 -3
  113. package/src/bmm-skills/v6-shims/bmad-dev-auto/SKILL.md +19 -0
  114. package/src/bmm-skills/{4-implementation → v6-shims}/bmad-dev-story/SKILL.md +5 -3
  115. package/src/bmm-skills/{4-implementation → v6-shims}/bmad-dev-story/customize.toml +3 -0
  116. package/src/bmm-skills/v6-shims/bmad-document-project/SKILL.md +14 -0
  117. package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +14 -0
  118. package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/SKILL.md +4 -4
  119. package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +14 -0
  120. package/src/bmm-skills/v6-shims/bmad-quick-dev/SKILL.md +19 -0
  121. package/src/bmm-skills/v6-shims/bmad-sprint-status/SKILL.md +26 -0
  122. package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +14 -0
  123. package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/SKILL.md +4 -4
  124. package/src/core-skills/bmad-advanced-elicitation/SKILL.md +26 -103
  125. package/src/core-skills/bmad-advanced-elicitation/customize.toml +54 -0
  126. package/src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
  127. package/src/core-skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
  128. package/src/core-skills/bmad-brainstorming/SKILL.md +3 -3
  129. package/src/core-skills/bmad-brainstorming/assets/brain-selector.html +2 -0
  130. package/src/core-skills/bmad-brainstorming/references/mode-autonomous.md +1 -1
  131. package/src/core-skills/bmad-brainstorming/scripts/brain.py +36 -6
  132. package/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +22 -0
  133. package/src/core-skills/bmad-customize/SKILL.md +2 -2
  134. package/src/core-skills/bmad-deep-recon/SKILL.md +82 -0
  135. package/src/core-skills/bmad-deep-recon/assets/research.template.md +18 -0
  136. package/src/core-skills/bmad-deep-recon/customize.toml +212 -0
  137. package/src/core-skills/bmad-deep-recon/references/draft.md +8 -0
  138. package/src/core-skills/bmad-deep-recon/references/finalize.md +11 -0
  139. package/src/core-skills/bmad-deep-recon/references/html-briefing.md +16 -0
  140. package/src/core-skills/bmad-deep-recon/references/lifecycle.md +11 -0
  141. package/src/core-skills/bmad-deep-recon/references/process.md +10 -0
  142. package/src/core-skills/bmad-deep-recon/references/run.md +73 -0
  143. package/src/core-skills/bmad-deep-recon/references/selection.md +13 -0
  144. package/src/core-skills/bmad-deep-recon/references/synthesis.md +16 -0
  145. package/src/core-skills/bmad-deep-recon/references/verification.md +29 -0
  146. package/src/core-skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
  147. package/src/core-skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
  148. package/src/core-skills/bmad-deep-recon/types/academic-lit.md +19 -0
  149. package/src/core-skills/bmad-deep-recon/types/competitive.md +19 -0
  150. package/src/core-skills/bmad-deep-recon/types/domain.md +19 -0
  151. package/src/core-skills/bmad-deep-recon/types/market.md +19 -0
  152. package/src/core-skills/bmad-deep-recon/types/technical.md +19 -0
  153. package/src/core-skills/bmad-deep-recon/types/user-voice.md +19 -0
  154. package/src/core-skills/bmad-forge-idea/SKILL.md +2 -2
  155. package/src/core-skills/bmad-forge-idea/scripts/resolve_personas.py +7 -2
  156. package/src/core-skills/bmad-help/SKILL.md +3 -3
  157. package/src/core-skills/bmad-party-mode/SKILL.md +2 -2
  158. package/src/core-skills/bmad-party-mode/customize.toml +1 -1
  159. package/src/core-skills/bmad-party-mode/scripts/resolve_party.py +14 -4
  160. package/src/core-skills/bmad-review/SKILL.md +49 -0
  161. package/src/core-skills/bmad-review/customize.toml +141 -0
  162. package/src/core-skills/bmad-review/references/editorial-common.md +56 -0
  163. package/src/core-skills/bmad-review/references/lens-adversarial.md +19 -0
  164. package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +54 -0
  165. package/src/core-skills/bmad-review/references/lens-prose.md +7 -0
  166. package/src/core-skills/bmad-review/references/lens-structure.md +9 -0
  167. package/src/core-skills/bmad-review/references/lens-verification-gap.md +92 -0
  168. package/src/core-skills/bmad-review/references/structure-models.md +44 -0
  169. package/src/core-skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
  170. package/src/core-skills/bmad-review/scripts/word_metrics.py +102 -0
  171. package/src/core-skills/module-help.csv +4 -8
  172. package/src/core-skills/module.yaml +5 -0
  173. package/src/core-skills/v6-shims/README.md +25 -0
  174. package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +6 -0
  175. package/src/core-skills/v6-shims/bmad-editorial-review/customize.toml +31 -0
  176. package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +6 -0
  177. package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +6 -0
  178. package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +6 -0
  179. package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +6 -0
  180. package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +6 -0
  181. package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
  182. package/src/scripts/config_utils.py +119 -0
  183. package/src/scripts/render_skill.py +401 -0
  184. package/src/scripts/resolve_config.py +32 -136
  185. package/src/scripts/resolve_customization.py +43 -184
  186. package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
  187. package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
  188. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
  189. package/src/scripts/tests/test_config_utils.py +85 -0
  190. package/src/scripts/tests/test_resolve_config.py +89 -0
  191. package/src/scripts/tests/test_resolve_customization.py +27 -0
  192. package/tools/installer/cli-utils.js +6 -2
  193. package/tools/installer/core/installer.js +31 -9
  194. package/tools/installer/core/manifest-generator.js +1 -1
  195. package/tools/installer/core/uv-check.js +122 -24
  196. package/tools/installer/ide/_config-driven.js +1 -1
  197. package/tools/installer/ide/platform-codes.yaml +7 -0
  198. package/tools/installer/ide/shared/path-utils.js +2 -2
  199. package/tools/installer/install-messages.yaml +3 -2
  200. package/tools/installer/modules/custom-module-manager.js +12 -6
  201. package/tools/installer/modules/external-manager.js +12 -8
  202. package/tools/installer/modules/git-env.js +47 -0
  203. package/tools/installer/modules/official-modules.js +1 -1
  204. package/tools/installer/prompts.js +41 -102
  205. package/tools/installer/ui.js +99 -22
  206. package/tools/skill-validator.md +11 -1
  207. package/tools/validate-published-implementation-model.mjs +68 -0
  208. package/tools/validate-skills.js +33 -0
  209. package/web-bundles/prd-coach/prd-template.md +1 -1
  210. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +0 -76
  211. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/customize.toml +0 -81
  212. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/explain-concept.md +0 -20
  213. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/mermaid-gen.md +0 -20
  214. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/validate-doc.md +0 -19
  215. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/write-document.md +0 -20
  216. package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +0 -62
  217. package/src/bmm-skills/1-analysis/bmad-document-project/checklist.md +0 -245
  218. package/src/bmm-skills/1-analysis/bmad-document-project/customize.toml +0 -41
  219. package/src/bmm-skills/1-analysis/bmad-document-project/documentation-requirements.csv +0 -12
  220. package/src/bmm-skills/1-analysis/bmad-document-project/instructions.md +0 -128
  221. package/src/bmm-skills/1-analysis/bmad-document-project/templates/deep-dive-template.md +0 -345
  222. package/src/bmm-skills/1-analysis/bmad-document-project/templates/index-template.md +0 -169
  223. package/src/bmm-skills/1-analysis/bmad-document-project/templates/project-overview-template.md +0 -103
  224. package/src/bmm-skills/1-analysis/bmad-document-project/templates/project-scan-report-schema.json +0 -160
  225. package/src/bmm-skills/1-analysis/bmad-document-project/templates/source-tree-template.md +0 -135
  226. package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-instructions.md +0 -300
  227. package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-workflow.md +0 -34
  228. package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-instructions.md +0 -1108
  229. package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-workflow.md +0 -34
  230. package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +0 -96
  231. package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +0 -41
  232. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-01-init.md +0 -137
  233. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-02-domain-analysis.md +0 -229
  234. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-03-competitive-landscape.md +0 -238
  235. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-04-regulatory-focus.md +0 -206
  236. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-05-technical-trends.md +0 -234
  237. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +0 -450
  238. package/src/bmm-skills/1-analysis/research/bmad-domain-research/research.template.md +0 -29
  239. package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +0 -96
  240. package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +0 -41
  241. package/src/bmm-skills/1-analysis/research/bmad-market-research/research.template.md +0 -29
  242. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-01-init.md +0 -184
  243. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-02-customer-behavior.md +0 -239
  244. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-03-customer-pain-points.md +0 -251
  245. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-04-customer-decisions.md +0 -261
  246. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-05-competitive-analysis.md +0 -173
  247. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +0 -484
  248. package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +0 -96
  249. package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +0 -41
  250. package/src/bmm-skills/1-analysis/research/bmad-technical-research/research.template.md +0 -29
  251. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-01-init.md +0 -137
  252. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-02-technical-overview.md +0 -239
  253. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-03-integration-patterns.md +0 -248
  254. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-04-architectural-patterns.md +0 -202
  255. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-05-implementation-research.md +0 -233
  256. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +0 -493
  257. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +0 -91
  258. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/customize.toml +0 -41
  259. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +0 -179
  260. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +0 -168
  261. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +0 -169
  262. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-04-ux-alignment.md +0 -129
  263. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-05-epic-quality-review.md +0 -241
  264. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +0 -132
  265. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/templates/readiness-report-template.md +0 -4
  266. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +0 -81
  267. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/customize.toml +0 -41
  268. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/project-context-template.md +0 -21
  269. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-01-discover.md +0 -186
  270. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-02-generate.md +0 -321
  271. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-03-complete.md +0 -284
  272. package/src/bmm-skills/4-implementation/bmad-dev-auto/SKILL.md +0 -103
  273. package/src/bmm-skills/4-implementation/bmad-dev-auto/customize.toml +0 -108
  274. package/src/bmm-skills/4-implementation/bmad-dev-auto/step-01-clarify-and-route.md +0 -66
  275. package/src/bmm-skills/4-implementation/bmad-dev-auto/step-02-plan.md +0 -31
  276. package/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +0 -115
  277. package/src/bmm-skills/4-implementation/bmad-quick-dev/customize.toml +0 -83
  278. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +0 -82
  279. package/src/bmm-skills/4-implementation/bmad-quick-dev/sync-sprint-status.md +0 -19
  280. package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +0 -1527
  281. package/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md +0 -319
  282. package/src/bmm-skills/4-implementation/bmad-sprint-planning/checklist.md +0 -34
  283. package/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md +0 -311
  284. package/src/core-skills/bmad-brainstorming/analysis/catalog-analysis.md +0 -239
  285. package/src/core-skills/bmad-brainstorming/analysis/method-matrix.csv +0 -109
  286. package/src/core-skills/bmad-editorial-review-prose/SKILL.md +0 -86
  287. package/src/core-skills/bmad-editorial-review-structure/SKILL.md +0 -179
  288. package/src/core-skills/bmad-index-docs/SKILL.md +0 -66
  289. package/src/core-skills/bmad-review-adversarial-general/SKILL.md +0 -37
  290. package/src/core-skills/bmad-shard-doc/SKILL.md +0 -105
  291. /package/src/bmm-skills/{2-plan-workflows → agents}/bmad-agent-ux-designer/customize.toml +0 -0
  292. /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/assets/spine-template.md +0 -0
  293. /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/references/headless.md +0 -0
  294. /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/references/reviewer-gate.md +0 -0
  295. /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/scripts/lint_spine.py +0 -0
  296. /package/src/bmm-skills/{3-solutioning → plan}/bmad-architecture/scripts/tests/test_lint_spine.py +0 -0
  297. /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/customize.toml +0 -0
  298. /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +0 -0
  299. /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-02-design-epics.md +0 -0
  300. /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/steps/step-03-create-stories.md +0 -0
  301. /package/src/bmm-skills/{3-solutioning → plan}/bmad-create-epics-and-stories/templates/epics-template.md +0 -0
  302. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/headless-schemas.md +0 -0
  303. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/prd-validation-checklist.md +0 -0
  304. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/assets/validation-report-template.html +0 -0
  305. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-prd/references/headless.md +0 -0
  306. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/agents/artifact-analyzer.md +0 -0
  307. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/agents/web-researcher.md +0 -0
  308. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/assets/prfaq-template.md +0 -0
  309. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/customize.toml +0 -0
  310. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/customer-faq.md +0 -0
  311. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/internal-faq.md +0 -0
  312. /package/src/bmm-skills/{1-analysis → plan}/bmad-prfaq/references/press-release.md +0 -0
  313. /package/src/bmm-skills/{1-analysis → plan}/bmad-product-brief/assets/brief-template.md +0 -0
  314. /package/src/{core-skills → bmm-skills/plan}/bmad-spec/assets/headless-schemas.md +0 -0
  315. /package/src/bmm-skills/{4-implementation → plan}/bmad-sprint-planning/customize.toml +0 -0
  316. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-directions.md +0 -0
  317. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-example-editorial.md +0 -0
  318. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-example-mobile.md +0 -0
  319. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/design-example-shadcn.md +0 -0
  320. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/excalidraw-wireframe.md +0 -0
  321. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/experience-example-mobile.md +0 -0
  322. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/experience-example-shadcn.md +0 -0
  323. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/headless-schemas.md +0 -0
  324. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/key-screens.md +0 -0
  325. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/assets/validation-report-template.html +0 -0
  326. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/design-md-spec.md +0 -0
  327. /package/src/bmm-skills/{2-plan-workflows → plan}/bmad-ux/references/headless.md +0 -0
  328. /package/src/bmm-skills/{4-implementation/bmad-quick-dev → ship/bmad-build}/spec-template.md +0 -0
  329. /package/src/{core-skills/bmad-review-edge-case-hunter → bmm-skills/ship/bmad-build-auto}/references/deletion-check.md +0 -0
  330. /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/customize.toml +0 -0
  331. /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/generate-trail.md +0 -0
  332. /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-01-orientation.md +0 -0
  333. /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-02-walkthrough.md +0 -0
  334. /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-03-detail-pass.md +0 -0
  335. /package/src/bmm-skills/{4-implementation → ship}/bmad-checkpoint-preview/step-04-testing.md +0 -0
  336. /package/src/bmm-skills/{4-implementation → ship}/bmad-code-review/steps/step-03-triage.md +0 -0
  337. /package/src/bmm-skills/{4-implementation → ship}/bmad-correct-course/checklist.md +0 -0
  338. /package/src/bmm-skills/{4-implementation → ship}/bmad-correct-course/customize.toml +0 -0
  339. /package/src/bmm-skills/{4-implementation → ship}/bmad-qa-generate-e2e-tests/checklist.md +0 -0
  340. /package/src/bmm-skills/{4-implementation → ship}/bmad-qa-generate-e2e-tests/customize.toml +0 -0
  341. /package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/customize.toml +0 -0
  342. /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/customize.toml +0 -0
  343. /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/checklist.md +0 -0
  344. /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/customize.toml +0 -0
  345. /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/discover-inputs.md +0 -0
  346. /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-create-story/template.md +0 -0
  347. /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-dev-story/checklist.md +0 -0
  348. /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/customize.toml +0 -0
  349. /package/src/bmm-skills/{4-implementation → v6-shims}/bmad-sprint-status/customize.toml +0 -0
  350. /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/customize.toml +0 -0
  351. /package/src/core-skills/bmad-advanced-elicitation/{methods.csv → assets/methods.csv} +0 -0
  352. /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
- Resolve customization for a BMad skill using three-layer TOML merge.
4
-
5
- Reads customization from three layers (highest priority first):
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 tomllib
45
- except ImportError:
46
- sys.stderr.write(
47
- "error: Python 3.11+ is required (stdlib `tomllib` not found).\n"
48
- "Install a newer Python or run the resolution manually per the\n"
49
- "fallback instructions in the skill's SKILL.md.\n"
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 = current.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 parts:
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 for a BMad skill using three-layer TOML merge.",
193
- add_help=True,
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
- "--skill", "-s", required=True,
197
- help="Absolute path to the skill directory (must contain customize.toml)",
62
+ "--project-root",
63
+ "-p",
64
+ help="Explicit project root containing _bmad/ (recommended)",
198
65
  )
199
66
  parser.add_argument(
200
- "--key", "-k", action="append", default=[],
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
- skill_name = skill_dir.name
207
- defaults_path = skill_dir / "customize.toml"
208
-
209
- defaults = load_toml(defaults_path, required=True)
210
-
211
- # Prefer the project that contains this skill. Only fall back to cwd if
212
- # the skill isn't inside a recognizable project tree (unusual but possible
213
- # for standalone skills invoked directly). Using cwd first is unsafe when
214
- # an ancestor of cwd happens to have a stray _bmad/ from another project.
215
- project_root = find_project_root(skill_dir) or find_project_root(Path.cwd())
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())
@@ -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
- const tagline = color.white(' Build More, Architect Dreams\n © BMad Code');
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
- * _bmad/custom/.gitignore on fresh installs so *.user.toml overrides
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
- // Skip certain directories
874
- if (entry.name === 'node_modules' || entry.name === '.git') {
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
- ` ${color.cyan('Tip:')} BMAD workflows increasingly run Python scripts via ${color.cyan('uv run')} — uv is`,
1242
- ` becoming the de facto standard. If you don't have it yet, ask your agent to set it up.`,
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