@zeyue0329/xiaoma-cli 1.0.7 → 1.0.9

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 (310) hide show
  1. package/CHANGELOG.md +686 -0
  2. package/LICENSE +6 -1
  3. package/README.md +173 -460
  4. package/common/tasks/create-doc.md +3 -1
  5. package/common/tasks/execute-checklist.md +2 -7
  6. package/common/utils/bmad-doc-template.md +7 -5
  7. package/common/utils/workflow-management.md +2 -0
  8. package/dist/agents/analyst.txt +1143 -1118
  9. package/dist/agents/architect.txt +1555 -1531
  10. package/dist/agents/dev.txt +170 -22
  11. package/dist/agents/pm.txt +1103 -1106
  12. package/dist/agents/po.txt +329 -334
  13. package/dist/agents/qa.txt +1773 -154
  14. package/dist/agents/sm.txt +101 -102
  15. package/dist/agents/ux-expert.txt +93 -91
  16. package/dist/agents/xiaoma-master.txt +745 -673
  17. package/dist/agents/xiaoma-orchestrator.txt +107 -77
  18. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +2386 -0
  19. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +1627 -0
  20. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +822 -0
  21. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +11008 -0
  22. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +4031 -0
  23. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +3717 -0
  24. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +456 -0
  25. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +982 -0
  26. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +15450 -0
  27. package/dist/expansion-packs/bmad-creative-writing/agents/beta-reader.txt +921 -0
  28. package/dist/expansion-packs/bmad-creative-writing/agents/book-critic.txt +81 -0
  29. package/dist/expansion-packs/bmad-creative-writing/agents/character-psychologist.txt +886 -0
  30. package/dist/expansion-packs/bmad-creative-writing/agents/cover-designer.txt +85 -0
  31. package/dist/expansion-packs/bmad-creative-writing/agents/dialog-specialist.txt +903 -0
  32. package/dist/expansion-packs/bmad-creative-writing/agents/editor.txt +837 -0
  33. package/dist/expansion-packs/bmad-creative-writing/agents/genre-specialist.txt +989 -0
  34. package/dist/expansion-packs/bmad-creative-writing/agents/narrative-designer.txt +888 -0
  35. package/dist/expansion-packs/bmad-creative-writing/agents/plot-architect.txt +1173 -0
  36. package/dist/expansion-packs/bmad-creative-writing/agents/world-builder.txt +914 -0
  37. package/dist/expansion-packs/bmad-creative-writing/teams/agent-team.txt +6511 -0
  38. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2087 -0
  39. package/dist/teams/team-all.txt +5710 -3857
  40. package/dist/teams/team-fullstack.txt +3242 -3157
  41. package/dist/teams/team-ide-minimal.txt +2330 -534
  42. package/dist/teams/team-no-ui.txt +2935 -2857
  43. package/docs/GUIDING-PRINCIPLES.md +91 -0
  44. package/docs/core-architecture.md +219 -0
  45. package/docs/enhanced-ide-development-workflow.md +248 -0
  46. package/docs/expansion-packs.md +200 -0
  47. package/docs/how-to-contribute-with-pull-requests.md +158 -0
  48. package/docs/user-guide.md +530 -0
  49. package/docs/versioning-and-releases.md +155 -0
  50. package/docs/versions.md +48 -0
  51. package/docs/working-in-the-brownfield.md +597 -0
  52. package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +14 -0
  53. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +73 -0
  54. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +80 -0
  55. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +66 -0
  56. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +203 -0
  57. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +162 -0
  58. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +9 -0
  59. package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +252 -0
  60. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +649 -0
  61. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +112 -0
  62. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +218 -0
  63. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +292 -0
  64. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +614 -0
  65. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +357 -0
  66. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +344 -0
  67. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +254 -0
  68. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +485 -0
  69. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +184 -0
  70. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +176 -0
  71. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +15 -0
  72. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +82 -0
  73. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +79 -0
  74. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +80 -0
  75. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +67 -0
  76. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +393 -0
  77. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +205 -0
  78. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +203 -0
  79. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +126 -0
  80. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +7 -0
  81. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +771 -0
  82. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +588 -0
  83. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +112 -0
  84. package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +143 -0
  85. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +186 -0
  86. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +292 -0
  87. package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +202 -0
  88. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +1031 -0
  89. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +357 -0
  90. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +706 -0
  91. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +257 -0
  92. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +485 -0
  93. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +184 -0
  94. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +176 -0
  95. package/expansion-packs/bmad-creative-writing/README.md +146 -0
  96. package/expansion-packs/bmad-creative-writing/agent-teams/agent-team.yaml +20 -0
  97. package/expansion-packs/bmad-creative-writing/agents/beta-reader.md +94 -0
  98. package/expansion-packs/bmad-creative-writing/agents/book-critic.md +40 -0
  99. package/expansion-packs/bmad-creative-writing/agents/character-psychologist.md +93 -0
  100. package/expansion-packs/bmad-creative-writing/agents/cover-designer.md +46 -0
  101. package/expansion-packs/bmad-creative-writing/agents/dialog-specialist.md +92 -0
  102. package/expansion-packs/bmad-creative-writing/agents/editor.md +93 -0
  103. package/expansion-packs/bmad-creative-writing/agents/genre-specialist.md +95 -0
  104. package/expansion-packs/bmad-creative-writing/agents/narrative-designer.md +93 -0
  105. package/expansion-packs/bmad-creative-writing/agents/plot-architect.md +95 -0
  106. package/expansion-packs/bmad-creative-writing/agents/world-builder.md +94 -0
  107. package/expansion-packs/bmad-creative-writing/checklists/beta-feedback-closure-checklist.md +23 -0
  108. package/expansion-packs/bmad-creative-writing/checklists/character-consistency-checklist.md +23 -0
  109. package/expansion-packs/bmad-creative-writing/checklists/comedic-timing-checklist.md +23 -0
  110. package/expansion-packs/bmad-creative-writing/checklists/cyberpunk-aesthetic-checklist.md +23 -0
  111. package/expansion-packs/bmad-creative-writing/checklists/ebook-formatting-checklist.md +21 -0
  112. package/expansion-packs/bmad-creative-writing/checklists/epic-poetry-meter-checklist.md +23 -0
  113. package/expansion-packs/bmad-creative-writing/checklists/fantasy-magic-system-checklist.md +23 -0
  114. package/expansion-packs/bmad-creative-writing/checklists/foreshadowing-payoff-checklist.md +22 -0
  115. package/expansion-packs/bmad-creative-writing/checklists/genre-tropes-checklist.md +22 -0
  116. package/expansion-packs/bmad-creative-writing/checklists/historical-accuracy-checklist.md +23 -0
  117. package/expansion-packs/bmad-creative-writing/checklists/horror-suspense-checklist.md +23 -0
  118. package/expansion-packs/bmad-creative-writing/checklists/kdp-cover-ready-checklist.md +25 -0
  119. package/expansion-packs/bmad-creative-writing/checklists/line-edit-quality-checklist.md +23 -0
  120. package/expansion-packs/bmad-creative-writing/checklists/marketing-copy-checklist.md +23 -0
  121. package/expansion-packs/bmad-creative-writing/checklists/mystery-clue-trail-checklist.md +23 -0
  122. package/expansion-packs/bmad-creative-writing/checklists/orbital-mechanics-checklist.md +23 -0
  123. package/expansion-packs/bmad-creative-writing/checklists/plot-structure-checklist.md +59 -0
  124. package/expansion-packs/bmad-creative-writing/checklists/publication-readiness-checklist.md +23 -0
  125. package/expansion-packs/bmad-creative-writing/checklists/romance-emotional-beats-checklist.md +23 -0
  126. package/expansion-packs/bmad-creative-writing/checklists/scene-quality-checklist.md +23 -0
  127. package/expansion-packs/bmad-creative-writing/checklists/scifi-technology-plausibility-checklist.md +22 -0
  128. package/expansion-packs/bmad-creative-writing/checklists/sensitivity-representation-checklist.md +23 -0
  129. package/expansion-packs/bmad-creative-writing/checklists/steampunk-gadget-checklist.md +23 -0
  130. package/expansion-packs/bmad-creative-writing/checklists/thriller-pacing-stakes-checklist.md +23 -0
  131. package/expansion-packs/bmad-creative-writing/checklists/timeline-continuity-checklist.md +23 -0
  132. package/expansion-packs/bmad-creative-writing/checklists/world-building-continuity-checklist.md +23 -0
  133. package/expansion-packs/bmad-creative-writing/checklists/ya-appropriateness-checklist.md +23 -0
  134. package/expansion-packs/bmad-creative-writing/config.yaml +12 -0
  135. package/expansion-packs/bmad-creative-writing/data/bmad-kb.md +209 -0
  136. package/expansion-packs/bmad-creative-writing/data/story-structures.md +67 -0
  137. package/expansion-packs/bmad-creative-writing/docs/brief.md +212 -0
  138. package/expansion-packs/bmad-creative-writing/tasks/advanced-elicitation.md +119 -0
  139. package/expansion-packs/bmad-creative-writing/tasks/analyze-reader-feedback.md +23 -0
  140. package/expansion-packs/bmad-creative-writing/tasks/analyze-story-structure.md +67 -0
  141. package/expansion-packs/bmad-creative-writing/tasks/assemble-kdp-package.md +29 -0
  142. package/expansion-packs/bmad-creative-writing/tasks/brainstorm-premise.md +23 -0
  143. package/expansion-packs/bmad-creative-writing/tasks/build-world.md +24 -0
  144. package/expansion-packs/bmad-creative-writing/tasks/character-depth-pass.md +22 -0
  145. package/expansion-packs/bmad-creative-writing/tasks/create-doc.md +103 -0
  146. package/expansion-packs/bmad-creative-writing/tasks/create-draft-section.md +26 -0
  147. package/expansion-packs/bmad-creative-writing/tasks/critical-review.md +26 -0
  148. package/expansion-packs/bmad-creative-writing/tasks/develop-character.md +24 -0
  149. package/expansion-packs/bmad-creative-writing/tasks/execute-checklist.md +88 -0
  150. package/expansion-packs/bmad-creative-writing/tasks/expand-premise.md +23 -0
  151. package/expansion-packs/bmad-creative-writing/tasks/expand-synopsis.md +23 -0
  152. package/expansion-packs/bmad-creative-writing/tasks/final-polish.md +23 -0
  153. package/expansion-packs/bmad-creative-writing/tasks/generate-cover-brief.md +25 -0
  154. package/expansion-packs/bmad-creative-writing/tasks/generate-cover-prompts.md +26 -0
  155. package/expansion-packs/bmad-creative-writing/tasks/generate-scene-list.md +23 -0
  156. package/expansion-packs/bmad-creative-writing/tasks/incorporate-feedback.md +25 -0
  157. package/expansion-packs/bmad-creative-writing/tasks/outline-scenes.md +23 -0
  158. package/expansion-packs/bmad-creative-writing/tasks/provide-feedback.md +24 -0
  159. package/expansion-packs/bmad-creative-writing/tasks/publish-chapter.md +23 -0
  160. package/expansion-packs/bmad-creative-writing/tasks/quick-feedback.md +22 -0
  161. package/expansion-packs/bmad-creative-writing/tasks/select-next-arc.md +23 -0
  162. package/expansion-packs/bmad-creative-writing/tasks/workshop-dialog.md +64 -0
  163. package/expansion-packs/bmad-creative-writing/templates/beta-feedback-form.yaml +97 -0
  164. package/expansion-packs/bmad-creative-writing/templates/chapter-draft-tmpl.yaml +82 -0
  165. package/expansion-packs/bmad-creative-writing/templates/character-profile-tmpl.yaml +92 -0
  166. package/expansion-packs/bmad-creative-writing/templates/cover-design-brief-tmpl.yaml +98 -0
  167. package/expansion-packs/bmad-creative-writing/templates/premise-brief-tmpl.yaml +78 -0
  168. package/expansion-packs/bmad-creative-writing/templates/scene-list-tmpl.yaml +55 -0
  169. package/expansion-packs/bmad-creative-writing/templates/story-outline-tmpl.yaml +96 -0
  170. package/expansion-packs/bmad-creative-writing/templates/world-guide-tmpl.yaml +89 -0
  171. package/expansion-packs/bmad-creative-writing/workflows/book-cover-design-workflow.md +218 -0
  172. package/expansion-packs/bmad-creative-writing/workflows/novel-greenfield-workflow.yaml +56 -0
  173. package/expansion-packs/bmad-creative-writing/workflows/novel-serial-workflow.yaml +50 -0
  174. package/expansion-packs/bmad-creative-writing/workflows/novel-snowflake-workflow.yaml +69 -0
  175. package/expansion-packs/bmad-creative-writing/workflows/novel-writing.yaml +91 -0
  176. package/expansion-packs/bmad-creative-writing/workflows/screenplay-development.yaml +85 -0
  177. package/expansion-packs/bmad-creative-writing/workflows/series-planning.yaml +78 -0
  178. package/expansion-packs/bmad-creative-writing/workflows/short-story-creation.yaml +64 -0
  179. package/expansion-packs/bmad-infrastructure-devops/README.md +147 -0
  180. package/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.md +73 -0
  181. package/expansion-packs/bmad-infrastructure-devops/checklists/infrastructure-checklist.md +486 -0
  182. package/expansion-packs/bmad-infrastructure-devops/config.yaml +10 -0
  183. package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +307 -0
  184. package/expansion-packs/bmad-infrastructure-devops/tasks/review-infrastructure.md +161 -0
  185. package/expansion-packs/bmad-infrastructure-devops/tasks/validate-infrastructure.md +155 -0
  186. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +425 -0
  187. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +630 -0
  188. package/implement-fork-friendly-ci.sh +229 -0
  189. package/package.json +75 -45
  190. package/prettier.config.mjs +32 -0
  191. package/test.md +1 -0
  192. package/tools/builders/web-builder.js +128 -129
  193. package/tools/bump-all-versions.js +42 -33
  194. package/tools/bump-expansion-version.js +23 -16
  195. package/tools/cli.js +15 -15
  196. package/tools/flattener/aggregate.js +76 -0
  197. package/tools/flattener/binary.js +80 -0
  198. package/tools/flattener/discovery.js +71 -0
  199. package/tools/flattener/files.js +35 -0
  200. package/tools/flattener/ignoreRules.js +176 -0
  201. package/tools/flattener/main.js +458 -460
  202. package/tools/flattener/projectRoot.js +206 -0
  203. package/tools/flattener/prompts.js +44 -0
  204. package/tools/flattener/stats.helpers.js +395 -0
  205. package/tools/flattener/stats.js +80 -0
  206. package/tools/flattener/test-matrix.js +413 -0
  207. package/tools/flattener/xml.js +88 -0
  208. package/tools/installer/README.md +1 -1
  209. package/tools/installer/bin/xiaoma.js +380 -87
  210. package/tools/installer/config/ide-agent-config.yaml +3 -3
  211. package/tools/installer/config/install.config.yaml +73 -22
  212. package/tools/installer/lib/config-loader.js +48 -44
  213. package/tools/installer/lib/file-manager.js +91 -113
  214. package/tools/installer/lib/ide-base-setup.js +57 -56
  215. package/tools/installer/lib/ide-setup.js +816 -407
  216. package/tools/installer/lib/installer.js +915 -687
  217. package/tools/installer/lib/memory-profiler.js +54 -53
  218. package/tools/installer/lib/module-manager.js +19 -15
  219. package/tools/installer/lib/resource-locator.js +26 -28
  220. package/tools/installer/package-lock.json +67 -56
  221. package/tools/installer/package.json +24 -23
  222. package/tools/lib/dependency-resolver.js +30 -34
  223. package/tools/lib/yaml-utils.js +7 -7
  224. package/tools/md-assets/web-agent-startup-instructions.md +1 -1
  225. package/tools/preview-release-notes.js +66 -0
  226. package/tools/setup-hooks.sh +37 -0
  227. package/tools/shared/bannerArt.js +105 -0
  228. package/tools/sync-installer-version.js +7 -9
  229. package/tools/sync-version.sh +23 -0
  230. package/tools/update-expansion-version.js +14 -15
  231. package/tools/upgraders/v3-to-v4-upgrader.js +208 -299
  232. package/tools/version-bump.js +41 -26
  233. package/tools/xiaoma-npx-wrapper.js +11 -11
  234. package/tools/yaml-format.js +56 -43
  235. package/xiaoma-core/agent-teams/team-all.yaml +2 -1
  236. package/xiaoma-core/agent-teams/team-fullstack.yaml +1 -0
  237. package/xiaoma-core/agent-teams/team-ide-minimal.yaml +1 -0
  238. package/xiaoma-core/agent-teams/team-no-ui.yaml +1 -0
  239. package/xiaoma-core/agents/analyst.md +20 -17
  240. package/xiaoma-core/agents/architect.md +15 -14
  241. package/xiaoma-core/agents/dev.md +23 -18
  242. package/xiaoma-core/agents/pm.md +18 -15
  243. package/xiaoma-core/agents/po.md +13 -10
  244. package/xiaoma-core/agents/qa.md +46 -24
  245. package/xiaoma-core/agents/sm.md +11 -8
  246. package/xiaoma-core/agents/ux-expert.md +10 -7
  247. package/xiaoma-core/agents/xiaoma-master.md +24 -22
  248. package/xiaoma-core/agents/xiaoma-orchestrator.md +30 -33
  249. package/xiaoma-core/checklists/architect-checklist.md +2 -5
  250. package/xiaoma-core/checklists/change-checklist.md +4 -2
  251. package/xiaoma-core/checklists/pm-checklist.md +2 -5
  252. package/xiaoma-core/checklists/po-master-checklist.md +2 -9
  253. package/xiaoma-core/checklists/story-dod-checklist.md +2 -7
  254. package/xiaoma-core/checklists/story-draft-checklist.md +2 -3
  255. package/xiaoma-core/core-config.yaml +4 -1
  256. package/xiaoma-core/data/{xiaoma-kb.md → bmad-kb.md} +43 -37
  257. package/xiaoma-core/data/brainstorming-techniques.md +2 -0
  258. package/xiaoma-core/data/elicitation-methods.md +22 -0
  259. package/xiaoma-core/data/technical-preferences.md +2 -0
  260. package/xiaoma-core/data/test-levels-framework.md +148 -0
  261. package/xiaoma-core/data/test-priorities-matrix.md +174 -0
  262. package/xiaoma-core/tasks/advanced-elicitation.md +2 -0
  263. package/xiaoma-core/tasks/apply-qa-fixes.md +150 -0
  264. package/xiaoma-core/tasks/brownfield-create-epic.md +2 -0
  265. package/xiaoma-core/tasks/brownfield-create-story.md +2 -0
  266. package/xiaoma-core/tasks/correct-course.md +2 -0
  267. package/xiaoma-core/tasks/create-brownfield-story.md +14 -4
  268. package/xiaoma-core/tasks/create-deep-research-prompt.md +2 -11
  269. package/xiaoma-core/tasks/create-next-story.md +3 -1
  270. package/xiaoma-core/tasks/document-project.md +17 -13
  271. package/xiaoma-core/tasks/facilitate-brainstorming-session.md +5 -3
  272. package/xiaoma-core/tasks/generate-ai-frontend-prompt.md +2 -0
  273. package/xiaoma-core/tasks/index-docs.md +2 -6
  274. package/xiaoma-core/tasks/kb-mode-interaction.md +17 -15
  275. package/xiaoma-core/tasks/nfr-assess.md +345 -0
  276. package/xiaoma-core/tasks/qa-gate.md +163 -0
  277. package/xiaoma-core/tasks/review-story.md +245 -74
  278. package/xiaoma-core/tasks/risk-profile.md +355 -0
  279. package/xiaoma-core/tasks/shard-doc.md +2 -2
  280. package/xiaoma-core/tasks/test-design.md +176 -0
  281. package/xiaoma-core/tasks/trace-requirements.md +266 -0
  282. package/xiaoma-core/tasks/validate-next-story.md +2 -0
  283. package/xiaoma-core/templates/architecture-tmpl.yaml +50 -49
  284. package/xiaoma-core/templates/brainstorming-output-tmpl.yaml +5 -5
  285. package/xiaoma-core/templates/brownfield-architecture-tmpl.yaml +32 -31
  286. package/xiaoma-core/templates/brownfield-prd-tmpl.yaml +14 -13
  287. package/xiaoma-core/templates/competitor-analysis-tmpl.yaml +20 -6
  288. package/xiaoma-core/templates/front-end-architecture-tmpl.yaml +22 -9
  289. package/xiaoma-core/templates/front-end-spec-tmpl.yaml +25 -24
  290. package/xiaoma-core/templates/fullstack-architecture-tmpl.yaml +123 -104
  291. package/xiaoma-core/templates/market-research-tmpl.yaml +3 -2
  292. package/xiaoma-core/templates/prd-tmpl.yaml +10 -9
  293. package/xiaoma-core/templates/project-brief-tmpl.yaml +5 -4
  294. package/xiaoma-core/templates/qa-gate-tmpl.yaml +103 -0
  295. package/xiaoma-core/templates/story-tmpl.yaml +13 -12
  296. package/xiaoma-core/workflows/brownfield-fullstack.yaml +13 -12
  297. package/xiaoma-core/workflows/brownfield-service.yaml +5 -4
  298. package/xiaoma-core/workflows/brownfield-ui.yaml +5 -4
  299. package/xiaoma-core/workflows/greenfield-fullstack.yaml +7 -6
  300. package/xiaoma-core/workflows/greenfield-service.yaml +5 -4
  301. package/xiaoma-core/workflows/greenfield-ui.yaml +6 -5
  302. package/.releaserc.json +0 -18
  303. package/.vscode/settings.json +0 -44
  304. package/docs/quick-start.md +0 -179
  305. package/tools/bmad-npx-wrapper.js +0 -39
  306. package/tools/semantic-release-sync-installer.js +0 -30
  307. package/xiaoma-core/bmad-core/user-guide.md +0 -0
  308. package/xiaoma-core/enhanced-ide-development-workflow.md +0 -43
  309. package/xiaoma-core/user-guide.md +0 -251
  310. package/xiaoma-core/working-in-the-brownfield.md +0 -364
@@ -1,13 +1,13 @@
1
- const path = require("node:path");
2
- const fs = require("fs-extra");
3
- const chalk = require("chalk");
4
- const ora = require("ora");
5
- const inquirer = require("inquirer");
6
- const fileManager = require("./file-manager");
7
- const configLoader = require("./config-loader");
8
- const ideSetup = require("./ide-setup");
9
- const { extractYamlFromAgent } = require("../../lib/yaml-utils");
10
- const resourceLocator = require("./resource-locator");
1
+ const path = require('node:path');
2
+ const fs = require('fs-extra');
3
+ const chalk = require('chalk');
4
+ const ora = require('ora');
5
+ const inquirer = require('inquirer');
6
+ const fileManager = require('./file-manager');
7
+ const configLoader = require('./config-loader');
8
+ const ideSetup = require('./ide-setup');
9
+ const { extractYamlFromAgent } = require('../../lib/yaml-utils');
10
+ const resourceLocator = require('./resource-locator');
11
11
 
12
12
  class Installer {
13
13
  async getCoreVersion() {
@@ -16,29 +16,29 @@ class Installer {
16
16
  const packagePath = path.join(__dirname, '..', '..', '..', 'package.json');
17
17
  const packageJson = require(packagePath);
18
18
  return packageJson.version;
19
- } catch (error) {
19
+ } catch {
20
20
  console.warn("Could not read version from package.json, using 'unknown'");
21
- return "unknown";
21
+ return 'unknown';
22
22
  }
23
23
  }
24
24
 
25
25
  async install(config) {
26
- const spinner = ora("Analyzing installation directory...").start();
27
-
26
+ const spinner = ora('Analyzing installation directory...').start();
27
+
28
28
  try {
29
29
  // Store the original CWD where npx was executed
30
30
  const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
31
-
31
+
32
32
  // Resolve installation directory relative to where the user ran the command
33
- let installDir = path.isAbsolute(config.directory)
34
- ? config.directory
33
+ let installDir = path.isAbsolute(config.directory)
34
+ ? config.directory
35
35
  : path.resolve(originalCwd, config.directory);
36
-
36
+
37
37
  if (path.basename(installDir) === '.xiaoma-core') {
38
38
  // If user points directly to .xiaoma-core, treat its parent as the project root
39
39
  installDir = path.dirname(installDir);
40
40
  }
41
-
41
+
42
42
  // Log resolved path for clarity
43
43
  if (!path.isAbsolute(config.directory)) {
44
44
  spinner.text = `Resolving "${config.directory}" to: ${installDir}`;
@@ -48,7 +48,7 @@ class Installer {
48
48
  if (!(await fileManager.pathExists(installDir))) {
49
49
  spinner.stop();
50
50
  console.log(`\nThe directory ${installDir} does not exist.`);
51
-
51
+
52
52
  const { action } = await inquirer.prompt([
53
53
  {
54
54
  type: 'list',
@@ -57,52 +57,61 @@ class Installer {
57
57
  choices: [
58
58
  {
59
59
  name: 'Create the directory and continue',
60
- value: 'create'
60
+ value: 'create',
61
61
  },
62
62
  {
63
63
  name: 'Choose a different directory',
64
- value: 'change'
64
+ value: 'change',
65
65
  },
66
66
  {
67
67
  name: 'Cancel installation',
68
- value: 'cancel'
69
- }
70
- ]
71
- }
68
+ value: 'cancel',
69
+ },
70
+ ],
71
+ },
72
72
  ]);
73
73
 
74
- if (action === 'cancel') {
74
+ switch (action) {
75
+ case 'cancel': {
75
76
  console.log('Installation cancelled.');
76
- process.exit(0);
77
- } else if (action === 'change') {
78
- const { newDirectory } = await inquirer.prompt([
79
- {
80
- type: 'input',
81
- name: 'newDirectory',
82
- message: 'Enter the new directory path:',
83
- validate: (input) => {
84
- if (!input.trim()) {
85
- return 'Please enter a valid directory path';
86
- }
87
- return true;
88
- }
77
+ process.exit(0);
78
+
79
+ break;
80
+ }
81
+ case 'change': {
82
+ const { newDirectory } = await inquirer.prompt([
83
+ {
84
+ type: 'input',
85
+ name: 'newDirectory',
86
+ message: 'Enter the new directory path:',
87
+ validate: (input) => {
88
+ if (!input.trim()) {
89
+ return 'Please enter a valid directory path';
90
+ }
91
+ return true;
92
+ },
93
+ },
94
+ ]);
95
+ // Preserve the original CWD for the recursive call
96
+ config.directory = newDirectory;
97
+ return await this.install(config); // Recursive call with new directory
98
+ }
99
+ case 'create': {
100
+ try {
101
+ await fileManager.ensureDirectory(installDir);
102
+ console.log(`✓ Created directory: ${installDir}`);
103
+ } catch (error) {
104
+ console.error(`Failed to create directory: ${error.message}`);
105
+ console.error('You may need to check permissions or use a different path.');
106
+ process.exit(1);
89
107
  }
90
- ]);
91
- // Preserve the original CWD for the recursive call
92
- config.directory = newDirectory;
93
- return await this.install(config); // Recursive call with new directory
94
- } else if (action === 'create') {
95
- try {
96
- await fileManager.ensureDirectory(installDir);
97
- console.log(`✓ Created directory: ${installDir}`);
98
- } catch (error) {
99
- console.error(`Failed to create directory: ${error.message}`);
100
- console.error('You may need to check permissions or use a different path.');
101
- process.exit(1);
108
+
109
+ break;
102
110
  }
111
+ // No default
103
112
  }
104
-
105
- spinner.start("Analyzing installation directory...");
113
+
114
+ spinner.start('Analyzing installation directory...');
106
115
  }
107
116
 
108
117
  // If this is an update request from early detection, handle it directly
@@ -121,39 +130,28 @@ class Installer {
121
130
 
122
131
  // Handle different states
123
132
  switch (state.type) {
124
- case "clean":
133
+ case 'clean': {
125
134
  return await this.performFreshInstall(config, installDir, spinner);
135
+ }
126
136
 
127
- case "v4_existing":
128
- return await this.handleExistingV4Installation(
129
- config,
130
- installDir,
131
- state,
132
- spinner
133
- );
137
+ case 'v4_existing': {
138
+ return await this.handleExistingV4Installation(config, installDir, state, spinner);
139
+ }
134
140
 
135
- case "v3_existing":
136
- return await this.handleV3Installation(
137
- config,
138
- installDir,
139
- state,
140
- spinner
141
- );
141
+ case 'v3_existing': {
142
+ return await this.handleV3Installation(config, installDir, state, spinner);
143
+ }
142
144
 
143
- case "unknown_existing":
144
- return await this.handleUnknownInstallation(
145
- config,
146
- installDir,
147
- state,
148
- spinner
149
- );
145
+ case 'unknown_existing': {
146
+ return await this.handleUnknownInstallation(config, installDir, state, spinner);
147
+ }
150
148
  }
151
149
  } catch (error) {
152
150
  // Check if modules were initialized
153
151
  if (spinner) {
154
- spinner.fail("Installation failed");
152
+ spinner.fail('Installation failed');
155
153
  } else {
156
- console.error("Installation failed:", error.message);
154
+ console.error('Installation failed:', error.message);
157
155
  }
158
156
  throw error;
159
157
  }
@@ -161,7 +159,7 @@ class Installer {
161
159
 
162
160
  async detectInstallationState(installDir) {
163
161
  const state = {
164
- type: "clean",
162
+ type: 'clean',
165
163
  hasV4Manifest: false,
166
164
  hasV3Structure: false,
167
165
  hasBmadCore: false,
@@ -176,11 +174,11 @@ class Installer {
176
174
  }
177
175
 
178
176
  // Check for V4 installation (has .xiaoma-core with manifest)
179
- const bmadCorePath = path.join(installDir, ".xiaoma-core");
180
- const manifestPath = path.join(bmadCorePath, "install-manifest.yaml");
177
+ const bmadCorePath = path.join(installDir, '.xiaoma-core');
178
+ const manifestPath = path.join(bmadCorePath, 'install-manifest.yaml');
181
179
 
182
180
  if (await fileManager.pathExists(manifestPath)) {
183
- state.type = "v4_existing";
181
+ state.type = 'v4_existing';
184
182
  state.hasV4Manifest = true;
185
183
  state.hasBmadCore = true;
186
184
  state.manifest = await fileManager.readManifest(installDir);
@@ -188,29 +186,29 @@ class Installer {
188
186
  }
189
187
 
190
188
  // Check for V3 installation (has bmad-agent directory)
191
- const bmadAgentPath = path.join(installDir, "bmad-agent");
189
+ const bmadAgentPath = path.join(installDir, 'bmad-agent');
192
190
  if (await fileManager.pathExists(bmadAgentPath)) {
193
- state.type = "v3_existing";
191
+ state.type = 'v3_existing';
194
192
  state.hasV3Structure = true;
195
193
  return state;
196
194
  }
197
195
 
198
196
  // Check for .xiaoma-core without manifest (broken V4 or manual copy)
199
197
  if (await fileManager.pathExists(bmadCorePath)) {
200
- state.type = "unknown_existing";
198
+ state.type = 'unknown_existing';
201
199
  state.hasBmadCore = true;
202
200
  return state;
203
201
  }
204
202
 
205
203
  // Check if directory has other files
206
- const files = await resourceLocator.findFiles("**/*", {
204
+ const files = await resourceLocator.findFiles('**/*', {
207
205
  cwd: installDir,
208
206
  nodir: true,
209
- ignore: ["**/.git/**", "**/node_modules/**"],
207
+ ignore: ['**/.git/**', '**/node_modules/**'],
210
208
  });
211
209
 
212
210
  if (files.length > 0) {
213
- // Directory has other files, but no XiaoMa installation.
211
+ // Directory has other files, but no BMad installation.
214
212
  // Treat as clean install but record that it isn't empty.
215
213
  state.hasOtherFiles = true;
216
214
  }
@@ -223,153 +221,184 @@ class Installer {
223
221
  }
224
222
 
225
223
  async performFreshInstall(config, installDir, spinner, options = {}) {
226
- spinner.text = "Installing XiaoMa CLI...";
224
+ spinner.text = 'Installing BMad Method...';
227
225
 
228
226
  let files = [];
229
227
 
230
- if (config.installType === "full") {
231
- // Full installation - copy entire .xiaoma-core folder as a subdirectory
232
- spinner.text = "Copying complete .xiaoma-core folder...";
233
- const sourceDir = resourceLocator.getBmadCorePath();
234
- const bmadCoreDestDir = path.join(installDir, ".xiaoma-core");
235
- await fileManager.copyDirectoryWithRootReplacement(sourceDir, bmadCoreDestDir, ".xiaoma-core");
236
-
237
- // Copy common/ items to .xiaoma-core
238
- spinner.text = "Copying common utilities...";
239
- await this.copyCommonItems(installDir, ".xiaoma-core", spinner);
240
-
241
- // Get list of all files for manifest
242
- const foundFiles = await resourceLocator.findFiles("**/*", {
243
- cwd: bmadCoreDestDir,
244
- nodir: true,
245
- ignore: ["**/.git/**", "**/node_modules/**"],
246
- });
247
- files = foundFiles.map((file) => path.join(".xiaoma-core", file));
248
- } else if (config.installType === "single-agent") {
249
- // Single agent installation
250
- spinner.text = `Installing ${config.agent} agent...`;
251
-
252
- // Copy agent file with {root} replacement
253
- const agentPath = configLoader.getAgentPath(config.agent);
254
- const destAgentPath = path.join(
255
- installDir,
256
- ".xiaoma-core",
257
- "agents",
258
- `${config.agent}.md`
259
- );
260
- await fileManager.copyFileWithRootReplacement(agentPath, destAgentPath, ".xiaoma-core");
261
- files.push(`.xiaoma-core/agents/${config.agent}.md`);
228
+ switch (config.installType) {
229
+ case 'full': {
230
+ // Full installation - copy entire .xiaoma-core folder as a subdirectory
231
+ spinner.text = 'Copying complete .xiaoma-core folder...';
232
+ const sourceDir = resourceLocator.getBmadCorePath();
233
+ const bmadCoreDestDir = path.join(installDir, '.xiaoma-core');
234
+ await fileManager.copyDirectoryWithRootReplacement(
235
+ sourceDir,
236
+ bmadCoreDestDir,
237
+ '.xiaoma-core',
238
+ );
262
239
 
263
- // Copy dependencies
264
- const { all: dependencies } = await resourceLocator.getAgentDependencies(
265
- config.agent
266
- );
267
- const sourceBase = resourceLocator.getBmadCorePath();
240
+ // Copy common/ items to .xiaoma-core
241
+ spinner.text = 'Copying common utilities...';
242
+ await this.copyCommonItems(installDir, '.xiaoma-core', spinner);
268
243
 
269
- for (const dep of dependencies) {
270
- spinner.text = `Copying dependency: ${dep}`;
244
+ // Copy documentation files from docs/ to .xiaoma-core
245
+ spinner.text = 'Copying documentation files...';
246
+ await this.copyDocsItems(installDir, '.xiaoma-core', spinner);
271
247
 
272
- if (dep.includes("*")) {
273
- // Handle glob patterns with {root} replacement
274
- const copiedFiles = await fileManager.copyGlobPattern(
275
- dep.replace(".xiaoma-core/", ""),
276
- sourceBase,
277
- path.join(installDir, ".xiaoma-core"),
278
- ".xiaoma-core"
279
- );
280
- files.push(...copiedFiles.map(f => `.xiaoma-core/${f}`));
281
- } else {
282
- // Handle single files with {root} replacement if needed
283
- const sourcePath = path.join(
284
- sourceBase,
285
- dep.replace(".xiaoma-core/", "")
286
- );
287
- const destPath = path.join(
288
- installDir,
289
- dep
290
- );
248
+ // Get list of all files for manifest
249
+ const foundFiles = await resourceLocator.findFiles('**/*', {
250
+ cwd: bmadCoreDestDir,
251
+ nodir: true,
252
+ ignore: ['**/.git/**', '**/node_modules/**'],
253
+ });
254
+ files = foundFiles.map((file) => path.join('.xiaoma-core', file));
255
+
256
+ break;
257
+ }
258
+ case 'single-agent': {
259
+ // Single agent installation
260
+ spinner.text = `Installing ${config.agent} agent...`;
291
261
 
292
- const needsRootReplacement = dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
293
- let success = false;
294
-
295
- if (needsRootReplacement) {
296
- success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, ".xiaoma-core");
262
+ // Copy agent file with {root} replacement
263
+ const agentPath = configLoader.getAgentPath(config.agent);
264
+ const destinationAgentPath = path.join(
265
+ installDir,
266
+ '.xiaoma-core',
267
+ 'agents',
268
+ `${config.agent}.md`,
269
+ );
270
+ await fileManager.copyFileWithRootReplacement(
271
+ agentPath,
272
+ destinationAgentPath,
273
+ '.xiaoma-core',
274
+ );
275
+ files.push(`.xiaoma-core/agents/${config.agent}.md`);
276
+
277
+ // Copy dependencies
278
+ const { all: dependencies } = await resourceLocator.getAgentDependencies(config.agent);
279
+ const sourceBase = resourceLocator.getBmadCorePath();
280
+
281
+ for (const dep of dependencies) {
282
+ spinner.text = `Copying dependency: ${dep}`;
283
+
284
+ if (dep.includes('*')) {
285
+ // Handle glob patterns with {root} replacement
286
+ const copiedFiles = await fileManager.copyGlobPattern(
287
+ dep.replace('.xiaoma-core/', ''),
288
+ sourceBase,
289
+ path.join(installDir, '.xiaoma-core'),
290
+ '.xiaoma-core',
291
+ );
292
+ files.push(...copiedFiles.map((f) => `.xiaoma-core/${f}`));
297
293
  } else {
298
- success = await fileManager.copyFile(sourcePath, destPath);
299
- }
294
+ // Handle single files with {root} replacement if needed
295
+ const sourcePath = path.join(sourceBase, dep.replace('.xiaoma-core/', ''));
296
+ const destinationPath = path.join(installDir, dep);
297
+
298
+ const needsRootReplacement =
299
+ dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
300
+ let success = false;
301
+
302
+ success = await (needsRootReplacement
303
+ ? fileManager.copyFileWithRootReplacement(sourcePath, destinationPath, '.xiaoma-core')
304
+ : fileManager.copyFile(sourcePath, destinationPath));
300
305
 
301
- if (success) {
302
- files.push(dep);
306
+ if (success) {
307
+ files.push(dep);
308
+ }
303
309
  }
304
310
  }
311
+
312
+ // Copy common/ items to .xiaoma-core
313
+ spinner.text = 'Copying common utilities...';
314
+ const commonFiles = await this.copyCommonItems(installDir, '.xiaoma-core', spinner);
315
+ files.push(...commonFiles);
316
+
317
+ // Copy documentation files from docs/ to .xiaoma-core
318
+ spinner.text = 'Copying documentation files...';
319
+ const documentFiles = await this.copyDocsItems(installDir, '.xiaoma-core', spinner);
320
+ files.push(...documentFiles);
321
+
322
+ break;
305
323
  }
306
-
307
- // Copy common/ items to .xiaoma-core
308
- spinner.text = "Copying common utilities...";
309
- const commonFiles = await this.copyCommonItems(installDir, ".xiaoma-core", spinner);
310
- files.push(...commonFiles);
311
- } else if (config.installType === "team") {
312
- // Team installation
313
- spinner.text = `Installing ${config.team} team...`;
314
-
315
- // Get team dependencies
316
- const teamDependencies = await configLoader.getTeamDependencies(config.team);
317
- const sourceBase = resourceLocator.getBmadCorePath();
318
-
319
- // Install all team dependencies
320
- for (const dep of teamDependencies) {
321
- spinner.text = `Copying team dependency: ${dep}`;
322
-
323
- if (dep.includes("*")) {
324
- // Handle glob patterns with {root} replacement
325
- const copiedFiles = await fileManager.copyGlobPattern(
326
- dep.replace(".xiaoma-core/", ""),
327
- sourceBase,
328
- path.join(installDir, ".xiaoma-core"),
329
- ".xiaoma-core"
330
- );
331
- files.push(...copiedFiles.map(f => `.xiaoma-core/${f}`));
332
- } else {
333
- // Handle single files with {root} replacement if needed
334
- const sourcePath = path.join(sourceBase, dep.replace(".xiaoma-core/", ""));
335
- const destPath = path.join(installDir, dep);
336
-
337
- const needsRootReplacement = dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
338
- let success = false;
339
-
340
- if (needsRootReplacement) {
341
- success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, ".xiaoma-core");
324
+ case 'team': {
325
+ // Team installation
326
+ spinner.text = `Installing ${config.team} team...`;
327
+
328
+ // Get team dependencies
329
+ const teamDependencies = await configLoader.getTeamDependencies(config.team);
330
+ const sourceBase = resourceLocator.getBmadCorePath();
331
+
332
+ // Install all team dependencies
333
+ for (const dep of teamDependencies) {
334
+ spinner.text = `Copying team dependency: ${dep}`;
335
+
336
+ if (dep.includes('*')) {
337
+ // Handle glob patterns with {root} replacement
338
+ const copiedFiles = await fileManager.copyGlobPattern(
339
+ dep.replace('.xiaoma-core/', ''),
340
+ sourceBase,
341
+ path.join(installDir, '.xiaoma-core'),
342
+ '.xiaoma-core',
343
+ );
344
+ files.push(...copiedFiles.map((f) => `.xiaoma-core/${f}`));
342
345
  } else {
343
- success = await fileManager.copyFile(sourcePath, destPath);
344
- }
346
+ // Handle single files with {root} replacement if needed
347
+ const sourcePath = path.join(sourceBase, dep.replace('.xiaoma-core/', ''));
348
+ const destinationPath = path.join(installDir, dep);
349
+
350
+ const needsRootReplacement =
351
+ dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
352
+ let success = false;
353
+
354
+ success = await (needsRootReplacement
355
+ ? fileManager.copyFileWithRootReplacement(sourcePath, destinationPath, '.xiaoma-core')
356
+ : fileManager.copyFile(sourcePath, destinationPath));
345
357
 
346
- if (success) {
347
- files.push(dep);
358
+ if (success) {
359
+ files.push(dep);
360
+ }
348
361
  }
349
362
  }
363
+
364
+ // Copy common/ items to .xiaoma-core
365
+ spinner.text = 'Copying common utilities...';
366
+ const commonFiles = await this.copyCommonItems(installDir, '.xiaoma-core', spinner);
367
+ files.push(...commonFiles);
368
+
369
+ // Copy documentation files from docs/ to .xiaoma-core
370
+ spinner.text = 'Copying documentation files...';
371
+ const documentFiles = await this.copyDocsItems(installDir, '.xiaoma-core', spinner);
372
+ files.push(...documentFiles);
373
+
374
+ break;
350
375
  }
351
-
352
- // Copy common/ items to .xiaoma-core
353
- spinner.text = "Copying common utilities...";
354
- const commonFiles = await this.copyCommonItems(installDir, ".xiaoma-core", spinner);
355
- files.push(...commonFiles);
356
- } else if (config.installType === "expansion-only") {
357
- // Expansion-only installation - DO NOT create .xiaoma-core
358
- // Only install expansion packs
359
- spinner.text = "Installing expansion packs only...";
376
+ case 'expansion-only': {
377
+ // Expansion-only installation - DO NOT create .xiaoma-core
378
+ // Only install expansion packs
379
+ spinner.text = 'Installing expansion packs only...';
380
+
381
+ break;
382
+ }
383
+ // No default
360
384
  }
361
385
 
362
386
  // Install expansion packs if requested
363
- const expansionFiles = await this.installExpansionPacks(installDir, config.expansionPacks, spinner, config);
387
+ const expansionFiles = await this.installExpansionPacks(
388
+ installDir,
389
+ config.expansionPacks,
390
+ spinner,
391
+ config,
392
+ );
364
393
  files.push(...expansionFiles);
365
394
 
366
395
  // Install web bundles if requested
367
396
  if (config.includeWebBundles && config.webBundlesDirectory) {
368
- spinner.text = "Installing web bundles...";
397
+ spinner.text = 'Installing web bundles...';
369
398
  // Resolve web bundles directory using the same logic as the main installation directory
370
399
  const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
371
- let resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
372
- ? config.webBundlesDirectory
400
+ let resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
401
+ ? config.webBundlesDirectory
373
402
  : path.resolve(originalCwd, config.webBundlesDirectory);
374
403
  await this.installWebBundles(resolvedWebBundlesDir, config, spinner);
375
404
  }
@@ -379,24 +408,32 @@ class Installer {
379
408
  if (ides.length > 0) {
380
409
  for (const ide of ides) {
381
410
  spinner.text = `Setting up ${ide} integration...`;
382
- const preConfiguredSettings = ide === 'github-copilot' ? config.githubCopilotConfig : null;
411
+ let preConfiguredSettings = null;
412
+ if (ide === 'github-copilot') {
413
+ preConfiguredSettings = config.githubCopilotConfig;
414
+ } else if (ide === 'auggie-cli') {
415
+ preConfiguredSettings = config.augmentCodeConfig;
416
+ }
383
417
  await ideSetup.setup(ide, installDir, config.agent, spinner, preConfiguredSettings);
384
418
  }
385
419
  }
386
420
 
387
421
  // Modify core-config.yaml if sharding preferences were provided
388
- if (config.installType !== "expansion-only" && (config.prdSharded !== undefined || config.architectureSharded !== undefined)) {
389
- spinner.text = "Configuring document sharding settings...";
422
+ if (
423
+ config.installType !== 'expansion-only' &&
424
+ (config.prdSharded !== undefined || config.architectureSharded !== undefined)
425
+ ) {
426
+ spinner.text = 'Configuring document sharding settings...';
390
427
  await fileManager.modifyCoreConfig(installDir, config);
391
428
  }
392
429
 
393
430
  // Create manifest (skip for expansion-only installations)
394
- if (config.installType !== "expansion-only") {
395
- spinner.text = "Creating installation manifest...";
431
+ if (config.installType !== 'expansion-only') {
432
+ spinner.text = 'Creating installation manifest...';
396
433
  await fileManager.createManifest(installDir, config, files);
397
434
  }
398
435
 
399
- spinner.succeed("Installation complete!");
436
+ spinner.succeed('Installation complete!');
400
437
  this.showSuccessMessage(config, installDir, options);
401
438
  }
402
439
 
@@ -407,44 +444,40 @@ class Installer {
407
444
  const newVersion = await this.getCoreVersion();
408
445
  const versionCompare = this.compareVersions(currentVersion, newVersion);
409
446
 
410
- console.log(chalk.yellow("\n🔍 Found existing XiaoMa v4 installation"));
447
+ console.log(chalk.yellow('\n🔍 Found existing BMad v4 installation'));
411
448
  console.log(` Directory: ${installDir}`);
412
449
  console.log(` Current version: ${currentVersion}`);
413
450
  console.log(` Available version: ${newVersion}`);
414
- console.log(
415
- ` Installed: ${new Date(
416
- state.manifest.installed_at
417
- ).toLocaleDateString()}`
418
- );
451
+ console.log(` Installed: ${new Date(state.manifest.installed_at).toLocaleDateString()}`);
419
452
 
420
453
  // Check file integrity
421
- spinner.start("Checking installation integrity...");
454
+ spinner.start('Checking installation integrity...');
422
455
  const integrity = await fileManager.checkFileIntegrity(installDir, state.manifest);
423
456
  spinner.stop();
424
-
457
+
425
458
  const hasMissingFiles = integrity.missing.length > 0;
426
459
  const hasModifiedFiles = integrity.modified.length > 0;
427
460
  const hasIntegrityIssues = hasMissingFiles || hasModifiedFiles;
428
-
461
+
429
462
  if (hasIntegrityIssues) {
430
- console.log(chalk.red("\n⚠️ Installation issues detected:"));
463
+ console.log(chalk.red('\n⚠️ Installation issues detected:'));
431
464
  if (hasMissingFiles) {
432
465
  console.log(chalk.red(` Missing files: ${integrity.missing.length}`));
433
466
  if (integrity.missing.length <= 5) {
434
- integrity.missing.forEach(file => console.log(chalk.dim(` - ${file}`)));
467
+ for (const file of integrity.missing) console.log(chalk.dim(` - ${file}`));
435
468
  }
436
469
  }
437
470
  if (hasModifiedFiles) {
438
471
  console.log(chalk.yellow(` Modified files: ${integrity.modified.length}`));
439
472
  if (integrity.modified.length <= 5) {
440
- integrity.modified.forEach(file => console.log(chalk.dim(` - ${file}`)));
473
+ for (const file of integrity.modified) console.log(chalk.dim(` - ${file}`));
441
474
  }
442
475
  }
443
476
  }
444
477
 
445
478
  // Show existing expansion packs
446
479
  if (Object.keys(state.expansionPacks).length > 0) {
447
- console.log(chalk.cyan("\n📦 Installed expansion packs:"));
480
+ console.log(chalk.cyan('\n📦 Installed expansion packs:'));
448
481
  for (const [packId, packInfo] of Object.entries(state.expansionPacks)) {
449
482
  if (packInfo.hasManifest && packInfo.manifest) {
450
483
  console.log(` - ${packId} (v${packInfo.manifest.version || 'unknown'})`);
@@ -455,236 +488,251 @@ class Installer {
455
488
  }
456
489
 
457
490
  let choices = [];
458
-
491
+
459
492
  if (versionCompare < 0) {
460
- console.log(chalk.cyan("\n⬆️ Upgrade available for XiaoMa core"));
461
- choices.push({ name: `Upgrade XiaoMa core (v${currentVersion} → v${newVersion})`, value: "upgrade" });
493
+ console.log(chalk.cyan('\n⬆️ Upgrade available for BMad core'));
494
+ choices.push({
495
+ name: `Upgrade BMad core (v${currentVersion} → v${newVersion})`,
496
+ value: 'upgrade',
497
+ });
462
498
  } else if (versionCompare === 0) {
463
499
  if (hasIntegrityIssues) {
464
500
  // Offer repair option when files are missing or modified
465
- choices.push({
466
- name: "Repair installation (restore missing/modified files)",
467
- value: "repair"
501
+ choices.push({
502
+ name: 'Repair installation (restore missing/modified files)',
503
+ value: 'repair',
468
504
  });
469
505
  }
470
- console.log(chalk.yellow("\n⚠️ Same version already installed"));
471
- choices.push({ name: `Force reinstall XiaoMa core (v${currentVersion} - reinstall)`, value: "reinstall" });
506
+ console.log(chalk.yellow('\n⚠️ Same version already installed'));
507
+ choices.push({
508
+ name: `Force reinstall BMad core (v${currentVersion} - reinstall)`,
509
+ value: 'reinstall',
510
+ });
472
511
  } else {
473
- console.log(chalk.yellow("\n⬇️ Installed version is newer than available"));
474
- choices.push({ name: `Downgrade XiaoMa core (v${currentVersion} → v${newVersion})`, value: "reinstall" });
512
+ console.log(chalk.yellow('\n⬇️ Installed version is newer than available'));
513
+ choices.push({
514
+ name: `Downgrade BMad core (v${currentVersion} → v${newVersion})`,
515
+ value: 'reinstall',
516
+ });
475
517
  }
476
-
518
+
477
519
  choices.push(
478
- { name: "Add/update expansion packs only", value: "expansions" },
479
- { name: "Cancel", value: "cancel" }
520
+ { name: 'Add/update expansion packs only', value: 'expansions' },
521
+ { name: 'Cancel', value: 'cancel' },
480
522
  );
481
523
 
482
524
  const { action } = await inquirer.prompt([
483
525
  {
484
- type: "list",
485
- name: "action",
486
- message: "What would you like to do?",
526
+ type: 'list',
527
+ name: 'action',
528
+ message: 'What would you like to do?',
487
529
  choices: choices,
488
530
  },
489
531
  ]);
490
532
 
491
533
  switch (action) {
492
- case "upgrade":
534
+ case 'upgrade': {
493
535
  return await this.performUpdate(config, installDir, state.manifest, spinner);
494
- case "repair":
536
+ }
537
+ case 'repair': {
495
538
  // For repair, restore missing/modified files while backing up modified ones
496
539
  return await this.performRepair(config, installDir, state.manifest, integrity, spinner);
497
- case "reinstall":
540
+ }
541
+ case 'reinstall': {
498
542
  // For reinstall, don't check for modifications - just overwrite
499
543
  return await this.performReinstall(config, installDir, spinner);
500
- case "expansions": {
544
+ }
545
+ case 'expansions': {
501
546
  // Ask which expansion packs to install
502
547
  const availableExpansionPacks = await resourceLocator.getExpansionPacks();
503
-
548
+
504
549
  if (availableExpansionPacks.length === 0) {
505
- console.log(chalk.yellow("No expansion packs available."));
550
+ console.log(chalk.yellow('No expansion packs available.'));
506
551
  return;
507
552
  }
508
-
553
+
509
554
  const { selectedPacks } = await inquirer.prompt([
510
555
  {
511
556
  type: 'checkbox',
512
557
  name: 'selectedPacks',
513
558
  message: 'Select expansion packs to install/update:',
514
- choices: availableExpansionPacks.map(pack => ({
559
+ choices: availableExpansionPacks.map((pack) => ({
515
560
  name: `${pack.name} (v${pack.version}) .${pack.id}`,
516
561
  value: pack.id,
517
- checked: state.expansionPacks[pack.id] !== undefined
518
- }))
519
- }
562
+ checked: state.expansionPacks[pack.id] !== undefined,
563
+ })),
564
+ },
520
565
  ]);
521
-
566
+
522
567
  if (selectedPacks.length === 0) {
523
- console.log(chalk.yellow("No expansion packs selected."));
568
+ console.log(chalk.yellow('No expansion packs selected.'));
524
569
  return;
525
570
  }
526
-
527
- spinner.start("Installing expansion packs...");
528
- const expansionFiles = await this.installExpansionPacks(installDir, selectedPacks, spinner, { ides: config.ides || [] });
529
- spinner.succeed("Expansion packs installed successfully!");
530
-
531
- console.log(chalk.green("\n✓ Installation complete!"));
571
+
572
+ spinner.start('Installing expansion packs...');
573
+ const expansionFiles = await this.installExpansionPacks(
574
+ installDir,
575
+ selectedPacks,
576
+ spinner,
577
+ { ides: config.ides || [] },
578
+ );
579
+ spinner.succeed('Expansion packs installed successfully!');
580
+
581
+ console.log(chalk.green('\n✓ Installation complete!'));
532
582
  console.log(chalk.green(`✓ Expansion packs installed/updated:`));
533
583
  for (const packId of selectedPacks) {
534
584
  console.log(chalk.green(` - ${packId} → .${packId}/`));
535
585
  }
536
586
  return;
537
587
  }
538
- case "cancel":
539
- console.log("Installation cancelled.");
588
+ case 'cancel': {
589
+ console.log('Installation cancelled.');
540
590
  return;
591
+ }
541
592
  }
542
593
  }
543
594
 
544
595
  async handleV3Installation(config, installDir, state, spinner) {
545
596
  spinner.stop();
546
597
 
547
- console.log(
548
- chalk.yellow("\n🔍 Found XiaoMa v3 installation (bmad-agent/ directory)")
549
- );
598
+ console.log(chalk.yellow('\n🔍 Found BMad v3 installation (bmad-agent/ directory)'));
550
599
  console.log(` Directory: ${installDir}`);
551
600
 
552
601
  const { action } = await inquirer.prompt([
553
602
  {
554
- type: "list",
555
- name: "action",
556
- message: "What would you like to do?",
603
+ type: 'list',
604
+ name: 'action',
605
+ message: 'What would you like to do?',
557
606
  choices: [
558
- { name: "Upgrade from v3 to v4 (recommended)", value: "upgrade" },
559
- { name: "Install v4 alongside v3", value: "alongside" },
560
- { name: "Cancel", value: "cancel" },
607
+ { name: 'Upgrade from v3 to v4 (recommended)', value: 'upgrade' },
608
+ { name: 'Install v4 alongside v3', value: 'alongside' },
609
+ { name: 'Cancel', value: 'cancel' },
561
610
  ],
562
611
  },
563
612
  ]);
564
613
 
565
614
  switch (action) {
566
- case "upgrade": {
567
- console.log(chalk.cyan("\n📦 Starting v3 to v4 upgrade process..."));
568
- const V3ToV4Upgrader = require("../../upgraders/v3-to-v4-upgrader");
615
+ case 'upgrade': {
616
+ console.log(chalk.cyan('\n📦 Starting v3 to v4 upgrade process...'));
617
+ const V3ToV4Upgrader = require('../../upgraders/v3-to-v4-upgrader');
569
618
  const upgrader = new V3ToV4Upgrader();
570
- return await upgrader.upgrade({
619
+ return await upgrader.upgrade({
571
620
  projectPath: installDir,
572
- ides: config.ides || [] // Pass IDE selections from initial config
621
+ ides: config.ides || [], // Pass IDE selections from initial config
573
622
  });
574
623
  }
575
- case "alongside":
624
+ case 'alongside': {
576
625
  return await this.performFreshInstall(config, installDir, spinner);
577
- case "cancel":
578
- console.log("Installation cancelled.");
626
+ }
627
+ case 'cancel': {
628
+ console.log('Installation cancelled.');
579
629
  return;
630
+ }
580
631
  }
581
632
  }
582
633
 
583
634
  async handleUnknownInstallation(config, installDir, state, spinner) {
584
635
  spinner.stop();
585
636
 
586
- console.log(chalk.yellow("\n⚠️ Directory contains existing files"));
637
+ console.log(chalk.yellow('\n⚠️ Directory contains existing files'));
587
638
  console.log(` Directory: ${installDir}`);
588
639
 
589
640
  if (state.hasBmadCore) {
590
- console.log(" Found: .xiaoma-core directory (but no manifest)");
641
+ console.log(' Found: .xiaoma-core directory (but no manifest)');
591
642
  }
592
643
  if (state.hasOtherFiles) {
593
- console.log(" Found: Other files in directory");
644
+ console.log(' Found: Other files in directory');
594
645
  }
595
646
 
596
647
  const { action } = await inquirer.prompt([
597
648
  {
598
- type: "list",
599
- name: "action",
600
- message: "What would you like to do?",
649
+ type: 'list',
650
+ name: 'action',
651
+ message: 'What would you like to do?',
601
652
  choices: [
602
- { name: "Install anyway (may overwrite files)", value: "force" },
603
- { name: "Choose different directory", value: "different" },
604
- { name: "Cancel", value: "cancel" },
653
+ { name: 'Install anyway (may overwrite files)', value: 'force' },
654
+ { name: 'Choose different directory', value: 'different' },
655
+ { name: 'Cancel', value: 'cancel' },
605
656
  ],
606
657
  },
607
658
  ]);
608
659
 
609
660
  switch (action) {
610
- case "force":
661
+ case 'force': {
611
662
  return await this.performFreshInstall(config, installDir, spinner);
612
- case "different": {
663
+ }
664
+ case 'different': {
613
665
  const { newDir } = await inquirer.prompt([
614
666
  {
615
- type: "input",
616
- name: "newDir",
617
- message: "Enter new installation directory:",
618
- default: path.join(path.dirname(installDir), "bmad-project"),
667
+ type: 'input',
668
+ name: 'newDir',
669
+ message: 'Enter new installation directory:',
670
+ default: path.join(path.dirname(installDir), 'bmad-project'),
619
671
  },
620
672
  ]);
621
673
  config.directory = newDir;
622
674
  return await this.install(config);
623
675
  }
624
- case "cancel":
625
- console.log("Installation cancelled.");
676
+ case 'cancel': {
677
+ console.log('Installation cancelled.');
626
678
  return;
679
+ }
627
680
  }
628
681
  }
629
682
 
630
683
  async performUpdate(newConfig, installDir, manifest, spinner) {
631
- spinner.start("Checking for updates...");
684
+ spinner.start('Checking for updates...');
632
685
 
633
686
  try {
634
687
  // Get current and new versions
635
688
  const currentVersion = manifest.version;
636
689
  const newVersion = await this.getCoreVersion();
637
690
  const versionCompare = this.compareVersions(currentVersion, newVersion);
638
-
691
+
639
692
  // Only check for modified files if it's an actual version upgrade
640
693
  let modifiedFiles = [];
641
694
  if (versionCompare !== 0) {
642
- spinner.text = "Checking for modified files...";
643
- modifiedFiles = await fileManager.checkModifiedFiles(
644
- installDir,
645
- manifest
646
- );
695
+ spinner.text = 'Checking for modified files...';
696
+ modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest);
647
697
  }
648
698
 
649
699
  if (modifiedFiles.length > 0) {
650
- spinner.warn("Found modified files");
651
- console.log(chalk.yellow("\nThe following files have been modified:"));
700
+ spinner.warn('Found modified files');
701
+ console.log(chalk.yellow('\nThe following files have been modified:'));
652
702
  for (const file of modifiedFiles) {
653
703
  console.log(` - ${file}`);
654
704
  }
655
705
 
656
706
  const { action } = await inquirer.prompt([
657
707
  {
658
- type: "list",
659
- name: "action",
660
- message: "How would you like to proceed?",
708
+ type: 'list',
709
+ name: 'action',
710
+ message: 'How would you like to proceed?',
661
711
  choices: [
662
- { name: "Backup and overwrite modified files", value: "backup" },
663
- { name: "Skip modified files", value: "skip" },
664
- { name: "Cancel update", value: "cancel" },
712
+ { name: 'Backup and overwrite modified files', value: 'backup' },
713
+ { name: 'Skip modified files', value: 'skip' },
714
+ { name: 'Cancel update', value: 'cancel' },
665
715
  ],
666
716
  },
667
717
  ]);
668
718
 
669
- if (action === "cancel") {
670
- console.log("Update cancelled.");
719
+ if (action === 'cancel') {
720
+ console.log('Update cancelled.');
671
721
  return;
672
722
  }
673
723
 
674
- if (action === "backup") {
675
- spinner.start("Backing up modified files...");
724
+ if (action === 'backup') {
725
+ spinner.start('Backing up modified files...');
676
726
  for (const file of modifiedFiles) {
677
727
  const filePath = path.join(installDir, file);
678
728
  const backupPath = await fileManager.backupFile(filePath);
679
- console.log(
680
- chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`)
681
- );
729
+ console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`));
682
730
  }
683
731
  }
684
732
  }
685
733
 
686
734
  // Perform update by re-running installation
687
- spinner.text = versionCompare === 0 ? "Reinstalling files..." : "Updating files...";
735
+ spinner.text = versionCompare === 0 ? 'Reinstalling files...' : 'Updating files...';
688
736
  const config = {
689
737
  installType: manifest.install_type,
690
738
  agent: manifest.agent,
@@ -693,23 +741,23 @@ class Installer {
693
741
  };
694
742
 
695
743
  await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
696
-
744
+
697
745
  // Clean up .yml files that now have .yaml counterparts
698
- spinner.text = "Cleaning up legacy .yml files...";
746
+ spinner.text = 'Cleaning up legacy .yml files...';
699
747
  await this.cleanupLegacyYmlFiles(installDir, spinner);
700
748
  } catch (error) {
701
- spinner.fail("Update failed");
749
+ spinner.fail('Update failed');
702
750
  throw error;
703
751
  }
704
752
  }
705
753
 
706
754
  async performRepair(config, installDir, manifest, integrity, spinner) {
707
- spinner.start("Preparing to repair installation...");
755
+ spinner.start('Preparing to repair installation...');
708
756
 
709
757
  try {
710
758
  // Back up modified files
711
759
  if (integrity.modified.length > 0) {
712
- spinner.text = "Backing up modified files...";
760
+ spinner.text = 'Backing up modified files...';
713
761
  for (const file of integrity.modified) {
714
762
  const filePath = path.join(installDir, file);
715
763
  if (await fileManager.pathExists(filePath)) {
@@ -720,42 +768,42 @@ class Installer {
720
768
  }
721
769
 
722
770
  // Restore missing and modified files
723
- spinner.text = "Restoring files...";
771
+ spinner.text = 'Restoring files...';
724
772
  const sourceBase = resourceLocator.getBmadCorePath();
725
773
  const filesToRestore = [...integrity.missing, ...integrity.modified];
726
-
774
+
727
775
  for (const file of filesToRestore) {
728
776
  // Skip the manifest file itself
729
777
  if (file.endsWith('install-manifest.yaml')) continue;
730
-
778
+
731
779
  const relativePath = file.replace('.xiaoma-core/', '');
732
- const destPath = path.join(installDir, file);
733
-
780
+ const destinationPath = path.join(installDir, file);
781
+
734
782
  // Check if this is a common/ file that needs special processing
735
783
  const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
736
784
  const commonSourcePath = path.join(commonBase, 'common', relativePath);
737
-
785
+
738
786
  if (await fileManager.pathExists(commonSourcePath)) {
739
787
  // This is a common/ file - needs template processing
740
- const fs = require('fs').promises;
788
+ const fs = require('node:fs').promises;
741
789
  const content = await fs.readFile(commonSourcePath, 'utf8');
742
- const updatedContent = content.replace(/\{root\}/g, '.xiaoma-core');
743
- await fileManager.ensureDirectory(path.dirname(destPath));
744
- await fs.writeFile(destPath, updatedContent, 'utf8');
790
+ const updatedContent = content.replaceAll('{root}', '.xiaoma-core');
791
+ await fileManager.ensureDirectory(path.dirname(destinationPath));
792
+ await fs.writeFile(destinationPath, updatedContent, 'utf8');
745
793
  spinner.text = `Restored: ${file}`;
746
794
  } else {
747
795
  // Regular file from xiaoma-core
748
796
  const sourcePath = path.join(sourceBase, relativePath);
749
797
  if (await fileManager.pathExists(sourcePath)) {
750
- await fileManager.copyFile(sourcePath, destPath);
798
+ await fileManager.copyFile(sourcePath, destinationPath);
751
799
  spinner.text = `Restored: ${file}`;
752
-
800
+
753
801
  // If this is a .yaml file, check for and remove corresponding .yml file
754
802
  if (file.endsWith('.yaml')) {
755
803
  const ymlFile = file.replace(/\.yaml$/, '.yml');
756
804
  const ymlPath = path.join(installDir, ymlFile);
757
805
  if (await fileManager.pathExists(ymlPath)) {
758
- const fs = require('fs').promises;
806
+ const fs = require('node:fs').promises;
759
807
  await fs.unlink(ymlPath);
760
808
  console.log(chalk.dim(` Removed legacy: ${ymlFile} (replaced by ${file})`));
761
809
  }
@@ -765,187 +813,192 @@ class Installer {
765
813
  }
766
814
  }
767
815
  }
768
-
816
+
769
817
  // Clean up .yml files that now have .yaml counterparts
770
- spinner.text = "Cleaning up legacy .yml files...";
818
+ spinner.text = 'Cleaning up legacy .yml files...';
771
819
  await this.cleanupLegacyYmlFiles(installDir, spinner);
772
-
773
- spinner.succeed("Repair completed successfully!");
774
-
820
+
821
+ spinner.succeed('Repair completed successfully!');
822
+
775
823
  // Show summary
776
- console.log(chalk.green("\n✓ Installation repaired!"));
824
+ console.log(chalk.green('\n✓ Installation repaired!'));
777
825
  if (integrity.missing.length > 0) {
778
826
  console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
779
827
  }
780
828
  if (integrity.modified.length > 0) {
781
- console.log(chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`));
829
+ console.log(
830
+ chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`),
831
+ );
782
832
  }
783
-
833
+
784
834
  // Warning for Cursor custom modes if agents were repaired
785
835
  const ides = manifest.ides_setup || [];
786
836
  if (ides.includes('cursor')) {
787
- console.log(chalk.yellow.bold("\n⚠️ IMPORTANT: Cursor Custom Modes Update Required"));
788
- console.log(chalk.yellow("Since agent files have been repaired, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs."));
837
+ console.log(chalk.yellow.bold('\n⚠️ IMPORTANT: Cursor Custom Modes Update Required'));
838
+ console.log(
839
+ chalk.yellow(
840
+ 'Since agent files have been repaired, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs.',
841
+ ),
842
+ );
789
843
  }
790
-
791
844
  } catch (error) {
792
- spinner.fail("Repair failed");
845
+ spinner.fail('Repair failed');
793
846
  throw error;
794
847
  }
795
848
  }
796
849
 
797
850
  async performReinstall(config, installDir, spinner) {
798
- spinner.start("Preparing to reinstall XiaoMa CLI...");
851
+ spinner.start('Preparing to reinstall BMad Method...');
799
852
 
800
853
  // Remove existing .xiaoma-core
801
- const bmadCorePath = path.join(installDir, ".xiaoma-core");
854
+ const bmadCorePath = path.join(installDir, '.xiaoma-core');
802
855
  if (await fileManager.pathExists(bmadCorePath)) {
803
- spinner.text = "Removing existing installation...";
856
+ spinner.text = 'Removing existing installation...';
804
857
  await fileManager.removeDirectory(bmadCorePath);
805
858
  }
806
-
807
- spinner.text = "Installing fresh copy...";
859
+
860
+ spinner.text = 'Installing fresh copy...';
808
861
  const result = await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
809
-
862
+
810
863
  // Clean up .yml files that now have .yaml counterparts
811
- spinner.text = "Cleaning up legacy .yml files...";
864
+ spinner.text = 'Cleaning up legacy .yml files...';
812
865
  await this.cleanupLegacyYmlFiles(installDir, spinner);
813
-
866
+
814
867
  return result;
815
868
  }
816
869
 
817
870
  showSuccessMessage(config, installDir, options = {}) {
818
- console.log(chalk.green("\n✓ XiaoMa CLI installed successfully!\n"));
871
+ console.log(chalk.green('\n✓ BMad Method installed successfully!\n'));
819
872
 
820
873
  const ides = config.ides || (config.ide ? [config.ide] : []);
821
874
  if (ides.length > 0) {
822
875
  for (const ide of ides) {
823
876
  const ideConfig = configLoader.getIdeConfiguration(ide);
824
877
  if (ideConfig?.instructions) {
825
- console.log(
826
- chalk.bold(`To use XiaoMa agents in ${ideConfig.name}:`)
827
- );
878
+ console.log(chalk.bold(`To use BMad agents in ${ideConfig.name}:`));
828
879
  console.log(ideConfig.instructions);
829
880
  }
830
881
  }
831
882
  } else {
832
- console.log(chalk.yellow("No IDE configuration was set up."));
833
- console.log(
834
- "You can manually configure your IDE using the agent files in:",
835
- installDir
836
- );
883
+ console.log(chalk.yellow('No IDE configuration was set up.'));
884
+ console.log('You can manually configure your IDE using the agent files in:', installDir);
837
885
  }
838
886
 
839
887
  // Information about installation components
840
- console.log(chalk.bold("\n🎯 Installation Summary:"));
841
- if (config.installType !== "expansion-only") {
842
- console.log(chalk.green("✓ .xiaoma-core framework installed with all agents and workflows"));
888
+ console.log(chalk.bold('\n🎯 Installation Summary:'));
889
+ if (config.installType !== 'expansion-only') {
890
+ console.log(chalk.green('✓ .xiaoma-core framework installed with all agents and workflows'));
843
891
  }
844
-
892
+
845
893
  if (config.expansionPacks && config.expansionPacks.length > 0) {
846
894
  console.log(chalk.green(`✓ Expansion packs installed:`));
847
895
  for (const packId of config.expansionPacks) {
848
896
  console.log(chalk.green(` - ${packId} → .${packId}/`));
849
897
  }
850
898
  }
851
-
899
+
852
900
  if (config.includeWebBundles && config.webBundlesDirectory) {
853
901
  const bundleInfo = this.getWebBundleInfo(config);
854
902
  // Resolve the web bundles directory for display
855
903
  const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
856
- const resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
857
- ? config.webBundlesDirectory
904
+ const resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
905
+ ? config.webBundlesDirectory
858
906
  : path.resolve(originalCwd, config.webBundlesDirectory);
859
- console.log(chalk.green(`✓ Web bundles (${bundleInfo}) installed to: ${resolvedWebBundlesDir}`));
907
+ console.log(
908
+ chalk.green(`✓ Web bundles (${bundleInfo}) installed to: ${resolvedWebBundlesDir}`),
909
+ );
860
910
  }
861
-
911
+
862
912
  if (ides.length > 0) {
863
- const ideNames = ides.map(ide => {
864
- const ideConfig = configLoader.getIdeConfiguration(ide);
865
- return ideConfig?.name || ide;
866
- }).join(", ");
913
+ const ideNames = ides
914
+ .map((ide) => {
915
+ const ideConfig = configLoader.getIdeConfiguration(ide);
916
+ return ideConfig?.name || ide;
917
+ })
918
+ .join(', ');
867
919
  console.log(chalk.green(`✓ IDE rules and configurations set up for: ${ideNames}`));
868
920
  }
869
-
870
-
871
921
 
872
922
  // Information about web bundles
873
923
  if (!config.includeWebBundles) {
874
- console.log(chalk.bold("\n📦 Web Bundles Available:"));
875
- console.log("Pre-built web bundles are available and can be added later:");
876
- console.log(chalk.cyan(" Run the installer again to add them to your project"));
877
- console.log("These bundles work independently and can be shared, moved, or used");
878
- console.log("in other projects as standalone files.");
924
+ console.log(chalk.bold('\n📦 Web Bundles Available:'));
925
+ console.log('Pre-built web bundles are available and can be added later:');
926
+ console.log(chalk.cyan(' Run the installer again to add them to your project'));
927
+ console.log('These bundles work independently and can be shared, moved, or used');
928
+ console.log('in other projects as standalone files.');
879
929
  }
880
930
 
881
- if (config.installType === "single-agent") {
882
- console.log(
883
- chalk.dim(
884
- "\nNeed other agents? Run: npx xiaoma-cli install --agent=<name>"
885
- )
886
- );
887
- console.log(
888
- chalk.dim("Need everything? Run: npx xiaoma-cli install --full")
889
- );
931
+ if (config.installType === 'single-agent') {
932
+ console.log(chalk.dim('\nNeed other agents? Run: npx bmad-method install --agent=<name>'));
933
+ console.log(chalk.dim('Need everything? Run: npx bmad-method install --full'));
890
934
  }
891
935
 
892
936
  // Warning for Cursor custom modes if agents were updated
893
937
  if (options.isUpdate && ides.includes('cursor')) {
894
- console.log(chalk.yellow.bold("\n⚠️ IMPORTANT: Cursor Custom Modes Update Required"));
895
- console.log(chalk.yellow("Since agents have been updated, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs."));
938
+ console.log(chalk.yellow.bold('\n⚠️ IMPORTANT: Cursor Custom Modes Update Required'));
939
+ console.log(
940
+ chalk.yellow(
941
+ 'Since agents have been updated, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs.',
942
+ ),
943
+ );
896
944
  }
897
945
 
898
946
  // Important notice to read the user guide
899
- console.log(chalk.red.bold("\n📖 IMPORTANT: Please read the user guide installed at .xiaoma-core/user-guide.md"));
900
- console.log(chalk.red("This guide contains essential information about the XiaoMa workflow and how to use the agents effectively."));
947
+ console.log(
948
+ chalk.red.bold(
949
+ '\n📖 IMPORTANT: Please read the user guide at docs/user-guide.md (also installed at .xiaoma-core/user-guide.md)',
950
+ ),
951
+ );
952
+ console.log(
953
+ chalk.red(
954
+ 'This guide contains essential information about the BMad workflow and how to use the agents effectively.',
955
+ ),
956
+ );
901
957
  }
902
958
 
903
959
  // Legacy method for backward compatibility
904
960
  async update() {
905
961
  console.log(chalk.yellow('The "update" command is deprecated.'));
906
962
  console.log(
907
- 'Please use "install" instead - it will detect and offer to update existing installations.'
963
+ 'Please use "install" instead - it will detect and offer to update existing installations.',
908
964
  );
909
965
 
910
966
  const installDir = await this.findInstallation();
911
967
  if (installDir) {
912
968
  const config = {
913
- installType: "full",
969
+ installType: 'full',
914
970
  directory: path.dirname(installDir),
915
971
  ide: null,
916
972
  };
917
973
  return await this.install(config);
918
974
  }
919
- console.log(chalk.red("No XiaoMa installation found."));
975
+ console.log(chalk.red('No BMad installation found.'));
920
976
  }
921
977
 
922
978
  async listAgents() {
923
979
  const agents = await resourceLocator.getAvailableAgents();
924
980
 
925
- console.log(chalk.bold("\nAvailable XiaoMa Agents:\n"));
981
+ console.log(chalk.bold('\nAvailable BMad Agents:\n'));
926
982
 
927
983
  for (const agent of agents) {
928
984
  console.log(chalk.cyan(` ${agent.id.padEnd(20)}`), agent.description);
929
985
  }
930
986
 
931
- console.log(
932
- chalk.dim("\nInstall with: npx xiaoma-cli install --agent=<id>\n")
933
- );
987
+ console.log(chalk.dim('\nInstall with: npx bmad-method install --agent=<id>\n'));
934
988
  }
935
989
 
936
990
  async listExpansionPacks() {
937
991
  const expansionPacks = await resourceLocator.getExpansionPacks();
938
992
 
939
- console.log(chalk.bold("\nAvailable XiaoMa Expansion Packs:\n"));
993
+ console.log(chalk.bold('\nAvailable BMad Expansion Packs:\n'));
940
994
 
941
995
  if (expansionPacks.length === 0) {
942
- console.log(chalk.yellow("No expansion packs found."));
996
+ console.log(chalk.yellow('No expansion packs found.'));
943
997
  return;
944
998
  }
945
999
 
946
1000
  for (const pack of expansionPacks) {
947
- console.log(chalk.cyan(` ${pack.id.padEnd(20)}`),
948
- `${pack.name} v${pack.version}`);
1001
+ console.log(chalk.cyan(` ${pack.id.padEnd(20)}`), `${pack.name} v${pack.version}`);
949
1002
  console.log(chalk.dim(` ${' '.repeat(22)}${pack.description}`));
950
1003
  if (pack.author && pack.author !== 'Unknown') {
951
1004
  console.log(chalk.dim(` ${' '.repeat(22)}by ${pack.author}`));
@@ -953,36 +1006,28 @@ class Installer {
953
1006
  console.log();
954
1007
  }
955
1008
 
956
- console.log(
957
- chalk.dim("Install with: npx xiaoma-cli install --full --expansion-packs <id>\n")
958
- );
1009
+ console.log(chalk.dim('Install with: npx bmad-method install --full --expansion-packs <id>\n'));
959
1010
  }
960
1011
 
961
1012
  async showStatus() {
962
1013
  const installDir = await this.findInstallation();
963
1014
 
964
1015
  if (!installDir) {
965
- console.log(
966
- chalk.yellow("No XiaoMa installation found in current directory tree")
967
- );
1016
+ console.log(chalk.yellow('No BMad installation found in current directory tree'));
968
1017
  return;
969
1018
  }
970
1019
 
971
1020
  const manifest = await fileManager.readManifest(installDir);
972
1021
 
973
1022
  if (!manifest) {
974
- console.log(chalk.red("Invalid installation - manifest not found"));
1023
+ console.log(chalk.red('Invalid installation - manifest not found'));
975
1024
  return;
976
1025
  }
977
1026
 
978
- console.log(chalk.bold("\nXiaoMa Installation Status:\n"));
1027
+ console.log(chalk.bold('\nBMad Installation Status:\n'));
979
1028
  console.log(` Directory: ${installDir}`);
980
1029
  console.log(` Version: ${manifest.version}`);
981
- console.log(
982
- ` Installed: ${new Date(
983
- manifest.installed_at
984
- ).toLocaleDateString()}`
985
- );
1030
+ console.log(` Installed: ${new Date(manifest.installed_at).toLocaleDateString()}`);
986
1031
  console.log(` Type: ${manifest.install_type}`);
987
1032
 
988
1033
  if (manifest.agent) {
@@ -996,15 +1041,12 @@ class Installer {
996
1041
  console.log(` Total Files: ${manifest.files.length}`);
997
1042
 
998
1043
  // Check for modifications
999
- const modifiedFiles = await fileManager.checkModifiedFiles(
1000
- installDir,
1001
- manifest
1002
- );
1044
+ const modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest);
1003
1045
  if (modifiedFiles.length > 0) {
1004
1046
  console.log(chalk.yellow(` Modified Files: ${modifiedFiles.length}`));
1005
1047
  }
1006
1048
 
1007
- console.log("");
1049
+ console.log('');
1008
1050
  }
1009
1051
 
1010
1052
  async getAvailableAgents() {
@@ -1028,34 +1070,35 @@ class Installer {
1028
1070
 
1029
1071
  for (const packId of selectedPacks) {
1030
1072
  spinner.text = `Installing expansion pack: ${packId}...`;
1031
-
1073
+
1032
1074
  try {
1033
1075
  const expansionPacks = await resourceLocator.getExpansionPacks();
1034
- const pack = expansionPacks.find(p => p.id === packId);
1035
-
1076
+ const pack = expansionPacks.find((p) => p.id === packId);
1077
+
1036
1078
  if (!pack) {
1037
1079
  console.warn(`Expansion pack ${packId} not found, skipping...`);
1038
1080
  continue;
1039
1081
  }
1040
-
1082
+
1041
1083
  // Check if expansion pack already exists
1042
1084
  let expansionDotFolder = path.join(installDir, `.${packId}`);
1043
1085
  const existingManifestPath = path.join(expansionDotFolder, 'install-manifest.yaml');
1044
-
1086
+
1045
1087
  if (await fileManager.pathExists(existingManifestPath)) {
1046
1088
  spinner.stop();
1047
1089
  const existingManifest = await fileManager.readExpansionPackManifest(installDir, packId);
1048
-
1090
+
1049
1091
  console.log(chalk.yellow(`\n🔍 Found existing ${pack.name} installation`));
1050
1092
  console.log(` Current version: ${existingManifest.version || 'unknown'}`);
1051
1093
  console.log(` New version: ${pack.version}`);
1052
-
1094
+
1053
1095
  // Check integrity of existing expansion pack
1054
1096
  const packIntegrity = await fileManager.checkFileIntegrity(installDir, existingManifest);
1055
- const hasPackIntegrityIssues = packIntegrity.missing.length > 0 || packIntegrity.modified.length > 0;
1056
-
1097
+ const hasPackIntegrityIssues =
1098
+ packIntegrity.missing.length > 0 || packIntegrity.modified.length > 0;
1099
+
1057
1100
  if (hasPackIntegrityIssues) {
1058
- console.log(chalk.red(" ⚠️ Installation issues detected:"));
1101
+ console.log(chalk.red(' ⚠️ Installation issues detected:'));
1059
1102
  if (packIntegrity.missing.length > 0) {
1060
1103
  console.log(chalk.red(` Missing files: ${packIntegrity.missing.length}`));
1061
1104
  }
@@ -1063,12 +1106,15 @@ class Installer {
1063
1106
  console.log(chalk.yellow(` Modified files: ${packIntegrity.modified.length}`));
1064
1107
  }
1065
1108
  }
1066
-
1067
- const versionCompare = this.compareVersions(existingManifest.version || '0.0.0', pack.version);
1068
-
1109
+
1110
+ const versionCompare = this.compareVersions(
1111
+ existingManifest.version || '0.0.0',
1112
+ pack.version,
1113
+ );
1114
+
1069
1115
  if (versionCompare === 0) {
1070
1116
  console.log(chalk.yellow(' ⚠️ Same version already installed'));
1071
-
1117
+
1072
1118
  const choices = [];
1073
1119
  if (hasPackIntegrityIssues) {
1074
1120
  choices.push({ name: 'Repair (restore missing/modified files)', value: 'repair' });
@@ -1076,75 +1122,92 @@ class Installer {
1076
1122
  choices.push(
1077
1123
  { name: 'Force reinstall (overwrite)', value: 'overwrite' },
1078
1124
  { name: 'Skip this expansion pack', value: 'skip' },
1079
- { name: 'Cancel installation', value: 'cancel' }
1125
+ { name: 'Cancel installation', value: 'cancel' },
1080
1126
  );
1081
-
1082
- const { action } = await inquirer.prompt([{
1083
- type: 'list',
1084
- name: 'action',
1085
- message: `${pack.name} v${pack.version} is already installed. What would you like to do?`,
1086
- choices: choices
1087
- }]);
1088
-
1089
- if (action === 'skip') {
1090
- spinner.start();
1091
- continue;
1092
- } else if (action === 'cancel') {
1127
+
1128
+ const { action } = await inquirer.prompt([
1129
+ {
1130
+ type: 'list',
1131
+ name: 'action',
1132
+ message: `${pack.name} v${pack.version} is already installed. What would you like to do?`,
1133
+ choices: choices,
1134
+ },
1135
+ ]);
1136
+
1137
+ switch (action) {
1138
+ case 'skip': {
1139
+ spinner.start();
1140
+ continue;
1141
+
1142
+ break;
1143
+ }
1144
+ case 'cancel': {
1093
1145
  console.log('Installation cancelled.');
1094
- process.exit(0);
1095
- } else if (action === 'repair') {
1096
- // Repair the expansion pack
1097
- await this.repairExpansionPack(installDir, packId, pack, packIntegrity, spinner);
1098
- continue;
1146
+ process.exit(0);
1147
+
1148
+ break;
1149
+ }
1150
+ case 'repair': {
1151
+ // Repair the expansion pack
1152
+ await this.repairExpansionPack(installDir, packId, pack, packIntegrity, spinner);
1153
+ continue;
1154
+
1155
+ break;
1156
+ }
1157
+ // No default
1099
1158
  }
1100
1159
  } else if (versionCompare < 0) {
1101
1160
  console.log(chalk.cyan(' ⬆️ Upgrade available'));
1102
-
1103
- const { proceed } = await inquirer.prompt([{
1104
- type: 'confirm',
1105
- name: 'proceed',
1106
- message: `Upgrade ${pack.name} from v${existingManifest.version} to v${pack.version}?`,
1107
- default: true
1108
- }]);
1109
-
1161
+
1162
+ const { proceed } = await inquirer.prompt([
1163
+ {
1164
+ type: 'confirm',
1165
+ name: 'proceed',
1166
+ message: `Upgrade ${pack.name} from v${existingManifest.version} to v${pack.version}?`,
1167
+ default: true,
1168
+ },
1169
+ ]);
1170
+
1110
1171
  if (!proceed) {
1111
1172
  spinner.start();
1112
1173
  continue;
1113
1174
  }
1114
1175
  } else {
1115
1176
  console.log(chalk.yellow(' ⬇️ Installed version is newer than available version'));
1116
-
1117
- const { action } = await inquirer.prompt([{
1118
- type: 'list',
1119
- name: 'action',
1120
- message: 'What would you like to do?',
1121
- choices: [
1122
- { name: 'Keep current version', value: 'skip' },
1123
- { name: 'Downgrade to available version', value: 'downgrade' },
1124
- { name: 'Cancel installation', value: 'cancel' }
1125
- ]
1126
- }]);
1127
-
1177
+
1178
+ const { action } = await inquirer.prompt([
1179
+ {
1180
+ type: 'list',
1181
+ name: 'action',
1182
+ message: 'What would you like to do?',
1183
+ choices: [
1184
+ { name: 'Keep current version', value: 'skip' },
1185
+ { name: 'Downgrade to available version', value: 'downgrade' },
1186
+ { name: 'Cancel installation', value: 'cancel' },
1187
+ ],
1188
+ },
1189
+ ]);
1190
+
1128
1191
  if (action === 'skip') {
1129
1192
  spinner.start();
1130
1193
  continue;
1131
1194
  } else if (action === 'cancel') {
1132
- console.log('Installation cancelled.');
1195
+ console.log('Installation cancelled.');
1133
1196
  process.exit(0);
1134
1197
  }
1135
1198
  }
1136
-
1199
+
1137
1200
  // If we get here, we're proceeding with installation
1138
1201
  spinner.start(`Removing old ${pack.name} installation...`);
1139
1202
  await fileManager.removeDirectory(expansionDotFolder);
1140
1203
  }
1141
1204
 
1142
1205
  const expansionPackDir = pack.path;
1143
-
1206
+
1144
1207
  // Ensure dedicated dot folder exists for this expansion pack
1145
1208
  expansionDotFolder = path.join(installDir, `.${packId}`);
1146
1209
  await fileManager.ensureDirectory(expansionDotFolder);
1147
-
1210
+
1148
1211
  // Define the folders to copy from expansion packs
1149
1212
  const foldersToSync = [
1150
1213
  'agents',
@@ -1155,35 +1218,34 @@ class Installer {
1155
1218
  'workflows',
1156
1219
  'data',
1157
1220
  'utils',
1158
- 'schemas'
1221
+ 'schemas',
1159
1222
  ];
1160
1223
 
1161
1224
  // Copy each folder if it exists
1162
1225
  for (const folder of foldersToSync) {
1163
1226
  const sourceFolder = path.join(expansionPackDir, folder);
1164
-
1227
+
1165
1228
  // Check if folder exists in expansion pack
1166
1229
  if (await fileManager.pathExists(sourceFolder)) {
1167
1230
  // Get all files in this folder
1168
1231
  const files = await resourceLocator.findFiles('**/*', {
1169
1232
  cwd: sourceFolder,
1170
- nodir: true
1233
+ nodir: true,
1171
1234
  });
1172
1235
 
1173
1236
  // Copy each file to the expansion pack's dot folder with {root} replacement
1174
1237
  for (const file of files) {
1175
1238
  const sourcePath = path.join(sourceFolder, file);
1176
- const destPath = path.join(expansionDotFolder, folder, file);
1177
-
1178
- const needsRootReplacement = file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml');
1239
+ const destinationPath = path.join(expansionDotFolder, folder, file);
1240
+
1241
+ const needsRootReplacement =
1242
+ file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml');
1179
1243
  let success = false;
1180
-
1181
- if (needsRootReplacement) {
1182
- success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, `.${packId}`);
1183
- } else {
1184
- success = await fileManager.copyFile(sourcePath, destPath);
1185
- }
1186
-
1244
+
1245
+ success = await (needsRootReplacement
1246
+ ? fileManager.copyFileWithRootReplacement(sourcePath, destinationPath, `.${packId}`)
1247
+ : fileManager.copyFile(sourcePath, destinationPath));
1248
+
1187
1249
  if (success) {
1188
1250
  installedFiles.push(path.join(`.${packId}`, folder, file));
1189
1251
  }
@@ -1194,17 +1256,29 @@ class Installer {
1194
1256
  // Copy config.yaml with {root} replacement
1195
1257
  const configPath = path.join(expansionPackDir, 'config.yaml');
1196
1258
  if (await fileManager.pathExists(configPath)) {
1197
- const configDestPath = path.join(expansionDotFolder, 'config.yaml');
1198
- if (await fileManager.copyFileWithRootReplacement(configPath, configDestPath, `.${packId}`)) {
1259
+ const configDestinationPath = path.join(expansionDotFolder, 'config.yaml');
1260
+ if (
1261
+ await fileManager.copyFileWithRootReplacement(
1262
+ configPath,
1263
+ configDestinationPath,
1264
+ `.${packId}`,
1265
+ )
1266
+ ) {
1199
1267
  installedFiles.push(path.join(`.${packId}`, 'config.yaml'));
1200
1268
  }
1201
1269
  }
1202
-
1270
+
1203
1271
  // Copy README if it exists with {root} replacement
1204
1272
  const readmePath = path.join(expansionPackDir, 'README.md');
1205
1273
  if (await fileManager.pathExists(readmePath)) {
1206
- const readmeDestPath = path.join(expansionDotFolder, 'README.md');
1207
- if (await fileManager.copyFileWithRootReplacement(readmePath, readmeDestPath, `.${packId}`)) {
1274
+ const readmeDestinationPath = path.join(expansionDotFolder, 'README.md');
1275
+ if (
1276
+ await fileManager.copyFileWithRootReplacement(
1277
+ readmePath,
1278
+ readmeDestinationPath,
1279
+ `.${packId}`,
1280
+ )
1281
+ ) {
1208
1282
  installedFiles.push(path.join(`.${packId}`, 'README.md'));
1209
1283
  }
1210
1284
  }
@@ -1212,10 +1286,16 @@ class Installer {
1212
1286
  // Copy common/ items to expansion pack folder
1213
1287
  spinner.text = `Copying common utilities to ${packId}...`;
1214
1288
  await this.copyCommonItems(installDir, `.${packId}`, spinner);
1215
-
1289
+
1216
1290
  // Check and resolve core dependencies
1217
- await this.resolveExpansionPackCoreDependencies(installDir, expansionDotFolder, packId, pack, spinner);
1218
-
1291
+ await this.resolveExpansionPackCoreDependencies(
1292
+ installDir,
1293
+ expansionDotFolder,
1294
+ packId,
1295
+ pack,
1296
+ spinner,
1297
+ );
1298
+
1219
1299
  // Check and resolve core agents referenced by teams
1220
1300
  await this.resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner);
1221
1301
 
@@ -1226,17 +1306,22 @@ class Installer {
1226
1306
  expansionPackId: packId,
1227
1307
  expansionPackName: pack.name,
1228
1308
  expansionPackVersion: pack.version,
1229
- ides: config.ides || [] // Use ides_setup instead of ide_setup
1309
+ ides: config.ides || [], // Use ides_setup instead of ide_setup
1230
1310
  };
1231
-
1311
+
1232
1312
  // Get all files installed in this expansion pack
1233
1313
  const foundFiles = await resourceLocator.findFiles('**/*', {
1234
1314
  cwd: expansionDotFolder,
1235
- nodir: true
1315
+ nodir: true,
1236
1316
  });
1237
- const expansionPackFiles = foundFiles.map(f => path.join(`.${packId}`, f));
1238
-
1239
- await fileManager.createExpansionPackManifest(installDir, packId, expansionConfig, expansionPackFiles);
1317
+ const expansionPackFiles = foundFiles.map((f) => path.join(`.${packId}`, f));
1318
+
1319
+ await fileManager.createExpansionPackManifest(
1320
+ installDir,
1321
+ packId,
1322
+ expansionConfig,
1323
+ expansionPackFiles,
1324
+ );
1240
1325
 
1241
1326
  console.log(chalk.green(`✓ Installed expansion pack: ${pack.name} to ${`.${packId}`}`));
1242
1327
  } catch (error) {
@@ -1248,63 +1333,96 @@ class Installer {
1248
1333
  return installedFiles;
1249
1334
  }
1250
1335
 
1251
- async resolveExpansionPackCoreDependencies(installDir, expansionDotFolder, packId, pack, spinner) {
1336
+ async resolveExpansionPackCoreDependencies(
1337
+ installDir,
1338
+ expansionDotFolder,
1339
+ packId,
1340
+ pack,
1341
+ spinner,
1342
+ ) {
1252
1343
  const yaml = require('js-yaml');
1253
- const fs = require('fs').promises;
1254
-
1344
+ const fs = require('node:fs').promises;
1345
+
1255
1346
  // Find all agent files in the expansion pack
1256
1347
  const agentFiles = await resourceLocator.findFiles('agents/*.md', {
1257
- cwd: expansionDotFolder
1348
+ cwd: expansionDotFolder,
1258
1349
  });
1259
1350
 
1260
1351
  for (const agentFile of agentFiles) {
1261
1352
  const agentPath = path.join(expansionDotFolder, agentFile);
1262
1353
  const agentContent = await fs.readFile(agentPath, 'utf8');
1263
-
1354
+
1264
1355
  // Extract YAML frontmatter to check dependencies
1265
1356
  const yamlContent = extractYamlFromAgent(agentContent);
1266
1357
  if (yamlContent) {
1267
1358
  try {
1268
1359
  const agentConfig = yaml.load(yamlContent);
1269
1360
  const dependencies = agentConfig.dependencies || {};
1270
-
1361
+
1271
1362
  // Check for core dependencies (those that don't exist in the expansion pack)
1272
- for (const depType of ['tasks', 'templates', 'checklists', 'workflows', 'utils', 'data']) {
1363
+ for (const depType of [
1364
+ 'tasks',
1365
+ 'templates',
1366
+ 'checklists',
1367
+ 'workflows',
1368
+ 'utils',
1369
+ 'data',
1370
+ ]) {
1273
1371
  const deps = dependencies[depType] || [];
1274
-
1372
+
1275
1373
  for (const dep of deps) {
1276
- const depFileName = dep.endsWith('.md') || dep.endsWith('.yaml') ? dep :
1277
- (depType === 'templates' ? `${dep}.yaml` : `${dep}.md`);
1374
+ const depFileName =
1375
+ dep.endsWith('.md') || dep.endsWith('.yaml')
1376
+ ? dep
1377
+ : depType === 'templates'
1378
+ ? `${dep}.yaml`
1379
+ : `${dep}.md`;
1278
1380
  const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
1279
-
1381
+
1280
1382
  // Check if dependency exists in expansion pack dot folder
1281
1383
  if (!(await fileManager.pathExists(expansionDepPath))) {
1282
1384
  // Try to find it in expansion pack source
1283
1385
  const sourceDepPath = path.join(pack.path, depType, depFileName);
1284
-
1386
+
1285
1387
  if (await fileManager.pathExists(sourceDepPath)) {
1286
1388
  // Copy from expansion pack source
1287
1389
  spinner.text = `Copying ${packId} dependency ${dep}...`;
1288
- const destPath = path.join(expansionDotFolder, depType, depFileName);
1289
- await fileManager.copyFileWithRootReplacement(sourceDepPath, destPath, `.${packId}`);
1390
+ const destinationPath = path.join(expansionDotFolder, depType, depFileName);
1391
+ await fileManager.copyFileWithRootReplacement(
1392
+ sourceDepPath,
1393
+ destinationPath,
1394
+ `.${packId}`,
1395
+ );
1290
1396
  console.log(chalk.dim(` Added ${packId} dependency: ${depType}/${depFileName}`));
1291
1397
  } else {
1292
1398
  // Try to find it in core
1293
- const coreDepPath = path.join(resourceLocator.getBmadCorePath(), depType, depFileName);
1294
-
1295
- if (await fileManager.pathExists(coreDepPath)) {
1296
- spinner.text = `Copying core dependency ${dep} for ${packId}...`;
1297
-
1298
- // Copy from core to expansion pack dot folder with {root} replacement
1299
- const destPath = path.join(expansionDotFolder, depType, depFileName);
1300
- await fileManager.copyFileWithRootReplacement(coreDepPath, destPath, `.${packId}`);
1301
-
1302
- console.log(chalk.dim(` Added core dependency: ${depType}/${depFileName}`));
1303
- } else {
1304
- console.warn(chalk.yellow(` Warning: Dependency ${depType}/${dep} not found in core or expansion pack`));
1305
- }
1399
+ const coreDepPath = path.join(
1400
+ resourceLocator.getBmadCorePath(),
1401
+ depType,
1402
+ depFileName,
1403
+ );
1404
+
1405
+ if (await fileManager.pathExists(coreDepPath)) {
1406
+ spinner.text = `Copying core dependency ${dep} for ${packId}...`;
1407
+
1408
+ // Copy from core to expansion pack dot folder with {root} replacement
1409
+ const destinationPath = path.join(expansionDotFolder, depType, depFileName);
1410
+ await fileManager.copyFileWithRootReplacement(
1411
+ coreDepPath,
1412
+ destinationPath,
1413
+ `.${packId}`,
1414
+ );
1415
+
1416
+ console.log(chalk.dim(` Added core dependency: ${depType}/${depFileName}`));
1417
+ } else {
1418
+ console.warn(
1419
+ chalk.yellow(
1420
+ ` Warning: Dependency ${depType}/${dep} not found in core or expansion pack`,
1421
+ ),
1422
+ );
1306
1423
  }
1307
1424
  }
1425
+ }
1308
1426
  }
1309
1427
  }
1310
1428
  } catch (error) {
@@ -1316,17 +1434,17 @@ class Installer {
1316
1434
 
1317
1435
  async resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner) {
1318
1436
  const yaml = require('js-yaml');
1319
- const fs = require('fs').promises;
1320
-
1437
+ const fs = require('node:fs').promises;
1438
+
1321
1439
  // Find all team files in the expansion pack
1322
1440
  const teamFiles = await resourceLocator.findFiles('agent-teams/*.yaml', {
1323
- cwd: expansionDotFolder
1441
+ cwd: expansionDotFolder,
1324
1442
  });
1325
1443
 
1326
1444
  // Also get existing agents in the expansion pack
1327
1445
  const existingAgents = new Set();
1328
1446
  const agentFiles = await resourceLocator.findFiles('agents/*.md', {
1329
- cwd: expansionDotFolder
1447
+ cwd: expansionDotFolder,
1330
1448
  });
1331
1449
  for (const agentFile of agentFiles) {
1332
1450
  const agentName = path.basename(agentFile, '.md');
@@ -1337,79 +1455,132 @@ class Installer {
1337
1455
  for (const teamFile of teamFiles) {
1338
1456
  const teamPath = path.join(expansionDotFolder, teamFile);
1339
1457
  const teamContent = await fs.readFile(teamPath, 'utf8');
1340
-
1458
+
1341
1459
  try {
1342
1460
  const teamConfig = yaml.load(teamContent);
1343
1461
  const agents = teamConfig.agents || [];
1344
-
1462
+
1345
1463
  // Add bmad-orchestrator if not present (required for all teams)
1346
1464
  if (!agents.includes('bmad-orchestrator')) {
1347
1465
  agents.unshift('bmad-orchestrator');
1348
1466
  }
1349
-
1467
+
1350
1468
  // Check each agent in the team
1351
1469
  for (const agentId of agents) {
1352
1470
  if (!existingAgents.has(agentId)) {
1353
1471
  // Agent not in expansion pack, try to get from core
1354
- const coreAgentPath = path.join(resourceLocator.getBmadCorePath(), 'agents', `${agentId}.md`);
1355
-
1472
+ const coreAgentPath = path.join(
1473
+ resourceLocator.getBmadCorePath(),
1474
+ 'agents',
1475
+ `${agentId}.md`,
1476
+ );
1477
+
1356
1478
  if (await fileManager.pathExists(coreAgentPath)) {
1357
1479
  spinner.text = `Copying core agent ${agentId} for ${packId}...`;
1358
-
1480
+
1359
1481
  // Copy agent file with {root} replacement
1360
- const destPath = path.join(expansionDotFolder, 'agents', `${agentId}.md`);
1361
- await fileManager.copyFileWithRootReplacement(coreAgentPath, destPath, `.${packId}`);
1482
+ const destinationPath = path.join(expansionDotFolder, 'agents', `${agentId}.md`);
1483
+ await fileManager.copyFileWithRootReplacement(
1484
+ coreAgentPath,
1485
+ destinationPath,
1486
+ `.${packId}`,
1487
+ );
1362
1488
  existingAgents.add(agentId);
1363
-
1489
+
1364
1490
  console.log(chalk.dim(` Added core agent: ${agentId}`));
1365
-
1491
+
1366
1492
  // Now resolve this agent's dependencies too
1367
1493
  const agentContent = await fs.readFile(coreAgentPath, 'utf8');
1368
1494
  const yamlContent = extractYamlFromAgent(agentContent, true);
1369
-
1495
+
1370
1496
  if (yamlContent) {
1371
1497
  try {
1372
-
1373
1498
  const agentConfig = yaml.load(yamlContent);
1374
1499
  const dependencies = agentConfig.dependencies || {};
1375
-
1500
+
1376
1501
  // Copy all dependencies for this agent
1377
- for (const depType of ['tasks', 'templates', 'checklists', 'workflows', 'utils', 'data']) {
1502
+ for (const depType of [
1503
+ 'tasks',
1504
+ 'templates',
1505
+ 'checklists',
1506
+ 'workflows',
1507
+ 'utils',
1508
+ 'data',
1509
+ ]) {
1378
1510
  const deps = dependencies[depType] || [];
1379
-
1511
+
1380
1512
  for (const dep of deps) {
1381
- const depFileName = dep.endsWith('.md') || dep.endsWith('.yaml') ? dep :
1382
- (depType === 'templates' ? `${dep}.yaml` : `${dep}.md`);
1513
+ const depFileName =
1514
+ dep.endsWith('.md') || dep.endsWith('.yaml')
1515
+ ? dep
1516
+ : depType === 'templates'
1517
+ ? `${dep}.yaml`
1518
+ : `${dep}.md`;
1383
1519
  const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
1384
-
1520
+
1385
1521
  // Check if dependency exists in expansion pack
1386
1522
  if (!(await fileManager.pathExists(expansionDepPath))) {
1387
1523
  // Try to find it in core
1388
- const coreDepPath = path.join(resourceLocator.getBmadCorePath(), depType, depFileName);
1389
-
1524
+ const coreDepPath = path.join(
1525
+ resourceLocator.getBmadCorePath(),
1526
+ depType,
1527
+ depFileName,
1528
+ );
1529
+
1390
1530
  if (await fileManager.pathExists(coreDepPath)) {
1391
- const destDepPath = path.join(expansionDotFolder, depType, depFileName);
1392
- await fileManager.copyFileWithRootReplacement(coreDepPath, destDepPath, `.${packId}`);
1393
- console.log(chalk.dim(` Added agent dependency: ${depType}/${depFileName}`));
1531
+ const destinationDepPath = path.join(
1532
+ expansionDotFolder,
1533
+ depType,
1534
+ depFileName,
1535
+ );
1536
+ await fileManager.copyFileWithRootReplacement(
1537
+ coreDepPath,
1538
+ destinationDepPath,
1539
+ `.${packId}`,
1540
+ );
1541
+ console.log(
1542
+ chalk.dim(` Added agent dependency: ${depType}/${depFileName}`),
1543
+ );
1394
1544
  } else {
1395
1545
  // Try common folder
1396
- const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
1397
- const commonDepPath = path.join(sourceBase, 'common', depType, depFileName);
1546
+ const sourceBase = path.dirname(
1547
+ path.dirname(path.dirname(path.dirname(__filename))),
1548
+ ); // Go up to project root
1549
+ const commonDepPath = path.join(
1550
+ sourceBase,
1551
+ 'common',
1552
+ depType,
1553
+ depFileName,
1554
+ );
1398
1555
  if (await fileManager.pathExists(commonDepPath)) {
1399
- const destDepPath = path.join(expansionDotFolder, depType, depFileName);
1400
- await fileManager.copyFile(commonDepPath, destDepPath);
1401
- console.log(chalk.dim(` Added agent dependency from common: ${depType}/${depFileName}`));
1556
+ const destinationDepPath = path.join(
1557
+ expansionDotFolder,
1558
+ depType,
1559
+ depFileName,
1560
+ );
1561
+ await fileManager.copyFile(commonDepPath, destinationDepPath);
1562
+ console.log(
1563
+ chalk.dim(
1564
+ ` Added agent dependency from common: ${depType}/${depFileName}`,
1565
+ ),
1566
+ );
1402
1567
  }
1403
1568
  }
1404
1569
  }
1405
1570
  }
1406
1571
  }
1407
1572
  } catch (error) {
1408
- console.warn(` Warning: Could not parse agent ${agentId} dependencies: ${error.message}`);
1573
+ console.warn(
1574
+ ` Warning: Could not parse agent ${agentId} dependencies: ${error.message}`,
1575
+ );
1409
1576
  }
1410
1577
  }
1411
1578
  } else {
1412
- console.warn(chalk.yellow(` Warning: Core agent ${agentId} not found for team ${path.basename(teamFile, '.yaml')}`));
1579
+ console.warn(
1580
+ chalk.yellow(
1581
+ ` Warning: Core agent ${agentId} not found for team ${path.basename(teamFile, '.yaml')}`,
1582
+ ),
1583
+ );
1413
1584
  }
1414
1585
  }
1415
1586
  }
@@ -1421,16 +1592,19 @@ class Installer {
1421
1592
 
1422
1593
  getWebBundleInfo(config) {
1423
1594
  const webBundleType = config.webBundleType || 'all';
1424
-
1595
+
1425
1596
  switch (webBundleType) {
1426
- case 'all':
1597
+ case 'all': {
1427
1598
  return 'all bundles';
1428
- case 'agents':
1599
+ }
1600
+ case 'agents': {
1429
1601
  return 'individual agents only';
1430
- case 'teams':
1431
- return config.selectedWebBundleTeams ?
1432
- `teams: ${config.selectedWebBundleTeams.join(', ')}` :
1433
- 'selected teams';
1602
+ }
1603
+ case 'teams': {
1604
+ return config.selectedWebBundleTeams
1605
+ ? `teams: ${config.selectedWebBundleTeams.join(', ')}`
1606
+ : 'selected teams';
1607
+ }
1434
1608
  case 'custom': {
1435
1609
  const parts = [];
1436
1610
  if (config.selectedWebBundleTeams && config.selectedWebBundleTeams.length > 0) {
@@ -1441,17 +1615,17 @@ class Installer {
1441
1615
  }
1442
1616
  return parts.length > 0 ? parts.join(' + ') : 'custom selection';
1443
1617
  }
1444
- default:
1618
+ default: {
1445
1619
  return 'selected bundles';
1620
+ }
1446
1621
  }
1447
1622
  }
1448
1623
 
1449
1624
  async installWebBundles(webBundlesDirectory, config, spinner) {
1450
-
1451
1625
  try {
1452
- // Find the dist directory in the XiaoMa installation
1626
+ // Find the dist directory in the BMad installation
1453
1627
  const distDir = configLoader.getDistPath();
1454
-
1628
+
1455
1629
  if (!(await fileManager.pathExists(distDir))) {
1456
1630
  console.warn('Web bundles not found. Run "npm run build" to generate them.');
1457
1631
  return;
@@ -1459,18 +1633,21 @@ class Installer {
1459
1633
 
1460
1634
  // Ensure web bundles directory exists
1461
1635
  await fileManager.ensureDirectory(webBundlesDirectory);
1462
-
1636
+
1463
1637
  const webBundleType = config.webBundleType || 'all';
1464
-
1638
+
1465
1639
  if (webBundleType === 'all') {
1466
1640
  // Copy the entire dist directory structure
1467
1641
  await fileManager.copyDirectory(distDir, webBundlesDirectory);
1468
1642
  console.log(chalk.green(`✓ Installed all web bundles to: ${webBundlesDirectory}`));
1469
1643
  } else {
1470
1644
  let copiedCount = 0;
1471
-
1645
+
1472
1646
  // Copy specific selections based on type
1473
- if (webBundleType === 'agents' || (webBundleType === 'custom' && config.includeIndividualAgents)) {
1647
+ if (
1648
+ webBundleType === 'agents' ||
1649
+ (webBundleType === 'custom' && config.includeIndividualAgents)
1650
+ ) {
1474
1651
  const agentsSource = path.join(distDir, 'agents');
1475
1652
  const agentsTarget = path.join(webBundlesDirectory, 'agents');
1476
1653
  if (await fileManager.pathExists(agentsSource)) {
@@ -1479,27 +1656,29 @@ class Installer {
1479
1656
  copiedCount += 10; // Approximate count for agents
1480
1657
  }
1481
1658
  }
1482
-
1483
- if (webBundleType === 'teams' || webBundleType === 'custom') {
1484
- if (config.selectedWebBundleTeams && config.selectedWebBundleTeams.length > 0) {
1485
- const teamsSource = path.join(distDir, 'teams');
1486
- const teamsTarget = path.join(webBundlesDirectory, 'teams');
1487
- await fileManager.ensureDirectory(teamsTarget);
1488
-
1489
- for (const teamId of config.selectedWebBundleTeams) {
1490
- const teamFile = `${teamId}.txt`;
1491
- const sourcePath = path.join(teamsSource, teamFile);
1492
- const targetPath = path.join(teamsTarget, teamFile);
1493
-
1494
- if (await fileManager.pathExists(sourcePath)) {
1495
- await fileManager.copyFile(sourcePath, targetPath);
1496
- copiedCount++;
1497
- console.log(chalk.green(`✓ Copied team bundle: ${teamId}`));
1498
- }
1659
+
1660
+ if (
1661
+ (webBundleType === 'teams' || webBundleType === 'custom') &&
1662
+ config.selectedWebBundleTeams &&
1663
+ config.selectedWebBundleTeams.length > 0
1664
+ ) {
1665
+ const teamsSource = path.join(distDir, 'teams');
1666
+ const teamsTarget = path.join(webBundlesDirectory, 'teams');
1667
+ await fileManager.ensureDirectory(teamsTarget);
1668
+
1669
+ for (const teamId of config.selectedWebBundleTeams) {
1670
+ const teamFile = `${teamId}.txt`;
1671
+ const sourcePath = path.join(teamsSource, teamFile);
1672
+ const targetPath = path.join(teamsTarget, teamFile);
1673
+
1674
+ if (await fileManager.pathExists(sourcePath)) {
1675
+ await fileManager.copyFile(sourcePath, targetPath);
1676
+ copiedCount++;
1677
+ console.log(chalk.green(`✓ Copied team bundle: ${teamId}`));
1499
1678
  }
1500
1679
  }
1501
1680
  }
1502
-
1681
+
1503
1682
  // Always copy expansion packs if they exist
1504
1683
  const expansionSource = path.join(distDir, 'expansion-packs');
1505
1684
  const expansionTarget = path.join(webBundlesDirectory, 'expansion-packs');
@@ -1507,8 +1686,10 @@ class Installer {
1507
1686
  await fileManager.copyDirectory(expansionSource, expansionTarget);
1508
1687
  console.log(chalk.green(`✓ Copied expansion pack bundles`));
1509
1688
  }
1510
-
1511
- console.log(chalk.green(`✓ Installed ${copiedCount} selected web bundles to: ${webBundlesDirectory}`));
1689
+
1690
+ console.log(
1691
+ chalk.green(`✓ Installed ${copiedCount} selected web bundles to: ${webBundlesDirectory}`),
1692
+ );
1512
1693
  }
1513
1694
  } catch (error) {
1514
1695
  console.error(`Failed to install web bundles: ${error.message}`);
@@ -1516,99 +1697,146 @@ class Installer {
1516
1697
  }
1517
1698
 
1518
1699
  async copyCommonItems(installDir, targetSubdir, spinner) {
1519
-
1520
- const fs = require('fs').promises;
1700
+ const fs = require('node:fs').promises;
1521
1701
  const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
1522
1702
  const commonPath = path.join(sourceBase, 'common');
1523
1703
  const targetPath = path.join(installDir, targetSubdir);
1524
1704
  const copiedFiles = [];
1525
-
1705
+
1526
1706
  // Check if common/ exists
1527
1707
  if (!(await fileManager.pathExists(commonPath))) {
1528
1708
  console.warn('Warning: common/ folder not found');
1529
1709
  return copiedFiles;
1530
1710
  }
1531
-
1711
+
1532
1712
  // Copy all items from common/ to target
1533
1713
  const commonItems = await resourceLocator.findFiles('**/*', {
1534
1714
  cwd: commonPath,
1535
- nodir: true
1715
+ nodir: true,
1536
1716
  });
1537
-
1717
+
1538
1718
  for (const item of commonItems) {
1539
1719
  const sourcePath = path.join(commonPath, item);
1540
- const destPath = path.join(targetPath, item);
1541
-
1720
+ const destinationPath = path.join(targetPath, item);
1721
+
1542
1722
  // Read the file content
1543
1723
  const content = await fs.readFile(sourcePath, 'utf8');
1544
-
1724
+
1545
1725
  // Replace {root} with the target subdirectory
1546
- const updatedContent = content.replace(/\{root\}/g, targetSubdir);
1547
-
1726
+ const updatedContent = content.replaceAll('{root}', targetSubdir);
1727
+
1548
1728
  // Ensure directory exists
1549
- await fileManager.ensureDirectory(path.dirname(destPath));
1550
-
1729
+ await fileManager.ensureDirectory(path.dirname(destinationPath));
1730
+
1551
1731
  // Write the updated content
1552
- await fs.writeFile(destPath, updatedContent, 'utf8');
1732
+ await fs.writeFile(destinationPath, updatedContent, 'utf8');
1553
1733
  copiedFiles.push(path.join(targetSubdir, item));
1554
1734
  }
1555
-
1735
+
1556
1736
  console.log(chalk.dim(` Added ${commonItems.length} common utilities`));
1557
1737
  return copiedFiles;
1558
1738
  }
1559
1739
 
1740
+ async copyDocsItems(installDir, targetSubdir, spinner) {
1741
+ const fs = require('node:fs').promises;
1742
+ const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
1743
+ const docsPath = path.join(sourceBase, 'docs');
1744
+ const targetPath = path.join(installDir, targetSubdir);
1745
+ const copiedFiles = [];
1746
+
1747
+ // Specific documentation files to copy
1748
+ const documentFiles = [
1749
+ 'enhanced-ide-development-workflow.md',
1750
+ 'user-guide.md',
1751
+ 'working-in-the-brownfield.md',
1752
+ ];
1753
+
1754
+ // Check if docs/ exists
1755
+ if (!(await fileManager.pathExists(docsPath))) {
1756
+ console.warn('Warning: docs/ folder not found');
1757
+ return copiedFiles;
1758
+ }
1759
+
1760
+ // Copy specific documentation files from docs/ to target
1761
+ for (const documentFile of documentFiles) {
1762
+ const sourcePath = path.join(docsPath, documentFile);
1763
+ const destinationPath = path.join(targetPath, documentFile);
1764
+
1765
+ // Check if the source file exists
1766
+ if (await fileManager.pathExists(sourcePath)) {
1767
+ // Read the file content
1768
+ const content = await fs.readFile(sourcePath, 'utf8');
1769
+
1770
+ // Replace {root} with the target subdirectory
1771
+ const updatedContent = content.replaceAll('{root}', targetSubdir);
1772
+
1773
+ // Ensure directory exists
1774
+ await fileManager.ensureDirectory(path.dirname(destinationPath));
1775
+
1776
+ // Write the updated content
1777
+ await fs.writeFile(destinationPath, updatedContent, 'utf8');
1778
+ copiedFiles.push(path.join(targetSubdir, documentFile));
1779
+ }
1780
+ }
1781
+
1782
+ if (copiedFiles.length > 0) {
1783
+ console.log(chalk.dim(` Added ${copiedFiles.length} documentation files`));
1784
+ }
1785
+ return copiedFiles;
1786
+ }
1787
+
1560
1788
  async detectExpansionPacks(installDir) {
1561
1789
  const expansionPacks = {};
1562
- const glob = require("glob");
1563
-
1790
+ const glob = require('glob');
1791
+
1564
1792
  // Find all dot folders that might be expansion packs
1565
- const dotFolders = glob.sync(".*", {
1793
+ const dotFolders = glob.sync('.*', {
1566
1794
  cwd: installDir,
1567
- ignore: [".git", ".git/**", ".xiaoma-core", ".xiaoma-core/**"],
1795
+ ignore: ['.git', '.git/**', '.xiaoma-core', '.xiaoma-core/**'],
1568
1796
  });
1569
-
1797
+
1570
1798
  for (const folder of dotFolders) {
1571
1799
  const folderPath = path.join(installDir, folder);
1572
1800
  const stats = await fileManager.pathExists(folderPath);
1573
-
1801
+
1574
1802
  if (stats) {
1575
1803
  // Check if it has a manifest
1576
- const manifestPath = path.join(folderPath, "install-manifest.yaml");
1804
+ const manifestPath = path.join(folderPath, 'install-manifest.yaml');
1577
1805
  if (await fileManager.pathExists(manifestPath)) {
1578
- const manifest = await fileManager.readExpansionPackManifest(installDir, folder.substring(1));
1806
+ const manifest = await fileManager.readExpansionPackManifest(installDir, folder.slice(1));
1579
1807
  if (manifest) {
1580
- expansionPacks[folder.substring(1)] = {
1808
+ expansionPacks[folder.slice(1)] = {
1581
1809
  path: folderPath,
1582
1810
  manifest: manifest,
1583
- hasManifest: true
1811
+ hasManifest: true,
1584
1812
  };
1585
1813
  }
1586
1814
  } else {
1587
1815
  // Check if it has a config.yaml (expansion pack without manifest)
1588
- const configPath = path.join(folderPath, "config.yaml");
1816
+ const configPath = path.join(folderPath, 'config.yaml');
1589
1817
  if (await fileManager.pathExists(configPath)) {
1590
- expansionPacks[folder.substring(1)] = {
1818
+ expansionPacks[folder.slice(1)] = {
1591
1819
  path: folderPath,
1592
1820
  manifest: null,
1593
- hasManifest: false
1821
+ hasManifest: false,
1594
1822
  };
1595
1823
  }
1596
1824
  }
1597
1825
  }
1598
1826
  }
1599
-
1827
+
1600
1828
  return expansionPacks;
1601
1829
  }
1602
1830
 
1603
1831
  async repairExpansionPack(installDir, packId, pack, integrity, spinner) {
1604
1832
  spinner.start(`Repairing ${pack.name}...`);
1605
-
1833
+
1606
1834
  try {
1607
1835
  const expansionDotFolder = path.join(installDir, `.${packId}`);
1608
-
1836
+
1609
1837
  // Back up modified files
1610
1838
  if (integrity.modified.length > 0) {
1611
- spinner.text = "Backing up modified files...";
1839
+ spinner.text = 'Backing up modified files...';
1612
1840
  for (const file of integrity.modified) {
1613
1841
  const filePath = path.join(installDir, file);
1614
1842
  if (await fileManager.pathExists(filePath)) {
@@ -1617,51 +1845,52 @@ class Installer {
1617
1845
  }
1618
1846
  }
1619
1847
  }
1620
-
1848
+
1621
1849
  // Restore missing and modified files
1622
- spinner.text = "Restoring files...";
1850
+ spinner.text = 'Restoring files...';
1623
1851
  const filesToRestore = [...integrity.missing, ...integrity.modified];
1624
-
1852
+
1625
1853
  for (const file of filesToRestore) {
1626
1854
  // Skip the manifest file itself
1627
1855
  if (file.endsWith('install-manifest.yaml')) continue;
1628
-
1856
+
1629
1857
  const relativePath = file.replace(`.${packId}/`, '');
1630
1858
  const sourcePath = path.join(pack.path, relativePath);
1631
- const destPath = path.join(installDir, file);
1632
-
1859
+ const destinationPath = path.join(installDir, file);
1860
+
1633
1861
  // Check if this is a common/ file that needs special processing
1634
1862
  const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
1635
1863
  const commonSourcePath = path.join(commonBase, 'common', relativePath);
1636
-
1864
+
1637
1865
  if (await fileManager.pathExists(commonSourcePath)) {
1638
1866
  // This is a common/ file - needs template processing
1639
- const fs = require('fs').promises;
1867
+ const fs = require('node:fs').promises;
1640
1868
  const content = await fs.readFile(commonSourcePath, 'utf8');
1641
- const updatedContent = content.replace(/\{root\}/g, `.${packId}`);
1642
- await fileManager.ensureDirectory(path.dirname(destPath));
1643
- await fs.writeFile(destPath, updatedContent, 'utf8');
1869
+ const updatedContent = content.replaceAll('{root}', `.${packId}`);
1870
+ await fileManager.ensureDirectory(path.dirname(destinationPath));
1871
+ await fs.writeFile(destinationPath, updatedContent, 'utf8');
1644
1872
  spinner.text = `Restored: ${file}`;
1645
1873
  } else if (await fileManager.pathExists(sourcePath)) {
1646
1874
  // Regular file from expansion pack
1647
- await fileManager.copyFile(sourcePath, destPath);
1875
+ await fileManager.copyFile(sourcePath, destinationPath);
1648
1876
  spinner.text = `Restored: ${file}`;
1649
1877
  } else {
1650
1878
  console.warn(chalk.yellow(` Warning: Source file not found: ${file}`));
1651
1879
  }
1652
1880
  }
1653
-
1881
+
1654
1882
  spinner.succeed(`${pack.name} repaired successfully!`);
1655
-
1883
+
1656
1884
  // Show summary
1657
1885
  console.log(chalk.green(`\n✓ ${pack.name} repaired!`));
1658
1886
  if (integrity.missing.length > 0) {
1659
1887
  console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
1660
1888
  }
1661
1889
  if (integrity.modified.length > 0) {
1662
- console.log(chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`));
1890
+ console.log(
1891
+ chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`),
1892
+ );
1663
1893
  }
1664
-
1665
1894
  } catch (error) {
1666
1895
  if (spinner) spinner.fail(`Failed to repair ${pack.name}`);
1667
1896
  console.error(`Error: ${error.message}`);
@@ -1672,37 +1901,37 @@ class Installer {
1672
1901
  // Simple semver comparison
1673
1902
  const parts1 = v1.split('.').map(Number);
1674
1903
  const parts2 = v2.split('.').map(Number);
1675
-
1676
- for (let i = 0; i < 3; i++) {
1677
- const part1 = parts1[i] || 0;
1678
- const part2 = parts2[i] || 0;
1679
-
1904
+
1905
+ for (let index = 0; index < 3; index++) {
1906
+ const part1 = parts1[index] || 0;
1907
+ const part2 = parts2[index] || 0;
1908
+
1680
1909
  if (part1 > part2) return 1;
1681
1910
  if (part1 < part2) return -1;
1682
1911
  }
1683
-
1912
+
1684
1913
  return 0;
1685
1914
  }
1686
1915
 
1687
1916
  async cleanupLegacyYmlFiles(installDir, spinner) {
1688
1917
  const glob = require('glob');
1689
- const fs = require('fs').promises;
1690
-
1918
+ const fs = require('node:fs').promises;
1919
+
1691
1920
  try {
1692
1921
  // Find all .yml files in the installation directory
1693
1922
  const ymlFiles = glob.sync('**/*.yml', {
1694
1923
  cwd: installDir,
1695
- ignore: ['**/node_modules/**', '**/.git/**']
1924
+ ignore: ['**/node_modules/**', '**/.git/**'],
1696
1925
  });
1697
-
1926
+
1698
1927
  let deletedCount = 0;
1699
-
1928
+
1700
1929
  for (const ymlFile of ymlFiles) {
1701
1930
  // Check if corresponding .yaml file exists
1702
1931
  const yamlFile = ymlFile.replace(/\.yml$/, '.yaml');
1703
1932
  const ymlPath = path.join(installDir, ymlFile);
1704
1933
  const yamlPath = path.join(installDir, yamlFile);
1705
-
1934
+
1706
1935
  if (await fileManager.pathExists(yamlPath)) {
1707
1936
  // .yaml counterpart exists, delete the .yml file
1708
1937
  await fs.unlink(ymlPath);
@@ -1710,11 +1939,10 @@ class Installer {
1710
1939
  console.log(chalk.dim(` Removed legacy: ${ymlFile} (replaced by ${yamlFile})`));
1711
1940
  }
1712
1941
  }
1713
-
1942
+
1714
1943
  if (deletedCount > 0) {
1715
1944
  console.log(chalk.green(`✓ Cleaned up ${deletedCount} legacy .yml files`));
1716
1945
  }
1717
-
1718
1946
  } catch (error) {
1719
1947
  console.warn(`Warning: Could not cleanup legacy .yml files: ${error.message}`);
1720
1948
  }
@@ -1725,21 +1953,21 @@ class Installer {
1725
1953
  let currentDir = process.cwd();
1726
1954
 
1727
1955
  while (currentDir !== path.dirname(currentDir)) {
1728
- const bmadDir = path.join(currentDir, ".xiaoma-core");
1729
- const manifestPath = path.join(bmadDir, "install-manifest.yaml");
1956
+ const bmadDir = path.join(currentDir, '.xiaoma-core');
1957
+ const manifestPath = path.join(bmadDir, 'install-manifest.yaml');
1730
1958
 
1731
1959
  if (await fileManager.pathExists(manifestPath)) {
1732
- return bmadDir;
1960
+ return currentDir; // Return parent directory, not .xiaoma-core itself
1733
1961
  }
1734
1962
 
1735
1963
  currentDir = path.dirname(currentDir);
1736
1964
  }
1737
1965
 
1738
1966
  // Also check if we're inside a .xiaoma-core directory
1739
- if (path.basename(process.cwd()) === ".xiaoma-core") {
1740
- const manifestPath = path.join(process.cwd(), "install-manifest.yaml");
1967
+ if (path.basename(process.cwd()) === '.xiaoma-core') {
1968
+ const manifestPath = path.join(process.cwd(), 'install-manifest.yaml');
1741
1969
  if (await fileManager.pathExists(manifestPath)) {
1742
- return process.cwd();
1970
+ return path.dirname(process.cwd()); // Return parent directory
1743
1971
  }
1744
1972
  }
1745
1973
 
@@ -1747,22 +1975,22 @@ class Installer {
1747
1975
  }
1748
1976
 
1749
1977
  async flatten(options) {
1750
- const { spawn } = require('child_process');
1978
+ const { spawn } = require('node:child_process');
1751
1979
  const flattenerPath = path.join(__dirname, '..', '..', 'flattener', 'main.js');
1752
-
1753
- const args = [];
1980
+
1981
+ const arguments_ = [];
1754
1982
  if (options.input) {
1755
- args.push('--input', options.input);
1983
+ arguments_.push('--input', options.input);
1756
1984
  }
1757
1985
  if (options.output) {
1758
- args.push('--output', options.output);
1986
+ arguments_.push('--output', options.output);
1759
1987
  }
1760
-
1761
- const child = spawn('node', [flattenerPath, ...args], {
1988
+
1989
+ const child = spawn('node', [flattenerPath, ...arguments_], {
1762
1990
  stdio: 'inherit',
1763
- cwd: process.cwd()
1991
+ cwd: process.cwd(),
1764
1992
  });
1765
-
1993
+
1766
1994
  child.on('exit', (code) => {
1767
1995
  process.exit(code);
1768
1996
  });