mdan-cli 2.5.0 → 2.5.2

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 (706) hide show
  1. package/.augment/code_review_guidelines.yaml +271 -0
  2. package/.claude/skills/bmad-os-audit-file-refs/SKILL.md +6 -0
  3. package/.claude/skills/bmad-os-audit-file-refs/prompts/instructions.md +59 -0
  4. package/.claude/skills/bmad-os-changelog-social/SKILL.md +177 -0
  5. package/.claude/skills/bmad-os-changelog-social/examples/discord-example.md +53 -0
  6. package/.claude/skills/bmad-os-changelog-social/examples/linkedin-example.md +49 -0
  7. package/.claude/skills/bmad-os-changelog-social/examples/twitter-example.md +55 -0
  8. package/.claude/skills/bmad-os-diataxis-style-fix/SKILL.md +6 -0
  9. package/.claude/skills/bmad-os-diataxis-style-fix/prompts/instructions.md +229 -0
  10. package/.claude/skills/bmad-os-draft-changelog/SKILL.md +6 -0
  11. package/.claude/skills/bmad-os-draft-changelog/prompts/instructions.md +82 -0
  12. package/.claude/skills/bmad-os-gh-triage/SKILL.md +6 -0
  13. package/.claude/skills/bmad-os-gh-triage/prompts/agent-prompt.md +60 -0
  14. package/.claude/skills/bmad-os-gh-triage/prompts/instructions.md +74 -0
  15. package/.claude/skills/bmad-os-release-module/SKILL.md +6 -0
  16. package/.claude/skills/bmad-os-release-module/prompts/instructions.md +53 -0
  17. package/.claude/skills/bmad-os-review-pr/SKILL.md +6 -0
  18. package/.claude/skills/bmad-os-review-pr/prompts/instructions.md +231 -0
  19. package/.claude/skills/bmad-os-root-cause-analysis/SKILL.md +12 -0
  20. package/.claude/skills/bmad-os-root-cause-analysis/prompts/instructions.md +74 -0
  21. package/.coderabbit.yaml +85 -0
  22. package/.github/CODE_OF_CONDUCT.md +128 -0
  23. package/.github/FUNDING.yaml +15 -0
  24. package/.github/ISSUE_TEMPLATE/bug-report.yaml +124 -0
  25. package/.github/ISSUE_TEMPLATE/config.yaml +8 -0
  26. package/.github/ISSUE_TEMPLATE/documentation.yaml +55 -0
  27. package/.github/ISSUE_TEMPLATE/feature-request.md +22 -0
  28. package/.github/ISSUE_TEMPLATE/issue.md +32 -0
  29. package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  30. package/.github/scripts/discord-helpers.sh +34 -0
  31. package/.github/workflows/coderabbit-review.yaml +22 -0
  32. package/.github/workflows/discord.yaml +90 -0
  33. package/.github/workflows/docs.yaml +64 -0
  34. package/.github/workflows/quality.yaml +116 -0
  35. package/.husky/pre-commit +20 -0
  36. package/.markdownlint-cli2.yaml +41 -0
  37. package/.nvmrc +1 -0
  38. package/.prettierignore +12 -0
  39. package/.vscode/settings.json +96 -0
  40. package/AGENTS.md +227 -165
  41. package/AGENTS_LIST.md +946 -0
  42. package/ARCHITECTURE.md +590 -0
  43. package/CHANGELOG.md +1770 -0
  44. package/CNAME +1 -0
  45. package/CONTRIBUTING.md +512 -0
  46. package/CONTRIBUTORS.md +32 -0
  47. package/INSTALL.md +246 -0
  48. package/LICENSE +30 -0
  49. package/README.md +133 -194
  50. package/RELEASE_NOTES.md +246 -0
  51. package/SECURITY.md +85 -0
  52. package/TRADEMARK.md +55 -0
  53. package/USAGE.md +368 -0
  54. package/Wordmark.png +0 -0
  55. package/app/__init__.py +5 -0
  56. package/app/cis/agents/__init__.py +31 -0
  57. package/app/cis/agents/brainstorming-coach/__init__.py +3 -0
  58. package/app/cis/agents/brainstorming-coach/agent.py +162 -0
  59. package/app/cis/agents/brainstorming-coach/prompt.yaml +53 -0
  60. package/app/cis/agents/creative-problem-solver/__init__.py +3 -0
  61. package/app/cis/agents/creative-problem-solver/agent.py +233 -0
  62. package/app/cis/agents/creative-problem-solver/prompt.yaml +74 -0
  63. package/app/cis/agents/design-thinking-coach/__init__.py +3 -0
  64. package/app/cis/agents/design-thinking-coach/agent.py +241 -0
  65. package/app/cis/agents/design-thinking-coach/prompt.yaml +77 -0
  66. package/app/cis/agents/innovation-strategist/__init__.py +3 -0
  67. package/app/cis/agents/innovation-strategist/agent.py +271 -0
  68. package/app/cis/agents/innovation-strategist/prompt.yaml +70 -0
  69. package/app/cis/agents/presentation-master/__init__.py +3 -0
  70. package/app/cis/agents/presentation-master/agent.py +420 -0
  71. package/app/cis/agents/presentation-master/prompt.yaml +62 -0
  72. package/app/cis/agents/storyteller/__init__.py +3 -0
  73. package/app/cis/agents/storyteller/agent.py +303 -0
  74. package/app/cis/agents/storyteller/prompt.yaml +99 -0
  75. package/app/core/__init__.py +5 -0
  76. package/app/core/agents/__init__.py +5 -0
  77. package/app/core/agents/mdan-master/__init__.py +7 -0
  78. package/app/core/agents/mdan-master/agent.py +302 -0
  79. package/app/core/agents/mdan-master/prompt.yaml +105 -0
  80. package/app/mmb/agents/__init__.py +24 -0
  81. package/app/mmb/agents/agent-builder/__init__.py +5 -0
  82. package/app/mmb/agents/agent-builder/agent.py +261 -0
  83. package/app/mmb/agents/agent-builder/prompt.yaml +48 -0
  84. package/app/mmb/agents/module-builder/__init__.py +5 -0
  85. package/app/mmb/agents/module-builder/agent.py +299 -0
  86. package/app/mmb/agents/module-builder/prompt.yaml +50 -0
  87. package/app/mmb/agents/workflow-builder/__init__.py +5 -0
  88. package/app/mmb/agents/workflow-builder/agent.py +318 -0
  89. package/app/mmb/agents/workflow-builder/prompt.yaml +52 -0
  90. package/app/mmm/agents/__init__.py +48 -0
  91. package/app/mmm/agents/analyst/__init__.py +7 -0
  92. package/app/mmm/agents/analyst/agent.py +384 -0
  93. package/app/mmm/agents/analyst/prompt.yaml +62 -0
  94. package/app/mmm/agents/architect/__init__.py +7 -0
  95. package/app/mmm/agents/architect/agent.py +300 -0
  96. package/app/mmm/agents/architect/prompt.yaml +66 -0
  97. package/app/mmm/agents/dev/__init__.py +7 -0
  98. package/app/mmm/agents/dev/agent.py +285 -0
  99. package/app/mmm/agents/dev/prompt.yaml +62 -0
  100. package/app/mmm/agents/pm/__init__.py +7 -0
  101. package/app/mmm/agents/pm/agent.py +417 -0
  102. package/app/mmm/agents/pm/prompt.yaml +64 -0
  103. package/app/mmm/agents/qa/__init__.py +7 -0
  104. package/app/mmm/agents/qa/agent.py +267 -0
  105. package/app/mmm/agents/qa/prompt.yaml +67 -0
  106. package/app/mmm/agents/quick-flow-solo-dev/__init__.py +7 -0
  107. package/app/mmm/agents/quick-flow-solo-dev/agent.py +319 -0
  108. package/app/mmm/agents/quick-flow-solo-dev/prompt.yaml +60 -0
  109. package/app/mmm/agents/sm/__init__.py +7 -0
  110. package/app/mmm/agents/sm/agent.py +357 -0
  111. package/app/mmm/agents/sm/prompt.yaml +61 -0
  112. package/app/mmm/agents/tech-writer/__init__.py +7 -0
  113. package/app/mmm/agents/tech-writer/agent.py +420 -0
  114. package/app/mmm/agents/tech-writer/prompt.yaml +70 -0
  115. package/app/mmm/agents/ux-designer/__init__.py +14 -0
  116. package/app/mmm/agents/ux-designer/agent.py +412 -0
  117. package/app/mmm/agents/ux-designer/prompt.yaml +37 -0
  118. package/app/packs/__init__.py +32 -0
  119. package/app/packs/db-optimization/__init__.py +13 -0
  120. package/app/packs/db-optimization/agents/__init__.py +11 -0
  121. package/app/packs/db-optimization/agents/db-performance-analyst/__init__.py +5 -0
  122. package/app/packs/db-optimization/agents/db-performance-analyst/agent.py +559 -0
  123. package/app/packs/db-optimization/agents/db-performance-analyst/prompt.yaml +63 -0
  124. package/app/packs/db-optimization/agents/indexing-specialist/__init__.py +5 -0
  125. package/app/packs/db-optimization/agents/indexing-specialist/agent.py +713 -0
  126. package/app/packs/db-optimization/agents/indexing-specialist/prompt.yaml +92 -0
  127. package/app/packs/db-optimization/agents/query-optimizer/__init__.py +5 -0
  128. package/app/packs/db-optimization/agents/query-optimizer/agent.py +566 -0
  129. package/app/packs/db-optimization/agents/query-optimizer/prompt.yaml +74 -0
  130. package/app/packs/devops-azure/__init__.py +13 -0
  131. package/app/packs/devops-azure/agents/__init__.py +11 -0
  132. package/app/packs/devops-azure/agents/azure-specialist/__init__.py +5 -0
  133. package/app/packs/devops-azure/agents/azure-specialist/agent.py +584 -0
  134. package/app/packs/devops-azure/agents/azure-specialist/prompt.yaml +301 -0
  135. package/app/packs/devops-azure/agents/cicd-architect/__init__.py +5 -0
  136. package/app/packs/devops-azure/agents/cicd-architect/agent.py +665 -0
  137. package/app/packs/devops-azure/agents/cicd-architect/prompt.yaml +409 -0
  138. package/app/packs/devops-azure/agents/devops-engineer/__init__.py +5 -0
  139. package/app/packs/devops-azure/agents/devops-engineer/agent.py +545 -0
  140. package/app/packs/devops-azure/agents/devops-engineer/prompt.yaml +263 -0
  141. package/app/packs/fintech/__init__.py +13 -0
  142. package/app/packs/fintech/agents/__init__.py +11 -0
  143. package/app/packs/fintech/agents/compliance-officer/__init__.py +5 -0
  144. package/app/packs/fintech/agents/compliance-officer/agent.py +449 -0
  145. package/app/packs/fintech/agents/compliance-officer/prompt.yaml +135 -0
  146. package/app/packs/fintech/agents/financial-analyst/__init__.py +5 -0
  147. package/app/packs/fintech/agents/financial-analyst/agent.py +392 -0
  148. package/app/packs/fintech/agents/financial-analyst/prompt.yaml +143 -0
  149. package/app/packs/fintech/agents/risk-manager/__init__.py +5 -0
  150. package/app/packs/fintech/agents/risk-manager/agent.py +664 -0
  151. package/app/packs/fintech/agents/risk-manager/prompt.yaml +240 -0
  152. package/app/tea/agents/tea/__init__.py +9 -0
  153. package/app/tea/agents/tea/agent.py +689 -0
  154. package/app/tea/agents/tea/prompt.yaml +100 -0
  155. package/banner-bmad-method.png +0 -0
  156. package/docs/404.md +9 -0
  157. package/docs/_STYLE_GUIDE.md +370 -0
  158. package/docs/explanation/advanced-elicitation.md +49 -0
  159. package/docs/explanation/adversarial-review.md +59 -0
  160. package/docs/explanation/brainstorming.md +33 -0
  161. package/docs/explanation/established-projects-faq.md +50 -0
  162. package/docs/explanation/party-mode.md +59 -0
  163. package/docs/explanation/preventing-agent-conflicts.md +112 -0
  164. package/docs/explanation/project-context.md +157 -0
  165. package/docs/explanation/quick-flow.md +73 -0
  166. package/docs/explanation/why-solutioning-matters.md +77 -0
  167. package/docs/how-to/customize-bmad.md +172 -0
  168. package/docs/how-to/established-projects.md +117 -0
  169. package/docs/how-to/get-answers-about-bmad.md +134 -0
  170. package/docs/how-to/install-bmad.md +97 -0
  171. package/docs/how-to/non-interactive-installation.md +171 -0
  172. package/docs/how-to/project-context.md +136 -0
  173. package/docs/how-to/quick-fixes.md +123 -0
  174. package/docs/how-to/shard-large-documents.md +78 -0
  175. package/docs/how-to/upgrade-to-v6.md +97 -0
  176. package/docs/index.md +59 -0
  177. package/docs/reference/agents.md +28 -0
  178. package/docs/reference/commands.md +151 -0
  179. package/docs/reference/modules.md +76 -0
  180. package/docs/reference/testing.md +106 -0
  181. package/docs/reference/workflow-map.md +89 -0
  182. package/docs/roadmap.mdx +136 -0
  183. package/docs/tutorials/getting-started.md +286 -0
  184. package/eslint.config.mjs +141 -0
  185. package/package.json +106 -37
  186. package/prettier.config.mjs +32 -0
  187. package/prompts/cis/brainstorming-coach.yaml +53 -0
  188. package/prompts/cis/creative-problem-solver.yaml +74 -0
  189. package/prompts/cis/design-thinking-coach.yaml +77 -0
  190. package/prompts/cis/innovation-strategist.yaml +70 -0
  191. package/prompts/cis/presentation-master.yaml +62 -0
  192. package/prompts/cis/storyteller.yaml +99 -0
  193. package/prompts/core/mdan-master.yaml +105 -0
  194. package/prompts/mmb/agent-builder.yaml +48 -0
  195. package/prompts/mmb/module-builder.yaml +50 -0
  196. package/prompts/mmb/workflow-builder.yaml +52 -0
  197. package/prompts/mmm/analyst.yaml +62 -0
  198. package/prompts/mmm/architect.yaml +66 -0
  199. package/prompts/mmm/dev.yaml +62 -0
  200. package/prompts/mmm/pm.yaml +64 -0
  201. package/prompts/mmm/qa.yaml +67 -0
  202. package/prompts/mmm/quick-flow-solo-dev.yaml +60 -0
  203. package/prompts/mmm/sm.yaml +61 -0
  204. package/prompts/mmm/tech-writer.yaml +70 -0
  205. package/prompts/mmm/ux-designer.yaml +33 -0
  206. package/prompts/packs/db-optimization/db-performance-analyst.yaml +63 -0
  207. package/prompts/packs/db-optimization/indexing-specialist.yaml +92 -0
  208. package/prompts/packs/db-optimization/query-optimizer.yaml +74 -0
  209. package/prompts/packs/devops-azure/azure-specialist.yaml +301 -0
  210. package/prompts/packs/devops-azure/cicd-architect.yaml +409 -0
  211. package/prompts/packs/devops-azure/devops-engineer.yaml +263 -0
  212. package/prompts/packs/fintech/compliance-officer.yaml +135 -0
  213. package/prompts/packs/fintech/financial-analyst.yaml +143 -0
  214. package/prompts/packs/fintech/risk-manager.yaml +240 -0
  215. package/prompts/tea/tea.yaml +100 -0
  216. package/prompts.json +237 -0
  217. package/src/bmm/agents/analyst.agent.yaml +43 -0
  218. package/src/bmm/agents/architect.agent.yaml +29 -0
  219. package/src/bmm/agents/dev.agent.yaml +38 -0
  220. package/src/bmm/agents/pm.agent.yaml +44 -0
  221. package/src/bmm/agents/qa.agent.yaml +58 -0
  222. package/src/bmm/agents/quick-flow-solo-dev.agent.yaml +32 -0
  223. package/src/bmm/agents/sm.agent.yaml +37 -0
  224. package/src/bmm/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +224 -0
  225. package/src/bmm/agents/tech-writer/tech-writer.agent.yaml +46 -0
  226. package/src/bmm/agents/ux-designer.agent.yaml +27 -0
  227. package/src/bmm/data/project-context-template.md +26 -0
  228. package/src/bmm/module-help.csv +31 -0
  229. package/src/bmm/module.yaml +50 -0
  230. package/src/bmm/teams/default-party.csv +20 -0
  231. package/src/bmm/teams/team-fullstack.yaml +12 -0
  232. package/src/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
  233. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -0
  234. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -0
  235. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -0
  236. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -0
  237. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -0
  238. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -0
  239. package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +162 -0
  240. package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -0
  241. package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
  242. package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
  243. package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
  244. package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
  245. package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
  246. package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +444 -0
  247. package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
  248. package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
  249. package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
  250. package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
  251. package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
  252. package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +476 -0
  253. package/src/bmm/workflows/1-analysis/research/research.template.md +29 -0
  254. package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
  255. package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
  256. package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
  257. package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
  258. package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +233 -0
  259. package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +487 -0
  260. package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
  261. package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
  262. package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
  263. package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +15 -0
  264. package/src/bmm/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +197 -0
  265. package/src/bmm/workflows/2-plan-workflows/create-prd/data/project-types.csv +11 -0
  266. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01-init.md +191 -0
  267. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +152 -0
  268. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +224 -0
  269. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +154 -0
  270. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +170 -0
  271. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +226 -0
  272. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +213 -0
  273. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +207 -0
  274. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +226 -0
  275. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +237 -0
  276. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +228 -0
  277. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +231 -0
  278. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +242 -0
  279. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +217 -0
  280. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +124 -0
  281. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +247 -0
  282. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
  283. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +249 -0
  284. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +253 -0
  285. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +168 -0
  286. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +226 -0
  287. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +191 -0
  288. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +209 -0
  289. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +174 -0
  290. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
  291. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +228 -0
  292. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +217 -0
  293. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
  294. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
  295. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +263 -0
  296. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +209 -0
  297. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
  298. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +242 -0
  299. package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +231 -0
  300. package/src/bmm/workflows/2-plan-workflows/create-prd/templates/prd-template.md +10 -0
  301. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -0
  302. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
  303. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -0
  304. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
  305. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
  306. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
  307. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
  308. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
  309. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
  310. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
  311. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
  312. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
  313. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
  314. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
  315. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
  316. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
  317. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
  318. package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +171 -0
  319. package/src/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
  320. package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +42 -0
  321. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +184 -0
  322. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +172 -0
  323. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +173 -0
  324. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +133 -0
  325. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +245 -0
  326. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -0
  327. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
  328. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -0
  329. package/src/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
  330. package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +13 -0
  331. package/src/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
  332. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -0
  333. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +173 -0
  334. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
  335. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +329 -0
  336. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -0
  337. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -0
  338. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
  339. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -0
  340. package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +76 -0
  341. package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -0
  342. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
  343. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
  344. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
  345. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +149 -0
  346. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
  347. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -0
  348. package/src/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
  349. package/src/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
  350. package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -0
  351. package/src/bmm/workflows/4-implementation/correct-course/checklist.md +288 -0
  352. package/src/bmm/workflows/4-implementation/correct-course/instructions.md +207 -0
  353. package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -0
  354. package/src/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
  355. package/src/bmm/workflows/4-implementation/create-story/instructions.xml +346 -0
  356. package/src/bmm/workflows/4-implementation/create-story/template.md +49 -0
  357. package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -0
  358. package/src/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
  359. package/src/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -0
  360. package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -0
  361. package/src/bmm/workflows/4-implementation/retrospective/instructions.md +1444 -0
  362. package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -0
  363. package/src/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
  364. package/src/bmm/workflows/4-implementation/sprint-planning/instructions.md +226 -0
  365. package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
  366. package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +47 -0
  367. package/src/bmm/workflows/4-implementation/sprint-status/instructions.md +230 -0
  368. package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -0
  369. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +174 -0
  370. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +118 -0
  371. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +111 -0
  372. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +111 -0
  373. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +104 -0
  374. package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -0
  375. package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -0
  376. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +189 -0
  377. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +143 -0
  378. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +126 -0
  379. package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +200 -0
  380. package/src/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
  381. package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
  382. package/src/bmm/workflows/document-project/checklist.md +245 -0
  383. package/src/bmm/workflows/document-project/documentation-requirements.csv +12 -0
  384. package/src/bmm/workflows/document-project/instructions.md +130 -0
  385. package/src/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
  386. package/src/bmm/workflows/document-project/templates/index-template.md +169 -0
  387. package/src/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
  388. package/src/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
  389. package/src/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
  390. package/src/bmm/workflows/document-project/workflow.yaml +22 -0
  391. package/src/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
  392. package/src/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
  393. package/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
  394. package/src/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
  395. package/src/bmm/workflows/generate-project-context/project-context-template.md +21 -0
  396. package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -0
  397. package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +318 -0
  398. package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +278 -0
  399. package/src/bmm/workflows/generate-project-context/workflow.md +49 -0
  400. package/src/bmm/workflows/qa-generate-e2e-tests/checklist.md +33 -0
  401. package/src/bmm/workflows/qa-generate-e2e-tests/instructions.md +110 -0
  402. package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -0
  403. package/src/core/agents/bmad-master.agent.yaml +30 -0
  404. package/src/core/module-help.csv +9 -0
  405. package/src/core/module.yaml +25 -0
  406. package/src/core/tasks/editorial-review-prose.xml +102 -0
  407. package/src/core/tasks/editorial-review-structure.xml +208 -0
  408. package/src/core/tasks/help.md +86 -0
  409. package/src/core/tasks/index-docs.xml +65 -0
  410. package/src/core/tasks/review-adversarial-general.xml +49 -0
  411. package/src/core/tasks/shard-doc.xml +108 -0
  412. package/src/core/tasks/workflow.xml +235 -0
  413. package/src/core/workflows/advanced-elicitation/methods.csv +51 -0
  414. package/src/core/workflows/advanced-elicitation/workflow.xml +118 -0
  415. package/src/core/workflows/brainstorming/brain-methods.csv +62 -0
  416. package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
  417. package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
  418. package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
  419. package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
  420. package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
  421. package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
  422. package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -0
  423. package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
  424. package/src/core/workflows/brainstorming/template.md +15 -0
  425. package/src/core/workflows/brainstorming/workflow.md +58 -0
  426. package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -0
  427. package/src/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -0
  428. package/src/core/workflows/party-mode/steps/step-03-graceful-exit.md +168 -0
  429. package/src/core/workflows/party-mode/workflow.md +194 -0
  430. package/src/utility/agent-components/activation-rules.txt +6 -0
  431. package/src/utility/agent-components/activation-steps.txt +14 -0
  432. package/src/utility/agent-components/agent-command-header.md +1 -0
  433. package/src/utility/agent-components/agent.customize.template.yaml +41 -0
  434. package/src/utility/agent-components/handler-action.txt +4 -0
  435. package/src/utility/agent-components/handler-data.txt +5 -0
  436. package/src/utility/agent-components/handler-exec.txt +6 -0
  437. package/src/utility/agent-components/handler-multi.txt +14 -0
  438. package/src/utility/agent-components/handler-tmpl.txt +5 -0
  439. package/src/utility/agent-components/handler-validate-workflow.txt +7 -0
  440. package/src/utility/agent-components/handler-workflow.txt +10 -0
  441. package/src/utility/agent-components/menu-handlers.txt +6 -0
  442. package/test/README.md +295 -0
  443. package/test/adversarial-review-tests/README.md +56 -0
  444. package/test/adversarial-review-tests/sample-content.md +46 -0
  445. package/test/adversarial-review-tests/test-cases.yaml +103 -0
  446. package/test/fixtures/agent-schema/invalid/critical-actions/actions-as-string.agent.yaml +27 -0
  447. package/test/fixtures/agent-schema/invalid/critical-actions/empty-string-in-actions.agent.yaml +30 -0
  448. package/test/fixtures/agent-schema/invalid/menu/empty-menu.agent.yaml +22 -0
  449. package/test/fixtures/agent-schema/invalid/menu/missing-menu.agent.yaml +20 -0
  450. package/test/fixtures/agent-schema/invalid/menu-commands/empty-command-target.agent.yaml +25 -0
  451. package/test/fixtures/agent-schema/invalid/menu-commands/no-command-target.agent.yaml +24 -0
  452. package/test/fixtures/agent-schema/invalid/menu-triggers/camel-case.agent.yaml +25 -0
  453. package/test/fixtures/agent-schema/invalid/menu-triggers/compound-invalid-format.agent.yaml +25 -0
  454. package/test/fixtures/agent-schema/invalid/menu-triggers/compound-mismatched-kebab.agent.yaml +25 -0
  455. package/test/fixtures/agent-schema/invalid/menu-triggers/duplicate-triggers.agent.yaml +31 -0
  456. package/test/fixtures/agent-schema/invalid/menu-triggers/empty-trigger.agent.yaml +25 -0
  457. package/test/fixtures/agent-schema/invalid/menu-triggers/leading-asterisk.agent.yaml +25 -0
  458. package/test/fixtures/agent-schema/invalid/menu-triggers/snake-case.agent.yaml +25 -0
  459. package/test/fixtures/agent-schema/invalid/menu-triggers/trigger-with-spaces.agent.yaml +25 -0
  460. package/test/fixtures/agent-schema/invalid/metadata/empty-module-string.agent.yaml +26 -0
  461. package/test/fixtures/agent-schema/invalid/metadata/empty-name.agent.yaml +24 -0
  462. package/test/fixtures/agent-schema/invalid/metadata/extra-metadata-fields.agent.yaml +27 -0
  463. package/test/fixtures/agent-schema/invalid/metadata/missing-id.agent.yaml +23 -0
  464. package/test/fixtures/agent-schema/invalid/persona/empty-principles-array.agent.yaml +24 -0
  465. package/test/fixtures/agent-schema/invalid/persona/empty-string-in-principles.agent.yaml +27 -0
  466. package/test/fixtures/agent-schema/invalid/persona/extra-persona-fields.agent.yaml +27 -0
  467. package/test/fixtures/agent-schema/invalid/persona/missing-role.agent.yaml +24 -0
  468. package/test/fixtures/agent-schema/invalid/prompts/empty-content.agent.yaml +29 -0
  469. package/test/fixtures/agent-schema/invalid/prompts/extra-prompt-fields.agent.yaml +31 -0
  470. package/test/fixtures/agent-schema/invalid/prompts/missing-content.agent.yaml +28 -0
  471. package/test/fixtures/agent-schema/invalid/prompts/missing-id.agent.yaml +28 -0
  472. package/test/fixtures/agent-schema/invalid/top-level/empty-file.agent.yaml +5 -0
  473. package/test/fixtures/agent-schema/invalid/top-level/extra-top-level-keys.agent.yaml +28 -0
  474. package/test/fixtures/agent-schema/invalid/top-level/missing-agent-key.agent.yaml +11 -0
  475. package/test/fixtures/agent-schema/invalid/yaml-errors/invalid-indentation.agent.yaml +19 -0
  476. package/test/fixtures/agent-schema/invalid/yaml-errors/malformed-yaml.agent.yaml +18 -0
  477. package/test/fixtures/agent-schema/valid/critical-actions/empty-critical-actions.agent.yaml +24 -0
  478. package/test/fixtures/agent-schema/valid/critical-actions/no-critical-actions.agent.yaml +22 -0
  479. package/test/fixtures/agent-schema/valid/critical-actions/valid-critical-actions.agent.yaml +27 -0
  480. package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +31 -0
  481. package/test/fixtures/agent-schema/valid/menu/single-menu-item.agent.yaml +22 -0
  482. package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +38 -0
  483. package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +24 -0
  484. package/test/fixtures/agent-schema/valid/menu-triggers/compound-triggers.agent.yaml +31 -0
  485. package/test/fixtures/agent-schema/valid/menu-triggers/kebab-case-triggers.agent.yaml +34 -0
  486. package/test/fixtures/agent-schema/valid/metadata/core-agent-with-module.agent.yaml +24 -0
  487. package/test/fixtures/agent-schema/valid/metadata/empty-module-name-in-path.agent.yaml +24 -0
  488. package/test/fixtures/agent-schema/valid/metadata/malformed-path-treated-as-core.agent.yaml +24 -0
  489. package/test/fixtures/agent-schema/valid/metadata/module-agent-correct.agent.yaml +24 -0
  490. package/test/fixtures/agent-schema/valid/metadata/module-agent-missing-module.agent.yaml +23 -0
  491. package/test/fixtures/agent-schema/valid/metadata/wrong-module-value.agent.yaml +24 -0
  492. package/test/fixtures/agent-schema/valid/persona/complete-persona.agent.yaml +24 -0
  493. package/test/fixtures/agent-schema/valid/prompts/empty-prompts.agent.yaml +24 -0
  494. package/test/fixtures/agent-schema/valid/prompts/no-prompts.agent.yaml +22 -0
  495. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-minimal.agent.yaml +28 -0
  496. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-with-description.agent.yaml +30 -0
  497. package/test/fixtures/agent-schema/valid/top-level/minimal-core-agent.agent.yaml +24 -0
  498. package/test/fixtures/file-refs-csv/invalid/all-empty-workflow.csv +3 -0
  499. package/test/fixtures/file-refs-csv/invalid/empty-data.csv +1 -0
  500. package/test/fixtures/file-refs-csv/invalid/no-workflow-column.csv +3 -0
  501. package/test/fixtures/file-refs-csv/invalid/unresolvable-vars.csv +3 -0
  502. package/test/fixtures/file-refs-csv/valid/bmm-style.csv +3 -0
  503. package/test/fixtures/file-refs-csv/valid/core-style.csv +3 -0
  504. package/test/fixtures/file-refs-csv/valid/minimal.csv +2 -0
  505. package/test/test-agent-schema.js +387 -0
  506. package/test/test-cli-integration.sh +159 -0
  507. package/test/test-file-refs-csv.js +133 -0
  508. package/test/test-installation-components.js +212 -0
  509. package/test/test-rehype-plugins.mjs +1050 -0
  510. package/test/unit-test-schema.js +133 -0
  511. package/tests/run_all_tests.py +80 -0
  512. package/tests/scenarios/cis/brainstorming-coach.test.py +150 -0
  513. package/tests/scenarios/cis/creative-problem-solver.test.py +167 -0
  514. package/tests/scenarios/cis/design-thinking-coach.test.py +177 -0
  515. package/tests/scenarios/cis/innovation-strategist.test.py +191 -0
  516. package/tests/scenarios/cis/presentation-master.test.py +240 -0
  517. package/tests/scenarios/cis/storyteller.test.py +324 -0
  518. package/tests/scenarios/core/mdan-master.test.py +281 -0
  519. package/tests/scenarios/mmb/agent-builder.test.py +124 -0
  520. package/tests/scenarios/mmb/module-builder.test.py +124 -0
  521. package/tests/scenarios/mmb/workflow-builder.test.py +124 -0
  522. package/tests/scenarios/mmm/analyst.test.py +138 -0
  523. package/tests/scenarios/mmm/architect.test.py +138 -0
  524. package/tests/scenarios/mmm/dev.test.py +138 -0
  525. package/tests/scenarios/mmm/pm.test.py +138 -0
  526. package/tests/scenarios/mmm/qa.test.py +138 -0
  527. package/tests/scenarios/mmm/quick-flow-solo-dev.test.py +138 -0
  528. package/tests/scenarios/mmm/sm.test.py +138 -0
  529. package/tests/scenarios/mmm/tech-writer.test.py +138 -0
  530. package/tests/scenarios/mmm/ux-designer.test.py +294 -0
  531. package/tests/scenarios/packs/db-optimization/db-performance-analyst.test.py +108 -0
  532. package/tests/scenarios/packs/db-optimization/indexing-specialist.test.py +108 -0
  533. package/tests/scenarios/packs/db-optimization/query-optimizer.test.py +106 -0
  534. package/tests/scenarios/packs/devops-azure/azure-specialist.test.py +125 -0
  535. package/tests/scenarios/packs/devops-azure/cicd-architect.test.py +122 -0
  536. package/tests/scenarios/packs/devops-azure/devops-engineer.test.py +128 -0
  537. package/tests/scenarios/packs/fintech/compliance-officer.test.py +165 -0
  538. package/tests/scenarios/packs/fintech/financial-analyst.test.py +184 -0
  539. package/tests/scenarios/packs/fintech/risk-manager.test.py +171 -0
  540. package/tests/scenarios/tea/tea.test.py +346 -0
  541. package/tests/simple_cis_test.py +285 -0
  542. package/tests/simple_db_optimization_test.py +199 -0
  543. package/tests/simple_devops_test.py +193 -0
  544. package/tests/simple_fintech_test.py +205 -0
  545. package/tests/simple_mmb_test.py +103 -0
  546. package/tests/simple_mmm_test.py +159 -0
  547. package/tests/simple_tea_test.py +80 -0
  548. package/tests/simple_test.py +111 -0
  549. package/tests/simple_ux_designer_test.py +144 -0
  550. package/tests/validate_yaml.py +86 -0
  551. package/tools/bmad-npx-wrapper.js +38 -0
  552. package/tools/build-docs.mjs +463 -0
  553. package/tools/cli/README.md +60 -0
  554. package/tools/cli/bmad-cli.js +106 -0
  555. package/tools/cli/commands/install.js +87 -0
  556. package/tools/cli/commands/status.js +65 -0
  557. package/tools/cli/commands/uninstall.js +167 -0
  558. package/tools/cli/external-official-modules.yaml +53 -0
  559. package/tools/cli/installers/install-messages.yaml +39 -0
  560. package/tools/cli/installers/lib/core/config-collector.js +1285 -0
  561. package/tools/cli/installers/lib/core/custom-module-cache.js +260 -0
  562. package/tools/cli/installers/lib/core/dependency-resolver.js +743 -0
  563. package/tools/cli/installers/lib/core/detector.js +223 -0
  564. package/tools/cli/installers/lib/core/ide-config-manager.js +157 -0
  565. package/tools/cli/installers/lib/core/installer.js +3162 -0
  566. package/tools/cli/installers/lib/core/manifest-generator.js +1081 -0
  567. package/tools/cli/installers/lib/core/manifest.js +1038 -0
  568. package/tools/cli/installers/lib/custom/handler.js +358 -0
  569. package/tools/cli/installers/lib/ide/_base-ide.js +665 -0
  570. package/tools/cli/installers/lib/ide/_config-driven.js +634 -0
  571. package/tools/cli/installers/lib/ide/codex.js +440 -0
  572. package/tools/cli/installers/lib/ide/github-copilot.js +699 -0
  573. package/tools/cli/installers/lib/ide/kilo.js +269 -0
  574. package/tools/cli/installers/lib/ide/manager.js +342 -0
  575. package/tools/cli/installers/lib/ide/platform-codes.js +100 -0
  576. package/tools/cli/installers/lib/ide/platform-codes.yaml +243 -0
  577. package/tools/cli/installers/lib/ide/rovodev.js +257 -0
  578. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +180 -0
  579. package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +174 -0
  580. package/tools/cli/installers/lib/ide/shared/module-injections.js +136 -0
  581. package/tools/cli/installers/lib/ide/shared/path-utils.js +299 -0
  582. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +366 -0
  583. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +318 -0
  584. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +14 -0
  585. package/tools/cli/installers/lib/ide/templates/combined/antigravity.md +8 -0
  586. package/tools/cli/installers/lib/ide/templates/combined/default-agent.md +15 -0
  587. package/tools/cli/installers/lib/ide/templates/combined/default-task.md +10 -0
  588. package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +10 -0
  589. package/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md +14 -0
  590. package/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +6 -0
  591. package/tools/cli/installers/lib/ide/templates/combined/gemini-agent.toml +14 -0
  592. package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +11 -0
  593. package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +11 -0
  594. package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow-yaml.toml +16 -0
  595. package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow.toml +14 -0
  596. package/tools/cli/installers/lib/ide/templates/combined/kiro-agent.md +16 -0
  597. package/tools/cli/installers/lib/ide/templates/combined/kiro-task.md +9 -0
  598. package/tools/cli/installers/lib/ide/templates/combined/kiro-tool.md +9 -0
  599. package/tools/cli/installers/lib/ide/templates/combined/kiro-workflow-yaml.md +15 -0
  600. package/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md +7 -0
  601. package/tools/cli/installers/lib/ide/templates/combined/opencode-agent.md +15 -0
  602. package/tools/cli/installers/lib/ide/templates/combined/opencode-task.md +13 -0
  603. package/tools/cli/installers/lib/ide/templates/combined/opencode-tool.md +13 -0
  604. package/tools/cli/installers/lib/ide/templates/combined/opencode-workflow-yaml.md +16 -0
  605. package/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md +16 -0
  606. package/tools/cli/installers/lib/ide/templates/combined/rovodev.md +9 -0
  607. package/tools/cli/installers/lib/ide/templates/combined/trae.md +9 -0
  608. package/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +10 -0
  609. package/tools/cli/installers/lib/ide/templates/split/.gitkeep +0 -0
  610. package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +13 -0
  611. package/tools/cli/installers/lib/ide/templates/workflow-commander.md +5 -0
  612. package/tools/cli/installers/lib/message-loader.js +83 -0
  613. package/tools/cli/installers/lib/modules/external-manager.js +136 -0
  614. package/tools/cli/installers/lib/modules/manager.js +1498 -0
  615. package/tools/cli/lib/activation-builder.js +165 -0
  616. package/tools/cli/lib/agent/compiler.js +525 -0
  617. package/tools/cli/lib/agent/installer.js +680 -0
  618. package/tools/cli/lib/agent/template-engine.js +152 -0
  619. package/tools/cli/lib/agent-analyzer.js +109 -0
  620. package/tools/cli/lib/agent-party-generator.js +194 -0
  621. package/tools/cli/lib/cli-utils.js +182 -0
  622. package/tools/cli/lib/config.js +213 -0
  623. package/tools/cli/lib/file-ops.js +204 -0
  624. package/tools/cli/lib/platform-codes.js +116 -0
  625. package/tools/cli/lib/project-root.js +77 -0
  626. package/tools/cli/lib/prompts.js +809 -0
  627. package/tools/cli/lib/ui.js +1936 -0
  628. package/tools/cli/lib/xml-handler.js +177 -0
  629. package/tools/cli/lib/xml-to-markdown.js +82 -0
  630. package/tools/cli/lib/yaml-format.js +245 -0
  631. package/tools/cli/lib/yaml-xml-builder.js +587 -0
  632. package/tools/docs/_prompt-external-modules-page.md +59 -0
  633. package/tools/docs/fix-refs.md +91 -0
  634. package/tools/fix-doc-links.js +285 -0
  635. package/tools/format-workflow-md.js +263 -0
  636. package/tools/lib/xml-utils.js +13 -0
  637. package/tools/migrate-custom-module-paths.js +124 -0
  638. package/tools/platform-codes.yaml +157 -0
  639. package/tools/schema/agent.js +491 -0
  640. package/tools/validate-agent-schema.js +110 -0
  641. package/tools/validate-doc-links.js +407 -0
  642. package/tools/validate-file-refs.js +554 -0
  643. package/tools/validate-svg-changes.sh +356 -0
  644. package/website/README.md +75 -0
  645. package/website/astro.config.mjs +136 -0
  646. package/website/public/favicon.ico +0 -0
  647. package/website/public/img/bmad-dark.png +0 -0
  648. package/website/public/img/bmad-light.png +0 -0
  649. package/website/public/workflow-map-diagram.html +361 -0
  650. package/website/src/components/Banner.astro +62 -0
  651. package/website/src/components/Header.astro +96 -0
  652. package/website/src/components/MobileMenuFooter.astro +33 -0
  653. package/website/src/content/config.ts +6 -0
  654. package/website/src/lib/site-url.mjs +25 -0
  655. package/website/src/pages/404.astro +11 -0
  656. package/website/src/pages/robots.txt.ts +48 -0
  657. package/website/src/rehype-base-paths.js +112 -0
  658. package/website/src/rehype-markdown-links.js +119 -0
  659. package/website/src/styles/custom.css +805 -0
  660. package/.mcp.json +0 -46
  661. package/agents/AGENTS-REGISTRY.md +0 -215
  662. package/agents/architect.md +0 -160
  663. package/agents/dev.md +0 -166
  664. package/agents/devops.md +0 -230
  665. package/agents/doc.md +0 -189
  666. package/agents/learn.md +0 -377
  667. package/agents/product.md +0 -124
  668. package/agents/security.md +0 -168
  669. package/agents/test.md +0 -209
  670. package/agents/ux.md +0 -207
  671. package/cli/mdan.js +0 -628
  672. package/cli/mdan.py +0 -316
  673. package/cli/mdan.sh +0 -724
  674. package/cli/postinstall.js +0 -4
  675. package/core/orchestrator.md +0 -238
  676. package/core/universal-envelope.md +0 -160
  677. package/install.sh +0 -91
  678. package/integrations/all-integrations.md +0 -300
  679. package/integrations/claude.md +0 -46
  680. package/integrations/cursor.md +0 -74
  681. package/integrations/mcp.md +0 -153
  682. package/integrations/windsurf.md +0 -48
  683. package/memory/MDAN-STATE.template.json +0 -44
  684. package/memory/MEMORY-SYSTEM.md +0 -197
  685. package/phases/01-discover.md +0 -136
  686. package/phases/02-design.md +0 -147
  687. package/phases/03-build.md +0 -113
  688. package/phases/04-verify.md +0 -107
  689. package/phases/05-ship.md +0 -156
  690. package/skills/find-skills/skill.md +0 -133
  691. package/templates/ARCHITECTURE.md +0 -186
  692. package/templates/CHANGELOG.md +0 -41
  693. package/templates/MDAN-KNOWLEDGE.md +0 -73
  694. package/templates/PRD.md +0 -120
  695. package/templates/SECURITY-REVIEW.md +0 -99
  696. package/templates/TEST-PLAN.md +0 -97
  697. package/templates/prompts/README.md +0 -108
  698. package/templates/prompts/dev-agent.yaml +0 -85
  699. package/templates/prompts/orchestrator.yaml +0 -97
  700. package/templates/prompts.json +0 -81
  701. package/templates/tests/evaluations/README.md +0 -80
  702. package/templates/tests/evaluations/classification_eval.md +0 -136
  703. package/templates/tests/evaluations/rag_eval.md +0 -116
  704. package/templates/tests/scenarios/README.md +0 -62
  705. package/templates/tests/scenarios/basic_authentication.test.md +0 -82
  706. package/templates/tests/scenarios/user_registration.test.md +0 -107
@@ -0,0 +1,1285 @@
1
+ const path = require('node:path');
2
+ const fs = require('fs-extra');
3
+ const yaml = require('yaml');
4
+ const { getProjectRoot, getModulePath } = require('../../../lib/project-root');
5
+ const { CLIUtils } = require('../../../lib/cli-utils');
6
+ const prompts = require('../../../lib/prompts');
7
+
8
+ class ConfigCollector {
9
+ constructor() {
10
+ this.collectedConfig = {};
11
+ this.existingConfig = null;
12
+ this.currentProjectDir = null;
13
+ this._moduleManagerInstance = null;
14
+ }
15
+
16
+ /**
17
+ * Get or create a cached ModuleManager instance (lazy initialization)
18
+ * @returns {Object} ModuleManager instance
19
+ */
20
+ _getModuleManager() {
21
+ if (!this._moduleManagerInstance) {
22
+ const { ModuleManager } = require('../modules/manager');
23
+ this._moduleManagerInstance = new ModuleManager();
24
+ }
25
+ return this._moduleManagerInstance;
26
+ }
27
+
28
+ /**
29
+ * Find the bmad installation directory in a project
30
+ * V6+ installations can use ANY folder name but ALWAYS have _config/manifest.yaml
31
+ * @param {string} projectDir - Project directory
32
+ * @returns {Promise<string>} Path to bmad directory
33
+ */
34
+ async findBmadDir(projectDir) {
35
+ // Check if project directory exists
36
+ if (!(await fs.pathExists(projectDir))) {
37
+ // Project doesn't exist yet, return default
38
+ return path.join(projectDir, 'bmad');
39
+ }
40
+
41
+ // V6+ strategy: Look for ANY directory with _config/manifest.yaml
42
+ // This is the definitive marker of a V6+ installation
43
+ try {
44
+ const entries = await fs.readdir(projectDir, { withFileTypes: true });
45
+ for (const entry of entries) {
46
+ if (entry.isDirectory()) {
47
+ const manifestPath = path.join(projectDir, entry.name, '_config', 'manifest.yaml');
48
+ if (await fs.pathExists(manifestPath)) {
49
+ // Found a V6+ installation
50
+ return path.join(projectDir, entry.name);
51
+ }
52
+ }
53
+ }
54
+ } catch {
55
+ // Ignore errors, fall through to default
56
+ }
57
+
58
+ // No V6+ installation found, return default
59
+ // This will be used for new installations
60
+ return path.join(projectDir, 'bmad');
61
+ }
62
+
63
+ /**
64
+ * Detect the existing BMAD folder name in a project
65
+ * @param {string} projectDir - Project directory
66
+ * @returns {Promise<string|null>} Folder name (just the name, not full path) or null if not found
67
+ */
68
+ async detectExistingBmadFolder(projectDir) {
69
+ // Check if project directory exists
70
+ if (!(await fs.pathExists(projectDir))) {
71
+ return null;
72
+ }
73
+
74
+ // Look for ANY directory with _config/manifest.yaml
75
+ try {
76
+ const entries = await fs.readdir(projectDir, { withFileTypes: true });
77
+ for (const entry of entries) {
78
+ if (entry.isDirectory()) {
79
+ const manifestPath = path.join(projectDir, entry.name, '_config', 'manifest.yaml');
80
+ if (await fs.pathExists(manifestPath)) {
81
+ // Found a V6+ installation, return just the folder name
82
+ return entry.name;
83
+ }
84
+ }
85
+ }
86
+ } catch {
87
+ // Ignore errors
88
+ }
89
+
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * Load existing config if it exists from module config files
95
+ * @param {string} projectDir - Target project directory
96
+ */
97
+ async loadExistingConfig(projectDir) {
98
+ this.existingConfig = {};
99
+
100
+ // Check if project directory exists first
101
+ if (!(await fs.pathExists(projectDir))) {
102
+ return false;
103
+ }
104
+
105
+ // Find the actual bmad directory (handles custom folder names)
106
+ const bmadDir = await this.findBmadDir(projectDir);
107
+
108
+ // Check if bmad directory exists
109
+ if (!(await fs.pathExists(bmadDir))) {
110
+ return false;
111
+ }
112
+
113
+ // Dynamically discover all installed modules by scanning bmad directory
114
+ // A directory is a module ONLY if it contains a config.yaml file
115
+ let foundAny = false;
116
+ const entries = await fs.readdir(bmadDir, { withFileTypes: true });
117
+
118
+ for (const entry of entries) {
119
+ if (entry.isDirectory()) {
120
+ // Skip the _config directory - it's for system use
121
+ if (entry.name === '_config' || entry.name === '_memory') {
122
+ continue;
123
+ }
124
+
125
+ const moduleConfigPath = path.join(bmadDir, entry.name, 'config.yaml');
126
+
127
+ if (await fs.pathExists(moduleConfigPath)) {
128
+ try {
129
+ const content = await fs.readFile(moduleConfigPath, 'utf8');
130
+ const moduleConfig = yaml.parse(content);
131
+ if (moduleConfig) {
132
+ this.existingConfig[entry.name] = moduleConfig;
133
+ foundAny = true;
134
+ }
135
+ } catch {
136
+ // Ignore parse errors for individual modules
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ return foundAny;
143
+ }
144
+
145
+ /**
146
+ * Pre-scan module schemas to gather metadata for the configuration gateway prompt.
147
+ * Returns info about which modules have configurable options.
148
+ * @param {Array} modules - List of non-core module names
149
+ * @returns {Promise<Array>} Array of {moduleName, displayName, questionCount, hasFieldsWithoutDefaults}
150
+ */
151
+ async scanModuleSchemas(modules) {
152
+ const metadataFields = new Set(['code', 'name', 'header', 'subheader', 'default_selected']);
153
+ const results = [];
154
+
155
+ for (const moduleName of modules) {
156
+ // Resolve module.yaml path - custom paths first, then standard location, then ModuleManager search
157
+ let moduleConfigPath = null;
158
+ const customPath = this.customModulePaths?.get(moduleName);
159
+ if (customPath) {
160
+ moduleConfigPath = path.join(customPath, 'module.yaml');
161
+ } else {
162
+ const standardPath = path.join(getModulePath(moduleName), 'module.yaml');
163
+ if (await fs.pathExists(standardPath)) {
164
+ moduleConfigPath = standardPath;
165
+ } else {
166
+ const moduleSourcePath = await this._getModuleManager().findModuleSource(moduleName, { silent: true });
167
+ if (moduleSourcePath) {
168
+ moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
169
+ }
170
+ }
171
+ }
172
+
173
+ if (!moduleConfigPath || !(await fs.pathExists(moduleConfigPath))) {
174
+ continue;
175
+ }
176
+
177
+ try {
178
+ const content = await fs.readFile(moduleConfigPath, 'utf8');
179
+ const moduleConfig = yaml.parse(content);
180
+ if (!moduleConfig) continue;
181
+
182
+ const displayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
183
+ const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
184
+ const questionKeys = configKeys.filter((key) => {
185
+ if (metadataFields.has(key)) return false;
186
+ const item = moduleConfig[key];
187
+ return item && typeof item === 'object' && item.prompt;
188
+ });
189
+
190
+ const hasFieldsWithoutDefaults = questionKeys.some((key) => {
191
+ const item = moduleConfig[key];
192
+ return item.default === undefined || item.default === null || item.default === '';
193
+ });
194
+
195
+ results.push({
196
+ moduleName,
197
+ displayName,
198
+ questionCount: questionKeys.length,
199
+ hasFieldsWithoutDefaults,
200
+ });
201
+ } catch (error) {
202
+ await prompts.log.warn(`Could not read schema for module "${moduleName}": ${error.message}`);
203
+ }
204
+ }
205
+
206
+ return results;
207
+ }
208
+
209
+ /**
210
+ * Collect configuration for all modules
211
+ * @param {Array} modules - List of modules to configure (including 'core')
212
+ * @param {string} projectDir - Target project directory
213
+ * @param {Object} options - Additional options
214
+ * @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
215
+ * @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
216
+ */
217
+ async collectAllConfigurations(modules, projectDir, options = {}) {
218
+ // Store custom module paths for use in collectModuleConfig
219
+ this.customModulePaths = options.customModulePaths || new Map();
220
+ this.skipPrompts = options.skipPrompts || false;
221
+ this.modulesToCustomize = undefined;
222
+ await this.loadExistingConfig(projectDir);
223
+
224
+ // Check if core was already collected (e.g., in early collection phase)
225
+ const coreAlreadyCollected = this.collectedConfig.core && Object.keys(this.collectedConfig.core).length > 0;
226
+
227
+ // If core wasn't already collected, include it
228
+ const allModules = coreAlreadyCollected ? modules.filter((m) => m !== 'core') : ['core', ...modules.filter((m) => m !== 'core')];
229
+
230
+ // Store all answers across modules for cross-referencing
231
+ if (!this.allAnswers) {
232
+ this.allAnswers = {};
233
+ }
234
+
235
+ // Split processing: core first, then gateway, then remaining modules
236
+ const coreModules = allModules.filter((m) => m === 'core');
237
+ const nonCoreModules = allModules.filter((m) => m !== 'core');
238
+
239
+ // Collect core config first (always fully prompted)
240
+ for (const moduleName of coreModules) {
241
+ await this.collectModuleConfig(moduleName, projectDir);
242
+ }
243
+
244
+ // Show batch configuration gateway for non-core modules
245
+ // Scan all non-core module schemas for display names and config metadata
246
+ let scannedModules = [];
247
+ if (!this.skipPrompts && nonCoreModules.length > 0) {
248
+ scannedModules = await this.scanModuleSchemas(nonCoreModules);
249
+ const customizableModules = scannedModules.filter((m) => m.questionCount > 0);
250
+
251
+ if (customizableModules.length > 0) {
252
+ const configMode = await prompts.select({
253
+ message: 'Module configuration',
254
+ choices: [
255
+ { name: 'Express Setup', value: 'express', hint: 'accept all defaults (recommended)' },
256
+ { name: 'Customize', value: 'customize', hint: 'choose modules to configure' },
257
+ ],
258
+ default: 'express',
259
+ });
260
+
261
+ if (configMode === 'customize') {
262
+ const choices = customizableModules.map((m) => ({
263
+ name: `${m.displayName} (${m.questionCount} option${m.questionCount === 1 ? '' : 's'})`,
264
+ value: m.moduleName,
265
+ hint: m.hasFieldsWithoutDefaults ? 'has fields without defaults' : undefined,
266
+ checked: m.hasFieldsWithoutDefaults,
267
+ }));
268
+ const selected = await prompts.multiselect({
269
+ message: 'Select modules to customize:',
270
+ choices,
271
+ required: false,
272
+ });
273
+ this.modulesToCustomize = new Set(selected);
274
+ } else {
275
+ // Express mode: no modules to customize
276
+ this.modulesToCustomize = new Set();
277
+ }
278
+ } else {
279
+ // All non-core modules have zero config - no gateway needed
280
+ this.modulesToCustomize = new Set();
281
+ }
282
+ }
283
+
284
+ // Collect remaining non-core modules
285
+ if (this.modulesToCustomize === undefined) {
286
+ // No gateway was shown (skipPrompts, no non-core modules, or direct call) - process all normally
287
+ for (const moduleName of nonCoreModules) {
288
+ await this.collectModuleConfig(moduleName, projectDir);
289
+ }
290
+ } else {
291
+ // Split into default modules (tasks progress) and customized modules (interactive)
292
+ const defaultModules = nonCoreModules.filter((m) => !this.modulesToCustomize.has(m));
293
+ const customizeModules = nonCoreModules.filter((m) => this.modulesToCustomize.has(m));
294
+
295
+ // Run default modules with a single spinner
296
+ if (defaultModules.length > 0) {
297
+ // Build display name map from all scanned modules for pre-call spinner messages
298
+ const displayNameMap = new Map();
299
+ for (const m of scannedModules) {
300
+ displayNameMap.set(m.moduleName, m.displayName);
301
+ }
302
+
303
+ const configSpinner = await prompts.spinner();
304
+ configSpinner.start('Configuring modules...');
305
+ try {
306
+ for (const moduleName of defaultModules) {
307
+ const displayName = displayNameMap.get(moduleName) || moduleName.toUpperCase();
308
+ configSpinner.message(`Configuring ${displayName}...`);
309
+ try {
310
+ this._silentConfig = true;
311
+ await this.collectModuleConfig(moduleName, projectDir);
312
+ } finally {
313
+ this._silentConfig = false;
314
+ }
315
+ }
316
+ } finally {
317
+ configSpinner.stop(customizeModules.length > 0 ? 'Module defaults applied' : 'Module configuration complete');
318
+ }
319
+ }
320
+
321
+ // Run customized modules individually (may show interactive prompts)
322
+ for (const moduleName of customizeModules) {
323
+ await this.collectModuleConfig(moduleName, projectDir);
324
+ }
325
+
326
+ if (customizeModules.length > 0) {
327
+ await prompts.log.step('Module configuration complete');
328
+ }
329
+ }
330
+
331
+ // Add metadata
332
+ this.collectedConfig._meta = {
333
+ version: require(path.join(getProjectRoot(), 'package.json')).version,
334
+ installDate: new Date().toISOString(),
335
+ lastModified: new Date().toISOString(),
336
+ };
337
+
338
+ return this.collectedConfig;
339
+ }
340
+
341
+ /**
342
+ * Collect configuration for a single module (Quick Update mode - only new fields)
343
+ * @param {string} moduleName - Module name
344
+ * @param {string} projectDir - Target project directory
345
+ * @param {boolean} silentMode - If true, only prompt for new/missing fields
346
+ * @returns {boolean} True if new fields were prompted, false if all fields existed
347
+ */
348
+ async collectModuleConfigQuick(moduleName, projectDir, silentMode = true) {
349
+ this.currentProjectDir = projectDir;
350
+
351
+ // Load existing config if not already loaded
352
+ if (!this.existingConfig) {
353
+ await this.loadExistingConfig(projectDir);
354
+ }
355
+
356
+ // Initialize allAnswers if not already initialized
357
+ if (!this.allAnswers) {
358
+ this.allAnswers = {};
359
+ }
360
+
361
+ // Load module's config schema from module.yaml
362
+ // First, try the standard src/modules location
363
+ let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
364
+
365
+ // If not found in src/modules, we need to find it by searching the project
366
+ if (!(await fs.pathExists(moduleConfigPath))) {
367
+ const moduleSourcePath = await this._getModuleManager().findModuleSource(moduleName, { silent: true });
368
+
369
+ if (moduleSourcePath) {
370
+ moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
371
+ }
372
+ }
373
+
374
+ let configPath = null;
375
+ let isCustomModule = false;
376
+
377
+ if (await fs.pathExists(moduleConfigPath)) {
378
+ configPath = moduleConfigPath;
379
+ } else {
380
+ // Check if this is a custom module with custom.yaml
381
+ const moduleSourcePath = await this._getModuleManager().findModuleSource(moduleName, { silent: true });
382
+
383
+ if (moduleSourcePath) {
384
+ const rootCustomConfigPath = path.join(moduleSourcePath, 'custom.yaml');
385
+
386
+ if (await fs.pathExists(rootCustomConfigPath)) {
387
+ isCustomModule = true;
388
+ // For custom modules, we don't have an install-config schema, so just use existing values
389
+ // The custom.yaml values will be loaded and merged during installation
390
+ }
391
+ }
392
+
393
+ // No config schema for this module - use existing values
394
+ if (this.existingConfig && this.existingConfig[moduleName]) {
395
+ if (!this.collectedConfig[moduleName]) {
396
+ this.collectedConfig[moduleName] = {};
397
+ }
398
+ this.collectedConfig[moduleName] = { ...this.existingConfig[moduleName] };
399
+ }
400
+ return false;
401
+ }
402
+
403
+ const configContent = await fs.readFile(configPath, 'utf8');
404
+ const moduleConfig = yaml.parse(configContent);
405
+
406
+ if (!moduleConfig) {
407
+ return false;
408
+ }
409
+
410
+ // Compare schema with existing config to find new/missing fields
411
+ const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
412
+ const existingKeys = this.existingConfig && this.existingConfig[moduleName] ? Object.keys(this.existingConfig[moduleName]) : [];
413
+
414
+ // Check if this module has no configuration keys at all (like CIS)
415
+ // Filter out metadata fields and only count actual config objects
416
+ const metadataFields = new Set(['code', 'name', 'header', 'subheader', 'default_selected']);
417
+ const actualConfigKeys = configKeys.filter((key) => !metadataFields.has(key));
418
+ const hasNoConfig = actualConfigKeys.length === 0;
419
+
420
+ // If module has no config keys at all, handle it specially
421
+ if (hasNoConfig && moduleConfig.subheader) {
422
+ const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
423
+ await prompts.log.step(moduleDisplayName);
424
+ await prompts.log.message(` \u2713 ${moduleConfig.subheader}`);
425
+ return false; // No new fields
426
+ }
427
+
428
+ // Find new interactive fields (with prompt)
429
+ const newKeys = configKeys.filter((key) => {
430
+ const item = moduleConfig[key];
431
+ // Check if it's a config item and doesn't exist in existing config
432
+ return item && typeof item === 'object' && item.prompt && !existingKeys.includes(key);
433
+ });
434
+
435
+ // Find new static fields (without prompt, just result)
436
+ const newStaticKeys = configKeys.filter((key) => {
437
+ const item = moduleConfig[key];
438
+ return item && typeof item === 'object' && !item.prompt && item.result && !existingKeys.includes(key);
439
+ });
440
+
441
+ // If in silent mode and no new keys (neither interactive nor static), use existing config and skip prompts
442
+ if (silentMode && newKeys.length === 0 && newStaticKeys.length === 0) {
443
+ if (this.existingConfig && this.existingConfig[moduleName]) {
444
+ if (!this.collectedConfig[moduleName]) {
445
+ this.collectedConfig[moduleName] = {};
446
+ }
447
+ this.collectedConfig[moduleName] = { ...this.existingConfig[moduleName] };
448
+
449
+ // Special handling for user_name: ensure it has a value
450
+ if (
451
+ moduleName === 'core' &&
452
+ (!this.collectedConfig[moduleName].user_name || this.collectedConfig[moduleName].user_name === '[USER_NAME]')
453
+ ) {
454
+ this.collectedConfig[moduleName].user_name = this.getDefaultUsername();
455
+ }
456
+
457
+ // Also populate allAnswers for cross-referencing
458
+ for (const [key, value] of Object.entries(this.existingConfig[moduleName])) {
459
+ // Ensure user_name is properly set in allAnswers too
460
+ let finalValue = value;
461
+ if (moduleName === 'core' && key === 'user_name' && (!value || value === '[USER_NAME]')) {
462
+ finalValue = this.getDefaultUsername();
463
+ }
464
+ this.allAnswers[`${moduleName}_${key}`] = finalValue;
465
+ }
466
+ } else if (moduleName === 'core') {
467
+ // No existing core config - ensure we at least have user_name
468
+ if (!this.collectedConfig[moduleName]) {
469
+ this.collectedConfig[moduleName] = {};
470
+ }
471
+ if (!this.collectedConfig[moduleName].user_name) {
472
+ this.collectedConfig[moduleName].user_name = this.getDefaultUsername();
473
+ this.allAnswers[`${moduleName}_user_name`] = this.getDefaultUsername();
474
+ }
475
+ }
476
+
477
+ // Show "no config" message for modules with no new questions (that have config keys)
478
+ await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module already up to date`);
479
+ return false; // No new fields
480
+ }
481
+
482
+ // If we have new fields (interactive or static), process them
483
+ if (newKeys.length > 0 || newStaticKeys.length > 0) {
484
+ const questions = [];
485
+ const staticAnswers = {};
486
+
487
+ // Build questions for interactive fields
488
+ for (const key of newKeys) {
489
+ const item = moduleConfig[key];
490
+ const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
491
+ if (question) {
492
+ questions.push(question);
493
+ }
494
+ }
495
+
496
+ // Prepare static answers (no prompt, just result)
497
+ for (const key of newStaticKeys) {
498
+ staticAnswers[`${moduleName}_${key}`] = undefined;
499
+ }
500
+
501
+ // Collect all answers (static + prompted)
502
+ let allAnswers = { ...staticAnswers };
503
+
504
+ if (questions.length > 0) {
505
+ // Only show header if we actually have questions
506
+ await CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
507
+ await prompts.log.message('');
508
+ const promptedAnswers = await prompts.prompt(questions);
509
+
510
+ // Merge prompted answers with static answers
511
+ Object.assign(allAnswers, promptedAnswers);
512
+ } else if (newStaticKeys.length > 0) {
513
+ // Only static fields, no questions - show no config message
514
+ await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configuration updated`);
515
+ }
516
+
517
+ // Store all answers for cross-referencing
518
+ Object.assign(this.allAnswers, allAnswers);
519
+
520
+ // Process all answers (both static and prompted)
521
+ // First, copy existing config to preserve values that aren't being updated
522
+ if (this.existingConfig && this.existingConfig[moduleName]) {
523
+ this.collectedConfig[moduleName] = { ...this.existingConfig[moduleName] };
524
+ } else {
525
+ this.collectedConfig[moduleName] = {};
526
+ }
527
+
528
+ for (const key of Object.keys(allAnswers)) {
529
+ const originalKey = key.replace(`${moduleName}_`, '');
530
+ const item = moduleConfig[originalKey];
531
+ const value = allAnswers[key];
532
+
533
+ let result;
534
+ if (Array.isArray(value)) {
535
+ result = value;
536
+ } else if (item.result) {
537
+ result = this.processResultTemplate(item.result, value);
538
+ } else {
539
+ result = value;
540
+ }
541
+
542
+ // Update the collected config with new/updated values
543
+ this.collectedConfig[moduleName][originalKey] = result;
544
+ }
545
+ }
546
+
547
+ // Copy over existing values for fields that weren't prompted
548
+ if (this.existingConfig && this.existingConfig[moduleName]) {
549
+ if (!this.collectedConfig[moduleName]) {
550
+ this.collectedConfig[moduleName] = {};
551
+ }
552
+ for (const [key, value] of Object.entries(this.existingConfig[moduleName])) {
553
+ if (!this.collectedConfig[moduleName][key]) {
554
+ this.collectedConfig[moduleName][key] = value;
555
+ this.allAnswers[`${moduleName}_${key}`] = value;
556
+ }
557
+ }
558
+ }
559
+
560
+ await this.displayModulePostConfigNotes(moduleName, moduleConfig);
561
+
562
+ return newKeys.length > 0 || newStaticKeys.length > 0; // Return true if we had any new fields (interactive or static)
563
+ }
564
+
565
+ /**
566
+ * Process a result template with value substitution
567
+ * @param {*} resultTemplate - The result template
568
+ * @param {*} value - The value to substitute
569
+ * @returns {*} Processed result
570
+ */
571
+ processResultTemplate(resultTemplate, value) {
572
+ let result = resultTemplate;
573
+
574
+ if (typeof result === 'string' && value !== undefined) {
575
+ if (typeof value === 'string') {
576
+ result = result.replace('{value}', value);
577
+ } else if (typeof value === 'boolean' || typeof value === 'number') {
578
+ if (result === '{value}') {
579
+ result = value;
580
+ } else {
581
+ result = result.replace('{value}', value);
582
+ }
583
+ } else {
584
+ result = value;
585
+ }
586
+
587
+ if (typeof result === 'string') {
588
+ result = result.replaceAll(/{([^}]+)}/g, (match, configKey) => {
589
+ if (configKey === 'project-root') {
590
+ return '{project-root}';
591
+ }
592
+ if (configKey === 'value') {
593
+ return match;
594
+ }
595
+
596
+ let configValue = this.allAnswers[configKey] || this.allAnswers[`${configKey}`];
597
+ if (!configValue) {
598
+ for (const [answerKey, answerValue] of Object.entries(this.allAnswers)) {
599
+ if (answerKey.endsWith(`_${configKey}`)) {
600
+ configValue = answerValue;
601
+ break;
602
+ }
603
+ }
604
+ }
605
+
606
+ if (!configValue) {
607
+ for (const mod of Object.keys(this.collectedConfig)) {
608
+ if (mod !== '_meta' && this.collectedConfig[mod] && this.collectedConfig[mod][configKey]) {
609
+ configValue = this.collectedConfig[mod][configKey];
610
+ if (typeof configValue === 'string' && configValue.includes('{project-root}/')) {
611
+ configValue = configValue.replace('{project-root}/', '');
612
+ }
613
+ break;
614
+ }
615
+ }
616
+ }
617
+
618
+ return configValue || match;
619
+ });
620
+ }
621
+ }
622
+
623
+ return result;
624
+ }
625
+
626
+ /**
627
+ * Get the default username from the system
628
+ * @returns {string} Capitalized username\
629
+ */
630
+ getDefaultUsername() {
631
+ let result = 'BMad';
632
+ try {
633
+ const os = require('node:os');
634
+ const userInfo = os.userInfo();
635
+ if (userInfo && userInfo.username) {
636
+ const username = userInfo.username;
637
+ result = username.charAt(0).toUpperCase() + username.slice(1);
638
+ }
639
+ } catch {
640
+ // Do nothing, just return 'BMad'
641
+ }
642
+ return result;
643
+ }
644
+
645
+ /**
646
+ * Collect configuration for a single module
647
+ * @param {string} moduleName - Module name
648
+ * @param {string} projectDir - Target project directory
649
+ * @param {boolean} skipLoadExisting - Skip loading existing config (for early core collection)
650
+ * @param {boolean} skipCompletion - Skip showing completion message (for early core collection)
651
+ */
652
+ async collectModuleConfig(moduleName, projectDir, skipLoadExisting = false, skipCompletion = false) {
653
+ this.currentProjectDir = projectDir;
654
+ // Load existing config if needed and not already loaded
655
+ if (!skipLoadExisting && !this.existingConfig) {
656
+ await this.loadExistingConfig(projectDir);
657
+ }
658
+
659
+ // Initialize allAnswers if not already initialized
660
+ if (!this.allAnswers) {
661
+ this.allAnswers = {};
662
+ }
663
+ // Load module's config
664
+ // First, check if we have a custom module path for this module
665
+ let moduleConfigPath = null;
666
+
667
+ if (this.customModulePaths && this.customModulePaths.has(moduleName)) {
668
+ const customPath = this.customModulePaths.get(moduleName);
669
+ moduleConfigPath = path.join(customPath, 'module.yaml');
670
+ } else {
671
+ // Try the standard src/modules location
672
+ moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
673
+ }
674
+
675
+ // If not found in src/modules or custom paths, search the project
676
+ if (!(await fs.pathExists(moduleConfigPath))) {
677
+ const moduleSourcePath = await this._getModuleManager().findModuleSource(moduleName, { silent: true });
678
+
679
+ if (moduleSourcePath) {
680
+ moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
681
+ }
682
+ }
683
+
684
+ let configPath = null;
685
+ if (await fs.pathExists(moduleConfigPath)) {
686
+ configPath = moduleConfigPath;
687
+ } else {
688
+ // No config for this module
689
+ return;
690
+ }
691
+
692
+ const configContent = await fs.readFile(configPath, 'utf8');
693
+ const moduleConfig = yaml.parse(configContent);
694
+
695
+ if (!moduleConfig) {
696
+ return;
697
+ }
698
+
699
+ // Process each config item
700
+ const questions = [];
701
+ const staticAnswers = {};
702
+ const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
703
+
704
+ for (const key of configKeys) {
705
+ const item = moduleConfig[key];
706
+
707
+ // Skip if not a config object
708
+ if (!item || typeof item !== 'object') {
709
+ continue;
710
+ }
711
+
712
+ // Handle static values (no prompt, just result)
713
+ if (!item.prompt && item.result) {
714
+ // Add to static answers with a marker value
715
+ staticAnswers[`${moduleName}_${key}`] = undefined;
716
+ continue;
717
+ }
718
+
719
+ // Handle interactive values (with prompt)
720
+ if (item.prompt) {
721
+ const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
722
+ if (question) {
723
+ questions.push(question);
724
+ }
725
+ }
726
+ }
727
+
728
+ // Collect all answers (static + prompted)
729
+ let allAnswers = { ...staticAnswers };
730
+
731
+ // If there are questions to ask, prompt for accepting defaults vs customizing
732
+ if (questions.length > 0) {
733
+ const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
734
+
735
+ // Skip prompts mode: use all defaults without asking
736
+ if (this.skipPrompts) {
737
+ await prompts.log.info(`Using default configuration for ${moduleDisplayName}`);
738
+ // Use defaults for all questions
739
+ for (const question of questions) {
740
+ const hasDefault = question.default !== undefined && question.default !== null && question.default !== '';
741
+ if (hasDefault && typeof question.default !== 'function') {
742
+ allAnswers[question.name] = question.default;
743
+ }
744
+ }
745
+ } else {
746
+ if (!this._silentConfig) await prompts.log.step(`Configuring ${moduleDisplayName}`);
747
+ let useDefaults = true;
748
+ if (moduleName === 'core') {
749
+ useDefaults = false; // Core: always show all questions
750
+ } else if (this.modulesToCustomize === undefined) {
751
+ // Fallback: original per-module confirm (backward compat for direct calls)
752
+ const customizeAnswer = await prompts.prompt([
753
+ {
754
+ type: 'confirm',
755
+ name: 'customize',
756
+ message: 'Accept Defaults (no to customize)?',
757
+ default: true,
758
+ },
759
+ ]);
760
+ useDefaults = customizeAnswer.customize;
761
+ } else {
762
+ // Batch mode: use defaults unless module was selected for customization
763
+ useDefaults = !this.modulesToCustomize.has(moduleName);
764
+ }
765
+
766
+ if (useDefaults && moduleName !== 'core') {
767
+ // Accept defaults - only ask questions that have NO default value
768
+ const questionsWithoutDefaults = questions.filter((q) => q.default === undefined || q.default === null || q.default === '');
769
+
770
+ if (questionsWithoutDefaults.length > 0) {
771
+ await prompts.log.message(` Asking required questions for ${moduleName.toUpperCase()}...`);
772
+ const promptedAnswers = await prompts.prompt(questionsWithoutDefaults);
773
+ Object.assign(allAnswers, promptedAnswers);
774
+ }
775
+
776
+ // For questions with defaults that weren't asked, we need to process them with their default values
777
+ const questionsWithDefaults = questions.filter((q) => q.default !== undefined && q.default !== null && q.default !== '');
778
+ for (const question of questionsWithDefaults) {
779
+ // Skip function defaults - these are dynamic and will be evaluated later
780
+ if (typeof question.default === 'function') {
781
+ continue;
782
+ }
783
+ allAnswers[question.name] = question.default;
784
+ }
785
+ } else {
786
+ const promptedAnswers = await prompts.prompt(questions);
787
+ Object.assign(allAnswers, promptedAnswers);
788
+ }
789
+ }
790
+ }
791
+
792
+ // Store all answers for cross-referencing
793
+ Object.assign(this.allAnswers, allAnswers);
794
+
795
+ // Process all answers (both static and prompted)
796
+ // Always process if we have any answers or static answers
797
+ if (Object.keys(allAnswers).length > 0 || Object.keys(staticAnswers).length > 0) {
798
+ const answers = allAnswers;
799
+
800
+ // Process answers and build result values
801
+ for (const key of Object.keys(answers)) {
802
+ const originalKey = key.replace(`${moduleName}_`, '');
803
+ const item = moduleConfig[originalKey];
804
+ const value = answers[key];
805
+
806
+ // Build the result using the template
807
+ let result;
808
+
809
+ // For arrays (multi-select), handle differently
810
+ if (Array.isArray(value)) {
811
+ result = value;
812
+ } else if (item.result) {
813
+ result = item.result;
814
+
815
+ // Replace placeholders only for strings
816
+ if (typeof result === 'string' && value !== undefined) {
817
+ // Replace {value} with the actual value
818
+ if (typeof value === 'string') {
819
+ result = result.replace('{value}', value);
820
+ } else if (typeof value === 'boolean' || typeof value === 'number') {
821
+ // For boolean and number values, if result is just "{value}", use the raw value
822
+ if (result === '{value}') {
823
+ result = value;
824
+ } else {
825
+ result = result.replace('{value}', value);
826
+ }
827
+ } else {
828
+ result = value;
829
+ }
830
+
831
+ // Only do further replacements if result is still a string
832
+ if (typeof result === 'string') {
833
+ // Replace references to other config values
834
+ result = result.replaceAll(/{([^}]+)}/g, (match, configKey) => {
835
+ // Check if it's a special placeholder
836
+ if (configKey === 'project-root') {
837
+ return '{project-root}';
838
+ }
839
+
840
+ // Skip if it's the 'value' placeholder we already handled
841
+ if (configKey === 'value') {
842
+ return match;
843
+ }
844
+
845
+ // Look for the config value across all modules
846
+ // First check if it's in the current module's answers
847
+ let configValue = answers[`${moduleName}_${configKey}`];
848
+
849
+ // Then check all answers (for cross-module references like outputFolder)
850
+ if (!configValue) {
851
+ // Try with various module prefixes
852
+ for (const [answerKey, answerValue] of Object.entries(this.allAnswers)) {
853
+ if (answerKey.endsWith(`_${configKey}`)) {
854
+ configValue = answerValue;
855
+ break;
856
+ }
857
+ }
858
+ }
859
+
860
+ // Check in already collected config
861
+ if (!configValue) {
862
+ for (const mod of Object.keys(this.collectedConfig)) {
863
+ if (mod !== '_meta' && this.collectedConfig[mod] && this.collectedConfig[mod][configKey]) {
864
+ configValue = this.collectedConfig[mod][configKey];
865
+ break;
866
+ }
867
+ }
868
+ }
869
+
870
+ return configValue || match;
871
+ });
872
+ }
873
+ }
874
+ } else {
875
+ result = value;
876
+ }
877
+
878
+ // Store only the result value (no prompts, defaults, examples, etc.)
879
+ if (!this.collectedConfig[moduleName]) {
880
+ this.collectedConfig[moduleName] = {};
881
+ }
882
+ this.collectedConfig[moduleName][originalKey] = result;
883
+ }
884
+
885
+ // No longer display completion boxes - keep output clean
886
+ } else {
887
+ // No questions for this module - show completion message with header if available
888
+ const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
889
+
890
+ // Check if this module has NO configuration keys at all (like CIS)
891
+ // Filter out metadata fields and only count actual config objects
892
+ const metadataFields = new Set(['code', 'name', 'header', 'subheader', 'default_selected']);
893
+ const actualConfigKeys = configKeys.filter((key) => !metadataFields.has(key));
894
+ const hasNoConfig = actualConfigKeys.length === 0;
895
+
896
+ if (!this._silentConfig) {
897
+ if (hasNoConfig && (moduleConfig.subheader || moduleConfig.header)) {
898
+ await prompts.log.step(moduleDisplayName);
899
+ if (moduleConfig.subheader) {
900
+ await prompts.log.message(` \u2713 ${moduleConfig.subheader}`);
901
+ } else {
902
+ await prompts.log.message(` \u2713 No custom configuration required`);
903
+ }
904
+ } else {
905
+ // Module has config but just no questions to ask
906
+ await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configured`);
907
+ }
908
+ }
909
+ }
910
+
911
+ // If we have no collected config for this module, but we have a module schema,
912
+ // ensure we have at least an empty object
913
+ if (!this.collectedConfig[moduleName]) {
914
+ this.collectedConfig[moduleName] = {};
915
+
916
+ // If we accepted defaults and have no answers, we still need to check
917
+ // if there are any static values in the schema that should be applied
918
+ if (moduleConfig) {
919
+ for (const key of Object.keys(moduleConfig)) {
920
+ if (key !== 'prompt' && moduleConfig[key] && typeof moduleConfig[key] === 'object') {
921
+ const item = moduleConfig[key];
922
+ // For static items (no prompt, just result), apply the result
923
+ if (!item.prompt && item.result) {
924
+ // Apply any placeholder replacements to the result
925
+ let result = item.result;
926
+ if (typeof result === 'string') {
927
+ result = this.replacePlaceholders(result, moduleName, moduleConfig);
928
+ }
929
+ this.collectedConfig[moduleName][key] = result;
930
+ }
931
+ }
932
+ }
933
+ }
934
+ }
935
+
936
+ await this.displayModulePostConfigNotes(moduleName, moduleConfig);
937
+ }
938
+
939
+ /**
940
+ * Replace placeholders in a string with collected config values
941
+ * @param {string} str - String with placeholders
942
+ * @param {string} currentModule - Current module name (to look up defaults in same module)
943
+ * @param {Object} moduleConfig - Current module's config schema (to look up defaults)
944
+ * @returns {string} String with placeholders replaced
945
+ */
946
+ replacePlaceholders(str, currentModule = null, moduleConfig = null) {
947
+ if (typeof str !== 'string') {
948
+ return str;
949
+ }
950
+
951
+ return str.replaceAll(/{([^}]+)}/g, (match, configKey) => {
952
+ // Preserve special placeholders
953
+ if (configKey === 'project-root' || configKey === 'value' || configKey === 'directory_name') {
954
+ return match;
955
+ }
956
+
957
+ // Look for the config value in allAnswers (already answered questions)
958
+ let configValue = this.allAnswers[configKey] || this.allAnswers[`core_${configKey}`];
959
+
960
+ // Check in already collected config
961
+ if (!configValue) {
962
+ for (const mod of Object.keys(this.collectedConfig)) {
963
+ if (mod !== '_meta' && this.collectedConfig[mod] && this.collectedConfig[mod][configKey]) {
964
+ configValue = this.collectedConfig[mod][configKey];
965
+ break;
966
+ }
967
+ }
968
+ }
969
+
970
+ // If still not found and we're in the same module, use the default from the config schema
971
+ if (!configValue && currentModule && moduleConfig && moduleConfig[configKey]) {
972
+ const referencedItem = moduleConfig[configKey];
973
+ if (referencedItem && referencedItem.default !== undefined) {
974
+ configValue = referencedItem.default;
975
+ }
976
+ }
977
+
978
+ return configValue || match;
979
+ });
980
+ }
981
+
982
+ /**
983
+ * Build a prompt question from a config item
984
+ * @param {string} moduleName - Module name
985
+ * @param {string} key - Config key
986
+ * @param {Object} item - Config item definition
987
+ * @param {Object} moduleConfig - Full module config schema (for resolving defaults)
988
+ */
989
+ async buildQuestion(moduleName, key, item, moduleConfig = null) {
990
+ const questionName = `${moduleName}_${key}`;
991
+
992
+ // Check for existing value
993
+ let existingValue = null;
994
+ if (this.existingConfig && this.existingConfig[moduleName]) {
995
+ existingValue = this.existingConfig[moduleName][key];
996
+
997
+ // Clean up existing value - remove {project-root}/ prefix if present
998
+ // This prevents duplication when the result template adds it back
999
+ if (typeof existingValue === 'string' && existingValue.startsWith('{project-root}/')) {
1000
+ existingValue = existingValue.replace('{project-root}/', '');
1001
+ }
1002
+ }
1003
+
1004
+ // Special handling for user_name: default to system user
1005
+ if (moduleName === 'core' && key === 'user_name' && !existingValue) {
1006
+ item.default = this.getDefaultUsername();
1007
+ }
1008
+
1009
+ // Determine question type and default value
1010
+ let questionType = 'input';
1011
+ let defaultValue = item.default;
1012
+ let choices = null;
1013
+
1014
+ // Check if default contains references to other fields in the same module
1015
+ const hasSameModuleReference = typeof defaultValue === 'string' && defaultValue.match(/{([^}]+)}/);
1016
+ let dynamicDefault = false;
1017
+
1018
+ // Replace placeholders in default value with collected config values
1019
+ if (typeof defaultValue === 'string') {
1020
+ if (defaultValue.includes('{directory_name}') && this.currentProjectDir) {
1021
+ const dirName = path.basename(this.currentProjectDir);
1022
+ defaultValue = defaultValue.replaceAll('{directory_name}', dirName);
1023
+ }
1024
+
1025
+ // Check if this references another field in the same module (for dynamic defaults)
1026
+ if (hasSameModuleReference && moduleConfig) {
1027
+ const matches = defaultValue.match(/{([^}]+)}/g);
1028
+ if (matches) {
1029
+ for (const match of matches) {
1030
+ const fieldName = match.slice(1, -1); // Remove { }
1031
+ // Check if this field exists in the same module config
1032
+ if (moduleConfig[fieldName]) {
1033
+ dynamicDefault = true;
1034
+ break;
1035
+ }
1036
+ }
1037
+ }
1038
+ }
1039
+
1040
+ // If not dynamic, replace placeholders now
1041
+ if (!dynamicDefault) {
1042
+ defaultValue = this.replacePlaceholders(defaultValue, moduleName, moduleConfig);
1043
+ }
1044
+
1045
+ // Strip {project-root}/ from defaults since it will be added back by result template
1046
+ // This makes the display cleaner and user input simpler
1047
+ if (defaultValue.includes('{project-root}/')) {
1048
+ defaultValue = defaultValue.replace('{project-root}/', '');
1049
+ }
1050
+ }
1051
+
1052
+ // Handle different question types
1053
+ if (item['single-select']) {
1054
+ questionType = 'list';
1055
+ choices = item['single-select'].map((choice) => {
1056
+ // If choice is an object with label and value
1057
+ if (typeof choice === 'object' && choice.label && choice.value !== undefined) {
1058
+ return {
1059
+ name: choice.label,
1060
+ value: choice.value,
1061
+ };
1062
+ }
1063
+ // Otherwise it's a simple string choice
1064
+ return {
1065
+ name: choice,
1066
+ value: choice,
1067
+ };
1068
+ });
1069
+ if (existingValue) {
1070
+ defaultValue = existingValue;
1071
+ }
1072
+ } else if (item['multi-select']) {
1073
+ questionType = 'checkbox';
1074
+ choices = item['multi-select'].map((choice) => {
1075
+ // If choice is an object with label and value
1076
+ if (typeof choice === 'object' && choice.label && choice.value !== undefined) {
1077
+ return {
1078
+ name: choice.label,
1079
+ value: choice.value,
1080
+ checked: existingValue
1081
+ ? existingValue.includes(choice.value)
1082
+ : item.default && Array.isArray(item.default)
1083
+ ? item.default.includes(choice.value)
1084
+ : false,
1085
+ };
1086
+ }
1087
+ // Otherwise it's a simple string choice
1088
+ return {
1089
+ name: choice,
1090
+ value: choice,
1091
+ checked: existingValue
1092
+ ? existingValue.includes(choice)
1093
+ : item.default && Array.isArray(item.default)
1094
+ ? item.default.includes(choice)
1095
+ : false,
1096
+ };
1097
+ });
1098
+ } else if (typeof defaultValue === 'boolean') {
1099
+ questionType = 'confirm';
1100
+ }
1101
+
1102
+ // Build the prompt message
1103
+ let message = '';
1104
+
1105
+ // Handle array prompts for multi-line messages
1106
+ if (Array.isArray(item.prompt)) {
1107
+ message = item.prompt.join('\n');
1108
+ } else {
1109
+ message = item.prompt;
1110
+ }
1111
+
1112
+ // Replace placeholders in prompt message with collected config values
1113
+ if (typeof message === 'string') {
1114
+ message = this.replacePlaceholders(message, moduleName, moduleConfig);
1115
+ }
1116
+
1117
+ // Add current value indicator for existing configs
1118
+ const color = await prompts.getColor();
1119
+ if (existingValue !== null && existingValue !== undefined) {
1120
+ if (typeof existingValue === 'boolean') {
1121
+ message += color.dim(` (current: ${existingValue ? 'true' : 'false'})`);
1122
+ } else if (Array.isArray(existingValue)) {
1123
+ message += color.dim(` (current: ${existingValue.join(', ')})`);
1124
+ } else if (questionType !== 'list') {
1125
+ // Show the cleaned value (without {project-root}/) for display
1126
+ message += color.dim(` (current: ${existingValue})`);
1127
+ }
1128
+ } else if (item.example && questionType === 'input') {
1129
+ // Show example for input fields
1130
+ let exampleText = typeof item.example === 'string' ? item.example : JSON.stringify(item.example);
1131
+ // Replace placeholders in example
1132
+ if (typeof exampleText === 'string') {
1133
+ exampleText = this.replacePlaceholders(exampleText, moduleName, moduleConfig);
1134
+ exampleText = exampleText.replace('{project-root}/', '');
1135
+ }
1136
+ message += color.dim(` (e.g., ${exampleText})`);
1137
+ }
1138
+
1139
+ // Build the question object
1140
+ const question = {
1141
+ type: questionType,
1142
+ name: questionName,
1143
+ message: message,
1144
+ };
1145
+
1146
+ // Set default - if it's dynamic, use a function that the prompt will evaluate with current answers
1147
+ // But if we have an existing value, always use that instead
1148
+ if (existingValue !== null && existingValue !== undefined && questionType !== 'list') {
1149
+ question.default = existingValue;
1150
+ } else if (dynamicDefault && typeof item.default === 'string') {
1151
+ const originalDefault = item.default;
1152
+ question.default = (answers) => {
1153
+ // Replace placeholders using answers from previous questions in the same batch
1154
+ let resolved = originalDefault;
1155
+ resolved = resolved.replaceAll(/{([^}]+)}/g, (match, fieldName) => {
1156
+ // Look for the answer in the current batch (prefixed with module name)
1157
+ const answerKey = `${moduleName}_${fieldName}`;
1158
+ if (answers[answerKey] !== undefined) {
1159
+ return answers[answerKey];
1160
+ }
1161
+ // Fall back to collected config
1162
+ return this.collectedConfig[moduleName]?.[fieldName] || match;
1163
+ });
1164
+ // Strip {project-root}/ for cleaner display
1165
+ if (resolved.includes('{project-root}/')) {
1166
+ resolved = resolved.replace('{project-root}/', '');
1167
+ }
1168
+ return resolved;
1169
+ };
1170
+ } else {
1171
+ question.default = defaultValue;
1172
+ }
1173
+
1174
+ // Add choices for select types
1175
+ if (choices) {
1176
+ question.choices = choices;
1177
+ }
1178
+
1179
+ // Add validation for input fields
1180
+ if (questionType === 'input') {
1181
+ question.validate = (input) => {
1182
+ if (!input && item.required) {
1183
+ return 'This field is required';
1184
+ }
1185
+ // Validate against regex pattern if provided
1186
+ if (input && item.regex) {
1187
+ const regex = new RegExp(item.regex);
1188
+ if (!regex.test(input)) {
1189
+ return `Invalid format. Must match pattern: ${item.regex}`;
1190
+ }
1191
+ }
1192
+ return true;
1193
+ };
1194
+ }
1195
+
1196
+ // Add validation for checkbox (multi-select) fields
1197
+ if (questionType === 'checkbox' && item.required) {
1198
+ question.validate = (answers) => {
1199
+ if (!answers || answers.length === 0) {
1200
+ return 'At least one option must be selected';
1201
+ }
1202
+ return true;
1203
+ };
1204
+ }
1205
+
1206
+ return question;
1207
+ }
1208
+
1209
+ /**
1210
+ * Display post-configuration notes for a module
1211
+ * Shows prerequisite guidance based on collected config values
1212
+ * Reads notes from the module's `post-install-notes` section in module.yaml
1213
+ * Supports two formats:
1214
+ * - Simple string: always displayed
1215
+ * - Object keyed by config field name, with value-specific messages
1216
+ * @param {string} moduleName - Module name
1217
+ * @param {Object} moduleConfig - Parsed module.yaml content
1218
+ */
1219
+ async displayModulePostConfigNotes(moduleName, moduleConfig) {
1220
+ if (this._silentConfig) return;
1221
+ if (!moduleConfig || !moduleConfig['post-install-notes']) return;
1222
+
1223
+ const notes = moduleConfig['post-install-notes'];
1224
+ const color = await prompts.getColor();
1225
+
1226
+ // Format 1: Simple string - always display
1227
+ if (typeof notes === 'string') {
1228
+ await prompts.log.message('');
1229
+ for (const line of notes.trim().split('\n')) {
1230
+ await prompts.log.message(color.dim(line));
1231
+ }
1232
+ return;
1233
+ }
1234
+
1235
+ // Format 2: Conditional on config values
1236
+ if (typeof notes === 'object') {
1237
+ const config = this.collectedConfig[moduleName];
1238
+ if (!config) return;
1239
+
1240
+ let hasOutput = false;
1241
+ for (const [configKey, valueMessages] of Object.entries(notes)) {
1242
+ const selectedValue = config[configKey];
1243
+ if (!selectedValue || !valueMessages[selectedValue]) continue;
1244
+
1245
+ if (hasOutput) await prompts.log.message('');
1246
+ hasOutput = true;
1247
+
1248
+ const message = valueMessages[selectedValue];
1249
+ for (const line of message.trim().split('\n')) {
1250
+ const trimmedLine = line.trim();
1251
+ if (trimmedLine.endsWith(':') && !trimmedLine.startsWith(' ')) {
1252
+ await prompts.log.info(color.bold(trimmedLine));
1253
+ } else {
1254
+ await prompts.log.message(color.dim(' ' + trimmedLine));
1255
+ }
1256
+ }
1257
+ }
1258
+ }
1259
+ }
1260
+
1261
+ /**
1262
+ * Deep merge two objects
1263
+ * @param {Object} target - Target object
1264
+ * @param {Object} source - Source object
1265
+ */
1266
+ deepMerge(target, source) {
1267
+ const result = { ...target };
1268
+
1269
+ for (const key in source) {
1270
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
1271
+ if (result[key] && typeof result[key] === 'object' && !Array.isArray(result[key])) {
1272
+ result[key] = this.deepMerge(result[key], source[key]);
1273
+ } else {
1274
+ result[key] = source[key];
1275
+ }
1276
+ } else {
1277
+ result[key] = source[key];
1278
+ }
1279
+ }
1280
+
1281
+ return result;
1282
+ }
1283
+ }
1284
+
1285
+ module.exports = { ConfigCollector };