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