bmad-method 6.0.0-alpha.14 → 6.0.0-alpha.16

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 (412) hide show
  1. package/.coderabbit.yaml +36 -0
  2. package/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +4 -4
  3. package/CHANGELOG.md +168 -409
  4. package/README.md +4 -1
  5. package/docs/agent-customization-guide.md +7 -7
  6. package/docs/custom-content-installation.md +245 -0
  7. package/docs/ide-info/crush.md +1 -1
  8. package/docs/ide-info/cursor.md +7 -7
  9. package/docs/ide-info/iflow.md +3 -3
  10. package/docs/ide-info/opencode.md +1 -1
  11. package/docs/index.md +2 -2
  12. package/docs/installers-bundlers/ide-injections.md +2 -2
  13. package/docs/installers-bundlers/installers-modules-platforms-reference.md +29 -28
  14. package/docs/v4-to-v6-upgrade.md +10 -10
  15. package/docs/web-bundles-gemini-gpt-guide.md +2 -2
  16. package/package.json +1 -1
  17. package/src/core/_module-installer/installer.js +1 -1
  18. package/src/core/agents/bmad-master.agent.yaml +5 -5
  19. package/src/core/agents/bmad-web-orchestrator.agent.xml +6 -6
  20. package/src/core/{_module-installer/install-config.yaml → module.yaml} +0 -7
  21. package/src/core/resources/excalidraw/README.md +4 -4
  22. package/src/core/tasks/advanced-elicitation.xml +3 -3
  23. package/src/core/tasks/index-docs.xml +1 -1
  24. package/src/core/tasks/validate-workflow.xml +1 -1
  25. package/src/core/tasks/workflow.xml +4 -4
  26. package/src/core/tools/shard-doc.xml +1 -1
  27. package/src/core/workflows/brainstorming/workflow.md +2 -2
  28. package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +2 -2
  29. package/src/core/workflows/party-mode/workflow.md +4 -4
  30. package/src/modules/bmb/README.md +2 -2
  31. package/src/modules/bmb/_module-installer/installer.js +1 -1
  32. package/src/modules/bmb/agents/bmad-builder.agent.yaml +15 -15
  33. package/src/modules/bmb/docs/agents/agent-compilation.md +2 -2
  34. package/src/modules/bmb/docs/agents/agent-menu-patterns.md +22 -22
  35. package/src/modules/bmb/docs/agents/expert-agent-architecture.md +1 -1
  36. package/src/modules/bmb/docs/agents/index.md +2 -2
  37. package/src/modules/bmb/docs/agents/module-agent-architecture.md +50 -51
  38. package/src/modules/bmb/docs/agents/understanding-agent-types.md +3 -3
  39. package/src/modules/bmb/docs/workflows/architecture.md +1 -1
  40. package/src/modules/bmb/docs/workflows/common-workflow-tools.csv +3 -3
  41. package/src/modules/bmb/docs/workflows/templates/step-01-init-continuable-template.md +1 -1
  42. package/src/modules/bmb/docs/workflows/templates/step-1b-template.md +1 -1
  43. package/src/modules/bmb/docs/workflows/templates/step-file.md +3 -3
  44. package/src/modules/bmb/docs/workflows/templates/step-template.md +3 -3
  45. package/src/modules/bmb/docs/workflows/templates/workflow-template.md +2 -2
  46. package/src/modules/bmb/docs/workflows/templates/workflow.md +1 -1
  47. package/src/modules/bmb/{_module-installer/install-config.yaml → module.yaml} +0 -1
  48. package/src/modules/bmb/reference/agents/module-examples/README.md +1 -1
  49. package/src/modules/bmb/reference/agents/module-examples/security-engineer.agent.yaml +5 -5
  50. package/src/modules/bmb/reference/agents/module-examples/trend-analyst.agent.yaml +7 -7
  51. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +1 -1
  52. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +1 -1
  53. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +3 -3
  54. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +3 -3
  55. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +5 -5
  56. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +5 -5
  57. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +5 -5
  58. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/workflow.md +2 -2
  59. package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/README.md +1 -1
  60. package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/security-engineer.agent.yaml +6 -6
  61. package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/trend-analyst.agent.yaml +7 -7
  62. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +1 -1
  63. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +1 -1
  64. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +3 -3
  65. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +3 -3
  66. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +5 -5
  67. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +5 -5
  68. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +5 -5
  69. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/workflow.md +2 -2
  70. package/src/modules/bmb/workflows/create-agent/data/validation-complete.md +5 -5
  71. package/src/modules/bmb/workflows/create-agent/steps/step-01-brainstorm.md +4 -4
  72. package/src/modules/bmb/workflows/create-agent/steps/step-02-discover.md +4 -4
  73. package/src/modules/bmb/workflows/create-agent/steps/step-03-persona.md +4 -4
  74. package/src/modules/bmb/workflows/create-agent/steps/step-04-commands.md +7 -7
  75. package/src/modules/bmb/workflows/create-agent/steps/step-05-name.md +3 -3
  76. package/src/modules/bmb/workflows/create-agent/steps/step-06-build.md +4 -4
  77. package/src/modules/bmb/workflows/create-agent/steps/step-07-validate.md +4 -4
  78. package/src/modules/bmb/workflows/create-agent/steps/step-08-setup.md +3 -3
  79. package/src/modules/bmb/workflows/create-agent/steps/step-09-customize.md +4 -4
  80. package/src/modules/bmb/workflows/create-agent/steps/step-10-build-tools.md +3 -3
  81. package/src/modules/bmb/workflows/create-agent/steps/step-11-celebrate.md +3 -3
  82. package/src/modules/bmb/workflows/create-agent/workflow.md +13 -13
  83. package/src/modules/bmb/workflows/create-module/steps/step-01-init.md +2 -2
  84. package/src/modules/bmb/workflows/create-module/steps/step-02-concept.md +4 -4
  85. package/src/modules/bmb/workflows/create-module/steps/step-03-components.md +4 -4
  86. package/src/modules/bmb/workflows/create-module/steps/step-04-structure.md +6 -6
  87. package/src/modules/bmb/workflows/create-module/steps/step-05-config.md +5 -5
  88. package/src/modules/bmb/workflows/create-module/steps/step-06-agents.md +6 -6
  89. package/src/modules/bmb/workflows/create-module/steps/step-07-workflows.md +3 -3
  90. package/src/modules/bmb/workflows/create-module/steps/step-08-installer.md +12 -12
  91. package/src/modules/bmb/workflows/create-module/steps/step-09-documentation.md +6 -5
  92. package/src/modules/bmb/workflows/create-module/steps/step-10-roadmap.md +6 -5
  93. package/src/modules/bmb/workflows/create-module/steps/step-11-validate.md +7 -7
  94. package/src/modules/bmb/workflows/create-module/templates/agent.template.md +7 -7
  95. package/src/modules/bmb/workflows/create-module/templates/installer.template.js +1 -1
  96. package/src/modules/bmb/workflows/create-module/templates/{install-config.template.yaml → module.template.yaml} +1 -1
  97. package/src/modules/bmb/workflows/create-module/validation.md +3 -3
  98. package/src/modules/bmb/workflows/create-module/workflow.md +2 -2
  99. package/src/modules/bmb/workflows/create-workflow/steps/step-01-init.md +3 -3
  100. package/src/modules/bmb/workflows/create-workflow/steps/step-02-gather.md +5 -5
  101. package/src/modules/bmb/workflows/create-workflow/steps/step-03-tools-configuration.md +4 -4
  102. package/src/modules/bmb/workflows/create-workflow/steps/step-04-plan-review.md +3 -3
  103. package/src/modules/bmb/workflows/create-workflow/steps/step-05-output-format-design.md +3 -3
  104. package/src/modules/bmb/workflows/create-workflow/steps/step-06-design.md +10 -10
  105. package/src/modules/bmb/workflows/create-workflow/steps/step-07-build.md +13 -13
  106. package/src/modules/bmb/workflows/create-workflow/steps/step-08-review.md +3 -3
  107. package/src/modules/bmb/workflows/create-workflow/steps/step-09-complete.md +1 -1
  108. package/src/modules/bmb/workflows/create-workflow/workflow.md +1 -1
  109. package/src/modules/bmb/workflows/edit-agent/steps/step-01-discover-intent.md +3 -3
  110. package/src/modules/bmb/workflows/edit-agent/steps/step-02-analyze-agent.md +13 -13
  111. package/src/modules/bmb/workflows/edit-agent/steps/step-03-propose-changes.md +5 -5
  112. package/src/modules/bmb/workflows/edit-agent/steps/step-04-apply-changes.md +3 -3
  113. package/src/modules/bmb/workflows/edit-agent/steps/step-05-validate.md +5 -5
  114. package/src/modules/bmb/workflows/edit-agent/workflow.md +1 -1
  115. package/src/modules/bmb/workflows/edit-workflow/steps/step-01-analyze.md +4 -4
  116. package/src/modules/bmb/workflows/edit-workflow/steps/step-02-discover.md +3 -3
  117. package/src/modules/bmb/workflows/edit-workflow/steps/step-03-improve.md +6 -6
  118. package/src/modules/bmb/workflows/edit-workflow/steps/step-04-validate.md +3 -3
  119. package/src/modules/bmb/workflows/edit-workflow/steps/step-05-compliance-check.md +3 -3
  120. package/src/modules/bmb/workflows/edit-workflow/workflow.md +1 -1
  121. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-01-validate-goal.md +3 -3
  122. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-02-workflow-validation.md +5 -5
  123. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-03-step-validation.md +6 -6
  124. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-04-file-validation.md +4 -4
  125. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-05-intent-spectrum-validation.md +4 -4
  126. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-06-web-subprocess-validation.md +4 -4
  127. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-07-holistic-analysis.md +4 -4
  128. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-08-generate-report.md +3 -3
  129. package/src/modules/bmb/workflows/workflow-compliance-check/workflow.md +1 -1
  130. package/src/modules/bmb/workflows-legacy/edit-module/README.md +2 -2
  131. package/src/modules/bmb/workflows-legacy/edit-module/checklist.md +2 -2
  132. package/src/modules/bmb/workflows-legacy/edit-module/instructions.md +4 -4
  133. package/src/modules/bmb/workflows-legacy/edit-module/workflow.yaml +10 -10
  134. package/src/modules/bmb/workflows-legacy/module-brief/README.md +2 -2
  135. package/src/modules/bmb/workflows-legacy/module-brief/instructions.md +2 -2
  136. package/src/modules/bmb/workflows-legacy/module-brief/workflow.yaml +4 -4
  137. package/src/modules/bmgd/README.md +3 -2
  138. package/src/modules/bmgd/agents/game-architect.agent.yaml +6 -6
  139. package/src/modules/bmgd/agents/game-designer.agent.yaml +7 -7
  140. package/src/modules/bmgd/agents/game-dev.agent.yaml +9 -9
  141. package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +21 -21
  142. package/src/modules/bmgd/{_module-installer/install-config.yaml → module.yaml} +0 -1
  143. package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/instructions.md +1 -1
  144. package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.yaml +9 -9
  145. package/src/modules/bmgd/workflows/1-preproduction/game-brief/instructions.md +1 -1
  146. package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.yaml +8 -8
  147. package/src/modules/bmgd/workflows/2-design/gdd/instructions-gdd.md +4 -4
  148. package/src/modules/bmgd/workflows/2-design/gdd/workflow.yaml +30 -30
  149. package/src/modules/bmgd/workflows/2-design/narrative/instructions-narrative.md +1 -1
  150. package/src/modules/bmgd/workflows/2-design/narrative/workflow.yaml +5 -5
  151. package/src/modules/bmgd/workflows/3-technical/game-architecture/instructions.md +1 -1
  152. package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.yaml +2 -2
  153. package/src/modules/bmgd/workflows/4-production/code-review/instructions.md +2 -2
  154. package/src/modules/bmgd/workflows/4-production/code-review/workflow.yaml +2 -2
  155. package/src/modules/bmgd/workflows/4-production/correct-course/checklist.md +1 -1
  156. package/src/modules/bmgd/workflows/4-production/correct-course/instructions.md +2 -2
  157. package/src/modules/bmgd/workflows/4-production/correct-course/workflow.yaml +2 -2
  158. package/src/modules/bmgd/workflows/4-production/create-story/instructions.md +2 -2
  159. package/src/modules/bmgd/workflows/4-production/create-story/workflow.yaml +2 -2
  160. package/src/modules/bmgd/workflows/4-production/dev-story/instructions.md +2 -2
  161. package/src/modules/bmgd/workflows/4-production/dev-story/workflow.yaml +2 -2
  162. package/src/modules/bmgd/workflows/4-production/epic-tech-context/checklist.md +1 -1
  163. package/src/modules/bmgd/workflows/4-production/epic-tech-context/instructions.md +2 -2
  164. package/src/modules/bmgd/workflows/4-production/epic-tech-context/workflow.yaml +2 -2
  165. package/src/modules/bmgd/workflows/4-production/retrospective/instructions.md +2 -2
  166. package/src/modules/bmgd/workflows/4-production/retrospective/workflow.yaml +3 -3
  167. package/src/modules/bmgd/workflows/4-production/sprint-planning/instructions.md +2 -2
  168. package/src/modules/bmgd/workflows/4-production/sprint-planning/workflow.yaml +2 -2
  169. package/src/modules/bmgd/workflows/4-production/story-context/checklist.md +1 -1
  170. package/src/modules/bmgd/workflows/4-production/story-context/context-template.xml +1 -1
  171. package/src/modules/bmgd/workflows/4-production/story-context/instructions.md +2 -2
  172. package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +2 -2
  173. package/src/modules/bmgd/workflows/4-production/story-done/instructions.md +1 -1
  174. package/src/modules/bmgd/workflows/4-production/story-done/workflow.yaml +2 -2
  175. package/src/modules/bmgd/workflows/4-production/story-ready/instructions.md +1 -1
  176. package/src/modules/bmgd/workflows/4-production/story-ready/workflow.yaml +2 -2
  177. package/src/modules/bmm/_module-installer/installer.js +1 -1
  178. package/src/modules/bmm/_module-installer/platform-specifics/claude-code.js +1 -1
  179. package/src/modules/bmm/_module-installer/platform-specifics/windsurf.js +1 -1
  180. package/src/modules/bmm/agents/analyst.agent.yaml +8 -8
  181. package/src/modules/bmm/agents/architect.agent.yaml +8 -8
  182. package/src/modules/bmm/agents/dev.agent.yaml +3 -3
  183. package/src/modules/bmm/agents/pm.agent.yaml +8 -8
  184. package/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml +5 -5
  185. package/src/modules/bmm/agents/sm.agent.yaml +9 -9
  186. package/src/modules/bmm/agents/tea.agent.yaml +13 -13
  187. package/src/modules/bmm/agents/tech-writer.agent.yaml +9 -9
  188. package/src/modules/bmm/agents/ux-designer.agent.yaml +6 -6
  189. package/src/modules/bmm/docs/README.md +0 -25
  190. package/src/modules/bmm/docs/agents-guide.md +4 -4
  191. package/src/modules/bmm/docs/brownfield-guide.md +4 -4
  192. package/src/modules/bmm/docs/enterprise-agentic-development.md +3 -3
  193. package/src/modules/bmm/docs/faq.md +1 -1
  194. package/src/modules/bmm/docs/party-mode.md +3 -3
  195. package/src/modules/bmm/docs/quick-start.md +1 -1
  196. package/src/modules/bmm/docs/test-architecture.md +3 -3
  197. package/src/modules/bmm/docs/workflow-document-project-reference.md +1 -1
  198. package/src/modules/bmm/docs/workflows-implementation.md +0 -100
  199. package/src/modules/bmm/{_module-installer/install-config.yaml → module.yaml} +1 -2
  200. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-01-init.md +1 -1
  201. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-01b-continue.md +1 -1
  202. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-02-vision.md +3 -3
  203. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-03-users.md +3 -3
  204. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-04-metrics.md +3 -3
  205. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-05-scope.md +3 -3
  206. package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-06-complete.md +5 -3
  207. package/src/modules/bmm/workflows/1-analysis/product-brief/workflow.md +2 -2
  208. package/src/modules/bmm/workflows/1-analysis/research/workflow.md +2 -2
  209. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +4 -4
  210. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +4 -4
  211. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +4 -4
  212. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +4 -4
  213. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +4 -4
  214. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +4 -4
  215. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +4 -4
  216. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +4 -4
  217. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +4 -4
  218. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +4 -4
  219. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +4 -4
  220. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +4 -4
  221. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +2 -2
  222. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-01-init.md +1 -1
  223. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-01b-continue.md +1 -1
  224. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-02-discovery.md +3 -3
  225. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-03-success.md +7 -7
  226. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-04-journeys.md +8 -8
  227. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-05-domain.md +12 -12
  228. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-06-innovation.md +12 -12
  229. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-07-project-type.md +10 -10
  230. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-08-scoping.md +7 -7
  231. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-09-functional.md +9 -9
  232. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-10-nonfunctional.md +9 -9
  233. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-11-complete.md +1 -1
  234. package/src/modules/bmm/workflows/2-plan-workflows/prd/workflow.md +1 -1
  235. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-02-context.md +4 -4
  236. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-03-starter.md +4 -4
  237. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-04-decisions.md +4 -4
  238. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-05-patterns.md +4 -4
  239. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-06-structure.md +4 -4
  240. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-07-validation.md +4 -4
  241. package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-08-complete.md +1 -1
  242. package/src/modules/bmm/workflows/3-solutioning/architecture/workflow.md +2 -2
  243. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +1 -1
  244. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +1 -1
  245. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +1 -1
  246. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +1 -1
  247. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +2 -2
  248. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-01-document-discovery.md +1 -1
  249. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-02-prd-analysis.md +1 -1
  250. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-03-epic-coverage-validation.md +6 -6
  251. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-04-ux-alignment.md +1 -1
  252. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-05-epic-quality-review.md +2 -2
  253. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-06-final-assessment.md +1 -1
  254. package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/workflow.md +1 -1
  255. package/src/modules/bmm/workflows/4-implementation/code-review/instructions.xml +1 -1
  256. package/src/modules/bmm/workflows/4-implementation/code-review/workflow.yaml +2 -2
  257. package/src/modules/bmm/workflows/4-implementation/correct-course/checklist.md +1 -1
  258. package/src/modules/bmm/workflows/4-implementation/correct-course/instructions.md +2 -2
  259. package/src/modules/bmm/workflows/4-implementation/correct-course/workflow.yaml +2 -2
  260. package/src/modules/bmm/workflows/4-implementation/create-story/checklist.md +2 -2
  261. package/src/modules/bmm/workflows/4-implementation/create-story/instructions.xml +2 -2
  262. package/src/modules/bmm/workflows/4-implementation/create-story/workflow.yaml +2 -2
  263. package/src/modules/bmm/workflows/4-implementation/dev-story/instructions.xml +1 -1
  264. package/src/modules/bmm/workflows/4-implementation/dev-story/workflow.yaml +2 -2
  265. package/src/modules/bmm/workflows/4-implementation/retrospective/instructions.md +2 -2
  266. package/src/modules/bmm/workflows/4-implementation/retrospective/workflow.yaml +3 -3
  267. package/src/modules/bmm/workflows/4-implementation/sprint-planning/instructions.md +2 -2
  268. package/src/modules/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +2 -2
  269. package/src/modules/bmm/workflows/4-implementation/sprint-status/instructions.md +2 -2
  270. package/src/modules/bmm/workflows/4-implementation/sprint-status/workflow.yaml +2 -2
  271. package/src/modules/bmm/workflows/bmad-quick-flow/create-tech-spec/workflow.yaml +5 -5
  272. package/src/modules/bmm/workflows/bmad-quick-flow/quick-dev/workflow.yaml +7 -7
  273. package/src/modules/bmm/workflows/diagrams/create-dataflow/instructions.md +1 -1
  274. package/src/modules/bmm/workflows/diagrams/create-dataflow/workflow.yaml +5 -5
  275. package/src/modules/bmm/workflows/diagrams/create-diagram/instructions.md +2 -2
  276. package/src/modules/bmm/workflows/diagrams/create-diagram/workflow.yaml +5 -5
  277. package/src/modules/bmm/workflows/diagrams/create-flowchart/instructions.md +2 -2
  278. package/src/modules/bmm/workflows/diagrams/create-flowchart/workflow.yaml +5 -5
  279. package/src/modules/bmm/workflows/diagrams/create-wireframe/instructions.md +1 -1
  280. package/src/modules/bmm/workflows/diagrams/create-wireframe/workflow.yaml +5 -5
  281. package/src/modules/bmm/workflows/document-project/instructions.md +5 -5
  282. package/src/modules/bmm/workflows/document-project/workflow.yaml +2 -2
  283. package/src/modules/bmm/workflows/document-project/workflows/deep-dive.yaml +5 -5
  284. package/src/modules/bmm/workflows/document-project/workflows/full-scan.yaml +5 -5
  285. package/src/modules/bmm/workflows/generate-project-context/steps/step-02-generate.md +2 -2
  286. package/src/modules/bmm/workflows/generate-project-context/workflow.md +2 -2
  287. package/src/modules/bmm/workflows/testarch/atdd/instructions.md +2 -2
  288. package/src/modules/bmm/workflows/testarch/atdd/workflow.yaml +2 -2
  289. package/src/modules/bmm/workflows/testarch/automate/instructions.md +2 -2
  290. package/src/modules/bmm/workflows/testarch/automate/workflow.yaml +2 -2
  291. package/src/modules/bmm/workflows/testarch/ci/instructions.md +1 -1
  292. package/src/modules/bmm/workflows/testarch/ci/workflow.yaml +2 -2
  293. package/src/modules/bmm/workflows/testarch/framework/instructions.md +3 -3
  294. package/src/modules/bmm/workflows/testarch/framework/workflow.yaml +2 -2
  295. package/src/modules/bmm/workflows/testarch/nfr-assess/instructions.md +1 -1
  296. package/src/modules/bmm/workflows/testarch/nfr-assess/workflow.yaml +2 -2
  297. package/src/modules/bmm/workflows/testarch/test-design/instructions.md +3 -3
  298. package/src/modules/bmm/workflows/testarch/test-design/test-design-template.md +1 -1
  299. package/src/modules/bmm/workflows/testarch/test-design/workflow.yaml +2 -2
  300. package/src/modules/bmm/workflows/testarch/test-review/instructions.md +1 -1
  301. package/src/modules/bmm/workflows/testarch/test-review/workflow.yaml +2 -2
  302. package/src/modules/bmm/workflows/testarch/trace/instructions.md +6 -6
  303. package/src/modules/bmm/workflows/testarch/trace/workflow.yaml +2 -2
  304. package/src/modules/bmm/workflows/workflow-status/init/instructions.md +1 -1
  305. package/src/modules/bmm/workflows/workflow-status/init/workflow.yaml +4 -4
  306. package/src/modules/bmm/workflows/workflow-status/instructions.md +3 -3
  307. package/src/modules/bmm/workflows/workflow-status/project-levels.yaml +1 -1
  308. package/src/modules/bmm/workflows/workflow-status/workflow.yaml +2 -2
  309. package/src/modules/cis/README.md +1 -1
  310. package/src/modules/cis/_module-installer/installer.js +1 -1
  311. package/src/modules/cis/agents/README.md +1 -1
  312. package/src/modules/cis/agents/brainstorming-coach.agent.yaml +4 -4
  313. package/src/modules/cis/agents/creative-problem-solver.agent.yaml +4 -4
  314. package/src/modules/cis/agents/design-thinking-coach.agent.yaml +4 -4
  315. package/src/modules/cis/agents/innovation-strategist.agent.yaml +4 -4
  316. package/src/modules/cis/agents/presentation-master.agent.yaml +3 -3
  317. package/src/modules/cis/agents/storyteller.agent.yaml +4 -4
  318. package/src/modules/cis/{_module-installer/install-config.yaml → module.yaml} +0 -1
  319. package/src/modules/cis/workflows/README.md +1 -1
  320. package/src/modules/cis/workflows/design-thinking/instructions.md +2 -2
  321. package/src/modules/cis/workflows/design-thinking/workflow.yaml +7 -7
  322. package/src/modules/cis/workflows/innovation-strategy/instructions.md +2 -2
  323. package/src/modules/cis/workflows/innovation-strategy/workflow.yaml +7 -7
  324. package/src/modules/cis/workflows/problem-solving/instructions.md +2 -2
  325. package/src/modules/cis/workflows/problem-solving/workflow.yaml +7 -7
  326. package/src/modules/cis/workflows/storytelling/instructions.md +2 -2
  327. package/src/modules/cis/workflows/storytelling/workflow.yaml +7 -7
  328. package/src/utility/models/agent-activation-ide.xml +3 -3
  329. package/src/utility/models/agent-activation-web.xml +3 -3
  330. package/src/utility/models/fragments/activation-steps.xml +1 -1
  331. package/src/utility/models/fragments/handler-validate-workflow.xml +1 -1
  332. package/src/utility/models/fragments/handler-workflow.xml +1 -1
  333. package/src/utility/models/fragments/web-bundle-activation-steps.xml +3 -3
  334. package/tools/cli/README.md +23 -23
  335. package/tools/cli/bundlers/web-bundler.js +23 -33
  336. package/tools/cli/commands/build.js +5 -5
  337. package/tools/cli/installers/lib/core/config-collector.js +33 -20
  338. package/tools/cli/installers/lib/core/custom-module-cache.js +251 -0
  339. package/tools/cli/installers/lib/core/detector.js +8 -4
  340. package/tools/cli/installers/lib/core/installer.js +885 -98
  341. package/tools/cli/installers/lib/core/manifest-generator.js +177 -14
  342. package/tools/cli/installers/lib/core/manifest.js +47 -0
  343. package/tools/cli/installers/lib/custom/handler.js +147 -20
  344. package/tools/cli/installers/lib/ide/_base-ide.js +14 -14
  345. package/tools/cli/installers/lib/ide/gemini.js +4 -4
  346. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
  347. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +3 -3
  348. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +1 -1
  349. package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +3 -3
  350. package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +3 -3
  351. package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +1 -1
  352. package/tools/cli/installers/lib/modules/manager.js +99 -53
  353. package/tools/cli/lib/agent/compiler.js +6 -14
  354. package/tools/cli/lib/agent/installer.js +4 -3
  355. package/tools/cli/lib/cli-utils.js +21 -4
  356. package/tools/cli/lib/ui.js +496 -12
  357. package/tools/cli/regenerate-manifests.js +3 -4
  358. package/tools/maintainer/review-pr-README.md +55 -0
  359. package/tools/maintainer/review-pr.md +242 -0
  360. package/tools/migrate-custom-module-paths.js +124 -0
  361. package/bmad-method-6.0.0-alpha.14.tgz +0 -0
  362. package/docs/custom-agent-installation.md +0 -137
  363. package/example-custom-content/README.md +0 -4
  364. package/example-custom-content/agents/commit-poet/commit-poet.agent.yaml +0 -129
  365. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/instructions.md +0 -70
  366. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/bundlers.md +0 -111
  367. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/deploy.md +0 -70
  368. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/docs.md +0 -114
  369. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +0 -134
  370. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +0 -161
  371. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/tests.md +0 -103
  372. package/example-custom-content/agents/toolsmith/toolsmith-sidecar/memories.md +0 -17
  373. package/example-custom-content/agents/toolsmith/toolsmith.agent.yaml +0 -109
  374. package/example-custom-content/custom.yaml +0 -3
  375. package/example-custom-content/workflows/quiz-master/steps/step-01-init.md +0 -168
  376. package/example-custom-content/workflows/quiz-master/steps/step-02-q1.md +0 -155
  377. package/example-custom-content/workflows/quiz-master/steps/step-03-q2.md +0 -89
  378. package/example-custom-content/workflows/quiz-master/steps/step-04-q3.md +0 -36
  379. package/example-custom-content/workflows/quiz-master/steps/step-05-q4.md +0 -36
  380. package/example-custom-content/workflows/quiz-master/steps/step-06-q5.md +0 -36
  381. package/example-custom-content/workflows/quiz-master/steps/step-07-q6.md +0 -36
  382. package/example-custom-content/workflows/quiz-master/steps/step-08-q7.md +0 -36
  383. package/example-custom-content/workflows/quiz-master/steps/step-09-q8.md +0 -36
  384. package/example-custom-content/workflows/quiz-master/steps/step-10-q9.md +0 -36
  385. package/example-custom-content/workflows/quiz-master/steps/step-11-q10.md +0 -36
  386. package/example-custom-content/workflows/quiz-master/steps/step-12-results.md +0 -150
  387. package/example-custom-content/workflows/quiz-master/templates/csv-headers.template +0 -1
  388. package/example-custom-content/workflows/quiz-master/workflow-plan-quiz-master.md +0 -269
  389. package/example-custom-content/workflows/quiz-master/workflow.md +0 -54
  390. package/example-custom-content/workflows/wassup/workflow.md +0 -26
  391. package/example-custom-module/mwm/README.md +0 -4
  392. package/example-custom-module/mwm/_module-installer/install-config.yaml +0 -27
  393. package/example-custom-module/mwm/agents/cbt-coach/cbt-coach-sidecar/cognitive-distortions.md +0 -47
  394. package/example-custom-module/mwm/agents/cbt-coach/cbt-coach-sidecar/thought-records.md +0 -17
  395. package/example-custom-module/mwm/agents/cbt-coach/cbt-coach.agent.yaml +0 -150
  396. package/example-custom-module/mwm/agents/crisis-navigator.agent.yaml +0 -137
  397. package/example-custom-module/mwm/agents/meditation-guide.agent.yaml +0 -137
  398. package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/insights.md +0 -13
  399. package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/instructions.md +0 -30
  400. package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/memories.md +0 -13
  401. package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/patterns.md +0 -17
  402. package/example-custom-module/mwm/agents/wellness-companion/wellness-companion.agent.yaml +0 -124
  403. package/example-custom-module/mwm/workflows/cbt-thought-record/README.md +0 -31
  404. package/example-custom-module/mwm/workflows/cbt-thought-record/workflow.md +0 -45
  405. package/example-custom-module/mwm/workflows/crisis-support/README.md +0 -31
  406. package/example-custom-module/mwm/workflows/crisis-support/workflow.md +0 -45
  407. package/example-custom-module/mwm/workflows/daily-checkin/README.md +0 -32
  408. package/example-custom-module/mwm/workflows/daily-checkin/workflow.md +0 -45
  409. package/example-custom-module/mwm/workflows/guided-meditation/README.md +0 -31
  410. package/example-custom-module/mwm/workflows/guided-meditation/workflow.md +0 -45
  411. package/example-custom-module/mwm/workflows/wellness-journal/README.md +0 -31
  412. package/example-custom-module/mwm/workflows/wellness-journal/workflow.md +0 -45
@@ -14,7 +14,7 @@
14
14
  * @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete BMAD installation
15
15
  * @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js
16
16
  * @entrypoints Called by install.js command via installer.install(config)
17
- * @patterns Injection point processing (AgentVibes), placeholder replacement ({bmad_folder}), module dependency resolution
17
+ * @patterns Injection point processing (AgentVibes), placeholder replacement (.bmad), module dependency resolution
18
18
  * @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement()
19
19
  */
20
20
 
@@ -22,6 +22,7 @@ const path = require('node:path');
22
22
  const fs = require('fs-extra');
23
23
  const chalk = require('chalk');
24
24
  const ora = require('ora');
25
+ const inquirer = require('inquirer');
25
26
  const { Detector } = require('./detector');
26
27
  const { Manifest } = require('./manifest');
27
28
  const { ModuleManager } = require('../modules/manager');
@@ -38,6 +39,7 @@ const { CLIUtils } = require('../../../lib/cli-utils');
38
39
  const { ManifestGenerator } = require('./manifest-generator');
39
40
  const { IdeConfigManager } = require('./ide-config-manager');
40
41
  const { replaceAgentSidecarFolders } = require('./post-install-sidecar-replacement');
42
+ const { CustomHandler } = require('../custom/handler');
41
43
 
42
44
  class Installer {
43
45
  constructor() {
@@ -65,7 +67,7 @@ class Installer {
65
67
  // Check if project directory exists
66
68
  if (!(await fs.pathExists(projectDir))) {
67
69
  // Project doesn't exist yet, return default
68
- return path.join(projectDir, 'bmad');
70
+ return path.join(projectDir, '.bmad');
69
71
  }
70
72
 
71
73
  // V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
@@ -87,13 +89,13 @@ class Installer {
87
89
 
88
90
  // No V6+ installation found, return default
89
91
  // This will be used for new installations
90
- return path.join(projectDir, 'bmad');
92
+ return path.join(projectDir, '.bmad');
91
93
  }
92
94
 
93
95
  /**
94
96
  * @function copyFileWithPlaceholderReplacement
95
97
  * @intent Copy files from BMAD source to installation directory with dynamic content transformation
96
- * @why Enables installation-time customization: {bmad_folder} replacement + optional AgentVibes TTS injection
98
+ * @why Enables installation-time customization: .bmad replacement + optional AgentVibes TTS injection
97
99
  * @param {string} sourcePath - Absolute path to source file in BMAD repository
98
100
  * @param {string} targetPath - Absolute path to destination file in user's project
99
101
  * @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad')
@@ -103,11 +105,6 @@ class Installer {
103
105
  * @calledby installCore(), installModule(), IDE installers during file vendoring
104
106
  * @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy()
105
107
  *
106
- * AI NOTE: This is the core transformation pipeline for ALL BMAD installation file copies.
107
- * It performs two transformations in sequence:
108
- * 1. {bmad_folder} → user's custom folder name (e.g., ".bmad" or "bmad")
109
- * 2. <!-- TTS_INJECTION:* --> → TTS bash calls (if enabled) OR stripped (if disabled)
110
- *
111
108
  * The injection point processing enables loose coupling between BMAD and TTS providers:
112
109
  * - BMAD source contains injection markers (not actual TTS code)
113
110
  * - At install-time, markers are replaced OR removed based on user preference
@@ -129,7 +126,7 @@ class Installer {
129
126
  */
130
127
  async copyFileWithPlaceholderReplacement(sourcePath, targetPath, bmadFolderName) {
131
128
  // List of text file extensions that should have placeholder replacement
132
- const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv'];
129
+ const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv', '.xml'];
133
130
  const ext = path.extname(sourcePath).toLowerCase();
134
131
 
135
132
  // Check if this is a text file that might contain placeholders
@@ -138,16 +135,6 @@ class Installer {
138
135
  // Read the file content
139
136
  let content = await fs.readFile(sourcePath, 'utf8');
140
137
 
141
- // Replace {bmad_folder} placeholder with actual folder name
142
- if (content.includes('{bmad_folder}')) {
143
- content = content.replaceAll('{bmad_folder}', bmadFolderName);
144
- }
145
-
146
- // Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
147
- if (content.includes('{*bmad_folder*}')) {
148
- content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
149
- }
150
-
151
138
  // Process AgentVibes injection points (pass targetPath for tracking)
152
139
  content = this.processTTSInjectionPoints(content, targetPath);
153
140
 
@@ -406,7 +393,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
406
393
  * @param {string[]} config.ides - IDEs to configure
407
394
  * @param {boolean} config.skipIde - Skip IDE configuration
408
395
  */
409
- async install(config) {
396
+ async install(originalConfig) {
397
+ // Clone config to avoid mutating the caller's object
398
+ const config = { ...originalConfig };
399
+
410
400
  // Display BMAD logo
411
401
  CLIUtils.displayLogo();
412
402
 
@@ -434,12 +424,56 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
434
424
  // Quick update already collected all configs, use them directly
435
425
  moduleConfigs = this.configCollector.collectedConfig;
436
426
  } else {
427
+ // Build custom module paths map from customContent
428
+ const customModulePaths = new Map();
429
+
430
+ // Handle selectedFiles (from existing install path or manual directory input)
431
+ if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
432
+ const customHandler = new CustomHandler();
433
+ for (const customFile of config.customContent.selectedFiles) {
434
+ const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory));
435
+ if (customInfo && customInfo.id) {
436
+ customModulePaths.set(customInfo.id, customInfo.path);
437
+ }
438
+ }
439
+ }
440
+
441
+ // Handle cachedModules (from new install path where modules are cached)
442
+ // Only include modules that were actually selected for installation
443
+ if (config.customContent && config.customContent.cachedModules) {
444
+ // Get selected cached module IDs (if available)
445
+ const selectedCachedIds = config.customContent.selectedCachedModules || [];
446
+ // If no selection info, include all cached modules (for backward compatibility)
447
+ const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected;
448
+
449
+ for (const cachedModule of config.customContent.cachedModules) {
450
+ // For cached modules, the path is the cachePath which contains the module.yaml
451
+ if (
452
+ cachedModule.id &&
453
+ cachedModule.cachePath && // Include if selected or if we should include all
454
+ (shouldIncludeAll || selectedCachedIds.includes(cachedModule.id))
455
+ ) {
456
+ customModulePaths.set(cachedModule.id, cachedModule.cachePath);
457
+ }
458
+ }
459
+ }
460
+
461
+ // Get list of all modules including custom modules
462
+ const allModulesForConfig = [...(config.modules || [])];
463
+ for (const [moduleId] of customModulePaths) {
464
+ if (!allModulesForConfig.includes(moduleId)) {
465
+ allModulesForConfig.push(moduleId);
466
+ }
467
+ }
468
+
437
469
  // Regular install - collect configurations (core was already collected in UI.promptInstall if interactive)
438
- moduleConfigs = await this.configCollector.collectAllConfigurations(config.modules || [], path.resolve(config.directory));
470
+ moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
471
+ customModulePaths,
472
+ });
439
473
  }
440
474
 
441
- // Get bmad_folder from config (default to 'bmad' for backwards compatibility)
442
- const bmadFolderName = moduleConfigs.core && moduleConfigs.core.bmad_folder ? moduleConfigs.core.bmad_folder : 'bmad';
475
+ // Always use .bmad as the folder name
476
+ const bmadFolderName = '.bmad';
443
477
  this.bmadFolderName = bmadFolderName; // Store for use in other methods
444
478
 
445
479
  // Store AgentVibes configuration for injection point processing
@@ -458,7 +492,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
458
492
  // Resolve target directory (path.resolve handles platform differences)
459
493
  const projectDir = path.resolve(config.directory);
460
494
 
461
- // Check if bmad_folder has changed from existing installation (only if project dir exists)
462
495
  let existingBmadDir = null;
463
496
  let existingBmadFolderName = null;
464
497
 
@@ -467,54 +500,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
467
500
  existingBmadFolderName = path.basename(existingBmadDir);
468
501
  }
469
502
 
470
- const targetBmadDir = path.join(projectDir, bmadFolderName);
471
-
472
- // If bmad_folder changed during update/upgrade, back up old folder and do fresh install
473
- if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== bmadFolderName) {
474
- spinner.stop();
475
- console.log(chalk.yellow(`\n⚠️ bmad_folder has changed: ${existingBmadFolderName} → ${bmadFolderName}`));
476
- console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
477
-
478
- const inquirer = require('inquirer');
479
- const { confirmFreshInstall } = await inquirer.prompt([
480
- {
481
- type: 'confirm',
482
- name: 'confirmFreshInstall',
483
- message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
484
- default: true,
485
- },
486
- ]);
487
-
488
- if (!confirmFreshInstall) {
489
- console.log(chalk.yellow('Installation cancelled.'));
490
- return { success: false, cancelled: true };
491
- }
492
-
493
- spinner.start('Backing up existing installation...');
494
-
495
- // Find a unique backup name
496
- let backupDir = `${existingBmadDir}-bak`;
497
- let counter = 1;
498
- while (await fs.pathExists(backupDir)) {
499
- backupDir = `${existingBmadDir}-bak-${counter}`;
500
- counter++;
501
- }
502
-
503
- // Rename the old folder to backup
504
- await fs.move(existingBmadDir, backupDir);
505
-
506
- spinner.succeed(`Backed up ${existingBmadFolderName} → ${path.basename(backupDir)}`);
507
- console.log(chalk.cyan('\n📋 Important:'));
508
- console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
509
- console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
510
- console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
511
- console.log(chalk.dim(` - To the new location:`));
512
- console.log(chalk.dim(` ${bmadFolderName}/_cfg/`));
513
- console.log('');
514
-
515
- spinner.start('Starting fresh installation...');
516
- }
517
-
518
503
  // Create a project directory if it doesn't exist (user already confirmed)
519
504
  if (!(await fs.pathExists(projectDir))) {
520
505
  spinner.text = 'Creating installation directory...';
@@ -750,13 +735,80 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
750
735
  spinner.text = 'Creating directory structure...';
751
736
  await this.createDirectoryStructure(bmadDir);
752
737
 
753
- // Resolve dependencies for selected modules
754
- spinner.text = 'Resolving dependencies...';
738
+ // Get project root
755
739
  const projectRoot = getProjectRoot();
756
- const modulesToInstall = config.installCore ? ['core', ...config.modules] : config.modules;
740
+
741
+ // Step 1: Install core module first (if requested)
742
+ if (config.installCore) {
743
+ spinner.start('Installing BMAD core...');
744
+ await this.installCoreWithDependencies(bmadDir, { core: {} });
745
+ spinner.succeed('Core installed');
746
+
747
+ // Generate core config file
748
+ await this.generateModuleConfigs(bmadDir, { core: config.coreConfig || {} });
749
+ }
750
+
751
+ // Custom content is already handled in UI before module selection
752
+ let finalCustomContent = config.customContent;
753
+
754
+ // Step 3: Prepare modules list including cached custom modules
755
+ let allModules = [...(config.modules || [])];
756
+
757
+ // During quick update, we might have custom module sources from the manifest
758
+ if (config._customModuleSources) {
759
+ // Add custom modules from stored sources
760
+ for (const [moduleId, customInfo] of config._customModuleSources) {
761
+ if (!allModules.includes(moduleId) && (await fs.pathExists(customInfo.sourcePath))) {
762
+ allModules.push(moduleId);
763
+ }
764
+ }
765
+ }
766
+
767
+ // Add cached custom modules
768
+ if (finalCustomContent && finalCustomContent.cachedModules) {
769
+ for (const cachedModule of finalCustomContent.cachedModules) {
770
+ if (!allModules.includes(cachedModule.id)) {
771
+ allModules.push(cachedModule.id);
772
+ }
773
+ }
774
+ }
775
+
776
+ // Regular custom content from user input (non-cached)
777
+ if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
778
+ // Add custom modules to the installation list
779
+ const customHandler = new CustomHandler();
780
+ for (const customFile of finalCustomContent.selectedFiles) {
781
+ const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
782
+ if (customInfo && customInfo.id) {
783
+ allModules.push(customInfo.id);
784
+ }
785
+ }
786
+ }
787
+
788
+ // Don't include core again if already installed
789
+ if (config.installCore) {
790
+ allModules = allModules.filter((m) => m !== 'core');
791
+ }
792
+
793
+ const modulesToInstall = allModules;
757
794
 
758
795
  // For dependency resolution, we need to pass the project root
759
- const resolution = await this.dependencyResolver.resolve(projectRoot, config.modules || [], { verbose: config.verbose });
796
+ // Create a temporary module manager that knows about custom content locations
797
+ const tempModuleManager = new ModuleManager({
798
+ scanProjectForModules: true,
799
+ bmadDir: bmadDir, // Pass bmadDir so we can check cache
800
+ });
801
+
802
+ // Make sure custom modules are discoverable
803
+ if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
804
+ // The dependency resolver needs to know about these modules
805
+ // We'll handle custom modules separately in the installation loop
806
+ }
807
+
808
+ const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
809
+ verbose: config.verbose,
810
+ moduleManager: tempModuleManager,
811
+ });
760
812
 
761
813
  if (config.verbose) {
762
814
  spinner.succeed('Dependencies resolved');
@@ -764,24 +816,182 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
764
816
  spinner.succeed('Dependencies resolved');
765
817
  }
766
818
 
767
- // Install core if requested or if dependencies require it
768
- if (config.installCore || resolution.byModule.core) {
769
- spinner.start('Installing BMAD core...');
770
- await this.installCoreWithDependencies(bmadDir, resolution.byModule.core);
771
- spinner.succeed('Core installed');
772
- }
819
+ // Core is already installed above, skip if included in resolution
773
820
 
774
821
  // Install modules with their dependencies
775
- if (config.modules && config.modules.length > 0) {
776
- for (const moduleName of config.modules) {
822
+ if (allModules && allModules.length > 0) {
823
+ const installedModuleNames = new Set();
824
+
825
+ for (const moduleName of allModules) {
826
+ // Skip if already installed
827
+ if (installedModuleNames.has(moduleName)) {
828
+ continue;
829
+ }
830
+ installedModuleNames.add(moduleName);
831
+
777
832
  spinner.start(`Installing module: ${moduleName}...`);
778
- await this.installModuleWithDependencies(moduleName, bmadDir, resolution.byModule[moduleName]);
833
+
834
+ // Check if this is a custom module
835
+ let isCustomModule = false;
836
+ let customInfo = null;
837
+ let useCache = false;
838
+
839
+ // First check if we have a cached version
840
+ if (finalCustomContent && finalCustomContent.cachedModules) {
841
+ const cachedModule = finalCustomContent.cachedModules.find((m) => m.id === moduleName);
842
+ if (cachedModule) {
843
+ isCustomModule = true;
844
+ customInfo = {
845
+ id: moduleName,
846
+ path: cachedModule.cachePath,
847
+ config: {},
848
+ };
849
+ useCache = true;
850
+ }
851
+ }
852
+
853
+ // Then check if we have custom module sources from the manifest (for quick update)
854
+ if (!isCustomModule && config._customModuleSources && config._customModuleSources.has(moduleName)) {
855
+ customInfo = config._customModuleSources.get(moduleName);
856
+ isCustomModule = true;
857
+
858
+ // Check if this is a cached module (source path starts with _cfg)
859
+ if (customInfo.sourcePath && (customInfo.sourcePath.startsWith('_cfg') || customInfo.sourcePath.includes('_cfg/custom'))) {
860
+ useCache = true;
861
+ // Make sure we have the right path structure
862
+ if (!customInfo.path) {
863
+ customInfo.path = customInfo.sourcePath;
864
+ }
865
+ }
866
+ }
867
+
868
+ // Finally check regular custom content
869
+ if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
870
+ const customHandler = new CustomHandler();
871
+ for (const customFile of finalCustomContent.selectedFiles) {
872
+ const info = await customHandler.getCustomInfo(customFile, projectDir);
873
+ if (info && info.id === moduleName) {
874
+ isCustomModule = true;
875
+ customInfo = info;
876
+ break;
877
+ }
878
+ }
879
+ }
880
+
881
+ if (isCustomModule && customInfo) {
882
+ // Install custom module using CustomHandler but as a proper module
883
+ const customHandler = new CustomHandler();
884
+
885
+ // Install to module directory instead of custom directory
886
+ const moduleTargetPath = path.join(bmadDir, moduleName);
887
+ await fs.ensureDir(moduleTargetPath);
888
+
889
+ // Get collected config for this custom module (from module.yaml prompts)
890
+ const collectedModuleConfig = moduleConfigs[moduleName] || {};
891
+
892
+ const result = await customHandler.install(
893
+ customInfo.path,
894
+ path.join(bmadDir, 'temp-custom'),
895
+ { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig, _bmadDir: bmadDir },
896
+ (filePath) => {
897
+ // Track installed files with correct path
898
+ const relativePath = path.relative(path.join(bmadDir, 'temp-custom'), filePath);
899
+ const finalPath = path.join(moduleTargetPath, relativePath);
900
+ this.installedFiles.push(finalPath);
901
+ },
902
+ );
903
+
904
+ // Move from temp-custom to actual module directory
905
+ const tempCustomPath = path.join(bmadDir, 'temp-custom');
906
+ if (await fs.pathExists(tempCustomPath)) {
907
+ const customDir = path.join(tempCustomPath, 'custom');
908
+ if (await fs.pathExists(customDir)) {
909
+ // Move contents to module directory
910
+ const items = await fs.readdir(customDir);
911
+ const movedItems = [];
912
+ try {
913
+ for (const item of items) {
914
+ const srcPath = path.join(customDir, item);
915
+ const destPath = path.join(moduleTargetPath, item);
916
+
917
+ // If destination exists, remove it first (or we could merge)
918
+ if (await fs.pathExists(destPath)) {
919
+ await fs.remove(destPath);
920
+ }
921
+
922
+ await fs.move(srcPath, destPath);
923
+ movedItems.push({ src: srcPath, dest: destPath });
924
+ }
925
+ } catch (moveError) {
926
+ // Rollback: restore any successfully moved items
927
+ for (const moved of movedItems) {
928
+ try {
929
+ await fs.move(moved.dest, moved.src);
930
+ } catch {
931
+ // Best-effort rollback - log if it fails
932
+ console.error(`Failed to rollback ${moved.dest} during cleanup`);
933
+ }
934
+ }
935
+ throw new Error(`Failed to move custom module files: ${moveError.message}`);
936
+ }
937
+ }
938
+ try {
939
+ await fs.remove(tempCustomPath);
940
+ } catch (cleanupError) {
941
+ // Non-fatal: temp directory cleanup failed but files were moved successfully
942
+ console.warn(`Warning: Could not clean up temp directory: ${cleanupError.message}`);
943
+ }
944
+ }
945
+
946
+ // Create module config (include collected config from module.yaml prompts)
947
+ await this.generateModuleConfigs(bmadDir, {
948
+ [moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig },
949
+ });
950
+
951
+ // Store custom module info for later manifest update
952
+ if (!config._customModulesToTrack) {
953
+ config._customModulesToTrack = [];
954
+ }
955
+
956
+ // For cached modules, use appropriate path handling
957
+ let sourcePath;
958
+ if (useCache) {
959
+ // Check if we have cached modules info (from initial install)
960
+ if (finalCustomContent && finalCustomContent.cachedModules) {
961
+ sourcePath = finalCustomContent.cachedModules.find((m) => m.id === moduleName)?.relativePath;
962
+ } else {
963
+ // During update, the sourcePath is already cache-relative if it starts with _cfg
964
+ sourcePath =
965
+ customInfo.sourcePath && customInfo.sourcePath.startsWith('_cfg')
966
+ ? customInfo.sourcePath
967
+ : path.relative(bmadDir, customInfo.path || customInfo.sourcePath);
968
+ }
969
+ } else {
970
+ sourcePath = path.resolve(customInfo.path || customInfo.sourcePath);
971
+ }
972
+
973
+ config._customModulesToTrack.push({
974
+ id: customInfo.id,
975
+ name: customInfo.name,
976
+ sourcePath: sourcePath,
977
+ installDate: new Date().toISOString(),
978
+ });
979
+ } else {
980
+ // Regular module installation
981
+ // Special case for core module
982
+ if (moduleName === 'core') {
983
+ await this.installCoreWithDependencies(bmadDir, resolution.byModule[moduleName]);
984
+ } else {
985
+ await this.installModuleWithDependencies(moduleName, bmadDir, resolution.byModule[moduleName]);
986
+ }
987
+ }
988
+
779
989
  spinner.succeed(`Module installed: ${moduleName}`);
780
990
  }
781
991
 
782
992
  // Install partial modules (only dependencies)
783
993
  for (const [module, files] of Object.entries(resolution.byModule)) {
784
- if (!config.modules.includes(module) && module !== 'core') {
994
+ if (!allModules.includes(module) && module !== 'core') {
785
995
  const totalFiles =
786
996
  files.agents.length +
787
997
  files.tasks.length +
@@ -798,6 +1008,70 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
798
1008
  }
799
1009
  }
800
1010
 
1011
+ // Install custom content if provided AND selected
1012
+ // Process custom content that wasn't installed as modules
1013
+ // This is now handled in the module installation loop above
1014
+ // This section is kept for backward compatibility with any custom content
1015
+ // that doesn't have a module structure
1016
+ const remainingCustomContent = [];
1017
+ if (
1018
+ config.customContent &&
1019
+ config.customContent.hasCustomContent &&
1020
+ config.customContent.customPath &&
1021
+ config.customContent.selected &&
1022
+ config.customContent.selectedFiles
1023
+ ) {
1024
+ // Filter out custom modules that were already installed
1025
+ const customHandler = new CustomHandler();
1026
+ for (const customFile of config.customContent.selectedFiles) {
1027
+ const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
1028
+
1029
+ // Skip if this was installed as a module
1030
+ if (!customInfo || !customInfo.id || !allModules.includes(customInfo.id)) {
1031
+ remainingCustomContent.push(customFile);
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ if (remainingCustomContent.length > 0) {
1037
+ spinner.start('Installing remaining custom content...');
1038
+ const customHandler = new CustomHandler();
1039
+
1040
+ // Use the remaining files
1041
+ const customFiles = remainingCustomContent;
1042
+
1043
+ if (customFiles.length > 0) {
1044
+ console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
1045
+ for (const customFile of customFiles) {
1046
+ const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
1047
+ if (customInfo) {
1048
+ console.log(chalk.dim(` • ${customInfo.name} (${customInfo.relativePath})`));
1049
+
1050
+ // Install the custom content
1051
+ const result = await customHandler.install(
1052
+ customInfo.path,
1053
+ bmadDir,
1054
+ { ...config.coreConfig, ...customInfo.config },
1055
+ (filePath) => {
1056
+ // Track installed files
1057
+ this.installedFiles.push(filePath);
1058
+ },
1059
+ );
1060
+
1061
+ if (result.errors.length > 0) {
1062
+ console.log(chalk.yellow(` ⚠️ ${result.errors.length} error(s) occurred`));
1063
+ for (const error of result.errors) {
1064
+ console.log(chalk.dim(` - ${error}`));
1065
+ }
1066
+ } else {
1067
+ console.log(chalk.green(` ✓ Installed ${result.agentsInstalled} agents, ${result.workflowsInstalled} workflows`));
1068
+ }
1069
+ }
1070
+ }
1071
+ }
1072
+ spinner.succeed('Custom content installed');
1073
+ }
1074
+
801
1075
  // Generate clean config.yaml files for each installed module
802
1076
  spinner.start('Generating module configurations...');
803
1077
  await this.generateModuleConfigs(bmadDir, moduleConfigs);
@@ -820,14 +1094,37 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
820
1094
  spinner.start('Generating workflow and agent manifests...');
821
1095
  const manifestGen = new ManifestGenerator();
822
1096
 
823
- // Include preserved modules (from quick update) in the manifest
824
- const allModulesToList = config._preserveModules ? [...(config.modules || []), ...config._preserveModules] : config.modules || [];
1097
+ // For quick update, we need ALL installed modules in the manifest
1098
+ // Not just the ones being updated
1099
+ const allModulesForManifest = config._quickUpdate
1100
+ ? config._existingModules || allModules || []
1101
+ : config._preserveModules
1102
+ ? [...allModules, ...config._preserveModules]
1103
+ : allModules || [];
1104
+
1105
+ // For regular installs (including when called from quick update), use what we have
1106
+ let modulesForCsvPreserve;
1107
+ if (config._quickUpdate) {
1108
+ // Quick update - use existing modules or fall back to modules being updated
1109
+ modulesForCsvPreserve = config._existingModules || allModules || [];
1110
+ } else {
1111
+ // Regular install - use the modules we're installing plus any preserved ones
1112
+ modulesForCsvPreserve = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules;
1113
+ }
825
1114
 
826
- const manifestStats = await manifestGen.generateManifests(bmadDir, config.modules || [], this.installedFiles, {
1115
+ const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, this.installedFiles, {
827
1116
  ides: config.ides || [],
828
- preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir
1117
+ preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
829
1118
  });
830
1119
 
1120
+ // Add custom modules to manifest (now that it exists)
1121
+ if (config._customModulesToTrack && config._customModulesToTrack.length > 0) {
1122
+ spinner.text = 'Storing custom module sources...';
1123
+ for (const customModule of config._customModulesToTrack) {
1124
+ await this.manifest.addCustomModule(bmadDir, customModule);
1125
+ }
1126
+ }
1127
+
831
1128
  spinner.succeed(
832
1129
  `Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
833
1130
  );
@@ -1090,6 +1387,30 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1090
1387
  const currentVersion = existingInstall.version;
1091
1388
  const newVersion = require(path.join(getProjectRoot(), 'package.json')).version;
1092
1389
 
1390
+ // Check for custom modules with missing sources before update
1391
+ const customModuleSources = new Map();
1392
+ if (existingInstall.customModules) {
1393
+ for (const customModule of existingInstall.customModules) {
1394
+ customModuleSources.set(customModule.id, customModule);
1395
+ }
1396
+ }
1397
+
1398
+ if (customModuleSources.size > 0) {
1399
+ spinner.stop();
1400
+ console.log(chalk.yellow('\nChecking custom module sources before update...'));
1401
+
1402
+ const projectRoot = getProjectRoot();
1403
+ await this.handleMissingCustomSources(
1404
+ customModuleSources,
1405
+ bmadDir,
1406
+ projectRoot,
1407
+ 'update',
1408
+ existingInstall.modules.map((m) => m.id),
1409
+ );
1410
+
1411
+ spinner.start('Preparing update...');
1412
+ }
1413
+
1093
1414
  if (config.dryRun) {
1094
1415
  spinner.stop();
1095
1416
  console.log(chalk.cyan('\n🔍 Update Preview (Dry Run)\n'));
@@ -1547,6 +1868,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1547
1868
  // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
1548
1869
  // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
1549
1870
 
1871
+ // Replace .bmad with actual folder name
1872
+ xmlContent = xmlContent.replaceAll('.bmad', this.bmadFolderName || 'bmad');
1873
+
1550
1874
  // Replace {agent_sidecar_folder} if configured
1551
1875
  const coreConfig = this.configCollector.collectedConfig.core || {};
1552
1876
  if (coreConfig.agent_sidecar_folder && xmlContent.includes('{agent_sidecar_folder}')) {
@@ -1592,7 +1916,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1592
1916
  // Resolve path variables
1593
1917
  const resolvedSidecarFolder = agentSidecarFolder
1594
1918
  .replaceAll('{project-root}', projectDir)
1595
- .replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
1919
+ .replaceAll('.bmad', this.bmadFolderName || 'bmad');
1596
1920
 
1597
1921
  // Create sidecar directory for this agent
1598
1922
  const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
@@ -1858,6 +2182,24 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1858
2182
  throw new Error(`BMAD not installed at ${bmadDir}`);
1859
2183
  }
1860
2184
 
2185
+ // Check for custom modules with missing sources
2186
+ const manifest = await this.manifest.read(bmadDir);
2187
+ if (manifest && manifest.customModules && manifest.customModules.length > 0) {
2188
+ spinner.stop();
2189
+ console.log(chalk.yellow('\nChecking custom module sources before compilation...'));
2190
+
2191
+ const customModuleSources = new Map();
2192
+ for (const customModule of manifest.customModules) {
2193
+ customModuleSources.set(customModule.id, customModule);
2194
+ }
2195
+
2196
+ const projectRoot = getProjectRoot();
2197
+ const installedModules = manifest.modules || [];
2198
+ await this.handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, 'compile-agents', installedModules);
2199
+
2200
+ spinner.start('Rebuilding agent files...');
2201
+ }
2202
+
1861
2203
  let agentCount = 0;
1862
2204
  let taskCount = 0;
1863
2205
 
@@ -2002,17 +2344,234 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2002
2344
  const existingInstall = await this.detector.detect(bmadDir);
2003
2345
  const installedModules = existingInstall.modules.map((m) => m.id);
2004
2346
  const configuredIdes = existingInstall.ides || [];
2347
+ const projectRoot = path.dirname(bmadDir);
2348
+
2349
+ // Get custom module sources from manifest
2350
+ const customModuleSources = new Map();
2351
+ if (existingInstall.customModules) {
2352
+ for (const customModule of existingInstall.customModules) {
2353
+ // Ensure we have an absolute sourcePath
2354
+ let absoluteSourcePath = customModule.sourcePath;
2355
+
2356
+ // Check if sourcePath is a cache-relative path (starts with _cfg/)
2357
+ if (absoluteSourcePath && absoluteSourcePath.startsWith('_cfg')) {
2358
+ // Convert cache-relative path to absolute path
2359
+ absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
2360
+ }
2361
+ // If no sourcePath but we have relativePath, convert it
2362
+ else if (!absoluteSourcePath && customModule.relativePath) {
2363
+ // relativePath is relative to the project root (parent of bmad dir)
2364
+ absoluteSourcePath = path.resolve(projectRoot, customModule.relativePath);
2365
+ }
2366
+ // Ensure sourcePath is absolute for anything else
2367
+ else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
2368
+ absoluteSourcePath = path.resolve(absoluteSourcePath);
2369
+ }
2370
+
2371
+ // Update the custom module object with the absolute path
2372
+ const updatedModule = {
2373
+ ...customModule,
2374
+ sourcePath: absoluteSourcePath,
2375
+ };
2376
+
2377
+ customModuleSources.set(customModule.id, updatedModule);
2378
+ }
2379
+ }
2005
2380
 
2006
2381
  // Load saved IDE configurations
2007
2382
  const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
2008
2383
 
2009
2384
  // Get available modules (what we have source for)
2010
- const availableModules = await this.moduleManager.listAvailable();
2011
- const availableModuleIds = new Set(availableModules.map((m) => m.id));
2385
+ const availableModulesData = await this.moduleManager.listAvailable();
2386
+ const availableModules = [...availableModulesData.modules, ...availableModulesData.customModules];
2387
+
2388
+ // Add custom modules from manifest if their sources exist
2389
+ for (const [moduleId, customModule] of customModuleSources) {
2390
+ // Use the absolute sourcePath
2391
+ const sourcePath = customModule.sourcePath;
2392
+
2393
+ // Check if source exists at the recorded path
2394
+ if (
2395
+ sourcePath &&
2396
+ (await fs.pathExists(sourcePath)) && // Add to available modules if not already there
2397
+ !availableModules.some((m) => m.id === moduleId)
2398
+ ) {
2399
+ availableModules.push({
2400
+ id: moduleId,
2401
+ name: customModule.name || moduleId,
2402
+ path: sourcePath,
2403
+ isCustom: true,
2404
+ fromManifest: true,
2405
+ });
2406
+ }
2407
+ }
2408
+
2409
+ // Check for untracked custom modules (installed but not in manifest)
2410
+ const untrackedCustomModules = [];
2411
+ for (const installedModule of installedModules) {
2412
+ // Skip standard modules and core
2413
+ const standardModuleIds = ['bmb', 'bmgd', 'bmm', 'cis', 'core'];
2414
+ if (standardModuleIds.includes(installedModule)) {
2415
+ continue;
2416
+ }
2417
+
2418
+ // Check if this installed module is not tracked in customModules
2419
+ if (!customModuleSources.has(installedModule)) {
2420
+ const modulePath = path.join(bmadDir, installedModule);
2421
+ if (await fs.pathExists(modulePath)) {
2422
+ untrackedCustomModules.push({
2423
+ id: installedModule,
2424
+ name: installedModule, // We don't have the original name
2425
+ path: modulePath,
2426
+ untracked: true,
2427
+ });
2428
+ }
2429
+ }
2430
+ }
2431
+
2432
+ // If we found untracked custom modules, offer to track them
2433
+ if (untrackedCustomModules.length > 0) {
2434
+ spinner.stop();
2435
+ console.log(chalk.yellow(`\n⚠️ Found ${untrackedCustomModules.length} custom module(s) not tracked in manifest:`));
2436
+
2437
+ for (const untracked of untrackedCustomModules) {
2438
+ console.log(chalk.dim(` • ${untracked.id} (installed at ${path.relative(projectRoot, untracked.path)})`));
2439
+ }
2440
+
2441
+ const { trackModules } = await inquirer.prompt([
2442
+ {
2443
+ type: 'confirm',
2444
+ name: 'trackModules',
2445
+ message: chalk.cyan('Would you like to scan for their source locations?'),
2446
+ default: true,
2447
+ },
2448
+ ]);
2449
+
2450
+ if (trackModules) {
2451
+ const { scanDirectory } = await inquirer.prompt([
2452
+ {
2453
+ type: 'input',
2454
+ name: 'scanDirectory',
2455
+ message: 'Enter directory to scan for custom module sources (or leave blank to skip):',
2456
+ default: projectRoot,
2457
+ validate: async (input) => {
2458
+ if (input && input.trim() !== '') {
2459
+ const expandedPath = path.resolve(input.trim());
2460
+ if (!(await fs.pathExists(expandedPath))) {
2461
+ return 'Directory does not exist';
2462
+ }
2463
+ const stats = await fs.stat(expandedPath);
2464
+ if (!stats.isDirectory()) {
2465
+ return 'Path must be a directory';
2466
+ }
2467
+ }
2468
+ return true;
2469
+ },
2470
+ },
2471
+ ]);
2472
+
2473
+ if (scanDirectory && scanDirectory.trim() !== '') {
2474
+ console.log(chalk.dim('\nScanning for custom module sources...'));
2475
+
2476
+ // Scan for all module.yaml files
2477
+ const allModulePaths = await this.moduleManager.findModulesInProject(scanDirectory);
2478
+ const { ModuleManager } = require('../modules/manager');
2479
+ const mm = new ModuleManager({ scanProjectForModules: true });
2480
+
2481
+ for (const untracked of untrackedCustomModules) {
2482
+ let foundSource = null;
2483
+
2484
+ // Try to find by module ID
2485
+ for (const modulePath of allModulePaths) {
2486
+ try {
2487
+ const moduleInfo = await mm.getModuleInfo(modulePath);
2488
+ if (moduleInfo && moduleInfo.id === untracked.id) {
2489
+ foundSource = {
2490
+ path: modulePath,
2491
+ info: moduleInfo,
2492
+ };
2493
+ break;
2494
+ }
2495
+ } catch {
2496
+ // Continue searching
2497
+ }
2498
+ }
2499
+
2500
+ if (foundSource) {
2501
+ console.log(chalk.green(` ✓ Found source for ${untracked.id}: ${path.relative(projectRoot, foundSource.path)}`));
2502
+
2503
+ // Add to manifest
2504
+ await this.manifest.addCustomModule(bmadDir, {
2505
+ id: untracked.id,
2506
+ name: foundSource.info.name || untracked.name,
2507
+ sourcePath: path.resolve(foundSource.path),
2508
+ installDate: new Date().toISOString(),
2509
+ tracked: true,
2510
+ });
2511
+
2512
+ // Add to customModuleSources for processing
2513
+ customModuleSources.set(untracked.id, {
2514
+ id: untracked.id,
2515
+ name: foundSource.info.name || untracked.name,
2516
+ sourcePath: path.resolve(foundSource.path),
2517
+ });
2518
+ } else {
2519
+ console.log(chalk.yellow(` ⚠ Could not find source for ${untracked.id}`));
2520
+ }
2521
+ }
2522
+ }
2523
+ }
2524
+
2525
+ console.log(chalk.dim('\nUntracked custom modules will remain installed but cannot be updated without their source.'));
2526
+ spinner.start('Preparing update...');
2527
+ }
2528
+
2529
+ // Handle missing custom module sources using shared method
2530
+ const customModuleResult = await this.handleMissingCustomSources(
2531
+ customModuleSources,
2532
+ bmadDir,
2533
+ projectRoot,
2534
+ 'update',
2535
+ installedModules,
2536
+ );
2537
+
2538
+ const { validCustomModules, keptModulesWithoutSources } = customModuleResult;
2539
+
2540
+ const customModulesFromManifest = validCustomModules.map((m) => ({
2541
+ ...m,
2542
+ isCustom: true,
2543
+ hasUpdate: true,
2544
+ }));
2545
+
2546
+ // Add untracked modules to the update list but mark them as untrackable
2547
+ for (const untracked of untrackedCustomModules) {
2548
+ if (!customModuleSources.has(untracked.id)) {
2549
+ customModulesFromManifest.push({
2550
+ ...untracked,
2551
+ isCustom: true,
2552
+ hasUpdate: false, // Can't update without source
2553
+ untracked: true,
2554
+ });
2555
+ }
2556
+ }
2557
+
2558
+ const allAvailableModules = [...availableModules, ...customModulesFromManifest];
2559
+ const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
2560
+
2561
+ // Core module is special - never include it in update flow
2562
+ const nonCoreInstalledModules = installedModules.filter((id) => id !== 'core');
2012
2563
 
2013
2564
  // Only update modules that are BOTH installed AND available (we have source for)
2014
- const modulesToUpdate = installedModules.filter((id) => availableModuleIds.has(id));
2015
- const skippedModules = installedModules.filter((id) => !availableModuleIds.has(id));
2565
+ const modulesToUpdate = nonCoreInstalledModules.filter((id) => availableModuleIds.has(id));
2566
+ const skippedModules = nonCoreInstalledModules.filter((id) => !availableModuleIds.has(id));
2567
+
2568
+ // Add custom modules that were kept without sources to the skipped modules
2569
+ // This ensures their agents are preserved in the manifest
2570
+ for (const keptModule of keptModulesWithoutSources) {
2571
+ if (!skippedModules.includes(keptModule)) {
2572
+ skippedModules.push(keptModule);
2573
+ }
2574
+ }
2016
2575
 
2017
2576
  spinner.succeed(`Found ${modulesToUpdate.length} module(s) to update and ${configuredIdes.length} configured tool(s)`);
2018
2577
 
@@ -2051,7 +2610,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2051
2610
  lastModified: new Date().toISOString(),
2052
2611
  };
2053
2612
 
2054
- // Check if bmad_folder has changed
2055
2613
  const existingBmadFolderName = path.basename(bmadDir);
2056
2614
  const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
2057
2615
 
@@ -2077,6 +2635,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2077
2635
  _quickUpdate: true, // Flag to skip certain prompts
2078
2636
  _preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
2079
2637
  _savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
2638
+ _customModuleSources: customModuleSources, // Pass custom module sources for updates
2639
+ _existingModules: installedModules, // Pass all installed modules for manifest generation
2080
2640
  };
2081
2641
 
2082
2642
  // Call the standard install method
@@ -2647,7 +3207,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2647
3207
  const agentSidecarFolder = config.coreConfig?.agent_sidecar_folder;
2648
3208
 
2649
3209
  // Resolve path variables
2650
- const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('{bmad_folder}', bmadDir);
3210
+ const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('.bmad', bmadDir);
2651
3211
 
2652
3212
  // Create sidecar directory for this agent
2653
3213
  const agentSidecarDir = path.join(resolvedSidecarFolder, finalAgentName);
@@ -2716,6 +3276,233 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2716
3276
  }
2717
3277
  }
2718
3278
  }
3279
+
3280
+ /**
3281
+ * Handle missing custom module sources interactively
3282
+ * @param {Map} customModuleSources - Map of custom module ID to info
3283
+ * @param {string} bmadDir - BMAD directory
3284
+ * @param {string} projectRoot - Project root directory
3285
+ * @param {string} operation - Current operation ('update', 'compile', etc.)
3286
+ * @param {Array} installedModules - Array of installed module IDs (will be modified)
3287
+ * @returns {Object} Object with validCustomModules array and keptModulesWithoutSources array
3288
+ */
3289
+ async handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, operation, installedModules) {
3290
+ const validCustomModules = [];
3291
+ const keptModulesWithoutSources = []; // Track modules kept without sources
3292
+ const customModulesWithMissingSources = [];
3293
+
3294
+ // Check which sources exist
3295
+ for (const [moduleId, customInfo] of customModuleSources) {
3296
+ if (await fs.pathExists(customInfo.sourcePath)) {
3297
+ validCustomModules.push({
3298
+ id: moduleId,
3299
+ name: customInfo.name,
3300
+ path: customInfo.sourcePath,
3301
+ info: customInfo,
3302
+ });
3303
+ } else {
3304
+ customModulesWithMissingSources.push({
3305
+ id: moduleId,
3306
+ name: customInfo.name,
3307
+ sourcePath: customInfo.sourcePath,
3308
+ relativePath: customInfo.relativePath,
3309
+ info: customInfo,
3310
+ });
3311
+ }
3312
+ }
3313
+
3314
+ // If no missing sources, return immediately
3315
+ if (customModulesWithMissingSources.length === 0) {
3316
+ return {
3317
+ validCustomModules,
3318
+ keptModulesWithoutSources: [],
3319
+ };
3320
+ }
3321
+
3322
+ // Stop any spinner for interactive prompts
3323
+ const currentSpinner = ora();
3324
+ if (currentSpinner.isSpinning) {
3325
+ currentSpinner.stop();
3326
+ }
3327
+
3328
+ console.log(chalk.yellow(`\n⚠️ Found ${customModulesWithMissingSources.length} custom module(s) with missing sources:`));
3329
+
3330
+ const inquirer = require('inquirer');
3331
+ let keptCount = 0;
3332
+ let updatedCount = 0;
3333
+ let removedCount = 0;
3334
+
3335
+ for (const missing of customModulesWithMissingSources) {
3336
+ console.log(chalk.dim(` • ${missing.name} (${missing.id})`));
3337
+ console.log(chalk.dim(` Original source: ${missing.relativePath}`));
3338
+ console.log(chalk.dim(` Full path: ${missing.sourcePath}`));
3339
+
3340
+ const choices = [
3341
+ {
3342
+ name: 'Keep installed (will not be processed)',
3343
+ value: 'keep',
3344
+ short: 'Keep',
3345
+ },
3346
+ {
3347
+ name: 'Specify new source location',
3348
+ value: 'update',
3349
+ short: 'Update',
3350
+ },
3351
+ ];
3352
+
3353
+ // Only add remove option if not just compiling agents
3354
+ if (operation !== 'compile-agents') {
3355
+ choices.push({
3356
+ name: '⚠️ REMOVE module completely (destructive!)',
3357
+ value: 'remove',
3358
+ short: 'Remove',
3359
+ });
3360
+ }
3361
+
3362
+ const { action } = await inquirer.prompt([
3363
+ {
3364
+ type: 'list',
3365
+ name: 'action',
3366
+ message: `How would you like to handle "${missing.name}"?`,
3367
+ choices,
3368
+ },
3369
+ ]);
3370
+
3371
+ switch (action) {
3372
+ case 'update': {
3373
+ const { newSourcePath } = await inquirer.prompt([
3374
+ {
3375
+ type: 'input',
3376
+ name: 'newSourcePath',
3377
+ message: 'Enter the new path to the custom module:',
3378
+ default: missing.sourcePath,
3379
+ validate: async (input) => {
3380
+ if (!input || input.trim() === '') {
3381
+ return 'Please enter a path';
3382
+ }
3383
+ const expandedPath = path.resolve(input.trim());
3384
+ if (!(await fs.pathExists(expandedPath))) {
3385
+ return 'Path does not exist';
3386
+ }
3387
+ // Check if it looks like a valid module
3388
+ const moduleYamlPath = path.join(expandedPath, 'module.yaml');
3389
+ const agentsPath = path.join(expandedPath, 'agents');
3390
+ const workflowsPath = path.join(expandedPath, 'workflows');
3391
+
3392
+ if (!(await fs.pathExists(moduleYamlPath)) && !(await fs.pathExists(agentsPath)) && !(await fs.pathExists(workflowsPath))) {
3393
+ return 'Path does not appear to contain a valid custom module';
3394
+ }
3395
+ return true;
3396
+ },
3397
+ },
3398
+ ]);
3399
+
3400
+ // Update the source in manifest
3401
+ const resolvedPath = path.resolve(newSourcePath.trim());
3402
+ missing.info.sourcePath = resolvedPath;
3403
+ // Remove relativePath - we only store absolute sourcePath now
3404
+ delete missing.info.relativePath;
3405
+ await this.manifest.addCustomModule(bmadDir, missing.info);
3406
+
3407
+ validCustomModules.push({
3408
+ id: moduleId,
3409
+ name: missing.name,
3410
+ path: resolvedPath,
3411
+ info: missing.info,
3412
+ });
3413
+
3414
+ updatedCount++;
3415
+ console.log(chalk.green(`✓ Updated source location`));
3416
+
3417
+ break;
3418
+ }
3419
+ case 'remove': {
3420
+ // Extra confirmation for destructive remove
3421
+ console.log(chalk.red.bold(`\n⚠️ WARNING: This will PERMANENTLY DELETE "${missing.name}" and all its files!`));
3422
+ console.log(chalk.red(` Module location: ${path.join(bmadDir, moduleId)}`));
3423
+
3424
+ const { confirm } = await inquirer.prompt([
3425
+ {
3426
+ type: 'confirm',
3427
+ name: 'confirm',
3428
+ message: chalk.red.bold('Are you absolutely sure you want to delete this module?'),
3429
+ default: false,
3430
+ },
3431
+ ]);
3432
+
3433
+ if (confirm) {
3434
+ const { typedConfirm } = await inquirer.prompt([
3435
+ {
3436
+ type: 'input',
3437
+ name: 'typedConfirm',
3438
+ message: chalk.red.bold('Type "DELETE" to confirm permanent deletion:'),
3439
+ validate: (input) => {
3440
+ if (input !== 'DELETE') {
3441
+ return chalk.red('You must type "DELETE" exactly to proceed');
3442
+ }
3443
+ return true;
3444
+ },
3445
+ },
3446
+ ]);
3447
+
3448
+ if (typedConfirm === 'DELETE') {
3449
+ // Remove the module from filesystem and manifest
3450
+ const modulePath = path.join(bmadDir, moduleId);
3451
+ if (await fs.pathExists(modulePath)) {
3452
+ const fsExtra = require('fs-extra');
3453
+ await fsExtra.remove(modulePath);
3454
+ console.log(chalk.yellow(` ✓ Deleted module directory: ${path.relative(projectRoot, modulePath)}`));
3455
+ }
3456
+
3457
+ await this.manifest.removeModule(bmadDir, moduleId);
3458
+ await this.manifest.removeCustomModule(bmadDir, moduleId);
3459
+ console.log(chalk.yellow(` ✓ Removed from manifest`));
3460
+
3461
+ // Also remove from installedModules list
3462
+ if (installedModules && installedModules.includes(moduleId)) {
3463
+ const index = installedModules.indexOf(moduleId);
3464
+ if (index !== -1) {
3465
+ installedModules.splice(index, 1);
3466
+ }
3467
+ }
3468
+
3469
+ removedCount++;
3470
+ console.log(chalk.red.bold(`✓ "${missing.name}" has been permanently removed`));
3471
+ } else {
3472
+ console.log(chalk.dim(' Removal cancelled - module will be kept'));
3473
+ keptCount++;
3474
+ }
3475
+ } else {
3476
+ console.log(chalk.dim(' Removal cancelled - module will be kept'));
3477
+ keptCount++;
3478
+ }
3479
+
3480
+ break;
3481
+ }
3482
+ case 'keep': {
3483
+ keptCount++;
3484
+ keptModulesWithoutSources.push(moduleId);
3485
+ console.log(chalk.dim(` Module will be kept as-is`));
3486
+
3487
+ break;
3488
+ }
3489
+ // No default
3490
+ }
3491
+ }
3492
+
3493
+ // Show summary
3494
+ if (keptCount > 0 || updatedCount > 0 || removedCount > 0) {
3495
+ console.log(chalk.dim(`\nSummary for custom modules with missing sources:`));
3496
+ if (keptCount > 0) console.log(chalk.dim(` • ${keptCount} module(s) kept as-is`));
3497
+ if (updatedCount > 0) console.log(chalk.dim(` • ${updatedCount} module(s) updated with new sources`));
3498
+ if (removedCount > 0) console.log(chalk.red(` • ${removedCount} module(s) permanently deleted`));
3499
+ }
3500
+
3501
+ return {
3502
+ validCustomModules,
3503
+ keptModulesWithoutSources,
3504
+ };
3505
+ }
2719
3506
  }
2720
3507
 
2721
3508
  module.exports = { Installer };