bmad-method 6.0.0-alpha.15 → 6.0.0-alpha.17

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 (512) hide show
  1. package/.coderabbit.yaml +8 -4
  2. package/.github/scripts/discord-helpers.sh +21 -2
  3. package/.github/workflows/discord.yaml +31 -7
  4. package/.github/workflows/manual-release.yaml +12 -42
  5. package/.markdownlint-cli2.yaml +2 -2
  6. package/.prettierignore +2 -2
  7. package/.vscode/settings.json +1 -1
  8. package/CHANGELOG.md +126 -1
  9. package/LICENSE +1 -1
  10. package/README.md +29 -2
  11. package/docs/agent-customization-guide.md +10 -10
  12. package/docs/custom-content-installation.md +100 -196
  13. package/docs/custom-content.md +122 -0
  14. package/docs/ide-info/crush.md +1 -1
  15. package/docs/ide-info/cursor.md +7 -7
  16. package/docs/ide-info/iflow.md +3 -3
  17. package/docs/ide-info/opencode.md +1 -1
  18. package/docs/ide-info/rovo-dev.md +1 -1
  19. package/docs/index.md +2 -2
  20. package/docs/installers-bundlers/ide-injections.md +2 -2
  21. package/docs/installers-bundlers/installers-modules-platforms-reference.md +25 -25
  22. package/docs/sample-custom-modules/README.md +11 -0
  23. package/docs/sample-custom-modules/sample-unitary-module/README.md +8 -0
  24. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/commit-poet/commit-poet.agent.yaml +1 -1
  25. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith.agent.yaml +14 -14
  26. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/module.yaml +5 -1
  27. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-01-init.md +2 -2
  28. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-02-q1.md +1 -1
  29. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-03-q2.md +1 -1
  30. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-04-q3.md +1 -1
  31. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-05-q4.md +1 -1
  32. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-06-q5.md +1 -1
  33. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-07-q6.md +1 -1
  34. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-08-q7.md +1 -1
  35. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-09-q8.md +1 -1
  36. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-10-q9.md +1 -1
  37. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-11-q10.md +1 -1
  38. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-12-results.md +1 -1
  39. package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/workflow.md +1 -1
  40. package/docs/sample-custom-modules/sample-wellness-module/README.md +6 -0
  41. package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/meditation-guide.agent.yaml +37 -39
  42. package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/foo.md +3 -0
  43. package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion-sidecar/addition1.md +1 -0
  44. package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion.agent.yaml +8 -13
  45. package/docs/sample-custom-modules/sample-wellness-module/module.yaml +17 -0
  46. package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/wellness-journal/workflow.md +3 -3
  47. package/docs/v4-to-v6-upgrade.md +27 -27
  48. package/docs/web-bundles-gemini-gpt-guide.md +6 -458
  49. package/eslint.config.mjs +2 -2
  50. package/package.json +1 -1
  51. package/src/core/agents/bmad-master.agent.yaml +6 -11
  52. package/src/core/module.yaml +10 -24
  53. package/src/core/resources/excalidraw/README.md +6 -6
  54. package/src/core/tasks/advanced-elicitation.xml +3 -3
  55. package/src/core/tasks/index-docs.xml +1 -1
  56. package/src/core/tasks/validate-workflow.xml +1 -1
  57. package/src/core/tasks/workflow.xml +4 -4
  58. package/src/core/tools/shard-doc.xml +12 -12
  59. package/src/core/workflows/brainstorming/workflow.md +3 -3
  60. package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +2 -2
  61. package/src/core/workflows/party-mode/workflow.md +4 -4
  62. package/src/modules/bmb/agents/bmad-builder.agent.yaml +15 -15
  63. package/src/modules/bmb/{README.md → docs/README.md} +15 -27
  64. package/src/modules/bmb/docs/agents/agent-compilation.md +3 -3
  65. package/src/modules/bmb/docs/agents/agent-menu-patterns.md +23 -24
  66. package/src/modules/bmb/docs/agents/expert-agent-architecture.md +21 -22
  67. package/src/modules/bmb/docs/agents/index.md +1 -1
  68. package/src/modules/bmb/docs/agents/simple-agent-architecture.md +17 -52
  69. package/src/modules/bmb/docs/agents/understanding-agent-types.md +6 -6
  70. package/src/modules/bmb/docs/workflows/architecture.md +1 -1
  71. package/src/modules/bmb/docs/workflows/common-workflow-tools.csv +3 -3
  72. package/src/modules/bmb/docs/workflows/templates/step-01-init-continuable-template.md +1 -1
  73. package/src/modules/bmb/docs/workflows/templates/step-1b-template.md +1 -1
  74. package/src/modules/bmb/docs/workflows/templates/step-file.md +3 -3
  75. package/src/modules/bmb/docs/workflows/templates/step-template.md +3 -3
  76. package/src/modules/bmb/docs/workflows/templates/workflow-template.md +2 -2
  77. package/src/modules/bmb/docs/workflows/templates/workflow.md +1 -1
  78. package/src/modules/bmb/module.yaml +6 -16
  79. package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper.agent.yaml +7 -7
  80. package/src/modules/bmb/reference/agents/module-examples/README.md +3 -4
  81. package/src/modules/bmb/reference/agents/module-examples/security-engineer.agent.yaml +5 -5
  82. package/src/modules/bmb/reference/agents/module-examples/trend-analyst.agent.yaml +7 -7
  83. package/src/modules/bmb/reference/agents/simple-examples/README.md +1 -1
  84. package/src/modules/bmb/reference/agents/simple-examples/commit-poet.agent.yaml +1 -1
  85. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +1 -1
  86. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +1 -1
  87. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +3 -3
  88. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +3 -3
  89. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +5 -5
  90. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +5 -5
  91. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +5 -5
  92. package/src/modules/bmb/reference/workflows/meal-prep-nutrition/workflow.md +2 -2
  93. package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/README.md +1 -3
  94. package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/security-engineer.agent.yaml +6 -6
  95. package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/trend-analyst.agent.yaml +7 -7
  96. package/src/modules/bmb/workflows/create-agent/data/reference/agents/simple-examples/README.md +1 -1
  97. package/src/modules/bmb/workflows/create-agent/data/reference/agents/simple-examples/commit-poet.agent.yaml +1 -1
  98. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +1 -1
  99. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +1 -1
  100. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +3 -3
  101. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +3 -3
  102. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +5 -5
  103. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +5 -5
  104. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +5 -5
  105. package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/workflow.md +2 -2
  106. package/src/modules/bmb/workflows/create-agent/data/validation-complete.md +5 -5
  107. package/src/modules/bmb/workflows/create-agent/steps/step-01-brainstorm.md +5 -5
  108. package/src/modules/bmb/workflows/create-agent/steps/step-02-discover.md +7 -11
  109. package/src/modules/bmb/workflows/create-agent/steps/step-03-persona.md +7 -7
  110. package/src/modules/bmb/workflows/create-agent/steps/step-04-commands.md +10 -10
  111. package/src/modules/bmb/workflows/create-agent/steps/step-05-name.md +7 -6
  112. package/src/modules/bmb/workflows/create-agent/steps/step-06-build.md +20 -58
  113. package/src/modules/bmb/workflows/create-agent/steps/step-07-validate.md +6 -6
  114. package/src/modules/bmb/workflows/create-agent/steps/{step-11-celebrate.md → step-08-celebrate.md} +3 -7
  115. package/src/modules/bmb/workflows/create-agent/templates/agent-plan.template.md +3 -0
  116. package/src/modules/bmb/workflows/create-agent/templates/expert-agent.template.md +364 -0
  117. package/src/modules/bmb/workflows/create-agent/templates/simple-agent.template.md +257 -0
  118. package/src/modules/bmb/workflows/create-agent/workflow.md +2 -35
  119. package/src/modules/bmb/workflows/create-module/steps/step-01-init.md +4 -4
  120. package/src/modules/bmb/workflows/create-module/steps/step-01b-continue.md +2 -2
  121. package/src/modules/bmb/workflows/create-module/steps/step-02-concept.md +5 -5
  122. package/src/modules/bmb/workflows/create-module/steps/step-03-components.md +5 -5
  123. package/src/modules/bmb/workflows/create-module/steps/step-04-structure.md +6 -6
  124. package/src/modules/bmb/workflows/create-module/steps/step-05-config.md +5 -5
  125. package/src/modules/bmb/workflows/create-module/steps/step-06-agents.md +9 -9
  126. package/src/modules/bmb/workflows/create-module/steps/step-07-workflows.md +5 -5
  127. package/src/modules/bmb/workflows/create-module/steps/step-08-installer.md +8 -8
  128. package/src/modules/bmb/workflows/create-module/steps/step-09-documentation.md +7 -7
  129. package/src/modules/bmb/workflows/create-module/steps/step-10-roadmap.md +7 -7
  130. package/src/modules/bmb/workflows/create-module/steps/step-11-validate.md +6 -6
  131. package/src/modules/bmb/workflows/create-module/templates/agent.template.md +15 -19
  132. package/src/modules/bmb/workflows/create-module/templates/module.template.yaml +1 -1
  133. package/src/modules/bmb/workflows/create-module/workflow.md +3 -3
  134. package/src/modules/bmb/workflows/create-workflow/steps/step-01-init.md +4 -4
  135. package/src/modules/bmb/workflows/create-workflow/steps/step-02-gather.md +6 -6
  136. package/src/modules/bmb/workflows/create-workflow/steps/step-03-tools-configuration.md +5 -5
  137. package/src/modules/bmb/workflows/create-workflow/steps/step-04-plan-review.md +4 -4
  138. package/src/modules/bmb/workflows/create-workflow/steps/step-05-output-format-design.md +4 -4
  139. package/src/modules/bmb/workflows/create-workflow/steps/step-06-design.md +11 -11
  140. package/src/modules/bmb/workflows/create-workflow/steps/step-07-build.md +14 -14
  141. package/src/modules/bmb/workflows/create-workflow/steps/step-08-review.md +4 -4
  142. package/src/modules/bmb/workflows/create-workflow/steps/step-09-complete.md +2 -2
  143. package/src/modules/bmb/workflows/create-workflow/workflow.md +2 -2
  144. package/src/modules/bmb/workflows/edit-agent/steps/step-01-discover-intent.md +3 -3
  145. package/src/modules/bmb/workflows/edit-agent/steps/step-02-analyze-agent.md +13 -13
  146. package/src/modules/bmb/workflows/edit-agent/steps/step-03-propose-changes.md +5 -5
  147. package/src/modules/bmb/workflows/edit-agent/steps/step-04-apply-changes.md +3 -3
  148. package/src/modules/bmb/workflows/edit-agent/steps/step-05-validate.md +5 -5
  149. package/src/modules/bmb/workflows/edit-agent/workflow.md +1 -1
  150. package/src/modules/bmb/workflows/edit-workflow/steps/step-01-analyze.md +4 -4
  151. package/src/modules/bmb/workflows/edit-workflow/steps/step-02-discover.md +3 -3
  152. package/src/modules/bmb/workflows/edit-workflow/steps/step-03-improve.md +6 -6
  153. package/src/modules/bmb/workflows/edit-workflow/steps/step-04-validate.md +3 -3
  154. package/src/modules/bmb/workflows/edit-workflow/steps/step-05-compliance-check.md +3 -3
  155. package/src/modules/bmb/workflows/edit-workflow/workflow.md +1 -1
  156. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-01-validate-goal.md +3 -3
  157. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-02-workflow-validation.md +5 -5
  158. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-03-step-validation.md +6 -6
  159. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-04-file-validation.md +4 -4
  160. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-05-intent-spectrum-validation.md +4 -4
  161. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-06-web-subprocess-validation.md +4 -4
  162. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-07-holistic-analysis.md +4 -4
  163. package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-08-generate-report.md +3 -3
  164. package/src/modules/bmb/workflows/workflow-compliance-check/workflow.md +1 -1
  165. package/src/modules/bmb/workflows-legacy/edit-module/README.md +1 -17
  166. package/src/modules/bmb/workflows-legacy/edit-module/checklist.md +3 -4
  167. package/src/modules/bmb/workflows-legacy/edit-module/instructions.md +4 -5
  168. package/src/modules/bmb/workflows-legacy/edit-module/workflow.yaml +10 -10
  169. package/src/modules/bmb/workflows-legacy/module-brief/README.md +2 -2
  170. package/src/modules/bmb/workflows-legacy/module-brief/instructions.md +2 -2
  171. package/src/modules/bmb/workflows-legacy/module-brief/workflow.yaml +4 -4
  172. package/src/modules/bmgd/README.md +1 -1
  173. package/src/modules/bmgd/agents/game-architect.agent.yaml +6 -6
  174. package/src/modules/bmgd/agents/game-designer.agent.yaml +7 -7
  175. package/src/modules/bmgd/agents/game-dev.agent.yaml +10 -10
  176. package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +21 -21
  177. package/src/modules/bmgd/module.yaml +2 -8
  178. package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/instructions.md +1 -1
  179. package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.yaml +9 -9
  180. package/src/modules/bmgd/workflows/1-preproduction/game-brief/instructions.md +1 -1
  181. package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.yaml +8 -8
  182. package/src/modules/bmgd/workflows/2-design/gdd/instructions-gdd.md +4 -4
  183. package/src/modules/bmgd/workflows/2-design/gdd/workflow.yaml +30 -30
  184. package/src/modules/bmgd/workflows/2-design/narrative/instructions-narrative.md +1 -1
  185. package/src/modules/bmgd/workflows/2-design/narrative/workflow.yaml +5 -5
  186. package/src/modules/bmgd/workflows/3-technical/game-architecture/instructions.md +1 -1
  187. package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.yaml +2 -2
  188. package/src/modules/bmgd/workflows/4-production/code-review/instructions.md +4 -4
  189. package/src/modules/bmgd/workflows/4-production/code-review/workflow.yaml +2 -2
  190. package/src/modules/bmgd/workflows/4-production/correct-course/checklist.md +1 -1
  191. package/src/modules/bmgd/workflows/4-production/correct-course/instructions.md +2 -2
  192. package/src/modules/bmgd/workflows/4-production/correct-course/workflow.yaml +2 -2
  193. package/src/modules/bmgd/workflows/4-production/create-story/instructions.md +2 -2
  194. package/src/modules/bmgd/workflows/4-production/create-story/workflow.yaml +2 -2
  195. package/src/modules/bmgd/workflows/4-production/dev-story/instructions.md +2 -2
  196. package/src/modules/bmgd/workflows/4-production/dev-story/workflow.yaml +2 -2
  197. package/src/modules/bmgd/workflows/4-production/epic-tech-context/checklist.md +1 -1
  198. package/src/modules/bmgd/workflows/4-production/epic-tech-context/instructions.md +2 -2
  199. package/src/modules/bmgd/workflows/4-production/epic-tech-context/workflow.yaml +2 -2
  200. package/src/modules/bmgd/workflows/4-production/retrospective/instructions.md +2 -2
  201. package/src/modules/bmgd/workflows/4-production/retrospective/workflow.yaml +3 -3
  202. package/src/modules/bmgd/workflows/4-production/sprint-planning/instructions.md +2 -2
  203. package/src/modules/bmgd/workflows/4-production/sprint-planning/workflow.yaml +2 -2
  204. package/src/modules/bmgd/workflows/4-production/story-context/checklist.md +1 -1
  205. package/src/modules/bmgd/workflows/4-production/story-context/context-template.xml +2 -2
  206. package/src/modules/bmgd/workflows/4-production/story-context/instructions.md +2 -2
  207. package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +2 -2
  208. package/src/modules/bmgd/workflows/4-production/story-done/instructions.md +1 -1
  209. package/src/modules/bmgd/workflows/4-production/story-done/workflow.yaml +2 -2
  210. package/src/modules/bmgd/workflows/4-production/story-ready/instructions.md +1 -1
  211. package/src/modules/bmgd/workflows/4-production/story-ready/workflow.yaml +2 -2
  212. package/src/modules/bmm/README.md +1 -1
  213. package/src/modules/bmm/agents/analyst.agent.yaml +9 -9
  214. package/src/modules/bmm/agents/architect.agent.yaml +8 -8
  215. package/src/modules/bmm/agents/dev.agent.yaml +5 -5
  216. package/src/modules/bmm/agents/pm.agent.yaml +8 -8
  217. package/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml +5 -5
  218. package/src/modules/bmm/agents/sm.agent.yaml +11 -11
  219. package/src/modules/bmm/agents/tea.agent.yaml +13 -13
  220. package/src/modules/bmm/agents/tech-writer.agent.yaml +9 -9
  221. package/src/modules/bmm/agents/ux-designer.agent.yaml +6 -6
  222. package/src/modules/bmm/docs/README.md +0 -25
  223. package/src/modules/bmm/docs/agents-guide.md +16 -14
  224. package/src/modules/bmm/docs/brownfield-guide.md +6 -6
  225. package/src/modules/bmm/docs/enterprise-agentic-development.md +3 -3
  226. package/src/modules/bmm/docs/faq.md +5 -18
  227. package/src/modules/bmm/docs/glossary.md +3 -4
  228. package/src/modules/bmm/docs/images/README.md +1 -1
  229. package/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw +8 -8
  230. package/src/modules/bmm/docs/party-mode.md +3 -3
  231. package/src/modules/bmm/docs/quick-flow-solo-dev.md +5 -5
  232. package/src/modules/bmm/docs/quick-spec-flow.md +2 -16
  233. package/src/modules/bmm/docs/quick-start.md +3 -3
  234. package/src/modules/bmm/docs/test-architecture.md +15 -23
  235. package/src/modules/bmm/docs/troubleshooting.md +6 -25
  236. package/src/modules/bmm/docs/workflow-document-project-reference.md +1 -1
  237. package/src/modules/bmm/docs/workflows-implementation.md +3 -104
  238. package/src/modules/bmm/module.yaml +25 -24
  239. package/src/modules/bmm/testarch/knowledge/overview.md +0 -1
  240. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-01-init.md +1 -1
  241. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-01b-continue.md +1 -1
  242. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-02-vision.md +3 -3
  243. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-03-users.md +3 -3
  244. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-04-metrics.md +3 -3
  245. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-05-scope.md +3 -3
  246. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-06-complete.md +5 -3
  247. package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/workflow.md +2 -2
  248. package/src/modules/bmm/workflows/1-analysis/research/workflow.md +2 -2
  249. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +4 -4
  250. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +4 -4
  251. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +4 -4
  252. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +4 -4
  253. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +4 -4
  254. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +4 -4
  255. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +4 -4
  256. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +4 -4
  257. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +4 -4
  258. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +4 -4
  259. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +4 -4
  260. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +4 -4
  261. package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +2 -2
  262. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-01-init.md +1 -1
  263. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-01b-continue.md +1 -1
  264. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-02-discovery.md +3 -3
  265. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-03-success.md +7 -7
  266. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-04-journeys.md +8 -8
  267. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-05-domain.md +12 -12
  268. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-06-innovation.md +12 -12
  269. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-07-project-type.md +10 -10
  270. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-08-scoping.md +7 -7
  271. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-09-functional.md +9 -9
  272. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-10-nonfunctional.md +9 -9
  273. package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-11-complete.md +1 -1
  274. package/src/modules/bmm/workflows/2-plan-workflows/prd/workflow.md +2 -2
  275. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-01-document-discovery.md +1 -1
  276. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-02-prd-analysis.md +1 -1
  277. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-03-epic-coverage-validation.md +6 -6
  278. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-04-ux-alignment.md +1 -1
  279. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-05-epic-quality-review.md +2 -2
  280. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-06-final-assessment.md +1 -1
  281. package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/workflow.md +1 -1
  282. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-01-init.md +1 -1
  283. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-02-context.md +4 -4
  284. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-03-starter.md +4 -4
  285. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-04-decisions.md +4 -4
  286. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-05-patterns.md +4 -4
  287. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-06-structure.md +4 -4
  288. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-07-validation.md +4 -4
  289. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-08-complete.md +3 -3
  290. package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/workflow.md +2 -2
  291. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +3 -3
  292. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +3 -3
  293. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +3 -3
  294. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +3 -3
  295. package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +3 -3
  296. package/src/modules/bmm/workflows/4-implementation/code-review/instructions.xml +4 -3
  297. package/src/modules/bmm/workflows/4-implementation/code-review/workflow.yaml +2 -2
  298. package/src/modules/bmm/workflows/4-implementation/correct-course/checklist.md +1 -1
  299. package/src/modules/bmm/workflows/4-implementation/correct-course/instructions.md +2 -2
  300. package/src/modules/bmm/workflows/4-implementation/correct-course/workflow.yaml +2 -2
  301. package/src/modules/bmm/workflows/4-implementation/create-story/checklist.md +2 -2
  302. package/src/modules/bmm/workflows/4-implementation/create-story/instructions.xml +5 -5
  303. package/src/modules/bmm/workflows/4-implementation/create-story/template.md +3 -5
  304. package/src/modules/bmm/workflows/4-implementation/create-story/workflow.yaml +2 -2
  305. package/src/modules/bmm/workflows/4-implementation/dev-story/checklist.md +2 -2
  306. package/src/modules/bmm/workflows/4-implementation/dev-story/instructions.xml +11 -8
  307. package/src/modules/bmm/workflows/4-implementation/dev-story/workflow.yaml +2 -2
  308. package/src/modules/bmm/workflows/4-implementation/retrospective/instructions.md +3 -3
  309. package/src/modules/bmm/workflows/4-implementation/retrospective/workflow.yaml +3 -3
  310. package/src/modules/bmm/workflows/4-implementation/sprint-planning/instructions.md +15 -22
  311. package/src/modules/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +4 -5
  312. package/src/modules/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +2 -2
  313. package/src/modules/bmm/workflows/4-implementation/sprint-status/instructions.md +90 -35
  314. package/src/modules/bmm/workflows/4-implementation/sprint-status/workflow.yaml +2 -2
  315. package/src/modules/bmm/workflows/bmad-quick-flow/create-tech-spec/workflow.yaml +5 -5
  316. package/src/modules/bmm/workflows/bmad-quick-flow/quick-dev/workflow.yaml +7 -7
  317. package/src/modules/bmm/workflows/document-project/instructions.md +5 -5
  318. package/src/modules/bmm/workflows/document-project/workflow.yaml +2 -2
  319. package/src/modules/bmm/workflows/document-project/workflows/deep-dive.yaml +5 -5
  320. package/src/modules/bmm/workflows/document-project/workflows/full-scan.yaml +5 -5
  321. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-dataflow/instructions.md +1 -1
  322. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-dataflow/workflow.yaml +6 -6
  323. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-diagram/instructions.md +2 -2
  324. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-diagram/workflow.yaml +6 -6
  325. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-flowchart/instructions.md +2 -2
  326. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-flowchart/workflow.yaml +6 -6
  327. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-wireframe/instructions.md +1 -1
  328. package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-wireframe/workflow.yaml +6 -6
  329. package/src/modules/bmm/workflows/generate-project-context/steps/step-01-discover.md +2 -2
  330. package/src/modules/bmm/workflows/generate-project-context/steps/step-02-generate.md +3 -3
  331. package/src/modules/bmm/workflows/generate-project-context/steps/step-03-complete.md +2 -2
  332. package/src/modules/bmm/workflows/generate-project-context/workflow.md +5 -5
  333. package/src/modules/bmm/workflows/testarch/atdd/instructions.md +2 -2
  334. package/src/modules/bmm/workflows/testarch/atdd/workflow.yaml +2 -2
  335. package/src/modules/bmm/workflows/testarch/automate/instructions.md +2 -2
  336. package/src/modules/bmm/workflows/testarch/automate/workflow.yaml +2 -2
  337. package/src/modules/bmm/workflows/testarch/ci/instructions.md +1 -1
  338. package/src/modules/bmm/workflows/testarch/ci/workflow.yaml +2 -2
  339. package/src/modules/bmm/workflows/testarch/framework/instructions.md +3 -3
  340. package/src/modules/bmm/workflows/testarch/framework/workflow.yaml +2 -2
  341. package/src/modules/bmm/workflows/testarch/nfr-assess/instructions.md +1 -1
  342. package/src/modules/bmm/workflows/testarch/nfr-assess/workflow.yaml +2 -2
  343. package/src/modules/bmm/workflows/testarch/test-design/instructions.md +3 -3
  344. package/src/modules/bmm/workflows/testarch/test-design/test-design-template.md +1 -1
  345. package/src/modules/bmm/workflows/testarch/test-design/workflow.yaml +10 -4
  346. package/src/modules/bmm/workflows/testarch/test-review/instructions.md +1 -1
  347. package/src/modules/bmm/workflows/testarch/test-review/workflow.yaml +2 -2
  348. package/src/modules/bmm/workflows/testarch/trace/instructions.md +6 -6
  349. package/src/modules/bmm/workflows/testarch/trace/workflow.yaml +2 -2
  350. package/src/modules/bmm/workflows/workflow-status/init/instructions.md +1 -1
  351. package/src/modules/bmm/workflows/workflow-status/init/workflow.yaml +4 -4
  352. package/src/modules/bmm/workflows/workflow-status/instructions.md +3 -3
  353. package/src/modules/bmm/workflows/workflow-status/project-levels.yaml +1 -1
  354. package/src/modules/bmm/workflows/workflow-status/workflow.yaml +2 -2
  355. package/src/modules/cis/agents/brainstorming-coach.agent.yaml +4 -4
  356. package/src/modules/cis/agents/creative-problem-solver.agent.yaml +4 -4
  357. package/src/modules/cis/agents/design-thinking-coach.agent.yaml +4 -4
  358. package/src/modules/cis/agents/innovation-strategist.agent.yaml +4 -4
  359. package/src/modules/cis/agents/presentation-master.agent.yaml +3 -3
  360. package/src/modules/cis/agents/storyteller/storyteller-sidecar/stories-told.md +7 -0
  361. package/src/modules/cis/agents/storyteller/storyteller-sidecar/story-preferences.md +7 -0
  362. package/src/modules/cis/agents/{storyteller.agent.yaml → storyteller/storyteller.agent.yaml} +9 -4
  363. package/src/modules/cis/module.yaml +3 -8
  364. package/src/modules/cis/{README.md → readme.md} +1 -1
  365. package/src/modules/cis/workflows/README.md +1 -1
  366. package/src/modules/cis/workflows/design-thinking/instructions.md +2 -2
  367. package/src/modules/cis/workflows/design-thinking/workflow.yaml +7 -7
  368. package/src/modules/cis/workflows/innovation-strategy/instructions.md +2 -2
  369. package/src/modules/cis/workflows/innovation-strategy/workflow.yaml +7 -7
  370. package/src/modules/cis/workflows/problem-solving/instructions.md +2 -2
  371. package/src/modules/cis/workflows/problem-solving/workflow.yaml +7 -7
  372. package/src/modules/cis/workflows/storytelling/instructions.md +2 -2
  373. package/src/modules/cis/workflows/storytelling/workflow.yaml +7 -7
  374. package/src/utility/agent-components/activation-rules.txt +7 -0
  375. package/src/utility/agent-components/activation-steps.txt +13 -0
  376. package/src/utility/agent-components/agent-command-header.md +1 -0
  377. package/src/utility/{templates → agent-components}/agent.customize.template.yaml +0 -1
  378. package/src/utility/agent-components/handler-action.txt +4 -0
  379. package/src/utility/agent-components/handler-exec.txt +6 -0
  380. package/src/utility/agent-components/handler-multi.txt +14 -0
  381. package/src/utility/agent-components/handler-tmpl.txt +5 -0
  382. package/src/utility/agent-components/handler-validate-workflow.txt +7 -0
  383. package/src/utility/agent-components/handler-workflow.txt +10 -0
  384. package/src/utility/agent-components/menu-handlers.txt +6 -0
  385. package/test/README.md +1 -1
  386. package/test/test-agent-schema.js +2 -2
  387. package/tools/cli/README.md +1 -607
  388. package/tools/cli/commands/build.js +7 -7
  389. package/tools/cli/commands/install.js +9 -20
  390. package/tools/cli/commands/list.js +13 -1
  391. package/tools/cli/installers/lib/core/config-collector.js +243 -73
  392. package/tools/cli/installers/lib/core/custom-module-cache.js +36 -16
  393. package/tools/cli/installers/lib/core/dependency-resolver.js +2 -2
  394. package/tools/cli/installers/lib/core/detector.js +16 -16
  395. package/tools/cli/installers/lib/core/ide-config-manager.js +9 -7
  396. package/tools/cli/installers/lib/core/installer.js +595 -1006
  397. package/tools/cli/installers/lib/core/manifest-generator.js +43 -40
  398. package/tools/cli/installers/lib/core/manifest.js +23 -20
  399. package/tools/cli/installers/lib/custom/handler.js +16 -49
  400. package/tools/cli/installers/lib/ide/_base-ide.js +26 -33
  401. package/tools/cli/installers/lib/ide/antigravity.js +3 -3
  402. package/tools/cli/installers/lib/ide/claude-code.js +3 -3
  403. package/tools/cli/installers/lib/ide/codex.js +2 -2
  404. package/tools/cli/installers/lib/ide/gemini.js +6 -6
  405. package/tools/cli/installers/lib/ide/kiro-cli.js +2 -2
  406. package/tools/cli/installers/lib/ide/opencode.js +2 -2
  407. package/tools/cli/installers/lib/ide/roo.js +15 -5
  408. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
  409. package/tools/cli/installers/lib/ide/shared/module-injections.js +2 -2
  410. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +2 -2
  411. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +4 -4
  412. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +1 -1
  413. package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +3 -3
  414. package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +3 -3
  415. package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +1 -1
  416. package/tools/cli/installers/lib/modules/manager.js +304 -313
  417. package/tools/cli/lib/activation-builder.js +8 -13
  418. package/tools/cli/lib/agent/compiler.js +68 -168
  419. package/tools/cli/lib/agent/installer.js +8 -128
  420. package/tools/cli/lib/agent-analyzer.js +2 -2
  421. package/tools/cli/lib/agent-party-generator.js +5 -17
  422. package/tools/cli/lib/config.js +2 -2
  423. package/tools/cli/lib/platform-codes.js +2 -2
  424. package/tools/cli/lib/ui.js +610 -551
  425. package/tools/cli/lib/xml-handler.js +3 -55
  426. package/tools/cli/lib/yaml-format.js +4 -6
  427. package/tools/cli/lib/yaml-xml-builder.js +14 -26
  428. package/tools/flattener/ignoreRules.js +1 -1
  429. package/tools/flattener/xml.js +1 -7
  430. package/tools/lib/xml-utils.js +13 -0
  431. package/tools/migrate-custom-module-paths.js +2 -2
  432. package/tools/validate-agent-schema.js +2 -2
  433. package/docs/v6-open-items.md +0 -17
  434. package/example-custom-content/README.md +0 -8
  435. package/example-custom-module/mwm/README.md +0 -9
  436. package/example-custom-module/mwm/agents/cbt-coach/cbt-coach-sidecar/cognitive-distortions.md +0 -47
  437. package/example-custom-module/mwm/agents/cbt-coach/cbt-coach-sidecar/thought-records.md +0 -17
  438. package/example-custom-module/mwm/agents/cbt-coach/cbt-coach.agent.yaml +0 -151
  439. package/example-custom-module/mwm/agents/crisis-navigator.agent.yaml +0 -138
  440. package/example-custom-module/mwm/module.yaml +0 -28
  441. package/example-custom-module/mwm/workflows/cbt-thought-record/README.md +0 -31
  442. package/example-custom-module/mwm/workflows/cbt-thought-record/workflow.md +0 -45
  443. package/example-custom-module/mwm/workflows/crisis-support/README.md +0 -31
  444. package/example-custom-module/mwm/workflows/crisis-support/workflow.md +0 -45
  445. package/src/core/agents/bmad-web-orchestrator.agent.xml +0 -113
  446. package/src/modules/bmb/_module-installer/installer.js +0 -76
  447. package/src/modules/bmb/docs/agents/module-agent-architecture.md +0 -367
  448. package/src/modules/bmb/workflows/create-agent/steps/step-08-setup.md +0 -179
  449. package/src/modules/bmb/workflows/create-agent/steps/step-09-customize.md +0 -197
  450. package/src/modules/bmb/workflows/create-agent/steps/step-10-build-tools.md +0 -180
  451. package/src/modules/bmb/workflows/create-agent/templates/agent_commands.md +0 -21
  452. package/src/modules/bmb/workflows/create-agent/templates/agent_persona.md +0 -25
  453. package/src/modules/bmb/workflows/create-agent/templates/agent_purpose_and_type.md +0 -23
  454. package/src/modules/bmm/tasks/daily-standup.xml +0 -85
  455. package/src/modules/cis/agents/README.md +0 -104
  456. package/src/utility/models/action-command-header.md +0 -0
  457. package/src/utility/models/agent-activation-ide.xml +0 -51
  458. package/src/utility/models/agent-activation-web.xml +0 -50
  459. package/src/utility/models/agent-command-header.md +0 -1
  460. package/src/utility/models/agent-config-template.md +0 -23
  461. package/src/utility/models/agent-in-team-activation.xml +0 -3
  462. package/src/utility/models/fragments/activation-rules.xml +0 -7
  463. package/src/utility/models/fragments/activation-steps.xml +0 -16
  464. package/src/utility/models/fragments/handler-action.xml +0 -4
  465. package/src/utility/models/fragments/handler-exec.xml +0 -6
  466. package/src/utility/models/fragments/handler-multi.xml +0 -14
  467. package/src/utility/models/fragments/handler-tmpl.xml +0 -5
  468. package/src/utility/models/fragments/handler-validate-workflow.xml +0 -7
  469. package/src/utility/models/fragments/handler-workflow.xml +0 -9
  470. package/src/utility/models/fragments/menu-handlers.xml +0 -6
  471. package/src/utility/models/fragments/web-bundle-activation-steps.xml +0 -32
  472. package/tools/cli/bundlers/bundle-web.js +0 -179
  473. package/tools/cli/bundlers/test-analyst.js +0 -28
  474. package/tools/cli/bundlers/test-bundler.js +0 -118
  475. package/tools/cli/bundlers/web-bundler.js +0 -1764
  476. package/tools/cli/installers/lib/core/post-install-sidecar-replacement.js +0 -79
  477. package/tools/cli/lib/replace-project-root.js +0 -239
  478. package/tools/cli/regenerate-manifests.js +0 -28
  479. package/tools/cli/test-yaml-builder.js +0 -43
  480. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/instructions.md +0 -0
  481. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/bundlers.md +0 -0
  482. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/deploy.md +0 -0
  483. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/docs.md +0 -0
  484. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +0 -0
  485. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +0 -0
  486. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/tests.md +0 -0
  487. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/memories.md +0 -0
  488. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/templates/csv-headers.template +0 -0
  489. /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/wassup/workflow.md +0 -0
  490. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/insights.md +0 -0
  491. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/instructions.md +0 -0
  492. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/memories.md +0 -0
  493. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/patterns.md +0 -0
  494. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/daily-checkin/README.md +0 -0
  495. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/daily-checkin/workflow.md +0 -0
  496. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/guided-meditation/README.md +0 -0
  497. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/guided-meditation/workflow.md +0 -0
  498. /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/wellness-journal/README.md +0 -0
  499. /package/src/modules/bmgd/workflows/4-production/code-review/{backlog_template.md → backlog-template.md} +0 -0
  500. /package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/product-brief.template.md +0 -0
  501. /package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/templates/readiness-report-template.md +0 -0
  502. /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/architecture-decision-template.md +0 -0
  503. /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/data/domain-complexity.csv +0 -0
  504. /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/data/project-types.csv +0 -0
  505. /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-01b-continue.md +0 -0
  506. /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/_shared/excalidraw-library.json +0 -0
  507. /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/_shared/excalidraw-templates.yaml +0 -0
  508. /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-dataflow/checklist.md +0 -0
  509. /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-diagram/checklist.md +0 -0
  510. /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-flowchart/checklist.md +0 -0
  511. /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-wireframe/checklist.md +0 -0
  512. /package/src/utility/{models/fragments/handler-data.xml → agent-components/handler-data.txt} +0 -0
@@ -1,23 +1,3 @@
1
- /**
2
- * File: tools/cli/installers/lib/core/installer.js
3
- *
4
- * BMAD Method - Business Model Agile Development Method
5
- * Repository: https://github.com/paulpreibisch/BMAD-METHOD
6
- *
7
- * Copyright (c) 2025 Paul Preibisch
8
- * Licensed under the Apache License, Version 2.0
9
- *
10
- * ---
11
- *
12
- * @fileoverview Core BMAD installation orchestrator with AgentVibes injection point support
13
- * @context Manages complete BMAD installation flow including core agents, modules, IDE configs, and optional TTS integration
14
- * @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete BMAD installation
15
- * @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js
16
- * @entrypoints Called by install.js command via installer.install(config)
17
- * @patterns Injection point processing (AgentVibes), placeholder replacement ({bmad_folder}), module dependency resolution
18
- * @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement()
19
- */
20
-
21
1
  const path = require('node:path');
22
2
  const fs = require('fs-extra');
23
3
  const chalk = require('chalk');
@@ -32,13 +12,13 @@ const { Config } = require('../../../lib/config');
32
12
  const { XmlHandler } = require('../../../lib/xml-handler');
33
13
  const { DependencyResolver } = require('./dependency-resolver');
34
14
  const { ConfigCollector } = require('./config-collector');
35
- // processInstallation no longer needed - LLMs understand {project-root}
36
15
  const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
37
16
  const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
38
17
  const { CLIUtils } = require('../../../lib/cli-utils');
39
18
  const { ManifestGenerator } = require('./manifest-generator');
40
19
  const { IdeConfigManager } = require('./ide-config-manager');
41
- const { replaceAgentSidecarFolders } = require('./post-install-sidecar-replacement');
20
+ const { CustomHandler } = require('../custom/handler');
21
+ const { filterCustomizationData } = require('../../../lib/agent/compiler');
42
22
 
43
23
  class Installer {
44
24
  constructor() {
@@ -52,49 +32,66 @@ class Installer {
52
32
  this.dependencyResolver = new DependencyResolver();
53
33
  this.configCollector = new ConfigCollector();
54
34
  this.ideConfigManager = new IdeConfigManager();
55
- this.installedFiles = []; // Track all installed files
35
+ this.installedFiles = new Set(); // Track all installed files
56
36
  this.ttsInjectedFiles = []; // Track files with TTS injection applied
57
37
  }
58
38
 
59
39
  /**
60
40
  * Find the bmad installation directory in a project
61
- * V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
41
+ * V6+ installations can use ANY folder name but ALWAYS have _config/manifest.yaml
42
+ * Also checks for legacy _cfg folder for migration
62
43
  * @param {string} projectDir - Project directory
63
- * @returns {Promise<string>} Path to bmad directory
44
+ * @returns {Promise<Object>} { bmadDir: string, hasLegacyCfg: boolean }
64
45
  */
65
46
  async findBmadDir(projectDir) {
66
47
  // Check if project directory exists
67
48
  if (!(await fs.pathExists(projectDir))) {
68
49
  // Project doesn't exist yet, return default
69
- return path.join(projectDir, 'bmad');
50
+ return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
70
51
  }
71
52
 
72
- // V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
73
- // This is the definitive marker of a V6+ installation
53
+ let bmadDir = null;
54
+ let hasLegacyCfg = false;
55
+
74
56
  try {
75
57
  const entries = await fs.readdir(projectDir, { withFileTypes: true });
76
58
  for (const entry of entries) {
77
59
  if (entry.isDirectory()) {
78
- const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
60
+ const bmadPath = path.join(projectDir, entry.name);
61
+
62
+ // Check for current _config folder
63
+ const manifestPath = path.join(bmadPath, '_config', 'manifest.yaml');
79
64
  if (await fs.pathExists(manifestPath)) {
80
- // Found a V6+ installation
81
- return path.join(projectDir, entry.name);
65
+ // Found a V6+ installation with current _config folder
66
+ return { bmadDir: bmadPath, hasLegacyCfg: false };
67
+ }
68
+
69
+ // Check for legacy _cfg folder
70
+ const legacyManifestPath = path.join(bmadPath, '_cfg', 'manifest.yaml');
71
+ if (await fs.pathExists(legacyManifestPath)) {
72
+ bmadDir = bmadPath;
73
+ hasLegacyCfg = true;
82
74
  }
83
75
  }
84
76
  }
85
77
  } catch {
86
- // Ignore errors, fall through to default
78
+ console.log(chalk.red('Error reading project directory for BMAD installation detection'));
79
+ }
80
+
81
+ // If we found a bmad directory (with or without legacy _cfg)
82
+ if (bmadDir) {
83
+ return { bmadDir, hasLegacyCfg };
87
84
  }
88
85
 
89
86
  // No V6+ installation found, return default
90
87
  // This will be used for new installations
91
- return path.join(projectDir, 'bmad');
88
+ return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
92
89
  }
93
90
 
94
91
  /**
95
92
  * @function copyFileWithPlaceholderReplacement
96
93
  * @intent Copy files from BMAD source to installation directory with dynamic content transformation
97
- * @why Enables installation-time customization: {bmad_folder} replacement + optional AgentVibes TTS injection
94
+ * @why Enables installation-time customization: _bmad replacement + optional AgentVibes TTS injection
98
95
  * @param {string} sourcePath - Absolute path to source file in BMAD repository
99
96
  * @param {string} targetPath - Absolute path to destination file in user's project
100
97
  * @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad')
@@ -104,11 +101,6 @@ class Installer {
104
101
  * @calledby installCore(), installModule(), IDE installers during file vendoring
105
102
  * @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy()
106
103
  *
107
- * AI NOTE: This is the core transformation pipeline for ALL BMAD installation file copies.
108
- * It performs two transformations in sequence:
109
- * 1. {bmad_folder} → user's custom folder name (e.g., ".bmad" or "bmad")
110
- * 2. <!-- TTS_INJECTION:* --> → TTS bash calls (if enabled) OR stripped (if disabled)
111
- *
112
104
  * The injection point processing enables loose coupling between BMAD and TTS providers:
113
105
  * - BMAD source contains injection markers (not actual TTS code)
114
106
  * - At install-time, markers are replaced OR removed based on user preference
@@ -139,16 +131,6 @@ class Installer {
139
131
  // Read the file content
140
132
  let content = await fs.readFile(sourcePath, 'utf8');
141
133
 
142
- // Replace {bmad_folder} placeholder with actual folder name
143
- if (content.includes('{bmad_folder}')) {
144
- content = content.replaceAll('{bmad_folder}', bmadFolderName);
145
- }
146
-
147
- // Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
148
- if (content.includes('{*bmad_folder*}')) {
149
- content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
150
- }
151
-
152
134
  // Process AgentVibes injection points (pass targetPath for tracking)
153
135
  content = this.processTTSInjectionPoints(content, targetPath);
154
136
 
@@ -338,6 +320,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
338
320
 
339
321
  for (const ide of newlySelectedIdes) {
340
322
  // List of IDEs that have interactive prompts
323
+ //TODO: Why is this here, hardcoding this list here is bad, fix me!
341
324
  const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes(
342
325
  ide,
343
326
  );
@@ -361,7 +344,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
361
344
  } else if (ideModule.default) {
362
345
  SetupClass = ideModule.default;
363
346
  } else {
364
- // Skip if no setup class found
365
347
  continue;
366
348
  }
367
349
 
@@ -407,12 +389,21 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
407
389
  * @param {string[]} config.ides - IDEs to configure
408
390
  * @param {boolean} config.skipIde - Skip IDE configuration
409
391
  */
410
- async install(config) {
411
- // Display BMAD logo
412
- CLIUtils.displayLogo();
392
+ async install(originalConfig) {
393
+ // Clone config to avoid mutating the caller's object
394
+ const config = { ...originalConfig };
395
+
396
+ // Check if core config was already collected in UI
397
+ const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
398
+
399
+ // Only display logo if core config wasn't already collected (meaning we're not continuing from UI)
400
+ if (!hasCoreConfig) {
401
+ // Display BMAD logo
402
+ CLIUtils.displayLogo();
413
403
 
414
- // Display welcome message
415
- CLIUtils.displaySection('BMAD Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
404
+ // Display welcome message
405
+ CLIUtils.displaySection('BMad Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
406
+ }
416
407
 
417
408
  // Note: Legacy V4 detection now happens earlier in UI.promptInstall()
418
409
  // before any config collection, so we don't need to check again here
@@ -420,7 +411,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
420
411
  const projectDir = path.resolve(config.directory);
421
412
 
422
413
  // If core config was pre-collected (from interactive mode), use it
423
- if (config.coreConfig) {
414
+ if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
424
415
  this.configCollector.collectedConfig.core = config.coreConfig;
425
416
  // Also store in allAnswers for cross-referencing
426
417
  this.configCollector.allAnswers = {};
@@ -431,16 +422,118 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
431
422
 
432
423
  // Collect configurations for modules (skip if quick update already collected them)
433
424
  let moduleConfigs;
425
+ let customModulePaths = new Map();
426
+
434
427
  if (config._quickUpdate) {
435
428
  // Quick update already collected all configs, use them directly
436
429
  moduleConfigs = this.configCollector.collectedConfig;
430
+
431
+ // For quick update, populate customModulePaths from _customModuleSources
432
+ if (config._customModuleSources) {
433
+ for (const [moduleId, customInfo] of config._customModuleSources) {
434
+ customModulePaths.set(moduleId, customInfo.sourcePath);
435
+ }
436
+ }
437
437
  } else {
438
- // Regular install - collect configurations (core was already collected in UI.promptInstall if interactive)
439
- moduleConfigs = await this.configCollector.collectAllConfigurations(config.modules || [], path.resolve(config.directory));
438
+ // For regular updates (modify flow), check manifest for custom module sources
439
+ if (config._isUpdate && config._existingInstall && config._existingInstall.customModules) {
440
+ for (const customModule of config._existingInstall.customModules) {
441
+ // Ensure we have an absolute sourcePath
442
+ let absoluteSourcePath = customModule.sourcePath;
443
+
444
+ // Check if sourcePath is a cache-relative path (starts with _config)
445
+ if (absoluteSourcePath && absoluteSourcePath.startsWith('_config')) {
446
+ // Convert cache-relative path to absolute path
447
+ absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
448
+ }
449
+ // If no sourcePath but we have relativePath, convert it
450
+ else if (!absoluteSourcePath && customModule.relativePath) {
451
+ // relativePath is relative to the project root (parent of bmad dir)
452
+ absoluteSourcePath = path.resolve(projectDir, customModule.relativePath);
453
+ }
454
+ // Ensure sourcePath is absolute for anything else
455
+ else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
456
+ absoluteSourcePath = path.resolve(absoluteSourcePath);
457
+ }
458
+
459
+ if (absoluteSourcePath) {
460
+ customModulePaths.set(customModule.id, absoluteSourcePath);
461
+ }
462
+ }
463
+ }
464
+
465
+ // Build custom module paths map from customContent
466
+
467
+ // Handle selectedFiles (from existing install path or manual directory input)
468
+ if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
469
+ const customHandler = new CustomHandler();
470
+ for (const customFile of config.customContent.selectedFiles) {
471
+ const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory));
472
+ if (customInfo && customInfo.id) {
473
+ customModulePaths.set(customInfo.id, customInfo.path);
474
+ }
475
+ }
476
+ }
477
+
478
+ // Handle new custom content sources from UI
479
+ if (config.customContent && config.customContent.sources) {
480
+ for (const source of config.customContent.sources) {
481
+ customModulePaths.set(source.id, source.path);
482
+ }
483
+ }
484
+
485
+ // Handle cachedModules (from new install path where modules are cached)
486
+ // Only include modules that were actually selected for installation
487
+ if (config.customContent && config.customContent.cachedModules) {
488
+ // Get selected cached module IDs (if available)
489
+ const selectedCachedIds = config.customContent.selectedCachedModules || [];
490
+ // If no selection info, include all cached modules (for backward compatibility)
491
+ const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected;
492
+
493
+ for (const cachedModule of config.customContent.cachedModules) {
494
+ // For cached modules, the path is the cachePath which contains the module.yaml
495
+ if (
496
+ cachedModule.id &&
497
+ cachedModule.cachePath && // Include if selected or if we should include all
498
+ (shouldIncludeAll || selectedCachedIds.includes(cachedModule.id))
499
+ ) {
500
+ customModulePaths.set(cachedModule.id, cachedModule.cachePath);
501
+ }
502
+ }
503
+ }
504
+
505
+ // Get list of all modules including custom modules
506
+ // Order: core first, then official modules, then custom modules
507
+ const allModulesForConfig = ['core'];
508
+
509
+ // Add official modules (excluding core and any custom modules)
510
+ const officialModules = (config.modules || []).filter((m) => m !== 'core' && !customModulePaths.has(m));
511
+ allModulesForConfig.push(...officialModules);
512
+
513
+ // Add custom modules at the end
514
+ for (const [moduleId] of customModulePaths) {
515
+ if (!allModulesForConfig.includes(moduleId)) {
516
+ allModulesForConfig.push(moduleId);
517
+ }
518
+ }
519
+
520
+ // Check if core was already collected in UI
521
+ if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
522
+ // Core already collected, skip it in config collection
523
+ const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
524
+ moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, path.resolve(config.directory), {
525
+ customModulePaths,
526
+ });
527
+ } else {
528
+ // Core not collected yet, include it
529
+ moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
530
+ customModulePaths,
531
+ });
532
+ }
440
533
  }
441
534
 
442
- // Get bmad_folder from config (default to 'bmad' for backwards compatibility)
443
- const bmadFolderName = moduleConfigs.core && moduleConfigs.core.bmad_folder ? moduleConfigs.core.bmad_folder : 'bmad';
535
+ // Always use _bmad as the folder name
536
+ const bmadFolderName = '_bmad';
444
537
  this.bmadFolderName = bmadFolderName; // Store for use in other methods
445
538
 
446
539
  // Store AgentVibes configuration for injection point processing
@@ -449,6 +542,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
449
542
  // Set bmad folder name on module manager and IDE manager for placeholder replacement
450
543
  this.moduleManager.setBmadFolderName(bmadFolderName);
451
544
  this.moduleManager.setCoreConfig(moduleConfigs.core || {});
545
+ this.moduleManager.setCustomModulePaths(customModulePaths);
452
546
  this.ideManager.setBmadFolderName(bmadFolderName);
453
547
 
454
548
  // Tool selection will be collected after we determine if it's a reinstall/update/new install
@@ -459,63 +553,15 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
459
553
  // Resolve target directory (path.resolve handles platform differences)
460
554
  const projectDir = path.resolve(config.directory);
461
555
 
462
- // Check if bmad_folder has changed from existing installation (only if project dir exists)
463
556
  let existingBmadDir = null;
464
557
  let existingBmadFolderName = null;
465
558
 
466
559
  if (await fs.pathExists(projectDir)) {
467
- existingBmadDir = await this.findBmadDir(projectDir);
560
+ const result = await this.findBmadDir(projectDir);
561
+ existingBmadDir = result.bmadDir;
468
562
  existingBmadFolderName = path.basename(existingBmadDir);
469
563
  }
470
564
 
471
- const targetBmadDir = path.join(projectDir, bmadFolderName);
472
-
473
- // If bmad_folder changed during update/upgrade, back up old folder and do fresh install
474
- if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== bmadFolderName) {
475
- spinner.stop();
476
- console.log(chalk.yellow(`\n⚠️ bmad_folder has changed: ${existingBmadFolderName} → ${bmadFolderName}`));
477
- console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
478
-
479
- const inquirer = require('inquirer');
480
- const { confirmFreshInstall } = await inquirer.prompt([
481
- {
482
- type: 'confirm',
483
- name: 'confirmFreshInstall',
484
- message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
485
- default: true,
486
- },
487
- ]);
488
-
489
- if (!confirmFreshInstall) {
490
- console.log(chalk.yellow('Installation cancelled.'));
491
- return { success: false, cancelled: true };
492
- }
493
-
494
- spinner.start('Backing up existing installation...');
495
-
496
- // Find a unique backup name
497
- let backupDir = `${existingBmadDir}-bak`;
498
- let counter = 1;
499
- while (await fs.pathExists(backupDir)) {
500
- backupDir = `${existingBmadDir}-bak-${counter}`;
501
- counter++;
502
- }
503
-
504
- // Rename the old folder to backup
505
- await fs.move(existingBmadDir, backupDir);
506
-
507
- spinner.succeed(`Backed up ${existingBmadFolderName} → ${path.basename(backupDir)}`);
508
- console.log(chalk.cyan('\n📋 Important:'));
509
- console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
510
- console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
511
- console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
512
- console.log(chalk.dim(` - To the new location:`));
513
- console.log(chalk.dim(` ${bmadFolderName}/_cfg/`));
514
- console.log('');
515
-
516
- spinner.start('Starting fresh installation...');
517
- }
518
-
519
565
  // Create a project directory if it doesn't exist (user already confirmed)
520
566
  if (!(await fs.pathExists(projectDir))) {
521
567
  spinner.text = 'Creating installation directory...';
@@ -547,9 +593,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
547
593
 
548
594
  // Check if user already decided what to do (from early menu in ui.js)
549
595
  let action = null;
550
- if (config._requestedReinstall) {
551
- action = 'reinstall';
552
- } else if (config.actionType === 'update') {
596
+ if (config.actionType === 'update') {
553
597
  action = 'update';
554
598
  } else {
555
599
  // Fallback: Ask the user (backwards compatibility for other code paths)
@@ -561,64 +605,49 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
561
605
  action = promptResult.action;
562
606
  }
563
607
 
564
- if (action === 'cancel') {
565
- console.log('Installation cancelled.');
566
- return { success: false, cancelled: true };
567
- }
568
-
569
- if (action === 'reinstall') {
570
- // Warn about destructive operation
571
- console.log(chalk.red.bold('\n⚠️ WARNING: This is a destructive operation!'));
572
- console.log(chalk.red('All custom files and modifications in the bmad directory will be lost.'));
573
-
574
- const inquirer = require('inquirer');
575
- const { confirmReinstall } = await inquirer.prompt([
576
- {
577
- type: 'confirm',
578
- name: 'confirmReinstall',
579
- message: chalk.yellow('Are you sure you want to delete and reinstall?'),
580
- default: false,
581
- },
582
- ]);
583
-
584
- if (!confirmReinstall) {
585
- console.log('Installation cancelled.');
586
- return { success: false, cancelled: true };
587
- }
588
-
589
- // Remember previously configured IDEs before deleting
590
- config._previouslyConfiguredIdes = existingInstall.ides || [];
591
-
592
- // Remove existing installation
593
- await fs.remove(bmadDir);
594
- console.log(chalk.green('✓ Removed existing installation\n'));
595
-
596
- // Mark this as a full reinstall so we re-collect IDE configurations
597
- config._isFullReinstall = true;
598
- } else if (action === 'update') {
608
+ if (action === 'update') {
599
609
  // Store that we're updating for later processing
600
610
  config._isUpdate = true;
601
611
  config._existingInstall = existingInstall;
602
612
 
603
613
  // Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
604
614
  const existingFilesManifest = await this.readFilesManifest(bmadDir);
605
- console.log(chalk.dim(`DEBUG: Read ${existingFilesManifest.length} files from manifest`));
606
- console.log(chalk.dim(`DEBUG: Manifest has hashes: ${existingFilesManifest.some((f) => f.hash)}`));
607
-
608
615
  const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
609
616
 
610
- console.log(chalk.dim(`DEBUG: Found ${customFiles.length} custom files, ${modifiedFiles.length} modified files`));
611
- if (modifiedFiles.length > 0) {
612
- console.log(chalk.yellow('DEBUG: Modified files:'));
613
- for (const f of modifiedFiles) console.log(chalk.dim(` - ${f.path}`));
614
- }
615
-
616
617
  config._customFiles = customFiles;
617
618
  config._modifiedFiles = modifiedFiles;
618
619
 
620
+ // Also check cache directory for custom modules (like quick update does)
621
+ const cacheDir = path.join(bmadDir, '_config', 'custom');
622
+ if (await fs.pathExists(cacheDir)) {
623
+ const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
624
+
625
+ for (const cachedModule of cachedModules) {
626
+ if (cachedModule.isDirectory()) {
627
+ const moduleId = cachedModule.name;
628
+
629
+ // Skip if we already have this module from manifest
630
+ if (customModulePaths.has(moduleId)) {
631
+ continue;
632
+ }
633
+
634
+ const cachedPath = path.join(cacheDir, moduleId);
635
+
636
+ // Check if this is actually a custom module (has module.yaml)
637
+ const moduleYamlPath = path.join(cachedPath, 'module.yaml');
638
+ if (await fs.pathExists(moduleYamlPath)) {
639
+ customModulePaths.set(moduleId, cachedPath);
640
+ }
641
+ }
642
+ }
643
+
644
+ // Update module manager with the new custom module paths from cache
645
+ this.moduleManager.setCustomModulePaths(customModulePaths);
646
+ }
647
+
619
648
  // If there are custom files, back them up temporarily
620
649
  if (customFiles.length > 0) {
621
- const tempBackupDir = path.join(projectDir, '.bmad-custom-backup-temp');
650
+ const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
622
651
  await fs.ensureDir(tempBackupDir);
623
652
 
624
653
  spinner.start(`Backing up ${customFiles.length} custom files...`);
@@ -635,23 +664,19 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
635
664
 
636
665
  // For modified files, back them up to temp directory (will be restored as .bak files after install)
637
666
  if (modifiedFiles.length > 0) {
638
- const tempModifiedBackupDir = path.join(projectDir, '.bmad-modified-backup-temp');
667
+ const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
639
668
  await fs.ensureDir(tempModifiedBackupDir);
640
669
 
641
- console.log(chalk.yellow(`\nDEBUG: Backing up ${modifiedFiles.length} modified files to temp location`));
642
670
  spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
643
671
  for (const modifiedFile of modifiedFiles) {
644
672
  const relativePath = path.relative(bmadDir, modifiedFile.path);
645
673
  const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
646
- console.log(chalk.dim(`DEBUG: Backing up ${relativePath} to temp`));
647
674
  await fs.ensureDir(path.dirname(tempBackupPath));
648
675
  await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
649
676
  }
650
677
  spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
651
678
 
652
679
  config._tempModifiedBackupDir = tempModifiedBackupDir;
653
- } else {
654
- console.log(chalk.dim('DEBUG: No modified files detected'));
655
680
  }
656
681
  }
657
682
  } else if (existingInstall.installed && config._quickUpdate) {
@@ -667,9 +692,37 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
667
692
  config._customFiles = customFiles;
668
693
  config._modifiedFiles = modifiedFiles;
669
694
 
695
+ // Also check cache directory for custom modules (like quick update does)
696
+ const cacheDir = path.join(bmadDir, '_config', 'custom');
697
+ if (await fs.pathExists(cacheDir)) {
698
+ const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
699
+
700
+ for (const cachedModule of cachedModules) {
701
+ if (cachedModule.isDirectory()) {
702
+ const moduleId = cachedModule.name;
703
+
704
+ // Skip if we already have this module from manifest
705
+ if (customModulePaths.has(moduleId)) {
706
+ continue;
707
+ }
708
+
709
+ const cachedPath = path.join(cacheDir, moduleId);
710
+
711
+ // Check if this is actually a custom module (has module.yaml)
712
+ const moduleYamlPath = path.join(cachedPath, 'module.yaml');
713
+ if (await fs.pathExists(moduleYamlPath)) {
714
+ customModulePaths.set(moduleId, cachedPath);
715
+ }
716
+ }
717
+ }
718
+
719
+ // Update module manager with the new custom module paths from cache
720
+ this.moduleManager.setCustomModulePaths(customModulePaths);
721
+ }
722
+
670
723
  // Back up custom files
671
724
  if (customFiles.length > 0) {
672
- const tempBackupDir = path.join(projectDir, '.bmad-custom-backup-temp');
725
+ const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
673
726
  await fs.ensureDir(tempBackupDir);
674
727
 
675
728
  spinner.start(`Backing up ${customFiles.length} custom files...`);
@@ -685,7 +738,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
685
738
 
686
739
  // Back up modified files
687
740
  if (modifiedFiles.length > 0) {
688
- const tempModifiedBackupDir = path.join(projectDir, '.bmad-modified-backup-temp');
741
+ const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
689
742
  await fs.ensureDir(tempModifiedBackupDir);
690
743
 
691
744
  spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
@@ -740,7 +793,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
740
793
  config.skipIde = toolSelection.skipIde;
741
794
  const ideConfigurations = toolSelection.configurations;
742
795
 
743
- // Check if spinner is already running (e.g., from folder name change scenario)
744
796
  if (spinner.isSpinning) {
745
797
  spinner.text = 'Continuing installation...';
746
798
  } else {
@@ -751,7 +803,26 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
751
803
  spinner.text = 'Creating directory structure...';
752
804
  await this.createDirectoryStructure(bmadDir);
753
805
 
754
- // Get project root
806
+ // Cache custom modules if any
807
+ if (customModulePaths && customModulePaths.size > 0) {
808
+ spinner.text = 'Caching custom modules...';
809
+ const { CustomModuleCache } = require('./custom-module-cache');
810
+ const customCache = new CustomModuleCache(bmadDir);
811
+
812
+ for (const [moduleId, sourcePath] of customModulePaths) {
813
+ const cachedInfo = await customCache.cacheModule(moduleId, sourcePath, {
814
+ sourcePath: sourcePath, // Store original path for updates
815
+ });
816
+
817
+ // Update the customModulePaths to use the cached location
818
+ customModulePaths.set(moduleId, cachedInfo.cachePath);
819
+ }
820
+
821
+ // Update module manager with the cached paths
822
+ this.moduleManager.setCustomModulePaths(customModulePaths);
823
+ spinner.succeed('Custom modules cached');
824
+ }
825
+
755
826
  const projectRoot = getProjectRoot();
756
827
 
757
828
  // Step 1: Install core module first (if requested)
@@ -792,9 +863,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
792
863
  // Regular custom content from user input (non-cached)
793
864
  if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
794
865
  // Add custom modules to the installation list
866
+ const customHandler = new CustomHandler();
795
867
  for (const customFile of finalCustomContent.selectedFiles) {
796
- const { CustomHandler } = require('../custom/handler');
797
- const customHandler = new CustomHandler();
798
868
  const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
799
869
  if (customInfo && customInfo.id) {
800
870
  allModules.push(customInfo.id);
@@ -809,31 +879,32 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
809
879
 
810
880
  const modulesToInstall = allModules;
811
881
 
882
+ // For dependency resolution, we only need regular modules (not custom modules)
883
+ // Custom modules are already installed in _bmad and don't need dependency resolution from source
884
+ const regularModulesForResolution = allModules.filter((module) => {
885
+ // Check if this is a custom module
886
+ const isCustom =
887
+ customModulePaths.has(module) ||
888
+ (finalCustomContent && finalCustomContent.cachedModules && finalCustomContent.cachedModules.some((cm) => cm.id === module)) ||
889
+ (finalCustomContent &&
890
+ finalCustomContent.selected &&
891
+ finalCustomContent.selectedFiles &&
892
+ finalCustomContent.selectedFiles.some((f) => f.includes(module)));
893
+ return !isCustom;
894
+ });
895
+
812
896
  // For dependency resolution, we need to pass the project root
813
897
  // Create a temporary module manager that knows about custom content locations
814
898
  const tempModuleManager = new ModuleManager({
815
- scanProjectForModules: true,
816
899
  bmadDir: bmadDir, // Pass bmadDir so we can check cache
817
900
  });
818
901
 
819
- // Make sure custom modules are discoverable
820
- if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
821
- // The dependency resolver needs to know about these modules
822
- // We'll handle custom modules separately in the installation loop
823
- }
824
-
825
- const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
902
+ const resolution = await this.dependencyResolver.resolve(projectRoot, regularModulesForResolution, {
826
903
  verbose: config.verbose,
827
904
  moduleManager: tempModuleManager,
828
905
  });
829
906
 
830
- if (config.verbose) {
831
- spinner.succeed('Dependencies resolved');
832
- } else {
833
- spinner.succeed('Dependencies resolved');
834
- }
835
-
836
- // Core is already installed above, skip if included in resolution
907
+ spinner.succeed('Dependencies resolved');
837
908
 
838
909
  // Install modules with their dependencies
839
910
  if (allModules && allModules.length > 0) {
@@ -846,7 +917,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
846
917
  }
847
918
  installedModuleNames.add(moduleName);
848
919
 
849
- spinner.start(`Installing module: ${moduleName}...`);
920
+ // Show appropriate message based on whether this is a quick update
921
+ const isQuickUpdate = config._quickUpdate || false;
922
+ spinner.start(`${isQuickUpdate ? 'Updating' : 'Installing'} module: ${moduleName}...`);
850
923
 
851
924
  // Check if this is a custom module
852
925
  let isCustomModule = false;
@@ -872,8 +945,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
872
945
  customInfo = config._customModuleSources.get(moduleName);
873
946
  isCustomModule = true;
874
947
 
875
- // Check if this is a cached module (source path starts with _cfg)
876
- if (customInfo.sourcePath && (customInfo.sourcePath.startsWith('_cfg') || customInfo.sourcePath.includes('_cfg/custom'))) {
948
+ // Check if this is a cached module (source path starts with _config)
949
+ if (
950
+ customInfo.sourcePath &&
951
+ (customInfo.sourcePath.startsWith('_config') || customInfo.sourcePath.includes('_config/custom'))
952
+ ) {
877
953
  useCache = true;
878
954
  // Make sure we have the right path structure
879
955
  if (!customInfo.path) {
@@ -884,7 +960,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
884
960
 
885
961
  // Finally check regular custom content
886
962
  if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
887
- const { CustomHandler } = require('../custom/handler');
888
963
  const customHandler = new CustomHandler();
889
964
  for (const customFile of finalCustomContent.selectedFiles) {
890
965
  const info = await customHandler.getCustomInfo(customFile, projectDir);
@@ -897,78 +972,33 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
897
972
  }
898
973
 
899
974
  if (isCustomModule && customInfo) {
900
- // Install custom module using CustomHandler but as a proper module
901
- const { CustomHandler } = require('../custom/handler');
902
- const customHandler = new CustomHandler();
975
+ // Custom modules are now installed via ModuleManager just like standard modules
976
+ // The custom module path should already be in customModulePaths from earlier setup
977
+ if (!customModulePaths.has(moduleName) && customInfo.path) {
978
+ customModulePaths.set(moduleName, customInfo.path);
979
+ this.moduleManager.setCustomModulePaths(customModulePaths);
980
+ }
903
981
 
904
- // Install to module directory instead of custom directory
905
- const moduleTargetPath = path.join(bmadDir, moduleName);
906
- await fs.ensureDir(moduleTargetPath);
982
+ const collectedModuleConfig = moduleConfigs[moduleName] || {};
907
983
 
908
- const result = await customHandler.install(
909
- customInfo.path,
910
- path.join(bmadDir, 'temp-custom'),
911
- { ...config.coreConfig, ...customInfo.config, _bmadDir: bmadDir },
984
+ // Use ModuleManager to install the custom module
985
+ await this.moduleManager.install(
986
+ moduleName,
987
+ bmadDir,
912
988
  (filePath) => {
913
- // Track installed files with correct path
914
- const relativePath = path.relative(path.join(bmadDir, 'temp-custom'), filePath);
915
- const finalPath = path.join(moduleTargetPath, relativePath);
916
- this.installedFiles.push(finalPath);
989
+ this.installedFiles.add(filePath);
990
+ },
991
+ {
992
+ isCustom: true,
993
+ moduleConfig: collectedModuleConfig,
994
+ isQuickUpdate: config._quickUpdate || false,
995
+ installer: this,
917
996
  },
918
997
  );
919
998
 
920
- // Move from temp-custom to actual module directory
921
- const tempCustomPath = path.join(bmadDir, 'temp-custom');
922
- if (await fs.pathExists(tempCustomPath)) {
923
- const customDir = path.join(tempCustomPath, 'custom');
924
- if (await fs.pathExists(customDir)) {
925
- // Move contents to module directory
926
- const items = await fs.readdir(customDir);
927
- for (const item of items) {
928
- const srcPath = path.join(customDir, item);
929
- const destPath = path.join(moduleTargetPath, item);
930
-
931
- // If destination exists, remove it first (or we could merge)
932
- if (await fs.pathExists(destPath)) {
933
- await fs.remove(destPath);
934
- }
935
-
936
- await fs.move(srcPath, destPath);
937
- }
938
- }
939
- await fs.remove(tempCustomPath);
940
- }
941
-
942
- // Create module config
943
- await this.generateModuleConfigs(bmadDir, { [moduleName]: { ...config.coreConfig, ...customInfo.config } });
944
-
945
- // Store custom module info for later manifest update
946
- if (!config._customModulesToTrack) {
947
- config._customModulesToTrack = [];
948
- }
949
-
950
- // For cached modules, use appropriate path handling
951
- let sourcePath;
952
- if (useCache) {
953
- // Check if we have cached modules info (from initial install)
954
- if (finalCustomContent && finalCustomContent.cachedModules) {
955
- sourcePath = finalCustomContent.cachedModules.find((m) => m.id === moduleName)?.relativePath;
956
- } else {
957
- // During update, the sourcePath is already cache-relative if it starts with _cfg
958
- sourcePath =
959
- customInfo.sourcePath && customInfo.sourcePath.startsWith('_cfg')
960
- ? customInfo.sourcePath
961
- : path.relative(bmadDir, customInfo.path || customInfo.sourcePath);
962
- }
963
- } else {
964
- sourcePath = path.resolve(customInfo.path || customInfo.sourcePath);
965
- }
966
-
967
- config._customModulesToTrack.push({
968
- id: customInfo.id,
969
- name: customInfo.name,
970
- sourcePath: sourcePath,
971
- installDate: new Date().toISOString(),
999
+ // Create module config (include collected config from module.yaml prompts)
1000
+ await this.generateModuleConfigs(bmadDir, {
1001
+ [moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig },
972
1002
  });
973
1003
  } else {
974
1004
  // Regular module installation
@@ -980,7 +1010,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
980
1010
  }
981
1011
  }
982
1012
 
983
- spinner.succeed(`Module installed: ${moduleName}`);
1013
+ spinner.succeed(`Module ${isQuickUpdate ? 'updated' : 'installed'}: ${moduleName}`);
984
1014
  }
985
1015
 
986
1016
  // Install partial modules (only dependencies)
@@ -1002,71 +1032,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1002
1032
  }
1003
1033
  }
1004
1034
 
1005
- // Install custom content if provided AND selected
1006
- // Process custom content that wasn't installed as modules
1007
- // This is now handled in the module installation loop above
1008
- // This section is kept for backward compatibility with any custom content
1009
- // that doesn't have a module structure
1010
- const remainingCustomContent = [];
1011
- if (
1012
- config.customContent &&
1013
- config.customContent.hasCustomContent &&
1014
- config.customContent.customPath &&
1015
- config.customContent.selected &&
1016
- config.customContent.selectedFiles
1017
- ) {
1018
- // Filter out custom modules that were already installed
1019
- for (const customFile of config.customContent.selectedFiles) {
1020
- const { CustomHandler } = require('../custom/handler');
1021
- const customHandler = new CustomHandler();
1022
- const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
1023
-
1024
- // Skip if this was installed as a module
1025
- if (!customInfo || !customInfo.id || !allModules.includes(customInfo.id)) {
1026
- remainingCustomContent.push(customFile);
1027
- }
1028
- }
1029
- }
1030
-
1031
- if (remainingCustomContent.length > 0) {
1032
- spinner.start('Installing remaining custom content...');
1033
- const { CustomHandler } = require('../custom/handler');
1034
- const customHandler = new CustomHandler();
1035
-
1036
- // Use the remaining files
1037
- const customFiles = remainingCustomContent;
1038
-
1039
- if (customFiles.length > 0) {
1040
- console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
1041
- for (const customFile of customFiles) {
1042
- const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
1043
- if (customInfo) {
1044
- console.log(chalk.dim(` • ${customInfo.name} (${customInfo.relativePath})`));
1045
-
1046
- // Install the custom content
1047
- const result = await customHandler.install(
1048
- customInfo.path,
1049
- bmadDir,
1050
- { ...config.coreConfig, ...customInfo.config },
1051
- (filePath) => {
1052
- // Track installed files
1053
- this.installedFiles.push(filePath);
1054
- },
1055
- );
1056
-
1057
- if (result.errors.length > 0) {
1058
- console.log(chalk.yellow(` ⚠️ ${result.errors.length} error(s) occurred`));
1059
- for (const error of result.errors) {
1060
- console.log(chalk.dim(` - ${error}`));
1061
- }
1062
- } else {
1063
- console.log(chalk.green(` ✓ Installed ${result.agentsInstalled} agents, ${result.workflowsInstalled} workflows`));
1064
- }
1065
- }
1066
- }
1067
- }
1068
- spinner.succeed('Custom content installed');
1069
- }
1035
+ // All content is now installed as modules - no separate custom content handling needed
1070
1036
 
1071
1037
  // Generate clean config.yaml files for each installed module
1072
1038
  spinner.start('Generating module configurations...');
@@ -1078,13 +1044,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1078
1044
  // Customize templates are now created in processAgentFiles when building YAML agents
1079
1045
 
1080
1046
  // Pre-register manifest files that will be created (except files-manifest.csv to avoid recursion)
1081
- const cfgDir = path.join(bmadDir, '_cfg');
1082
- this.installedFiles.push(
1083
- path.join(cfgDir, 'manifest.yaml'),
1084
- path.join(cfgDir, 'workflow-manifest.csv'),
1085
- path.join(cfgDir, 'agent-manifest.csv'),
1086
- path.join(cfgDir, 'task-manifest.csv'),
1087
- );
1047
+ const cfgDir = path.join(bmadDir, '_config');
1048
+ this.installedFiles.add(path.join(cfgDir, 'manifest.yaml'));
1049
+ this.installedFiles.add(path.join(cfgDir, 'workflow-manifest.csv'));
1050
+ this.installedFiles.add(path.join(cfgDir, 'agent-manifest.csv'));
1051
+ this.installedFiles.add(path.join(cfgDir, 'task-manifest.csv'));
1088
1052
 
1089
1053
  // Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes BEFORE IDE setup
1090
1054
  spinner.start('Generating workflow and agent manifests...');
@@ -1108,18 +1072,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1108
1072
  modulesForCsvPreserve = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules;
1109
1073
  }
1110
1074
 
1111
- const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, this.installedFiles, {
1075
+ const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, [...this.installedFiles], {
1112
1076
  ides: config.ides || [],
1113
1077
  preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
1114
1078
  });
1115
1079
 
1116
- // Add custom modules to manifest (now that it exists)
1117
- if (config._customModulesToTrack && config._customModulesToTrack.length > 0) {
1118
- spinner.text = 'Storing custom module sources...';
1119
- for (const customModule of config._customModulesToTrack) {
1120
- await this.manifest.addCustomModule(bmadDir, customModule);
1121
- }
1122
- }
1080
+ // Custom modules are now included in the main modules list - no separate tracking needed
1123
1081
 
1124
1082
  spinner.succeed(
1125
1083
  `Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
@@ -1160,7 +1118,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1160
1118
 
1161
1119
  // Pass pre-collected configuration to avoid re-prompting
1162
1120
  await this.ideManager.setup(ide, projectDir, bmadDir, {
1163
- selectedModules: config.modules || [],
1121
+ selectedModules: allModules || [],
1164
1122
  preCollectedConfig: ideConfigurations[ide] || null,
1165
1123
  verbose: config.verbose,
1166
1124
  });
@@ -1180,24 +1138,24 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1180
1138
  console.log = originalLog;
1181
1139
 
1182
1140
  if (spinner.isSpinning) {
1183
- spinner.succeed(`Configured ${validIdes.length} IDE${validIdes.length > 1 ? 's' : ''}`);
1141
+ spinner.succeed(`Configured: ${validIdes.join(', ')}`);
1184
1142
  } else {
1185
- console.log(chalk.green(`✓ Configured ${validIdes.length} IDE${validIdes.length > 1 ? 's' : ''}`));
1143
+ console.log(chalk.green(`✓ Configured: ${validIdes.join(', ')}`));
1186
1144
  }
1187
1145
  }
1188
-
1189
- // Copy IDE-specific documentation (only for valid IDEs)
1190
- const validIdesForDocs = (config.ides || []).filter((ide) => ide && typeof ide === 'string');
1191
- if (validIdesForDocs.length > 0) {
1192
- spinner.start('Copying IDE documentation...');
1193
- await this.copyIdeDocumentation(validIdesForDocs, bmadDir);
1194
- spinner.succeed('IDE documentation copied');
1195
- }
1196
1146
  }
1197
1147
 
1198
1148
  // Run module-specific installers after IDE setup
1199
1149
  spinner.start('Running module-specific installers...');
1200
1150
 
1151
+ // Create a conditional logger based on verbose mode
1152
+ const verboseMode = process.env.BMAD_VERBOSE_INSTALL === 'true' || config.verbose;
1153
+ const moduleLogger = {
1154
+ log: (msg) => (verboseMode ? console.log(msg) : {}), // Only log in verbose mode
1155
+ error: (msg) => console.error(msg), // Always show errors
1156
+ warn: (msg) => console.warn(msg), // Always show warnings
1157
+ };
1158
+
1201
1159
  // Run core module installer if core was installed
1202
1160
  if (config.installCore || resolution.byModule.core) {
1203
1161
  spinner.text = 'Running core module installer...';
@@ -1206,11 +1164,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1206
1164
  installedIDEs: config.ides || [],
1207
1165
  moduleConfig: moduleConfigs.core || {},
1208
1166
  coreConfig: moduleConfigs.core || {},
1209
- logger: {
1210
- log: (msg) => console.log(msg),
1211
- error: (msg) => console.error(msg),
1212
- warn: (msg) => console.warn(msg),
1213
- },
1167
+ logger: moduleLogger,
1214
1168
  });
1215
1169
  }
1216
1170
 
@@ -1224,11 +1178,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1224
1178
  installedIDEs: config.ides || [],
1225
1179
  moduleConfig: moduleConfigs[moduleName] || {},
1226
1180
  coreConfig: moduleConfigs.core || {},
1227
- logger: {
1228
- log: (msg) => console.log(msg),
1229
- error: (msg) => console.error(msg),
1230
- warn: (msg) => console.warn(msg),
1231
- },
1181
+ logger: moduleLogger,
1232
1182
  });
1233
1183
  }
1234
1184
  }
@@ -1295,44 +1245,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1295
1245
  // Report custom and modified files if any were found
1296
1246
  if (customFiles.length > 0) {
1297
1247
  console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`));
1298
- console.log(chalk.dim('The following custom files were found and restored:\n'));
1299
- for (const file of customFiles) {
1300
- console.log(chalk.dim(` - ${path.relative(bmadDir, file)}`));
1301
- }
1302
- console.log('');
1303
1248
  }
1304
1249
 
1305
1250
  if (modifiedFiles.length > 0) {
1306
- console.log(chalk.yellow(`\n⚠️ Modified files detected: ${modifiedFiles.length}`));
1307
- console.log(chalk.dim('The following files were modified and backed up with .bak extension:\n'));
1308
- for (const file of modifiedFiles) {
1309
- console.log(chalk.dim(` - ${file.relativePath} → ${file.relativePath}.bak`));
1310
- }
1311
- console.log(chalk.dim('\nThese files have been updated with the new version.'));
1312
- console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n'));
1313
- }
1314
-
1315
- // Reinstall custom agents from _cfg/custom/agents/ sources
1316
- const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
1317
- if (customAgentResults.count > 0) {
1318
- console.log(chalk.green(`\n✓ Reinstalled ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`));
1319
- for (const agent of customAgentResults.agents) {
1320
- console.log(chalk.dim(` - ${agent}`));
1321
- }
1322
- }
1323
-
1324
- // Replace {agent_sidecar_folder} placeholders in all agent files
1325
- console.log(chalk.dim('\n Configuring agent sidecar folders...'));
1326
- const sidecarResults = await replaceAgentSidecarFolders(bmadDir);
1327
-
1328
- if (sidecarResults.filesReplaced > 0) {
1251
+ console.log(chalk.yellow(`\n⚠️ User modified files detected: ${modifiedFiles.length}`));
1329
1252
  console.log(
1330
- chalk.green(
1331
- ` ✓ Updated ${sidecarResults.filesReplaced} agent file(s) with ${sidecarResults.totalReplacements} sidecar reference(s)`,
1253
+ chalk.dim(
1254
+ '\nThese user modified files have been updated with the new version, search the project for .bak files that had your customizations.',
1332
1255
  ),
1333
1256
  );
1334
- } else {
1335
- console.log(chalk.dim(' No agent sidecar references found'));
1257
+ console.log(chalk.dim('Remove these .bak files it no longer needed\n'));
1336
1258
  }
1337
1259
 
1338
1260
  // Display completion message
@@ -1369,7 +1291,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1369
1291
 
1370
1292
  try {
1371
1293
  const projectDir = path.resolve(config.directory);
1372
- const bmadDir = await this.findBmadDir(projectDir);
1294
+ const { bmadDir } = await this.findBmadDir(projectDir);
1373
1295
  const existingInstall = await this.detector.detect(bmadDir);
1374
1296
 
1375
1297
  if (!existingInstall.installed) {
@@ -1385,12 +1307,44 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1385
1307
 
1386
1308
  // Check for custom modules with missing sources before update
1387
1309
  const customModuleSources = new Map();
1310
+
1311
+ // Check manifest for backward compatibility
1388
1312
  if (existingInstall.customModules) {
1389
1313
  for (const customModule of existingInstall.customModules) {
1390
1314
  customModuleSources.set(customModule.id, customModule);
1391
1315
  }
1392
1316
  }
1393
1317
 
1318
+ // Also check cache directory
1319
+ const cacheDir = path.join(bmadDir, '_config', 'custom');
1320
+ if (await fs.pathExists(cacheDir)) {
1321
+ const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
1322
+
1323
+ for (const cachedModule of cachedModules) {
1324
+ if (cachedModule.isDirectory()) {
1325
+ const moduleId = cachedModule.name;
1326
+
1327
+ // Skip if we already have this module
1328
+ if (customModuleSources.has(moduleId)) {
1329
+ continue;
1330
+ }
1331
+
1332
+ const cachedPath = path.join(cacheDir, moduleId);
1333
+
1334
+ // Check if this is actually a custom module (has module.yaml)
1335
+ const moduleYamlPath = path.join(cachedPath, 'module.yaml');
1336
+ if (await fs.pathExists(moduleYamlPath)) {
1337
+ customModuleSources.set(moduleId, {
1338
+ id: moduleId,
1339
+ name: moduleId,
1340
+ sourcePath: path.join('_config', 'custom', moduleId), // Relative path
1341
+ cached: true,
1342
+ });
1343
+ }
1344
+ }
1345
+ }
1346
+ }
1347
+
1394
1348
  if (customModuleSources.size > 0) {
1395
1349
  spinner.stop();
1396
1350
  console.log(chalk.yellow('\nChecking custom module sources before update...'));
@@ -1454,7 +1408,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1454
1408
  */
1455
1409
  async getStatus(directory) {
1456
1410
  const projectDir = path.resolve(directory);
1457
- const bmadDir = await this.findBmadDir(projectDir);
1411
+ const { bmadDir } = await this.findBmadDir(projectDir);
1458
1412
  return await this.detector.detect(bmadDir);
1459
1413
  }
1460
1414
 
@@ -1470,7 +1424,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1470
1424
  */
1471
1425
  async uninstall(directory) {
1472
1426
  const projectDir = path.resolve(directory);
1473
- const bmadDir = await this.findBmadDir(projectDir);
1427
+ const { bmadDir } = await this.findBmadDir(projectDir);
1474
1428
 
1475
1429
  if (await fs.pathExists(bmadDir)) {
1476
1430
  await fs.remove(bmadDir);
@@ -1487,8 +1441,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1487
1441
  */
1488
1442
  async createDirectoryStructure(bmadDir) {
1489
1443
  await fs.ensureDir(bmadDir);
1490
- await fs.ensureDir(path.join(bmadDir, '_cfg'));
1491
- await fs.ensureDir(path.join(bmadDir, '_cfg', 'agents'));
1444
+ await fs.ensureDir(path.join(bmadDir, '_config'));
1445
+ await fs.ensureDir(path.join(bmadDir, '_config', 'agents'));
1446
+ await fs.ensureDir(path.join(bmadDir, '_config', 'custom'));
1492
1447
  }
1493
1448
 
1494
1449
  /**
@@ -1497,7 +1452,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1497
1452
  * @param {Object} moduleConfigs - Collected configuration values
1498
1453
  */
1499
1454
  async generateModuleConfigs(bmadDir, moduleConfigs) {
1500
- const yaml = require('js-yaml');
1455
+ const yaml = require('yaml');
1501
1456
 
1502
1457
  // Extract core config values to share with other modules
1503
1458
  const coreConfig = moduleConfigs.core || {};
@@ -1505,7 +1460,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1505
1460
  // Get all installed module directories
1506
1461
  const entries = await fs.readdir(bmadDir, { withFileTypes: true });
1507
1462
  const installedModules = entries
1508
- .filter((entry) => entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs')
1463
+ .filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs')
1509
1464
  .map((entry) => entry.name);
1510
1465
 
1511
1466
  // Generate config.yaml for each installed module
@@ -1543,12 +1498,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1543
1498
  coreSection = '\n# Core Configuration Values\n';
1544
1499
  }
1545
1500
 
1501
+ // Clean the config to remove any non-serializable values (like functions)
1502
+ const cleanConfig = structuredClone(finalConfig);
1503
+
1546
1504
  // Convert config to YAML
1547
- let yamlContent = yaml.dump(finalConfig, {
1505
+ let yamlContent = yaml.stringify(cleanConfig, {
1548
1506
  indent: 2,
1549
- lineWidth: -1,
1550
- noRefs: true,
1551
- sortKeys: false,
1507
+ lineWidth: 0,
1508
+ minContentWidth: 0,
1552
1509
  });
1553
1510
 
1554
1511
  // If we have core values, reorganize the YAML to group them with their comment
@@ -1580,7 +1537,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1580
1537
  await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
1581
1538
 
1582
1539
  // Track the config file in installedFiles
1583
- this.installedFiles.push(configPath);
1540
+ this.installedFiles.add(configPath);
1584
1541
  }
1585
1542
  }
1586
1543
  }
@@ -1593,14 +1550,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1593
1550
  async installCoreWithDependencies(bmadDir, coreFiles) {
1594
1551
  const sourcePath = getModulePath('core');
1595
1552
  const targetPath = path.join(bmadDir, 'core');
1596
-
1597
- // Install full core
1598
1553
  await this.installCore(bmadDir);
1599
-
1600
- // If there are specific dependency files, ensure they're included
1601
- if (coreFiles) {
1602
- // Already handled by installCore for core module
1603
- }
1604
1554
  }
1605
1555
 
1606
1556
  /**
@@ -1619,11 +1569,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1619
1569
  moduleName,
1620
1570
  bmadDir,
1621
1571
  (filePath) => {
1622
- this.installedFiles.push(filePath);
1572
+ this.installedFiles.add(filePath);
1623
1573
  },
1624
1574
  {
1625
1575
  skipModuleInstaller: true, // We'll run it later after IDE setup
1626
1576
  moduleConfig: moduleConfig, // Pass module config for conditional filtering
1577
+ installer: this,
1627
1578
  },
1628
1579
  );
1629
1580
 
@@ -1656,7 +1607,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1656
1607
 
1657
1608
  if (await fs.pathExists(sourcePath)) {
1658
1609
  await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
1659
- this.installedFiles.push(targetPath);
1610
+ this.installedFiles.add(targetPath);
1660
1611
  }
1661
1612
  }
1662
1613
  }
@@ -1672,7 +1623,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1672
1623
 
1673
1624
  if (await fs.pathExists(sourcePath)) {
1674
1625
  await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
1675
- this.installedFiles.push(targetPath);
1626
+ this.installedFiles.add(targetPath);
1676
1627
  }
1677
1628
  }
1678
1629
  }
@@ -1688,7 +1639,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1688
1639
 
1689
1640
  if (await fs.pathExists(sourcePath)) {
1690
1641
  await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
1691
- this.installedFiles.push(targetPath);
1642
+ this.installedFiles.add(targetPath);
1692
1643
  }
1693
1644
  }
1694
1645
  }
@@ -1704,7 +1655,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1704
1655
 
1705
1656
  if (await fs.pathExists(sourcePath)) {
1706
1657
  await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
1707
- this.installedFiles.push(targetPath);
1658
+ this.installedFiles.add(targetPath);
1708
1659
  }
1709
1660
  }
1710
1661
  }
@@ -1719,7 +1670,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1719
1670
 
1720
1671
  if (await fs.pathExists(dataPath)) {
1721
1672
  await this.copyFileWithPlaceholderReplacement(dataPath, targetPath, this.bmadFolderName || 'bmad');
1722
- this.installedFiles.push(targetPath);
1673
+ this.installedFiles.add(targetPath);
1723
1674
  }
1724
1675
  }
1725
1676
  }
@@ -1740,25 +1691,55 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1740
1691
  const sourcePath = getModulePath('core');
1741
1692
  const targetPath = path.join(bmadDir, 'core');
1742
1693
 
1743
- // Copy core files with filtering for localskip agents
1744
- await this.copyDirectoryWithFiltering(sourcePath, targetPath);
1694
+ // Copy core files (skip .agent.yaml files like modules do)
1695
+ await this.copyCoreFiles(sourcePath, targetPath);
1696
+
1697
+ // Compile agents using the same compiler as modules
1698
+ const { ModuleManager } = require('../modules/manager');
1699
+ const moduleManager = new ModuleManager();
1700
+ await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
1745
1701
 
1746
1702
  // Process agent files to inject activation block
1747
1703
  await this.processAgentFiles(targetPath, 'core');
1748
1704
  }
1749
1705
 
1750
1706
  /**
1751
- * Copy directory with filtering for localskip agents
1752
- * @param {string} sourcePath - Source directory path
1753
- * @param {string} targetPath - Target directory path
1707
+ * Copy core files (similar to copyModuleWithFiltering but for core)
1708
+ * @param {string} sourcePath - Source path
1709
+ * @param {string} targetPath - Target path
1754
1710
  */
1755
- async copyDirectoryWithFiltering(sourcePath, targetPath) {
1756
- // Get all files in source directory
1711
+ async copyCoreFiles(sourcePath, targetPath) {
1712
+ // Get all files in source
1757
1713
  const files = await this.getFileList(sourcePath);
1758
1714
 
1759
1715
  for (const file of files) {
1716
+ // Skip sub-modules directory - these are IDE-specific and handled separately
1717
+ if (file.startsWith('sub-modules/')) {
1718
+ continue;
1719
+ }
1720
+
1721
+ // Skip sidecar directories - they are handled separately during agent compilation
1722
+ if (
1723
+ path
1724
+ .dirname(file)
1725
+ .split('/')
1726
+ .some((dir) => dir.toLowerCase().includes('sidecar'))
1727
+ ) {
1728
+ continue;
1729
+ }
1730
+
1731
+ // Skip _module-installer directory - it's only needed at install time
1732
+ if (file.startsWith('_module-installer/') || file === 'module.yaml') {
1733
+ continue;
1734
+ }
1735
+
1760
1736
  // Skip config.yaml templates - we'll generate clean ones with actual values
1761
- if (file === 'config.yaml' || file.endsWith('/config.yaml')) {
1737
+ if (file === 'config.yaml' || file.endsWith('/config.yaml') || file === 'custom.yaml' || file.endsWith('/custom.yaml')) {
1738
+ continue;
1739
+ }
1740
+
1741
+ // Skip .agent.yaml files - they will be compiled separately
1742
+ if (file.endsWith('.agent.yaml')) {
1762
1743
  continue;
1763
1744
  }
1764
1745
 
@@ -1766,7 +1747,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1766
1747
  const targetFile = path.join(targetPath, file);
1767
1748
 
1768
1749
  // Check if this is an agent file
1769
- if (file.includes('agents/') && file.endsWith('.md')) {
1750
+ if (file.startsWith('agents/') && file.endsWith('.md')) {
1770
1751
  // Read the file to check for localskip
1771
1752
  const content = await fs.readFile(sourceFile, 'utf8');
1772
1753
 
@@ -1778,11 +1759,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1778
1759
  }
1779
1760
  }
1780
1761
 
1781
- // Copy the file with placeholder replacement
1782
- await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile, this.bmadFolderName || 'bmad');
1762
+ // Check if this is a workflow.yaml file
1763
+ if (file.endsWith('workflow.yaml')) {
1764
+ await fs.ensureDir(path.dirname(targetFile));
1765
+ await this.copyWorkflowYamlStripped(sourceFile, targetFile);
1766
+ } else {
1767
+ // Copy the file with placeholder replacement
1768
+ await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile, this.bmadFolderName || 'bmad');
1769
+ }
1783
1770
 
1784
1771
  // Track the installed file
1785
- this.installedFiles.push(targetFile);
1772
+ this.installedFiles.add(targetFile);
1786
1773
  }
1787
1774
  }
1788
1775
 
@@ -1829,128 +1816,39 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1829
1816
 
1830
1817
  // Determine project directory (parent of bmad/ directory)
1831
1818
  const bmadDir = path.dirname(modulePath);
1832
- const projectDir = path.dirname(bmadDir);
1833
- const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
1819
+ const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
1834
1820
 
1835
- // Ensure _cfg/agents directory exists
1821
+ // Ensure _config/agents directory exists
1836
1822
  await fs.ensureDir(cfgAgentsDir);
1837
1823
 
1838
1824
  // Get all agent files
1839
1825
  const agentFiles = await fs.readdir(agentsPath);
1840
1826
 
1841
1827
  for (const agentFile of agentFiles) {
1842
- // Handle YAML agents - build them to .md
1828
+ // Skip .agent.yaml files - they should already be compiled by compileModuleAgents
1843
1829
  if (agentFile.endsWith('.agent.yaml')) {
1844
- const agentName = agentFile.replace('.agent.yaml', '');
1845
- const yamlPath = path.join(agentsPath, agentFile);
1846
- const mdPath = path.join(agentsPath, `${agentName}.md`);
1847
- const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
1848
-
1849
- // Create customize template if it doesn't exist
1850
- if (!(await fs.pathExists(customizePath))) {
1851
- const genericTemplatePath = getSourcePath('utility', 'templates', 'agent.customize.template.yaml');
1852
- if (await fs.pathExists(genericTemplatePath)) {
1853
- await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
1854
- console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
1855
- }
1856
- }
1857
-
1858
- // Build YAML + customize to .md
1859
- const customizeExists = await fs.pathExists(customizePath);
1860
- let xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
1861
- includeMetadata: true,
1862
- });
1863
-
1864
- // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
1865
- // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
1866
-
1867
- // Replace {bmad_folder} with actual folder name
1868
- xmlContent = xmlContent.replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
1869
-
1870
- // Replace {agent_sidecar_folder} if configured
1871
- const coreConfig = this.configCollector.collectedConfig.core || {};
1872
- if (coreConfig.agent_sidecar_folder && xmlContent.includes('{agent_sidecar_folder}')) {
1873
- xmlContent = xmlContent.replaceAll('{agent_sidecar_folder}', coreConfig.agent_sidecar_folder);
1874
- }
1875
-
1876
- // Process TTS injection points (pass targetPath for tracking)
1877
- xmlContent = this.processTTSInjectionPoints(xmlContent, mdPath);
1878
-
1879
- // Check if agent has sidecar and copy it
1880
- let agentYamlContent = null;
1881
- let hasSidecar = false;
1882
-
1883
- try {
1884
- agentYamlContent = await fs.readFile(yamlPath, 'utf8');
1885
- const yamlLib = require('yaml');
1886
- const agentYaml = yamlLib.parse(agentYamlContent);
1887
- hasSidecar = agentYaml?.agent?.metadata?.hasSidecar === true;
1888
- } catch {
1889
- // Continue without sidecar processing
1890
- }
1891
-
1892
- // Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
1893
- const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
1894
- await fs.writeFile(mdPath, content, 'utf8');
1895
- this.installedFiles.push(mdPath);
1896
-
1897
- // Copy sidecar files if agent has hasSidecar flag
1898
- if (hasSidecar) {
1899
- const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
1900
-
1901
- // Get agent sidecar folder from core config
1902
- const coreConfigPath = path.join(bmadDir, 'bmb', 'config.yaml');
1903
- let agentSidecarFolder;
1904
-
1905
- if (await fs.pathExists(coreConfigPath)) {
1906
- const yamlLib = require('yaml');
1907
- const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
1908
- const coreConfig = yamlLib.parse(coreConfigContent);
1909
- agentSidecarFolder = coreConfig.agent_sidecar_folder || agentSidecarFolder;
1910
- }
1911
-
1912
- // Resolve path variables
1913
- const resolvedSidecarFolder = agentSidecarFolder
1914
- .replaceAll('{project-root}', projectDir)
1915
- .replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
1916
-
1917
- // Create sidecar directory for this agent
1918
- const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
1919
- await fs.ensureDir(agentSidecarDir);
1830
+ continue;
1831
+ }
1920
1832
 
1921
- // Find and copy sidecar folder from source module
1922
- const sourceModulePath = getSourcePath(`modules/${moduleName}`);
1923
- const sourceAgentPath = path.join(sourceModulePath, 'agents');
1833
+ // Only process .md files (already compiled from YAML)
1834
+ if (!agentFile.endsWith('.md')) {
1835
+ continue;
1836
+ }
1924
1837
 
1925
- // Copy sidecar files (preserve existing, add new)
1926
- const sidecarResult = copyAgentSidecarFiles(sourceAgentPath, agentSidecarDir, yamlPath);
1838
+ const agentName = agentFile.replace('.md', '');
1839
+ const mdPath = path.join(agentsPath, agentFile);
1840
+ const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
1927
1841
 
1928
- if (sidecarResult.copied.length > 0) {
1929
- console.log(chalk.dim(` Copied ${sidecarResult.copied.length} new sidecar file(s) to: ${agentSidecarDir}`));
1930
- }
1931
- if (sidecarResult.preserved.length > 0) {
1932
- console.log(chalk.dim(` Preserved ${sidecarResult.preserved.length} existing sidecar file(s)`));
1842
+ // For .md files that are already compiled, we don't need to do much
1843
+ // Just ensure the customize template exists
1844
+ if (!(await fs.pathExists(customizePath))) {
1845
+ const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
1846
+ if (await fs.pathExists(genericTemplatePath)) {
1847
+ await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
1848
+ if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
1849
+ console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
1933
1850
  }
1934
1851
  }
1935
-
1936
- // Remove the source YAML file - we can regenerate from installer source if needed
1937
- await fs.remove(yamlPath);
1938
-
1939
- console.log(chalk.dim(` Built agent: ${agentName}.md${hasSidecar ? ' (with sidecar)' : ''}`));
1940
- }
1941
- // Handle legacy .md agents - inject activation if needed
1942
- else if (agentFile.endsWith('.md')) {
1943
- const agentPath = path.join(agentsPath, agentFile);
1944
- let content = await fs.readFile(agentPath, 'utf8');
1945
-
1946
- // Check if content has agent XML and no activation block
1947
- if (content.includes('<agent') && !content.includes('<activation')) {
1948
- // Inject the activation block using XML handler
1949
- content = this.xmlHandler.injectActivationSimple(content);
1950
- // Ensure POSIX-compliant final newline
1951
- const finalContent = content.endsWith('\n') ? content : content + '\n';
1952
- await fs.writeFile(agentPath, finalContent, 'utf8');
1953
- }
1954
1852
  }
1955
1853
  }
1956
1854
  }
@@ -1962,7 +1860,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1962
1860
  */
1963
1861
  async buildStandaloneAgents(bmadDir, projectDir) {
1964
1862
  const standaloneAgentsPath = path.join(bmadDir, 'agents');
1965
- const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
1863
+ const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
1966
1864
 
1967
1865
  // Check if standalone agents directory exists
1968
1866
  if (!(await fs.pathExists(standaloneAgentsPath))) {
@@ -1994,8 +1892,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
1994
1892
 
1995
1893
  if (customizeExists) {
1996
1894
  const customizeContent = await fs.readFile(customizePath, 'utf8');
1997
- const yaml = require('js-yaml');
1998
- const customizeYaml = yaml.load(customizeContent);
1895
+ const yaml = require('yaml');
1896
+ const customizeYaml = yaml.parse(customizeContent);
1999
1897
 
2000
1898
  // Detect what fields are customized (similar to rebuildAgentFiles)
2001
1899
  if (customizeYaml) {
@@ -2063,7 +1961,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2063
1961
  // Determine project directory (parent of bmad/ directory)
2064
1962
  const bmadDir = path.dirname(modulePath);
2065
1963
  const projectDir = path.dirname(bmadDir);
2066
- const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
1964
+ const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
2067
1965
  const targetAgentsPath = path.join(modulePath, 'agents');
2068
1966
 
2069
1967
  // Ensure target directory exists
@@ -2085,8 +1983,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2085
1983
 
2086
1984
  if (customizeExists) {
2087
1985
  const customizeContent = await fs.readFile(customizePath, 'utf8');
2088
- const yaml = require('js-yaml');
2089
- const customizeYaml = yaml.load(customizeContent);
1986
+ const yaml = require('yaml');
1987
+ const customizeYaml = yaml.parse(customizeContent);
2090
1988
 
2091
1989
  // Detect what fields are customized
2092
1990
  if (customizeYaml) {
@@ -2119,34 +2017,59 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2119
2017
  }
2120
2018
  }
2121
2019
 
2122
- // Build YAML + customize to .md
2123
- let xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
2124
- includeMetadata: true,
2125
- });
2020
+ // Read the YAML content
2021
+ const yamlContent = await fs.readFile(sourceYamlPath, 'utf8');
2126
2022
 
2127
- // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
2128
- // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
2023
+ // Read customize content if exists
2024
+ let customizeData = {};
2025
+ if (customizeExists) {
2026
+ const customizeContent = await fs.readFile(customizePath, 'utf8');
2027
+ const yaml = require('yaml');
2028
+ customizeData = yaml.parse(customizeContent);
2029
+ }
2129
2030
 
2130
- // Replace {agent_sidecar_folder} if configured
2131
- const coreConfigPath = path.join(bmadDir, 'bmb', 'config.yaml');
2132
- let agentSidecarFolder = null;
2031
+ // Build agent answers from customize data (filter empty values)
2032
+ const answers = {};
2033
+ if (customizeData.persona) {
2034
+ Object.assign(answers, filterCustomizationData(customizeData.persona));
2035
+ }
2036
+ if (customizeData.agent?.metadata) {
2037
+ const filteredMetadata = filterCustomizationData(customizeData.agent.metadata);
2038
+ if (Object.keys(filteredMetadata).length > 0) {
2039
+ Object.assign(answers, { metadata: filteredMetadata });
2040
+ }
2041
+ }
2042
+ if (customizeData.critical_actions && customizeData.critical_actions.length > 0) {
2043
+ answers.critical_actions = customizeData.critical_actions;
2044
+ }
2045
+ if (customizeData.memories && customizeData.memories.length > 0) {
2046
+ answers.memories = customizeData.memories;
2047
+ }
2133
2048
 
2049
+ const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
2050
+ let coreConfig = {};
2134
2051
  if (await fs.pathExists(coreConfigPath)) {
2135
- const yamlLib = require('yaml');
2052
+ const yaml = require('yaml');
2136
2053
  const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
2137
- const coreConfig = yamlLib.parse(coreConfigContent);
2138
- agentSidecarFolder = coreConfig.agent_sidecar_folder;
2054
+ coreConfig = yaml.parse(coreConfigContent);
2139
2055
  }
2140
2056
 
2141
- if (agentSidecarFolder && xmlContent.includes('{agent_sidecar_folder}')) {
2142
- xmlContent = xmlContent.replaceAll('{agent_sidecar_folder}', agentSidecarFolder);
2057
+ // Compile using the same compiler as initial installation
2058
+ const { compileAgent } = require('../../../lib/agent/compiler');
2059
+ const result = await compileAgent(yamlContent, answers, agentName, path.relative(bmadDir, targetMdPath), {
2060
+ config: coreConfig,
2061
+ });
2062
+
2063
+ // Check if compilation succeeded
2064
+ if (!result || !result.xml) {
2065
+ throw new Error(`Failed to compile agent ${agentName}: No XML returned from compiler`);
2143
2066
  }
2144
2067
 
2145
- // Process TTS injection points (pass targetPath for tracking)
2146
- xmlContent = this.processTTSInjectionPoints(xmlContent, targetMdPath);
2068
+ // Replace _bmad with actual folder name if needed
2069
+ const finalXml = result.xml.replaceAll('_bmad', path.basename(bmadDir));
2147
2070
 
2148
2071
  // Write the rebuilt .md file with POSIX-compliant final newline
2149
- const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
2072
+ const content = finalXml.endsWith('\n') ? finalXml : finalXml + '\n';
2150
2073
  await fs.writeFile(targetMdPath, content, 'utf8');
2151
2074
 
2152
2075
  // Display result with customizations if any
@@ -2165,23 +2088,28 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2165
2088
  * @returns {Object} Compilation results
2166
2089
  */
2167
2090
  async compileAgents(config) {
2168
- const ora = require('ora');
2169
- const spinner = ora('Starting agent compilation...').start();
2170
-
2171
2091
  try {
2172
2092
  const projectDir = path.resolve(config.directory);
2173
- const bmadDir = await this.findBmadDir(projectDir);
2093
+ const { bmadDir } = await this.findBmadDir(projectDir);
2174
2094
 
2175
2095
  // Check if bmad directory exists
2176
2096
  if (!(await fs.pathExists(bmadDir))) {
2177
- spinner.fail('No BMAD installation found');
2178
2097
  throw new Error(`BMAD not installed at ${bmadDir}`);
2179
2098
  }
2180
2099
 
2100
+ // Get installed modules from manifest
2101
+ const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
2102
+ let installedModules = [];
2103
+ let manifest = null;
2104
+ if (await fs.pathExists(manifestPath)) {
2105
+ const manifestContent = await fs.readFile(manifestPath, 'utf8');
2106
+ const yaml = require('yaml');
2107
+ manifest = yaml.parse(manifestContent);
2108
+ installedModules = manifest.modules || [];
2109
+ }
2110
+
2181
2111
  // Check for custom modules with missing sources
2182
- const manifest = await this.manifest.read(bmadDir);
2183
2112
  if (manifest && manifest.customModules && manifest.customModules.length > 0) {
2184
- spinner.stop();
2185
2113
  console.log(chalk.yellow('\nChecking custom module sources before compilation...'));
2186
2114
 
2187
2115
  const customModuleSources = new Map();
@@ -2190,26 +2118,21 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2190
2118
  }
2191
2119
 
2192
2120
  const projectRoot = getProjectRoot();
2193
- const installedModules = manifest.modules || [];
2194
2121
  await this.handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, 'compile-agents', installedModules);
2195
-
2196
- spinner.start('Rebuilding agent files...');
2197
2122
  }
2198
2123
 
2199
2124
  let agentCount = 0;
2200
2125
  let taskCount = 0;
2201
2126
 
2202
2127
  // Process all modules in bmad directory
2203
- spinner.text = 'Rebuilding agent files...';
2204
2128
  const entries = await fs.readdir(bmadDir, { withFileTypes: true });
2205
2129
 
2206
2130
  for (const entry of entries) {
2207
- if (entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs') {
2131
+ if (entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs') {
2208
2132
  const modulePath = path.join(bmadDir, entry.name);
2209
2133
 
2210
2134
  // Special handling for standalone agents in bmad/agents/ directory
2211
2135
  if (entry.name === 'agents') {
2212
- spinner.text = 'Building standalone agents...';
2213
2136
  await this.buildStandaloneAgents(bmadDir, projectDir);
2214
2137
 
2215
2138
  // Count standalone agents
@@ -2241,60 +2164,22 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2241
2164
  }
2242
2165
  }
2243
2166
 
2244
- // Reinstall custom agents from _cfg/custom/agents/ sources
2245
- spinner.start('Rebuilding custom agents...');
2246
- const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
2247
- if (customAgentResults.count > 0) {
2248
- spinner.succeed(`Rebuilt ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`);
2249
- agentCount += customAgentResults.count;
2250
- } else {
2251
- spinner.succeed('No custom agents found to rebuild');
2252
- }
2253
-
2254
- // Skip full manifest regeneration during compileAgents to preserve custom agents
2255
- // Custom agents are already added to manifests during individual installation
2256
- // Only regenerate YAML manifest for IDE updates if needed
2257
- const existingManifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
2258
- let existingIdes = [];
2259
- if (await fs.pathExists(existingManifestPath)) {
2260
- const manifestContent = await fs.readFile(existingManifestPath, 'utf8');
2261
- const yaml = require('js-yaml');
2262
- const manifest = yaml.load(manifestContent);
2263
- existingIdes = manifest.ides || [];
2264
- }
2265
-
2266
2167
  // Update IDE configurations using the existing IDE list from manifest
2267
- if (existingIdes && existingIdes.length > 0) {
2268
- spinner.start('Updating IDE configurations...');
2269
-
2270
- for (const ide of existingIdes) {
2271
- spinner.text = `Updating ${ide}...`;
2272
-
2273
- // Stop spinner before IDE setup to prevent blocking any potential prompts
2274
- // However, we pass _alreadyConfigured to skip all prompts during compile
2275
- spinner.stop();
2276
-
2168
+ if (manifest && manifest.ides && manifest.ides.length > 0) {
2169
+ for (const ide of manifest.ides) {
2277
2170
  await this.ideManager.setup(ide, projectDir, bmadDir, {
2278
2171
  selectedModules: installedModules,
2279
2172
  skipModuleInstall: true, // Skip module installation, just update IDE files
2280
2173
  verbose: config.verbose,
2281
2174
  preCollectedConfig: { _alreadyConfigured: true }, // Skip all interactive prompts during compile
2282
2175
  });
2283
-
2284
- // Restart spinner for next IDE
2285
- if (existingIdes.indexOf(ide) < existingIdes.length - 1) {
2286
- spinner.start('Updating IDE configurations...');
2287
- }
2288
2176
  }
2289
-
2290
2177
  console.log(chalk.green('✓ IDE configurations updated'));
2291
2178
  } else {
2292
2179
  console.log(chalk.yellow('⚠️ No IDEs configured. Skipping IDE update.'));
2293
2180
  }
2294
-
2295
2181
  return { agentCount, taskCount };
2296
2182
  } catch (error) {
2297
- spinner.fail('Compilation failed');
2298
2183
  throw error;
2299
2184
  }
2300
2185
  }
@@ -2312,6 +2197,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2312
2197
  } else {
2313
2198
  // Selective update - preserve user modifications
2314
2199
  await this.fileOps.syncDirectory(sourcePath, targetPath);
2200
+
2201
+ // Recompile agents (#1133)
2202
+ const { ModuleManager } = require('../modules/manager');
2203
+ const moduleManager = new ModuleManager();
2204
+ await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
2205
+ await this.processAgentFiles(targetPath, 'core');
2315
2206
  }
2316
2207
  }
2317
2208
 
@@ -2326,7 +2217,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2326
2217
 
2327
2218
  try {
2328
2219
  const projectDir = path.resolve(config.directory);
2329
- const bmadDir = await this.findBmadDir(projectDir);
2220
+ const { bmadDir } = await this.findBmadDir(projectDir);
2330
2221
 
2331
2222
  // Check if bmad directory exists
2332
2223
  if (!(await fs.pathExists(bmadDir))) {
@@ -2342,35 +2233,35 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2342
2233
  const configuredIdes = existingInstall.ides || [];
2343
2234
  const projectRoot = path.dirname(bmadDir);
2344
2235
 
2345
- // Get custom module sources from manifest
2236
+ // Get custom module sources from cache
2346
2237
  const customModuleSources = new Map();
2347
- if (existingInstall.customModules) {
2348
- for (const customModule of existingInstall.customModules) {
2349
- // Ensure we have an absolute sourcePath
2350
- let absoluteSourcePath = customModule.sourcePath;
2238
+ const cacheDir = path.join(bmadDir, '_config', 'custom');
2239
+ if (await fs.pathExists(cacheDir)) {
2240
+ const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
2351
2241
 
2352
- // Check if sourcePath is a cache-relative path (starts with _cfg/)
2353
- if (absoluteSourcePath && absoluteSourcePath.startsWith('_cfg')) {
2354
- // Convert cache-relative path to absolute path
2355
- absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
2356
- }
2357
- // If no sourcePath but we have relativePath, convert it
2358
- else if (!absoluteSourcePath && customModule.relativePath) {
2359
- // relativePath is relative to the project root (parent of bmad dir)
2360
- absoluteSourcePath = path.resolve(projectRoot, customModule.relativePath);
2361
- }
2362
- // Ensure sourcePath is absolute for anything else
2363
- else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
2364
- absoluteSourcePath = path.resolve(absoluteSourcePath);
2365
- }
2242
+ for (const cachedModule of cachedModules) {
2243
+ if (cachedModule.isDirectory()) {
2244
+ const moduleId = cachedModule.name;
2366
2245
 
2367
- // Update the custom module object with the absolute path
2368
- const updatedModule = {
2369
- ...customModule,
2370
- sourcePath: absoluteSourcePath,
2371
- };
2246
+ // Skip if we already have this module from manifest
2247
+ if (customModuleSources.has(moduleId)) {
2248
+ continue;
2249
+ }
2372
2250
 
2373
- customModuleSources.set(customModule.id, updatedModule);
2251
+ const cachedPath = path.join(cacheDir, moduleId);
2252
+
2253
+ // Check if this is actually a custom module (has module.yaml)
2254
+ const moduleYamlPath = path.join(cachedPath, 'module.yaml');
2255
+ if (await fs.pathExists(moduleYamlPath)) {
2256
+ // For quick update, we always rebuild from cache
2257
+ customModuleSources.set(moduleId, {
2258
+ id: moduleId,
2259
+ name: moduleId, // We'll read the actual name if needed
2260
+ sourcePath: cachedPath,
2261
+ cached: true, // Flag to indicate this is from cache
2262
+ });
2263
+ }
2264
+ }
2374
2265
  }
2375
2266
  }
2376
2267
 
@@ -2402,126 +2293,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2402
2293
  }
2403
2294
  }
2404
2295
 
2405
- // Check for untracked custom modules (installed but not in manifest)
2406
- const untrackedCustomModules = [];
2407
- for (const installedModule of installedModules) {
2408
- // Skip standard modules and core
2409
- const standardModuleIds = ['bmb', 'bmgd', 'bmm', 'cis', 'core'];
2410
- if (standardModuleIds.includes(installedModule)) {
2411
- continue;
2412
- }
2413
-
2414
- // Check if this installed module is not tracked in customModules
2415
- if (!customModuleSources.has(installedModule)) {
2416
- const modulePath = path.join(bmadDir, installedModule);
2417
- if (await fs.pathExists(modulePath)) {
2418
- untrackedCustomModules.push({
2419
- id: installedModule,
2420
- name: installedModule, // We don't have the original name
2421
- path: modulePath,
2422
- untracked: true,
2423
- });
2424
- }
2425
- }
2426
- }
2427
-
2428
- // If we found untracked custom modules, offer to track them
2429
- if (untrackedCustomModules.length > 0) {
2430
- spinner.stop();
2431
- console.log(chalk.yellow(`\n⚠️ Found ${untrackedCustomModules.length} custom module(s) not tracked in manifest:`));
2432
-
2433
- for (const untracked of untrackedCustomModules) {
2434
- console.log(chalk.dim(` • ${untracked.id} (installed at ${path.relative(projectRoot, untracked.path)})`));
2435
- }
2436
-
2437
- const { trackModules } = await inquirer.prompt([
2438
- {
2439
- type: 'confirm',
2440
- name: 'trackModules',
2441
- message: chalk.cyan('Would you like to scan for their source locations?'),
2442
- default: true,
2443
- },
2444
- ]);
2445
-
2446
- if (trackModules) {
2447
- const { scanDirectory } = await inquirer.prompt([
2448
- {
2449
- type: 'input',
2450
- name: 'scanDirectory',
2451
- message: 'Enter directory to scan for custom module sources (or leave blank to skip):',
2452
- default: projectRoot,
2453
- validate: async (input) => {
2454
- if (input && input.trim() !== '') {
2455
- const expandedPath = path.resolve(input.trim());
2456
- if (!(await fs.pathExists(expandedPath))) {
2457
- return 'Directory does not exist';
2458
- }
2459
- const stats = await fs.stat(expandedPath);
2460
- if (!stats.isDirectory()) {
2461
- return 'Path must be a directory';
2462
- }
2463
- }
2464
- return true;
2465
- },
2466
- },
2467
- ]);
2468
-
2469
- if (scanDirectory && scanDirectory.trim() !== '') {
2470
- console.log(chalk.dim('\nScanning for custom module sources...'));
2471
-
2472
- // Scan for all module.yaml files
2473
- const allModulePaths = await this.moduleManager.findModulesInProject(scanDirectory);
2474
- const { ModuleManager } = require('../modules/manager');
2475
- const mm = new ModuleManager({ scanProjectForModules: true });
2476
-
2477
- for (const untracked of untrackedCustomModules) {
2478
- let foundSource = null;
2479
-
2480
- // Try to find by module ID
2481
- for (const modulePath of allModulePaths) {
2482
- try {
2483
- const moduleInfo = await mm.getModuleInfo(modulePath);
2484
- if (moduleInfo && moduleInfo.id === untracked.id) {
2485
- foundSource = {
2486
- path: modulePath,
2487
- info: moduleInfo,
2488
- };
2489
- break;
2490
- }
2491
- } catch {
2492
- // Continue searching
2493
- }
2494
- }
2495
-
2496
- if (foundSource) {
2497
- console.log(chalk.green(` ✓ Found source for ${untracked.id}: ${path.relative(projectRoot, foundSource.path)}`));
2498
-
2499
- // Add to manifest
2500
- await this.manifest.addCustomModule(bmadDir, {
2501
- id: untracked.id,
2502
- name: foundSource.info.name || untracked.name,
2503
- sourcePath: path.resolve(foundSource.path),
2504
- installDate: new Date().toISOString(),
2505
- tracked: true,
2506
- });
2507
-
2508
- // Add to customModuleSources for processing
2509
- customModuleSources.set(untracked.id, {
2510
- id: untracked.id,
2511
- name: foundSource.info.name || untracked.name,
2512
- sourcePath: path.resolve(foundSource.path),
2513
- });
2514
- } else {
2515
- console.log(chalk.yellow(` ⚠ Could not find source for ${untracked.id}`));
2516
- }
2517
- }
2518
- }
2519
- }
2520
-
2521
- console.log(chalk.dim('\nUntracked custom modules will remain installed but cannot be updated without their source.'));
2522
- spinner.start('Preparing update...');
2523
- }
2524
-
2525
2296
  // Handle missing custom module sources using shared method
2526
2297
  const customModuleResult = await this.handleMissingCustomSources(
2527
2298
  customModuleSources,
@@ -2531,18 +2302,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2531
2302
  installedModules,
2532
2303
  );
2533
2304
 
2534
- // Handle both old return format (array) and new format (object)
2535
- let validCustomModules = [];
2536
- let keptModulesWithoutSources = [];
2537
-
2538
- if (Array.isArray(customModuleResult)) {
2539
- // Old format - just an array
2540
- validCustomModules = customModuleResult;
2541
- } else if (customModuleResult && typeof customModuleResult === 'object') {
2542
- // New format - object with two arrays
2543
- validCustomModules = customModuleResult.validCustomModules || [];
2544
- keptModulesWithoutSources = customModuleResult.keptModulesWithoutSources || [];
2545
- }
2305
+ const { validCustomModules, keptModulesWithoutSources } = customModuleResult;
2546
2306
 
2547
2307
  const customModulesFromManifest = validCustomModules.map((m) => ({
2548
2308
  ...m,
@@ -2550,18 +2310,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2550
2310
  hasUpdate: true,
2551
2311
  }));
2552
2312
 
2553
- // Add untracked modules to the update list but mark them as untrackable
2554
- for (const untracked of untrackedCustomModules) {
2555
- if (!customModuleSources.has(untracked.id)) {
2556
- customModulesFromManifest.push({
2557
- ...untracked,
2558
- isCustom: true,
2559
- hasUpdate: false, // Can't update without source
2560
- untracked: true,
2561
- });
2562
- }
2563
- }
2564
-
2565
2313
  const allAvailableModules = [...availableModules, ...customModulesFromManifest];
2566
2314
  const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
2567
2315
 
@@ -2617,20 +2365,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2617
2365
  lastModified: new Date().toISOString(),
2618
2366
  };
2619
2367
 
2620
- // Check if bmad_folder has changed
2621
- const existingBmadFolderName = path.basename(bmadDir);
2622
- const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
2623
-
2624
- if (existingBmadFolderName === newBmadFolderName) {
2625
- // Normal quick update - start the spinner
2626
- console.log(chalk.cyan('Updating BMAD installation...'));
2627
- } else {
2628
- // Folder name has changed - stop spinner and let install() handle it
2629
- spinner.stop();
2630
- console.log(chalk.yellow(`\n⚠️ Folder name will change: ${existingBmadFolderName} → ${newBmadFolderName}`));
2631
- console.log(chalk.yellow('The installer will handle the folder migration.\n'));
2632
- }
2633
-
2634
2368
  // Build the config object for the installer
2635
2369
  const installConfig = {
2636
2370
  directory: projectDir,
@@ -2680,11 +2414,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2680
2414
  type: 'list',
2681
2415
  name: 'action',
2682
2416
  message: 'What would you like to do?',
2683
- choices: [
2684
- { name: 'Update existing installation', value: 'update' },
2685
- { name: 'Remove and reinstall', value: 'reinstall' },
2686
- { name: 'Cancel', value: 'cancel' },
2687
- ],
2417
+ choices: [{ name: 'Update existing installation', value: 'update' }],
2688
2418
  },
2689
2419
  ]);
2690
2420
  }
@@ -2698,14 +2428,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2698
2428
  console.log(chalk.yellow.bold('\n⚠️ Legacy BMAD v4 detected'));
2699
2429
  console.log(chalk.dim('The installer found legacy artefacts in your project.\n'));
2700
2430
 
2701
- // Separate .bmad* folders (auto-backup) from other offending paths (manual cleanup)
2431
+ // Separate _bmad* folders (auto-backup) from other offending paths (manual cleanup)
2702
2432
  const bmadFolders = legacyV4.offenders.filter((p) => {
2703
2433
  const name = path.basename(p);
2704
- return name.startsWith('.bmad'); // Only dot-prefixed folders get auto-backed up
2434
+ return name.startsWith('_bmad'); // Only dot-prefixed folders get auto-backed up
2705
2435
  });
2706
2436
  const otherOffenders = legacyV4.offenders.filter((p) => {
2707
2437
  const name = path.basename(p);
2708
- return !name.startsWith('.bmad'); // Everything else is manual cleanup
2438
+ return !name.startsWith('_bmad'); // Everything else is manual cleanup
2709
2439
  });
2710
2440
 
2711
2441
  const inquirer = require('inquirer');
@@ -2738,7 +2468,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2738
2468
  }
2739
2469
  }
2740
2470
 
2741
- // Handle .bmad* folders with automatic backup
2471
+ // Handle _bmad* folders with automatic backup
2742
2472
  if (bmadFolders.length > 0) {
2743
2473
  console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:'));
2744
2474
  for (const p of bmadFolders) console.log(chalk.dim(` - ${p}`));
@@ -2782,7 +2512,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2782
2512
  * @returns {Array} Array of file entries from files-manifest.csv
2783
2513
  */
2784
2514
  async readFilesManifest(bmadDir) {
2785
- const filesManifestPath = path.join(bmadDir, '_cfg', 'files-manifest.csv');
2515
+ const filesManifestPath = path.join(bmadDir, '_config', 'files-manifest.csv');
2786
2516
  if (!(await fs.pathExists(filesManifestPath))) {
2787
2517
  return [];
2788
2518
  }
@@ -2842,6 +2572,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2842
2572
  const customFiles = [];
2843
2573
  const modifiedFiles = [];
2844
2574
 
2575
+ // Memory is always in _bmad/_memory
2576
+ const bmadMemoryPath = '_memory';
2577
+
2845
2578
  // Check if the manifest has hashes - if not, we can't detect modifications
2846
2579
  let manifestHasHashes = false;
2847
2580
  if (existingFilesManifest && existingFilesManifest.length > 0) {
@@ -2852,14 +2585,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2852
2585
  const installedFilesMap = new Map();
2853
2586
  for (const fileEntry of existingFilesManifest) {
2854
2587
  if (fileEntry.path) {
2855
- // Paths are relative to bmadDir. Legacy manifests incorrectly prefixed 'bmad/' -
2856
- // strip it if present. This is safe because no real path inside bmadDir would
2857
- // start with 'bmad/' (you'd never have .bmad/bmad/... as an actual structure).
2858
- const relativePath = fileEntry.path.startsWith('bmad/') ? fileEntry.path.slice(5) : fileEntry.path;
2859
- const absolutePath = path.join(bmadDir, relativePath);
2588
+ const absolutePath = path.join(bmadDir, fileEntry.path);
2860
2589
  installedFilesMap.set(path.normalize(absolutePath), {
2861
2590
  hash: fileEntry.hash,
2862
- relativePath: relativePath,
2591
+ relativePath: fileEntry.path,
2863
2592
  });
2864
2593
  }
2865
2594
  }
@@ -2885,20 +2614,47 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2885
2614
  const relativePath = path.relative(bmadDir, fullPath);
2886
2615
  const fileName = path.basename(fullPath);
2887
2616
 
2888
- // Skip _cfg directory - system files
2889
- if (relativePath.startsWith('_cfg/') || relativePath.startsWith('_cfg\\')) {
2617
+ // Skip _config directory EXCEPT for modified agent customizations
2618
+ if (relativePath.startsWith('_config/') || relativePath.startsWith('_config\\')) {
2619
+ // Special handling for .customize.yaml files - only preserve if modified
2620
+ if (relativePath.includes('/agents/') && fileName.endsWith('.customize.yaml')) {
2621
+ // Check if the customization file has been modified from manifest
2622
+ const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
2623
+ if (await fs.pathExists(manifestPath)) {
2624
+ const crypto = require('node:crypto');
2625
+ const currentContent = await fs.readFile(fullPath, 'utf8');
2626
+ const currentHash = crypto.createHash('sha256').update(currentContent).digest('hex');
2627
+
2628
+ const yaml = require('yaml');
2629
+ const manifestContent = await fs.readFile(manifestPath, 'utf8');
2630
+ const manifestData = yaml.parse(manifestContent);
2631
+ const originalHash = manifestData.agentCustomizations?.[relativePath];
2632
+
2633
+ // Only add to customFiles if hash differs (user modified)
2634
+ if (originalHash && currentHash !== originalHash) {
2635
+ customFiles.push(fullPath);
2636
+ }
2637
+ }
2638
+ }
2639
+ continue;
2640
+ }
2641
+
2642
+ if (relativePath.startsWith(bmadMemoryPath + '/') && path.dirname(relativePath).includes('-sidecar')) {
2890
2643
  continue;
2891
2644
  }
2892
2645
 
2893
2646
  // Skip config.yaml files - these are regenerated on each install/update
2894
- // Users should use _cfg/agents/ override files instead
2895
2647
  if (fileName === 'config.yaml') {
2896
2648
  continue;
2897
2649
  }
2898
2650
 
2899
2651
  if (!fileInfo) {
2900
2652
  // File not in manifest = custom file
2901
- customFiles.push(fullPath);
2653
+ // EXCEPT: Agent .md files in module folders are generated files, not custom
2654
+ // Only treat .md files under _config/agents/ as custom
2655
+ if (!(fileName.endsWith('.md') && relativePath.includes('/agents/') && !relativePath.startsWith('_config/'))) {
2656
+ customFiles.push(fullPath);
2657
+ }
2902
2658
  } else if (manifestHasHashes && fileInfo.hash) {
2903
2659
  // File in manifest with hash - check if it was modified
2904
2660
  const currentHash = await this.manifest.calculateFileHash(fullPath);
@@ -2910,8 +2666,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2910
2666
  });
2911
2667
  }
2912
2668
  }
2913
- // If manifest doesn't have hashes, we can't detect modifications
2914
- // so we just skip files that are in the manifest
2915
2669
  }
2916
2670
  }
2917
2671
  } catch {
@@ -2929,7 +2683,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2929
2683
  * @param {Object} userInfo - User information including name and language
2930
2684
  */
2931
2685
  async createAgentConfigs(bmadDir, userInfo = null) {
2932
- const agentConfigDir = path.join(bmadDir, '_cfg', 'agents');
2686
+ const agentConfigDir = path.join(bmadDir, '_config', 'agents');
2933
2687
  await fs.ensureDir(agentConfigDir);
2934
2688
 
2935
2689
  // Get all agents from all modules
@@ -2939,7 +2693,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
2939
2693
  // Check modules for agents (including core)
2940
2694
  const entries = await fs.readdir(bmadDir, { withFileTypes: true });
2941
2695
  for (const entry of entries) {
2942
- if (entry.isDirectory() && entry.name !== '_cfg') {
2696
+ if (entry.isDirectory() && entry.name !== '_config') {
2943
2697
  const moduleAgentsPath = path.join(bmadDir, entry.name, 'agents');
2944
2698
  if (await fs.pathExists(moduleAgentsPath)) {
2945
2699
  const agentFiles = await fs.readdir(moduleAgentsPath);
@@ -3041,7 +2795,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
3041
2795
  }
3042
2796
 
3043
2797
  await fs.writeFile(configPath, configContent, 'utf8');
3044
- this.installedFiles.push(configPath); // Track agent config files
2798
+ this.installedFiles.add(configPath); // Track agent config files
3045
2799
  createdCount++;
3046
2800
  }
3047
2801
 
@@ -3057,7 +2811,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
3057
2811
  * @param {Array} agentDetails - Array of agent details
3058
2812
  */
3059
2813
  async generateAgentManifest(bmadDir, agentDetails) {
3060
- const manifestPath = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
2814
+ const manifestPath = path.join(bmadDir, '_config', 'agent-manifest.csv');
3061
2815
  await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
3062
2816
  }
3063
2817
 
@@ -3107,184 +2861,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
3107
2861
  return nodes;
3108
2862
  }
3109
2863
 
3110
- /**
3111
- * Reinstall custom agents from backup and source locations
3112
- * This preserves custom agents across quick updates/reinstalls
3113
- * @param {string} projectDir - Project directory
3114
- * @param {string} bmadDir - BMAD installation directory
3115
- * @returns {Object} Result with count and agent names
3116
- */
3117
- async reinstallCustomAgents(projectDir, bmadDir) {
3118
- const {
3119
- discoverAgents,
3120
- loadAgentConfig,
3121
- extractManifestData,
3122
- addToManifest,
3123
- createIdeSlashCommands,
3124
- updateManifestYaml,
3125
- } = require('../../../lib/agent/installer');
3126
- const { compileAgent } = require('../../../lib/agent/compiler');
3127
-
3128
- const results = { count: 0, agents: [] };
3129
-
3130
- // Check multiple locations for custom agents
3131
- const sourceLocations = [
3132
- path.join(bmadDir, '_cfg', 'custom', 'agents'), // Backup location
3133
- path.join(bmadDir, 'custom', 'src', 'agents'), // BMAD folder source location
3134
- path.join(projectDir, 'custom', 'src', 'agents'), // Project root source location
3135
- ];
3136
-
3137
- let foundAgents = [];
3138
- let processedAgents = new Set(); // Track to avoid duplicates
3139
-
3140
- // Discover agents from all locations
3141
- for (const location of sourceLocations) {
3142
- if (await fs.pathExists(location)) {
3143
- const agents = discoverAgents(location);
3144
- // Only add agents we haven't processed yet
3145
- const newAgents = agents.filter((agent) => !processedAgents.has(agent.name));
3146
- foundAgents.push(...newAgents);
3147
- for (const agent of newAgents) processedAgents.add(agent.name);
3148
- }
3149
- }
3150
-
3151
- if (foundAgents.length === 0) {
3152
- return results;
3153
- }
3154
-
3155
- try {
3156
- const customAgentsDir = path.join(bmadDir, 'custom', 'agents');
3157
- await fs.ensureDir(customAgentsDir);
3158
-
3159
- const manifestFile = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
3160
- const manifestYamlFile = path.join(bmadDir, '_cfg', 'manifest.yaml');
3161
-
3162
- for (const agent of foundAgents) {
3163
- try {
3164
- const agentConfig = loadAgentConfig(agent.yamlFile);
3165
- const finalAgentName = agent.name; // Already named correctly from save
3166
-
3167
- // Determine agent type from the name (e.g., "fred-commit-poet" → "commit-poet")
3168
- let agentType = finalAgentName;
3169
- const parts = finalAgentName.split('-');
3170
- if (parts.length >= 2) {
3171
- // Try to extract type (last part or last two parts)
3172
- // For "fred-commit-poet", we want "commit-poet"
3173
- // This is heuristic - could be improved with metadata storage
3174
- agentType = parts.slice(-2).join('-'); // Take last 2 parts as type
3175
- }
3176
-
3177
- // Create target directory - use relative path if agent is in a subdirectory
3178
- const agentTargetDir = agent.relativePath
3179
- ? path.join(customAgentsDir, agent.relativePath)
3180
- : path.join(customAgentsDir, finalAgentName);
3181
- await fs.ensureDir(agentTargetDir);
3182
-
3183
- // Calculate paths
3184
- const compiledFileName = `${finalAgentName}.md`;
3185
- const compiledPath = path.join(agentTargetDir, compiledFileName);
3186
- const relativePath = path.relative(projectDir, compiledPath);
3187
-
3188
- // Compile with embedded defaults (answers are already in defaults section)
3189
- const { xml, metadata } = compileAgent(
3190
- await fs.readFile(agent.yamlFile, 'utf8'),
3191
- agentConfig.defaults || {},
3192
- finalAgentName,
3193
- relativePath,
3194
- { config: config.coreConfig },
3195
- );
3196
-
3197
- // Write compiled agent
3198
- await fs.writeFile(compiledPath, xml, 'utf8');
3199
-
3200
- // Backup source YAML to _cfg/custom/agents if not already there
3201
- const cfgAgentsBackupDir = path.join(bmadDir, '_cfg', 'custom', 'agents');
3202
- await fs.ensureDir(cfgAgentsBackupDir);
3203
- const backupYamlPath = path.join(cfgAgentsBackupDir, `${finalAgentName}.agent.yaml`);
3204
-
3205
- // Only backup if source is not already in backup location
3206
- if (agent.yamlFile !== backupYamlPath) {
3207
- await fs.copy(agent.yamlFile, backupYamlPath);
3208
- }
3209
-
3210
- // Copy sidecar files for agents with hasSidecar flag
3211
- if (agentConfig.hasSidecar === true && agent.type === 'expert') {
3212
- const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
3213
-
3214
- // Get agent sidecar folder from config or use default
3215
- const agentSidecarFolder = config.coreConfig?.agent_sidecar_folder;
3216
-
3217
- // Resolve path variables
3218
- const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('{bmad_folder}', bmadDir);
3219
-
3220
- // Create sidecar directory for this agent
3221
- const agentSidecarDir = path.join(resolvedSidecarFolder, finalAgentName);
3222
- await fs.ensureDir(agentSidecarDir);
3223
-
3224
- // Copy sidecar files (preserve existing, add new)
3225
- const sidecarResult = copyAgentSidecarFiles(agent.path, agentSidecarDir, agent.yamlFile);
3226
-
3227
- if (sidecarResult.copied.length > 0 || sidecarResult.preserved.length > 0) {
3228
- console.log(chalk.dim(` Sidecar: ${sidecarResult.copied.length} new, ${sidecarResult.preserved.length} preserved`));
3229
- }
3230
- }
3231
-
3232
- // Update manifest CSV
3233
- if (await fs.pathExists(manifestFile)) {
3234
- // Preserve YAML metadata for persona name, but override id for filename
3235
- const manifestMetadata = {
3236
- ...metadata,
3237
- id: relativePath, // Use the compiled agent path for id
3238
- name: metadata.name || finalAgentName, // Use YAML metadata.name (persona name) or fallback
3239
- title: metadata.title, // Use YAML title
3240
- icon: metadata.icon, // Use YAML icon
3241
- };
3242
- const manifestData = extractManifestData(xml, manifestMetadata, relativePath, 'custom');
3243
- manifestData.name = finalAgentName; // Use filename for the name field
3244
- manifestData.path = relativePath;
3245
- addToManifest(manifestFile, manifestData);
3246
- }
3247
-
3248
- // Create IDE slash commands (async function)
3249
- await createIdeSlashCommands(projectDir, finalAgentName, relativePath, metadata);
3250
-
3251
- // Update manifest.yaml
3252
- if (await fs.pathExists(manifestYamlFile)) {
3253
- updateManifestYaml(manifestYamlFile, finalAgentName, agentType);
3254
- }
3255
-
3256
- results.count++;
3257
- results.agents.push(finalAgentName);
3258
- } catch (agentError) {
3259
- console.log(chalk.yellow(` ⚠️ Failed to reinstall ${agent.name}: ${agentError.message}`));
3260
- }
3261
- }
3262
- } catch (error) {
3263
- console.log(chalk.yellow(` ⚠️ Error reinstalling custom agents: ${error.message}`));
3264
- }
3265
-
3266
- return results;
3267
- }
3268
-
3269
- /**
3270
- * Copy IDE-specific documentation to BMAD docs
3271
- * @param {Array} ides - List of selected IDEs
3272
- * @param {string} bmadDir - BMAD installation directory
3273
- */
3274
- async copyIdeDocumentation(ides, bmadDir) {
3275
- const docsDir = path.join(bmadDir, 'docs');
3276
- await fs.ensureDir(docsDir);
3277
-
3278
- for (const ide of ides) {
3279
- const sourceDocPath = path.join(getProjectRoot(), 'docs', 'ide-info', `${ide}.md`);
3280
- const targetDocPath = path.join(docsDir, `${ide}-instructions.md`);
3281
-
3282
- if (await fs.pathExists(sourceDocPath)) {
3283
- await this.copyFileWithPlaceholderReplacement(sourceDocPath, targetDocPath, this.bmadFolderName || 'bmad');
3284
- }
3285
- }
3286
- }
3287
-
3288
2864
  /**
3289
2865
  * Handle missing custom module sources interactively
3290
2866
  * @param {Map} customModuleSources - Map of custom module ID to info
@@ -3309,19 +2885,32 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
3309
2885
  info: customInfo,
3310
2886
  });
3311
2887
  } else {
3312
- customModulesWithMissingSources.push({
3313
- id: moduleId,
3314
- name: customInfo.name,
3315
- sourcePath: customInfo.sourcePath,
3316
- relativePath: customInfo.relativePath,
3317
- info: customInfo,
3318
- });
2888
+ // For cached modules that are missing, we just skip them without prompting
2889
+ if (customInfo.cached) {
2890
+ // Skip cached modules without prompting
2891
+ keptModulesWithoutSources.push({
2892
+ id: moduleId,
2893
+ name: customInfo.name,
2894
+ cached: true,
2895
+ });
2896
+ } else {
2897
+ customModulesWithMissingSources.push({
2898
+ id: moduleId,
2899
+ name: customInfo.name,
2900
+ sourcePath: customInfo.sourcePath,
2901
+ relativePath: customInfo.relativePath,
2902
+ info: customInfo,
2903
+ });
2904
+ }
3319
2905
  }
3320
2906
  }
3321
2907
 
3322
2908
  // If no missing sources, return immediately
3323
2909
  if (customModulesWithMissingSources.length === 0) {
3324
- return validCustomModules;
2910
+ return {
2911
+ validCustomModules,
2912
+ keptModulesWithoutSources: [],
2913
+ };
3325
2914
  }
3326
2915
 
3327
2916
  // Stop any spinner for interactive prompts