bmad-stella 1.0.0

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 (398) hide show
  1. package/.github/FORK_GUIDE.md +106 -0
  2. package/.github/FUNDING.yaml +15 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  5. package/.github/workflows/discord.yaml +26 -0
  6. package/.github/workflows/format-check.yaml +44 -0
  7. package/.github/workflows/manual-release.yaml +174 -0
  8. package/.github/workflows/pr-validation.yaml +55 -0
  9. package/.husky/pre-commit +3 -0
  10. package/.vscode/settings.json +69 -0
  11. package/CHANGELOG.md +686 -0
  12. package/CONTRIBUTING.md +243 -0
  13. package/LICENSE +26 -0
  14. package/PR-opencode-agents-generator.md +40 -0
  15. package/README.md +159 -0
  16. package/REVIEWER-AGENT-SIMPLE.md +66 -0
  17. package/bmad-core/agent-teams/team-all.yaml +15 -0
  18. package/bmad-core/agent-teams/team-fullstack.yaml +19 -0
  19. package/bmad-core/agent-teams/team-ide-minimal.yaml +11 -0
  20. package/bmad-core/agent-teams/team-no-ui.yaml +14 -0
  21. package/bmad-core/agents/analyst.md +84 -0
  22. package/bmad-core/agents/architect.md +85 -0
  23. package/bmad-core/agents/bmad-master.md +110 -0
  24. package/bmad-core/agents/bmad-orchestrator.md +147 -0
  25. package/bmad-core/agents/dev.md +96 -0
  26. package/bmad-core/agents/planner.md +86 -0
  27. package/bmad-core/agents/pm.md +84 -0
  28. package/bmad-core/agents/po.md +79 -0
  29. package/bmad-core/agents/qa.md +80 -0
  30. package/bmad-core/agents/reviewer.md +61 -0
  31. package/bmad-core/agents/sm.md +65 -0
  32. package/bmad-core/agents/ux-expert.md +69 -0
  33. package/bmad-core/checklists/architect-checklist.md +440 -0
  34. package/bmad-core/checklists/change-checklist.md +184 -0
  35. package/bmad-core/checklists/planner-validation-checklist.md +167 -0
  36. package/bmad-core/checklists/pm-checklist.md +372 -0
  37. package/bmad-core/checklists/po-master-checklist.md +434 -0
  38. package/bmad-core/checklists/story-dod-checklist.md +96 -0
  39. package/bmad-core/checklists/story-draft-checklist.md +155 -0
  40. package/bmad-core/checklists/task-dod-checklist.md +110 -0
  41. package/bmad-core/core-config.yaml +26 -0
  42. package/bmad-core/data/bmad-kb.md +809 -0
  43. package/bmad-core/data/brainstorming-techniques.md +38 -0
  44. package/bmad-core/data/elicitation-methods.md +156 -0
  45. package/bmad-core/data/technical-preferences.md +5 -0
  46. package/bmad-core/data/test-levels-framework.md +148 -0
  47. package/bmad-core/data/test-priorities-matrix.md +174 -0
  48. package/bmad-core/tasks/advanced-elicitation.md +119 -0
  49. package/bmad-core/tasks/apply-qa-fixes.md +150 -0
  50. package/bmad-core/tasks/brownfield-create-epic.md +162 -0
  51. package/bmad-core/tasks/brownfield-create-story.md +149 -0
  52. package/bmad-core/tasks/correct-course.md +72 -0
  53. package/bmad-core/tasks/create-brownfield-story.md +314 -0
  54. package/bmad-core/tasks/create-deep-research-prompt.md +280 -0
  55. package/bmad-core/tasks/create-implementation-plan.md +298 -0
  56. package/bmad-core/tasks/create-next-story.md +114 -0
  57. package/bmad-core/tasks/decompose-task.md +162 -0
  58. package/bmad-core/tasks/document-project.md +345 -0
  59. package/bmad-core/tasks/facilitate-brainstorming-session.md +138 -0
  60. package/bmad-core/tasks/generate-ai-frontend-prompt.md +53 -0
  61. package/bmad-core/tasks/identify-dependencies.md +347 -0
  62. package/bmad-core/tasks/implement-test.md +188 -0
  63. package/bmad-core/tasks/index-docs.md +175 -0
  64. package/bmad-core/tasks/kb-mode-interaction.md +77 -0
  65. package/bmad-core/tasks/nfr-assess.md +345 -0
  66. package/bmad-core/tasks/qa-gate.md +163 -0
  67. package/bmad-core/tasks/review-and-improve.md +95 -0
  68. package/bmad-core/tasks/review-story.md +316 -0
  69. package/bmad-core/tasks/risk-profile.md +355 -0
  70. package/bmad-core/tasks/shard-doc.md +187 -0
  71. package/bmad-core/tasks/test-design.md +176 -0
  72. package/bmad-core/tasks/trace-requirements.md +266 -0
  73. package/bmad-core/tasks/validate-next-story.md +136 -0
  74. package/bmad-core/templates/architecture-tmpl.yaml +651 -0
  75. package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -0
  76. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +477 -0
  77. package/bmad-core/templates/brownfield-prd-tmpl.yaml +281 -0
  78. package/bmad-core/templates/competitor-analysis-tmpl.yaml +307 -0
  79. package/bmad-core/templates/front-end-architecture-tmpl.yaml +219 -0
  80. package/bmad-core/templates/front-end-spec-tmpl.yaml +350 -0
  81. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +824 -0
  82. package/bmad-core/templates/implementation-plan-tmpl.yaml +224 -0
  83. package/bmad-core/templates/market-research-tmpl.yaml +253 -0
  84. package/bmad-core/templates/prd-tmpl.yaml +203 -0
  85. package/bmad-core/templates/project-brief-tmpl.yaml +222 -0
  86. package/bmad-core/templates/qa-gate-tmpl.yaml +103 -0
  87. package/bmad-core/templates/story-tmpl.yaml +138 -0
  88. package/bmad-core/workflows/brownfield-fullstack.yaml +298 -0
  89. package/bmad-core/workflows/brownfield-service.yaml +188 -0
  90. package/bmad-core/workflows/brownfield-ui.yaml +198 -0
  91. package/bmad-core/workflows/greenfield-fullstack.yaml +241 -0
  92. package/bmad-core/workflows/greenfield-service.yaml +207 -0
  93. package/bmad-core/workflows/greenfield-ui.yaml +236 -0
  94. package/common/tasks/create-doc.md +103 -0
  95. package/common/tasks/execute-checklist.md +88 -0
  96. package/common/utils/bmad-doc-template.md +327 -0
  97. package/common/utils/workflow-management.md +71 -0
  98. package/dist/agents/analyst.txt +2907 -0
  99. package/dist/agents/architect.txt +3567 -0
  100. package/dist/agents/bmad-master.txt +8828 -0
  101. package/dist/agents/bmad-orchestrator.txt +1520 -0
  102. package/dist/agents/dev.txt +576 -0
  103. package/dist/agents/pm.txt +2226 -0
  104. package/dist/agents/po.txt +1359 -0
  105. package/dist/agents/qa.txt +2001 -0
  106. package/dist/agents/sm.txt +667 -0
  107. package/dist/agents/ux-expert.txt +703 -0
  108. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +2386 -0
  109. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +1627 -0
  110. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +822 -0
  111. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +11008 -0
  112. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +4031 -0
  113. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +3717 -0
  114. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +456 -0
  115. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +982 -0
  116. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +15450 -0
  117. package/dist/expansion-packs/bmad-creative-writing/agents/beta-reader.txt +921 -0
  118. package/dist/expansion-packs/bmad-creative-writing/agents/book-critic.txt +81 -0
  119. package/dist/expansion-packs/bmad-creative-writing/agents/character-psychologist.txt +886 -0
  120. package/dist/expansion-packs/bmad-creative-writing/agents/cover-designer.txt +85 -0
  121. package/dist/expansion-packs/bmad-creative-writing/agents/dialog-specialist.txt +903 -0
  122. package/dist/expansion-packs/bmad-creative-writing/agents/editor.txt +837 -0
  123. package/dist/expansion-packs/bmad-creative-writing/agents/genre-specialist.txt +989 -0
  124. package/dist/expansion-packs/bmad-creative-writing/agents/narrative-designer.txt +888 -0
  125. package/dist/expansion-packs/bmad-creative-writing/agents/plot-architect.txt +1173 -0
  126. package/dist/expansion-packs/bmad-creative-writing/agents/world-builder.txt +914 -0
  127. package/dist/expansion-packs/bmad-creative-writing/teams/agent-team.txt +6511 -0
  128. package/dist/expansion-packs/bmad-godot-game-dev/agents/bmad-orchestrator.txt +1513 -0
  129. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-analyst.txt +3190 -0
  130. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-architect.txt +4499 -0
  131. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-designer.txt +3925 -0
  132. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-developer.txt +666 -0
  133. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-pm.txt +2381 -0
  134. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-po.txt +1612 -0
  135. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-qa.txt +1741 -0
  136. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-sm.txt +1208 -0
  137. package/dist/expansion-packs/bmad-godot-game-dev/agents/game-ux-expert.txt +958 -0
  138. package/dist/expansion-packs/bmad-godot-game-dev/teams/godot-game-team.txt +27717 -0
  139. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2087 -0
  140. package/dist/teams/team-all.txt +12911 -0
  141. package/dist/teams/team-fullstack.txt +10477 -0
  142. package/dist/teams/team-ide-minimal.txt +5299 -0
  143. package/dist/teams/team-no-ui.txt +9029 -0
  144. package/docs/GUIDING-PRINCIPLES.md +91 -0
  145. package/docs/core-architecture.md +219 -0
  146. package/docs/enhanced-ide-development-workflow.md +248 -0
  147. package/docs/expansion-packs.md +200 -0
  148. package/docs/flattener.md +91 -0
  149. package/docs/how-to-contribute-with-pull-requests.md +158 -0
  150. package/docs/stella-user-guide.md +637 -0
  151. package/docs/user-guide.md +577 -0
  152. package/docs/versioning-and-releases.md +155 -0
  153. package/docs/versions.md +48 -0
  154. package/docs/working-in-the-brownfield.md +606 -0
  155. package/eslint.config.mjs +119 -0
  156. package/expansion-packs/README.md +3 -0
  157. package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +14 -0
  158. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +73 -0
  159. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +80 -0
  160. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +66 -0
  161. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +203 -0
  162. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +162 -0
  163. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +9 -0
  164. package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +252 -0
  165. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +649 -0
  166. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +112 -0
  167. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +218 -0
  168. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +292 -0
  169. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +614 -0
  170. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +357 -0
  171. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +344 -0
  172. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +254 -0
  173. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +485 -0
  174. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +184 -0
  175. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +176 -0
  176. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +15 -0
  177. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +82 -0
  178. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +79 -0
  179. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +80 -0
  180. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +67 -0
  181. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +393 -0
  182. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +205 -0
  183. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +203 -0
  184. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +126 -0
  185. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +7 -0
  186. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +771 -0
  187. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +588 -0
  188. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +112 -0
  189. package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +143 -0
  190. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +186 -0
  191. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +292 -0
  192. package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +202 -0
  193. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +1031 -0
  194. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +357 -0
  195. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +706 -0
  196. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +257 -0
  197. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +485 -0
  198. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +184 -0
  199. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +176 -0
  200. package/expansion-packs/bmad-creative-writing/README.md +146 -0
  201. package/expansion-packs/bmad-creative-writing/agent-teams/agent-team.yaml +20 -0
  202. package/expansion-packs/bmad-creative-writing/agents/beta-reader.md +94 -0
  203. package/expansion-packs/bmad-creative-writing/agents/book-critic.md +40 -0
  204. package/expansion-packs/bmad-creative-writing/agents/character-psychologist.md +93 -0
  205. package/expansion-packs/bmad-creative-writing/agents/cover-designer.md +46 -0
  206. package/expansion-packs/bmad-creative-writing/agents/dialog-specialist.md +92 -0
  207. package/expansion-packs/bmad-creative-writing/agents/editor.md +93 -0
  208. package/expansion-packs/bmad-creative-writing/agents/genre-specialist.md +95 -0
  209. package/expansion-packs/bmad-creative-writing/agents/narrative-designer.md +93 -0
  210. package/expansion-packs/bmad-creative-writing/agents/plot-architect.md +95 -0
  211. package/expansion-packs/bmad-creative-writing/agents/world-builder.md +94 -0
  212. package/expansion-packs/bmad-creative-writing/checklists/beta-feedback-closure-checklist.md +23 -0
  213. package/expansion-packs/bmad-creative-writing/checklists/character-consistency-checklist.md +23 -0
  214. package/expansion-packs/bmad-creative-writing/checklists/comedic-timing-checklist.md +23 -0
  215. package/expansion-packs/bmad-creative-writing/checklists/cyberpunk-aesthetic-checklist.md +23 -0
  216. package/expansion-packs/bmad-creative-writing/checklists/ebook-formatting-checklist.md +21 -0
  217. package/expansion-packs/bmad-creative-writing/checklists/epic-poetry-meter-checklist.md +23 -0
  218. package/expansion-packs/bmad-creative-writing/checklists/fantasy-magic-system-checklist.md +23 -0
  219. package/expansion-packs/bmad-creative-writing/checklists/foreshadowing-payoff-checklist.md +22 -0
  220. package/expansion-packs/bmad-creative-writing/checklists/genre-tropes-checklist.md +22 -0
  221. package/expansion-packs/bmad-creative-writing/checklists/historical-accuracy-checklist.md +23 -0
  222. package/expansion-packs/bmad-creative-writing/checklists/horror-suspense-checklist.md +23 -0
  223. package/expansion-packs/bmad-creative-writing/checklists/kdp-cover-ready-checklist.md +25 -0
  224. package/expansion-packs/bmad-creative-writing/checklists/line-edit-quality-checklist.md +23 -0
  225. package/expansion-packs/bmad-creative-writing/checklists/marketing-copy-checklist.md +23 -0
  226. package/expansion-packs/bmad-creative-writing/checklists/mystery-clue-trail-checklist.md +23 -0
  227. package/expansion-packs/bmad-creative-writing/checklists/orbital-mechanics-checklist.md +23 -0
  228. package/expansion-packs/bmad-creative-writing/checklists/plot-structure-checklist.md +59 -0
  229. package/expansion-packs/bmad-creative-writing/checklists/publication-readiness-checklist.md +23 -0
  230. package/expansion-packs/bmad-creative-writing/checklists/romance-emotional-beats-checklist.md +23 -0
  231. package/expansion-packs/bmad-creative-writing/checklists/scene-quality-checklist.md +23 -0
  232. package/expansion-packs/bmad-creative-writing/checklists/scifi-technology-plausibility-checklist.md +22 -0
  233. package/expansion-packs/bmad-creative-writing/checklists/sensitivity-representation-checklist.md +23 -0
  234. package/expansion-packs/bmad-creative-writing/checklists/steampunk-gadget-checklist.md +23 -0
  235. package/expansion-packs/bmad-creative-writing/checklists/thriller-pacing-stakes-checklist.md +23 -0
  236. package/expansion-packs/bmad-creative-writing/checklists/timeline-continuity-checklist.md +23 -0
  237. package/expansion-packs/bmad-creative-writing/checklists/world-building-continuity-checklist.md +23 -0
  238. package/expansion-packs/bmad-creative-writing/checklists/ya-appropriateness-checklist.md +23 -0
  239. package/expansion-packs/bmad-creative-writing/config.yaml +12 -0
  240. package/expansion-packs/bmad-creative-writing/data/bmad-kb.md +209 -0
  241. package/expansion-packs/bmad-creative-writing/data/story-structures.md +67 -0
  242. package/expansion-packs/bmad-creative-writing/docs/brief.md +212 -0
  243. package/expansion-packs/bmad-creative-writing/tasks/advanced-elicitation.md +119 -0
  244. package/expansion-packs/bmad-creative-writing/tasks/analyze-reader-feedback.md +23 -0
  245. package/expansion-packs/bmad-creative-writing/tasks/analyze-story-structure.md +67 -0
  246. package/expansion-packs/bmad-creative-writing/tasks/assemble-kdp-package.md +29 -0
  247. package/expansion-packs/bmad-creative-writing/tasks/brainstorm-premise.md +23 -0
  248. package/expansion-packs/bmad-creative-writing/tasks/build-world.md +24 -0
  249. package/expansion-packs/bmad-creative-writing/tasks/character-depth-pass.md +22 -0
  250. package/expansion-packs/bmad-creative-writing/tasks/create-doc.md +103 -0
  251. package/expansion-packs/bmad-creative-writing/tasks/create-draft-section.md +26 -0
  252. package/expansion-packs/bmad-creative-writing/tasks/critical-review.md +26 -0
  253. package/expansion-packs/bmad-creative-writing/tasks/develop-character.md +24 -0
  254. package/expansion-packs/bmad-creative-writing/tasks/execute-checklist.md +88 -0
  255. package/expansion-packs/bmad-creative-writing/tasks/expand-premise.md +23 -0
  256. package/expansion-packs/bmad-creative-writing/tasks/expand-synopsis.md +23 -0
  257. package/expansion-packs/bmad-creative-writing/tasks/final-polish.md +23 -0
  258. package/expansion-packs/bmad-creative-writing/tasks/generate-cover-brief.md +25 -0
  259. package/expansion-packs/bmad-creative-writing/tasks/generate-cover-prompts.md +26 -0
  260. package/expansion-packs/bmad-creative-writing/tasks/generate-scene-list.md +23 -0
  261. package/expansion-packs/bmad-creative-writing/tasks/incorporate-feedback.md +25 -0
  262. package/expansion-packs/bmad-creative-writing/tasks/outline-scenes.md +23 -0
  263. package/expansion-packs/bmad-creative-writing/tasks/provide-feedback.md +24 -0
  264. package/expansion-packs/bmad-creative-writing/tasks/publish-chapter.md +23 -0
  265. package/expansion-packs/bmad-creative-writing/tasks/quick-feedback.md +22 -0
  266. package/expansion-packs/bmad-creative-writing/tasks/select-next-arc.md +23 -0
  267. package/expansion-packs/bmad-creative-writing/tasks/workshop-dialog.md +64 -0
  268. package/expansion-packs/bmad-creative-writing/templates/beta-feedback-form.yaml +97 -0
  269. package/expansion-packs/bmad-creative-writing/templates/chapter-draft-tmpl.yaml +82 -0
  270. package/expansion-packs/bmad-creative-writing/templates/character-profile-tmpl.yaml +92 -0
  271. package/expansion-packs/bmad-creative-writing/templates/cover-design-brief-tmpl.yaml +98 -0
  272. package/expansion-packs/bmad-creative-writing/templates/premise-brief-tmpl.yaml +78 -0
  273. package/expansion-packs/bmad-creative-writing/templates/scene-list-tmpl.yaml +55 -0
  274. package/expansion-packs/bmad-creative-writing/templates/story-outline-tmpl.yaml +96 -0
  275. package/expansion-packs/bmad-creative-writing/templates/world-guide-tmpl.yaml +89 -0
  276. package/expansion-packs/bmad-creative-writing/workflows/book-cover-design-workflow.md +218 -0
  277. package/expansion-packs/bmad-creative-writing/workflows/novel-greenfield-workflow.yaml +56 -0
  278. package/expansion-packs/bmad-creative-writing/workflows/novel-serial-workflow.yaml +50 -0
  279. package/expansion-packs/bmad-creative-writing/workflows/novel-snowflake-workflow.yaml +69 -0
  280. package/expansion-packs/bmad-creative-writing/workflows/novel-writing.yaml +91 -0
  281. package/expansion-packs/bmad-creative-writing/workflows/screenplay-development.yaml +85 -0
  282. package/expansion-packs/bmad-creative-writing/workflows/series-planning.yaml +78 -0
  283. package/expansion-packs/bmad-creative-writing/workflows/short-story-creation.yaml +64 -0
  284. package/expansion-packs/bmad-godot-game-dev/README.md +244 -0
  285. package/expansion-packs/bmad-godot-game-dev/agent-teams/godot-game-team.yaml +18 -0
  286. package/expansion-packs/bmad-godot-game-dev/agents/bmad-orchestrator.md +147 -0
  287. package/expansion-packs/bmad-godot-game-dev/agents/game-analyst.md +84 -0
  288. package/expansion-packs/bmad-godot-game-dev/agents/game-architect.md +146 -0
  289. package/expansion-packs/bmad-godot-game-dev/agents/game-designer.md +78 -0
  290. package/expansion-packs/bmad-godot-game-dev/agents/game-developer.md +124 -0
  291. package/expansion-packs/bmad-godot-game-dev/agents/game-pm.md +82 -0
  292. package/expansion-packs/bmad-godot-game-dev/agents/game-po.md +115 -0
  293. package/expansion-packs/bmad-godot-game-dev/agents/game-qa.md +159 -0
  294. package/expansion-packs/bmad-godot-game-dev/agents/game-sm.md +66 -0
  295. package/expansion-packs/bmad-godot-game-dev/agents/game-ux-expert.md +75 -0
  296. package/expansion-packs/bmad-godot-game-dev/checklists/game-architect-checklist.md +377 -0
  297. package/expansion-packs/bmad-godot-game-dev/checklists/game-change-checklist.md +250 -0
  298. package/expansion-packs/bmad-godot-game-dev/checklists/game-design-checklist.md +225 -0
  299. package/expansion-packs/bmad-godot-game-dev/checklists/game-po-checklist.md +448 -0
  300. package/expansion-packs/bmad-godot-game-dev/checklists/game-story-dod-checklist.md +202 -0
  301. package/expansion-packs/bmad-godot-game-dev/config.yaml +30 -0
  302. package/expansion-packs/bmad-godot-game-dev/data/bmad-kb.md +811 -0
  303. package/expansion-packs/bmad-godot-game-dev/data/brainstorming-techniques.md +36 -0
  304. package/expansion-packs/bmad-godot-game-dev/data/development-guidelines.md +893 -0
  305. package/expansion-packs/bmad-godot-game-dev/data/elicitation-methods.md +156 -0
  306. package/expansion-packs/bmad-godot-game-dev/data/technical-preferences.md +3 -0
  307. package/expansion-packs/bmad-godot-game-dev/tasks/advanced-elicitation.md +110 -0
  308. package/expansion-packs/bmad-godot-game-dev/tasks/apply-qa-fixes.md +224 -0
  309. package/expansion-packs/bmad-godot-game-dev/tasks/brownfield-create-epic.md +162 -0
  310. package/expansion-packs/bmad-godot-game-dev/tasks/brownfield-create-story.md +149 -0
  311. package/expansion-packs/bmad-godot-game-dev/tasks/correct-course-game.md +159 -0
  312. package/expansion-packs/bmad-godot-game-dev/tasks/create-deep-research-prompt.md +278 -0
  313. package/expansion-packs/bmad-godot-game-dev/tasks/create-doc.md +103 -0
  314. package/expansion-packs/bmad-godot-game-dev/tasks/create-game-story.md +202 -0
  315. package/expansion-packs/bmad-godot-game-dev/tasks/document-project.md +343 -0
  316. package/expansion-packs/bmad-godot-game-dev/tasks/execute-checklist.md +88 -0
  317. package/expansion-packs/bmad-godot-game-dev/tasks/facilitate-brainstorming-session.md +136 -0
  318. package/expansion-packs/bmad-godot-game-dev/tasks/game-brownfield-create-epic.md +160 -0
  319. package/expansion-packs/bmad-godot-game-dev/tasks/game-brownfield-create-story.md +147 -0
  320. package/expansion-packs/bmad-godot-game-dev/tasks/game-design-brainstorming.md +290 -0
  321. package/expansion-packs/bmad-godot-game-dev/tasks/game-risk-profile.md +368 -0
  322. package/expansion-packs/bmad-godot-game-dev/tasks/game-test-design.md +219 -0
  323. package/expansion-packs/bmad-godot-game-dev/tasks/generate-ai-frontend-prompt.md +51 -0
  324. package/expansion-packs/bmad-godot-game-dev/tasks/kb-mode-interaction.md +77 -0
  325. package/expansion-packs/bmad-godot-game-dev/tasks/review-game-story.md +364 -0
  326. package/expansion-packs/bmad-godot-game-dev/tasks/shard-doc.md +187 -0
  327. package/expansion-packs/bmad-godot-game-dev/tasks/validate-game-story.md +208 -0
  328. package/expansion-packs/bmad-godot-game-dev/templates/brainstorming-output-tmpl.yaml +156 -0
  329. package/expansion-packs/bmad-godot-game-dev/templates/brownfield-prd-tmpl.yaml +281 -0
  330. package/expansion-packs/bmad-godot-game-dev/templates/competitor-analysis-tmpl.yaml +306 -0
  331. package/expansion-packs/bmad-godot-game-dev/templates/game-architecture-tmpl.yaml +1111 -0
  332. package/expansion-packs/bmad-godot-game-dev/templates/game-brief-tmpl.yaml +356 -0
  333. package/expansion-packs/bmad-godot-game-dev/templates/game-design-doc-tmpl.yaml +724 -0
  334. package/expansion-packs/bmad-godot-game-dev/templates/game-prd-tmpl.yaml +209 -0
  335. package/expansion-packs/bmad-godot-game-dev/templates/game-qa-gate-tmpl.yaml +186 -0
  336. package/expansion-packs/bmad-godot-game-dev/templates/game-story-tmpl.yaml +406 -0
  337. package/expansion-packs/bmad-godot-game-dev/templates/game-ui-spec-tmpl.yaml +601 -0
  338. package/expansion-packs/bmad-godot-game-dev/templates/level-design-doc-tmpl.yaml +620 -0
  339. package/expansion-packs/bmad-godot-game-dev/templates/market-research-tmpl.yaml +418 -0
  340. package/expansion-packs/bmad-godot-game-dev/utils/bmad-doc-template.md +327 -0
  341. package/expansion-packs/bmad-godot-game-dev/utils/workflow-management.md +71 -0
  342. package/expansion-packs/bmad-godot-game-dev/workflows/game-dev-greenfield.yaml +245 -0
  343. package/expansion-packs/bmad-godot-game-dev/workflows/game-prototype.yaml +213 -0
  344. package/expansion-packs/bmad-infrastructure-devops/README.md +147 -0
  345. package/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.md +73 -0
  346. package/expansion-packs/bmad-infrastructure-devops/checklists/infrastructure-checklist.md +486 -0
  347. package/expansion-packs/bmad-infrastructure-devops/config.yaml +10 -0
  348. package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +307 -0
  349. package/expansion-packs/bmad-infrastructure-devops/tasks/review-infrastructure.md +161 -0
  350. package/expansion-packs/bmad-infrastructure-devops/tasks/validate-infrastructure.md +155 -0
  351. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +425 -0
  352. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +630 -0
  353. package/package.json +116 -0
  354. package/prettier.config.mjs +32 -0
  355. package/tools/bmad-npx-wrapper.js +39 -0
  356. package/tools/builders/web-builder.js +675 -0
  357. package/tools/bump-all-versions.js +115 -0
  358. package/tools/bump-expansion-version.js +90 -0
  359. package/tools/cli.js +152 -0
  360. package/tools/flattener/aggregate.js +76 -0
  361. package/tools/flattener/binary.js +80 -0
  362. package/tools/flattener/discovery.js +71 -0
  363. package/tools/flattener/files.js +35 -0
  364. package/tools/flattener/ignoreRules.js +178 -0
  365. package/tools/flattener/main.js +568 -0
  366. package/tools/flattener/projectRoot.js +206 -0
  367. package/tools/flattener/prompts.js +44 -0
  368. package/tools/flattener/stats.helpers.js +395 -0
  369. package/tools/flattener/stats.js +80 -0
  370. package/tools/flattener/test-matrix.js +413 -0
  371. package/tools/flattener/xml.js +88 -0
  372. package/tools/implement-fork-friendly-ci.sh +229 -0
  373. package/tools/installer/README.md +8 -0
  374. package/tools/installer/bin/bmad.js +633 -0
  375. package/tools/installer/config/ide-agent-config.yaml +58 -0
  376. package/tools/installer/config/install.config.yaml +184 -0
  377. package/tools/installer/lib/config-loader.js +257 -0
  378. package/tools/installer/lib/dependency-manager.js +383 -0
  379. package/tools/installer/lib/file-manager.js +394 -0
  380. package/tools/installer/lib/ide-base-setup.js +228 -0
  381. package/tools/installer/lib/ide-setup.js +2461 -0
  382. package/tools/installer/lib/installer.js +2094 -0
  383. package/tools/installer/lib/memory-profiler.js +225 -0
  384. package/tools/installer/lib/module-manager.js +114 -0
  385. package/tools/installer/lib/resource-locator.js +308 -0
  386. package/tools/installer/package.json +47 -0
  387. package/tools/lib/dependency-resolver.js +175 -0
  388. package/tools/lib/yaml-utils.js +29 -0
  389. package/tools/md-assets/web-agent-startup-instructions.md +39 -0
  390. package/tools/preview-release-notes.js +66 -0
  391. package/tools/setup-hooks.sh +37 -0
  392. package/tools/shared/bannerArt.js +105 -0
  393. package/tools/sync-installer-version.js +32 -0
  394. package/tools/sync-version.sh +23 -0
  395. package/tools/update-expansion-version.js +53 -0
  396. package/tools/upgraders/v3-to-v4-upgrader.js +673 -0
  397. package/tools/version-bump.js +94 -0
  398. package/tools/yaml-format.js +253 -0
@@ -0,0 +1,2461 @@
1
+ const path = require('node:path');
2
+ const fs = require('fs-extra');
3
+ const yaml = require('js-yaml');
4
+ const chalk = require('chalk');
5
+ const inquirer = require('inquirer');
6
+ const cjson = require('comment-json');
7
+ const fileManager = require('./file-manager');
8
+ const configLoader = require('./config-loader');
9
+ const { extractYamlFromAgent } = require('../../lib/yaml-utils');
10
+ const BaseIdeSetup = require('./ide-base-setup');
11
+ const resourceLocator = require('./resource-locator');
12
+
13
+ class IdeSetup extends BaseIdeSetup {
14
+ constructor() {
15
+ super();
16
+ this.ideAgentConfig = null;
17
+ }
18
+
19
+ async loadIdeAgentConfig() {
20
+ if (this.ideAgentConfig) return this.ideAgentConfig;
21
+
22
+ try {
23
+ const configPath = path.join(__dirname, '..', 'config', 'ide-agent-config.yaml');
24
+ const configContent = await fs.readFile(configPath, 'utf8');
25
+ this.ideAgentConfig = yaml.load(configContent);
26
+ return this.ideAgentConfig;
27
+ } catch {
28
+ console.warn('Failed to load IDE agent configuration, using defaults');
29
+ return {
30
+ 'roo-permissions': {},
31
+ 'cline-order': {},
32
+ };
33
+ }
34
+ }
35
+
36
+ async setup(ide, installDir, selectedAgent = null, spinner = null, preConfiguredSettings = null) {
37
+ const ideConfig = await configLoader.getIdeConfiguration(ide);
38
+
39
+ if (!ideConfig) {
40
+ console.log(chalk.yellow(`\nNo configuration available for ${ide}`));
41
+ return false;
42
+ }
43
+
44
+ switch (ide) {
45
+ case 'cursor': {
46
+ return this.setupCursor(installDir, selectedAgent);
47
+ }
48
+ case 'opencode': {
49
+ return this.setupOpenCode(installDir, selectedAgent, spinner, preConfiguredSettings);
50
+ }
51
+ case 'claude-code': {
52
+ return this.setupClaudeCode(installDir, selectedAgent);
53
+ }
54
+ case 'iflow-cli': {
55
+ return this.setupIFlowCli(installDir, selectedAgent);
56
+ }
57
+ case 'crush': {
58
+ return this.setupCrush(installDir, selectedAgent);
59
+ }
60
+ case 'windsurf': {
61
+ return this.setupWindsurf(installDir, selectedAgent);
62
+ }
63
+ case 'trae': {
64
+ return this.setupTrae(installDir, selectedAgent);
65
+ }
66
+ case 'roo': {
67
+ return this.setupRoo(installDir, selectedAgent);
68
+ }
69
+ case 'cline': {
70
+ return this.setupCline(installDir, selectedAgent);
71
+ }
72
+ case 'kilo': {
73
+ return this.setupKilocode(installDir, selectedAgent);
74
+ }
75
+ case 'gemini': {
76
+ return this.setupGeminiCli(installDir, selectedAgent);
77
+ }
78
+ case 'github-copilot': {
79
+ return this.setupGitHubCopilot(installDir, selectedAgent, spinner, preConfiguredSettings);
80
+ }
81
+ case 'qwen-code': {
82
+ return this.setupQwenCode(installDir, selectedAgent);
83
+ }
84
+ case 'auggie-cli': {
85
+ return this.setupAuggieCLI(installDir, selectedAgent, spinner, preConfiguredSettings);
86
+ }
87
+ case 'codex': {
88
+ return this.setupCodex(installDir, selectedAgent, { webEnabled: false });
89
+ }
90
+ case 'codex-web': {
91
+ return this.setupCodex(installDir, selectedAgent, { webEnabled: true });
92
+ }
93
+ default: {
94
+ console.log(chalk.yellow(`\nIDE ${ide} not yet supported`));
95
+ return false;
96
+ }
97
+ }
98
+ }
99
+
100
+ async setupOpenCode(installDir, selectedAgent, spinner = null, preConfiguredSettings = null) {
101
+ // Minimal JSON-only integration per plan:
102
+ // - If opencode.json or opencode.jsonc exists: only ensure instructions include .bmad-core/core-config.yaml
103
+ // - If none exists: create minimal opencode.jsonc with $schema and instructions array including that file
104
+
105
+ const jsonPath = path.join(installDir, 'opencode.json');
106
+ const jsoncPath = path.join(installDir, 'opencode.jsonc');
107
+ const hasJson = await fileManager.pathExists(jsonPath);
108
+ const hasJsonc = await fileManager.pathExists(jsoncPath);
109
+
110
+ // Determine key prefix preferences (with sensible defaults)
111
+ // Defaults: non-prefixed (agents = "dev", commands = "create-doc")
112
+ let useAgentPrefix = false;
113
+ let useCommandPrefix = false;
114
+
115
+ // Allow pre-configuration (if passed) to skip prompts
116
+ const pre = preConfiguredSettings && preConfiguredSettings.opencode;
117
+ if (pre && typeof pre.useAgentPrefix === 'boolean') useAgentPrefix = pre.useAgentPrefix;
118
+ if (pre && typeof pre.useCommandPrefix === 'boolean') useCommandPrefix = pre.useCommandPrefix;
119
+
120
+ // If no pre-config and in interactive mode, prompt the user
121
+ if (!pre) {
122
+ // Pause spinner during prompts if active
123
+ let spinnerWasActive = false;
124
+ if (spinner && spinner.isSpinning) {
125
+ spinner.stop();
126
+ spinnerWasActive = true;
127
+ }
128
+
129
+ try {
130
+ const resp = await inquirer.prompt([
131
+ {
132
+ type: 'confirm',
133
+ name: 'useAgentPrefix',
134
+ message:
135
+ "Prefix agent keys with 'bmad-'? (Recommended to avoid collisions, e.g., 'bmad-dev')",
136
+ default: true,
137
+ },
138
+ {
139
+ type: 'confirm',
140
+ name: 'useCommandPrefix',
141
+ message:
142
+ "Prefix command keys with 'bmad:tasks:'? (Recommended, e.g., 'bmad:tasks:create-doc')",
143
+ default: true,
144
+ },
145
+ ]);
146
+ useAgentPrefix = resp.useAgentPrefix;
147
+ useCommandPrefix = resp.useCommandPrefix;
148
+ } catch {
149
+ // Keep defaults if prompt fails or is not interactive
150
+ } finally {
151
+ if (spinner && spinnerWasActive) spinner.start();
152
+ }
153
+ }
154
+
155
+ const ensureInstructionRef = (obj) => {
156
+ const preferred = '.bmad-core/core-config.yaml';
157
+ const alt = './.bmad-core/core-config.yaml';
158
+ if (!obj.instructions) obj.instructions = [];
159
+ if (!Array.isArray(obj.instructions)) obj.instructions = [obj.instructions];
160
+ // Normalize alternative form (with './') to preferred without './'
161
+ obj.instructions = obj.instructions.map((it) =>
162
+ typeof it === 'string' && it === alt ? preferred : it,
163
+ );
164
+ const hasPreferred = obj.instructions.some(
165
+ (it) => typeof it === 'string' && it === preferred,
166
+ );
167
+ if (!hasPreferred) obj.instructions.push(preferred);
168
+ return obj;
169
+ };
170
+
171
+ const mergeBmadAgentsAndCommands = async (configObj) => {
172
+ // Ensure objects exist
173
+ if (!configObj.agent || typeof configObj.agent !== 'object') configObj.agent = {};
174
+ if (!configObj.command || typeof configObj.command !== 'object') configObj.command = {};
175
+ if (!configObj.instructions) configObj.instructions = [];
176
+ if (!Array.isArray(configObj.instructions)) configObj.instructions = [configObj.instructions];
177
+
178
+ // Track a concise summary of changes
179
+ const summary = {
180
+ target: null,
181
+ created: false,
182
+ agentsAdded: 0,
183
+ agentsUpdated: 0,
184
+ agentsSkipped: 0,
185
+ commandsAdded: 0,
186
+ commandsUpdated: 0,
187
+ commandsSkipped: 0,
188
+ };
189
+
190
+ // Determine package scope: previously SELECTED packages in installer UI
191
+ const selectedPackages = preConfiguredSettings?.selectedPackages || {
192
+ includeCore: true,
193
+ packs: [],
194
+ };
195
+
196
+ // Helper: ensure an instruction path is present without './' prefix, de-duplicating './' variants
197
+ const ensureInstructionPath = (pathNoDot) => {
198
+ const withDot = `./${pathNoDot}`;
199
+ // Normalize any existing './' variant to non './'
200
+ configObj.instructions = configObj.instructions.map((it) =>
201
+ typeof it === 'string' && it === withDot ? pathNoDot : it,
202
+ );
203
+ const has = configObj.instructions.some((it) => typeof it === 'string' && it === pathNoDot);
204
+ if (!has) configObj.instructions.push(pathNoDot);
205
+ };
206
+
207
+ // Helper: detect orchestrator agents to set as primary mode
208
+ const isOrchestratorAgent = (agentId) => /(^|-)orchestrator$/i.test(agentId);
209
+
210
+ // Helper: extract whenToUse string from an agent markdown file
211
+ const extractWhenToUseFromFile = async (absPath) => {
212
+ try {
213
+ const raw = await fileManager.readFile(absPath);
214
+ const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
215
+ const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
216
+ if (!yamlBlock) return null;
217
+ // Try quoted first, then unquoted
218
+ const quoted = yamlBlock.match(/whenToUse:\s*"([^"]+)"/i);
219
+ if (quoted && quoted[1]) return quoted[1].trim();
220
+ const unquoted = yamlBlock.match(/whenToUse:\s*([^\n\r]+)/i);
221
+ if (unquoted && unquoted[1]) return unquoted[1].trim();
222
+ } catch {
223
+ // ignore
224
+ }
225
+ return null;
226
+ };
227
+
228
+ // Helper: extract Purpose string from a task file (YAML fenced block, Markdown heading, or inline 'Purpose:')
229
+ const extractTaskPurposeFromFile = async (absPath) => {
230
+ const cleanupAndSummarize = (text) => {
231
+ if (!text) return null;
232
+ let t = String(text);
233
+ // Drop code fences and HTML comments
234
+ t = t.replaceAll(/```[\s\S]*?```/g, '');
235
+ t = t.replaceAll(/<!--([\s\S]*?)-->/g, '');
236
+ // Normalize line endings
237
+ t = t.replaceAll(/\r\n?/g, '\n');
238
+ // Take the first non-empty paragraph
239
+ const paragraphs = t.split(/\n\s*\n/g).map((p) => p.trim());
240
+ let first = paragraphs.find((p) => p.length > 0) || '';
241
+ // Remove leading list markers, quotes, and headings remnants
242
+ first = first.replaceAll(/^\s*[>*-]\s+/gm, '');
243
+ first = first.replaceAll(/^#{1,6}\s+/gm, '');
244
+ // Strip simple Markdown formatting
245
+ first = first.replaceAll(/\*\*([^*]+)\*\*/g, '$1').replaceAll(/\*([^*]+)\*/g, '$1');
246
+ first = first.replaceAll(/`([^`]+)`/g, '$1');
247
+ // Collapse whitespace
248
+ first = first.replaceAll(/\s+/g, ' ').trim();
249
+ if (!first) return null;
250
+ // Prefer ending at a sentence boundary if long
251
+ const maxLen = 320;
252
+ if (first.length > maxLen) {
253
+ const boundary = first.slice(0, maxLen + 40).match(/^[\s\S]*?[.!?](\s|$)/);
254
+ const cut = boundary ? boundary[0] : first.slice(0, maxLen);
255
+ return cut.trim();
256
+ }
257
+ return first;
258
+ };
259
+
260
+ try {
261
+ const raw = await fileManager.readFile(absPath);
262
+ // 1) YAML fenced block: look for Purpose fields
263
+ const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
264
+ const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
265
+ if (yamlBlock) {
266
+ try {
267
+ const data = yaml.load(yamlBlock);
268
+ if (data) {
269
+ let val = data.Purpose ?? data.purpose;
270
+ if (!val && data.task && (data.task.Purpose || data.task.purpose)) {
271
+ val = data.task.Purpose ?? data.task.purpose;
272
+ }
273
+ if (typeof val === 'string') {
274
+ const cleaned = cleanupAndSummarize(val);
275
+ if (cleaned) return cleaned;
276
+ }
277
+ }
278
+ } catch {
279
+ // ignore YAML parse errors
280
+ }
281
+ // Fallback regex inside YAML block
282
+ const quoted = yamlBlock.match(/(?:^|\n)\s*(?:Purpose|purpose):\s*"([^"]+)"/);
283
+ if (quoted && quoted[1]) {
284
+ const cleaned = cleanupAndSummarize(quoted[1]);
285
+ if (cleaned) return cleaned;
286
+ }
287
+ const unquoted = yamlBlock.match(/(?:^|\n)\s*(?:Purpose|purpose):\s*([^\n\r]+)/);
288
+ if (unquoted && unquoted[1]) {
289
+ const cleaned = cleanupAndSummarize(unquoted[1]);
290
+ if (cleaned) return cleaned;
291
+ }
292
+ }
293
+
294
+ // 2) Markdown heading section: ## Purpose (any level >= 2)
295
+ const headingRe = /^(#{2,6})\s*Purpose\s*$/im;
296
+ const headingMatch = headingRe.exec(raw);
297
+ if (headingMatch) {
298
+ const headingLevel = headingMatch[1].length;
299
+ const sectionStart = headingMatch.index + headingMatch[0].length;
300
+ const rest = raw.slice(sectionStart);
301
+ // Next heading of same or higher level ends the section
302
+ const nextHeadingRe = new RegExp(`^#{1,${headingLevel}}\\s+[^\n]+`, 'im');
303
+ const nextMatch = nextHeadingRe.exec(rest);
304
+ const section = nextMatch ? rest.slice(0, nextMatch.index) : rest;
305
+ const cleaned = cleanupAndSummarize(section);
306
+ if (cleaned) return cleaned;
307
+ }
308
+
309
+ // 3) Inline single-line fallback: Purpose: ...
310
+ const inline = raw.match(/(?:^|\n)\s*Purpose\s*:\s*([^\n\r]+)/i);
311
+ if (inline && inline[1]) {
312
+ const cleaned = cleanupAndSummarize(inline[1]);
313
+ if (cleaned) return cleaned;
314
+ }
315
+ } catch {
316
+ // ignore
317
+ }
318
+ return null;
319
+ };
320
+
321
+ // Build core sets
322
+ const coreAgentIds = new Set();
323
+ const coreTaskIds = new Set();
324
+ if (selectedPackages.includeCore) {
325
+ for (const id of await this.getCoreAgentIds(installDir)) coreAgentIds.add(id);
326
+ for (const id of await this.getCoreTaskIds(installDir)) coreTaskIds.add(id);
327
+ }
328
+
329
+ // Build packs info: { packId, packPath, packKey, agents:Set, tasks:Set }
330
+ const packsInfo = [];
331
+ if (Array.isArray(selectedPackages.packs)) {
332
+ for (const packId of selectedPackages.packs) {
333
+ const dotPackPath = path.join(installDir, `.${packId}`);
334
+ const altPackPath = path.join(installDir, 'expansion-packs', packId);
335
+ const packPath = (await fileManager.pathExists(dotPackPath))
336
+ ? dotPackPath
337
+ : (await fileManager.pathExists(altPackPath))
338
+ ? altPackPath
339
+ : null;
340
+ if (!packPath) continue;
341
+
342
+ // Ensure pack config.yaml is added to instructions (relative path, no './')
343
+ const packConfigAbs = path.join(packPath, 'config.yaml');
344
+ if (await fileManager.pathExists(packConfigAbs)) {
345
+ const relCfg = path.relative(installDir, packConfigAbs).replaceAll('\\', '/');
346
+ ensureInstructionPath(relCfg);
347
+ }
348
+
349
+ const packKey = packId.replace(/^bmad-/, '').replaceAll('/', '-');
350
+ const info = { packId, packPath, packKey, agents: new Set(), tasks: new Set() };
351
+
352
+ const glob = require('glob');
353
+ const agentsDir = path.join(packPath, 'agents');
354
+ if (await fileManager.pathExists(agentsDir)) {
355
+ const files = glob.sync('*.md', { cwd: agentsDir });
356
+ for (const f of files) info.agents.add(path.basename(f, '.md'));
357
+ }
358
+ const tasksDir = path.join(packPath, 'tasks');
359
+ if (await fileManager.pathExists(tasksDir)) {
360
+ const files = glob.sync('*.md', { cwd: tasksDir });
361
+ for (const f of files) info.tasks.add(path.basename(f, '.md'));
362
+ }
363
+ packsInfo.push(info);
364
+ }
365
+ }
366
+
367
+ // Generate agents - core first (respect optional agent prefix)
368
+ for (const agentId of coreAgentIds) {
369
+ const p = await this.findAgentPath(agentId, installDir); // prefers core
370
+ if (!p) continue;
371
+ const rel = path.relative(installDir, p).replaceAll('\\', '/');
372
+ const fileRef = `{file:./${rel}}`;
373
+ const baseKey = agentId;
374
+ const key = useAgentPrefix
375
+ ? baseKey.startsWith('bmad-')
376
+ ? baseKey
377
+ : `bmad-${baseKey}`
378
+ : baseKey;
379
+ const existing = configObj.agent[key];
380
+ const whenToUse = await extractWhenToUseFromFile(p);
381
+ const agentDef = {
382
+ prompt: fileRef,
383
+ mode: isOrchestratorAgent(agentId) ? 'primary' : 'all',
384
+ tools: { write: true, edit: true, bash: true },
385
+ ...(whenToUse ? { description: whenToUse } : {}),
386
+ };
387
+ if (!existing) {
388
+ configObj.agent[key] = agentDef;
389
+ summary.agentsAdded++;
390
+ } else if (
391
+ existing &&
392
+ typeof existing === 'object' &&
393
+ typeof existing.prompt === 'string' &&
394
+ existing.prompt.includes(rel)
395
+ ) {
396
+ existing.prompt = agentDef.prompt;
397
+ existing.mode = agentDef.mode;
398
+ if (whenToUse) existing.description = whenToUse;
399
+ existing.tools = { write: true, edit: true, bash: true };
400
+ configObj.agent[key] = existing;
401
+ summary.agentsUpdated++;
402
+ } else {
403
+ summary.agentsSkipped++;
404
+ // Collision warning: key exists but does not appear BMAD-managed (different prompt path)
405
+ console.log(
406
+ chalk.yellow(
407
+ `⚠︎ Skipped agent key '${key}' (existing entry not BMAD-managed). Tip: enable agent prefixes to avoid collisions.`,
408
+ ),
409
+ );
410
+ }
411
+ }
412
+
413
+ // Generate agents - expansion packs (forced pack-specific prefix)
414
+ for (const pack of packsInfo) {
415
+ for (const agentId of pack.agents) {
416
+ const p = path.join(pack.packPath, 'agents', `${agentId}.md`);
417
+ if (!(await fileManager.pathExists(p))) continue;
418
+ const rel = path.relative(installDir, p).replaceAll('\\', '/');
419
+ const fileRef = `{file:./${rel}}`;
420
+ const prefixedKey = `bmad-${pack.packKey}-${agentId}`;
421
+ const existing = configObj.agent[prefixedKey];
422
+ const whenToUse = await extractWhenToUseFromFile(p);
423
+ const agentDef = {
424
+ prompt: fileRef,
425
+ mode: isOrchestratorAgent(agentId) ? 'primary' : 'all',
426
+ tools: { write: true, edit: true, bash: true },
427
+ ...(whenToUse ? { description: whenToUse } : {}),
428
+ };
429
+ if (!existing) {
430
+ configObj.agent[prefixedKey] = agentDef;
431
+ summary.agentsAdded++;
432
+ } else if (
433
+ existing &&
434
+ typeof existing === 'object' &&
435
+ typeof existing.prompt === 'string' &&
436
+ existing.prompt.includes(rel)
437
+ ) {
438
+ existing.prompt = agentDef.prompt;
439
+ existing.mode = agentDef.mode;
440
+ if (whenToUse) existing.description = whenToUse;
441
+ existing.tools = { write: true, edit: true, bash: true };
442
+ configObj.agent[prefixedKey] = existing;
443
+ summary.agentsUpdated++;
444
+ } else {
445
+ summary.agentsSkipped++;
446
+ console.log(
447
+ chalk.yellow(
448
+ `⚠︎ Skipped agent key '${prefixedKey}' (existing entry not BMAD-managed). Tip: enable agent prefixes to avoid collisions.`,
449
+ ),
450
+ );
451
+ }
452
+ }
453
+ }
454
+
455
+ // Generate commands - core first (respect optional command prefix)
456
+ for (const taskId of coreTaskIds) {
457
+ const p = await this.findTaskPath(taskId, installDir); // prefers core/common
458
+ if (!p) continue;
459
+ const rel = path.relative(installDir, p).replaceAll('\\', '/');
460
+ const fileRef = `{file:./${rel}}`;
461
+ const key = useCommandPrefix ? `bmad:tasks:${taskId}` : `${taskId}`;
462
+ const existing = configObj.command[key];
463
+ const purpose = await extractTaskPurposeFromFile(p);
464
+ const cmdDef = { template: fileRef, ...(purpose ? { description: purpose } : {}) };
465
+ if (!existing) {
466
+ configObj.command[key] = cmdDef;
467
+ summary.commandsAdded++;
468
+ } else if (
469
+ existing &&
470
+ typeof existing === 'object' &&
471
+ typeof existing.template === 'string' &&
472
+ existing.template.includes(rel)
473
+ ) {
474
+ existing.template = cmdDef.template;
475
+ if (purpose) existing.description = purpose;
476
+ configObj.command[key] = existing;
477
+ summary.commandsUpdated++;
478
+ } else {
479
+ summary.commandsSkipped++;
480
+ console.log(
481
+ chalk.yellow(
482
+ `⚠︎ Skipped command key '${key}' (existing entry not BMAD-managed). Tip: enable command prefixes to avoid collisions.`,
483
+ ),
484
+ );
485
+ }
486
+ }
487
+
488
+ // Generate commands - expansion packs (forced pack-specific prefix)
489
+ for (const pack of packsInfo) {
490
+ for (const taskId of pack.tasks) {
491
+ const p = path.join(pack.packPath, 'tasks', `${taskId}.md`);
492
+ if (!(await fileManager.pathExists(p))) continue;
493
+ const rel = path.relative(installDir, p).replaceAll('\\', '/');
494
+ const fileRef = `{file:./${rel}}`;
495
+ const prefixedKey = `bmad:${pack.packKey}:${taskId}`;
496
+ const existing = configObj.command[prefixedKey];
497
+ const purpose = await extractTaskPurposeFromFile(p);
498
+ const cmdDef = { template: fileRef, ...(purpose ? { description: purpose } : {}) };
499
+ if (!existing) {
500
+ configObj.command[prefixedKey] = cmdDef;
501
+ summary.commandsAdded++;
502
+ } else if (
503
+ existing &&
504
+ typeof existing === 'object' &&
505
+ typeof existing.template === 'string' &&
506
+ existing.template.includes(rel)
507
+ ) {
508
+ existing.template = cmdDef.template;
509
+ if (purpose) existing.description = purpose;
510
+ configObj.command[prefixedKey] = existing;
511
+ summary.commandsUpdated++;
512
+ } else {
513
+ summary.commandsSkipped++;
514
+ console.log(
515
+ chalk.yellow(
516
+ `⚠︎ Skipped command key '${prefixedKey}' (existing entry not BMAD-managed). Tip: enable command prefixes to avoid collisions.`,
517
+ ),
518
+ );
519
+ }
520
+ }
521
+ }
522
+
523
+ return { configObj, summary };
524
+ };
525
+
526
+ // Helper: generate AGENTS.md section for OpenCode (acts as system prompt memory)
527
+ const generateOpenCodeAgentsMd = async () => {
528
+ try {
529
+ const filePath = path.join(installDir, 'AGENTS.md');
530
+ const startMarker = '<!-- BEGIN: BMAD-AGENTS-OPENCODE -->';
531
+ const endMarker = '<!-- END: BMAD-AGENTS-OPENCODE -->';
532
+
533
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
534
+ const tasks = await this.getAllTaskIds(installDir);
535
+
536
+ let section = '';
537
+ section += `${startMarker}\n`;
538
+ section += `# BMAD-METHOD Agents and Tasks (OpenCode)\n\n`;
539
+ section += `OpenCode reads AGENTS.md during initialization and uses it as part of its system prompt for the session. This section is auto-generated by BMAD-METHOD for OpenCode.\n\n`;
540
+ section += `## How To Use With OpenCode\n\n`;
541
+ section += `- Run \`opencode\` in this project. OpenCode will read \`AGENTS.md\` and your OpenCode config (opencode.json[c]).\n`;
542
+ section += `- Reference a role naturally, e.g., "As dev, implement ..." or use commands defined in your BMAD tasks.\n`;
543
+ section += `- Commit \`.bmad-core\` and \`AGENTS.md\` if you want teammates to share the same configuration.\n`;
544
+ section += `- Refresh this section after BMAD updates: \`npx bmad-method install -f -i opencode\`.\n\n`;
545
+
546
+ section += `### Helpful Commands\n\n`;
547
+ section += `- List agents: \`npx bmad-method list:agents\`\n`;
548
+ section += `- Reinstall BMAD core and regenerate this section: \`npx bmad-method install -f -i opencode\`\n`;
549
+ section += `- Validate configuration: \`npx bmad-method validate\`\n\n`;
550
+
551
+ // Brief context note for modes and tools
552
+ section += `Note\n`;
553
+ section += `- Orchestrators run as mode: primary; other agents as all.\n`;
554
+ section += `- All agents have tools enabled: write, edit, bash.\n\n`;
555
+
556
+ section += `## Agents\n\n`;
557
+ section += `### Directory\n\n`;
558
+ section += `| Title | ID | When To Use |\n|---|---|---|\n`;
559
+
560
+ // Fallback descriptions for core agents (used if whenToUse is missing)
561
+ const fallbackDescriptions = {
562
+ 'ux-expert':
563
+ 'Use for UI/UX design, wireframes, prototypes, front-end specs, and user experience optimization',
564
+ sm: 'Use for story creation, epic management, retrospectives in party-mode, and agile process guidance',
565
+ qa: 'Ensure quality strategy, test design, risk profiling, and QA gates across features',
566
+ po: 'Backlog management, story refinement, acceptance criteria, sprint planning, prioritization decisions',
567
+ pm: 'PRDs, product strategy, feature prioritization, roadmap planning, and stakeholder communication',
568
+ dev: 'Code implementation, debugging, refactoring, and development best practices',
569
+ 'bmad-orchestrator':
570
+ 'Workflow coordination, multi-agent tasks, role switching guidance, and when unsure which specialist to consult',
571
+ 'bmad-master':
572
+ 'Comprehensive cross-domain execution for tasks that do not require a specific persona',
573
+ architect:
574
+ 'System design, architecture docs, technology selection, API design, and infrastructure planning',
575
+ analyst:
576
+ 'Discovery/research, competitive analysis, project briefs, initial discovery, and brownfield documentation',
577
+ };
578
+
579
+ const sanitizeDesc = (s) => {
580
+ if (!s) return '';
581
+ let t = String(s).trim();
582
+ // Drop surrounding single/double/backtick quotes
583
+ t = t.replaceAll(/^['"`]+|['"`]+$/g, '');
584
+ // Collapse whitespace
585
+ t = t.replaceAll(/\s+/g, ' ').trim();
586
+ return t;
587
+ };
588
+ const agentSummaries = [];
589
+ for (const agentId of agents) {
590
+ const agentPath = await this.findAgentPath(agentId, installDir);
591
+ if (!agentPath) continue;
592
+ let whenToUse = '';
593
+ try {
594
+ const raw = await fileManager.readFile(agentPath);
595
+ const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
596
+ const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
597
+ if (yamlBlock) {
598
+ try {
599
+ const data = yaml.load(yamlBlock);
600
+ if (data && typeof data.whenToUse === 'string') {
601
+ whenToUse = data.whenToUse;
602
+ }
603
+ } catch {
604
+ // ignore YAML parse errors
605
+ }
606
+ if (!whenToUse) {
607
+ // Fallback regex supporting single or double quotes
608
+ const m1 = yamlBlock.match(/whenToUse:\s*"([^\n"]+)"/i);
609
+ const m2 = yamlBlock.match(/whenToUse:\s*'([^\n']+)'/i);
610
+ const m3 = yamlBlock.match(/whenToUse:\s*([^\n\r]+)/i);
611
+ whenToUse = (m1?.[1] || m2?.[1] || m3?.[1] || '').trim();
612
+ }
613
+ }
614
+ } catch {
615
+ // ignore read/parse errors for agent metadata extraction
616
+ }
617
+ const title = await this.getAgentTitle(agentId, installDir);
618
+ const finalDesc = sanitizeDesc(whenToUse) || fallbackDescriptions[agentId] || '—';
619
+ agentSummaries.push({ agentId, title, whenToUse: finalDesc, path: agentPath });
620
+ // Strict 3-column row
621
+ section += `| ${title} | ${agentId} | ${finalDesc} |\n`;
622
+ }
623
+ section += `\n`;
624
+
625
+ for (const { agentId, title, whenToUse, path: agentPath } of agentSummaries) {
626
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
627
+ section += `### ${title} (id: ${agentId})\n`;
628
+ section += `Source: [${relativePath}](${relativePath})\n\n`;
629
+ if (whenToUse) section += `- When to use: ${whenToUse}\n`;
630
+ section += `- How to activate: Mention "As ${agentId}, ..." to get role-aligned behavior\n`;
631
+ section += `- Full definition: open the source file above (content not embedded)\n\n`;
632
+ }
633
+
634
+ if (tasks && tasks.length > 0) {
635
+ section += `## Tasks\n\n`;
636
+ section += `These are reusable task briefs; use the paths to open them as needed.\n\n`;
637
+ for (const taskId of tasks) {
638
+ const taskPath = await this.findTaskPath(taskId, installDir);
639
+ if (!taskPath) continue;
640
+ const relativePath = path.relative(installDir, taskPath).replaceAll('\\', '/');
641
+ section += `### Task: ${taskId}\n`;
642
+ section += `Source: [${relativePath}](${relativePath})\n`;
643
+ section += `- How to use: Reference the task in your prompt or execute via your configured commands.\n`;
644
+ section += `- Full brief: open the source file above (content not embedded)\n\n`;
645
+ }
646
+ }
647
+
648
+ section += `${endMarker}\n`;
649
+
650
+ let finalContent = '';
651
+ if (await fileManager.pathExists(filePath)) {
652
+ const existing = await fileManager.readFile(filePath);
653
+ if (existing.includes(startMarker) && existing.includes(endMarker)) {
654
+ const pattern = String.raw`${startMarker}[\s\S]*?${endMarker}`;
655
+ const replaced = existing.replace(new RegExp(pattern, 'm'), section);
656
+ finalContent = replaced;
657
+ } else {
658
+ finalContent = existing.trimEnd() + `\n\n` + section;
659
+ }
660
+ } else {
661
+ finalContent += '# Project Agents\n\n';
662
+ finalContent += 'This file provides guidance and memory for your coding CLI.\n\n';
663
+ finalContent += section;
664
+ }
665
+
666
+ await fileManager.writeFile(filePath, finalContent);
667
+ console.log(chalk.green('✓ Created/updated AGENTS.md for OpenCode CLI integration'));
668
+ console.log(
669
+ chalk.dim(
670
+ 'OpenCode reads AGENTS.md automatically on init. Run `opencode` in this project to use BMAD agents.',
671
+ ),
672
+ );
673
+ } catch {
674
+ console.log(chalk.yellow('⚠︎ Skipped creating AGENTS.md for OpenCode (write failed)'));
675
+ }
676
+ };
677
+
678
+ if (hasJson || hasJsonc) {
679
+ // Preserve existing top-level fields; only touch instructions
680
+ const targetPath = hasJsonc ? jsoncPath : jsonPath;
681
+ try {
682
+ const raw = await fs.readFile(targetPath, 'utf8');
683
+ // Use comment-json for both .json and .jsonc for resilience
684
+ const parsed = cjson.parse(raw, undefined, true);
685
+ ensureInstructionRef(parsed);
686
+ const { configObj, summary } = await mergeBmadAgentsAndCommands(parsed);
687
+ const output = cjson.stringify(parsed, null, 2);
688
+ await fs.writeFile(targetPath, output + (output.endsWith('\n') ? '' : '\n'));
689
+ console.log(
690
+ chalk.green(
691
+ '✓ Updated OpenCode config: ensured BMAD instructions and merged agents/commands',
692
+ ),
693
+ );
694
+ // Summary output
695
+ console.log(
696
+ chalk.dim(
697
+ ` File: ${path.basename(targetPath)} | Agents +${summary.agentsAdded} ~${summary.agentsUpdated} ⨯${summary.agentsSkipped} | Commands +${summary.commandsAdded} ~${summary.commandsUpdated} ⨯${summary.commandsSkipped}`,
698
+ ),
699
+ );
700
+ // Ensure AGENTS.md is created/updated for OpenCode as well
701
+ await generateOpenCodeAgentsMd();
702
+ } catch (error) {
703
+ console.log(chalk.red('✗ Failed to update existing OpenCode config'), error.message);
704
+ return false;
705
+ }
706
+ return true;
707
+ }
708
+
709
+ // Create minimal opencode.jsonc
710
+ const minimal = {
711
+ $schema: 'https://opencode.ai/config.json',
712
+ instructions: ['.bmad-core/core-config.yaml'],
713
+ agent: {},
714
+ command: {},
715
+ };
716
+ try {
717
+ const { configObj, summary } = await mergeBmadAgentsAndCommands(minimal);
718
+ const output = cjson.stringify(minimal, null, 2);
719
+ await fs.writeFile(jsoncPath, output + (output.endsWith('\n') ? '' : '\n'));
720
+ console.log(
721
+ chalk.green('✓ Created opencode.jsonc with BMAD instructions, agents, and commands'),
722
+ );
723
+ console.log(
724
+ chalk.dim(
725
+ ` File: opencode.jsonc | Agents +${summary.agentsAdded} | Commands +${summary.commandsAdded}`,
726
+ ),
727
+ );
728
+ // Also create/update AGENTS.md for OpenCode on new-config path
729
+ await generateOpenCodeAgentsMd();
730
+ return true;
731
+ } catch (error) {
732
+ console.log(chalk.red('✗ Failed to create opencode.jsonc'), error.message);
733
+ return false;
734
+ }
735
+ }
736
+
737
+ async setupCodex(installDir, selectedAgent, options) {
738
+ options = options ?? { webEnabled: false };
739
+ // Codex reads AGENTS.md at the project root as project memory (CLI & Web).
740
+ // Inject/update a BMAD section with guidance, directory, and details.
741
+ const filePath = path.join(installDir, 'AGENTS.md');
742
+ const startMarker = '<!-- BEGIN: BMAD-AGENTS -->';
743
+ const endMarker = '<!-- END: BMAD-AGENTS -->';
744
+
745
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
746
+ const tasks = await this.getAllTaskIds(installDir);
747
+
748
+ // Build BMAD section content
749
+ let section = '';
750
+ section += `${startMarker}\n`;
751
+ section += `# BMAD-METHOD Agents and Tasks\n\n`;
752
+ section += `This section is auto-generated by BMAD-METHOD for Codex. Codex merges this AGENTS.md into context.\n\n`;
753
+ section += `## How To Use With Codex\n\n`;
754
+ section += `- Codex CLI: run \`codex\` in this project. Reference an agent naturally, e.g., "As dev, implement ...".\n`;
755
+ section += `- Codex Web: open this repo and reference roles the same way; Codex reads \`AGENTS.md\`.\n`;
756
+ section += `- Commit \`.bmad-core\` and this \`AGENTS.md\` file to your repo so Codex (Web/CLI) can read full agent definitions.\n`;
757
+ section += `- Refresh this section after agent updates: \`npx bmad-method install -f -i codex\`.\n\n`;
758
+
759
+ section += `### Helpful Commands\n\n`;
760
+ section += `- List agents: \`npx bmad-method list:agents\`\n`;
761
+ section += `- Reinstall BMAD core and regenerate AGENTS.md: \`npx bmad-method install -f -i codex\`\n`;
762
+ section += `- Validate configuration: \`npx bmad-method validate\`\n\n`;
763
+
764
+ // Agents directory table
765
+ section += `## Agents\n\n`;
766
+ section += `### Directory\n\n`;
767
+ section += `| Title | ID | When To Use |\n|---|---|---|\n`;
768
+ const agentSummaries = [];
769
+ for (const agentId of agents) {
770
+ const agentPath = await this.findAgentPath(agentId, installDir);
771
+ if (!agentPath) continue;
772
+ const raw = await fileManager.readFile(agentPath);
773
+ const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
774
+ const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
775
+ const title = await this.getAgentTitle(agentId, installDir);
776
+ const whenToUse = yamlBlock?.match(/whenToUse:\s*"?([^\n"]+)"?/i)?.[1]?.trim() || '';
777
+ agentSummaries.push({ agentId, title, whenToUse, yamlBlock, raw, path: agentPath });
778
+ section += `| ${title} | ${agentId} | ${whenToUse || '—'} |\n`;
779
+ }
780
+ section += `\n`;
781
+
782
+ // Detailed agent sections
783
+ for (const { agentId, title, whenToUse, yamlBlock, raw, path: agentPath } of agentSummaries) {
784
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
785
+ section += `### ${title} (id: ${agentId})\n`;
786
+ section += `Source: ${relativePath}\n\n`;
787
+ if (whenToUse) section += `- When to use: ${whenToUse}\n`;
788
+ section += `- How to activate: Mention "As ${agentId}, ..." or "Use ${title} to ..."\n\n`;
789
+ if (yamlBlock) {
790
+ section += '```yaml\n' + yamlBlock + '\n```\n\n';
791
+ } else {
792
+ section += '```md\n' + raw.trim() + '\n```\n\n';
793
+ }
794
+ }
795
+
796
+ // Tasks
797
+ if (tasks && tasks.length > 0) {
798
+ section += `## Tasks\n\n`;
799
+ section += `These are reusable task briefs you can reference directly in Codex.\n\n`;
800
+ for (const taskId of tasks) {
801
+ const taskPath = await this.findTaskPath(taskId, installDir);
802
+ if (!taskPath) continue;
803
+ const raw = await fileManager.readFile(taskPath);
804
+ const relativePath = path.relative(installDir, taskPath).replaceAll('\\', '/');
805
+ section += `### Task: ${taskId}\n`;
806
+ section += `Source: ${relativePath}\n`;
807
+ section += `- How to use: "Use task ${taskId} with the appropriate agent" and paste relevant parts as needed.\n\n`;
808
+ section += '```md\n' + raw.trim() + '\n```\n\n';
809
+ }
810
+ }
811
+
812
+ section += `${endMarker}\n`;
813
+
814
+ // Write or update AGENTS.md
815
+ let finalContent = '';
816
+ if (await fileManager.pathExists(filePath)) {
817
+ const existing = await fileManager.readFile(filePath);
818
+ if (existing.includes(startMarker) && existing.includes(endMarker)) {
819
+ // Replace existing BMAD block
820
+ const pattern = String.raw`${startMarker}[\s\S]*?${endMarker}`;
821
+ const replaced = existing.replace(new RegExp(pattern, 'm'), section);
822
+ finalContent = replaced;
823
+ } else {
824
+ // Append BMAD block to existing file
825
+ finalContent = existing.trimEnd() + `\n\n` + section;
826
+ }
827
+ } else {
828
+ // Create fresh AGENTS.md with a small header and BMAD block
829
+ finalContent += '# Project Agents\n\n';
830
+ finalContent += 'This file provides guidance and memory for Codex CLI.\n\n';
831
+ finalContent += section;
832
+ }
833
+
834
+ await fileManager.writeFile(filePath, finalContent);
835
+ console.log(chalk.green('✓ Created/updated AGENTS.md for Codex CLI integration'));
836
+ console.log(
837
+ chalk.dim(
838
+ 'Codex reads AGENTS.md automatically. Run `codex` in this project to use BMAD agents.',
839
+ ),
840
+ );
841
+
842
+ // Optionally add helpful npm scripts if a package.json exists
843
+ try {
844
+ const pkgPath = path.join(installDir, 'package.json');
845
+ if (await fileManager.pathExists(pkgPath)) {
846
+ const pkgRaw = await fileManager.readFile(pkgPath);
847
+ const pkg = JSON.parse(pkgRaw);
848
+ pkg.scripts = pkg.scripts || {};
849
+ const updated = { ...pkg.scripts };
850
+ if (!updated['bmad:refresh']) updated['bmad:refresh'] = 'bmad-method install -f -i codex';
851
+ if (!updated['bmad:list']) updated['bmad:list'] = 'bmad-method list:agents';
852
+ if (!updated['bmad:validate']) updated['bmad:validate'] = 'bmad-method validate';
853
+ const changed = JSON.stringify(updated) !== JSON.stringify(pkg.scripts);
854
+ if (changed) {
855
+ const newPkg = { ...pkg, scripts: updated };
856
+ await fileManager.writeFile(pkgPath, JSON.stringify(newPkg, null, 2) + '\n');
857
+ console.log(chalk.green('✓ Added npm scripts: bmad:refresh, bmad:list, bmad:validate'));
858
+ }
859
+ }
860
+ } catch {
861
+ console.log(
862
+ chalk.yellow('⚠︎ Skipped adding npm scripts (package.json not writable or invalid)'),
863
+ );
864
+ }
865
+
866
+ // Adjust .gitignore behavior depending on Codex mode
867
+ try {
868
+ const gitignorePath = path.join(installDir, '.gitignore');
869
+ const ignoreLines = ['# BMAD (local only)', '.bmad-core/', '.bmad-*/'];
870
+ const exists = await fileManager.pathExists(gitignorePath);
871
+ if (options.webEnabled) {
872
+ if (exists) {
873
+ let gi = await fileManager.readFile(gitignorePath);
874
+ const updated = gi
875
+ .split(/\r?\n/)
876
+ .filter((l) => !/^\s*\.bmad-core\/?\s*$/.test(l) && !/^\s*\.bmad-\*\/?\s*$/.test(l))
877
+ .join('\n');
878
+ if (updated !== gi) {
879
+ await fileManager.writeFile(gitignorePath, updated.trimEnd() + '\n');
880
+ console.log(chalk.green('✓ Updated .gitignore to include .bmad-core in commits'));
881
+ }
882
+ }
883
+ } else {
884
+ // Local-only: add ignores if missing
885
+ let base = exists ? await fileManager.readFile(gitignorePath) : '';
886
+ const haveCore = base.includes('.bmad-core/');
887
+ const haveStar = base.includes('.bmad-*/');
888
+ if (!haveCore || !haveStar) {
889
+ const sep = base.endsWith('\n') || base.length === 0 ? '' : '\n';
890
+ const add = [!haveCore || !haveStar ? ignoreLines.join('\n') : '']
891
+ .filter(Boolean)
892
+ .join('\n');
893
+ const out = base + sep + add + '\n';
894
+ await fileManager.writeFile(gitignorePath, out);
895
+ console.log(chalk.green('✓ Added .bmad-core/* to .gitignore for local-only Codex setup'));
896
+ }
897
+ }
898
+ } catch {
899
+ console.log(chalk.yellow('⚠︎ Could not update .gitignore (skipping)'));
900
+ }
901
+
902
+ return true;
903
+ }
904
+
905
+ async setupCursor(installDir, selectedAgent) {
906
+ const cursorRulesDir = path.join(installDir, '.cursor', 'rules', 'bmad');
907
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
908
+
909
+ await fileManager.ensureDirectory(cursorRulesDir);
910
+
911
+ for (const agentId of agents) {
912
+ const agentPath = await this.findAgentPath(agentId, installDir);
913
+
914
+ if (agentPath) {
915
+ const mdcContent = await this.createAgentRuleContent(agentId, agentPath, installDir, 'mdc');
916
+ const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
917
+ await fileManager.writeFile(mdcPath, mdcContent);
918
+ console.log(chalk.green(`✓ Created rule: ${agentId}.mdc`));
919
+ }
920
+ }
921
+
922
+ console.log(chalk.green(`\n✓ Created Cursor rules in ${cursorRulesDir}`));
923
+ return true;
924
+ }
925
+
926
+ async setupCrush(installDir, selectedAgent) {
927
+ // Setup bmad-core commands
928
+ const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
929
+ const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
930
+ const coreTasks = await this.getCoreTaskIds(installDir);
931
+ await this.setupCrushForPackage(
932
+ installDir,
933
+ 'core',
934
+ coreSlashPrefix,
935
+ coreAgents,
936
+ coreTasks,
937
+ '.bmad-core',
938
+ );
939
+
940
+ // Setup expansion pack commands
941
+ const expansionPacks = await this.getInstalledExpansionPacks(installDir);
942
+ for (const packInfo of expansionPacks) {
943
+ const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
944
+ const packAgents = await this.getExpansionPackAgents(packInfo.path);
945
+ const packTasks = await this.getExpansionPackTasks(packInfo.path);
946
+
947
+ if (packAgents.length > 0 || packTasks.length > 0) {
948
+ // Use the actual directory name where the expansion pack is installed
949
+ const rootPath = path.relative(installDir, packInfo.path);
950
+ await this.setupCrushForPackage(
951
+ installDir,
952
+ packInfo.name,
953
+ packSlashPrefix,
954
+ packAgents,
955
+ packTasks,
956
+ rootPath,
957
+ );
958
+ }
959
+ }
960
+
961
+ return true;
962
+ }
963
+
964
+ async setupClaudeCode(installDir, selectedAgent) {
965
+ // Setup bmad-core commands
966
+ const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
967
+ const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
968
+ const coreTasks = await this.getCoreTaskIds(installDir);
969
+ await this.setupClaudeCodeForPackage(
970
+ installDir,
971
+ 'core',
972
+ coreSlashPrefix,
973
+ coreAgents,
974
+ coreTasks,
975
+ '.bmad-core',
976
+ );
977
+
978
+ // Setup expansion pack commands
979
+ const expansionPacks = await this.getInstalledExpansionPacks(installDir);
980
+ for (const packInfo of expansionPacks) {
981
+ const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
982
+ const packAgents = await this.getExpansionPackAgents(packInfo.path);
983
+ const packTasks = await this.getExpansionPackTasks(packInfo.path);
984
+
985
+ if (packAgents.length > 0 || packTasks.length > 0) {
986
+ // Use the actual directory name where the expansion pack is installed
987
+ const rootPath = path.relative(installDir, packInfo.path);
988
+ await this.setupClaudeCodeForPackage(
989
+ installDir,
990
+ packInfo.name,
991
+ packSlashPrefix,
992
+ packAgents,
993
+ packTasks,
994
+ rootPath,
995
+ );
996
+ }
997
+ }
998
+
999
+ return true;
1000
+ }
1001
+
1002
+ async setupClaudeCodeForPackage(
1003
+ installDir,
1004
+ packageName,
1005
+ slashPrefix,
1006
+ agentIds,
1007
+ taskIds,
1008
+ rootPath,
1009
+ ) {
1010
+ const commandsBaseDir = path.join(installDir, '.claude', 'commands', slashPrefix);
1011
+ const agentsDir = path.join(commandsBaseDir, 'agents');
1012
+ const tasksDir = path.join(commandsBaseDir, 'tasks');
1013
+
1014
+ // Ensure directories exist
1015
+ await fileManager.ensureDirectory(agentsDir);
1016
+ await fileManager.ensureDirectory(tasksDir);
1017
+
1018
+ // Setup agents
1019
+ for (const agentId of agentIds) {
1020
+ // Find the agent file - for expansion packs, prefer the expansion pack version
1021
+ let agentPath;
1022
+ if (packageName === 'core') {
1023
+ // For core, use the normal search
1024
+ agentPath = await this.findAgentPath(agentId, installDir);
1025
+ } else {
1026
+ // For expansion packs, first try to find the agent in the expansion pack directory
1027
+ const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
1028
+ if (await fileManager.pathExists(expansionPackPath)) {
1029
+ agentPath = expansionPackPath;
1030
+ } else {
1031
+ // Fall back to core if not found in expansion pack
1032
+ agentPath = await this.findAgentPath(agentId, installDir);
1033
+ }
1034
+ }
1035
+
1036
+ const commandPath = path.join(agentsDir, `${agentId}.md`);
1037
+
1038
+ if (agentPath) {
1039
+ // Create command file with agent content
1040
+ let agentContent = await fileManager.readFile(agentPath);
1041
+
1042
+ // Replace {root} placeholder with the appropriate root path for this context
1043
+ agentContent = agentContent.replaceAll('{root}', rootPath);
1044
+
1045
+ // Add command header
1046
+ let commandContent = `# /${agentId} Command\n\n`;
1047
+ commandContent += `When this command is used, adopt the following agent persona:\n\n`;
1048
+ commandContent += agentContent;
1049
+
1050
+ await fileManager.writeFile(commandPath, commandContent);
1051
+ console.log(chalk.green(`✓ Created agent command: /${agentId}`));
1052
+ }
1053
+ }
1054
+
1055
+ // Setup tasks
1056
+ for (const taskId of taskIds) {
1057
+ // Find the task file - for expansion packs, prefer the expansion pack version
1058
+ let taskPath;
1059
+ if (packageName === 'core') {
1060
+ // For core, use the normal search
1061
+ taskPath = await this.findTaskPath(taskId, installDir);
1062
+ } else {
1063
+ // For expansion packs, first try to find the task in the expansion pack directory
1064
+ const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
1065
+ if (await fileManager.pathExists(expansionPackPath)) {
1066
+ taskPath = expansionPackPath;
1067
+ } else {
1068
+ // Fall back to core if not found in expansion pack
1069
+ taskPath = await this.findTaskPath(taskId, installDir);
1070
+ }
1071
+ }
1072
+
1073
+ const commandPath = path.join(tasksDir, `${taskId}.md`);
1074
+
1075
+ if (taskPath) {
1076
+ // Create command file with task content
1077
+ let taskContent = await fileManager.readFile(taskPath);
1078
+
1079
+ // Replace {root} placeholder with the appropriate root path for this context
1080
+ taskContent = taskContent.replaceAll('{root}', rootPath);
1081
+
1082
+ // Add command header
1083
+ let commandContent = `# /${taskId} Task\n\n`;
1084
+ commandContent += `When this command is used, execute the following task:\n\n`;
1085
+ commandContent += taskContent;
1086
+
1087
+ await fileManager.writeFile(commandPath, commandContent);
1088
+ console.log(chalk.green(`✓ Created task command: /${taskId}`));
1089
+ }
1090
+ }
1091
+
1092
+ console.log(
1093
+ chalk.green(`\n✓ Created Claude Code commands for ${packageName} in ${commandsBaseDir}`),
1094
+ );
1095
+ console.log(chalk.dim(` - Agents in: ${agentsDir}`));
1096
+ console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
1097
+ }
1098
+
1099
+ async setupIFlowCli(installDir, selectedAgent) {
1100
+ // Setup bmad-core commands
1101
+ const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
1102
+ const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
1103
+ const coreTasks = await this.getCoreTaskIds(installDir);
1104
+ await this.setupIFlowCliForPackage(
1105
+ installDir,
1106
+ 'core',
1107
+ coreSlashPrefix,
1108
+ coreAgents,
1109
+ coreTasks,
1110
+ '.bmad-core',
1111
+ );
1112
+
1113
+ // Setup expansion pack commands
1114
+ const expansionPacks = await this.getInstalledExpansionPacks(installDir);
1115
+ for (const packInfo of expansionPacks) {
1116
+ const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
1117
+ const packAgents = await this.getExpansionPackAgents(packInfo.path);
1118
+ const packTasks = await this.getExpansionPackTasks(packInfo.path);
1119
+
1120
+ if (packAgents.length > 0 || packTasks.length > 0) {
1121
+ // Use the actual directory name where the expansion pack is installed
1122
+ const rootPath = path.relative(installDir, packInfo.path);
1123
+ await this.setupIFlowCliForPackage(
1124
+ installDir,
1125
+ packInfo.name,
1126
+ packSlashPrefix,
1127
+ packAgents,
1128
+ packTasks,
1129
+ rootPath,
1130
+ );
1131
+ }
1132
+ }
1133
+
1134
+ return true;
1135
+ }
1136
+
1137
+ async setupIFlowCliForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
1138
+ const commandsBaseDir = path.join(installDir, '.iflow', 'commands', slashPrefix);
1139
+ const agentsDir = path.join(commandsBaseDir, 'agents');
1140
+ const tasksDir = path.join(commandsBaseDir, 'tasks');
1141
+
1142
+ // Ensure directories exist
1143
+ await fileManager.ensureDirectory(agentsDir);
1144
+ await fileManager.ensureDirectory(tasksDir);
1145
+
1146
+ // Setup agents
1147
+ for (const agentId of agentIds) {
1148
+ // Find the agent file - for expansion packs, prefer the expansion pack version
1149
+ let agentPath;
1150
+ if (packageName === 'core') {
1151
+ // For core, use the normal search
1152
+ agentPath = await this.findAgentPath(agentId, installDir);
1153
+ } else {
1154
+ // For expansion packs, first try to find the agent in the expansion pack directory
1155
+ const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
1156
+ if (await fileManager.pathExists(expansionPackPath)) {
1157
+ agentPath = expansionPackPath;
1158
+ } else {
1159
+ // Fall back to core if not found in expansion pack
1160
+ agentPath = await this.findAgentPath(agentId, installDir);
1161
+ }
1162
+ }
1163
+
1164
+ const commandPath = path.join(agentsDir, `${agentId}.md`);
1165
+
1166
+ if (agentPath) {
1167
+ // Create command file with agent content
1168
+ let agentContent = await fileManager.readFile(agentPath);
1169
+
1170
+ // Replace {root} placeholder with the appropriate root path for this context
1171
+ agentContent = agentContent.replaceAll('{root}', rootPath);
1172
+
1173
+ // Add command header
1174
+ let commandContent = `# /${agentId} Command\n\n`;
1175
+ commandContent += `When this command is used, adopt the following agent persona:\n\n`;
1176
+ commandContent += agentContent;
1177
+
1178
+ await fileManager.writeFile(commandPath, commandContent);
1179
+ console.log(chalk.green(`✓ Created agent command: /${agentId}`));
1180
+ }
1181
+ }
1182
+
1183
+ // Setup tasks
1184
+ for (const taskId of taskIds) {
1185
+ // Find the task file - for expansion packs, prefer the expansion pack version
1186
+ let taskPath;
1187
+ if (packageName === 'core') {
1188
+ // For core, use the normal search
1189
+ taskPath = await this.findTaskPath(taskId, installDir);
1190
+ } else {
1191
+ // For expansion packs, first try to find the task in the expansion pack directory
1192
+ const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
1193
+ if (await fileManager.pathExists(expansionPackPath)) {
1194
+ taskPath = expansionPackPath;
1195
+ } else {
1196
+ // Fall back to core if not found in expansion pack
1197
+ taskPath = await this.findTaskPath(taskId, installDir);
1198
+ }
1199
+ }
1200
+
1201
+ const commandPath = path.join(tasksDir, `${taskId}.md`);
1202
+
1203
+ if (taskPath) {
1204
+ // Create command file with task content
1205
+ let taskContent = await fileManager.readFile(taskPath);
1206
+
1207
+ // Replace {root} placeholder with the appropriate root path for this context
1208
+ taskContent = taskContent.replaceAll('{root}', rootPath);
1209
+
1210
+ // Add command header
1211
+ let commandContent = `# /${taskId} Task\n\n`;
1212
+ commandContent += `When this command is used, execute the following task:\n\n`;
1213
+ commandContent += taskContent;
1214
+
1215
+ await fileManager.writeFile(commandPath, commandContent);
1216
+ console.log(chalk.green(`✓ Created task command: /${taskId}`));
1217
+ }
1218
+ }
1219
+
1220
+ console.log(
1221
+ chalk.green(`\n✓ Created iFlow CLI commands for ${packageName} in ${commandsBaseDir}`),
1222
+ );
1223
+ console.log(chalk.dim(` - Agents in: ${agentsDir}`));
1224
+ console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
1225
+ }
1226
+
1227
+ async setupCrushForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
1228
+ const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix);
1229
+ const agentsDir = path.join(commandsBaseDir, 'agents');
1230
+ const tasksDir = path.join(commandsBaseDir, 'tasks');
1231
+
1232
+ // Ensure directories exist
1233
+ await fileManager.ensureDirectory(agentsDir);
1234
+ await fileManager.ensureDirectory(tasksDir);
1235
+
1236
+ // Setup agents
1237
+ for (const agentId of agentIds) {
1238
+ // Find the agent file - for expansion packs, prefer the expansion pack version
1239
+ let agentPath;
1240
+ if (packageName === 'core') {
1241
+ // For core, use the normal search
1242
+ agentPath = await this.findAgentPath(agentId, installDir);
1243
+ } else {
1244
+ // For expansion packs, first try to find the agent in the expansion pack directory
1245
+ const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
1246
+ if (await fileManager.pathExists(expansionPackPath)) {
1247
+ agentPath = expansionPackPath;
1248
+ } else {
1249
+ // Fall back to core if not found in expansion pack
1250
+ agentPath = await this.findAgentPath(agentId, installDir);
1251
+ }
1252
+ }
1253
+
1254
+ const commandPath = path.join(agentsDir, `${agentId}.md`);
1255
+
1256
+ if (agentPath) {
1257
+ // Create command file with agent content
1258
+ let agentContent = await fileManager.readFile(agentPath);
1259
+
1260
+ // Replace {root} placeholder with the appropriate root path for this context
1261
+ agentContent = agentContent.replaceAll('{root}', rootPath);
1262
+
1263
+ // Add command header
1264
+ let commandContent = `# /${agentId} Command\n\n`;
1265
+ commandContent += `When this command is used, adopt the following agent persona:\n\n`;
1266
+ commandContent += agentContent;
1267
+
1268
+ await fileManager.writeFile(commandPath, commandContent);
1269
+ console.log(chalk.green(`✓ Created agent command: /${agentId}`));
1270
+ }
1271
+ }
1272
+
1273
+ // Setup tasks
1274
+ for (const taskId of taskIds) {
1275
+ // Find the task file - for expansion packs, prefer the expansion pack version
1276
+ let taskPath;
1277
+ if (packageName === 'core') {
1278
+ // For core, use the normal search
1279
+ taskPath = await this.findTaskPath(taskId, installDir);
1280
+ } else {
1281
+ // For expansion packs, first try to find the task in the expansion pack directory
1282
+ const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
1283
+ if (await fileManager.pathExists(expansionPackPath)) {
1284
+ taskPath = expansionPackPath;
1285
+ } else {
1286
+ // Fall back to core if not found in expansion pack
1287
+ taskPath = await this.findTaskPath(taskId, installDir);
1288
+ }
1289
+ }
1290
+
1291
+ const commandPath = path.join(tasksDir, `${taskId}.md`);
1292
+
1293
+ if (taskPath) {
1294
+ // Create command file with task content
1295
+ let taskContent = await fileManager.readFile(taskPath);
1296
+
1297
+ // Replace {root} placeholder with the appropriate root path for this context
1298
+ taskContent = taskContent.replaceAll('{root}', rootPath);
1299
+
1300
+ // Add command header
1301
+ let commandContent = `# /${taskId} Task\n\n`;
1302
+ commandContent += `When this command is used, execute the following task:\n\n`;
1303
+ commandContent += taskContent;
1304
+
1305
+ await fileManager.writeFile(commandPath, commandContent);
1306
+ console.log(chalk.green(`✓ Created task command: /${taskId}`));
1307
+ }
1308
+ }
1309
+
1310
+ console.log(chalk.green(`\n✓ Created Crush commands for ${packageName} in ${commandsBaseDir}`));
1311
+ console.log(chalk.dim(` - Agents in: ${agentsDir}`));
1312
+ console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
1313
+ }
1314
+
1315
+ async setupWindsurf(installDir, selectedAgent) {
1316
+ const windsurfWorkflowDir = path.join(installDir, '.windsurf', 'workflows');
1317
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
1318
+
1319
+ await fileManager.ensureDirectory(windsurfWorkflowDir);
1320
+
1321
+ for (const agentId of agents) {
1322
+ // Find the agent file
1323
+ const agentPath = await this.findAgentPath(agentId, installDir);
1324
+
1325
+ if (agentPath) {
1326
+ const agentContent = await fileManager.readFile(agentPath);
1327
+ const mdPath = path.join(windsurfWorkflowDir, `${agentId}.md`);
1328
+
1329
+ // Write the agent file contents prefixed with Windsurf frontmatter
1330
+ let mdContent = `---\n`;
1331
+ mdContent += `description: ${agentId}\n`;
1332
+ mdContent += `auto_execution_mode: 3\n`;
1333
+ mdContent += `---\n\n`;
1334
+ mdContent += agentContent;
1335
+
1336
+ await fileManager.writeFile(mdPath, mdContent);
1337
+ console.log(chalk.green(`✓ Created workflow: ${agentId}.md`));
1338
+ }
1339
+ }
1340
+
1341
+ console.log(chalk.green(`\n✓ Created Windsurf workflows in ${windsurfWorkflowDir}`));
1342
+
1343
+ return true;
1344
+ }
1345
+
1346
+ async setupTrae(installDir, selectedAgent) {
1347
+ const traeRulesDir = path.join(installDir, '.trae', 'rules');
1348
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
1349
+
1350
+ await fileManager.ensureDirectory(traeRulesDir);
1351
+
1352
+ for (const agentId of agents) {
1353
+ // Find the agent file
1354
+ const agentPath = await this.findAgentPath(agentId, installDir);
1355
+
1356
+ if (agentPath) {
1357
+ const agentContent = await fileManager.readFile(agentPath);
1358
+ const mdPath = path.join(traeRulesDir, `${agentId}.md`);
1359
+
1360
+ // Create MD content (similar to Cursor but without frontmatter)
1361
+ let mdContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
1362
+ mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${await this.getAgentTitle(
1363
+ agentId,
1364
+ installDir,
1365
+ )} agent persona.\n\n`;
1366
+ mdContent += '## Agent Activation\n\n';
1367
+ mdContent +=
1368
+ 'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n';
1369
+ mdContent += '```yaml\n';
1370
+ // Extract just the YAML content from the agent file
1371
+ const yamlContent = extractYamlFromAgent(agentContent);
1372
+ if (yamlContent) {
1373
+ mdContent += yamlContent;
1374
+ } else {
1375
+ // If no YAML found, include the whole content minus the header
1376
+ mdContent += agentContent.replace(/^#.*$/m, '').trim();
1377
+ }
1378
+ mdContent += '\n```\n\n';
1379
+ mdContent += '## File Reference\n\n';
1380
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
1381
+ mdContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
1382
+ mdContent += '## Usage\n\n';
1383
+ mdContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
1384
+ agentId,
1385
+ installDir,
1386
+ )} persona and follow all instructions defined in the YAML configuration above.\n`;
1387
+
1388
+ await fileManager.writeFile(mdPath, mdContent);
1389
+ console.log(chalk.green(`✓ Created rule: ${agentId}.md`));
1390
+ }
1391
+ }
1392
+ }
1393
+
1394
+ async findAgentPath(agentId, installDir) {
1395
+ // Try to find the agent file in various locations
1396
+ const possiblePaths = [
1397
+ path.join(installDir, '.bmad-core', 'agents', `${agentId}.md`),
1398
+ path.join(installDir, 'agents', `${agentId}.md`),
1399
+ ];
1400
+
1401
+ // Also check expansion pack directories
1402
+ const glob = require('glob');
1403
+ const expansionDirectories = glob.sync('.*/agents', { cwd: installDir });
1404
+ for (const expDir of expansionDirectories) {
1405
+ possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
1406
+ }
1407
+
1408
+ for (const agentPath of possiblePaths) {
1409
+ if (await fileManager.pathExists(agentPath)) {
1410
+ return agentPath;
1411
+ }
1412
+ }
1413
+
1414
+ return null;
1415
+ }
1416
+
1417
+ async getAllAgentIds(installDir) {
1418
+ const glob = require('glob');
1419
+ const allAgentIds = [];
1420
+
1421
+ // Check core agents in .bmad-core or root
1422
+ let agentsDir = path.join(installDir, '.bmad-core', 'agents');
1423
+ if (!(await fileManager.pathExists(agentsDir))) {
1424
+ agentsDir = path.join(installDir, 'agents');
1425
+ }
1426
+
1427
+ if (await fileManager.pathExists(agentsDir)) {
1428
+ const agentFiles = glob.sync('*.md', { cwd: agentsDir });
1429
+ allAgentIds.push(...agentFiles.map((file) => path.basename(file, '.md')));
1430
+ }
1431
+
1432
+ // Also check for expansion pack agents in dot folders
1433
+ const expansionDirectories = glob.sync('.*/agents', { cwd: installDir });
1434
+ for (const expDir of expansionDirectories) {
1435
+ const fullExpDir = path.join(installDir, expDir);
1436
+ const expAgentFiles = glob.sync('*.md', { cwd: fullExpDir });
1437
+ allAgentIds.push(...expAgentFiles.map((file) => path.basename(file, '.md')));
1438
+ }
1439
+
1440
+ // Remove duplicates
1441
+ return [...new Set(allAgentIds)];
1442
+ }
1443
+
1444
+ async getCoreAgentIds(installDir) {
1445
+ const allAgentIds = [];
1446
+
1447
+ // Check core agents in .bmad-core or root only
1448
+ let agentsDir = path.join(installDir, '.bmad-core', 'agents');
1449
+ if (!(await fileManager.pathExists(agentsDir))) {
1450
+ agentsDir = path.join(installDir, 'bmad-core', 'agents');
1451
+ }
1452
+
1453
+ if (await fileManager.pathExists(agentsDir)) {
1454
+ const glob = require('glob');
1455
+ const agentFiles = glob.sync('*.md', { cwd: agentsDir });
1456
+ allAgentIds.push(...agentFiles.map((file) => path.basename(file, '.md')));
1457
+ }
1458
+
1459
+ return [...new Set(allAgentIds)];
1460
+ }
1461
+
1462
+ async getCoreTaskIds(installDir) {
1463
+ const allTaskIds = [];
1464
+ const glob = require('glob');
1465
+
1466
+ // Check core tasks in .bmad-core or root only
1467
+ let tasksDir = path.join(installDir, '.bmad-core', 'tasks');
1468
+ if (!(await fileManager.pathExists(tasksDir))) {
1469
+ tasksDir = path.join(installDir, 'bmad-core', 'tasks');
1470
+ }
1471
+
1472
+ if (await fileManager.pathExists(tasksDir)) {
1473
+ const taskFiles = glob.sync('*.md', { cwd: tasksDir });
1474
+ allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
1475
+ }
1476
+
1477
+ // Check common tasks
1478
+ const commonTasksDir = path.join(installDir, 'common', 'tasks');
1479
+ if (await fileManager.pathExists(commonTasksDir)) {
1480
+ const glob = require('glob');
1481
+ const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir });
1482
+ allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
1483
+ }
1484
+
1485
+ return [...new Set(allTaskIds)];
1486
+ }
1487
+
1488
+ async getAgentTitle(agentId, installDir) {
1489
+ // Try to find the agent file in various locations
1490
+ const possiblePaths = [
1491
+ path.join(installDir, '.bmad-core', 'agents', `${agentId}.md`),
1492
+ path.join(installDir, 'agents', `${agentId}.md`),
1493
+ ];
1494
+
1495
+ // Also check expansion pack directories
1496
+ const glob = require('glob');
1497
+ const expansionDirectories = glob.sync('.*/agents', { cwd: installDir });
1498
+ for (const expDir of expansionDirectories) {
1499
+ possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
1500
+ }
1501
+
1502
+ for (const agentPath of possiblePaths) {
1503
+ if (await fileManager.pathExists(agentPath)) {
1504
+ try {
1505
+ const agentContent = await fileManager.readFile(agentPath);
1506
+ const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
1507
+
1508
+ if (yamlMatch) {
1509
+ const yaml = yamlMatch[1];
1510
+ const titleMatch = yaml.match(/title:\s*(.+)/);
1511
+ if (titleMatch) {
1512
+ return titleMatch[1].trim();
1513
+ }
1514
+ }
1515
+ } catch (error) {
1516
+ console.warn(`Failed to read agent title for ${agentId}: ${error.message}`);
1517
+ }
1518
+ }
1519
+ }
1520
+
1521
+ // Fallback to formatted agent ID
1522
+ return agentId
1523
+ .split('-')
1524
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
1525
+ .join(' ');
1526
+ }
1527
+
1528
+ async getAllTaskIds(installDir) {
1529
+ const glob = require('glob');
1530
+ const allTaskIds = [];
1531
+
1532
+ // Check core tasks in .bmad-core or root
1533
+ let tasksDir = path.join(installDir, '.bmad-core', 'tasks');
1534
+ if (!(await fileManager.pathExists(tasksDir))) {
1535
+ tasksDir = path.join(installDir, 'bmad-core', 'tasks');
1536
+ }
1537
+
1538
+ if (await fileManager.pathExists(tasksDir)) {
1539
+ const taskFiles = glob.sync('*.md', { cwd: tasksDir });
1540
+ allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
1541
+ }
1542
+
1543
+ // Check common tasks
1544
+ const commonTasksDir = path.join(installDir, 'common', 'tasks');
1545
+ if (await fileManager.pathExists(commonTasksDir)) {
1546
+ const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir });
1547
+ allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
1548
+ }
1549
+
1550
+ // Also check for expansion pack tasks in dot folders
1551
+ const expansionDirectories = glob.sync('.*/tasks', { cwd: installDir });
1552
+ for (const expDir of expansionDirectories) {
1553
+ const fullExpDir = path.join(installDir, expDir);
1554
+ const expTaskFiles = glob.sync('*.md', { cwd: fullExpDir });
1555
+ allTaskIds.push(...expTaskFiles.map((file) => path.basename(file, '.md')));
1556
+ }
1557
+
1558
+ // Check expansion-packs folder tasks
1559
+ const expansionPacksDir = path.join(installDir, 'expansion-packs');
1560
+ if (await fileManager.pathExists(expansionPacksDir)) {
1561
+ const expPackDirectories = glob.sync('*/tasks', { cwd: expansionPacksDir });
1562
+ for (const expDir of expPackDirectories) {
1563
+ const fullExpDir = path.join(expansionPacksDir, expDir);
1564
+ const expTaskFiles = glob.sync('*.md', { cwd: fullExpDir });
1565
+ allTaskIds.push(...expTaskFiles.map((file) => path.basename(file, '.md')));
1566
+ }
1567
+ }
1568
+
1569
+ // Remove duplicates
1570
+ return [...new Set(allTaskIds)];
1571
+ }
1572
+
1573
+ async findTaskPath(taskId, installDir) {
1574
+ // Try to find the task file in various locations
1575
+ const possiblePaths = [
1576
+ path.join(installDir, '.bmad-core', 'tasks', `${taskId}.md`),
1577
+ path.join(installDir, 'bmad-core', 'tasks', `${taskId}.md`),
1578
+ path.join(installDir, 'common', 'tasks', `${taskId}.md`),
1579
+ ];
1580
+
1581
+ // Also check expansion pack directories
1582
+ const glob = require('glob');
1583
+
1584
+ // Check dot folder expansion packs
1585
+ const expansionDirectories = glob.sync('.*/tasks', { cwd: installDir });
1586
+ for (const expDir of expansionDirectories) {
1587
+ possiblePaths.push(path.join(installDir, expDir, `${taskId}.md`));
1588
+ }
1589
+
1590
+ // Check expansion-packs folder
1591
+ const expansionPacksDir = path.join(installDir, 'expansion-packs');
1592
+ if (await fileManager.pathExists(expansionPacksDir)) {
1593
+ const expPackDirectories = glob.sync('*/tasks', { cwd: expansionPacksDir });
1594
+ for (const expDir of expPackDirectories) {
1595
+ possiblePaths.push(path.join(expansionPacksDir, expDir, `${taskId}.md`));
1596
+ }
1597
+ }
1598
+
1599
+ for (const taskPath of possiblePaths) {
1600
+ if (await fileManager.pathExists(taskPath)) {
1601
+ return taskPath;
1602
+ }
1603
+ }
1604
+
1605
+ return null;
1606
+ }
1607
+
1608
+ async getCoreSlashPrefix(installDir) {
1609
+ try {
1610
+ const coreConfigPath = path.join(installDir, '.bmad-core', 'core-config.yaml');
1611
+ if (!(await fileManager.pathExists(coreConfigPath))) {
1612
+ // Try bmad-core directory
1613
+ const altConfigPath = path.join(installDir, 'bmad-core', 'core-config.yaml');
1614
+ if (await fileManager.pathExists(altConfigPath)) {
1615
+ const configContent = await fileManager.readFile(altConfigPath);
1616
+ const config = yaml.load(configContent);
1617
+ return config.slashPrefix || 'BMad';
1618
+ }
1619
+ return 'BMad'; // fallback
1620
+ }
1621
+
1622
+ const configContent = await fileManager.readFile(coreConfigPath);
1623
+ const config = yaml.load(configContent);
1624
+ return config.slashPrefix || 'BMad';
1625
+ } catch (error) {
1626
+ console.warn(`Failed to read core slashPrefix, using default 'BMad': ${error.message}`);
1627
+ return 'BMad';
1628
+ }
1629
+ }
1630
+
1631
+ async getInstalledExpansionPacks(installDir) {
1632
+ const expansionPacks = [];
1633
+
1634
+ // Check for dot-prefixed expansion packs in install directory
1635
+ const glob = require('glob');
1636
+ const dotExpansions = glob.sync('.bmad-*', { cwd: installDir });
1637
+
1638
+ for (const dotExpansion of dotExpansions) {
1639
+ if (dotExpansion !== '.bmad-core') {
1640
+ const packPath = path.join(installDir, dotExpansion);
1641
+ const packName = dotExpansion.slice(1); // remove the dot
1642
+ expansionPacks.push({
1643
+ name: packName,
1644
+ path: packPath,
1645
+ });
1646
+ }
1647
+ }
1648
+
1649
+ // Check for expansion-packs directory style
1650
+ const expansionPacksDir = path.join(installDir, 'expansion-packs');
1651
+ if (await fileManager.pathExists(expansionPacksDir)) {
1652
+ const packDirectories = glob.sync('*', { cwd: expansionPacksDir });
1653
+
1654
+ for (const packDir of packDirectories) {
1655
+ const packPath = path.join(expansionPacksDir, packDir);
1656
+ if (
1657
+ (await fileManager.pathExists(packPath)) &&
1658
+ (await fileManager.pathExists(path.join(packPath, 'config.yaml')))
1659
+ ) {
1660
+ expansionPacks.push({
1661
+ name: packDir,
1662
+ path: packPath,
1663
+ });
1664
+ }
1665
+ }
1666
+ }
1667
+
1668
+ return expansionPacks;
1669
+ }
1670
+
1671
+ async getExpansionPackSlashPrefix(packPath) {
1672
+ try {
1673
+ const configPath = path.join(packPath, 'config.yaml');
1674
+ if (await fileManager.pathExists(configPath)) {
1675
+ const configContent = await fileManager.readFile(configPath);
1676
+ const config = yaml.load(configContent);
1677
+ return config.slashPrefix || path.basename(packPath);
1678
+ }
1679
+ } catch (error) {
1680
+ console.warn(`Failed to read expansion pack slashPrefix from ${packPath}: ${error.message}`);
1681
+ }
1682
+
1683
+ return path.basename(packPath); // fallback to directory name
1684
+ }
1685
+
1686
+ async getExpansionPackAgents(packPath) {
1687
+ const agentsDir = path.join(packPath, 'agents');
1688
+ if (!(await fileManager.pathExists(agentsDir))) {
1689
+ return [];
1690
+ }
1691
+
1692
+ try {
1693
+ const glob = require('glob');
1694
+ const agentFiles = glob.sync('*.md', { cwd: agentsDir });
1695
+ return agentFiles.map((file) => path.basename(file, '.md'));
1696
+ } catch (error) {
1697
+ console.warn(`Failed to read expansion pack agents from ${packPath}: ${error.message}`);
1698
+ return [];
1699
+ }
1700
+ }
1701
+
1702
+ async getExpansionPackTasks(packPath) {
1703
+ const tasksDir = path.join(packPath, 'tasks');
1704
+ if (!(await fileManager.pathExists(tasksDir))) {
1705
+ return [];
1706
+ }
1707
+
1708
+ try {
1709
+ const glob = require('glob');
1710
+ const taskFiles = glob.sync('*.md', { cwd: tasksDir });
1711
+ return taskFiles.map((file) => path.basename(file, '.md'));
1712
+ } catch (error) {
1713
+ console.warn(`Failed to read expansion pack tasks from ${packPath}: ${error.message}`);
1714
+ return [];
1715
+ }
1716
+ }
1717
+
1718
+ async setupRoo(installDir, selectedAgent) {
1719
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
1720
+
1721
+ // Check for existing .roomodes file in project root
1722
+ const roomodesPath = path.join(installDir, '.roomodes');
1723
+ let existingModes = [];
1724
+ let existingContent = '';
1725
+
1726
+ if (await fileManager.pathExists(roomodesPath)) {
1727
+ existingContent = await fileManager.readFile(roomodesPath);
1728
+ // Parse existing modes to avoid duplicates
1729
+ const modeMatches = existingContent.matchAll(/- slug: ([\w-]+)/g);
1730
+ for (const match of modeMatches) {
1731
+ existingModes.push(match[1]);
1732
+ }
1733
+ console.log(chalk.yellow(`Found existing .roomodes file with ${existingModes.length} modes`));
1734
+ }
1735
+
1736
+ // Create new modes content
1737
+ let newModesContent = '';
1738
+
1739
+ // Load dynamic agent permissions from configuration
1740
+ const config = await this.loadIdeAgentConfig();
1741
+ const agentPermissions = config['roo-permissions'] || {};
1742
+
1743
+ for (const agentId of agents) {
1744
+ // Skip if already exists
1745
+ // Check both with and without bmad- prefix to handle both cases
1746
+ const checkSlug = agentId.startsWith('bmad-') ? agentId : `bmad-${agentId}`;
1747
+ if (existingModes.includes(checkSlug)) {
1748
+ console.log(chalk.dim(`Skipping ${agentId} - already exists in .roomodes`));
1749
+ continue;
1750
+ }
1751
+
1752
+ // Read agent file to extract all information
1753
+ const agentPath = await this.findAgentPath(agentId, installDir);
1754
+
1755
+ if (agentPath) {
1756
+ const agentContent = await fileManager.readFile(agentPath);
1757
+
1758
+ // Extract YAML content
1759
+ const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
1760
+ if (yamlMatch) {
1761
+ const yaml = yamlMatch[1];
1762
+
1763
+ // Extract agent info from YAML
1764
+ const titleMatch = yaml.match(/title:\s*(.+)/);
1765
+ const iconMatch = yaml.match(/icon:\s*(.+)/);
1766
+ const whenToUseMatch = yaml.match(/whenToUse:\s*"(.+)"/);
1767
+ const roleDefinitionMatch = yaml.match(/roleDefinition:\s*"(.+)"/);
1768
+
1769
+ const title = titleMatch
1770
+ ? titleMatch[1].trim()
1771
+ : await this.getAgentTitle(agentId, installDir);
1772
+ const icon = iconMatch ? iconMatch[1].trim() : '🤖';
1773
+ const whenToUse = whenToUseMatch ? whenToUseMatch[1].trim() : `Use for ${title} tasks`;
1774
+ const roleDefinition = roleDefinitionMatch
1775
+ ? roleDefinitionMatch[1].trim()
1776
+ : `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
1777
+
1778
+ // Add permissions based on agent type
1779
+ const permissions = agentPermissions[agentId];
1780
+ // Build mode entry with proper formatting (matching exact indentation)
1781
+ // Avoid double "bmad-" prefix for agents that already have it
1782
+ const slug = agentId.startsWith('bmad-') ? agentId : `bmad-${agentId}`;
1783
+ newModesContent += ` - slug: ${slug}\n`;
1784
+ newModesContent += ` name: '${icon} ${title}'\n`;
1785
+ if (permissions) {
1786
+ newModesContent += ` description: '${permissions.description}'\n`;
1787
+ }
1788
+ newModesContent += ` roleDefinition: ${roleDefinition}\n`;
1789
+ newModesContent += ` whenToUse: ${whenToUse}\n`;
1790
+ // Get relative path from installDir to agent file
1791
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
1792
+ newModesContent += ` customInstructions: CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
1793
+ newModesContent += ` groups:\n`;
1794
+ newModesContent += ` - read\n`;
1795
+
1796
+ if (permissions) {
1797
+ newModesContent += ` - - edit\n`;
1798
+ newModesContent += ` - fileRegex: ${permissions.fileRegex}\n`;
1799
+ newModesContent += ` description: ${permissions.description}\n`;
1800
+ } else {
1801
+ newModesContent += ` - edit\n`;
1802
+ }
1803
+
1804
+ console.log(chalk.green(`✓ Added mode: bmad-${agentId} (${icon} ${title})`));
1805
+ }
1806
+ }
1807
+ }
1808
+
1809
+ // Build final roomodes content
1810
+ let roomodesContent = '';
1811
+ if (existingContent) {
1812
+ // If there's existing content, append new modes to it
1813
+ roomodesContent = existingContent.trim() + '\n' + newModesContent;
1814
+ } else {
1815
+ // Create new .roomodes file with proper YAML structure
1816
+ roomodesContent = 'customModes:\n' + newModesContent;
1817
+ }
1818
+
1819
+ // Write .roomodes file
1820
+ await fileManager.writeFile(roomodesPath, roomodesContent);
1821
+ console.log(chalk.green('✓ Created .roomodes file in project root'));
1822
+
1823
+ console.log(chalk.green(`\n✓ Roo Code setup complete!`));
1824
+ console.log(chalk.dim('Custom modes will be available when you open this project in Roo Code'));
1825
+
1826
+ return true;
1827
+ }
1828
+
1829
+ async setupKilocode(installDir, selectedAgent) {
1830
+ const filePath = path.join(installDir, '.kilocodemodes');
1831
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
1832
+
1833
+ let existingModes = [],
1834
+ existingContent = '';
1835
+ if (await fileManager.pathExists(filePath)) {
1836
+ existingContent = await fileManager.readFile(filePath);
1837
+ for (const match of existingContent.matchAll(/- slug: ([\w-]+)/g)) {
1838
+ existingModes.push(match[1]);
1839
+ }
1840
+ console.log(
1841
+ chalk.yellow(`Found existing .kilocodemodes file with ${existingModes.length} modes`),
1842
+ );
1843
+ }
1844
+
1845
+ const config = await this.loadIdeAgentConfig();
1846
+ const permissions = config['roo-permissions'] || {}; // reuse same roo permissions block (Kilo Code understands same mode schema)
1847
+
1848
+ let newContent = '';
1849
+
1850
+ for (const agentId of agents) {
1851
+ const slug = agentId.startsWith('bmad-') ? agentId : `bmad-${agentId}`;
1852
+ if (existingModes.includes(slug)) {
1853
+ console.log(chalk.dim(`Skipping ${agentId} - already exists in .kilocodemodes`));
1854
+ continue;
1855
+ }
1856
+
1857
+ const agentPath = await this.findAgentPath(agentId, installDir);
1858
+ if (!agentPath) {
1859
+ console.log(chalk.red(`✗ Could not find agent file for ${agentId}`));
1860
+ continue;
1861
+ }
1862
+
1863
+ const agentContent = await fileManager.readFile(agentPath);
1864
+ const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
1865
+ if (!yamlMatch) {
1866
+ console.log(chalk.red(`✗ Could not extract YAML block for ${agentId}`));
1867
+ continue;
1868
+ }
1869
+
1870
+ const yaml = yamlMatch[1];
1871
+
1872
+ // Robust fallback for title and icon
1873
+ const title =
1874
+ yaml.match(/title:\s*(.+)/)?.[1]?.trim() || (await this.getAgentTitle(agentId, installDir));
1875
+ const icon = yaml.match(/icon:\s*(.+)/)?.[1]?.trim() || '🤖';
1876
+ const whenToUse = yaml.match(/whenToUse:\s*"(.+)"/)?.[1]?.trim() || `Use for ${title} tasks`;
1877
+ const roleDefinition =
1878
+ yaml.match(/roleDefinition:\s*"(.+)"/)?.[1]?.trim() ||
1879
+ `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
1880
+
1881
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
1882
+ const customInstructions = `CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode`;
1883
+
1884
+ // Add permissions from config if they exist
1885
+ const agentPermission = permissions[agentId];
1886
+
1887
+ // Begin .kilocodemodes block
1888
+ newContent += ` - slug: ${slug}\n`;
1889
+ newContent += ` name: '${icon} ${title}'\n`;
1890
+ if (agentPermission) {
1891
+ newContent += ` description: '${agentPermission.description}'\n`;
1892
+ }
1893
+
1894
+ newContent += ` roleDefinition: ${roleDefinition}\n`;
1895
+ newContent += ` whenToUse: ${whenToUse}\n`;
1896
+ newContent += ` customInstructions: ${customInstructions}\n`;
1897
+ newContent += ` groups:\n`;
1898
+ newContent += ` - read\n`;
1899
+
1900
+ if (agentPermission) {
1901
+ newContent += ` - - edit\n`;
1902
+ newContent += ` - fileRegex: ${agentPermission.fileRegex}\n`;
1903
+ newContent += ` description: ${agentPermission.description}\n`;
1904
+ } else {
1905
+ // Fallback to generic edit
1906
+ newContent += ` - edit\n`;
1907
+ }
1908
+
1909
+ console.log(chalk.green(`✓ Added Kilo mode: ${slug} (${icon} ${title})`));
1910
+ }
1911
+
1912
+ const finalContent = existingContent
1913
+ ? existingContent.trim() + '\n' + newContent
1914
+ : 'customModes:\n' + newContent;
1915
+
1916
+ await fileManager.writeFile(filePath, finalContent);
1917
+ console.log(chalk.green('✓ Created .kilocodemodes file in project root'));
1918
+ console.log(chalk.green(`✓ KiloCode setup complete!`));
1919
+ console.log(chalk.dim('Custom modes will be available when you open this project in KiloCode'));
1920
+
1921
+ return true;
1922
+ }
1923
+
1924
+ async setupCline(installDir, selectedAgent) {
1925
+ const clineRulesDir = path.join(installDir, '.clinerules');
1926
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
1927
+
1928
+ await fileManager.ensureDirectory(clineRulesDir);
1929
+
1930
+ // Load dynamic agent ordering from configuration
1931
+ const config = await this.loadIdeAgentConfig();
1932
+ const agentOrder = config['cline-order'] || {};
1933
+
1934
+ for (const agentId of agents) {
1935
+ // Find the agent file
1936
+ const agentPath = await this.findAgentPath(agentId, installDir);
1937
+
1938
+ if (agentPath) {
1939
+ const agentContent = await fileManager.readFile(agentPath);
1940
+
1941
+ // Get numeric prefix for ordering
1942
+ const order = agentOrder[agentId] || 99;
1943
+ const prefix = order.toString().padStart(2, '0');
1944
+ const mdPath = path.join(clineRulesDir, `${prefix}-${agentId}.md`);
1945
+
1946
+ // Create MD content for Cline (focused on project standards and role)
1947
+ let mdContent = `# ${await this.getAgentTitle(agentId, installDir)} Agent\n\n`;
1948
+ mdContent += `This rule defines the ${await this.getAgentTitle(agentId, installDir)} persona and project standards.\n\n`;
1949
+ mdContent += '## Role Definition\n\n';
1950
+ mdContent +=
1951
+ 'When the user types `@' +
1952
+ agentId +
1953
+ '`, adopt this persona and follow these guidelines:\n\n';
1954
+ mdContent += '```yaml\n';
1955
+ // Extract just the YAML content from the agent file
1956
+ const yamlContent = extractYamlFromAgent(agentContent);
1957
+ if (yamlContent) {
1958
+ mdContent += yamlContent;
1959
+ } else {
1960
+ // If no YAML found, include the whole content minus the header
1961
+ mdContent += agentContent.replace(/^#.*$/m, '').trim();
1962
+ }
1963
+ mdContent += '\n```\n\n';
1964
+ mdContent += '## Project Standards\n\n';
1965
+ mdContent += `- Always maintain consistency with project documentation in .bmad-core/\n`;
1966
+ mdContent += `- Follow the agent's specific guidelines and constraints\n`;
1967
+ mdContent += `- Update relevant project files when making changes\n`;
1968
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
1969
+ mdContent += `- Reference the complete agent definition in [${relativePath}](${relativePath})\n\n`;
1970
+ mdContent += '## Usage\n\n';
1971
+ mdContent += `Type \`@${agentId}\` to activate this ${await this.getAgentTitle(agentId, installDir)} persona.\n`;
1972
+
1973
+ await fileManager.writeFile(mdPath, mdContent);
1974
+ console.log(chalk.green(`✓ Created rule: ${prefix}-${agentId}.md`));
1975
+ }
1976
+ }
1977
+
1978
+ console.log(chalk.green(`\n✓ Created Cline rules in ${clineRulesDir}`));
1979
+
1980
+ return true;
1981
+ }
1982
+
1983
+ async setupGeminiCli(installDir, selectedAgent) {
1984
+ const ideConfig = await configLoader.getIdeConfiguration('gemini');
1985
+ const bmadCommandsDir = path.join(installDir, ideConfig['rule-dir']);
1986
+
1987
+ const agentCommandsDir = path.join(bmadCommandsDir, 'agents');
1988
+ const taskCommandsDir = path.join(bmadCommandsDir, 'tasks');
1989
+ await fileManager.ensureDirectory(agentCommandsDir);
1990
+ await fileManager.ensureDirectory(taskCommandsDir);
1991
+
1992
+ // Process Agents
1993
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
1994
+ for (const agentId of agents) {
1995
+ const agentPath = await this.findAgentPath(agentId, installDir);
1996
+ if (!agentPath) {
1997
+ console.log(chalk.yellow(`✗ Agent file not found for ${agentId}, skipping.`));
1998
+ continue;
1999
+ }
2000
+
2001
+ const agentTitle = await this.getAgentTitle(agentId, installDir);
2002
+ const commandPath = path.join(agentCommandsDir, `${agentId}.toml`);
2003
+
2004
+ // Get relative path from installDir to agent file for @{file} reference
2005
+ const relativeAgentPath = path.relative(installDir, agentPath).replaceAll('\\', '/');
2006
+
2007
+ const tomlContent = `description = "Activates the ${agentTitle} agent from the BMad Method."
2008
+ prompt = """
2009
+ CRITICAL: You are now the BMad '${agentTitle}' agent. Adopt its persona, follow its instructions, and use its capabilities. The full agent definition is below.
2010
+
2011
+ @{${relativeAgentPath}}
2012
+ """`;
2013
+
2014
+ await fileManager.writeFile(commandPath, tomlContent);
2015
+ console.log(chalk.green(`✓ Created agent command: /bmad:agents:${agentId}`));
2016
+ }
2017
+
2018
+ // Process Tasks
2019
+ const tasks = await this.getAllTaskIds(installDir);
2020
+ for (const taskId of tasks) {
2021
+ const taskPath = await this.findTaskPath(taskId, installDir);
2022
+ if (!taskPath) {
2023
+ console.log(chalk.yellow(`✗ Task file not found for ${taskId}, skipping.`));
2024
+ continue;
2025
+ }
2026
+
2027
+ const taskTitle = taskId
2028
+ .split('-')
2029
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
2030
+ .join(' ');
2031
+ const commandPath = path.join(taskCommandsDir, `${taskId}.toml`);
2032
+
2033
+ // Get relative path from installDir to task file for @{file} reference
2034
+ const relativeTaskPath = path.relative(installDir, taskPath).replaceAll('\\', '/');
2035
+
2036
+ const tomlContent = `description = "Executes the BMad Task: ${taskTitle}"
2037
+ prompt = """
2038
+ CRITICAL: You are to execute the BMad Task defined below.
2039
+
2040
+ @{${relativeTaskPath}}
2041
+ """`;
2042
+
2043
+ await fileManager.writeFile(commandPath, tomlContent);
2044
+ console.log(chalk.green(`✓ Created task command: /bmad:tasks:${taskId}`));
2045
+ }
2046
+
2047
+ console.log(
2048
+ chalk.green(`
2049
+ ✓ Created Gemini CLI extension in ${bmadCommandsDir}`),
2050
+ );
2051
+ console.log(
2052
+ chalk.dim('You can now use commands like /bmad:agents:dev or /bmad:tasks:create-doc.'),
2053
+ );
2054
+
2055
+ return true;
2056
+ }
2057
+
2058
+ async setupQwenCode(installDir, selectedAgent) {
2059
+ const qwenDir = path.join(installDir, '.qwen');
2060
+ const bmadMethodDir = path.join(qwenDir, 'bmad-method');
2061
+ await fileManager.ensureDirectory(bmadMethodDir);
2062
+
2063
+ // Update logic for existing settings.json
2064
+ const settingsPath = path.join(qwenDir, 'settings.json');
2065
+ if (await fileManager.pathExists(settingsPath)) {
2066
+ try {
2067
+ const settingsContent = await fileManager.readFile(settingsPath);
2068
+ const settings = JSON.parse(settingsContent);
2069
+ let updated = false;
2070
+
2071
+ // Handle contextFileName property
2072
+ if (settings.contextFileName && Array.isArray(settings.contextFileName)) {
2073
+ const originalLength = settings.contextFileName.length;
2074
+ settings.contextFileName = settings.contextFileName.filter(
2075
+ (fileName) => !fileName.startsWith('agents/'),
2076
+ );
2077
+ if (settings.contextFileName.length !== originalLength) {
2078
+ updated = true;
2079
+ }
2080
+ }
2081
+
2082
+ if (updated) {
2083
+ await fileManager.writeFile(settingsPath, JSON.stringify(settings, null, 2));
2084
+ console.log(chalk.green('✓ Updated .qwen/settings.json - removed agent file references'));
2085
+ }
2086
+ } catch (error) {
2087
+ console.warn(chalk.yellow('Could not update .qwen/settings.json'), error);
2088
+ }
2089
+ }
2090
+
2091
+ // Remove old agents directory
2092
+ const agentsDir = path.join(qwenDir, 'agents');
2093
+ if (await fileManager.pathExists(agentsDir)) {
2094
+ await fileManager.removeDirectory(agentsDir);
2095
+ console.log(chalk.green('✓ Removed old .qwen/agents directory'));
2096
+ }
2097
+
2098
+ // Get all available agents
2099
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
2100
+ let concatenatedContent = '';
2101
+
2102
+ for (const agentId of agents) {
2103
+ // Find the source agent file
2104
+ const agentPath = await this.findAgentPath(agentId, installDir);
2105
+
2106
+ if (agentPath) {
2107
+ const agentContent = await fileManager.readFile(agentPath);
2108
+
2109
+ // Create properly formatted agent rule content (similar to gemini)
2110
+ let agentRuleContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
2111
+ agentRuleContent += `This rule is triggered when the user types \`*${agentId}\` and activates the ${await this.getAgentTitle(
2112
+ agentId,
2113
+ installDir,
2114
+ )} agent persona.\n\n`;
2115
+ agentRuleContent += '## Agent Activation\n\n';
2116
+ agentRuleContent +=
2117
+ 'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n';
2118
+ agentRuleContent += '```yaml\n';
2119
+ // Extract just the YAML content from the agent file
2120
+ const yamlContent = extractYamlFromAgent(agentContent);
2121
+ if (yamlContent) {
2122
+ agentRuleContent += yamlContent;
2123
+ } else {
2124
+ // If no YAML found, include the whole content minus the header
2125
+ agentRuleContent += agentContent.replace(/^#.*$/m, '').trim();
2126
+ }
2127
+ agentRuleContent += '\n```\n\n';
2128
+ agentRuleContent += '## File Reference\n\n';
2129
+ const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
2130
+ agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
2131
+ agentRuleContent += '## Usage\n\n';
2132
+ agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
2133
+ agentId,
2134
+ installDir,
2135
+ )} persona and follow all instructions defined in the YAML configuration above.\n`;
2136
+
2137
+ // Add to concatenated content with separator
2138
+ concatenatedContent += agentRuleContent + '\n\n---\n\n';
2139
+ console.log(chalk.green(`✓ Added context for *${agentId}`));
2140
+ }
2141
+ }
2142
+
2143
+ // Write the concatenated content to QWEN.md
2144
+ const qwenMdPath = path.join(bmadMethodDir, 'QWEN.md');
2145
+ await fileManager.writeFile(qwenMdPath, concatenatedContent);
2146
+ console.log(chalk.green(`\n✓ Created QWEN.md in ${bmadMethodDir}`));
2147
+
2148
+ return true;
2149
+ }
2150
+
2151
+ async setupGitHubCopilot(
2152
+ installDir,
2153
+ selectedAgent,
2154
+ spinner = null,
2155
+ preConfiguredSettings = null,
2156
+ ) {
2157
+ // Configure VS Code workspace settings first to avoid UI conflicts with loading spinners
2158
+ await this.configureVsCodeSettings(installDir, spinner, preConfiguredSettings);
2159
+
2160
+ const chatmodesDir = path.join(installDir, '.github', 'chatmodes');
2161
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
2162
+
2163
+ await fileManager.ensureDirectory(chatmodesDir);
2164
+
2165
+ for (const agentId of agents) {
2166
+ // Find the agent file
2167
+ const agentPath = await this.findAgentPath(agentId, installDir);
2168
+ const chatmodePath = path.join(chatmodesDir, `${agentId}.chatmode.md`);
2169
+
2170
+ if (agentPath) {
2171
+ // Create chat mode file with agent content
2172
+ const agentContent = await fileManager.readFile(agentPath);
2173
+ const agentTitle = await this.getAgentTitle(agentId, installDir);
2174
+
2175
+ // Extract whenToUse for the description
2176
+ const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
2177
+ let description = `Activates the ${agentTitle} agent persona.`;
2178
+ if (yamlMatch) {
2179
+ const whenToUseMatch = yamlMatch[1].match(/whenToUse:\s*"(.*?)"/);
2180
+ if (whenToUseMatch && whenToUseMatch[1]) {
2181
+ description = whenToUseMatch[1];
2182
+ }
2183
+ }
2184
+
2185
+ let chatmodeContent = `---
2186
+ description: "${description.replaceAll('"', String.raw`\"`)}"
2187
+ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems', 'usages', 'editFiles', 'runCommands', 'runTasks', 'runTests', 'search', 'searchResults', 'terminalLastCommand', 'terminalSelection', 'testFailure']
2188
+ ---
2189
+
2190
+ `;
2191
+ chatmodeContent += agentContent;
2192
+
2193
+ await fileManager.writeFile(chatmodePath, chatmodeContent);
2194
+ console.log(chalk.green(`✓ Created chat mode: ${agentId}.chatmode.md`));
2195
+ }
2196
+ }
2197
+
2198
+ console.log(chalk.green(`\n✓ Github Copilot setup complete!`));
2199
+ console.log(chalk.dim(`You can now find the BMad agents in the Chat view's mode selector.`));
2200
+
2201
+ return true;
2202
+ }
2203
+
2204
+ async configureVsCodeSettings(installDir, spinner, preConfiguredSettings = null) {
2205
+ const vscodeDir = path.join(installDir, '.vscode');
2206
+ const settingsPath = path.join(vscodeDir, 'settings.json');
2207
+
2208
+ await fileManager.ensureDirectory(vscodeDir);
2209
+
2210
+ // Read existing settings if they exist
2211
+ let existingSettings = {};
2212
+ if (await fileManager.pathExists(settingsPath)) {
2213
+ try {
2214
+ const existingContent = await fileManager.readFile(settingsPath);
2215
+ existingSettings = JSON.parse(existingContent);
2216
+ console.log(chalk.yellow('Found existing .vscode/settings.json. Merging BMad settings...'));
2217
+ } catch {
2218
+ console.warn(chalk.yellow('Could not parse existing settings.json. Creating new one.'));
2219
+ existingSettings = {};
2220
+ }
2221
+ }
2222
+
2223
+ // Use pre-configured settings if provided, otherwise prompt
2224
+ let configChoice;
2225
+ if (preConfiguredSettings && preConfiguredSettings.configChoice) {
2226
+ configChoice = preConfiguredSettings.configChoice;
2227
+ console.log(chalk.dim(`Using pre-configured GitHub Copilot settings: ${configChoice}`));
2228
+ } else {
2229
+ // Clear any previous output and add spacing to avoid conflicts with loaders
2230
+ console.log('\n'.repeat(2));
2231
+ console.log(chalk.blue('🔧 Github Copilot Agent Settings Configuration'));
2232
+ console.log(
2233
+ chalk.dim('BMad works best with specific VS Code settings for optimal agent experience.'),
2234
+ );
2235
+ console.log(''); // Add extra spacing
2236
+
2237
+ const response = await inquirer.prompt([
2238
+ {
2239
+ type: 'list',
2240
+ name: 'configChoice',
2241
+ message: chalk.yellow('How would you like to configure GitHub Copilot settings?'),
2242
+ choices: [
2243
+ {
2244
+ name: 'Use recommended defaults (fastest setup)',
2245
+ value: 'defaults',
2246
+ },
2247
+ {
2248
+ name: 'Configure each setting manually (customize to your preferences)',
2249
+ value: 'manual',
2250
+ },
2251
+ {
2252
+ name: "Skip settings configuration (I'll configure manually later)",
2253
+ value: 'skip',
2254
+ },
2255
+ ],
2256
+ default: 'defaults',
2257
+ },
2258
+ ]);
2259
+ configChoice = response.configChoice;
2260
+ }
2261
+
2262
+ let bmadSettings = {};
2263
+
2264
+ if (configChoice === 'skip') {
2265
+ console.log(chalk.yellow('⚠️ Skipping VS Code settings configuration.'));
2266
+ console.log(chalk.dim('You can manually configure these settings in .vscode/settings.json:'));
2267
+ console.log(chalk.dim(' • chat.agent.enabled: true'));
2268
+ console.log(chalk.dim(' • chat.agent.maxRequests: 15'));
2269
+ console.log(chalk.dim(' • github.copilot.chat.agent.runTasks: true'));
2270
+ console.log(chalk.dim(' • chat.mcp.discovery.enabled: true'));
2271
+ console.log(chalk.dim(' • github.copilot.chat.agent.autoFix: true'));
2272
+ console.log(chalk.dim(' • chat.tools.autoApprove: false'));
2273
+ return true;
2274
+ }
2275
+
2276
+ if (configChoice === 'defaults') {
2277
+ // Use recommended defaults
2278
+ bmadSettings = {
2279
+ 'chat.agent.enabled': true,
2280
+ 'chat.agent.maxRequests': 15,
2281
+ 'github.copilot.chat.agent.runTasks': true,
2282
+ 'chat.mcp.discovery.enabled': true,
2283
+ 'github.copilot.chat.agent.autoFix': true,
2284
+ 'chat.tools.autoApprove': false,
2285
+ };
2286
+ console.log(chalk.green('✓ Using recommended BMad defaults for Github Copilot settings'));
2287
+ } else {
2288
+ // Manual configuration
2289
+ console.log(chalk.blue("\n📋 Let's configure each setting for your preferences:"));
2290
+
2291
+ // Pause spinner during manual configuration prompts
2292
+ let spinnerWasActive = false;
2293
+ if (spinner && spinner.isSpinning) {
2294
+ spinner.stop();
2295
+ spinnerWasActive = true;
2296
+ }
2297
+
2298
+ const manualSettings = await inquirer.prompt([
2299
+ {
2300
+ type: 'input',
2301
+ name: 'maxRequests',
2302
+ message: 'Maximum requests per agent session (recommended: 15)?',
2303
+ default: '15',
2304
+ validate: (input) => {
2305
+ const number_ = Number.parseInt(input);
2306
+ if (isNaN(number_) || number_ < 1 || number_ > 50) {
2307
+ return 'Please enter a number between 1 and 50';
2308
+ }
2309
+ return true;
2310
+ },
2311
+ },
2312
+ {
2313
+ type: 'confirm',
2314
+ name: 'runTasks',
2315
+ message: 'Allow agents to run workspace tasks (package.json scripts, etc.)?',
2316
+ default: true,
2317
+ },
2318
+ {
2319
+ type: 'confirm',
2320
+ name: 'mcpDiscovery',
2321
+ message: 'Enable MCP (Model Context Protocol) server discovery?',
2322
+ default: true,
2323
+ },
2324
+ {
2325
+ type: 'confirm',
2326
+ name: 'autoFix',
2327
+ message: 'Enable automatic error detection and fixing in generated code?',
2328
+ default: true,
2329
+ },
2330
+ {
2331
+ type: 'confirm',
2332
+ name: 'autoApprove',
2333
+ message: 'Auto-approve ALL tools without confirmation? (⚠️ EXPERIMENTAL - less secure)',
2334
+ default: false,
2335
+ },
2336
+ ]);
2337
+
2338
+ // Restart spinner if it was active before prompts
2339
+ if (spinner && spinnerWasActive) {
2340
+ spinner.start();
2341
+ }
2342
+
2343
+ bmadSettings = {
2344
+ 'chat.agent.enabled': true, // Always enabled - required for BMad agents
2345
+ 'chat.agent.maxRequests': Number.parseInt(manualSettings.maxRequests),
2346
+ 'github.copilot.chat.agent.runTasks': manualSettings.runTasks,
2347
+ 'chat.mcp.discovery.enabled': manualSettings.mcpDiscovery,
2348
+ 'github.copilot.chat.agent.autoFix': manualSettings.autoFix,
2349
+ 'chat.tools.autoApprove': manualSettings.autoApprove,
2350
+ };
2351
+
2352
+ console.log(chalk.green('✓ Custom settings configured'));
2353
+ }
2354
+
2355
+ // Merge settings (existing settings take precedence to avoid overriding user preferences)
2356
+ const mergedSettings = { ...bmadSettings, ...existingSettings };
2357
+
2358
+ // Write the updated settings
2359
+ await fileManager.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
2360
+
2361
+ console.log(chalk.green('✓ VS Code workspace settings configured successfully'));
2362
+ console.log(chalk.dim(' Settings written to .vscode/settings.json:'));
2363
+ for (const [key, value] of Object.entries(bmadSettings)) {
2364
+ console.log(chalk.dim(` • ${key}: ${value}`));
2365
+ }
2366
+ console.log(chalk.dim(''));
2367
+ console.log(chalk.dim('You can modify these settings anytime in .vscode/settings.json'));
2368
+ }
2369
+
2370
+ async setupAuggieCLI(installDir, selectedAgent, spinner = null, preConfiguredSettings = null) {
2371
+ const os = require('node:os');
2372
+ const inquirer = require('inquirer');
2373
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
2374
+
2375
+ // Get the IDE configuration to access location options
2376
+ const ideConfig = await configLoader.getIdeConfiguration('auggie-cli');
2377
+ const locations = ideConfig.locations;
2378
+
2379
+ // Use pre-configured settings if provided, otherwise prompt
2380
+ let selectedLocations;
2381
+ if (preConfiguredSettings && preConfiguredSettings.selectedLocations) {
2382
+ selectedLocations = preConfiguredSettings.selectedLocations;
2383
+ console.log(
2384
+ chalk.dim(
2385
+ `Using pre-configured Auggie CLI (Augment Code) locations: ${selectedLocations.join(', ')}`,
2386
+ ),
2387
+ );
2388
+ } else {
2389
+ // Pause spinner during location selection to avoid UI conflicts
2390
+ let spinnerWasActive = false;
2391
+ if (spinner && spinner.isSpinning) {
2392
+ spinner.stop();
2393
+ spinnerWasActive = true;
2394
+ }
2395
+
2396
+ // Clear any previous output and add spacing to avoid conflicts with loaders
2397
+ console.log('\n'.repeat(2));
2398
+ console.log(chalk.blue('📍 Auggie CLI Location Configuration'));
2399
+ console.log(chalk.dim('Choose where to install BMad agents for Auggie CLI access.'));
2400
+ console.log(''); // Add extra spacing
2401
+
2402
+ const response = await inquirer.prompt([
2403
+ {
2404
+ type: 'checkbox',
2405
+ name: 'selectedLocations',
2406
+ message: 'Select Auggie CLI command locations:',
2407
+ choices: Object.entries(locations).map(([key, location]) => ({
2408
+ name: `${location.name}: ${location.description}`,
2409
+ value: key,
2410
+ })),
2411
+ validate: (selected) => {
2412
+ if (selected.length === 0) {
2413
+ return 'Please select at least one location';
2414
+ }
2415
+ return true;
2416
+ },
2417
+ },
2418
+ ]);
2419
+ selectedLocations = response.selectedLocations;
2420
+
2421
+ // Restart spinner if it was active before prompts
2422
+ if (spinner && spinnerWasActive) {
2423
+ spinner.start();
2424
+ }
2425
+ }
2426
+
2427
+ // Install to each selected location
2428
+ for (const locationKey of selectedLocations) {
2429
+ const location = locations[locationKey];
2430
+ let commandsDir = location['rule-dir'];
2431
+
2432
+ // Handle tilde expansion for user directory
2433
+ if (commandsDir.startsWith('~/')) {
2434
+ commandsDir = path.join(os.homedir(), commandsDir.slice(2));
2435
+ } else if (commandsDir.startsWith('./')) {
2436
+ commandsDir = path.join(installDir, commandsDir.slice(2));
2437
+ }
2438
+
2439
+ await fileManager.ensureDirectory(commandsDir);
2440
+
2441
+ for (const agentId of agents) {
2442
+ // Find the agent file
2443
+ const agentPath = await this.findAgentPath(agentId, installDir);
2444
+
2445
+ if (agentPath) {
2446
+ const agentContent = await fileManager.readFile(agentPath);
2447
+ const mdPath = path.join(commandsDir, `${agentId}.md`);
2448
+ await fileManager.writeFile(mdPath, agentContent);
2449
+ console.log(chalk.green(`✓ Created command: ${agentId}.md in ${location.name}`));
2450
+ }
2451
+ }
2452
+
2453
+ console.log(chalk.green(`\n✓ Created Auggie CLI commands in ${commandsDir}`));
2454
+ console.log(chalk.dim(` Location: ${location.name} - ${location.description}`));
2455
+ }
2456
+
2457
+ return true;
2458
+ }
2459
+ }
2460
+
2461
+ module.exports = new IdeSetup();