bmad-method 5.1.3 → 6.0.0-Beta.1

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 (739) hide show
  1. package/.coderabbit.yaml +40 -0
  2. package/.github/CODE_OF_CONDUCT.md +128 -0
  3. package/.github/FUNDING.yaml +2 -2
  4. package/.github/ISSUE_TEMPLATE/config.yaml +8 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -11
  6. package/.github/ISSUE_TEMPLATE/issue.md +32 -0
  7. package/.github/scripts/discord-helpers.sh +34 -0
  8. package/.github/workflows/discord.yaml +82 -17
  9. package/.github/workflows/docs.yaml +63 -0
  10. package/.github/workflows/manual-release.yaml +40 -20
  11. package/.github/workflows/quality.yaml +115 -0
  12. package/.husky/pre-commit +17 -0
  13. package/.markdownlint-cli2.yaml +41 -0
  14. package/.nvmrc +1 -0
  15. package/.prettierignore +9 -0
  16. package/.vscode/settings.json +38 -10
  17. package/CHANGELOG.md +1167 -372
  18. package/CNAME +1 -0
  19. package/CONTRIBUTING.md +100 -142
  20. package/CONTRIBUTORS.md +32 -0
  21. package/LICENSE +10 -1
  22. package/README.md +67 -170
  23. package/SECURITY.md +85 -0
  24. package/TRADEMARK.md +55 -0
  25. package/Wordmark.png +0 -0
  26. package/banner-bmad-method.png +0 -0
  27. package/docs/404.md +9 -0
  28. package/docs/_STYLE_GUIDE.md +367 -0
  29. package/docs/downloads.md +74 -0
  30. package/docs/explanation/advanced-elicitation.md +24 -0
  31. package/docs/explanation/adversarial-review.md +57 -0
  32. package/docs/explanation/brainstorming.md +31 -0
  33. package/docs/explanation/brownfield-faq.md +55 -0
  34. package/docs/explanation/party-mode.md +57 -0
  35. package/docs/explanation/preventing-agent-conflicts.md +110 -0
  36. package/docs/explanation/quick-flow.md +27 -0
  37. package/docs/explanation/why-solutioning-matters.md +75 -0
  38. package/docs/how-to/brownfield/index.md +84 -0
  39. package/docs/how-to/brownfield/quick-fix-in-brownfield.md +76 -0
  40. package/docs/how-to/customize-bmad.md +158 -0
  41. package/docs/how-to/get-answers-about-bmad.md +102 -0
  42. package/docs/how-to/install-bmad.md +82 -0
  43. package/docs/how-to/shard-large-documents.md +101 -0
  44. package/docs/how-to/upgrade-to-v6.md +131 -0
  45. package/docs/index.md +56 -0
  46. package/docs/reference/workflow-map.md +83 -0
  47. package/docs/tea/explanation/engagement-models.md +710 -0
  48. package/docs/tea/explanation/fixture-architecture.md +457 -0
  49. package/docs/tea/explanation/knowledge-base-system.md +554 -0
  50. package/docs/tea/explanation/network-first-patterns.md +853 -0
  51. package/docs/tea/explanation/risk-based-testing.md +586 -0
  52. package/docs/tea/explanation/tea-overview.md +410 -0
  53. package/docs/tea/explanation/test-quality-standards.md +907 -0
  54. package/docs/tea/explanation/testing-as-engineering.md +112 -0
  55. package/docs/tea/glossary/index.md +159 -0
  56. package/docs/tea/how-to/brownfield/use-tea-for-enterprise.md +525 -0
  57. package/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md +577 -0
  58. package/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md +424 -0
  59. package/docs/tea/how-to/customization/integrate-playwright-utils.md +813 -0
  60. package/docs/tea/how-to/workflows/run-atdd.md +436 -0
  61. package/docs/tea/how-to/workflows/run-automate.md +653 -0
  62. package/docs/tea/how-to/workflows/run-nfr-assess.md +679 -0
  63. package/docs/tea/how-to/workflows/run-test-design.md +135 -0
  64. package/docs/tea/how-to/workflows/run-test-review.md +605 -0
  65. package/docs/tea/how-to/workflows/run-trace.md +883 -0
  66. package/docs/tea/how-to/workflows/setup-ci.md +712 -0
  67. package/docs/tea/how-to/workflows/setup-test-framework.md +98 -0
  68. package/docs/tea/reference/commands.md +276 -0
  69. package/docs/tea/reference/configuration.md +678 -0
  70. package/docs/tea/reference/knowledge-base.md +340 -0
  71. package/docs/tea/tutorials/tea-lite-quickstart.md +444 -0
  72. package/docs/tutorials/getting-started.md +205 -0
  73. package/eslint.config.mjs +42 -9
  74. package/package.json +50 -38
  75. package/prettier.config.mjs +1 -1
  76. package/src/bmm/_module-installer/installer.js +48 -0
  77. package/src/bmm/agents/analyst.agent.yaml +36 -0
  78. package/src/bmm/agents/architect.agent.yaml +28 -0
  79. package/src/bmm/agents/dev.agent.yaml +38 -0
  80. package/src/bmm/agents/pm.agent.yaml +46 -0
  81. package/src/bmm/agents/quick-flow-solo-dev.agent.yaml +32 -0
  82. package/src/bmm/agents/sm.agent.yaml +36 -0
  83. package/src/bmm/agents/tea.agent.yaml +63 -0
  84. package/src/bmm/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +224 -0
  85. package/src/bmm/agents/tech-writer/tech-writer.agent.yaml +45 -0
  86. package/src/bmm/agents/ux-designer.agent.yaml +26 -0
  87. package/src/bmm/data/project-context-template.md +26 -0
  88. package/src/bmm/module-help.csv +32 -0
  89. package/src/bmm/module.yaml +44 -0
  90. package/src/bmm/teams/default-party.csv +21 -0
  91. package/src/bmm/teams/team-fullstack.yaml +12 -0
  92. package/src/bmm/testarch/knowledge/adr-quality-readiness-checklist.md +350 -0
  93. package/src/bmm/testarch/knowledge/api-request.md +442 -0
  94. package/src/bmm/testarch/knowledge/api-testing-patterns.md +843 -0
  95. package/src/bmm/testarch/knowledge/auth-session.md +552 -0
  96. package/src/bmm/testarch/knowledge/burn-in.md +273 -0
  97. package/src/bmm/testarch/knowledge/ci-burn-in.md +675 -0
  98. package/src/bmm/testarch/knowledge/component-tdd.md +486 -0
  99. package/src/bmm/testarch/knowledge/contract-testing.md +957 -0
  100. package/src/bmm/testarch/knowledge/data-factories.md +500 -0
  101. package/src/bmm/testarch/knowledge/email-auth.md +721 -0
  102. package/src/bmm/testarch/knowledge/error-handling.md +725 -0
  103. package/src/bmm/testarch/knowledge/feature-flags.md +750 -0
  104. package/src/bmm/testarch/knowledge/file-utils.md +463 -0
  105. package/src/bmm/testarch/knowledge/fixture-architecture.md +401 -0
  106. package/src/bmm/testarch/knowledge/fixtures-composition.md +382 -0
  107. package/src/bmm/testarch/knowledge/intercept-network-call.md +430 -0
  108. package/src/bmm/testarch/knowledge/log.md +429 -0
  109. package/src/bmm/testarch/knowledge/network-error-monitor.md +405 -0
  110. package/src/bmm/testarch/knowledge/network-first.md +486 -0
  111. package/src/bmm/testarch/knowledge/network-recorder.md +527 -0
  112. package/src/bmm/testarch/knowledge/nfr-criteria.md +670 -0
  113. package/src/bmm/testarch/knowledge/overview.md +286 -0
  114. package/src/bmm/testarch/knowledge/playwright-config.md +730 -0
  115. package/src/bmm/testarch/knowledge/probability-impact.md +601 -0
  116. package/src/bmm/testarch/knowledge/recurse.md +421 -0
  117. package/src/bmm/testarch/knowledge/risk-governance.md +615 -0
  118. package/src/bmm/testarch/knowledge/selective-testing.md +732 -0
  119. package/src/bmm/testarch/knowledge/selector-resilience.md +527 -0
  120. package/src/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
  121. package/src/bmm/testarch/knowledge/test-levels-framework.md +473 -0
  122. package/src/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
  123. package/src/bmm/testarch/knowledge/test-quality.md +664 -0
  124. package/src/bmm/testarch/knowledge/timing-debugging.md +372 -0
  125. package/src/bmm/testarch/knowledge/visual-debugging.md +524 -0
  126. package/src/bmm/testarch/tea-index.csv +35 -0
  127. package/src/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
  128. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -0
  129. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -0
  130. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -0
  131. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -0
  132. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -0
  133. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -0
  134. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +162 -0
  135. package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +58 -0
  136. package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
  137. package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
  138. package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
  139. package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
  140. package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
  141. package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
  142. package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
  143. package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
  144. package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +200 -0
  145. package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
  146. package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
  147. package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
  148. package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
  149. package/src/bmm/workflows/1-analysis/research/research.template.md +29 -0
  150. package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
  151. package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
  152. package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
  153. package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
  154. package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +239 -0
  155. package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
  156. package/src/bmm/workflows/1-analysis/research/workflow.md +173 -0
  157. package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +13 -0
  158. package/src/bmm/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +197 -0
  159. package/src/bmm/workflows/2-plan-workflows/create-prd/data/project-types.csv +11 -0
  160. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01-init.md +191 -0
  161. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +153 -0
  162. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +224 -0
  163. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +226 -0
  164. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +213 -0
  165. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +207 -0
  166. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +226 -0
  167. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +237 -0
  168. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +228 -0
  169. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +231 -0
  170. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +242 -0
  171. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +217 -0
  172. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +124 -0
  173. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +247 -0
  174. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
  175. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +249 -0
  176. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +253 -0
  177. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +168 -0
  178. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +218 -0
  179. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +191 -0
  180. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +209 -0
  181. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +174 -0
  182. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
  183. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +228 -0
  184. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +217 -0
  185. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
  186. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
  187. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +263 -0
  188. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +209 -0
  189. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
  190. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +242 -0
  191. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +231 -0
  192. package/src/bmm/workflows/2-plan-workflows/create-prd/templates/prd-template.md +10 -0
  193. package/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +433 -0
  194. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md +150 -0
  195. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
  196. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
  197. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
  198. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
  199. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
  200. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
  201. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
  202. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
  203. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
  204. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
  205. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
  206. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
  207. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
  208. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
  209. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +171 -0
  210. package/src/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
  211. package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +43 -0
  212. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +190 -0
  213. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +178 -0
  214. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
  215. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +139 -0
  216. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
  217. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +135 -0
  218. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
  219. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +55 -0
  220. package/src/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
  221. package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +11 -0
  222. package/src/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
  223. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -0
  224. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -0
  225. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
  226. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +331 -0
  227. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -0
  228. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -0
  229. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
  230. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -0
  231. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +76 -0
  232. package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +50 -0
  233. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
  234. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
  235. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
  236. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +149 -0
  237. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
  238. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +59 -0
  239. package/src/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
  240. package/src/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
  241. package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +51 -0
  242. package/src/bmm/workflows/4-implementation/correct-course/checklist.md +288 -0
  243. package/src/bmm/workflows/4-implementation/correct-course/instructions.md +206 -0
  244. package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +60 -0
  245. package/src/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
  246. package/src/bmm/workflows/4-implementation/create-story/instructions.xml +345 -0
  247. package/src/bmm/workflows/4-implementation/create-story/template.md +49 -0
  248. package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +61 -0
  249. package/src/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
  250. package/src/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -0
  251. package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +27 -0
  252. package/src/bmm/workflows/4-implementation/retrospective/instructions.md +1443 -0
  253. package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +58 -0
  254. package/src/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
  255. package/src/bmm/workflows/4-implementation/sprint-planning/instructions.md +225 -0
  256. package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
  257. package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +54 -0
  258. package/src/bmm/workflows/4-implementation/sprint-status/instructions.md +229 -0
  259. package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +36 -0
  260. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +176 -0
  261. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +120 -0
  262. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +113 -0
  263. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +113 -0
  264. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +106 -0
  265. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +149 -0
  266. package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -0
  267. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +192 -0
  268. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +145 -0
  269. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +128 -0
  270. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -0
  271. package/src/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
  272. package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
  273. package/src/bmm/workflows/document-project/checklist.md +245 -0
  274. package/src/bmm/workflows/document-project/documentation-requirements.csv +12 -0
  275. package/src/bmm/workflows/document-project/instructions.md +221 -0
  276. package/src/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
  277. package/src/bmm/workflows/document-project/templates/index-template.md +169 -0
  278. package/src/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
  279. package/src/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
  280. package/src/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
  281. package/src/bmm/workflows/document-project/workflow.yaml +30 -0
  282. package/src/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
  283. package/src/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
  284. package/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
  285. package/src/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
  286. package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +90 -0
  287. package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +127 -0
  288. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +39 -0
  289. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +130 -0
  290. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +27 -0
  291. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +43 -0
  292. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +141 -0
  293. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +27 -0
  294. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +49 -0
  295. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +241 -0
  296. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +27 -0
  297. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +38 -0
  298. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +133 -0
  299. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +27 -0
  300. package/src/bmm/workflows/testarch/atdd/atdd-checklist-template.md +363 -0
  301. package/src/bmm/workflows/testarch/atdd/checklist.md +374 -0
  302. package/src/bmm/workflows/testarch/atdd/instructions.md +806 -0
  303. package/src/bmm/workflows/testarch/atdd/workflow.yaml +47 -0
  304. package/src/bmm/workflows/testarch/automate/checklist.md +582 -0
  305. package/src/bmm/workflows/testarch/automate/instructions.md +1324 -0
  306. package/src/bmm/workflows/testarch/automate/workflow.yaml +54 -0
  307. package/src/bmm/workflows/testarch/ci/checklist.md +247 -0
  308. package/src/bmm/workflows/testarch/ci/github-actions-template.yaml +198 -0
  309. package/src/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +149 -0
  310. package/src/bmm/workflows/testarch/ci/instructions.md +536 -0
  311. package/src/bmm/workflows/testarch/ci/workflow.yaml +47 -0
  312. package/src/bmm/workflows/testarch/framework/checklist.md +320 -0
  313. package/src/bmm/workflows/testarch/framework/instructions.md +481 -0
  314. package/src/bmm/workflows/testarch/framework/workflow.yaml +49 -0
  315. package/src/bmm/workflows/testarch/nfr-assess/checklist.md +407 -0
  316. package/src/bmm/workflows/testarch/nfr-assess/instructions.md +726 -0
  317. package/src/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +461 -0
  318. package/src/bmm/workflows/testarch/nfr-assess/workflow.yaml +49 -0
  319. package/src/bmm/workflows/testarch/test-design/checklist.md +407 -0
  320. package/src/bmm/workflows/testarch/test-design/instructions.md +1158 -0
  321. package/src/bmm/workflows/testarch/test-design/test-design-architecture-template.md +213 -0
  322. package/src/bmm/workflows/testarch/test-design/test-design-qa-template.md +286 -0
  323. package/src/bmm/workflows/testarch/test-design/test-design-template.md +294 -0
  324. package/src/bmm/workflows/testarch/test-design/workflow.yaml +71 -0
  325. package/src/bmm/workflows/testarch/test-review/checklist.md +472 -0
  326. package/src/bmm/workflows/testarch/test-review/instructions.md +628 -0
  327. package/src/bmm/workflows/testarch/test-review/test-review-template.md +390 -0
  328. package/src/bmm/workflows/testarch/test-review/workflow.yaml +48 -0
  329. package/src/bmm/workflows/testarch/trace/checklist.md +642 -0
  330. package/src/bmm/workflows/testarch/trace/instructions.md +1030 -0
  331. package/src/bmm/workflows/testarch/trace/trace-template.md +675 -0
  332. package/src/bmm/workflows/testarch/trace/workflow.yaml +57 -0
  333. package/src/core/_module-installer/installer.js +60 -0
  334. package/src/core/agents/bmad-master.agent.yaml +29 -0
  335. package/src/core/module-help.csv +9 -0
  336. package/src/core/module.yaml +25 -0
  337. package/src/core/resources/excalidraw/README.md +160 -0
  338. package/src/core/resources/excalidraw/excalidraw-helpers.md +127 -0
  339. package/src/core/resources/excalidraw/library-loader.md +50 -0
  340. package/src/core/resources/excalidraw/validate-json-instructions.md +79 -0
  341. package/src/core/tasks/editorial-review-prose.xml +100 -0
  342. package/src/core/tasks/editorial-review-structure.xml +209 -0
  343. package/src/core/tasks/help.md +62 -0
  344. package/src/core/tasks/index-docs.xml +65 -0
  345. package/src/core/tasks/review-adversarial-general.xml +48 -0
  346. package/src/core/tasks/shard-doc.xml +109 -0
  347. package/src/core/tasks/workflow.xml +235 -0
  348. package/src/core/workflows/advanced-elicitation/methods.csv +51 -0
  349. package/src/core/workflows/advanced-elicitation/workflow.xml +117 -0
  350. package/src/core/workflows/brainstorming/brain-methods.csv +62 -0
  351. package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
  352. package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
  353. package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
  354. package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
  355. package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
  356. package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
  357. package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -0
  358. package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
  359. package/src/core/workflows/brainstorming/template.md +15 -0
  360. package/src/core/workflows/brainstorming/workflow.md +58 -0
  361. package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -0
  362. package/src/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -0
  363. package/src/core/workflows/party-mode/steps/step-03-graceful-exit.md +157 -0
  364. package/src/core/workflows/party-mode/workflow.md +194 -0
  365. package/src/utility/agent-components/activation-rules.txt +6 -0
  366. package/src/utility/agent-components/activation-steps.txt +14 -0
  367. package/src/utility/agent-components/agent-command-header.md +1 -0
  368. package/src/utility/agent-components/agent.customize.template.yaml +41 -0
  369. package/src/utility/agent-components/handler-action.txt +4 -0
  370. package/src/utility/agent-components/handler-data.txt +5 -0
  371. package/src/utility/agent-components/handler-exec.txt +6 -0
  372. package/src/utility/agent-components/handler-multi.txt +14 -0
  373. package/src/utility/agent-components/handler-tmpl.txt +5 -0
  374. package/src/utility/agent-components/handler-validate-workflow.txt +7 -0
  375. package/src/utility/agent-components/handler-workflow.txt +10 -0
  376. package/src/utility/agent-components/menu-handlers.txt +6 -0
  377. package/test/README.md +295 -0
  378. package/test/adversarial-review-tests/README.md +56 -0
  379. package/test/adversarial-review-tests/sample-content.md +46 -0
  380. package/test/adversarial-review-tests/test-cases.yaml +103 -0
  381. package/test/fixtures/agent-schema/invalid/critical-actions/actions-as-string.agent.yaml +27 -0
  382. package/test/fixtures/agent-schema/invalid/critical-actions/empty-string-in-actions.agent.yaml +30 -0
  383. package/test/fixtures/agent-schema/invalid/menu/empty-menu.agent.yaml +22 -0
  384. package/test/fixtures/agent-schema/invalid/menu/missing-menu.agent.yaml +20 -0
  385. package/test/fixtures/agent-schema/invalid/menu-commands/empty-command-target.agent.yaml +25 -0
  386. package/test/fixtures/agent-schema/invalid/menu-commands/no-command-target.agent.yaml +24 -0
  387. package/test/fixtures/agent-schema/invalid/menu-triggers/camel-case.agent.yaml +25 -0
  388. package/test/fixtures/agent-schema/invalid/menu-triggers/compound-invalid-format.agent.yaml +25 -0
  389. package/test/fixtures/agent-schema/invalid/menu-triggers/compound-mismatched-kebab.agent.yaml +25 -0
  390. package/test/fixtures/agent-schema/invalid/menu-triggers/duplicate-triggers.agent.yaml +31 -0
  391. package/test/fixtures/agent-schema/invalid/menu-triggers/empty-trigger.agent.yaml +25 -0
  392. package/test/fixtures/agent-schema/invalid/menu-triggers/leading-asterisk.agent.yaml +25 -0
  393. package/test/fixtures/agent-schema/invalid/menu-triggers/snake-case.agent.yaml +25 -0
  394. package/test/fixtures/agent-schema/invalid/menu-triggers/trigger-with-spaces.agent.yaml +25 -0
  395. package/test/fixtures/agent-schema/invalid/metadata/empty-module-string.agent.yaml +26 -0
  396. package/test/fixtures/agent-schema/invalid/metadata/empty-name.agent.yaml +24 -0
  397. package/test/fixtures/agent-schema/invalid/metadata/extra-metadata-fields.agent.yaml +27 -0
  398. package/test/fixtures/agent-schema/invalid/metadata/missing-id.agent.yaml +23 -0
  399. package/test/fixtures/agent-schema/invalid/persona/empty-principles-array.agent.yaml +24 -0
  400. package/test/fixtures/agent-schema/invalid/persona/empty-string-in-principles.agent.yaml +27 -0
  401. package/test/fixtures/agent-schema/invalid/persona/extra-persona-fields.agent.yaml +27 -0
  402. package/test/fixtures/agent-schema/invalid/persona/missing-role.agent.yaml +24 -0
  403. package/test/fixtures/agent-schema/invalid/prompts/empty-content.agent.yaml +29 -0
  404. package/test/fixtures/agent-schema/invalid/prompts/extra-prompt-fields.agent.yaml +31 -0
  405. package/test/fixtures/agent-schema/invalid/prompts/missing-content.agent.yaml +28 -0
  406. package/test/fixtures/agent-schema/invalid/prompts/missing-id.agent.yaml +28 -0
  407. package/test/fixtures/agent-schema/invalid/top-level/empty-file.agent.yaml +5 -0
  408. package/test/fixtures/agent-schema/invalid/top-level/extra-top-level-keys.agent.yaml +28 -0
  409. package/test/fixtures/agent-schema/invalid/top-level/missing-agent-key.agent.yaml +11 -0
  410. package/test/fixtures/agent-schema/invalid/yaml-errors/invalid-indentation.agent.yaml +19 -0
  411. package/test/fixtures/agent-schema/invalid/yaml-errors/malformed-yaml.agent.yaml +18 -0
  412. package/test/fixtures/agent-schema/valid/critical-actions/empty-critical-actions.agent.yaml +24 -0
  413. package/test/fixtures/agent-schema/valid/critical-actions/no-critical-actions.agent.yaml +22 -0
  414. package/test/fixtures/agent-schema/valid/critical-actions/valid-critical-actions.agent.yaml +27 -0
  415. package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +31 -0
  416. package/test/fixtures/agent-schema/valid/menu/single-menu-item.agent.yaml +22 -0
  417. package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +38 -0
  418. package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +24 -0
  419. package/test/fixtures/agent-schema/valid/menu-triggers/compound-triggers.agent.yaml +31 -0
  420. package/test/fixtures/agent-schema/valid/menu-triggers/kebab-case-triggers.agent.yaml +34 -0
  421. package/test/fixtures/agent-schema/valid/metadata/core-agent-with-module.agent.yaml +24 -0
  422. package/test/fixtures/agent-schema/valid/metadata/empty-module-name-in-path.agent.yaml +24 -0
  423. package/test/fixtures/agent-schema/valid/metadata/malformed-path-treated-as-core.agent.yaml +24 -0
  424. package/test/fixtures/agent-schema/valid/metadata/module-agent-correct.agent.yaml +24 -0
  425. package/test/fixtures/agent-schema/valid/metadata/module-agent-missing-module.agent.yaml +23 -0
  426. package/test/fixtures/agent-schema/valid/metadata/wrong-module-value.agent.yaml +24 -0
  427. package/test/fixtures/agent-schema/valid/persona/complete-persona.agent.yaml +24 -0
  428. package/test/fixtures/agent-schema/valid/prompts/empty-prompts.agent.yaml +24 -0
  429. package/test/fixtures/agent-schema/valid/prompts/no-prompts.agent.yaml +22 -0
  430. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-minimal.agent.yaml +28 -0
  431. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-with-description.agent.yaml +30 -0
  432. package/test/fixtures/agent-schema/valid/top-level/minimal-core-agent.agent.yaml +24 -0
  433. package/test/test-agent-schema.js +387 -0
  434. package/test/test-cli-integration.sh +159 -0
  435. package/test/test-installation-components.js +214 -0
  436. package/test/unit-test-schema.js +133 -0
  437. package/tools/bmad-npx-wrapper.js +11 -12
  438. package/tools/build-docs.js +577 -0
  439. package/tools/cli/README.md +7 -0
  440. package/tools/cli/bmad-cli.js +58 -0
  441. package/tools/cli/commands/install.js +87 -0
  442. package/tools/cli/commands/status.js +65 -0
  443. package/tools/cli/external-official-modules.yaml +44 -0
  444. package/tools/cli/installers/install-messages.yaml +59 -0
  445. package/tools/cli/installers/lib/core/config-collector.js +1079 -0
  446. package/tools/cli/installers/lib/core/custom-module-cache.js +259 -0
  447. package/tools/cli/installers/lib/core/dependency-resolver.js +739 -0
  448. package/tools/cli/installers/lib/core/detector.js +223 -0
  449. package/tools/cli/installers/lib/core/ide-config-manager.js +156 -0
  450. package/tools/cli/installers/lib/core/installer.js +2826 -0
  451. package/tools/cli/installers/lib/core/manifest-generator.js +1054 -0
  452. package/tools/cli/installers/lib/core/manifest.js +1036 -0
  453. package/tools/cli/installers/lib/custom/handler.js +363 -0
  454. package/tools/cli/installers/lib/ide/STANDARDIZATION_PLAN.md +208 -0
  455. package/tools/cli/installers/lib/ide/_base-ide.js +655 -0
  456. package/tools/cli/installers/lib/ide/antigravity.js +474 -0
  457. package/tools/cli/installers/lib/ide/auggie.js +244 -0
  458. package/tools/cli/installers/lib/ide/claude-code.js +506 -0
  459. package/tools/cli/installers/lib/ide/cline.js +272 -0
  460. package/tools/cli/installers/lib/ide/codex.js +412 -0
  461. package/tools/cli/installers/lib/ide/crush.js +149 -0
  462. package/tools/cli/installers/lib/ide/cursor.js +160 -0
  463. package/tools/cli/installers/lib/ide/gemini.js +301 -0
  464. package/tools/cli/installers/lib/ide/github-copilot.js +383 -0
  465. package/tools/cli/installers/lib/ide/iflow.js +191 -0
  466. package/tools/cli/installers/lib/ide/kilo.js +250 -0
  467. package/tools/cli/installers/lib/ide/kiro-cli.js +326 -0
  468. package/tools/cli/installers/lib/ide/manager.js +244 -0
  469. package/tools/cli/installers/lib/ide/opencode.js +257 -0
  470. package/tools/cli/installers/lib/ide/qwen.js +372 -0
  471. package/tools/cli/installers/lib/ide/roo.js +273 -0
  472. package/tools/cli/installers/lib/ide/rovo-dev.js +290 -0
  473. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +165 -0
  474. package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +158 -0
  475. package/tools/cli/installers/lib/ide/shared/module-injections.js +136 -0
  476. package/tools/cli/installers/lib/ide/shared/path-utils.js +165 -0
  477. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +268 -0
  478. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +293 -0
  479. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +14 -0
  480. package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +14 -0
  481. package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +12 -0
  482. package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +13 -0
  483. package/tools/cli/installers/lib/ide/templates/workflow-commander.md +5 -0
  484. package/tools/cli/installers/lib/ide/trae.js +313 -0
  485. package/tools/cli/installers/lib/ide/windsurf.js +258 -0
  486. package/tools/cli/installers/lib/message-loader.js +85 -0
  487. package/tools/cli/installers/lib/modules/external-manager.js +135 -0
  488. package/tools/cli/installers/lib/modules/manager.js +1375 -0
  489. package/tools/cli/lib/activation-builder.js +163 -0
  490. package/tools/cli/lib/agent/compiler.js +522 -0
  491. package/tools/cli/lib/agent/installer.js +716 -0
  492. package/tools/cli/lib/agent/template-engine.js +152 -0
  493. package/tools/cli/lib/agent-analyzer.js +109 -0
  494. package/tools/cli/lib/agent-party-generator.js +194 -0
  495. package/tools/cli/lib/cli-utils.js +227 -0
  496. package/tools/cli/lib/config.js +213 -0
  497. package/tools/cli/lib/file-ops.js +204 -0
  498. package/tools/cli/lib/platform-codes.js +116 -0
  499. package/tools/cli/lib/project-root.js +77 -0
  500. package/tools/cli/lib/prompts.js +433 -0
  501. package/tools/cli/lib/ui.js +1716 -0
  502. package/tools/cli/lib/xml-handler.js +177 -0
  503. package/tools/cli/lib/xml-to-markdown.js +82 -0
  504. package/tools/{yaml-format.js → cli/lib/yaml-format.js} +9 -17
  505. package/tools/cli/lib/yaml-xml-builder.js +587 -0
  506. package/tools/docs/BUNDLE_DISTRIBUTION_SETUP.md +95 -0
  507. package/tools/docs/fix-refs.md +91 -0
  508. package/tools/docs/index.md +2 -0
  509. package/tools/fix-doc-links.js +288 -0
  510. package/tools/flattener/ignoreRules.js +2 -6
  511. package/tools/flattener/main.js +31 -121
  512. package/tools/flattener/projectRoot.js +3 -8
  513. package/tools/flattener/stats.helpers.js +8 -35
  514. package/tools/flattener/stats.js +1 -6
  515. package/tools/flattener/test-matrix.js +1 -5
  516. package/tools/flattener/xml.js +1 -7
  517. package/tools/format-workflow-md.js +263 -0
  518. package/tools/lib/xml-utils.js +13 -0
  519. package/tools/maintainer/review-pr-README.md +55 -0
  520. package/tools/maintainer/review-pr.md +242 -0
  521. package/tools/migrate-custom-module-paths.js +124 -0
  522. package/tools/platform-codes.yaml +157 -0
  523. package/tools/schema/agent.js +491 -0
  524. package/tools/validate-agent-schema.js +110 -0
  525. package/tools/validate-doc-links.js +371 -0
  526. package/tools/validate-svg-changes.sh +356 -0
  527. package/website/README.md +76 -0
  528. package/website/_basement/components/WorkflowGuide.astro +444 -0
  529. package/website/_basement/pages/workflow-guide.astro +17 -0
  530. package/website/astro.config.mjs +169 -0
  531. package/website/public/favicon.ico +0 -0
  532. package/website/public/img/bmad-dark.png +0 -0
  533. package/website/public/img/bmad-light.png +0 -0
  534. package/website/public/img/logo.svg +4 -0
  535. package/website/public/img/workflow-map.png +0 -0
  536. package/website/public/robots.txt +37 -0
  537. package/website/public/workflow-map-diagram.html +361 -0
  538. package/website/src/components/Banner.astro +59 -0
  539. package/website/src/components/Header.astro +121 -0
  540. package/website/src/components/MobileMenuFooter.astro +53 -0
  541. package/website/src/content/config.ts +6 -0
  542. package/website/src/lib/site-url.js +25 -0
  543. package/website/src/pages/404.astro +11 -0
  544. package/website/src/rehype-base-paths.js +89 -0
  545. package/website/src/rehype-markdown-links.js +117 -0
  546. package/website/src/styles/custom.css +500 -0
  547. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
  548. package/.github/workflows/format-check.yaml +0 -42
  549. package/bmad-core/agent-teams/team-all.yaml +0 -14
  550. package/bmad-core/agent-teams/team-fullstack.yaml +0 -18
  551. package/bmad-core/agent-teams/team-ide-minimal.yaml +0 -10
  552. package/bmad-core/agent-teams/team-no-ui.yaml +0 -13
  553. package/bmad-core/agents/analyst.md +0 -81
  554. package/bmad-core/agents/architect.md +0 -83
  555. package/bmad-core/agents/bmad-master.md +0 -107
  556. package/bmad-core/agents/bmad-orchestrator.md +0 -149
  557. package/bmad-core/agents/dev.md +0 -75
  558. package/bmad-core/agents/pm.md +0 -81
  559. package/bmad-core/agents/po.md +0 -76
  560. package/bmad-core/agents/qa.md +0 -88
  561. package/bmad-core/agents/sm.md +0 -62
  562. package/bmad-core/agents/ux-expert.md +0 -66
  563. package/bmad-core/checklists/architect-checklist.md +0 -438
  564. package/bmad-core/checklists/change-checklist.md +0 -182
  565. package/bmad-core/checklists/pm-checklist.md +0 -370
  566. package/bmad-core/checklists/po-master-checklist.md +0 -432
  567. package/bmad-core/checklists/story-dod-checklist.md +0 -94
  568. package/bmad-core/checklists/story-draft-checklist.md +0 -153
  569. package/bmad-core/core-config.yaml +0 -20
  570. package/bmad-core/data/bmad-kb.md +0 -806
  571. package/bmad-core/data/brainstorming-techniques.md +0 -36
  572. package/bmad-core/data/elicitation-methods.md +0 -154
  573. package/bmad-core/data/technical-preferences.md +0 -3
  574. package/bmad-core/data/test-levels-framework.md +0 -146
  575. package/bmad-core/data/test-priorities-matrix.md +0 -172
  576. package/bmad-core/tasks/advanced-elicitation.md +0 -117
  577. package/bmad-core/tasks/brownfield-create-epic.md +0 -160
  578. package/bmad-core/tasks/brownfield-create-story.md +0 -147
  579. package/bmad-core/tasks/correct-course.md +0 -70
  580. package/bmad-core/tasks/create-brownfield-story.md +0 -312
  581. package/bmad-core/tasks/create-deep-research-prompt.md +0 -278
  582. package/bmad-core/tasks/create-next-story.md +0 -112
  583. package/bmad-core/tasks/document-project.md +0 -343
  584. package/bmad-core/tasks/facilitate-brainstorming-session.md +0 -136
  585. package/bmad-core/tasks/generate-ai-frontend-prompt.md +0 -51
  586. package/bmad-core/tasks/index-docs.md +0 -173
  587. package/bmad-core/tasks/kb-mode-interaction.md +0 -75
  588. package/bmad-core/tasks/nfr-assess.md +0 -343
  589. package/bmad-core/tasks/qa-gate.md +0 -159
  590. package/bmad-core/tasks/review-story.md +0 -314
  591. package/bmad-core/tasks/risk-profile.md +0 -353
  592. package/bmad-core/tasks/shard-doc.md +0 -185
  593. package/bmad-core/tasks/test-design.md +0 -174
  594. package/bmad-core/tasks/trace-requirements.md +0 -264
  595. package/bmad-core/tasks/validate-next-story.md +0 -134
  596. package/bmad-core/templates/architecture-tmpl.yaml +0 -650
  597. package/bmad-core/templates/brainstorming-output-tmpl.yaml +0 -156
  598. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +0 -476
  599. package/bmad-core/templates/brownfield-prd-tmpl.yaml +0 -280
  600. package/bmad-core/templates/competitor-analysis-tmpl.yaml +0 -306
  601. package/bmad-core/templates/front-end-architecture-tmpl.yaml +0 -218
  602. package/bmad-core/templates/front-end-spec-tmpl.yaml +0 -349
  603. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +0 -823
  604. package/bmad-core/templates/market-research-tmpl.yaml +0 -252
  605. package/bmad-core/templates/prd-tmpl.yaml +0 -202
  606. package/bmad-core/templates/project-brief-tmpl.yaml +0 -221
  607. package/bmad-core/templates/qa-gate-tmpl.yaml +0 -102
  608. package/bmad-core/templates/story-tmpl.yaml +0 -137
  609. package/bmad-core/workflows/brownfield-fullstack.yaml +0 -297
  610. package/bmad-core/workflows/brownfield-service.yaml +0 -187
  611. package/bmad-core/workflows/brownfield-ui.yaml +0 -197
  612. package/bmad-core/workflows/greenfield-fullstack.yaml +0 -240
  613. package/bmad-core/workflows/greenfield-service.yaml +0 -206
  614. package/bmad-core/workflows/greenfield-ui.yaml +0 -235
  615. package/common/tasks/create-doc.md +0 -101
  616. package/common/tasks/execute-checklist.md +0 -86
  617. package/common/utils/bmad-doc-template.md +0 -325
  618. package/common/utils/workflow-management.md +0 -69
  619. package/dist/agents/analyst.txt +0 -2889
  620. package/dist/agents/architect.txt +0 -3552
  621. package/dist/agents/bmad-master.txt +0 -8769
  622. package/dist/agents/bmad-orchestrator.txt +0 -1513
  623. package/dist/agents/dev.txt +0 -414
  624. package/dist/agents/pm.txt +0 -2204
  625. package/dist/agents/po.txt +0 -1346
  626. package/dist/agents/qa.txt +0 -1987
  627. package/dist/agents/sm.txt +0 -658
  628. package/dist/agents/ux-expert.txt +0 -694
  629. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +0 -2371
  630. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +0 -1620
  631. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +0 -815
  632. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +0 -10952
  633. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +0 -4012
  634. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +0 -3698
  635. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +0 -450
  636. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +0 -973
  637. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +0 -15376
  638. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +0 -2075
  639. package/dist/teams/team-all.txt +0 -12682
  640. package/dist/teams/team-fullstack.txt +0 -10421
  641. package/dist/teams/team-ide-minimal.txt +0 -5103
  642. package/dist/teams/team-no-ui.txt +0 -8980
  643. package/docs/GUIDING-PRINCIPLES.md +0 -91
  644. package/docs/core-architecture.md +0 -219
  645. package/docs/enhanced-ide-development-workflow.md +0 -248
  646. package/docs/expansion-packs.md +0 -280
  647. package/docs/how-to-contribute-with-pull-requests.md +0 -158
  648. package/docs/user-guide.md +0 -504
  649. package/docs/versioning-and-releases.md +0 -147
  650. package/docs/versions.md +0 -48
  651. package/docs/working-in-the-brownfield.md +0 -597
  652. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +0 -102
  653. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash copy.txt +0 -13
  654. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash.txt +0 -13
  655. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.2 Agent Development Kit Installation/1.2.2 - Basic Project Structure - txt.txt +0 -25
  656. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.1 - settings.py +0 -34
  657. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.2 - main.py - Base Application.py +0 -70
  658. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.4 Deployment Configuration/1.4.2 - cloudbuild.yaml +0 -26
  659. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +0 -109
  660. package/expansion-packs/README.md +0 -3
  661. package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +0 -13
  662. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +0 -71
  663. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +0 -78
  664. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +0 -64
  665. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +0 -201
  666. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +0 -160
  667. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +0 -8
  668. package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +0 -250
  669. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +0 -647
  670. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +0 -110
  671. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +0 -216
  672. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +0 -290
  673. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +0 -613
  674. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +0 -356
  675. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +0 -343
  676. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +0 -253
  677. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +0 -484
  678. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +0 -183
  679. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +0 -175
  680. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +0 -14
  681. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +0 -80
  682. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +0 -77
  683. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +0 -78
  684. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +0 -65
  685. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +0 -391
  686. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +0 -203
  687. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +0 -201
  688. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +0 -124
  689. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +0 -6
  690. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +0 -769
  691. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +0 -586
  692. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +0 -110
  693. package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +0 -141
  694. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +0 -184
  695. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +0 -290
  696. package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +0 -200
  697. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +0 -1030
  698. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +0 -356
  699. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +0 -705
  700. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +0 -256
  701. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +0 -484
  702. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +0 -183
  703. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +0 -175
  704. package/expansion-packs/bmad-infrastructure-devops/README.md +0 -147
  705. package/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.md +0 -71
  706. package/expansion-packs/bmad-infrastructure-devops/checklists/infrastructure-checklist.md +0 -484
  707. package/expansion-packs/bmad-infrastructure-devops/config.yaml +0 -9
  708. package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +0 -305
  709. package/expansion-packs/bmad-infrastructure-devops/tasks/review-infrastructure.md +0 -159
  710. package/expansion-packs/bmad-infrastructure-devops/tasks/validate-infrastructure.md +0 -153
  711. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +0 -424
  712. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +0 -629
  713. package/release_notes.md +0 -33
  714. package/tools/builders/web-builder.js +0 -675
  715. package/tools/bump-all-versions.js +0 -115
  716. package/tools/bump-expansion-version.js +0 -90
  717. package/tools/cli.js +0 -152
  718. package/tools/installer/README.md +0 -8
  719. package/tools/installer/bin/bmad.js +0 -585
  720. package/tools/installer/config/ide-agent-config.yaml +0 -58
  721. package/tools/installer/config/install.config.yaml +0 -123
  722. package/tools/installer/lib/config-loader.js +0 -257
  723. package/tools/installer/lib/file-manager.js +0 -389
  724. package/tools/installer/lib/ide-base-setup.js +0 -228
  725. package/tools/installer/lib/ide-setup.js +0 -1441
  726. package/tools/installer/lib/installer.js +0 -1995
  727. package/tools/installer/lib/memory-profiler.js +0 -225
  728. package/tools/installer/lib/module-manager.js +0 -114
  729. package/tools/installer/lib/resource-locator.js +0 -308
  730. package/tools/installer/package.json +0 -44
  731. package/tools/lib/dependency-resolver.js +0 -175
  732. package/tools/lib/yaml-utils.js +0 -29
  733. package/tools/md-assets/web-agent-startup-instructions.md +0 -39
  734. package/tools/preview-release-notes.js +0 -66
  735. package/tools/shared/bannerArt.js +0 -105
  736. package/tools/sync-installer-version.js +0 -32
  737. package/tools/update-expansion-version.js +0 -53
  738. package/tools/upgraders/v3-to-v4-upgrader.js +0 -672
  739. package/tools/version-bump.js +0 -94
@@ -0,0 +1,1716 @@
1
+ const chalk = require('chalk');
2
+ const path = require('node:path');
3
+ const os = require('node:os');
4
+ const fs = require('fs-extra');
5
+ const { CLIUtils } = require('./cli-utils');
6
+ const { CustomHandler } = require('../installers/lib/custom/handler');
7
+ const { ExternalModuleManager } = require('../installers/lib/modules/external-manager');
8
+ const prompts = require('./prompts');
9
+
10
+ // Separator class for visual grouping in select/multiselect prompts
11
+ // Note: @clack/prompts doesn't support separators natively, they are filtered out
12
+ class Separator {
13
+ constructor(text = '────────') {
14
+ this.line = text;
15
+ this.name = text;
16
+ }
17
+ type = 'separator';
18
+ }
19
+
20
+ // Separator for choice lists (compatible interface)
21
+ const choiceUtils = { Separator };
22
+
23
+ /**
24
+ * UI utilities for the installer
25
+ */
26
+ class UI {
27
+ /**
28
+ * Prompt for installation configuration
29
+ * @returns {Object} Installation configuration
30
+ */
31
+ async promptInstall() {
32
+ CLIUtils.displayLogo();
33
+
34
+ // Display version-specific start message from install-messages.yaml
35
+ const { MessageLoader } = require('../installers/lib/message-loader');
36
+ const messageLoader = new MessageLoader();
37
+ messageLoader.displayStartMessage();
38
+
39
+ const confirmedDirectory = await this.getConfirmedDirectory();
40
+
41
+ // Preflight: Check for legacy BMAD v4 footprints immediately after getting directory
42
+ const { Detector } = require('../installers/lib/core/detector');
43
+ const { Installer } = require('../installers/lib/core/installer');
44
+ const detector = new Detector();
45
+ const installer = new Installer();
46
+ const legacyV4 = await detector.detectLegacyV4(confirmedDirectory);
47
+ if (legacyV4.hasLegacyV4) {
48
+ await installer.handleLegacyV4Migration(confirmedDirectory, legacyV4);
49
+ }
50
+
51
+ // Check for legacy folders and prompt for rename before showing any menus
52
+ let hasLegacyCfg = false;
53
+ let hasLegacyBmadFolder = false;
54
+ let bmadDir = null;
55
+ let legacyBmadPath = null;
56
+
57
+ // First check for legacy .bmad folder (instead of _bmad)
58
+ // Only check if directory exists
59
+ if (await fs.pathExists(confirmedDirectory)) {
60
+ const entries = await fs.readdir(confirmedDirectory, { withFileTypes: true });
61
+ for (const entry of entries) {
62
+ if (entry.isDirectory() && (entry.name === '.bmad' || entry.name === 'bmad')) {
63
+ hasLegacyBmadFolder = true;
64
+ legacyBmadPath = path.join(confirmedDirectory, '.bmad');
65
+ bmadDir = legacyBmadPath;
66
+
67
+ // Check if it has _cfg folder
68
+ const cfgPath = path.join(legacyBmadPath, '_cfg');
69
+ if (await fs.pathExists(cfgPath)) {
70
+ hasLegacyCfg = true;
71
+ }
72
+ break;
73
+ }
74
+ }
75
+ }
76
+
77
+ // If no .bmad or bmad found, check for current installations _bmad
78
+ if (!hasLegacyBmadFolder) {
79
+ const bmadResult = await installer.findBmadDir(confirmedDirectory);
80
+ bmadDir = bmadResult.bmadDir;
81
+ hasLegacyCfg = bmadResult.hasLegacyCfg;
82
+ }
83
+
84
+ // Handle legacy .bmad or _cfg folder - these are very old (more than 2 versions behind)
85
+ // Show version warning instead of offering conversion
86
+ if (hasLegacyBmadFolder || hasLegacyCfg) {
87
+ console.log('');
88
+ console.log(chalk.yellow.bold('āš ļø LEGACY INSTALLATION DETECTED'));
89
+ console.log(chalk.yellow('─'.repeat(80)));
90
+ console.log(
91
+ chalk.yellow(
92
+ 'Found a ".bmad"/"bmad" folder, or a legacy "_cfg" folder under the bmad folder - this is from a old BMAD version that is out of date for automatic upgrade, manual intervention required.',
93
+ ),
94
+ );
95
+ console.log(chalk.yellow('This version is more than 2 alpha versions behind current.'));
96
+ console.log('');
97
+ console.log(chalk.dim('For stability, we only support updates from the previous 2 alpha versions.'));
98
+ console.log(chalk.dim('Legacy installations may have compatibility issues.'));
99
+ console.log('');
100
+ console.log(chalk.dim('For the best experience, we strongly recommend:'));
101
+ console.log(chalk.dim(' 1. Delete your current BMAD installation folder (.bmad or bmad)'));
102
+ console.log(
103
+ chalk.dim(
104
+ ' 2. Run a fresh installation\n\nIf you do not want to start fresh, you can attempt to proceed beyond this point IF you have ensured the bmad folder is named _bmad, and under it there is a _config folder. If you have a folder under your bmad folder named _cfg, you would need to rename it _config, and then restart the installer.',
105
+ ),
106
+ );
107
+ console.log('');
108
+ console.log(chalk.dim('Benefits of a fresh install:'));
109
+ console.log(chalk.dim(' • Cleaner configuration without legacy artifacts'));
110
+ console.log(chalk.dim(' • All new features properly configured'));
111
+ console.log(chalk.dim(' • Fewer potential conflicts'));
112
+ console.log(chalk.dim(''));
113
+ console.log(
114
+ chalk.dim(
115
+ 'If you have already produced output from an earlier alpha version, you can still retain those artifacts. After installation, ensure you configured during install the proper file locations for artifacts depending on the module you are using, or move the files to the proper locations.',
116
+ ),
117
+ );
118
+ console.log(chalk.yellow('─'.repeat(80)));
119
+ console.log('');
120
+
121
+ const proceed = await prompts.select({
122
+ message: 'How would you like to proceed?',
123
+ choices: [
124
+ {
125
+ name: 'Cancel and do a fresh install (recommended)',
126
+ value: 'cancel',
127
+ },
128
+ {
129
+ name: 'Proceed anyway (will attempt update, potentially may fail or have unstable behavior)',
130
+ value: 'proceed',
131
+ },
132
+ ],
133
+ default: 'cancel',
134
+ });
135
+
136
+ if (proceed === 'cancel') {
137
+ console.log('');
138
+ console.log(chalk.cyan('To do a fresh install:'));
139
+ console.log(chalk.dim(' 1. Delete the existing bmad folder in your project'));
140
+ console.log(chalk.dim(" 2. Run 'bmad install' again"));
141
+ console.log('');
142
+ process.exit(0);
143
+ return;
144
+ }
145
+
146
+ const ora = require('ora');
147
+ const spinner = ora('Updating folder structure...').start();
148
+ try {
149
+ // Handle .bmad folder
150
+ if (hasLegacyBmadFolder) {
151
+ const newBmadPath = path.join(confirmedDirectory, '_bmad');
152
+ await fs.move(legacyBmadPath, newBmadPath);
153
+ bmadDir = newBmadPath;
154
+ spinner.succeed('Renamed ".bmad" to "_bmad"');
155
+ }
156
+
157
+ // Handle _cfg folder (either from .bmad or standalone)
158
+ const cfgPath = path.join(bmadDir, '_cfg');
159
+ if (await fs.pathExists(cfgPath)) {
160
+ spinner.start('Renaming configuration folder...');
161
+ const newCfgPath = path.join(bmadDir, '_config');
162
+ await fs.move(cfgPath, newCfgPath);
163
+ spinner.succeed('Renamed "_cfg" to "_config"');
164
+ }
165
+ } catch (error) {
166
+ spinner.fail('Failed to update folder structure');
167
+ console.error(chalk.red(`Error: ${error.message}`));
168
+ process.exit(1);
169
+ }
170
+ }
171
+
172
+ // Check if there's an existing BMAD installation (after any folder renames)
173
+ const hasExistingInstall = await fs.pathExists(bmadDir);
174
+
175
+ let customContentConfig = { hasCustomContent: false };
176
+ if (!hasExistingInstall) {
177
+ customContentConfig._shouldAsk = true;
178
+ }
179
+
180
+ // Track action type (only set if there's an existing installation)
181
+ let actionType;
182
+
183
+ // Only show action menu if there's an existing installation
184
+ if (hasExistingInstall) {
185
+ // Get version information
186
+ const { existingInstall, bmadDir } = await this.getExistingInstallation(confirmedDirectory);
187
+ const packageJsonPath = path.join(__dirname, '../../../package.json');
188
+ const currentVersion = require(packageJsonPath).version;
189
+ const installedVersion = existingInstall.version || 'unknown';
190
+
191
+ // Check if version is too old and warn user
192
+ const shouldProceed = await this.showOldAlphaVersionWarning(installedVersion, currentVersion, path.basename(bmadDir));
193
+
194
+ // If user chose to cancel, exit the installer
195
+ if (!shouldProceed) {
196
+ process.exit(0);
197
+ return;
198
+ }
199
+
200
+ // Build menu choices dynamically
201
+ const choices = [];
202
+
203
+ // Always show Quick Update first (allows refreshing installation even on same version)
204
+ if (installedVersion !== 'unknown') {
205
+ choices.push({
206
+ name: `Quick Update (v${installedVersion} → v${currentVersion})`,
207
+ value: 'quick-update',
208
+ });
209
+ }
210
+
211
+ // Add custom agent compilation option
212
+ if (installedVersion !== 'unknown') {
213
+ choices.push({
214
+ name: 'Recompile Agents (apply customizations only)',
215
+ value: 'compile-agents',
216
+ });
217
+ }
218
+
219
+ // Common actions
220
+ choices.push({ name: 'Modify BMAD Installation', value: 'update' });
221
+
222
+ actionType = await prompts.select({
223
+ message: 'How would you like to proceed?',
224
+ choices: choices,
225
+ default: choices[0].value,
226
+ });
227
+
228
+ // Handle quick update separately
229
+ if (actionType === 'quick-update') {
230
+ // Quick update doesn't install custom content - just updates existing modules
231
+ return {
232
+ actionType: 'quick-update',
233
+ directory: confirmedDirectory,
234
+ customContent: { hasCustomContent: false },
235
+ };
236
+ }
237
+
238
+ // Handle compile agents separately
239
+ if (actionType === 'compile-agents') {
240
+ // Only recompile agents with customizations, don't update any files
241
+ return {
242
+ actionType: 'compile-agents',
243
+ directory: confirmedDirectory,
244
+ customContent: { hasCustomContent: false },
245
+ };
246
+ }
247
+
248
+ // If actionType === 'update', handle it with the new flow
249
+ // Return early with modify configuration
250
+ if (actionType === 'update') {
251
+ // Get existing installation info
252
+ const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
253
+
254
+ console.log(chalk.dim(` Found existing modules: ${[...installedModuleIds].join(', ')}`));
255
+
256
+ // Unified module selection - all modules in one grouped multiselect
257
+ let selectedModules = await this.selectAllModules(installedModuleIds);
258
+
259
+ // After module selection, ask about custom modules
260
+ console.log('');
261
+ const changeCustomModules = await prompts.confirm({
262
+ message: 'Modify custom modules, agents, or workflows?',
263
+ default: false,
264
+ });
265
+
266
+ let customModuleResult = { selectedCustomModules: [], customContentConfig: { hasCustomContent: false } };
267
+ if (changeCustomModules) {
268
+ customModuleResult = await this.handleCustomModulesInModifyFlow(confirmedDirectory, selectedModules);
269
+ } else {
270
+ // Preserve existing custom modules if user doesn't want to modify them
271
+ const { Installer } = require('../installers/lib/core/installer');
272
+ const installer = new Installer();
273
+ const { bmadDir } = await installer.findBmadDir(confirmedDirectory);
274
+
275
+ const cacheDir = path.join(bmadDir, '_config', 'custom');
276
+ if (await fs.pathExists(cacheDir)) {
277
+ const entries = await fs.readdir(cacheDir, { withFileTypes: true });
278
+ for (const entry of entries) {
279
+ if (entry.isDirectory()) {
280
+ customModuleResult.selectedCustomModules.push(entry.name);
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ // Merge any selected custom modules
287
+ if (customModuleResult.selectedCustomModules.length > 0) {
288
+ selectedModules.push(...customModuleResult.selectedCustomModules);
289
+ }
290
+
291
+ // Get tool selection
292
+ const toolSelection = await this.promptToolSelection(confirmedDirectory);
293
+
294
+ const coreConfig = await this.collectCoreConfig(confirmedDirectory);
295
+
296
+ return {
297
+ actionType: 'update',
298
+ directory: confirmedDirectory,
299
+ installCore: true,
300
+ modules: selectedModules,
301
+ ides: toolSelection.ides,
302
+ skipIde: toolSelection.skipIde,
303
+ coreConfig: coreConfig,
304
+ customContent: customModuleResult.customContentConfig,
305
+ };
306
+ }
307
+ }
308
+
309
+ // This section is only for new installations (update returns early above)
310
+ const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
311
+
312
+ // Unified module selection - all modules in one grouped multiselect
313
+ let selectedModules = await this.selectAllModules(installedModuleIds);
314
+
315
+ // Ask about custom content (local modules/agents/workflows)
316
+ const wantsCustomContent = await prompts.confirm({
317
+ message: 'Add custom modules, agents, or workflows from your computer?',
318
+ default: false,
319
+ });
320
+
321
+ if (wantsCustomContent) {
322
+ customContentConfig = await this.promptCustomContentSource();
323
+ }
324
+
325
+ // Add custom content modules if any were selected
326
+ if (customContentConfig && customContentConfig.selectedModuleIds) {
327
+ selectedModules.push(...customContentConfig.selectedModuleIds);
328
+ }
329
+
330
+ selectedModules = selectedModules.filter((m) => m !== 'core');
331
+ let toolSelection = await this.promptToolSelection(confirmedDirectory);
332
+ const coreConfig = await this.collectCoreConfig(confirmedDirectory);
333
+
334
+ return {
335
+ actionType: 'install',
336
+ directory: confirmedDirectory,
337
+ installCore: true,
338
+ modules: selectedModules,
339
+ ides: toolSelection.ides,
340
+ skipIde: toolSelection.skipIde,
341
+ coreConfig: coreConfig,
342
+ customContent: customContentConfig,
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Prompt for tool/IDE selection (called after module configuration)
348
+ * @param {string} projectDir - Project directory to check for existing IDEs
349
+ * @returns {Object} Tool configuration
350
+ */
351
+ async promptToolSelection(projectDir) {
352
+ // Check for existing configured IDEs - use findBmadDir to detect custom folder names
353
+ const { Detector } = require('../installers/lib/core/detector');
354
+ const { Installer } = require('../installers/lib/core/installer');
355
+ const detector = new Detector();
356
+ const installer = new Installer();
357
+ const bmadResult = await installer.findBmadDir(projectDir || process.cwd());
358
+ const bmadDir = bmadResult.bmadDir;
359
+ const existingInstall = await detector.detect(bmadDir);
360
+ const configuredIdes = existingInstall.ides || [];
361
+
362
+ // Get IDE manager to fetch available IDEs dynamically
363
+ const { IdeManager } = require('../installers/lib/ide/manager');
364
+ const ideManager = new IdeManager();
365
+
366
+ const preferredIdes = ideManager.getPreferredIdes();
367
+ const otherIdes = ideManager.getOtherIdes();
368
+
369
+ // Build grouped options object for groupMultiselect
370
+ const groupedOptions = {};
371
+ const processedIdes = new Set();
372
+ const initialValues = [];
373
+
374
+ // First, add previously configured IDEs, marked with āœ…
375
+ if (configuredIdes.length > 0) {
376
+ const configuredGroup = [];
377
+ for (const ideValue of configuredIdes) {
378
+ // Skip empty or invalid IDE values
379
+ if (!ideValue || typeof ideValue !== 'string') {
380
+ continue;
381
+ }
382
+
383
+ // Find the IDE in either preferred or other lists
384
+ const preferredIde = preferredIdes.find((ide) => ide.value === ideValue);
385
+ const otherIde = otherIdes.find((ide) => ide.value === ideValue);
386
+ const ide = preferredIde || otherIde;
387
+
388
+ if (ide) {
389
+ configuredGroup.push({
390
+ label: `${ide.name} āœ…`,
391
+ value: ide.value,
392
+ });
393
+ processedIdes.add(ide.value);
394
+ initialValues.push(ide.value); // Pre-select configured IDEs
395
+ } else {
396
+ // Warn about unrecognized IDE (but don't fail)
397
+ console.log(chalk.yellow(`āš ļø Previously configured IDE '${ideValue}' is no longer available`));
398
+ }
399
+ }
400
+ if (configuredGroup.length > 0) {
401
+ groupedOptions['Previously Configured'] = configuredGroup;
402
+ }
403
+ }
404
+
405
+ // Add preferred tools (excluding already processed)
406
+ const remainingPreferred = preferredIdes.filter((ide) => !processedIdes.has(ide.value));
407
+ if (remainingPreferred.length > 0) {
408
+ groupedOptions['Recommended Tools'] = remainingPreferred.map((ide) => {
409
+ processedIdes.add(ide.value);
410
+ return {
411
+ label: `${ide.name} ⭐`,
412
+ value: ide.value,
413
+ };
414
+ });
415
+ }
416
+
417
+ // Add other tools (excluding already processed)
418
+ const remainingOther = otherIdes.filter((ide) => !processedIdes.has(ide.value));
419
+ if (remainingOther.length > 0) {
420
+ groupedOptions['Additional Tools'] = remainingOther.map((ide) => ({
421
+ label: ide.name,
422
+ value: ide.value,
423
+ }));
424
+ }
425
+
426
+ // Add standalone "None" option at the end
427
+ groupedOptions[' '] = [
428
+ {
429
+ label: '⚠ None - I am not installing any tools',
430
+ value: '__NONE__',
431
+ },
432
+ ];
433
+
434
+ let selectedIdes = [];
435
+
436
+ selectedIdes = await prompts.groupMultiselect({
437
+ message: `Select tools to configure ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`,
438
+ options: groupedOptions,
439
+ initialValues: initialValues.length > 0 ? initialValues : undefined,
440
+ required: true,
441
+ selectableGroups: false,
442
+ });
443
+
444
+ // If user selected both "__NONE__" and other tools, honor the "None" choice
445
+ if (selectedIdes && selectedIdes.includes('__NONE__') && selectedIdes.length > 1) {
446
+ console.log();
447
+ console.log(chalk.yellow('āš ļø "None - I am not installing any tools" was selected, so no tools will be configured.'));
448
+ console.log();
449
+ selectedIdes = [];
450
+ } else if (selectedIdes && selectedIdes.includes('__NONE__')) {
451
+ // Only "__NONE__" was selected
452
+ selectedIdes = [];
453
+ }
454
+
455
+ return {
456
+ ides: selectedIdes || [],
457
+ skipIde: !selectedIdes || selectedIdes.length === 0,
458
+ };
459
+ }
460
+
461
+ /**
462
+ * Prompt for update configuration
463
+ * @returns {Object} Update configuration
464
+ */
465
+ async promptUpdate() {
466
+ const backupFirst = await prompts.confirm({
467
+ message: 'Create backup before updating?',
468
+ default: true,
469
+ });
470
+
471
+ const preserveCustomizations = await prompts.confirm({
472
+ message: 'Preserve local customizations?',
473
+ default: true,
474
+ });
475
+
476
+ return { backupFirst, preserveCustomizations };
477
+ }
478
+
479
+ /**
480
+ * Confirm action
481
+ * @param {string} message - Confirmation message
482
+ * @param {boolean} defaultValue - Default value
483
+ * @returns {boolean} User confirmation
484
+ */
485
+ async confirm(message, defaultValue = false) {
486
+ return await prompts.confirm({
487
+ message,
488
+ default: defaultValue,
489
+ });
490
+ }
491
+
492
+ /**
493
+ * Display installation summary
494
+ * @param {Object} result - Installation result
495
+ */
496
+ showInstallSummary(result) {
497
+ // Clean, simple completion message
498
+ console.log('\n' + chalk.green.bold('✨ BMAD is ready to use!'));
499
+
500
+ // Show installation summary in a simple format
501
+ console.log(chalk.dim(`Installed to: ${result.path}`));
502
+ if (result.modules && result.modules.length > 0) {
503
+ console.log(chalk.dim(`Modules: ${result.modules.join(', ')}`));
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Get confirmed directory from user
509
+ * @returns {string} Confirmed directory path
510
+ */
511
+ async getConfirmedDirectory() {
512
+ let confirmedDirectory = null;
513
+ while (!confirmedDirectory) {
514
+ const directoryAnswer = await this.promptForDirectory();
515
+ await this.displayDirectoryInfo(directoryAnswer.directory);
516
+
517
+ if (await this.confirmDirectory(directoryAnswer.directory)) {
518
+ confirmedDirectory = directoryAnswer.directory;
519
+ }
520
+ }
521
+ return confirmedDirectory;
522
+ }
523
+
524
+ /**
525
+ * Get existing installation info and installed modules
526
+ * @param {string} directory - Installation directory
527
+ * @returns {Object} Object with existingInstall, installedModuleIds, and bmadDir
528
+ */
529
+ async getExistingInstallation(directory) {
530
+ const { Detector } = require('../installers/lib/core/detector');
531
+ const { Installer } = require('../installers/lib/core/installer');
532
+ const detector = new Detector();
533
+ const installer = new Installer();
534
+ const bmadDirResult = await installer.findBmadDir(directory);
535
+ const bmadDir = bmadDirResult.bmadDir;
536
+ const existingInstall = await detector.detect(bmadDir);
537
+ const installedModuleIds = new Set(existingInstall.modules.map((mod) => mod.id));
538
+
539
+ return { existingInstall, installedModuleIds, bmadDir };
540
+ }
541
+
542
+ /**
543
+ * Collect core configuration
544
+ * @param {string} directory - Installation directory
545
+ * @returns {Object} Core configuration
546
+ */
547
+ async collectCoreConfig(directory) {
548
+ const { ConfigCollector } = require('../installers/lib/core/config-collector');
549
+ const configCollector = new ConfigCollector();
550
+ // Load existing configs first if they exist
551
+ await configCollector.loadExistingConfig(directory);
552
+ // Now collect with existing values as defaults (false = don't skip loading, true = skip completion message)
553
+ await configCollector.collectModuleConfig('core', directory, false, true);
554
+
555
+ const coreConfig = configCollector.collectedConfig.core;
556
+ // Ensure we always have a core config object, even if empty
557
+ return coreConfig || {};
558
+ }
559
+
560
+ /**
561
+ * Get module choices for selection
562
+ * @param {Set} installedModuleIds - Currently installed module IDs
563
+ * @param {Object} customContentConfig - Custom content configuration
564
+ * @returns {Array} Module choices for prompt
565
+ */
566
+ async getModuleChoices(installedModuleIds, customContentConfig = null) {
567
+ const moduleChoices = [];
568
+ const isNewInstallation = installedModuleIds.size === 0;
569
+
570
+ const customContentItems = [];
571
+ const hasCustomContentItems = false;
572
+
573
+ // Add custom content items
574
+ if (customContentConfig && customContentConfig.hasCustomContent && customContentConfig.customPath) {
575
+ // Existing installation - show from directory
576
+ const customHandler = new CustomHandler();
577
+ const customFiles = await customHandler.findCustomContent(customContentConfig.customPath);
578
+
579
+ for (const customFile of customFiles) {
580
+ const customInfo = await customHandler.getCustomInfo(customFile);
581
+ if (customInfo) {
582
+ customContentItems.push({
583
+ name: `${chalk.cyan('āœ“')} ${customInfo.name} ${chalk.gray(`(${customInfo.relativePath})`)}`,
584
+ value: `__CUSTOM_CONTENT__${customFile}`, // Unique value for each custom content
585
+ checked: true, // Default to selected since user chose to provide custom content
586
+ path: customInfo.path, // Track path to avoid duplicates
587
+ hint: customInfo.description || undefined,
588
+ });
589
+ }
590
+ }
591
+ }
592
+
593
+ // Add official modules
594
+ const { ModuleManager } = require('../installers/lib/modules/manager');
595
+ const moduleManager = new ModuleManager();
596
+ const { modules: availableModules, customModules: customModulesFromCache } = await moduleManager.listAvailable();
597
+
598
+ // First, add all items to appropriate sections
599
+ const allCustomModules = [];
600
+
601
+ // Add custom content items from directory
602
+ allCustomModules.push(...customContentItems);
603
+
604
+ // Add custom modules from cache
605
+ for (const mod of customModulesFromCache) {
606
+ // Skip if this module is already in customContentItems (by path)
607
+ const isDuplicate = allCustomModules.some((item) => item.path && mod.path && path.resolve(item.path) === path.resolve(mod.path));
608
+
609
+ if (!isDuplicate) {
610
+ allCustomModules.push({
611
+ name: `${chalk.cyan('āœ“')} ${mod.name} ${chalk.gray(`(cached)`)}`,
612
+ value: mod.id,
613
+ checked: isNewInstallation ? mod.defaultSelected || false : installedModuleIds.has(mod.id),
614
+ hint: mod.description || undefined,
615
+ });
616
+ }
617
+ }
618
+
619
+ // Add separators and modules in correct order
620
+ if (allCustomModules.length > 0) {
621
+ // Add separator for custom content, all custom modules, and official content separator
622
+ moduleChoices.push(
623
+ new choiceUtils.Separator('── Custom Content ──'),
624
+ ...allCustomModules,
625
+ new choiceUtils.Separator('── Official Content ──'),
626
+ );
627
+ }
628
+
629
+ // Add official modules (only non-custom ones)
630
+ for (const mod of availableModules) {
631
+ if (!mod.isCustom) {
632
+ moduleChoices.push({
633
+ name: mod.name,
634
+ value: mod.id,
635
+ checked: isNewInstallation ? mod.defaultSelected || false : installedModuleIds.has(mod.id),
636
+ hint: mod.description || undefined,
637
+ });
638
+ }
639
+ }
640
+
641
+ return moduleChoices;
642
+ }
643
+
644
+ /**
645
+ * Prompt for module selection
646
+ * @param {Array} moduleChoices - Available module choices
647
+ * @returns {Array} Selected module IDs
648
+ */
649
+ async selectModules(moduleChoices, defaultSelections = null) {
650
+ // If defaultSelections is provided, use it to override checked state
651
+ // Otherwise preserve the checked state from moduleChoices (set by getModuleChoices)
652
+ const choicesWithDefaults = moduleChoices.map((choice) => ({
653
+ ...choice,
654
+ ...(defaultSelections === null ? {} : { checked: defaultSelections.includes(choice.value) }),
655
+ }));
656
+
657
+ // Add a "None" option at the end for users who changed their mind
658
+ const choicesWithSkipOption = [
659
+ ...choicesWithDefaults,
660
+ {
661
+ value: '__NONE__',
662
+ label: '⚠ None / I changed my mind - skip module installation',
663
+ checked: false,
664
+ },
665
+ ];
666
+
667
+ const selected = await prompts.multiselect({
668
+ message: `Select modules to install ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`,
669
+ choices: choicesWithSkipOption,
670
+ required: true,
671
+ });
672
+
673
+ // If user selected both "__NONE__" and other items, honor the "None" choice
674
+ if (selected && selected.includes('__NONE__') && selected.length > 1) {
675
+ console.log();
676
+ console.log(chalk.yellow('āš ļø "None / I changed my mind" was selected, so no modules will be installed.'));
677
+ console.log();
678
+ return [];
679
+ }
680
+
681
+ // Filter out the special '__NONE__' value
682
+ return selected ? selected.filter((m) => m !== '__NONE__') : [];
683
+ }
684
+
685
+ /**
686
+ * Get external module choices for selection
687
+ * @returns {Array} External module choices for prompt
688
+ */
689
+ async getExternalModuleChoices() {
690
+ const externalManager = new ExternalModuleManager();
691
+ const modules = await externalManager.listAvailable();
692
+
693
+ return modules.map((mod) => ({
694
+ name: mod.name,
695
+ value: mod.code, // Use the code (e.g., 'cis') as the value
696
+ checked: mod.defaultSelected || false,
697
+ hint: mod.description || undefined, // Show description as hint
698
+ module: mod, // Store full module info for later use
699
+ }));
700
+ }
701
+
702
+ /**
703
+ * Prompt for external module selection
704
+ * @param {Array} externalModuleChoices - Available external module choices
705
+ * @param {Array} defaultSelections - Module codes to pre-select
706
+ * @returns {Array} Selected external module codes
707
+ */
708
+ async selectExternalModules(externalModuleChoices, defaultSelections = []) {
709
+ // Build a message showing available modules
710
+ const availableNames = externalModuleChoices.map((c) => c.name).join(', ');
711
+ const message = `Select official BMad modules to install ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`;
712
+
713
+ // Mark choices as checked based on defaultSelections
714
+ const choicesWithDefaults = externalModuleChoices.map((choice) => ({
715
+ ...choice,
716
+ checked: defaultSelections.includes(choice.value),
717
+ }));
718
+
719
+ // Add a "None" option at the end for users who changed their mind
720
+ const choicesWithSkipOption = [
721
+ ...choicesWithDefaults,
722
+ {
723
+ name: '⚠ None / I changed my mind - skip external module installation',
724
+ value: '__NONE__',
725
+ checked: false,
726
+ },
727
+ ];
728
+
729
+ const selected = await prompts.multiselect({
730
+ message,
731
+ choices: choicesWithSkipOption,
732
+ required: true,
733
+ });
734
+
735
+ // If user selected both "__NONE__" and other items, honor the "None" choice
736
+ if (selected && selected.includes('__NONE__') && selected.length > 1) {
737
+ console.log();
738
+ console.log(chalk.yellow('āš ļø "None / I changed my mind" was selected, so no external modules will be installed.'));
739
+ console.log();
740
+ return [];
741
+ }
742
+
743
+ // Filter out the special '__NONE__' value
744
+ return selected ? selected.filter((m) => m !== '__NONE__') : [];
745
+ }
746
+
747
+ /**
748
+ * Select all modules (core + official + community) using grouped multiselect
749
+ * @param {Set} installedModuleIds - Currently installed module IDs
750
+ * @returns {Array} Selected module codes
751
+ */
752
+ async selectAllModules(installedModuleIds = new Set()) {
753
+ const { ModuleManager } = require('../installers/lib/modules/manager');
754
+ const moduleManager = new ModuleManager();
755
+ const { modules: localModules } = await moduleManager.listAvailable();
756
+
757
+ // Get external modules
758
+ const externalManager = new ExternalModuleManager();
759
+ const externalModules = await externalManager.listAvailable();
760
+
761
+ // Build grouped options
762
+ const groupedOptions = {};
763
+ const initialValues = [];
764
+
765
+ // Helper to build module entry with proper sorting and selection
766
+ const buildModuleEntry = (mod, value) => {
767
+ const isInstalled = installedModuleIds.has(value);
768
+ const isDefault = mod.defaultSelected === true;
769
+ return {
770
+ label: mod.description ? `${mod.name} — ${mod.description}` : mod.name,
771
+ value,
772
+ // For sorting: defaultSelected=0, others=1
773
+ sortKey: isDefault ? 0 : 1,
774
+ // Pre-select if default selected OR already installed
775
+ selected: isDefault || isInstalled,
776
+ };
777
+ };
778
+
779
+ // Group 1: BMad Core (BMM, BMB)
780
+ const coreModules = [];
781
+ for (const mod of localModules) {
782
+ if (!mod.isCustom && (mod.id === 'bmm' || mod.id === 'bmb')) {
783
+ const entry = buildModuleEntry(mod, mod.id);
784
+ coreModules.push(entry);
785
+ if (entry.selected) {
786
+ initialValues.push(mod.id);
787
+ }
788
+ }
789
+ }
790
+ // Sort: defaultSelected first, then others
791
+ coreModules.sort((a, b) => a.sortKey - b.sortKey);
792
+ // Remove sortKey from final entries
793
+ if (coreModules.length > 0) {
794
+ groupedOptions['BMad Core'] = coreModules.map(({ label, value }) => ({ label, value }));
795
+ }
796
+
797
+ // Group 2: BMad Official Modules (type: bmad-org)
798
+ const officialModules = [];
799
+ for (const mod of externalModules) {
800
+ if (mod.type === 'bmad-org') {
801
+ const entry = buildModuleEntry(mod, mod.code);
802
+ officialModules.push(entry);
803
+ if (entry.selected) {
804
+ initialValues.push(mod.code);
805
+ }
806
+ }
807
+ }
808
+ officialModules.sort((a, b) => a.sortKey - b.sortKey);
809
+ if (officialModules.length > 0) {
810
+ groupedOptions['BMad Official Modules'] = officialModules.map(({ label, value }) => ({ label, value }));
811
+ }
812
+
813
+ // Group 3: Community Modules (type: community)
814
+ const communityModules = [];
815
+ for (const mod of externalModules) {
816
+ if (mod.type === 'community') {
817
+ const entry = buildModuleEntry(mod, mod.code);
818
+ communityModules.push(entry);
819
+ if (entry.selected) {
820
+ initialValues.push(mod.code);
821
+ }
822
+ }
823
+ }
824
+ communityModules.sort((a, b) => a.sortKey - b.sortKey);
825
+ if (communityModules.length > 0) {
826
+ groupedOptions['Community Modules'] = communityModules.map(({ label, value }) => ({ label, value }));
827
+ }
828
+
829
+ // Add "None" option at the end
830
+ groupedOptions[' '] = [
831
+ {
832
+ label: '⚠ None - Skip module installation',
833
+ value: '__NONE__',
834
+ },
835
+ ];
836
+
837
+ const selected = await prompts.groupMultiselect({
838
+ message: `Select modules to install ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`,
839
+ options: groupedOptions,
840
+ initialValues: initialValues.length > 0 ? initialValues : undefined,
841
+ required: true,
842
+ selectableGroups: false,
843
+ });
844
+
845
+ // If user selected both "__NONE__" and other items, honor the "None" choice
846
+ if (selected && selected.includes('__NONE__') && selected.length > 1) {
847
+ console.log();
848
+ console.log(chalk.yellow('āš ļø "None" was selected, so no modules will be installed.'));
849
+ console.log();
850
+ return [];
851
+ }
852
+
853
+ // Filter out the special '__NONE__' value
854
+ return selected ? selected.filter((m) => m !== '__NONE__') : [];
855
+ }
856
+
857
+ /**
858
+ * Prompt for directory selection
859
+ * @returns {Object} Directory answer from prompt
860
+ */
861
+ async promptForDirectory() {
862
+ // Use sync validation because @clack/prompts doesn't support async validate
863
+ const directory = await prompts.text({
864
+ message: 'Installation directory:',
865
+ default: process.cwd(),
866
+ placeholder: process.cwd(),
867
+ validate: (input) => this.validateDirectorySync(input),
868
+ });
869
+
870
+ // Apply filter logic
871
+ let filteredDir = directory;
872
+ if (!filteredDir || filteredDir.trim() === '') {
873
+ filteredDir = process.cwd();
874
+ } else {
875
+ filteredDir = this.expandUserPath(filteredDir);
876
+ }
877
+
878
+ return { directory: filteredDir };
879
+ }
880
+
881
+ /**
882
+ * Display directory information
883
+ * @param {string} directory - The directory path
884
+ */
885
+ async displayDirectoryInfo(directory) {
886
+ console.log(chalk.cyan('\nResolved installation path:'), chalk.bold(directory));
887
+
888
+ const dirExists = await fs.pathExists(directory);
889
+ if (dirExists) {
890
+ // Show helpful context about the existing path
891
+ const stats = await fs.stat(directory);
892
+ if (stats.isDirectory()) {
893
+ const files = await fs.readdir(directory);
894
+ if (files.length > 0) {
895
+ // Check for any bmad installation (any folder with _config/manifest.yaml)
896
+ const { Installer } = require('../installers/lib/core/installer');
897
+ const installer = new Installer();
898
+ const bmadResult = await installer.findBmadDir(directory);
899
+ const hasBmadInstall =
900
+ (await fs.pathExists(bmadResult.bmadDir)) && (await fs.pathExists(path.join(bmadResult.bmadDir, '_config', 'manifest.yaml')));
901
+
902
+ console.log(
903
+ chalk.gray(`Directory exists and contains ${files.length} item(s)`) +
904
+ (hasBmadInstall ? chalk.yellow(` including existing BMAD installation (${path.basename(bmadResult.bmadDir)})`) : ''),
905
+ );
906
+ } else {
907
+ console.log(chalk.gray('Directory exists and is empty'));
908
+ }
909
+ }
910
+ }
911
+ }
912
+
913
+ /**
914
+ * Confirm directory selection
915
+ * @param {string} directory - The directory path
916
+ * @returns {boolean} Whether user confirmed
917
+ */
918
+ async confirmDirectory(directory) {
919
+ const dirExists = await fs.pathExists(directory);
920
+
921
+ if (dirExists) {
922
+ const proceed = await prompts.confirm({
923
+ message: 'Install to this directory?',
924
+ default: true,
925
+ });
926
+
927
+ if (!proceed) {
928
+ console.log(chalk.yellow("\nLet's try again with a different path.\n"));
929
+ }
930
+
931
+ return proceed;
932
+ } else {
933
+ // Ask for confirmation to create the directory
934
+ const create = await prompts.confirm({
935
+ message: `Create directory: ${directory}?`,
936
+ default: false,
937
+ });
938
+
939
+ if (!create) {
940
+ console.log(chalk.yellow("\nLet's try again with a different path.\n"));
941
+ }
942
+
943
+ return create;
944
+ }
945
+ }
946
+
947
+ /**
948
+ * Validate directory path for installation (sync version for clack prompts)
949
+ * @param {string} input - User input path
950
+ * @returns {string|undefined} Error message or undefined if valid
951
+ */
952
+ validateDirectorySync(input) {
953
+ // Allow empty input to use the default
954
+ if (!input || input.trim() === '') {
955
+ return; // Empty means use default, undefined = valid for clack
956
+ }
957
+
958
+ let expandedPath;
959
+ try {
960
+ expandedPath = this.expandUserPath(input.trim());
961
+ } catch (error) {
962
+ return error.message;
963
+ }
964
+
965
+ // Check if the path exists
966
+ const pathExists = fs.pathExistsSync(expandedPath);
967
+
968
+ if (!pathExists) {
969
+ // Find the first existing parent directory
970
+ const existingParent = this.findExistingParentSync(expandedPath);
971
+
972
+ if (!existingParent) {
973
+ return 'Cannot create directory: no existing parent directory found';
974
+ }
975
+
976
+ // Check if the existing parent is writable
977
+ try {
978
+ fs.accessSync(existingParent, fs.constants.W_OK);
979
+ // Path doesn't exist but can be created - will prompt for confirmation later
980
+ return;
981
+ } catch {
982
+ // Provide a detailed error message explaining both issues
983
+ return `Directory '${expandedPath}' does not exist and cannot be created: parent directory '${existingParent}' is not writable`;
984
+ }
985
+ }
986
+
987
+ // If it exists, validate it's a directory and writable
988
+ const stat = fs.statSync(expandedPath);
989
+ if (!stat.isDirectory()) {
990
+ return `Path exists but is not a directory: ${expandedPath}`;
991
+ }
992
+
993
+ // Check write permissions
994
+ try {
995
+ fs.accessSync(expandedPath, fs.constants.W_OK);
996
+ } catch {
997
+ return `Directory is not writable: ${expandedPath}`;
998
+ }
999
+
1000
+ return;
1001
+ }
1002
+
1003
+ /**
1004
+ * Validate directory path for installation (async version)
1005
+ * @param {string} input - User input path
1006
+ * @returns {string|true} Error message or true if valid
1007
+ */
1008
+ async validateDirectory(input) {
1009
+ // Allow empty input to use the default
1010
+ if (!input || input.trim() === '') {
1011
+ return true; // Empty means use default
1012
+ }
1013
+
1014
+ let expandedPath;
1015
+ try {
1016
+ expandedPath = this.expandUserPath(input.trim());
1017
+ } catch (error) {
1018
+ return error.message;
1019
+ }
1020
+
1021
+ // Check if the path exists
1022
+ const pathExists = await fs.pathExists(expandedPath);
1023
+
1024
+ if (!pathExists) {
1025
+ // Find the first existing parent directory
1026
+ const existingParent = await this.findExistingParent(expandedPath);
1027
+
1028
+ if (!existingParent) {
1029
+ return 'Cannot create directory: no existing parent directory found';
1030
+ }
1031
+
1032
+ // Check if the existing parent is writable
1033
+ try {
1034
+ await fs.access(existingParent, fs.constants.W_OK);
1035
+ // Path doesn't exist but can be created - will prompt for confirmation later
1036
+ return true;
1037
+ } catch {
1038
+ // Provide a detailed error message explaining both issues
1039
+ return `Directory '${expandedPath}' does not exist and cannot be created: parent directory '${existingParent}' is not writable`;
1040
+ }
1041
+ }
1042
+
1043
+ // If it exists, validate it's a directory and writable
1044
+ const stat = await fs.stat(expandedPath);
1045
+ if (!stat.isDirectory()) {
1046
+ return `Path exists but is not a directory: ${expandedPath}`;
1047
+ }
1048
+
1049
+ // Check write permissions
1050
+ try {
1051
+ await fs.access(expandedPath, fs.constants.W_OK);
1052
+ } catch {
1053
+ return `Directory is not writable: ${expandedPath}`;
1054
+ }
1055
+
1056
+ return true;
1057
+ }
1058
+
1059
+ /**
1060
+ * Find the first existing parent directory (sync version)
1061
+ * @param {string} targetPath - The path to check
1062
+ * @returns {string|null} The first existing parent directory, or null if none found
1063
+ */
1064
+ findExistingParentSync(targetPath) {
1065
+ let currentPath = path.resolve(targetPath);
1066
+
1067
+ // Walk up the directory tree until we find an existing directory
1068
+ while (currentPath !== path.dirname(currentPath)) {
1069
+ // Stop at root
1070
+ const parent = path.dirname(currentPath);
1071
+ if (fs.pathExistsSync(parent)) {
1072
+ return parent;
1073
+ }
1074
+ currentPath = parent;
1075
+ }
1076
+
1077
+ return null; // No existing parent found (shouldn't happen in practice)
1078
+ }
1079
+
1080
+ /**
1081
+ * Find the first existing parent directory (async version)
1082
+ * @param {string} targetPath - The path to check
1083
+ * @returns {string|null} The first existing parent directory, or null if none found
1084
+ */
1085
+ async findExistingParent(targetPath) {
1086
+ let currentPath = path.resolve(targetPath);
1087
+
1088
+ // Walk up the directory tree until we find an existing directory
1089
+ while (currentPath !== path.dirname(currentPath)) {
1090
+ // Stop at root
1091
+ const parent = path.dirname(currentPath);
1092
+ if (await fs.pathExists(parent)) {
1093
+ return parent;
1094
+ }
1095
+ currentPath = parent;
1096
+ }
1097
+
1098
+ return null; // No existing parent found (shouldn't happen in practice)
1099
+ }
1100
+
1101
+ /**
1102
+ * Expands the user-provided path: handles ~ and resolves to absolute.
1103
+ * @param {string} inputPath - User input path.
1104
+ * @returns {string} Absolute expanded path.
1105
+ */
1106
+ expandUserPath(inputPath) {
1107
+ if (typeof inputPath !== 'string') {
1108
+ throw new TypeError('Path must be a string.');
1109
+ }
1110
+
1111
+ let expanded = inputPath.trim();
1112
+
1113
+ // Handle tilde expansion
1114
+ if (expanded.startsWith('~')) {
1115
+ if (expanded === '~') {
1116
+ expanded = os.homedir();
1117
+ } else if (expanded.startsWith('~' + path.sep)) {
1118
+ const pathAfterHome = expanded.slice(2); // Remove ~/ or ~\
1119
+ expanded = path.join(os.homedir(), pathAfterHome);
1120
+ } else {
1121
+ const restOfPath = expanded.slice(1);
1122
+ const separatorIndex = restOfPath.indexOf(path.sep);
1123
+ const username = separatorIndex === -1 ? restOfPath : restOfPath.slice(0, separatorIndex);
1124
+ if (username) {
1125
+ throw new Error(`Path expansion for ~${username} is not supported. Please use an absolute path or ~${path.sep}`);
1126
+ }
1127
+ }
1128
+ }
1129
+
1130
+ // Resolve to the absolute path relative to the current working directory
1131
+ return path.resolve(expanded);
1132
+ }
1133
+
1134
+ /**
1135
+ * Load existing configurations to use as defaults
1136
+ * @param {string} directory - Installation directory
1137
+ * @returns {Object} Existing configurations
1138
+ */
1139
+ async loadExistingConfigurations(directory) {
1140
+ const configs = {
1141
+ hasCustomContent: false,
1142
+ coreConfig: {},
1143
+ ideConfig: { ides: [], skipIde: false },
1144
+ };
1145
+
1146
+ try {
1147
+ // Load core config
1148
+ configs.coreConfig = await this.collectCoreConfig(directory);
1149
+
1150
+ // Load IDE configuration
1151
+ const configuredIdes = await this.getConfiguredIdes(directory);
1152
+ if (configuredIdes.length > 0) {
1153
+ configs.ideConfig.ides = configuredIdes;
1154
+ configs.ideConfig.skipIde = false;
1155
+ }
1156
+
1157
+ return configs;
1158
+ } catch {
1159
+ // If loading fails, return empty configs
1160
+ console.warn('Warning: Could not load existing configurations');
1161
+ return configs;
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * Get configured IDEs from existing installation
1167
+ * @param {string} directory - Installation directory
1168
+ * @returns {Array} List of configured IDEs
1169
+ */
1170
+ async getConfiguredIdes(directory) {
1171
+ const { Detector } = require('../installers/lib/core/detector');
1172
+ const { Installer } = require('../installers/lib/core/installer');
1173
+ const detector = new Detector();
1174
+ const installer = new Installer();
1175
+ const bmadResult = await installer.findBmadDir(directory);
1176
+ const existingInstall = await detector.detect(bmadResult.bmadDir);
1177
+ return existingInstall.ides || [];
1178
+ }
1179
+
1180
+ /**
1181
+ * Validate custom content path synchronously
1182
+ * @param {string} input - User input path
1183
+ * @returns {string|undefined} Error message or undefined if valid
1184
+ */
1185
+ validateCustomContentPathSync(input) {
1186
+ // Allow empty input to cancel
1187
+ if (!input || input.trim() === '') {
1188
+ return; // Allow empty to exit
1189
+ }
1190
+
1191
+ try {
1192
+ // Expand the path
1193
+ const expandedPath = this.expandUserPath(input.trim());
1194
+
1195
+ // Check if path exists
1196
+ if (!fs.pathExistsSync(expandedPath)) {
1197
+ return 'Path does not exist';
1198
+ }
1199
+
1200
+ // Check if it's a directory
1201
+ const stat = fs.statSync(expandedPath);
1202
+ if (!stat.isDirectory()) {
1203
+ return 'Path must be a directory';
1204
+ }
1205
+
1206
+ // Check for module.yaml in the root
1207
+ const moduleYamlPath = path.join(expandedPath, 'module.yaml');
1208
+ if (!fs.pathExistsSync(moduleYamlPath)) {
1209
+ return 'Directory must contain a module.yaml file in the root';
1210
+ }
1211
+
1212
+ // Try to parse the module.yaml to get the module ID
1213
+ try {
1214
+ const yaml = require('yaml');
1215
+ const content = fs.readFileSync(moduleYamlPath, 'utf8');
1216
+ const moduleData = yaml.parse(content);
1217
+ if (!moduleData.code) {
1218
+ return 'module.yaml must contain a "code" field for the module ID';
1219
+ }
1220
+ } catch (error) {
1221
+ return 'Invalid module.yaml file: ' + error.message;
1222
+ }
1223
+
1224
+ return; // Valid
1225
+ } catch (error) {
1226
+ return 'Error validating path: ' + error.message;
1227
+ }
1228
+ }
1229
+
1230
+ /**
1231
+ * Prompt user for custom content source location
1232
+ * @returns {Object} Custom content configuration
1233
+ */
1234
+ async promptCustomContentSource() {
1235
+ const customContentConfig = { hasCustomContent: true, sources: [] };
1236
+
1237
+ // Keep asking for more sources until user is done
1238
+ while (true) {
1239
+ // First ask if user wants to add another module or continue
1240
+ if (customContentConfig.sources.length > 0) {
1241
+ const action = await prompts.select({
1242
+ message: 'Would you like to:',
1243
+ choices: [
1244
+ { name: 'Add another custom module', value: 'add' },
1245
+ { name: 'Continue with installation', value: 'continue' },
1246
+ ],
1247
+ default: 'continue',
1248
+ });
1249
+
1250
+ if (action === 'continue') {
1251
+ break;
1252
+ }
1253
+ }
1254
+
1255
+ let sourcePath;
1256
+ let isValid = false;
1257
+
1258
+ while (!isValid) {
1259
+ // Use sync validation because @clack/prompts doesn't support async validate
1260
+ const inputPath = await prompts.text({
1261
+ message: 'Path to custom module folder (press Enter to skip):',
1262
+ validate: (input) => this.validateCustomContentPathSync(input),
1263
+ });
1264
+
1265
+ // If user pressed Enter without typing anything, exit the loop
1266
+ if (!inputPath || inputPath.trim() === '') {
1267
+ // If we have no modules yet, return false for no custom content
1268
+ if (customContentConfig.sources.length === 0) {
1269
+ return { hasCustomContent: false };
1270
+ }
1271
+ return customContentConfig;
1272
+ }
1273
+
1274
+ sourcePath = this.expandUserPath(inputPath);
1275
+ isValid = true;
1276
+ }
1277
+
1278
+ // Read module.yaml to get module info
1279
+ const yaml = require('yaml');
1280
+ const moduleYamlPath = path.join(sourcePath, 'module.yaml');
1281
+ const moduleContent = await fs.readFile(moduleYamlPath, 'utf8');
1282
+ const moduleData = yaml.parse(moduleContent);
1283
+
1284
+ // Add to sources
1285
+ customContentConfig.sources.push({
1286
+ path: sourcePath,
1287
+ id: moduleData.code,
1288
+ name: moduleData.name || moduleData.code,
1289
+ });
1290
+
1291
+ console.log(chalk.green(`āœ“ Confirmed local custom module: ${moduleData.name || moduleData.code}`));
1292
+ }
1293
+
1294
+ // Ask if user wants to add these to the installation
1295
+ const shouldInstall = await prompts.confirm({
1296
+ message: `Install these ${customContentConfig.sources.length} custom modules?`,
1297
+ default: true,
1298
+ });
1299
+
1300
+ if (shouldInstall) {
1301
+ customContentConfig.selected = true;
1302
+ // Store paths to module.yaml files, not directories
1303
+ customContentConfig.selectedFiles = customContentConfig.sources.map((s) => path.join(s.path, 'module.yaml'));
1304
+ // Also include module IDs for installation
1305
+ customContentConfig.selectedModuleIds = customContentConfig.sources.map((s) => s.id);
1306
+ }
1307
+
1308
+ return customContentConfig;
1309
+ }
1310
+
1311
+ /**
1312
+ * Handle custom modules in the modify flow
1313
+ * @param {string} directory - Installation directory
1314
+ * @param {Array} selectedModules - Currently selected modules
1315
+ * @returns {Object} Result with selected custom modules and custom content config
1316
+ */
1317
+ async handleCustomModulesInModifyFlow(directory, selectedModules) {
1318
+ // Get existing installation to find custom modules
1319
+ const { existingInstall } = await this.getExistingInstallation(directory);
1320
+
1321
+ // Check if there are any custom modules in cache
1322
+ const { Installer } = require('../installers/lib/core/installer');
1323
+ const installer = new Installer();
1324
+ const { bmadDir } = await installer.findBmadDir(directory);
1325
+
1326
+ const cacheDir = path.join(bmadDir, '_config', 'custom');
1327
+ const cachedCustomModules = [];
1328
+
1329
+ if (await fs.pathExists(cacheDir)) {
1330
+ const entries = await fs.readdir(cacheDir, { withFileTypes: true });
1331
+ for (const entry of entries) {
1332
+ if (entry.isDirectory()) {
1333
+ const moduleYamlPath = path.join(cacheDir, entry.name, 'module.yaml');
1334
+ if (await fs.pathExists(moduleYamlPath)) {
1335
+ const yaml = require('yaml');
1336
+ const content = await fs.readFile(moduleYamlPath, 'utf8');
1337
+ const moduleData = yaml.parse(content);
1338
+
1339
+ cachedCustomModules.push({
1340
+ id: entry.name,
1341
+ name: moduleData.name || entry.name,
1342
+ description: moduleData.description || 'Custom module from cache',
1343
+ checked: selectedModules.includes(entry.name),
1344
+ fromCache: true,
1345
+ });
1346
+ }
1347
+ }
1348
+ }
1349
+ }
1350
+
1351
+ const result = {
1352
+ selectedCustomModules: [],
1353
+ customContentConfig: { hasCustomContent: false },
1354
+ };
1355
+
1356
+ // Ask user about custom modules
1357
+ console.log(chalk.cyan('\nāš™ļø Custom Modules'));
1358
+ if (cachedCustomModules.length > 0) {
1359
+ console.log(chalk.dim('Found custom modules in your installation:'));
1360
+ } else {
1361
+ console.log(chalk.dim('No custom modules currently installed.'));
1362
+ }
1363
+
1364
+ // Build choices dynamically based on whether we have existing modules
1365
+ const choices = [];
1366
+ if (cachedCustomModules.length > 0) {
1367
+ choices.push(
1368
+ { name: 'Keep all existing custom modules', value: 'keep' },
1369
+ { name: 'Select which custom modules to keep', value: 'select' },
1370
+ { name: 'Add new custom modules', value: 'add' },
1371
+ { name: 'Remove all custom modules', value: 'remove' },
1372
+ );
1373
+ } else {
1374
+ choices.push({ name: 'Add new custom modules', value: 'add' }, { name: 'Cancel (no custom modules)', value: 'cancel' });
1375
+ }
1376
+
1377
+ const customAction = await prompts.select({
1378
+ message: cachedCustomModules.length > 0 ? 'Manage custom modules?' : 'Add custom modules?',
1379
+ choices: choices,
1380
+ default: cachedCustomModules.length > 0 ? 'keep' : 'add',
1381
+ });
1382
+
1383
+ switch (customAction) {
1384
+ case 'keep': {
1385
+ // Keep all existing custom modules
1386
+ result.selectedCustomModules = cachedCustomModules.map((m) => m.id);
1387
+ console.log(chalk.dim(`Keeping ${result.selectedCustomModules.length} custom module(s)`));
1388
+ break;
1389
+ }
1390
+
1391
+ case 'select': {
1392
+ // Let user choose which to keep
1393
+ const selectChoices = cachedCustomModules.map((m) => ({
1394
+ name: `${m.name} ${chalk.gray(`(${m.id})`)}`,
1395
+ value: m.id,
1396
+ checked: m.checked,
1397
+ }));
1398
+
1399
+ // Add "None / I changed my mind" option at the end
1400
+ const choicesWithSkip = [
1401
+ ...selectChoices,
1402
+ {
1403
+ name: '⚠ None / I changed my mind - keep no custom modules',
1404
+ value: '__NONE__',
1405
+ checked: false,
1406
+ },
1407
+ ];
1408
+
1409
+ const keepModules = await prompts.multiselect({
1410
+ message: `Select custom modules to keep ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`,
1411
+ choices: choicesWithSkip,
1412
+ required: true,
1413
+ });
1414
+
1415
+ // If user selected both "__NONE__" and other modules, honor the "None" choice
1416
+ if (keepModules && keepModules.includes('__NONE__') && keepModules.length > 1) {
1417
+ console.log();
1418
+ console.log(chalk.yellow('āš ļø "None / I changed my mind" was selected, so no custom modules will be kept.'));
1419
+ console.log();
1420
+ result.selectedCustomModules = [];
1421
+ } else {
1422
+ // Filter out the special '__NONE__' value
1423
+ result.selectedCustomModules = keepModules ? keepModules.filter((m) => m !== '__NONE__') : [];
1424
+ }
1425
+ break;
1426
+ }
1427
+
1428
+ case 'add': {
1429
+ // By default, keep existing modules when adding new ones
1430
+ // User chose "Add new" not "Replace", so we assume they want to keep existing
1431
+ result.selectedCustomModules = cachedCustomModules.map((m) => m.id);
1432
+
1433
+ // Then prompt for new ones (reuse existing method)
1434
+ const newCustomContent = await this.promptCustomContentSource();
1435
+ if (newCustomContent.hasCustomContent && newCustomContent.selected) {
1436
+ result.selectedCustomModules.push(...newCustomContent.selectedModuleIds);
1437
+ result.customContentConfig = newCustomContent;
1438
+ }
1439
+ break;
1440
+ }
1441
+
1442
+ case 'remove': {
1443
+ // Remove all custom modules
1444
+ console.log(chalk.yellow('All custom modules will be removed from the installation'));
1445
+ break;
1446
+ }
1447
+
1448
+ case 'cancel': {
1449
+ // User cancelled - no custom modules
1450
+ console.log(chalk.dim('No custom modules will be added'));
1451
+ break;
1452
+ }
1453
+ }
1454
+
1455
+ return result;
1456
+ }
1457
+
1458
+ /**
1459
+ * Parse alpha version string (e.g., "6.0.0-Alpha.20")
1460
+ * @param {string} version - Version string
1461
+ * @returns {Object|null} Object with alphaNumber and fullVersion, or null if invalid
1462
+ */
1463
+ parseAlphaVersion(version) {
1464
+ if (!version || version === 'unknown') {
1465
+ return null;
1466
+ }
1467
+
1468
+ // Remove 'v' prefix if present
1469
+ const cleanVersion = version.toString().replace(/^v/i, '');
1470
+
1471
+ // Match alpha version pattern: X.Y.Z-Alpha.N (case-insensitive)
1472
+ const match = cleanVersion.match(/[\d.]+-Alpha\.(\d+)/i);
1473
+
1474
+ if (!match) {
1475
+ return null;
1476
+ }
1477
+
1478
+ return {
1479
+ alphaNumber: parseInt(match[1], 10),
1480
+ fullVersion: cleanVersion,
1481
+ };
1482
+ }
1483
+
1484
+ /**
1485
+ * Check if installed version is more than 2 alpha versions behind current
1486
+ * @param {string} installedVersion - The installed version
1487
+ * @param {string} currentVersion - The current version
1488
+ * @returns {Object} Object with { isOldVersion, versionDiff, shouldWarn, installed, current }
1489
+ */
1490
+ checkAlphaVersionAge(installedVersion, currentVersion) {
1491
+ const installed = this.parseAlphaVersion(installedVersion);
1492
+ const current = this.parseAlphaVersion(currentVersion);
1493
+
1494
+ // If we can't parse either version, don't warn
1495
+ if (!installed || !current) {
1496
+ return { isOldVersion: false, versionDiff: 0, shouldWarn: false };
1497
+ }
1498
+
1499
+ // Calculate alpha version difference
1500
+ const versionDiff = current.alphaNumber - installed.alphaNumber;
1501
+
1502
+ // Consider it old if more than 2 versions behind
1503
+ const isOldVersion = versionDiff > 2;
1504
+
1505
+ return {
1506
+ isOldVersion,
1507
+ versionDiff,
1508
+ shouldWarn: isOldVersion,
1509
+ installed: installed.fullVersion,
1510
+ current: current.fullVersion,
1511
+ installedAlpha: installed.alphaNumber,
1512
+ currentAlpha: current.alphaNumber,
1513
+ };
1514
+ }
1515
+
1516
+ /**
1517
+ * Show warning for old alpha version and ask if user wants to proceed
1518
+ * @param {string} installedVersion - The installed version
1519
+ * @param {string} currentVersion - The current version
1520
+ * @param {string} bmadFolderName - Name of the BMAD folder
1521
+ * @returns {Promise<boolean>} True if user wants to proceed, false if they cancel
1522
+ */
1523
+ async showOldAlphaVersionWarning(installedVersion, currentVersion, bmadFolderName) {
1524
+ const versionInfo = this.checkAlphaVersionAge(installedVersion, currentVersion);
1525
+
1526
+ // Also warn if version is unknown or can't be parsed (legacy/unsupported)
1527
+ const isUnknownVersion = installedVersion === 'unknown' || !versionInfo.installed;
1528
+
1529
+ if (!versionInfo.shouldWarn && !isUnknownVersion) {
1530
+ return true; // Not old, proceed
1531
+ }
1532
+
1533
+ console.log('');
1534
+ console.log(chalk.yellow.bold('āš ļø VERSION WARNING'));
1535
+ console.log(chalk.yellow('─'.repeat(80)));
1536
+
1537
+ if (isUnknownVersion) {
1538
+ console.log(chalk.yellow('Unable to detect your installed BMAD version.'));
1539
+ console.log(chalk.yellow('This appears to be a legacy or unsupported installation.'));
1540
+ console.log('');
1541
+ console.log(chalk.dim('For stability, we only support updates from the previous 2 alpha versions.'));
1542
+ console.log(chalk.dim('Legacy installations may have compatibility issues.'));
1543
+ } else {
1544
+ console.log(chalk.yellow(`You are updating from ${versionInfo.installed} to ${versionInfo.current}.`));
1545
+ console.log(chalk.yellow(`This is ${versionInfo.versionDiff} alpha versions behind.`));
1546
+ console.log('');
1547
+ console.log(chalk.dim(`For stability, we only support updates from the previous 2 alpha versions`));
1548
+ console.log(chalk.dim(`(Alpha.${versionInfo.currentAlpha - 2} through Alpha.${versionInfo.currentAlpha - 1}).`));
1549
+ }
1550
+
1551
+ console.log('');
1552
+ console.log(chalk.dim('For the best experience, we recommend:'));
1553
+ console.log(chalk.dim(' 1. Delete your current BMAD installation folder'));
1554
+ console.log(chalk.dim(` (the "${bmadFolderName}/" folder in your project)`));
1555
+ console.log(chalk.dim(' 2. Run a fresh installation'));
1556
+ console.log('');
1557
+ console.log(chalk.dim('Benefits of a fresh install:'));
1558
+ console.log(chalk.dim(' • Cleaner configuration without legacy artifacts'));
1559
+ console.log(chalk.dim(' • All new features properly configured'));
1560
+ console.log(chalk.dim(' • Fewer potential conflicts'));
1561
+ console.log(chalk.yellow('─'.repeat(80)));
1562
+ console.log('');
1563
+
1564
+ const proceed = await prompts.select({
1565
+ message: 'How would you like to proceed?',
1566
+ choices: [
1567
+ {
1568
+ name: 'Proceed with update anyway (may have issues)',
1569
+ value: 'proceed',
1570
+ },
1571
+ {
1572
+ name: 'Cancel (recommended - do a fresh install instead)',
1573
+ value: 'cancel',
1574
+ },
1575
+ ],
1576
+ default: 'cancel',
1577
+ });
1578
+
1579
+ if (proceed === 'cancel') {
1580
+ console.log('');
1581
+ console.log(chalk.cyan('To do a fresh install:'));
1582
+ console.log(chalk.dim(` 1. Delete the "${bmadFolderName}/" folder in your project`));
1583
+ console.log(chalk.dim(" 2. Run 'bmad install' again"));
1584
+ console.log('');
1585
+ }
1586
+
1587
+ return proceed === 'proceed';
1588
+ }
1589
+
1590
+ /**
1591
+ * Display module versions with update availability
1592
+ * @param {Array} modules - Array of module info objects with version info
1593
+ * @param {Array} availableUpdates - Array of available updates
1594
+ */
1595
+ displayModuleVersions(modules, availableUpdates = []) {
1596
+ console.log('');
1597
+ console.log(chalk.cyan.bold('šŸ“¦ Module Versions'));
1598
+ console.log(chalk.gray('─'.repeat(80)));
1599
+
1600
+ // Group modules by source
1601
+ const builtIn = modules.filter((m) => m.source === 'built-in');
1602
+ const external = modules.filter((m) => m.source === 'external');
1603
+ const custom = modules.filter((m) => m.source === 'custom');
1604
+ const unknown = modules.filter((m) => m.source === 'unknown');
1605
+
1606
+ const displayGroup = (group, title) => {
1607
+ if (group.length === 0) return;
1608
+
1609
+ console.log(chalk.yellow(`\n${title}`));
1610
+ for (const module of group) {
1611
+ const updateInfo = availableUpdates.find((u) => u.name === module.name);
1612
+ const versionDisplay = module.version || chalk.gray('unknown');
1613
+
1614
+ if (updateInfo) {
1615
+ console.log(
1616
+ ` ${chalk.cyan(module.name.padEnd(20))} ${versionDisplay} → ${chalk.green(updateInfo.latestVersion)} ${chalk.green('↑')}`,
1617
+ );
1618
+ } else {
1619
+ console.log(` ${chalk.cyan(module.name.padEnd(20))} ${versionDisplay} ${chalk.gray('āœ“')}`);
1620
+ }
1621
+ }
1622
+ };
1623
+
1624
+ displayGroup(builtIn, 'Built-in Modules');
1625
+ displayGroup(external, 'External Modules (Official)');
1626
+ displayGroup(custom, 'Custom Modules');
1627
+ displayGroup(unknown, 'Other Modules');
1628
+
1629
+ console.log('');
1630
+ }
1631
+
1632
+ /**
1633
+ * Prompt user to select which modules to update
1634
+ * @param {Array} availableUpdates - Array of available updates
1635
+ * @returns {Array} Selected module names to update
1636
+ */
1637
+ async promptUpdateSelection(availableUpdates) {
1638
+ if (availableUpdates.length === 0) {
1639
+ return [];
1640
+ }
1641
+
1642
+ console.log('');
1643
+ console.log(chalk.cyan.bold('šŸ”„ Available Updates'));
1644
+ console.log(chalk.gray('─'.repeat(80)));
1645
+
1646
+ const choices = availableUpdates.map((update) => ({
1647
+ name: `${update.name} ${chalk.dim(`(v${update.installedVersion} → v${update.latestVersion})`)}`,
1648
+ value: update.name,
1649
+ checked: true, // Default to selecting all updates
1650
+ }));
1651
+
1652
+ // Add "Update All" and "Cancel" options
1653
+ const action = await prompts.select({
1654
+ message: 'How would you like to proceed?',
1655
+ choices: [
1656
+ { name: 'Update all available modules', value: 'all' },
1657
+ { name: 'Select specific modules to update', value: 'select' },
1658
+ { name: 'Skip updates for now', value: 'skip' },
1659
+ ],
1660
+ default: 'all',
1661
+ });
1662
+
1663
+ if (action === 'all') {
1664
+ return availableUpdates.map((u) => u.name);
1665
+ }
1666
+
1667
+ if (action === 'skip') {
1668
+ return [];
1669
+ }
1670
+
1671
+ // Allow specific selection
1672
+ const selected = await prompts.multiselect({
1673
+ message: `Select modules to update ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`,
1674
+ choices: choices,
1675
+ required: true,
1676
+ });
1677
+
1678
+ return selected || [];
1679
+ }
1680
+
1681
+ /**
1682
+ * Display status of all installed modules
1683
+ * @param {Object} statusData - Status data with modules, installation info, and available updates
1684
+ */
1685
+ displayStatus(statusData) {
1686
+ const { installation, modules, availableUpdates, bmadDir } = statusData;
1687
+
1688
+ console.log('');
1689
+ console.log(chalk.cyan.bold('šŸ“‹ BMAD Status'));
1690
+ console.log(chalk.gray('─'.repeat(80)));
1691
+
1692
+ // Installation info
1693
+ console.log(chalk.yellow('\nInstallation'));
1694
+ console.log(` ${chalk.gray('Version:'.padEnd(20))} ${installation.version || chalk.gray('unknown')}`);
1695
+ console.log(` ${chalk.gray('Location:'.padEnd(20))} ${bmadDir}`);
1696
+ console.log(` ${chalk.gray('Installed:'.padEnd(20))} ${new Date(installation.installDate).toLocaleDateString()}`);
1697
+ console.log(
1698
+ ` ${chalk.gray('Last Updated:'.padEnd(20))} ${installation.lastUpdated ? new Date(installation.lastUpdated).toLocaleDateString() : chalk.gray('unknown')}`,
1699
+ );
1700
+
1701
+ // Module versions
1702
+ this.displayModuleVersions(modules, availableUpdates);
1703
+
1704
+ // Update summary
1705
+ if (availableUpdates.length > 0) {
1706
+ console.log(chalk.yellow.bold(`\nāš ļø ${availableUpdates.length} update(s) available`));
1707
+ console.log(chalk.dim(` Run 'bmad install' and select "Quick Update" to update`));
1708
+ } else {
1709
+ console.log(chalk.green.bold('\nāœ“ All modules are up to date'));
1710
+ }
1711
+
1712
+ console.log('');
1713
+ }
1714
+ }
1715
+
1716
+ module.exports = { UI };