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
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # ///
5
+ """Render a skill's Markdown sources into an immutable project snapshot."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import hashlib
11
+ import json
12
+ import os
13
+ import re
14
+ import shutil
15
+ import sys
16
+ import tempfile
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ # Installed scripts are consumer files, not a location for interpreter caches.
21
+ sys.dont_write_bytecode = True
22
+
23
+ from config_utils import ConfigError, load_central_config, load_customization, load_toml
24
+
25
+
26
+ class RenderError(ValueError):
27
+ """Raised when rendering cannot safely publish a snapshot."""
28
+
29
+
30
+ _CONFIG_TOKEN = re.compile(r"\{\{config\.([A-Za-z0-9_.-]+)\}\}")
31
+ _SHORT_CONFIG_TOKEN = re.compile(r"\{\{\.([A-Za-z0-9_]+)\}\}")
32
+ _CUSTOM_TOKEN = re.compile(r"\{workflow\.([A-Za-z0-9_.-]+)\}")
33
+ _SNAPSHOT_TOKEN = re.compile(r"\[\[bmad-snapshot:([A-Za-z0-9_./-]+\.md)\]\]")
34
+
35
+
36
+ def _hash_bytes(content: bytes) -> str:
37
+ return hashlib.sha256(content).hexdigest()
38
+
39
+
40
+ def _canonical_json(value: Any) -> bytes:
41
+ return json.dumps(
42
+ value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
43
+ ).encode("utf-8")
44
+
45
+
46
+ def _lookup(data: dict[str, Any], dotted_path: str, label: str) -> Any:
47
+ current: Any = data
48
+ for part in dotted_path.split("."):
49
+ if not isinstance(current, dict) or part not in current:
50
+ raise RenderError(f"missing {label} `{dotted_path}`")
51
+ current = current[part]
52
+ return current
53
+
54
+
55
+ def _require_string(value: Any, label: str, *, allow_empty: bool = False) -> str:
56
+ if not isinstance(value, str):
57
+ raise RenderError(f"{label} must be a string, got {type(value).__name__}")
58
+ if not allow_empty and not value.strip():
59
+ raise RenderError(f"{label} must not be empty")
60
+ return value
61
+
62
+
63
+ def _require_string_list(value: Any, label: str) -> list[str]:
64
+ if not isinstance(value, list):
65
+ raise RenderError(f"{label} must be a list, got {type(value).__name__}")
66
+ result = []
67
+ for index, item in enumerate(value):
68
+ result.append(_require_string(item, f"{label}[{index}]"))
69
+ return result
70
+
71
+
72
+ def _require_review_layers(value: Any, label: str) -> list[dict[str, str]]:
73
+ if not isinstance(value, list):
74
+ raise RenderError(f"{label} must be a list of tables")
75
+ result: list[dict[str, str]] = []
76
+ seen: set[str] = set()
77
+ for index, item in enumerate(value):
78
+ item_label = f"{label}[{index}]"
79
+ if not isinstance(item, dict):
80
+ raise RenderError(f"{item_label} must be a table")
81
+ identifier = _require_string(item.get("id"), f"{item_label}.id")
82
+ if identifier in seen:
83
+ raise RenderError(f"duplicate review layer id `{identifier}`")
84
+ seen.add(identifier)
85
+ layer = {
86
+ "id": identifier,
87
+ "name": _require_string(item.get("name", identifier), f"{item_label}.name"),
88
+ "instruction": _require_string(
89
+ item.get("instruction"), f"{item_label}.instruction", allow_empty=True
90
+ ),
91
+ }
92
+ if "when" in item:
93
+ layer["when"] = _require_string(item["when"], f"{item_label}.when")
94
+ result.append(layer)
95
+ return result
96
+
97
+
98
+ def _load_sources(skill_dir: Path) -> dict[str, str]:
99
+ sources: dict[str, str] = {}
100
+ for candidate in sorted(skill_dir.rglob("*.md")):
101
+ if candidate.name == "SKILL.md":
102
+ continue
103
+ name = candidate.relative_to(skill_dir).as_posix()
104
+ path = candidate.resolve(strict=True)
105
+ if not path.is_relative_to(skill_dir):
106
+ raise RenderError(f"render source escapes skill directory: {name}")
107
+ if not path.is_file():
108
+ raise RenderError(f"render source is missing or not a file: {path}")
109
+ try:
110
+ sources[name] = path.read_text(encoding="utf-8")
111
+ except (OSError, UnicodeError) as error:
112
+ raise RenderError(f"failed to read render source {path}: {error}") from error
113
+ if "workflow.md" not in sources:
114
+ raise RenderError(f"render entry is missing: {skill_dir / 'workflow.md'}")
115
+ return sources
116
+
117
+
118
+ def _resolve_config_value(value: Any, label: str, project_root: Path) -> str:
119
+ text = _require_string(value, label)
120
+ if "{project-root}" not in text:
121
+ return text
122
+ resolved = text.replace("{project-root}", str(project_root))
123
+ if not Path(resolved).is_absolute():
124
+ raise RenderError(f"{label} must resolve to an absolute path: {resolved}")
125
+ return resolved
126
+
127
+
128
+ def _find_config_values(data: Any, key: str, prefix: str = "") -> list[tuple[str, Any]]:
129
+ matches: list[tuple[str, Any]] = []
130
+ if not isinstance(data, dict):
131
+ return matches
132
+ for name, value in data.items():
133
+ path = f"{prefix}.{name}" if prefix else name
134
+ if name == key and not isinstance(value, (dict, list)):
135
+ matches.append((path, value))
136
+ matches.extend(_find_config_values(value, key, path))
137
+ return matches
138
+
139
+
140
+ def _resolve_short_config(
141
+ central: dict[str, Any], key: str, project_root: Path
142
+ ) -> tuple[str, str]:
143
+ matches = _find_config_values(central, key)
144
+ if not matches:
145
+ raise RenderError(f"missing config value `{key}`")
146
+ if len(matches) > 1:
147
+ paths = ", ".join(path for path, _ in matches)
148
+ raise RenderError(f"ambiguous config value `{key}` found at: {paths}")
149
+ path, value = matches[0]
150
+ return path, _resolve_config_value(value, f"config.{path}", project_root)
151
+
152
+
153
+ def _format_markdown_list(items: list[str]) -> str:
154
+ if not items:
155
+ return "_None._"
156
+ rendered = []
157
+ for item in items:
158
+ lines = item.splitlines() or [""]
159
+ rendered.append("- " + lines[0])
160
+ rendered.extend(" " + line for line in lines[1:])
161
+ return "\n".join(rendered)
162
+
163
+
164
+ def _format_review_layers(layers: list[dict[str, str]]) -> str:
165
+ active = [layer for layer in layers if layer["instruction"].strip()]
166
+ if not active:
167
+ return "No active review layers. HALT with blocking condition `no active review layers`."
168
+ sections = []
169
+ for layer in active:
170
+ section = [f"#### {layer['name']} (`{layer['id']}`)"]
171
+ if layer.get("when"):
172
+ section.extend(["", f"Run only when: {layer['when']}"])
173
+ section.extend(["", layer["instruction"].strip()])
174
+ sections.append("\n".join(section))
175
+ return "\n\n".join(sections)
176
+
177
+
178
+ def _resolve_customization_value(value: Any, default: Any, label: str) -> tuple[Any, str]:
179
+ if isinstance(default, str):
180
+ allow_empty = not default.strip() or label == "customization.workflow.open_spec"
181
+ resolved = _require_string(value, label, allow_empty=allow_empty)
182
+ return resolved, resolved
183
+ if isinstance(default, list):
184
+ if default and all(isinstance(item, dict) for item in default):
185
+ resolved = _require_review_layers(value, label)
186
+ return resolved, _format_review_layers(resolved)
187
+ resolved = _require_string_list(value, label)
188
+ return resolved, _format_markdown_list(resolved)
189
+ raise RenderError(f"{label} has unsupported default type {type(default).__name__}")
190
+
191
+
192
+ def _resolve_replacements(
193
+ sources: dict[str, str],
194
+ central: dict[str, Any],
195
+ customization: dict[str, Any],
196
+ defaults: dict[str, Any] | None,
197
+ project_root: Path,
198
+ ) -> tuple[dict[str, str], dict[str, Any]]:
199
+ replacements: dict[str, str] = {}
200
+ input_values: dict[str, Any] = {}
201
+ for content in sources.values():
202
+ for match in _SHORT_CONFIG_TOKEN.finditer(content):
203
+ token, key = match.group(0), match.group(1)
204
+ path, resolved = _resolve_short_config(central, key, project_root)
205
+ source = f"config.{path}"
206
+ replacements[token] = resolved
207
+ input_values[source] = resolved
208
+ for match in _CONFIG_TOKEN.finditer(content):
209
+ token, path = match.group(0), match.group(1)
210
+ source = f"config.{path}"
211
+ resolved = _resolve_config_value(
212
+ _lookup(central, path, "config value"), source, project_root
213
+ )
214
+ replacements[token] = resolved
215
+ input_values[source] = resolved
216
+ for match in _CUSTOM_TOKEN.finditer(content):
217
+ if defaults is None:
218
+ raise RenderError("customization tokens require customize.toml")
219
+ token, relative_path = match.group(0), match.group(1)
220
+ path = f"workflow.{relative_path}"
221
+ source = f"customization.{path}"
222
+ resolved, rendered = _resolve_customization_value(
223
+ _lookup(customization, path, "customization value"),
224
+ _lookup(defaults, path, "customization default"),
225
+ source,
226
+ )
227
+ replacements[token] = rendered
228
+ input_values[source] = resolved
229
+ return replacements, input_values
230
+
231
+
232
+ def _render_sources(
233
+ sources: dict[str, str], replacements: dict[str, str], destination: Path
234
+ ) -> dict[str, str]:
235
+ """Resolve only tokens authored in installed sources in one opaque pass."""
236
+ # Workflow customization may reference installed skill files; bind those
237
+ # references to the immutable generation before inserting the prose.
238
+ replacements = {
239
+ token: value.replace("{skill-root}", str(destination))
240
+ if token.startswith("{workflow.")
241
+ else value
242
+ for token, value in replacements.items()
243
+ }
244
+ source_names = set(sources)
245
+ patterns = [
246
+ *(re.escape(token) for token in sorted(replacements, key=len, reverse=True)),
247
+ _SNAPSHOT_TOKEN.pattern,
248
+ ]
249
+ token_pattern = re.compile("|".join(patterns))
250
+
251
+ def replace(match: re.Match[str]) -> str:
252
+ token = match.group(0)
253
+ if token in replacements:
254
+ return replacements[token]
255
+ snapshot = _SNAPSHOT_TOKEN.fullmatch(token)
256
+ if snapshot is None:
257
+ raise RenderError(f"unsupported render token: {token}")
258
+ target = snapshot.group(1)
259
+ if target not in source_names:
260
+ raise RenderError(f"snapshot reference targets undeclared source: {target}")
261
+ return str(destination / target)
262
+
263
+ rendered: dict[str, str] = {}
264
+ for name, content in sources.items():
265
+ # Inserted paths and customization prose are never scanned as source tokens.
266
+ rendered[name] = token_pattern.sub(replace, content)
267
+ return rendered
268
+
269
+
270
+ def _verify_existing(destination: Path, manifest: dict[str, Any]) -> None:
271
+ manifest_path = destination / "manifest.json"
272
+ try:
273
+ existing = json.loads(manifest_path.read_text(encoding="utf-8"))
274
+ except (OSError, UnicodeError, json.JSONDecodeError) as error:
275
+ raise RenderError(f"corrupt existing generation {destination}: {error}") from error
276
+ if existing != manifest:
277
+ raise RenderError(f"generation collision or corruption at {destination}")
278
+ expected_files = set(manifest["outputs"]) | {"manifest.json"}
279
+ actual_files = {
280
+ path.relative_to(destination).as_posix()
281
+ for path in destination.rglob("*")
282
+ if path.is_file()
283
+ }
284
+ if actual_files != expected_files:
285
+ raise RenderError(f"generation contains unexpected or missing files: {destination}")
286
+ for name, expected_hash in manifest["outputs"].items():
287
+ try:
288
+ actual_hash = _hash_bytes((destination / name).read_bytes())
289
+ except OSError as error:
290
+ raise RenderError(f"failed to verify {destination / name}: {error}") from error
291
+ if actual_hash != expected_hash:
292
+ raise RenderError(f"generation output hash mismatch: {destination / name}")
293
+
294
+
295
+ def _publish(destination: Path, outputs: dict[str, bytes], manifest: dict[str, Any]) -> None:
296
+ destination.parent.mkdir(parents=True, exist_ok=True)
297
+ if destination.exists():
298
+ _verify_existing(destination, manifest)
299
+ return
300
+ staging = Path(tempfile.mkdtemp(prefix=".staging-", dir=destination.parent))
301
+ try:
302
+ for name, content in outputs.items():
303
+ path = staging / name
304
+ path.parent.mkdir(parents=True, exist_ok=True)
305
+ path.write_bytes(content)
306
+ (staging / "manifest.json").write_bytes(
307
+ json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True).encode("utf-8")
308
+ + b"\n"
309
+ )
310
+ try:
311
+ os.rename(staging, destination)
312
+ except OSError:
313
+ if destination.exists():
314
+ _verify_existing(destination, manifest)
315
+ else:
316
+ raise
317
+ finally:
318
+ if staging.exists():
319
+ shutil.rmtree(staging, ignore_errors=True)
320
+
321
+
322
+ def render(project_root: Path, skill_dir: Path) -> Path:
323
+ project_root = project_root.resolve(strict=True)
324
+ skill_dir = skill_dir.resolve(strict=True)
325
+ if not (project_root / "_bmad").is_dir():
326
+ raise RenderError(f"project root does not contain _bmad/: {project_root}")
327
+
328
+ sources = _load_sources(skill_dir)
329
+ central = load_central_config(project_root)
330
+ has_customization = any(
331
+ _CUSTOM_TOKEN.search(content) for content in sources.values()
332
+ )
333
+ defaults = (
334
+ load_toml(skill_dir / "customize.toml", required=True)
335
+ if has_customization
336
+ else None
337
+ )
338
+ customization = (
339
+ load_customization(project_root, skill_dir) if has_customization else {}
340
+ )
341
+ replacements, input_values = _resolve_replacements(
342
+ sources, central, customization, defaults, project_root
343
+ )
344
+ source_hashes = {
345
+ name: _hash_bytes(content.encode("utf-8")) for name, content in sources.items()
346
+ }
347
+ root_hash = _hash_bytes(str(project_root).encode("utf-8"))[:12]
348
+ slug = re.sub(r"[^a-z0-9]+", "-", project_root.name.lower()).strip("-") or "project"
349
+ slug = slug[:80].rstrip("-") or "project"
350
+ renderer_hash = _hash_bytes(Path(__file__).read_bytes())
351
+ identity = {
352
+ "project_root": str(project_root),
353
+ "renderer_sha256": renderer_hash,
354
+ "resolved_values": input_values,
355
+ "source_sha256": source_hashes,
356
+ }
357
+ generation_hash = _hash_bytes(_canonical_json(identity))[:20]
358
+ destination = (
359
+ project_root
360
+ / "_bmad"
361
+ / "render"
362
+ / skill_dir.name
363
+ / f"{slug}-{root_hash}"
364
+ / generation_hash
365
+ )
366
+ rendered = _render_sources(sources, replacements, destination)
367
+ outputs = {name: content.encode("utf-8") for name, content in rendered.items()}
368
+ output_hashes = {name: _hash_bytes(content) for name, content in outputs.items()}
369
+ manifest = {
370
+ "schema_version": 1,
371
+ "skill": skill_dir.name,
372
+ "project_root": str(project_root),
373
+ "project_slug": slug,
374
+ "root_hash": root_hash,
375
+ "generation_hash": generation_hash,
376
+ "inputs": identity,
377
+ "outputs": output_hashes,
378
+ }
379
+ _publish(destination, outputs, manifest)
380
+ return destination / "workflow.md"
381
+
382
+
383
+ def main() -> int:
384
+ parser = argparse.ArgumentParser(description=__doc__)
385
+ parser.add_argument("--project-root", required=True)
386
+ parser.add_argument("--skill", required=True)
387
+ args = parser.parse_args()
388
+ reconfigure = getattr(sys.stdout, "reconfigure", None)
389
+ if reconfigure is not None:
390
+ reconfigure(encoding="utf-8")
391
+ try:
392
+ entry = render(Path(args.project_root), Path(args.skill))
393
+ except (ConfigError, RenderError, OSError, UnicodeError, ValueError) as error:
394
+ sys.stdout.write(f"HALT: {error}\n")
395
+ return 1
396
+ sys.stdout.write(f"read and follow {entry}\n")
397
+ return 0
398
+
399
+
400
+ if __name__ == "__main__":
401
+ raise SystemExit(main())
@@ -1,134 +1,32 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- Resolve BMad's central config using four-layer TOML merge.
4
-
5
- Reads from four layers (highest priority last):
6
- 1. {project-root}/_bmad/config.toml (installer-owned team)
7
- 2. {project-root}/_bmad/config.user.toml (installer-owned user)
8
- 3. {project-root}/_bmad/custom/config.toml (human-authored team, committed)
9
- 4. {project-root}/_bmad/custom/config.user.toml (human-authored user, gitignored)
10
-
11
- Outputs merged JSON to stdout. Errors go to stderr.
12
-
13
- Uses only the Python stdlib (`tomllib`) — no third-party dependencies.
14
- BMad is standardizing on `uv run` to invoke scripts (uv provisions a suitable
15
- interpreter for you); a plain `python3` on PATH still works during the
16
- transition. Either runner needs Python 3.11+ for `tomllib`.
17
-
18
- uv run resolve_config.py --project-root /abs/path/to/project
19
- uv run resolve_config.py --project-root ... --key core
20
- uv run resolve_config.py --project-root ... --key agents
21
-
22
- Merge rules (same as resolve_customization.py):
23
- - Scalars: override wins
24
- - Tables: deep merge
25
- - Arrays of tables where every item shares `code` or `id`: merge by that key
26
- - All other arrays: append
27
- """
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # ///
5
+ """Resolve BMad's four central TOML layers to JSON."""
28
6
 
29
7
  import argparse
30
8
  import json
31
9
  import sys
32
10
  from pathlib import Path
33
11
 
12
+ # Installed scripts are consumer files, not a location for interpreter caches.
13
+ sys.dont_write_bytecode = True
14
+
34
15
  try:
35
- import tomllib
36
- except ImportError:
37
- sys.stderr.write(
38
- "error: Python 3.11+ is required (stdlib `tomllib` not found).\n"
39
- )
40
- sys.exit(3)
16
+ from config_utils import ConfigError, load_central_config
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
41
22
 
42
23
 
43
24
  _MISSING = object()
44
- _KEYED_MERGE_FIELDS = ("code", "id")
45
-
46
-
47
- def load_toml(file_path: Path, required: bool = False) -> dict:
48
- if not file_path.exists():
49
- if required:
50
- sys.stderr.write(f"error: required config file not found: {file_path}\n")
51
- sys.exit(1)
52
- return {}
53
- try:
54
- with file_path.open("rb") as f:
55
- parsed = tomllib.load(f)
56
- if not isinstance(parsed, dict):
57
- return {}
58
- return parsed
59
- except tomllib.TOMLDecodeError as error:
60
- level = "error" if required else "warning"
61
- sys.stderr.write(f"{level}: failed to parse {file_path}: {error}\n")
62
- if required:
63
- sys.exit(1)
64
- return {}
65
- except OSError as error:
66
- level = "error" if required else "warning"
67
- sys.stderr.write(f"{level}: failed to read {file_path}: {error}\n")
68
- if required:
69
- sys.exit(1)
70
- return {}
71
-
72
-
73
- def _detect_keyed_merge_field(items):
74
- if not items or not all(isinstance(item, dict) for item in items):
75
- return None
76
- for candidate in _KEYED_MERGE_FIELDS:
77
- if all(item.get(candidate) is not None for item in items):
78
- return candidate
79
- return None
80
-
81
-
82
- def _merge_by_key(base, override, key_name):
83
- result = []
84
- index_by_key = {}
85
- for item in base:
86
- if not isinstance(item, dict):
87
- continue
88
- if item.get(key_name) is not None:
89
- index_by_key[item[key_name]] = len(result)
90
- result.append(dict(item))
91
- for item in override:
92
- if not isinstance(item, dict):
93
- result.append(item)
94
- continue
95
- key = item.get(key_name)
96
- if key is not None and key in index_by_key:
97
- result[index_by_key[key]] = dict(item)
98
- else:
99
- if key is not None:
100
- index_by_key[key] = len(result)
101
- result.append(dict(item))
102
- return result
103
-
104
-
105
- def _merge_arrays(base, override):
106
- base_arr = base if isinstance(base, list) else []
107
- override_arr = override if isinstance(override, list) else []
108
- keyed_field = _detect_keyed_merge_field(base_arr + override_arr)
109
- if keyed_field:
110
- return _merge_by_key(base_arr, override_arr, keyed_field)
111
- return base_arr + override_arr
112
-
113
-
114
- def deep_merge(base, override):
115
- if isinstance(base, dict) and isinstance(override, dict):
116
- result = dict(base)
117
- for key, over_val in override.items():
118
- if key in result:
119
- result[key] = deep_merge(result[key], over_val)
120
- else:
121
- result[key] = over_val
122
- return result
123
- if isinstance(base, list) and isinstance(override, list):
124
- return _merge_arrays(base, override)
125
- return override
126
25
 
127
26
 
128
27
  def extract_key(data, dotted_key: str):
129
- parts = dotted_key.split(".")
130
28
  current = data
131
- for part in parts:
29
+ for part in dotted_key.split("."):
132
30
  if isinstance(current, dict) and part in current:
133
31
  current = current[part]
134
32
  else:
@@ -136,43 +34,41 @@ def extract_key(data, dotted_key: str):
136
34
  return current
137
35
 
138
36
 
139
- def main():
37
+ def main() -> int:
140
38
  parser = argparse.ArgumentParser(
141
- description="Resolve BMad central config using four-layer TOML merge.",
39
+ description="Resolve BMad central config using four-layer TOML merge."
142
40
  )
143
41
  parser.add_argument(
144
- "--project-root", "-p", required=True,
145
- help="Absolute path to the project root (contains _bmad/)",
42
+ "--project-root",
43
+ "-p",
44
+ required=True,
45
+ help="Absolute project root containing _bmad/",
146
46
  )
147
47
  parser.add_argument(
148
- "--key", "-k", action="append", default=[],
48
+ "--key",
49
+ "-k",
50
+ action="append",
51
+ default=[],
149
52
  help="Dotted field path to resolve (repeatable). Omit for full dump.",
150
53
  )
151
54
  args = parser.parse_args()
152
55
 
153
- project_root = Path(args.project_root).resolve()
154
- bmad_dir = project_root / "_bmad"
155
-
156
- base_team = load_toml(bmad_dir / "config.toml", required=True)
157
- base_user = load_toml(bmad_dir / "config.user.toml")
158
- custom_team = load_toml(bmad_dir / "custom" / "config.toml")
159
- custom_user = load_toml(bmad_dir / "custom" / "config.user.toml")
160
-
161
- merged = deep_merge(base_team, base_user)
162
- merged = deep_merge(merged, custom_team)
163
- merged = deep_merge(merged, custom_user)
56
+ try:
57
+ merged = load_central_config(Path(args.project_root).resolve())
58
+ except ConfigError as error:
59
+ sys.stderr.write(f"error: {error}\n")
60
+ return 1
164
61
 
62
+ output = merged
165
63
  if args.key:
166
64
  output = {}
167
65
  for key in args.key:
168
66
  value = extract_key(merged, key)
169
67
  if value is not _MISSING:
170
68
  output[key] = value
171
- else:
172
- output = merged
173
-
174
69
  sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n")
70
+ return 0
175
71
 
176
72
 
177
73
  if __name__ == "__main__":
178
- main()
74
+ raise SystemExit(main())