bmad-fh 6.0.0-alpha.23

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 (626) hide show
  1. package/.coderabbit.yaml +40 -0
  2. package/.githooks/post-checkout +129 -0
  3. package/.githooks/pre-commit +63 -0
  4. package/.githooks/pre-push +135 -0
  5. package/.github/CODE_OF_CONDUCT.md +128 -0
  6. package/.github/FUNDING.yaml +15 -0
  7. package/.github/ISSUE_TEMPLATE/config.yaml +8 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  9. package/.github/ISSUE_TEMPLATE/issue.md +32 -0
  10. package/.github/scripts/discord-helpers.sh +34 -0
  11. package/.github/workflows/bundle-latest.yaml +330 -0
  12. package/.github/workflows/discord.yaml +90 -0
  13. package/.github/workflows/docs.yaml +63 -0
  14. package/.github/workflows/manual-release.yaml +190 -0
  15. package/.github/workflows/publish-multi-artifact.yaml +50 -0
  16. package/.github/workflows/quality.yaml +115 -0
  17. package/.husky/pre-commit +20 -0
  18. package/.markdownlint-cli2.yaml +41 -0
  19. package/.nvmrc +1 -0
  20. package/.prettierignore +9 -0
  21. package/.vscode/settings.json +97 -0
  22. package/CHANGELOG.md +1394 -0
  23. package/CNAME +1 -0
  24. package/CONTRIBUTING.md +306 -0
  25. package/CONTRIBUTORS.md +32 -0
  26. package/LICENSE +30 -0
  27. package/README.md +126 -0
  28. package/SECURITY.md +85 -0
  29. package/TRADEMARK.md +55 -0
  30. package/Wordmark.png +0 -0
  31. package/banner-bmad-method.png +0 -0
  32. package/docs/404.md +9 -0
  33. package/docs/_README_WORKFLOW_DIAGRAMS.md +40 -0
  34. package/docs/_STYLE_GUIDE.md +367 -0
  35. package/docs/_archive/customize-workflows.md +30 -0
  36. package/docs/_archive/getting-started-bmadv4.md +247 -0
  37. package/docs/_archive/vendor-workflows.md +52 -0
  38. package/docs/downloads.md +72 -0
  39. package/docs/explanation/agents/barry-quick-flow.md +328 -0
  40. package/docs/explanation/agents/index.md +19 -0
  41. package/docs/explanation/architecture/four-phases.md +107 -0
  42. package/docs/explanation/architecture/preventing-agent-conflicts.md +111 -0
  43. package/docs/explanation/architecture/why-solutioning-matters.md +75 -0
  44. package/docs/explanation/bmm/index.md +131 -0
  45. package/docs/explanation/core/index.md +18 -0
  46. package/docs/explanation/core-concepts/agent-roles.md +179 -0
  47. package/docs/explanation/core-concepts/index.md +35 -0
  48. package/docs/explanation/core-concepts/what-are-agents.md +97 -0
  49. package/docs/explanation/core-concepts/what-are-modules.md +85 -0
  50. package/docs/explanation/core-concepts/what-are-workflows.md +204 -0
  51. package/docs/explanation/faq/brownfield-faq.md +73 -0
  52. package/docs/explanation/faq/getting-started-faq.md +67 -0
  53. package/docs/explanation/faq/implementation-faq.md +52 -0
  54. package/docs/explanation/faq/index.md +16 -0
  55. package/docs/explanation/faq/levels-and-tracks-faq.md +52 -0
  56. package/docs/explanation/faq/planning-faq.md +41 -0
  57. package/docs/explanation/faq/tools-faq.md +277 -0
  58. package/docs/explanation/faq/workflows-faq.md +61 -0
  59. package/docs/explanation/features/advanced-elicitation.md +95 -0
  60. package/docs/explanation/features/brainstorming-techniques.md +92 -0
  61. package/docs/explanation/features/party-mode.md +95 -0
  62. package/docs/explanation/features/quick-flow.md +149 -0
  63. package/docs/explanation/features/tea-overview.md +410 -0
  64. package/docs/explanation/features/web-bundles.md +34 -0
  65. package/docs/explanation/philosophy/facilitation-over-generation.md +333 -0
  66. package/docs/explanation/philosophy/testing-as-engineering.md +112 -0
  67. package/docs/explanation/tea/engagement-models.md +710 -0
  68. package/docs/explanation/tea/fixture-architecture.md +457 -0
  69. package/docs/explanation/tea/knowledge-base-system.md +554 -0
  70. package/docs/explanation/tea/network-first-patterns.md +853 -0
  71. package/docs/explanation/tea/risk-based-testing.md +586 -0
  72. package/docs/explanation/tea/test-quality-standards.md +907 -0
  73. package/docs/how-to/brownfield/add-feature-to-existing.md +74 -0
  74. package/docs/how-to/brownfield/document-existing-project.md +66 -0
  75. package/docs/how-to/brownfield/index.md +84 -0
  76. package/docs/how-to/brownfield/quick-fix-in-brownfield.md +77 -0
  77. package/docs/how-to/brownfield/use-tea-for-enterprise.md +526 -0
  78. package/docs/how-to/brownfield/use-tea-with-existing-tests.md +577 -0
  79. package/docs/how-to/customization/customize-agents.md +212 -0
  80. package/docs/how-to/customization/enable-tea-mcp-enhancements.md +424 -0
  81. package/docs/how-to/customization/index.md +23 -0
  82. package/docs/how-to/customization/integrate-playwright-utils.md +813 -0
  83. package/docs/how-to/customization/shard-large-documents.md +101 -0
  84. package/docs/how-to/get-answers-about-bmad.md +102 -0
  85. package/docs/how-to/installation/index.md +12 -0
  86. package/docs/how-to/installation/install-bmad.md +111 -0
  87. package/docs/how-to/installation/install-custom-modules.md +118 -0
  88. package/docs/how-to/installation/upgrade-to-v6.md +131 -0
  89. package/docs/how-to/workflows/bmgd-quick-flow.md +156 -0
  90. package/docs/how-to/workflows/conduct-research.md +97 -0
  91. package/docs/how-to/workflows/create-architecture.md +119 -0
  92. package/docs/how-to/workflows/create-epics-and-stories.md +109 -0
  93. package/docs/how-to/workflows/create-prd.md +91 -0
  94. package/docs/how-to/workflows/create-product-brief.md +94 -0
  95. package/docs/how-to/workflows/create-story.md +102 -0
  96. package/docs/how-to/workflows/create-ux-design.md +100 -0
  97. package/docs/how-to/workflows/implement-story.md +97 -0
  98. package/docs/how-to/workflows/quick-spec.md +122 -0
  99. package/docs/how-to/workflows/run-atdd.md +436 -0
  100. package/docs/how-to/workflows/run-automate.md +653 -0
  101. package/docs/how-to/workflows/run-brainstorming-session.md +73 -0
  102. package/docs/how-to/workflows/run-code-review.md +89 -0
  103. package/docs/how-to/workflows/run-implementation-readiness.md +125 -0
  104. package/docs/how-to/workflows/run-nfr-assess.md +679 -0
  105. package/docs/how-to/workflows/run-sprint-planning.md +94 -0
  106. package/docs/how-to/workflows/run-test-design.md +98 -0
  107. package/docs/how-to/workflows/run-test-review.md +605 -0
  108. package/docs/how-to/workflows/run-trace.md +883 -0
  109. package/docs/how-to/workflows/setup-ci.md +712 -0
  110. package/docs/how-to/workflows/setup-party-mode.md +89 -0
  111. package/docs/how-to/workflows/setup-test-framework.md +98 -0
  112. package/docs/index.md +63 -0
  113. package/docs/migration-guide.md +365 -0
  114. package/docs/multi-scope-guide.md +379 -0
  115. package/docs/plans/multi-scope-parallel-artifacts-plan.md +695 -0
  116. package/docs/reference/agents/index.md +109 -0
  117. package/docs/reference/configuration/core-tasks.md +67 -0
  118. package/docs/reference/configuration/global-config.md +28 -0
  119. package/docs/reference/glossary/index.md +159 -0
  120. package/docs/reference/tea/commands.md +254 -0
  121. package/docs/reference/tea/configuration.md +678 -0
  122. package/docs/reference/tea/knowledge-base.md +340 -0
  123. package/docs/reference/workflows/core-workflows.md +32 -0
  124. package/docs/reference/workflows/document-project.md +73 -0
  125. package/docs/reference/workflows/index.md +12 -0
  126. package/docs/tutorials/getting-started/getting-started-bmadv6.md +246 -0
  127. package/docs/tutorials/getting-started/images/workflow-method-greenfield.excalidraw +5034 -0
  128. package/docs/tutorials/getting-started/images/workflow-method-greenfield.svg +4 -0
  129. package/docs/tutorials/getting-started/images/workflow-overview.jpg +0 -0
  130. package/docs/tutorials/getting-started/tea-lite-quickstart.md +444 -0
  131. package/docs/tutorials/getting-started/workflow-overview.jpg +0 -0
  132. package/eslint.config.mjs +152 -0
  133. package/package.json +117 -0
  134. package/prettier.config.mjs +32 -0
  135. package/src/bmm/_module-installer/installer.js +48 -0
  136. package/src/bmm/_module-installer/platform-specifics/claude-code.js +35 -0
  137. package/src/bmm/_module-installer/platform-specifics/windsurf.js +32 -0
  138. package/src/bmm/agents/analyst.agent.yaml +41 -0
  139. package/src/bmm/agents/architect.agent.yaml +33 -0
  140. package/src/bmm/agents/dev.agent.yaml +38 -0
  141. package/src/bmm/agents/pm.agent.yaml +51 -0
  142. package/src/bmm/agents/quick-flow-solo-dev.agent.yaml +32 -0
  143. package/src/bmm/agents/sm.agent.yaml +47 -0
  144. package/src/bmm/agents/tea.agent.yaml +68 -0
  145. package/src/bmm/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +224 -0
  146. package/src/bmm/agents/tech-writer/tech-writer.agent.yaml +49 -0
  147. package/src/bmm/agents/ux-designer.agent.yaml +30 -0
  148. package/src/bmm/data/README.md +29 -0
  149. package/src/bmm/data/project-context-template.md +40 -0
  150. package/src/bmm/module.yaml +64 -0
  151. package/src/bmm/sub-modules/claude-code/config.yaml +4 -0
  152. package/src/bmm/sub-modules/claude-code/injections.yaml +242 -0
  153. package/src/bmm/sub-modules/claude-code/readme.md +87 -0
  154. package/src/bmm/teams/default-party.csv +21 -0
  155. package/src/bmm/teams/team-fullstack.yaml +12 -0
  156. package/src/bmm/testarch/knowledge/api-request.md +442 -0
  157. package/src/bmm/testarch/knowledge/api-testing-patterns.md +843 -0
  158. package/src/bmm/testarch/knowledge/auth-session.md +552 -0
  159. package/src/bmm/testarch/knowledge/burn-in.md +273 -0
  160. package/src/bmm/testarch/knowledge/ci-burn-in.md +675 -0
  161. package/src/bmm/testarch/knowledge/component-tdd.md +486 -0
  162. package/src/bmm/testarch/knowledge/contract-testing.md +957 -0
  163. package/src/bmm/testarch/knowledge/data-factories.md +500 -0
  164. package/src/bmm/testarch/knowledge/email-auth.md +721 -0
  165. package/src/bmm/testarch/knowledge/error-handling.md +725 -0
  166. package/src/bmm/testarch/knowledge/feature-flags.md +750 -0
  167. package/src/bmm/testarch/knowledge/file-utils.md +463 -0
  168. package/src/bmm/testarch/knowledge/fixture-architecture.md +401 -0
  169. package/src/bmm/testarch/knowledge/fixtures-composition.md +382 -0
  170. package/src/bmm/testarch/knowledge/intercept-network-call.md +430 -0
  171. package/src/bmm/testarch/knowledge/log.md +429 -0
  172. package/src/bmm/testarch/knowledge/network-error-monitor.md +405 -0
  173. package/src/bmm/testarch/knowledge/network-first.md +486 -0
  174. package/src/bmm/testarch/knowledge/network-recorder.md +527 -0
  175. package/src/bmm/testarch/knowledge/nfr-criteria.md +670 -0
  176. package/src/bmm/testarch/knowledge/overview.md +286 -0
  177. package/src/bmm/testarch/knowledge/playwright-config.md +730 -0
  178. package/src/bmm/testarch/knowledge/probability-impact.md +601 -0
  179. package/src/bmm/testarch/knowledge/recurse.md +421 -0
  180. package/src/bmm/testarch/knowledge/risk-governance.md +615 -0
  181. package/src/bmm/testarch/knowledge/selective-testing.md +732 -0
  182. package/src/bmm/testarch/knowledge/selector-resilience.md +527 -0
  183. package/src/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
  184. package/src/bmm/testarch/knowledge/test-levels-framework.md +473 -0
  185. package/src/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
  186. package/src/bmm/testarch/knowledge/test-quality.md +664 -0
  187. package/src/bmm/testarch/knowledge/timing-debugging.md +372 -0
  188. package/src/bmm/testarch/knowledge/visual-debugging.md +524 -0
  189. package/src/bmm/testarch/tea-index.csv +34 -0
  190. package/src/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
  191. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -0
  192. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -0
  193. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -0
  194. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -0
  195. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -0
  196. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -0
  197. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +194 -0
  198. package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +58 -0
  199. package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
  200. package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
  201. package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
  202. package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
  203. package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
  204. package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
  205. package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
  206. package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
  207. package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +200 -0
  208. package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
  209. package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
  210. package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
  211. package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
  212. package/src/bmm/workflows/1-analysis/research/research.template.md +29 -0
  213. package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
  214. package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
  215. package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
  216. package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
  217. package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +239 -0
  218. package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
  219. package/src/bmm/workflows/1-analysis/research/workflow.md +173 -0
  220. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
  221. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
  222. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
  223. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
  224. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
  225. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
  226. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
  227. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
  228. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
  229. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
  230. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
  231. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
  232. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
  233. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
  234. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +228 -0
  235. package/src/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
  236. package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +43 -0
  237. package/src/bmm/workflows/2-plan-workflows/prd/data/domain-complexity.csv +13 -0
  238. package/src/bmm/workflows/2-plan-workflows/prd/data/prd-purpose.md +197 -0
  239. package/src/bmm/workflows/2-plan-workflows/prd/data/project-types.csv +11 -0
  240. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-01-init.md +191 -0
  241. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-01b-continue.md +153 -0
  242. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-02-discovery.md +224 -0
  243. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-03-success.md +226 -0
  244. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-04-journeys.md +213 -0
  245. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-05-domain.md +207 -0
  246. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-06-innovation.md +226 -0
  247. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-07-project-type.md +237 -0
  248. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-08-scoping.md +228 -0
  249. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-09-functional.md +231 -0
  250. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-10-nonfunctional.md +242 -0
  251. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-11-polish.md +217 -0
  252. package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-12-complete.md +180 -0
  253. package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-01-discovery.md +247 -0
  254. package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
  255. package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-02-review.md +249 -0
  256. package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-03-edit.md +253 -0
  257. package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-04-complete.md +168 -0
  258. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-01-discovery.md +218 -0
  259. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-02-format-detection.md +191 -0
  260. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-02b-parity-check.md +209 -0
  261. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-03-density-validation.md +174 -0
  262. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
  263. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-05-measurability-validation.md +228 -0
  264. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-06-traceability-validation.md +217 -0
  265. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
  266. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
  267. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-09-project-type-validation.md +263 -0
  268. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-10-smart-validation.md +209 -0
  269. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
  270. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-12-completeness-validation.md +242 -0
  271. package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-13-report-complete.md +232 -0
  272. package/src/bmm/workflows/2-plan-workflows/prd/templates/prd-template.md +10 -0
  273. package/src/bmm/workflows/2-plan-workflows/prd/validation-report-prd-workflow.md +433 -0
  274. package/src/bmm/workflows/2-plan-workflows/prd/workflow.md +150 -0
  275. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +190 -0
  276. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +178 -0
  277. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
  278. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +139 -0
  279. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
  280. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +133 -0
  281. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
  282. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +55 -0
  283. package/src/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
  284. package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +11 -0
  285. package/src/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
  286. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -0
  287. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -0
  288. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
  289. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +331 -0
  290. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -0
  291. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -0
  292. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
  293. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -0
  294. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +352 -0
  295. package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +50 -0
  296. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
  297. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
  298. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
  299. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +145 -0
  300. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
  301. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +59 -0
  302. package/src/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
  303. package/src/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
  304. package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +51 -0
  305. package/src/bmm/workflows/4-implementation/correct-course/checklist.md +288 -0
  306. package/src/bmm/workflows/4-implementation/correct-course/instructions.md +206 -0
  307. package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +60 -0
  308. package/src/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
  309. package/src/bmm/workflows/4-implementation/create-story/instructions.xml +345 -0
  310. package/src/bmm/workflows/4-implementation/create-story/template.md +49 -0
  311. package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +61 -0
  312. package/src/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
  313. package/src/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -0
  314. package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +27 -0
  315. package/src/bmm/workflows/4-implementation/retrospective/instructions.md +1443 -0
  316. package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +58 -0
  317. package/src/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
  318. package/src/bmm/workflows/4-implementation/sprint-planning/instructions.md +225 -0
  319. package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
  320. package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +54 -0
  321. package/src/bmm/workflows/4-implementation/sprint-status/instructions.md +229 -0
  322. package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +36 -0
  323. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +156 -0
  324. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +120 -0
  325. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +113 -0
  326. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +113 -0
  327. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +106 -0
  328. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +140 -0
  329. package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +52 -0
  330. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +189 -0
  331. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +144 -0
  332. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +128 -0
  333. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +191 -0
  334. package/src/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
  335. package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
  336. package/src/bmm/workflows/document-project/checklist.md +245 -0
  337. package/src/bmm/workflows/document-project/documentation-requirements.csv +12 -0
  338. package/src/bmm/workflows/document-project/instructions.md +221 -0
  339. package/src/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
  340. package/src/bmm/workflows/document-project/templates/index-template.md +169 -0
  341. package/src/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
  342. package/src/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
  343. package/src/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
  344. package/src/bmm/workflows/document-project/workflow.yaml +30 -0
  345. package/src/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
  346. package/src/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
  347. package/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
  348. package/src/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
  349. package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +90 -0
  350. package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +127 -0
  351. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +39 -0
  352. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +130 -0
  353. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +27 -0
  354. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +43 -0
  355. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +141 -0
  356. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +27 -0
  357. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +49 -0
  358. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +241 -0
  359. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +27 -0
  360. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +38 -0
  361. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +133 -0
  362. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +27 -0
  363. package/src/bmm/workflows/generate-project-context/project-context-template.md +21 -0
  364. package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -0
  365. package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +318 -0
  366. package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +278 -0
  367. package/src/bmm/workflows/generate-project-context/workflow.md +49 -0
  368. package/src/bmm/workflows/testarch/atdd/atdd-checklist-template.md +364 -0
  369. package/src/bmm/workflows/testarch/atdd/checklist.md +374 -0
  370. package/src/bmm/workflows/testarch/atdd/instructions.md +806 -0
  371. package/src/bmm/workflows/testarch/atdd/workflow.yaml +47 -0
  372. package/src/bmm/workflows/testarch/automate/checklist.md +582 -0
  373. package/src/bmm/workflows/testarch/automate/instructions.md +1324 -0
  374. package/src/bmm/workflows/testarch/automate/workflow.yaml +54 -0
  375. package/src/bmm/workflows/testarch/ci/checklist.md +248 -0
  376. package/src/bmm/workflows/testarch/ci/github-actions-template.yaml +198 -0
  377. package/src/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +149 -0
  378. package/src/bmm/workflows/testarch/ci/instructions.md +536 -0
  379. package/src/bmm/workflows/testarch/ci/workflow.yaml +47 -0
  380. package/src/bmm/workflows/testarch/framework/checklist.md +321 -0
  381. package/src/bmm/workflows/testarch/framework/instructions.md +481 -0
  382. package/src/bmm/workflows/testarch/framework/workflow.yaml +49 -0
  383. package/src/bmm/workflows/testarch/nfr-assess/checklist.md +407 -0
  384. package/src/bmm/workflows/testarch/nfr-assess/instructions.md +722 -0
  385. package/src/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +445 -0
  386. package/src/bmm/workflows/testarch/nfr-assess/workflow.yaml +49 -0
  387. package/src/bmm/workflows/testarch/test-design/checklist.md +235 -0
  388. package/src/bmm/workflows/testarch/test-design/instructions.md +788 -0
  389. package/src/bmm/workflows/testarch/test-design/test-design-template.md +294 -0
  390. package/src/bmm/workflows/testarch/test-design/workflow.yaml +56 -0
  391. package/src/bmm/workflows/testarch/test-review/checklist.md +472 -0
  392. package/src/bmm/workflows/testarch/test-review/instructions.md +628 -0
  393. package/src/bmm/workflows/testarch/test-review/test-review-template.md +390 -0
  394. package/src/bmm/workflows/testarch/test-review/workflow.yaml +48 -0
  395. package/src/bmm/workflows/testarch/trace/checklist.md +655 -0
  396. package/src/bmm/workflows/testarch/trace/instructions.md +1047 -0
  397. package/src/bmm/workflows/testarch/trace/trace-template.md +675 -0
  398. package/src/bmm/workflows/testarch/trace/workflow.yaml +57 -0
  399. package/src/bmm/workflows/workflow-status/init/instructions.md +346 -0
  400. package/src/bmm/workflows/workflow-status/init/workflow.yaml +30 -0
  401. package/src/bmm/workflows/workflow-status/instructions.md +397 -0
  402. package/src/bmm/workflows/workflow-status/paths/enterprise-brownfield.yaml +103 -0
  403. package/src/bmm/workflows/workflow-status/paths/enterprise-greenfield.yaml +100 -0
  404. package/src/bmm/workflows/workflow-status/paths/method-brownfield.yaml +103 -0
  405. package/src/bmm/workflows/workflow-status/paths/method-greenfield.yaml +100 -0
  406. package/src/bmm/workflows/workflow-status/project-levels.yaml +59 -0
  407. package/src/bmm/workflows/workflow-status/workflow-status-template.yaml +24 -0
  408. package/src/bmm/workflows/workflow-status/workflow.yaml +32 -0
  409. package/src/core/_module-installer/installer.js +60 -0
  410. package/src/core/agents/bmad-master.agent.yaml +30 -0
  411. package/src/core/lib/scope/artifact-resolver.js +298 -0
  412. package/src/core/lib/scope/event-logger.js +411 -0
  413. package/src/core/lib/scope/index.js +30 -0
  414. package/src/core/lib/scope/scope-context.js +307 -0
  415. package/src/core/lib/scope/scope-initializer.js +458 -0
  416. package/src/core/lib/scope/scope-manager.js +512 -0
  417. package/src/core/lib/scope/scope-migrator.js +442 -0
  418. package/src/core/lib/scope/scope-sync.js +489 -0
  419. package/src/core/lib/scope/scope-validator.js +299 -0
  420. package/src/core/lib/scope/state-lock.js +342 -0
  421. package/src/core/module.yaml +53 -0
  422. package/src/core/resources/excalidraw/README.md +160 -0
  423. package/src/core/resources/excalidraw/excalidraw-helpers.md +127 -0
  424. package/src/core/resources/excalidraw/library-loader.md +50 -0
  425. package/src/core/resources/excalidraw/validate-json-instructions.md +79 -0
  426. package/src/core/tasks/editorial-review-prose.xml +91 -0
  427. package/src/core/tasks/editorial-review-structure.xml +198 -0
  428. package/src/core/tasks/index-docs.xml +65 -0
  429. package/src/core/tasks/review-adversarial-general.xml +46 -0
  430. package/src/core/tasks/shard-doc.xml +109 -0
  431. package/src/core/tasks/workflow.xml +277 -0
  432. package/src/core/workflows/advanced-elicitation/methods.csv +51 -0
  433. package/src/core/workflows/advanced-elicitation/workflow.xml +117 -0
  434. package/src/core/workflows/brainstorming/brain-methods.csv +62 -0
  435. package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
  436. package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
  437. package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
  438. package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
  439. package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
  440. package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
  441. package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -0
  442. package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
  443. package/src/core/workflows/brainstorming/template.md +15 -0
  444. package/src/core/workflows/brainstorming/workflow.md +58 -0
  445. package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -0
  446. package/src/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -0
  447. package/src/core/workflows/party-mode/steps/step-03-graceful-exit.md +157 -0
  448. package/src/core/workflows/party-mode/workflow.md +194 -0
  449. package/src/utility/agent-components/activation-rules.txt +6 -0
  450. package/src/utility/agent-components/activation-steps.txt +28 -0
  451. package/src/utility/agent-components/agent-command-header.md +1 -0
  452. package/src/utility/agent-components/agent.customize.template.yaml +41 -0
  453. package/src/utility/agent-components/handler-action.txt +4 -0
  454. package/src/utility/agent-components/handler-data.txt +5 -0
  455. package/src/utility/agent-components/handler-exec.txt +19 -0
  456. package/src/utility/agent-components/handler-multi.txt +14 -0
  457. package/src/utility/agent-components/handler-tmpl.txt +5 -0
  458. package/src/utility/agent-components/handler-validate-workflow.txt +7 -0
  459. package/src/utility/agent-components/handler-workflow.txt +10 -0
  460. package/src/utility/agent-components/menu-handlers.txt +6 -0
  461. package/test/README.md +295 -0
  462. package/test/fixtures/agent-schema/invalid/critical-actions/actions-as-string.agent.yaml +27 -0
  463. package/test/fixtures/agent-schema/invalid/critical-actions/empty-string-in-actions.agent.yaml +30 -0
  464. package/test/fixtures/agent-schema/invalid/menu/empty-menu.agent.yaml +22 -0
  465. package/test/fixtures/agent-schema/invalid/menu/missing-menu.agent.yaml +20 -0
  466. package/test/fixtures/agent-schema/invalid/menu-commands/empty-command-target.agent.yaml +25 -0
  467. package/test/fixtures/agent-schema/invalid/menu-commands/no-command-target.agent.yaml +24 -0
  468. package/test/fixtures/agent-schema/invalid/menu-triggers/camel-case.agent.yaml +25 -0
  469. package/test/fixtures/agent-schema/invalid/menu-triggers/compound-invalid-format.agent.yaml +25 -0
  470. package/test/fixtures/agent-schema/invalid/menu-triggers/compound-mismatched-kebab.agent.yaml +25 -0
  471. package/test/fixtures/agent-schema/invalid/menu-triggers/duplicate-triggers.agent.yaml +31 -0
  472. package/test/fixtures/agent-schema/invalid/menu-triggers/empty-trigger.agent.yaml +25 -0
  473. package/test/fixtures/agent-schema/invalid/menu-triggers/leading-asterisk.agent.yaml +25 -0
  474. package/test/fixtures/agent-schema/invalid/menu-triggers/snake-case.agent.yaml +25 -0
  475. package/test/fixtures/agent-schema/invalid/menu-triggers/trigger-with-spaces.agent.yaml +25 -0
  476. package/test/fixtures/agent-schema/invalid/metadata/empty-module-string.agent.yaml +26 -0
  477. package/test/fixtures/agent-schema/invalid/metadata/empty-name.agent.yaml +24 -0
  478. package/test/fixtures/agent-schema/invalid/metadata/extra-metadata-fields.agent.yaml +27 -0
  479. package/test/fixtures/agent-schema/invalid/metadata/missing-id.agent.yaml +23 -0
  480. package/test/fixtures/agent-schema/invalid/persona/empty-principles-array.agent.yaml +24 -0
  481. package/test/fixtures/agent-schema/invalid/persona/empty-string-in-principles.agent.yaml +27 -0
  482. package/test/fixtures/agent-schema/invalid/persona/extra-persona-fields.agent.yaml +27 -0
  483. package/test/fixtures/agent-schema/invalid/persona/missing-role.agent.yaml +24 -0
  484. package/test/fixtures/agent-schema/invalid/prompts/empty-content.agent.yaml +29 -0
  485. package/test/fixtures/agent-schema/invalid/prompts/extra-prompt-fields.agent.yaml +31 -0
  486. package/test/fixtures/agent-schema/invalid/prompts/missing-content.agent.yaml +28 -0
  487. package/test/fixtures/agent-schema/invalid/prompts/missing-id.agent.yaml +28 -0
  488. package/test/fixtures/agent-schema/invalid/top-level/empty-file.agent.yaml +5 -0
  489. package/test/fixtures/agent-schema/invalid/top-level/extra-top-level-keys.agent.yaml +28 -0
  490. package/test/fixtures/agent-schema/invalid/top-level/missing-agent-key.agent.yaml +11 -0
  491. package/test/fixtures/agent-schema/invalid/yaml-errors/invalid-indentation.agent.yaml +19 -0
  492. package/test/fixtures/agent-schema/invalid/yaml-errors/malformed-yaml.agent.yaml +18 -0
  493. package/test/fixtures/agent-schema/valid/critical-actions/empty-critical-actions.agent.yaml +24 -0
  494. package/test/fixtures/agent-schema/valid/critical-actions/no-critical-actions.agent.yaml +22 -0
  495. package/test/fixtures/agent-schema/valid/critical-actions/valid-critical-actions.agent.yaml +27 -0
  496. package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +31 -0
  497. package/test/fixtures/agent-schema/valid/menu/single-menu-item.agent.yaml +22 -0
  498. package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +38 -0
  499. package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +24 -0
  500. package/test/fixtures/agent-schema/valid/menu-triggers/compound-triggers.agent.yaml +31 -0
  501. package/test/fixtures/agent-schema/valid/menu-triggers/kebab-case-triggers.agent.yaml +34 -0
  502. package/test/fixtures/agent-schema/valid/metadata/core-agent-with-module.agent.yaml +24 -0
  503. package/test/fixtures/agent-schema/valid/metadata/empty-module-name-in-path.agent.yaml +24 -0
  504. package/test/fixtures/agent-schema/valid/metadata/malformed-path-treated-as-core.agent.yaml +24 -0
  505. package/test/fixtures/agent-schema/valid/metadata/module-agent-correct.agent.yaml +24 -0
  506. package/test/fixtures/agent-schema/valid/metadata/module-agent-missing-module.agent.yaml +23 -0
  507. package/test/fixtures/agent-schema/valid/metadata/wrong-module-value.agent.yaml +24 -0
  508. package/test/fixtures/agent-schema/valid/persona/complete-persona.agent.yaml +24 -0
  509. package/test/fixtures/agent-schema/valid/prompts/empty-prompts.agent.yaml +24 -0
  510. package/test/fixtures/agent-schema/valid/prompts/no-prompts.agent.yaml +22 -0
  511. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-minimal.agent.yaml +28 -0
  512. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-with-description.agent.yaml +30 -0
  513. package/test/fixtures/agent-schema/valid/top-level/minimal-core-agent.agent.yaml +24 -0
  514. package/test/test-agent-schema.js +387 -0
  515. package/test/test-cli-integration.sh +159 -0
  516. package/test/test-installation-components.js +214 -0
  517. package/test/test-scope-e2e.js +450 -0
  518. package/test/test-scope-system.js +787 -0
  519. package/test/unit-test-schema.js +133 -0
  520. package/tools/bmad-npx-wrapper.js +38 -0
  521. package/tools/build-docs.js +577 -0
  522. package/tools/cli/README.md +7 -0
  523. package/tools/cli/bmad-cli.js +58 -0
  524. package/tools/cli/commands/install.js +87 -0
  525. package/tools/cli/commands/scope.js +474 -0
  526. package/tools/cli/external-official-modules.yaml +41 -0
  527. package/tools/cli/installers/install-messages.yaml +58 -0
  528. package/tools/cli/installers/lib/core/config-collector.js +1079 -0
  529. package/tools/cli/installers/lib/core/custom-module-cache.js +259 -0
  530. package/tools/cli/installers/lib/core/dependency-resolver.js +739 -0
  531. package/tools/cli/installers/lib/core/detector.js +223 -0
  532. package/tools/cli/installers/lib/core/ide-config-manager.js +156 -0
  533. package/tools/cli/installers/lib/core/installer.js +2585 -0
  534. package/tools/cli/installers/lib/core/manifest-generator.js +963 -0
  535. package/tools/cli/installers/lib/core/manifest.js +590 -0
  536. package/tools/cli/installers/lib/custom/handler.js +363 -0
  537. package/tools/cli/installers/lib/ide/_base-ide.js +654 -0
  538. package/tools/cli/installers/lib/ide/antigravity.js +486 -0
  539. package/tools/cli/installers/lib/ide/auggie.js +244 -0
  540. package/tools/cli/installers/lib/ide/claude-code.js +487 -0
  541. package/tools/cli/installers/lib/ide/cline.js +269 -0
  542. package/tools/cli/installers/lib/ide/codex.js +375 -0
  543. package/tools/cli/installers/lib/ide/crush.js +300 -0
  544. package/tools/cli/installers/lib/ide/cursor.js +169 -0
  545. package/tools/cli/installers/lib/ide/gemini.js +301 -0
  546. package/tools/cli/installers/lib/ide/github-copilot.js +383 -0
  547. package/tools/cli/installers/lib/ide/iflow.js +191 -0
  548. package/tools/cli/installers/lib/ide/kilo.js +250 -0
  549. package/tools/cli/installers/lib/ide/kiro-cli.js +326 -0
  550. package/tools/cli/installers/lib/ide/manager.js +244 -0
  551. package/tools/cli/installers/lib/ide/opencode.js +257 -0
  552. package/tools/cli/installers/lib/ide/qwen.js +372 -0
  553. package/tools/cli/installers/lib/ide/roo.js +270 -0
  554. package/tools/cli/installers/lib/ide/rovo-dev.js +290 -0
  555. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +96 -0
  556. package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +158 -0
  557. package/tools/cli/installers/lib/ide/shared/module-injections.js +136 -0
  558. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +119 -0
  559. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +242 -0
  560. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +29 -0
  561. package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +14 -0
  562. package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +12 -0
  563. package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +30 -0
  564. package/tools/cli/installers/lib/ide/templates/workflow-commander.md +45 -0
  565. package/tools/cli/installers/lib/ide/trae.js +313 -0
  566. package/tools/cli/installers/lib/ide/windsurf.js +258 -0
  567. package/tools/cli/installers/lib/message-loader.js +85 -0
  568. package/tools/cli/installers/lib/modules/external-manager.js +133 -0
  569. package/tools/cli/installers/lib/modules/manager.js +1362 -0
  570. package/tools/cli/lib/activation-builder.js +163 -0
  571. package/tools/cli/lib/agent/compiler.js +522 -0
  572. package/tools/cli/lib/agent/installer.js +716 -0
  573. package/tools/cli/lib/agent/template-engine.js +152 -0
  574. package/tools/cli/lib/agent-analyzer.js +109 -0
  575. package/tools/cli/lib/agent-party-generator.js +194 -0
  576. package/tools/cli/lib/cli-utils.js +227 -0
  577. package/tools/cli/lib/config.js +213 -0
  578. package/tools/cli/lib/file-ops.js +204 -0
  579. package/tools/cli/lib/platform-codes.js +116 -0
  580. package/tools/cli/lib/project-root.js +77 -0
  581. package/tools/cli/lib/prompts.js +433 -0
  582. package/tools/cli/lib/ui.js +1591 -0
  583. package/tools/cli/lib/xml-handler.js +177 -0
  584. package/tools/cli/lib/xml-to-markdown.js +82 -0
  585. package/tools/cli/lib/yaml-format.js +245 -0
  586. package/tools/cli/lib/yaml-xml-builder.js +587 -0
  587. package/tools/cli/scripts/migrate-workflows.js +281 -0
  588. package/tools/docs/BUNDLE_DISTRIBUTION_SETUP.md +95 -0
  589. package/tools/docs/index.md +2 -0
  590. package/tools/fix-doc-links.js +288 -0
  591. package/tools/flattener/aggregate.js +76 -0
  592. package/tools/flattener/binary.js +80 -0
  593. package/tools/flattener/discovery.js +71 -0
  594. package/tools/flattener/files.js +35 -0
  595. package/tools/flattener/ignoreRules.js +172 -0
  596. package/tools/flattener/main.js +483 -0
  597. package/tools/flattener/projectRoot.js +201 -0
  598. package/tools/flattener/prompts.js +44 -0
  599. package/tools/flattener/stats.helpers.js +368 -0
  600. package/tools/flattener/stats.js +75 -0
  601. package/tools/flattener/test-matrix.js +409 -0
  602. package/tools/flattener/xml.js +82 -0
  603. package/tools/format-workflow-md.js +263 -0
  604. package/tools/lib/xml-utils.js +13 -0
  605. package/tools/maintainer/review-pr-README.md +55 -0
  606. package/tools/maintainer/review-pr.md +242 -0
  607. package/tools/migrate-custom-module-paths.js +124 -0
  608. package/tools/platform-codes.yaml +157 -0
  609. package/tools/schema/agent.js +493 -0
  610. package/tools/validate-agent-schema.js +110 -0
  611. package/tools/validate-doc-links.js +363 -0
  612. package/tools/validate-svg-changes.sh +356 -0
  613. package/website/README.md +76 -0
  614. package/website/astro.config.mjs +228 -0
  615. package/website/public/favicon.ico +0 -0
  616. package/website/public/img/bmad-dark.png +0 -0
  617. package/website/public/img/bmad-light.png +0 -0
  618. package/website/public/img/logo.svg +4 -0
  619. package/website/public/robots.txt +37 -0
  620. package/website/src/components/Banner.astro +59 -0
  621. package/website/src/components/Header.astro +121 -0
  622. package/website/src/components/MobileMenuFooter.astro +53 -0
  623. package/website/src/content/config.ts +6 -0
  624. package/website/src/lib/site-url.js +25 -0
  625. package/website/src/rehype-markdown-links.js +102 -0
  626. package/website/src/styles/custom.css +485 -0
@@ -0,0 +1,1591 @@
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
+ module.exports = { UI };