@zeyue0329/xiaoma-cli 1.0.49 → 6.0.0-alpha.14

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 (921) hide show
  1. package/README.md +0 -2
  2. package/custom/src/agents/commit-poet/commit-poet.agent.yaml +129 -0
  3. package/custom/src/agents/commit-poet/installation-guide.md +36 -0
  4. package/custom/src/agents/toolsmith/installation-guide.md +36 -0
  5. package/custom/src/agents/toolsmith/toolsmith-sidecar/instructions.md +70 -0
  6. package/custom/src/agents/toolsmith/toolsmith-sidecar/knowledge/bundlers.md +111 -0
  7. package/custom/src/agents/toolsmith/toolsmith-sidecar/knowledge/deploy.md +70 -0
  8. package/custom/src/agents/toolsmith/toolsmith-sidecar/knowledge/docs.md +114 -0
  9. package/custom/src/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +134 -0
  10. package/custom/src/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +160 -0
  11. package/custom/src/agents/toolsmith/toolsmith-sidecar/knowledge/tests.md +103 -0
  12. package/custom/src/agents/toolsmith/toolsmith-sidecar/memories.md +17 -0
  13. package/custom/src/agents/toolsmith/toolsmith.agent.yaml +108 -0
  14. package/docs/BUNDLE_DISTRIBUTION_SETUP.md +95 -0
  15. package/docs/agent-customization-guide.md +208 -0
  16. package/docs/custom-agent-installation.md +183 -0
  17. package/docs/document-sharding-guide.md +449 -0
  18. package/docs/ide-info/auggie.md +31 -0
  19. package/docs/ide-info/claude-code.md +25 -0
  20. package/docs/ide-info/cline.md +31 -0
  21. package/docs/ide-info/codex.md +21 -0
  22. package/docs/ide-info/crush.md +30 -0
  23. package/docs/ide-info/cursor.md +25 -0
  24. package/docs/ide-info/gemini.md +25 -0
  25. package/docs/ide-info/github-copilot.md +26 -0
  26. package/docs/ide-info/iflow.md +33 -0
  27. package/docs/ide-info/kilo.md +24 -0
  28. package/docs/ide-info/opencode.md +24 -0
  29. package/docs/ide-info/qwen.md +25 -0
  30. package/docs/ide-info/roo.md +27 -0
  31. package/docs/ide-info/rovo-dev.md +388 -0
  32. package/docs/ide-info/trae.md +25 -0
  33. package/docs/ide-info/windsurf.md +22 -0
  34. package/docs/index.md +144 -0
  35. package/docs/installers-bundlers/ide-injections.md +186 -0
  36. package/docs/installers-bundlers/installers-modules-platforms-reference.md +379 -0
  37. package/docs/rag/rag.md +812 -0
  38. package/docs/v4-to-v6-upgrade.md +220 -0
  39. package/docs/v6-open-items.md +17 -0
  40. package/docs/web-bundles-gemini-gpt-guide.md +468 -0
  41. package/eslint.config.mjs +133 -0
  42. package/package.json +41 -51
  43. package/prettier.config.mjs +32 -0
  44. package/src/core/_module-installer/install-config.yaml +29 -0
  45. package/src/core/_module-installer/installer.js +60 -0
  46. package/src/core/agents/xiaoma-master.agent.yaml +39 -0
  47. package/src/core/agents/xiaoma-web-orchestrator.agent.xml +113 -0
  48. package/src/core/resources/excalidraw/README.md +160 -0
  49. package/src/core/resources/excalidraw/excalidraw-helpers.md +127 -0
  50. package/src/core/resources/excalidraw/library-loader.md +50 -0
  51. package/src/core/resources/excalidraw/validate-json-instructions.md +79 -0
  52. package/src/core/tasks/advanced-elicitation-methods.csv +51 -0
  53. package/src/core/tasks/advanced-elicitation.xml +116 -0
  54. package/src/core/tasks/index-docs.xml +65 -0
  55. package/src/core/tasks/validate-workflow.xml +89 -0
  56. package/src/core/tasks/workflow.xml +235 -0
  57. package/src/core/tools/shard-doc.xml +109 -0
  58. package/src/core/workflows/brainstorming/brain-methods.csv +62 -0
  59. package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +196 -0
  60. package/src/core/workflows/brainstorming/steps/step-01b-continue.md +121 -0
  61. package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +224 -0
  62. package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +236 -0
  63. package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +208 -0
  64. package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +263 -0
  65. package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +339 -0
  66. package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +302 -0
  67. package/src/core/workflows/brainstorming/template.md +15 -0
  68. package/src/core/workflows/brainstorming/workflow.md +51 -0
  69. package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -0
  70. package/src/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +203 -0
  71. package/src/core/workflows/party-mode/steps/step-03-graceful-exit.md +159 -0
  72. package/src/core/workflows/party-mode/workflow.md +207 -0
  73. package/src/modules/cis/_module-installer/install-config.yaml +16 -0
  74. package/src/modules/cis/_module-installer/installer.js +92 -0
  75. package/src/modules/cis/agents/README.md +104 -0
  76. package/src/modules/cis/agents/brainstorming-coach.agent.yaml +29 -0
  77. package/src/modules/cis/agents/creative-problem-solver.agent.yaml +29 -0
  78. package/src/modules/cis/agents/design-thinking-coach.agent.yaml +29 -0
  79. package/src/modules/cis/agents/innovation-strategist.agent.yaml +29 -0
  80. package/src/modules/cis/agents/presentation-master.agent.yaml +61 -0
  81. package/src/modules/cis/agents/storyteller.agent.yaml +29 -0
  82. package/src/modules/cis/readme.md +153 -0
  83. package/src/modules/cis/teams/creative-squad.yaml +7 -0
  84. package/src/modules/cis/teams/default-party.csv +12 -0
  85. package/src/modules/cis/workflows/README.md +139 -0
  86. package/src/modules/cis/workflows/design-thinking/README.md +56 -0
  87. package/src/modules/cis/workflows/design-thinking/design-methods.csv +31 -0
  88. package/src/modules/cis/workflows/design-thinking/instructions.md +202 -0
  89. package/src/modules/cis/workflows/design-thinking/template.md +111 -0
  90. package/src/modules/cis/workflows/design-thinking/workflow.yaml +38 -0
  91. package/src/modules/cis/workflows/innovation-strategy/README.md +56 -0
  92. package/src/modules/cis/workflows/innovation-strategy/innovation-frameworks.csv +31 -0
  93. package/src/modules/cis/workflows/innovation-strategy/instructions.md +276 -0
  94. package/src/modules/cis/workflows/innovation-strategy/template.md +189 -0
  95. package/src/modules/cis/workflows/innovation-strategy/workflow.yaml +38 -0
  96. package/src/modules/cis/workflows/problem-solving/README.md +56 -0
  97. package/src/modules/cis/workflows/problem-solving/instructions.md +252 -0
  98. package/src/modules/cis/workflows/problem-solving/solving-methods.csv +31 -0
  99. package/src/modules/cis/workflows/problem-solving/template.md +165 -0
  100. package/src/modules/cis/workflows/problem-solving/workflow.yaml +38 -0
  101. package/src/modules/cis/workflows/storytelling/README.md +58 -0
  102. package/src/modules/cis/workflows/storytelling/instructions.md +293 -0
  103. package/src/modules/cis/workflows/storytelling/story-types.csv +26 -0
  104. package/src/modules/cis/workflows/storytelling/template.md +113 -0
  105. package/src/modules/cis/workflows/storytelling/workflow.yaml +38 -0
  106. package/src/modules/xmb/README.md +261 -0
  107. package/src/modules/xmb/_module-installer/install-config.yaml +28 -0
  108. package/src/modules/xmb/agents/xiaoma-builder.agent.yaml +71 -0
  109. package/src/modules/xmb/docs/agents/agent-compilation.md +340 -0
  110. package/src/modules/xmb/docs/agents/agent-menu-patterns.md +524 -0
  111. package/src/modules/xmb/docs/agents/expert-agent-architecture.md +364 -0
  112. package/src/modules/xmb/docs/agents/index.md +55 -0
  113. package/src/modules/xmb/docs/agents/kb.csv +0 -0
  114. package/src/modules/xmb/docs/agents/module-agent-architecture.md +367 -0
  115. package/src/modules/xmb/docs/agents/simple-agent-architecture.md +288 -0
  116. package/src/modules/xmb/docs/agents/understanding-agent-types.md +184 -0
  117. package/src/modules/xmb/docs/workflows/architecture.md +220 -0
  118. package/src/modules/xmb/docs/workflows/common-workflow-tools.csv +19 -0
  119. package/src/modules/xmb/docs/workflows/csv-data-file-standards.md +206 -0
  120. package/src/modules/xmb/docs/workflows/index.md +45 -0
  121. package/src/modules/xmb/docs/workflows/intent-vs-prescriptive-spectrum.md +220 -0
  122. package/src/modules/xmb/docs/workflows/kb.csv +0 -0
  123. package/src/modules/xmb/docs/workflows/step-template.md +283 -0
  124. package/src/modules/xmb/docs/workflows/terms.md +97 -0
  125. package/src/modules/xmb/docs/workflows/workflow-template.md +152 -0
  126. package/src/modules/xmb/reference/agents/expert-examples/journal-keeper/README.md +242 -0
  127. package/src/modules/xmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/breakthroughs.md +24 -0
  128. package/src/modules/xmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/instructions.md +108 -0
  129. package/src/modules/xmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/memories.md +46 -0
  130. package/src/modules/xmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/mood-patterns.md +39 -0
  131. package/src/modules/xmb/reference/agents/expert-examples/journal-keeper/journal-keeper.agent.yaml +152 -0
  132. package/src/modules/xmb/reference/agents/module-examples/README.md +50 -0
  133. package/src/modules/xmb/reference/agents/module-examples/security-engineer.agent.yaml +53 -0
  134. package/src/modules/xmb/reference/agents/module-examples/trend-analyst.agent.yaml +57 -0
  135. package/src/modules/xmb/reference/agents/simple-examples/README.md +223 -0
  136. package/src/modules/xmb/reference/agents/simple-examples/commit-poet.agent.yaml +126 -0
  137. package/src/modules/xmb/reference/readme.md +3 -0
  138. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/data/dietary-restrictions.csv +18 -0
  139. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/data/macro-calculator.csv +16 -0
  140. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/data/recipe-database.csv +28 -0
  141. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +177 -0
  142. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +150 -0
  143. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +164 -0
  144. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +152 -0
  145. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +182 -0
  146. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +167 -0
  147. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +194 -0
  148. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/templates/assessment-section.md +25 -0
  149. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/templates/nutrition-plan.md +68 -0
  150. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/templates/prep-schedule-section.md +29 -0
  151. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/templates/profile-section.md +47 -0
  152. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/templates/shopping-section.md +37 -0
  153. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/templates/strategy-section.md +18 -0
  154. package/src/modules/xmb/reference/workflows/meal-prep-nutrition/workflow.md +58 -0
  155. package/src/modules/xmb/workflows/create-agent/data/agent-validation-checklist.md +174 -0
  156. package/src/modules/xmb/workflows/create-agent/data/brainstorm-context.md +153 -0
  157. package/src/modules/xmb/workflows/create-agent/data/communication-presets.csv +61 -0
  158. package/src/modules/xmb/workflows/create-agent/data/info-and-installation-guide.md +17 -0
  159. package/src/modules/xmb/workflows/create-agent/data/reference/README.md +3 -0
  160. package/src/modules/xmb/workflows/create-agent/data/reference/agents/expert-examples/journal-keeper/README.md +242 -0
  161. package/src/modules/xmb/workflows/create-agent/data/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/breakthroughs.md +24 -0
  162. package/src/modules/xmb/workflows/create-agent/data/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/instructions.md +108 -0
  163. package/src/modules/xmb/workflows/create-agent/data/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/memories.md +46 -0
  164. package/src/modules/xmb/workflows/create-agent/data/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/mood-patterns.md +39 -0
  165. package/src/modules/xmb/workflows/create-agent/data/reference/agents/expert-examples/journal-keeper/journal-keeper.agent.yaml +152 -0
  166. package/src/modules/xmb/workflows/create-agent/data/reference/agents/module-examples/README.md +50 -0
  167. package/src/modules/xmb/workflows/create-agent/data/reference/agents/module-examples/security-engineer.agent.yaml +53 -0
  168. package/src/modules/xmb/workflows/create-agent/data/reference/agents/module-examples/trend-analyst.agent.yaml +57 -0
  169. package/src/modules/xmb/workflows/create-agent/data/reference/agents/simple-examples/README.md +223 -0
  170. package/src/modules/xmb/workflows/create-agent/data/reference/agents/simple-examples/commit-poet.agent.yaml +126 -0
  171. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/data/dietary-restrictions.csv +18 -0
  172. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/data/macro-calculator.csv +16 -0
  173. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/data/recipe-database.csv +28 -0
  174. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +177 -0
  175. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +150 -0
  176. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +164 -0
  177. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +152 -0
  178. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +182 -0
  179. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +167 -0
  180. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +194 -0
  181. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/templates/assessment-section.md +25 -0
  182. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/templates/nutrition-plan.md +68 -0
  183. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/templates/prep-schedule-section.md +29 -0
  184. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/templates/profile-section.md +47 -0
  185. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/templates/shopping-section.md +37 -0
  186. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/templates/strategy-section.md +18 -0
  187. package/src/modules/xmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/workflow.md +58 -0
  188. package/src/modules/xmb/workflows/create-agent/data/validation-complete.md +305 -0
  189. package/src/modules/xmb/workflows/create-agent/steps/step-01-brainstorm.md +145 -0
  190. package/src/modules/xmb/workflows/create-agent/steps/step-02-discover.md +210 -0
  191. package/src/modules/xmb/workflows/create-agent/steps/step-03-persona.md +260 -0
  192. package/src/modules/xmb/workflows/create-agent/steps/step-04-commands.md +237 -0
  193. package/src/modules/xmb/workflows/create-agent/steps/step-05-name.md +231 -0
  194. package/src/modules/xmb/workflows/create-agent/steps/step-06-build.md +224 -0
  195. package/src/modules/xmb/workflows/create-agent/steps/step-07-validate.md +234 -0
  196. package/src/modules/xmb/workflows/create-agent/steps/step-08-setup.md +179 -0
  197. package/src/modules/xmb/workflows/create-agent/steps/step-09-customize.md +197 -0
  198. package/src/modules/xmb/workflows/create-agent/steps/step-10-build-tools.md +180 -0
  199. package/src/modules/xmb/workflows/create-agent/steps/step-11-celebrate.md +222 -0
  200. package/src/modules/xmb/workflows/create-agent/templates/agent_commands.md +21 -0
  201. package/src/modules/xmb/workflows/create-agent/templates/agent_persona.md +25 -0
  202. package/src/modules/xmb/workflows/create-agent/templates/agent_purpose_and_type.md +23 -0
  203. package/src/modules/xmb/workflows/create-agent/workflow.md +91 -0
  204. package/src/modules/xmb/workflows/create-workflow/steps/step-01-init.md +168 -0
  205. package/src/modules/xmb/workflows/create-workflow/steps/step-02-gather.md +233 -0
  206. package/src/modules/xmb/workflows/create-workflow/steps/step-03-tools-overview.md +127 -0
  207. package/src/modules/xmb/workflows/create-workflow/steps/step-04-core-tools.md +145 -0
  208. package/src/modules/xmb/workflows/create-workflow/steps/step-05-memory-requirements.md +136 -0
  209. package/src/modules/xmb/workflows/create-workflow/steps/step-06-external-tools.md +154 -0
  210. package/src/modules/xmb/workflows/create-workflow/steps/step-07-installation-guidance.md +159 -0
  211. package/src/modules/xmb/workflows/create-workflow/steps/step-08-tools-summary.md +167 -0
  212. package/src/modules/xmb/workflows/create-workflow/steps/step-09-design.md +239 -0
  213. package/src/modules/xmb/workflows/create-workflow/steps/step-10-plan-review.md +215 -0
  214. package/src/modules/xmb/workflows/create-workflow/steps/step-11-build.md +262 -0
  215. package/src/modules/xmb/workflows/create-workflow/steps/step-12-review.md +270 -0
  216. package/src/modules/xmb/workflows/create-workflow/templates/build-summary.md +36 -0
  217. package/src/modules/xmb/workflows/create-workflow/templates/completion-section.md +39 -0
  218. package/src/modules/xmb/workflows/create-workflow/templates/content-template.md +21 -0
  219. package/src/modules/xmb/workflows/create-workflow/templates/design-section.md +53 -0
  220. package/src/modules/xmb/workflows/create-workflow/templates/project-info.md +18 -0
  221. package/src/modules/xmb/workflows/create-workflow/templates/requirements-section.md +47 -0
  222. package/src/modules/xmb/workflows/create-workflow/templates/review-section.md +56 -0
  223. package/src/modules/xmb/workflows/create-workflow/templates/step-file.md +139 -0
  224. package/src/modules/xmb/workflows/create-workflow/templates/workflow-plan.md +54 -0
  225. package/src/modules/xmb/workflows/create-workflow/templates/workflow.md +58 -0
  226. package/src/modules/xmb/workflows/create-workflow/workflow.md +58 -0
  227. package/src/modules/xmb/workflows/edit-agent/steps/step-01-discover-intent.md +134 -0
  228. package/src/modules/xmb/workflows/edit-agent/steps/step-02-analyze-agent.md +202 -0
  229. package/src/modules/xmb/workflows/edit-agent/steps/step-03-propose-changes.md +157 -0
  230. package/src/modules/xmb/workflows/edit-agent/steps/step-04-apply-changes.md +150 -0
  231. package/src/modules/xmb/workflows/edit-agent/steps/step-05-validate.md +150 -0
  232. package/src/modules/xmb/workflows/edit-agent/workflow.md +58 -0
  233. package/src/modules/xmb/workflows/edit-workflow/steps/step-01-analyze.md +221 -0
  234. package/src/modules/xmb/workflows/edit-workflow/steps/step-02-discover.md +253 -0
  235. package/src/modules/xmb/workflows/edit-workflow/steps/step-03-improve.md +217 -0
  236. package/src/modules/xmb/workflows/edit-workflow/steps/step-04-validate.md +193 -0
  237. package/src/modules/xmb/workflows/edit-workflow/steps/step-05-compliance-check.md +245 -0
  238. package/src/modules/xmb/workflows/edit-workflow/templates/completion-summary.md +75 -0
  239. package/src/modules/xmb/workflows/edit-workflow/templates/improvement-goals.md +68 -0
  240. package/src/modules/xmb/workflows/edit-workflow/templates/improvement-log.md +40 -0
  241. package/src/modules/xmb/workflows/edit-workflow/templates/validation-results.md +51 -0
  242. package/src/modules/xmb/workflows/edit-workflow/templates/workflow-analysis.md +56 -0
  243. package/src/modules/xmb/workflows/edit-workflow/workflow.md +58 -0
  244. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-01-validate-goal.md +152 -0
  245. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-02-workflow-validation.md +243 -0
  246. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-03-step-validation.md +274 -0
  247. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-04-file-validation.md +295 -0
  248. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-05-intent-spectrum-validation.md +264 -0
  249. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-06-web-subprocess-validation.md +360 -0
  250. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-07-holistic-analysis.md +258 -0
  251. package/src/modules/xmb/workflows/workflow-compliance-check/steps/step-08-generate-report.md +301 -0
  252. package/src/modules/xmb/workflows/workflow-compliance-check/templates/compliance-report.md +140 -0
  253. package/src/modules/xmb/workflows/workflow-compliance-check/workflow.md +58 -0
  254. package/src/modules/xmb/workflows-legacy/create-module/README.md +229 -0
  255. package/src/modules/xmb/workflows-legacy/create-module/brainstorm-context.md +137 -0
  256. package/src/modules/xmb/workflows-legacy/create-module/checklist.md +235 -0
  257. package/src/modules/xmb/workflows-legacy/create-module/installer-templates/install-config.yaml +92 -0
  258. package/src/modules/xmb/workflows-legacy/create-module/installer-templates/installer.js +231 -0
  259. package/src/modules/xmb/workflows-legacy/create-module/instructions.md +577 -0
  260. package/src/modules/xmb/workflows-legacy/create-module/module-structure.md +400 -0
  261. package/src/modules/xmb/workflows-legacy/create-module/workflow.yaml +52 -0
  262. package/src/modules/xmb/workflows-legacy/edit-module/README.md +187 -0
  263. package/src/modules/xmb/workflows-legacy/edit-module/checklist.md +165 -0
  264. package/src/modules/xmb/workflows-legacy/edit-module/instructions.md +341 -0
  265. package/src/modules/xmb/workflows-legacy/edit-module/workflow.yaml +34 -0
  266. package/src/modules/xmb/workflows-legacy/module-brief/README.md +264 -0
  267. package/src/modules/xmb/workflows-legacy/module-brief/checklist.md +116 -0
  268. package/src/modules/xmb/workflows-legacy/module-brief/instructions.md +268 -0
  269. package/src/modules/xmb/workflows-legacy/module-brief/template.md +275 -0
  270. package/src/modules/xmb/workflows-legacy/module-brief/workflow.yaml +36 -0
  271. package/src/modules/xmc/README.md +128 -0
  272. package/src/modules/xmc/_module-installer/install-config.yaml +53 -0
  273. package/src/modules/xmc/_module-installer/installer.js +131 -0
  274. package/src/modules/xmc/_module-installer/platform-specifics/claude-code.js +35 -0
  275. package/src/modules/xmc/_module-installer/platform-specifics/windsurf.js +32 -0
  276. package/src/modules/xmc/agents/analyst.agent.yaml +49 -0
  277. package/src/modules/xmc/agents/architect.agent.yaml +52 -0
  278. package/src/modules/xmc/agents/auto-iteration-orchestrator.agent.yaml +115 -0
  279. package/src/modules/xmc/agents/dev.agent.yaml +44 -0
  280. package/src/modules/xmc/agents/pm.agent.yaml +50 -0
  281. package/src/modules/xmc/agents/quick-flow-solo-dev.agent.yaml +36 -0
  282. package/src/modules/xmc/agents/sm.agent.yaml +55 -0
  283. package/src/modules/xmc/agents/tea.agent.yaml +70 -0
  284. package/src/modules/xmc/agents/tech-writer.agent.yaml +67 -0
  285. package/src/modules/xmc/agents/ux-designer.agent.yaml +45 -0
  286. package/src/modules/xmc/data/README.md +29 -0
  287. package/src/modules/xmc/data/documentation-standards.md +262 -0
  288. package/src/modules/xmc/data/project-context-template.md +40 -0
  289. package/src/modules/xmc/docs/README.md +252 -0
  290. package/src/modules/xmc/docs/agents-guide.md +952 -0
  291. package/src/modules/xmc/docs/brownfield-guide.md +750 -0
  292. package/src/modules/xmc/docs/enterprise-agentic-development.md +686 -0
  293. package/src/modules/xmc/docs/faq.md +561 -0
  294. package/src/modules/xmc/docs/glossary.md +303 -0
  295. package/src/modules/xmc/docs/images/workflow-method-greenfield.excalidraw +5174 -0
  296. package/src/modules/xmc/docs/images/workflow-method-greenfield.svg +2 -0
  297. package/src/modules/xmc/docs/iteration-development-guide.md +752 -0
  298. package/src/modules/xmc/docs/party-mode.md +224 -0
  299. package/src/modules/xmc/docs/quick-flow-solo-dev.md +337 -0
  300. package/src/modules/xmc/docs/quick-start.md +367 -0
  301. package/src/modules/xmc/docs/scale-adaptive-system.md +618 -0
  302. package/src/modules/xmc/docs/test-architecture.md +462 -0
  303. package/src/modules/xmc/docs/workflow-architecture-reference.md +366 -0
  304. package/src/modules/xmc/docs/workflow-document-project-reference.md +489 -0
  305. package/src/modules/xmc/docs/workflows-analysis.md +266 -0
  306. package/src/modules/xmc/docs/workflows-implementation.md +171 -0
  307. package/src/modules/xmc/docs/workflows-planning.md +451 -0
  308. package/src/modules/xmc/docs/workflows-solutioning.md +509 -0
  309. package/src/modules/xmc/docs/xiaoma-quick-flow.md +528 -0
  310. package/src/modules/xmc/sub-modules/claude-code/config.yaml +5 -0
  311. package/src/modules/xmc/sub-modules/claude-code/injections.yaml +242 -0
  312. package/src/modules/xmc/sub-modules/claude-code/readme.md +87 -0
  313. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-analysis/api-documenter.md +102 -0
  314. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-analysis/codebase-analyzer.md +82 -0
  315. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-analysis/data-analyst.md +101 -0
  316. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-analysis/pattern-detector.md +84 -0
  317. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/dependency-mapper.md +83 -0
  318. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/epic-optimizer.md +81 -0
  319. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/requirements-analyst.md +61 -0
  320. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/technical-decisions-curator.md +168 -0
  321. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/trend-spotter.md +115 -0
  322. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/user-journey-mapper.md +123 -0
  323. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-planning/user-researcher.md +72 -0
  324. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-research/market-researcher.md +51 -0
  325. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-research/tech-debt-auditor.md +106 -0
  326. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-review/document-reviewer.md +102 -0
  327. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-review/technical-evaluator.md +68 -0
  328. package/src/modules/xmc/sub-modules/claude-code/sub-agents/bmad-review/test-coverage-analyzer.md +108 -0
  329. package/src/modules/xmc/teams/default-party.csv +21 -0
  330. package/src/modules/xmc/teams/team-fullstack.yaml +12 -0
  331. package/src/modules/xmc/testarch/knowledge/api-request.md +303 -0
  332. package/src/modules/xmc/testarch/knowledge/auth-session.md +356 -0
  333. package/src/modules/xmc/testarch/knowledge/burn-in.md +273 -0
  334. package/src/modules/xmc/testarch/knowledge/ci-burn-in.md +675 -0
  335. package/src/modules/xmc/testarch/knowledge/component-tdd.md +486 -0
  336. package/src/modules/xmc/testarch/knowledge/contract-testing.md +957 -0
  337. package/src/modules/xmc/testarch/knowledge/data-factories.md +500 -0
  338. package/src/modules/xmc/testarch/knowledge/email-auth.md +721 -0
  339. package/src/modules/xmc/testarch/knowledge/error-handling.md +725 -0
  340. package/src/modules/xmc/testarch/knowledge/feature-flags.md +750 -0
  341. package/src/modules/xmc/testarch/knowledge/file-utils.md +260 -0
  342. package/src/modules/xmc/testarch/knowledge/fixture-architecture.md +401 -0
  343. package/src/modules/xmc/testarch/knowledge/fixtures-composition.md +382 -0
  344. package/src/modules/xmc/testarch/knowledge/intercept-network-call.md +280 -0
  345. package/src/modules/xmc/testarch/knowledge/log.md +294 -0
  346. package/src/modules/xmc/testarch/knowledge/network-error-monitor.md +272 -0
  347. package/src/modules/xmc/testarch/knowledge/network-first.md +486 -0
  348. package/src/modules/xmc/testarch/knowledge/network-recorder.md +265 -0
  349. package/src/modules/xmc/testarch/knowledge/nfr-criteria.md +670 -0
  350. package/src/modules/xmc/testarch/knowledge/overview.md +284 -0
  351. package/src/modules/xmc/testarch/knowledge/playwright-config.md +730 -0
  352. package/src/modules/xmc/testarch/knowledge/probability-impact.md +601 -0
  353. package/src/modules/xmc/testarch/knowledge/recurse.md +296 -0
  354. package/src/modules/xmc/testarch/knowledge/risk-governance.md +615 -0
  355. package/src/modules/xmc/testarch/knowledge/selective-testing.md +732 -0
  356. package/src/modules/xmc/testarch/knowledge/selector-resilience.md +527 -0
  357. package/src/modules/xmc/testarch/knowledge/test-healing-patterns.md +644 -0
  358. package/src/modules/xmc/testarch/knowledge/test-levels-framework.md +473 -0
  359. package/src/modules/xmc/testarch/knowledge/test-priorities-matrix.md +373 -0
  360. package/src/modules/xmc/testarch/knowledge/test-quality.md +664 -0
  361. package/src/modules/xmc/testarch/knowledge/timing-debugging.md +372 -0
  362. package/src/modules/xmc/testarch/knowledge/visual-debugging.md +524 -0
  363. package/src/modules/xmc/testarch/tea-index.csv +33 -0
  364. package/src/modules/xmc/workflows/1-analysis/product-brief/product-brief.template.md +8 -0
  365. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-01-init.md +192 -0
  366. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-01b-continue.md +167 -0
  367. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-02-vision.md +203 -0
  368. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-03-users.md +206 -0
  369. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-04-metrics.md +209 -0
  370. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-05-scope.md +223 -0
  371. package/src/modules/xmc/workflows/1-analysis/product-brief/steps/step-06-complete.md +199 -0
  372. package/src/modules/xmc/workflows/1-analysis/product-brief/workflow.md +58 -0
  373. package/src/modules/xmc/workflows/1-analysis/research/domain-steps/step-01-init.md +136 -0
  374. package/src/modules/xmc/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +228 -0
  375. package/src/modules/xmc/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +237 -0
  376. package/src/modules/xmc/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +205 -0
  377. package/src/modules/xmc/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +233 -0
  378. package/src/modules/xmc/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
  379. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
  380. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +235 -0
  381. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +198 -0
  382. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +247 -0
  383. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +257 -0
  384. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +175 -0
  385. package/src/modules/xmc/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
  386. package/src/modules/xmc/workflows/1-analysis/research/research.template.md +16 -0
  387. package/src/modules/xmc/workflows/1-analysis/research/technical-steps/step-01-init.md +136 -0
  388. package/src/modules/xmc/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +237 -0
  389. package/src/modules/xmc/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +246 -0
  390. package/src/modules/xmc/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +200 -0
  391. package/src/modules/xmc/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +237 -0
  392. package/src/modules/xmc/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
  393. package/src/modules/xmc/workflows/1-analysis/research/workflow.md +198 -0
  394. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +159 -0
  395. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +126 -0
  396. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +209 -0
  397. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +215 -0
  398. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +218 -0
  399. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +233 -0
  400. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +251 -0
  401. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +253 -0
  402. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +223 -0
  403. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +223 -0
  404. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +240 -0
  405. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +247 -0
  406. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +236 -0
  407. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +263 -0
  408. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +226 -0
  409. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
  410. package/src/modules/xmc/workflows/2-plan-workflows/create-ux-design/workflow.md +53 -0
  411. package/src/modules/xmc/workflows/2-plan-workflows/prd/domain-complexity.csv +13 -0
  412. package/src/modules/xmc/workflows/2-plan-workflows/prd/prd-template.md +9 -0
  413. package/src/modules/xmc/workflows/2-plan-workflows/prd/project-types.csv +11 -0
  414. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-01-init.md +161 -0
  415. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-01b-continue.md +123 -0
  416. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-02-discovery.md +275 -0
  417. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-03-success.md +271 -0
  418. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-04-journeys.md +272 -0
  419. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-05-domain.md +249 -0
  420. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-06-innovation.md +240 -0
  421. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-07-project-type.md +236 -0
  422. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-08-scoping.md +280 -0
  423. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-09-functional.md +251 -0
  424. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-10-nonfunctional.md +275 -0
  425. package/src/modules/xmc/workflows/2-plan-workflows/prd/steps/step-11-complete.md +210 -0
  426. package/src/modules/xmc/workflows/2-plan-workflows/prd/workflow.md +61 -0
  427. package/src/modules/xmc/workflows/3-solutioning/architecture/architecture-decision-template.md +13 -0
  428. package/src/modules/xmc/workflows/3-solutioning/architecture/data/domain-complexity.csv +11 -0
  429. package/src/modules/xmc/workflows/3-solutioning/architecture/data/project-types.csv +7 -0
  430. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-01-init.md +194 -0
  431. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-01b-continue.md +163 -0
  432. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-02-context.md +223 -0
  433. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-03-starter.md +330 -0
  434. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-04-decisions.md +317 -0
  435. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-05-patterns.md +358 -0
  436. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-06-structure.md +378 -0
  437. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-07-validation.md +358 -0
  438. package/src/modules/xmc/workflows/3-solutioning/architecture/steps/step-08-complete.md +351 -0
  439. package/src/modules/xmc/workflows/3-solutioning/architecture/workflow.md +48 -0
  440. package/src/modules/xmc/workflows/3-solutioning/create-epics-and-stories/epics-template.md +80 -0
  441. package/src/modules/xmc/workflows/3-solutioning/create-epics-and-stories/instructions.md +387 -0
  442. package/src/modules/xmc/workflows/3-solutioning/create-epics-and-stories/workflow.yaml +53 -0
  443. package/src/modules/xmc/workflows/3-solutioning/implementation-readiness/checklist.md +169 -0
  444. package/src/modules/xmc/workflows/3-solutioning/implementation-readiness/instructions.md +332 -0
  445. package/src/modules/xmc/workflows/3-solutioning/implementation-readiness/template.md +146 -0
  446. package/src/modules/xmc/workflows/3-solutioning/implementation-readiness/workflow.yaml +64 -0
  447. package/src/modules/xmc/workflows/4-implementation/code-review/instructions.xml +176 -0
  448. package/src/modules/xmc/workflows/4-implementation/code-review/workflow.yaml +54 -0
  449. package/src/modules/xmc/workflows/4-implementation/correct-course/checklist.md +279 -0
  450. package/src/modules/xmc/workflows/4-implementation/correct-course/instructions.md +206 -0
  451. package/src/modules/xmc/workflows/4-implementation/correct-course/workflow.yaml +58 -0
  452. package/src/modules/xmc/workflows/4-implementation/create-story/checklist.md +358 -0
  453. package/src/modules/xmc/workflows/4-implementation/create-story/instructions.xml +354 -0
  454. package/src/modules/xmc/workflows/4-implementation/create-story/template.md +51 -0
  455. package/src/modules/xmc/workflows/4-implementation/create-story/workflow.yaml +60 -0
  456. package/src/modules/xmc/workflows/4-implementation/dev-story/checklist.md +80 -0
  457. package/src/modules/xmc/workflows/4-implementation/dev-story/instructions.xml +406 -0
  458. package/src/modules/xmc/workflows/4-implementation/dev-story/workflow.yaml +27 -0
  459. package/src/modules/xmc/workflows/4-implementation/retrospective/instructions.md +1443 -0
  460. package/src/modules/xmc/workflows/4-implementation/retrospective/workflow.yaml +57 -0
  461. package/src/modules/xmc/workflows/4-implementation/sprint-planning/checklist.md +33 -0
  462. package/src/modules/xmc/workflows/4-implementation/sprint-planning/instructions.md +232 -0
  463. package/src/modules/xmc/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +56 -0
  464. package/src/modules/xmc/workflows/4-implementation/sprint-planning/workflow.yaml +53 -0
  465. package/src/modules/xmc/workflows/auto-iteration/full-auto-workflow.md +692 -0
  466. package/src/modules/xmc/workflows/auto-iteration/knowledge-base/kb-call-points.md +595 -0
  467. package/src/modules/xmc/workflows/auto-iteration/knowledge-base/kb-interface.md +347 -0
  468. package/src/modules/xmc/workflows/auto-iteration/knowledge-base/kb-query.md +368 -0
  469. package/src/modules/xmc/workflows/auto-iteration/knowledge-base/kb-setup.md +343 -0
  470. package/src/modules/xmc/workflows/auto-iteration/phases/phase-1-analyze.md +406 -0
  471. package/src/modules/xmc/workflows/auto-iteration/phases/phase-2-plan.md +574 -0
  472. package/src/modules/xmc/workflows/auto-iteration/phases/phase-3-design.md +628 -0
  473. package/src/modules/xmc/workflows/auto-iteration/phases/phase-4-develop.md +622 -0
  474. package/src/modules/xmc/workflows/auto-iteration/phases/phase-5-test.md +538 -0
  475. package/src/modules/xmc/workflows/auto-iteration/resume.md +254 -0
  476. package/src/modules/xmc/workflows/auto-iteration/status.md +194 -0
  477. package/src/modules/xmc/workflows/auto-iteration/templates/auto-iteration-status.template.yaml +142 -0
  478. package/src/modules/xmc/workflows/bmad-quick-flow/create-tech-spec/instructions.md +115 -0
  479. package/src/modules/xmc/workflows/bmad-quick-flow/create-tech-spec/workflow.yaml +26 -0
  480. package/src/modules/xmc/workflows/bmad-quick-flow/quick-dev/checklist.md +25 -0
  481. package/src/modules/xmc/workflows/bmad-quick-flow/quick-dev/instructions.md +105 -0
  482. package/src/modules/xmc/workflows/bmad-quick-flow/quick-dev/workflow.yaml +29 -0
  483. package/src/modules/xmc/workflows/diagrams/_shared/excalidraw-library.json +90 -0
  484. package/src/modules/xmc/workflows/diagrams/_shared/excalidraw-templates.yaml +127 -0
  485. package/src/modules/xmc/workflows/diagrams/create-dataflow/checklist.md +39 -0
  486. package/src/modules/xmc/workflows/diagrams/create-dataflow/instructions.md +130 -0
  487. package/src/modules/xmc/workflows/diagrams/create-dataflow/workflow.yaml +27 -0
  488. package/src/modules/xmc/workflows/diagrams/create-diagram/checklist.md +43 -0
  489. package/src/modules/xmc/workflows/diagrams/create-diagram/instructions.md +141 -0
  490. package/src/modules/xmc/workflows/diagrams/create-diagram/workflow.yaml +27 -0
  491. package/src/modules/xmc/workflows/diagrams/create-flowchart/checklist.md +49 -0
  492. package/src/modules/xmc/workflows/diagrams/create-flowchart/instructions.md +241 -0
  493. package/src/modules/xmc/workflows/diagrams/create-flowchart/workflow.yaml +27 -0
  494. package/src/modules/xmc/workflows/diagrams/create-wireframe/checklist.md +38 -0
  495. package/src/modules/xmc/workflows/diagrams/create-wireframe/instructions.md +133 -0
  496. package/src/modules/xmc/workflows/diagrams/create-wireframe/workflow.yaml +27 -0
  497. package/src/modules/xmc/workflows/document-project/checklist.md +245 -0
  498. package/src/modules/xmc/workflows/document-project/documentation-requirements.csv +12 -0
  499. package/src/modules/xmc/workflows/document-project/instructions.md +222 -0
  500. package/src/modules/xmc/workflows/document-project/templates/deep-dive-template.md +345 -0
  501. package/src/modules/xmc/workflows/document-project/templates/index-template.md +169 -0
  502. package/src/modules/xmc/workflows/document-project/templates/project-overview-template.md +103 -0
  503. package/src/modules/xmc/workflows/document-project/templates/project-scan-report-schema.json +160 -0
  504. package/src/modules/xmc/workflows/document-project/templates/source-tree-template.md +135 -0
  505. package/src/modules/xmc/workflows/document-project/workflow.yaml +31 -0
  506. package/src/modules/xmc/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
  507. package/src/modules/xmc/workflows/document-project/workflows/deep-dive.yaml +31 -0
  508. package/src/modules/xmc/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
  509. package/src/modules/xmc/workflows/document-project/workflows/full-scan.yaml +31 -0
  510. package/src/modules/xmc/workflows/generate-project-context/project-context-template.md +20 -0
  511. package/src/modules/xmc/workflows/generate-project-context/steps/step-01-discover.md +193 -0
  512. package/src/modules/xmc/workflows/generate-project-context/steps/step-02-generate.md +317 -0
  513. package/src/modules/xmc/workflows/generate-project-context/steps/step-03-complete.md +277 -0
  514. package/src/modules/xmc/workflows/generate-project-context/workflow.md +48 -0
  515. package/src/modules/xmc/workflows/testarch/atdd/atdd-checklist-template.md +363 -0
  516. package/src/modules/xmc/workflows/testarch/atdd/checklist.md +373 -0
  517. package/src/modules/xmc/workflows/testarch/atdd/instructions.md +805 -0
  518. package/src/modules/xmc/workflows/testarch/atdd/workflow.yaml +47 -0
  519. package/src/modules/xmc/workflows/testarch/automate/checklist.md +580 -0
  520. package/src/modules/xmc/workflows/testarch/automate/instructions.md +1324 -0
  521. package/src/modules/xmc/workflows/testarch/automate/workflow.yaml +54 -0
  522. package/src/modules/xmc/workflows/testarch/ci/checklist.md +246 -0
  523. package/src/modules/xmc/workflows/testarch/ci/github-actions-template.yaml +165 -0
  524. package/src/modules/xmc/workflows/testarch/ci/gitlab-ci-template.yaml +128 -0
  525. package/src/modules/xmc/workflows/testarch/ci/instructions.md +534 -0
  526. package/src/modules/xmc/workflows/testarch/ci/workflow.yaml +47 -0
  527. package/src/modules/xmc/workflows/testarch/framework/checklist.md +321 -0
  528. package/src/modules/xmc/workflows/testarch/framework/instructions.md +481 -0
  529. package/src/modules/xmc/workflows/testarch/framework/workflow.yaml +49 -0
  530. package/src/modules/xmc/workflows/testarch/nfr-assess/checklist.md +405 -0
  531. package/src/modules/xmc/workflows/testarch/nfr-assess/instructions.md +722 -0
  532. package/src/modules/xmc/workflows/testarch/nfr-assess/nfr-report-template.md +443 -0
  533. package/src/modules/xmc/workflows/testarch/nfr-assess/workflow.yaml +49 -0
  534. package/src/modules/xmc/workflows/testarch/test-design/checklist.md +234 -0
  535. package/src/modules/xmc/workflows/testarch/test-design/instructions.md +788 -0
  536. package/src/modules/xmc/workflows/testarch/test-design/test-design-template.md +285 -0
  537. package/src/modules/xmc/workflows/testarch/test-design/workflow.yaml +50 -0
  538. package/src/modules/xmc/workflows/testarch/test-review/checklist.md +470 -0
  539. package/src/modules/xmc/workflows/testarch/test-review/instructions.md +628 -0
  540. package/src/modules/xmc/workflows/testarch/test-review/test-review-template.md +388 -0
  541. package/src/modules/xmc/workflows/testarch/test-review/workflow.yaml +48 -0
  542. package/src/modules/xmc/workflows/testarch/trace/checklist.md +654 -0
  543. package/src/modules/xmc/workflows/testarch/trace/instructions.md +1045 -0
  544. package/src/modules/xmc/workflows/testarch/trace/trace-template.md +673 -0
  545. package/src/modules/xmc/workflows/testarch/trace/workflow.yaml +57 -0
  546. package/src/modules/xmc/workflows/workflow-status/init/instructions.md +331 -0
  547. package/src/modules/xmc/workflows/workflow-status/init/workflow.yaml +29 -0
  548. package/src/modules/xmc/workflows/workflow-status/instructions.md +395 -0
  549. package/src/modules/xmc/workflows/workflow-status/paths/enterprise-brownfield.yaml +127 -0
  550. package/src/modules/xmc/workflows/workflow-status/paths/enterprise-greenfield.yaml +115 -0
  551. package/src/modules/xmc/workflows/workflow-status/paths/method-brownfield.yaml +111 -0
  552. package/src/modules/xmc/workflows/workflow-status/paths/method-greenfield.yaml +102 -0
  553. package/src/modules/xmc/workflows/workflow-status/project-levels.yaml +59 -0
  554. package/src/modules/xmc/workflows/workflow-status/workflow-status-template.yaml +24 -0
  555. package/src/modules/xmc/workflows/workflow-status/workflow.yaml +30 -0
  556. package/src/utility/models/action-command-header.md +0 -0
  557. package/src/utility/models/agent-activation-ide.xml +51 -0
  558. package/src/utility/models/agent-activation-web.xml +50 -0
  559. package/src/utility/models/agent-command-header.md +1 -0
  560. package/src/utility/models/agent-config-template.md +23 -0
  561. package/src/utility/models/agent-in-team-activation.xml +3 -0
  562. package/src/utility/models/fragments/activation-rules.xml +7 -0
  563. package/src/utility/models/fragments/activation-steps.xml +16 -0
  564. package/src/utility/models/fragments/handler-action.xml +4 -0
  565. package/src/utility/models/fragments/handler-data.xml +5 -0
  566. package/src/utility/models/fragments/handler-exec.xml +6 -0
  567. package/src/utility/models/fragments/handler-multi.xml +14 -0
  568. package/src/utility/models/fragments/handler-tmpl.xml +5 -0
  569. package/src/utility/models/fragments/handler-validate-workflow.xml +7 -0
  570. package/src/utility/models/fragments/handler-workflow.xml +9 -0
  571. package/src/utility/models/fragments/menu-handlers.xml +6 -0
  572. package/src/utility/models/fragments/web-bundle-activation-steps.xml +32 -0
  573. package/src/utility/templates/agent.customize.template.yaml +42 -0
  574. package/test/README.md +295 -0
  575. package/test/fixtures/agent-schema/invalid/critical-actions/actions-as-string.agent.yaml +26 -0
  576. package/test/fixtures/agent-schema/invalid/critical-actions/empty-string-in-actions.agent.yaml +29 -0
  577. package/test/fixtures/agent-schema/invalid/menu/empty-menu.agent.yaml +21 -0
  578. package/test/fixtures/agent-schema/invalid/menu/missing-menu.agent.yaml +19 -0
  579. package/test/fixtures/agent-schema/invalid/menu-commands/empty-command-target.agent.yaml +24 -0
  580. package/test/fixtures/agent-schema/invalid/menu-commands/no-command-target.agent.yaml +23 -0
  581. package/test/fixtures/agent-schema/invalid/menu-triggers/camel-case.agent.yaml +24 -0
  582. package/test/fixtures/agent-schema/invalid/menu-triggers/duplicate-triggers.agent.yaml +30 -0
  583. package/test/fixtures/agent-schema/invalid/menu-triggers/empty-trigger.agent.yaml +24 -0
  584. package/test/fixtures/agent-schema/invalid/menu-triggers/leading-asterisk.agent.yaml +24 -0
  585. package/test/fixtures/agent-schema/invalid/menu-triggers/snake-case.agent.yaml +24 -0
  586. package/test/fixtures/agent-schema/invalid/menu-triggers/trigger-with-spaces.agent.yaml +24 -0
  587. package/test/fixtures/agent-schema/invalid/metadata/core-agent-with-module.agent.yaml +26 -0
  588. package/test/fixtures/agent-schema/invalid/metadata/empty-module-string.agent.yaml +26 -0
  589. package/test/fixtures/agent-schema/invalid/metadata/empty-name.agent.yaml +24 -0
  590. package/test/fixtures/agent-schema/invalid/metadata/extra-metadata-fields.agent.yaml +26 -0
  591. package/test/fixtures/agent-schema/invalid/metadata/missing-id.agent.yaml +23 -0
  592. package/test/fixtures/agent-schema/invalid/metadata/module-agent-missing-module.agent.yaml +25 -0
  593. package/test/fixtures/agent-schema/invalid/metadata/wrong-module-value.agent.yaml +26 -0
  594. package/test/fixtures/agent-schema/invalid/persona/empty-principles-array.agent.yaml +23 -0
  595. package/test/fixtures/agent-schema/invalid/persona/empty-string-in-principles.agent.yaml +26 -0
  596. package/test/fixtures/agent-schema/invalid/persona/extra-persona-fields.agent.yaml +26 -0
  597. package/test/fixtures/agent-schema/invalid/persona/missing-role.agent.yaml +23 -0
  598. package/test/fixtures/agent-schema/invalid/prompts/empty-content.agent.yaml +28 -0
  599. package/test/fixtures/agent-schema/invalid/prompts/extra-prompt-fields.agent.yaml +30 -0
  600. package/test/fixtures/agent-schema/invalid/prompts/missing-content.agent.yaml +27 -0
  601. package/test/fixtures/agent-schema/invalid/prompts/missing-id.agent.yaml +27 -0
  602. package/test/fixtures/agent-schema/invalid/top-level/empty-file.agent.yaml +5 -0
  603. package/test/fixtures/agent-schema/invalid/top-level/extra-top-level-keys.agent.yaml +27 -0
  604. package/test/fixtures/agent-schema/invalid/top-level/missing-agent-key.agent.yaml +11 -0
  605. package/test/fixtures/agent-schema/invalid/yaml-errors/invalid-indentation.agent.yaml +19 -0
  606. package/test/fixtures/agent-schema/invalid/yaml-errors/malformed-yaml.agent.yaml +18 -0
  607. package/test/fixtures/agent-schema/valid/critical-actions/empty-critical-actions.agent.yaml +23 -0
  608. package/test/fixtures/agent-schema/valid/critical-actions/no-critical-actions.agent.yaml +21 -0
  609. package/test/fixtures/agent-schema/valid/critical-actions/valid-critical-actions.agent.yaml +26 -0
  610. package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +30 -0
  611. package/test/fixtures/agent-schema/valid/menu/single-menu-item.agent.yaml +21 -0
  612. package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +37 -0
  613. package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +23 -0
  614. package/test/fixtures/agent-schema/valid/menu-triggers/kebab-case-triggers.agent.yaml +33 -0
  615. package/test/fixtures/agent-schema/valid/metadata/empty-module-name-in-path.agent.yaml +23 -0
  616. package/test/fixtures/agent-schema/valid/metadata/malformed-path-treated-as-core.agent.yaml +23 -0
  617. package/test/fixtures/agent-schema/valid/metadata/module-agent-correct.agent.yaml +23 -0
  618. package/test/fixtures/agent-schema/valid/persona/complete-persona.agent.yaml +23 -0
  619. package/test/fixtures/agent-schema/valid/prompts/empty-prompts.agent.yaml +23 -0
  620. package/test/fixtures/agent-schema/valid/prompts/no-prompts.agent.yaml +21 -0
  621. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-minimal.agent.yaml +27 -0
  622. package/test/fixtures/agent-schema/valid/prompts/valid-prompts-with-description.agent.yaml +29 -0
  623. package/test/fixtures/agent-schema/valid/top-level/minimal-core-agent.agent.yaml +23 -0
  624. package/test/test-agent-schema.js +387 -0
  625. package/test/test-cli-integration.sh +159 -0
  626. package/test/test-installation-components.js +214 -0
  627. package/test/unit-test-schema.js +133 -0
  628. package/tools/cli/README.md +609 -0
  629. package/tools/cli/bundlers/bundle-web.js +179 -0
  630. package/tools/cli/bundlers/test-analyst.js +28 -0
  631. package/tools/cli/bundlers/test-bundler.js +119 -0
  632. package/tools/cli/bundlers/web-bundler.js +1764 -0
  633. package/tools/cli/commands/agent-install.js +409 -0
  634. package/tools/cli/commands/build.js +458 -0
  635. package/tools/cli/commands/cleanup.js +141 -0
  636. package/tools/cli/commands/install.js +124 -0
  637. package/tools/cli/commands/list.js +28 -0
  638. package/tools/cli/commands/status.js +47 -0
  639. package/tools/cli/commands/uninstall.js +44 -0
  640. package/tools/cli/commands/update.js +28 -0
  641. package/tools/cli/installers/lib/core/config-collector.js +876 -0
  642. package/tools/cli/installers/lib/core/dependency-resolver.js +725 -0
  643. package/tools/cli/installers/lib/core/detector.js +329 -0
  644. package/tools/cli/installers/lib/core/ide-config-manager.js +154 -0
  645. package/tools/cli/installers/lib/core/installer.js +2956 -0
  646. package/tools/cli/installers/lib/core/manifest-generator.js +692 -0
  647. package/tools/cli/installers/lib/core/manifest.js +540 -0
  648. package/tools/cli/installers/lib/ide/_base-ide.js +651 -0
  649. package/tools/cli/installers/lib/ide/antigravity.js +510 -0
  650. package/tools/cli/installers/lib/ide/auggie.js +232 -0
  651. package/tools/cli/installers/lib/ide/claude-code.js +512 -0
  652. package/tools/cli/installers/lib/ide/cline.js +269 -0
  653. package/tools/cli/installers/lib/ide/codex.js +388 -0
  654. package/tools/cli/installers/lib/ide/crush.js +287 -0
  655. package/tools/cli/installers/lib/ide/cursor.js +400 -0
  656. package/tools/cli/installers/lib/ide/gemini.js +253 -0
  657. package/tools/cli/installers/lib/ide/github-copilot.js +387 -0
  658. package/tools/cli/installers/lib/ide/iflow.js +172 -0
  659. package/tools/cli/installers/lib/ide/kilo.js +249 -0
  660. package/tools/cli/installers/lib/ide/manager.js +245 -0
  661. package/tools/cli/installers/lib/ide/opencode.js +257 -0
  662. package/tools/cli/installers/lib/ide/qwen.js +372 -0
  663. package/tools/cli/installers/lib/ide/roo.js +324 -0
  664. package/tools/cli/installers/lib/ide/rovo-dev.js +290 -0
  665. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +90 -0
  666. package/tools/cli/installers/lib/ide/shared/module-injections.js +133 -0
  667. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +119 -0
  668. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +237 -0
  669. package/tools/cli/installers/lib/ide/shared/xiaoma-artifacts.js +143 -0
  670. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +14 -0
  671. package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +14 -0
  672. package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +12 -0
  673. package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +13 -0
  674. package/tools/cli/installers/lib/ide/trae.js +313 -0
  675. package/tools/cli/installers/lib/ide/windsurf.js +258 -0
  676. package/tools/cli/installers/lib/modules/manager.js +751 -0
  677. package/tools/cli/lib/activation-builder.js +168 -0
  678. package/tools/cli/lib/agent/compiler.js +524 -0
  679. package/tools/cli/lib/agent/installer.js +735 -0
  680. package/tools/cli/lib/agent/template-engine.js +152 -0
  681. package/tools/cli/lib/agent-analyzer.js +109 -0
  682. package/tools/cli/lib/agent-party-generator.js +206 -0
  683. package/tools/cli/lib/cli-utils.js +210 -0
  684. package/tools/cli/lib/config.js +212 -0
  685. package/tools/cli/lib/file-ops.js +204 -0
  686. package/tools/cli/lib/platform-codes.js +116 -0
  687. package/tools/cli/lib/project-root.js +71 -0
  688. package/tools/cli/lib/replace-project-root.js +239 -0
  689. package/tools/cli/lib/ui.js +769 -0
  690. package/tools/cli/lib/xml-handler.js +229 -0
  691. package/tools/cli/lib/xml-to-markdown.js +82 -0
  692. package/tools/{yaml-format.js → cli/lib/yaml-format.js} +36 -66
  693. package/tools/cli/lib/yaml-xml-builder.js +606 -0
  694. package/tools/cli/regenerate-manifests.js +28 -0
  695. package/tools/cli/test-yaml-builder.js +43 -0
  696. package/tools/cli/xiaoma-cli.js +40 -0
  697. package/tools/flattener/aggregate.js +12 -30
  698. package/tools/flattener/binary.js +43 -46
  699. package/tools/flattener/discovery.js +15 -23
  700. package/tools/flattener/files.js +6 -6
  701. package/tools/flattener/ignoreRules.js +122 -127
  702. package/tools/flattener/main.js +140 -330
  703. package/tools/flattener/projectRoot.js +71 -81
  704. package/tools/flattener/prompts.js +10 -12
  705. package/tools/flattener/stats.helpers.js +63 -119
  706. package/tools/flattener/stats.js +2 -7
  707. package/tools/flattener/test-matrix.js +169 -228
  708. package/tools/flattener/xml.js +23 -31
  709. package/tools/format-workflow-md.js +263 -0
  710. package/tools/platform-codes.yaml +145 -0
  711. package/tools/schema/agent.js +389 -0
  712. package/tools/validate-agent-schema.js +110 -0
  713. package/tools/validate-bundles.js +87 -0
  714. package/tools/xiaoma-npx-wrapper.js +18 -24
  715. package/.claude/agents/tech-translator.md +0 -124
  716. package/.claude/settings.local.json +0 -37
  717. package/.idea/XiaoMa-Cli.iml +0 -9
  718. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  719. package/.idea/misc.xml +0 -6
  720. package/.idea/modules.xml +0 -8
  721. package/.idea/prettier.xml +0 -6
  722. package/.idea/vcs.xml +0 -7
  723. package/.idea/workspace.xml +0 -117
  724. package/.xiaoma-core/.coordinator-state.json +0 -19
  725. package/CLAUDE.md +0 -283
  726. package/JAVA-BACKEND-COMMANDS-REFERENCE.md +0 -300
  727. package/JAVA-BACKEND-ITERATION-GUIDE.md +0 -2116
  728. package/common/tasks/create-doc.md +0 -103
  729. package/common/tasks/execute-checklist.md +0 -88
  730. package/common/utils/bmad-doc-template.md +0 -327
  731. package/common/utils/workflow-management.md +0 -71
  732. package/dist/agents/analyst.txt +0 -6308
  733. package/dist/agents/architect.txt +0 -5046
  734. package/dist/agents/automation-orchestrator.txt +0 -396
  735. package/dist/agents/dev.txt +0 -1180
  736. package/dist/agents/full-requirement-orchestrator.txt +0 -505
  737. package/dist/agents/pm.txt +0 -3078
  738. package/dist/agents/po.txt +0 -1358
  739. package/dist/agents/qa.txt +0 -2002
  740. package/dist/agents/sm.txt +0 -3044
  741. package/dist/agents/ux-expert.txt +0 -707
  742. package/dist/agents/workflow-executor.txt +0 -1029
  743. package/dist/agents/workflow-helper.txt +0 -93
  744. package/dist/agents/xiaoma-master.txt +0 -9008
  745. package/dist/agents/xiaoma-orchestrator.txt +0 -1523
  746. package/dist/teams/team-all.txt +0 -23101
  747. package/dist/teams/team-fullstack-with-database.txt +0 -25076
  748. package/dist/teams/team-fullstack.txt +0 -15820
  749. package/dist/teams/team-ide-minimal.txt +0 -8285
  750. package/dist/teams/team-no-ui.txt +0 -14368
  751. package/docs/GUIDING-PRINCIPLES.md +0 -91
  752. package/docs/architecture/workflow-coordinator-implementation.md +0 -1188
  753. package/docs/architecture-sharding-modification.md +0 -623
  754. package/docs/automated-requirements-analysis-outputs.md +0 -896
  755. package/docs/core-architecture.md +0 -219
  756. package/docs/enhanced-ide-development-workflow.md +0 -248
  757. package/docs/prd/workflow-coordinator-prd.md +0 -1214
  758. package/docs/user-guide.md +0 -530
  759. package/docs/versioning-and-releases.md +0 -155
  760. package/docs/versions.md +0 -48
  761. package/docs/working-in-the-brownfield.md +0 -597
  762. package/tools/api-server.js +0 -367
  763. package/tools/builders/web-builder.js +0 -830
  764. package/tools/bump-all-versions.js +0 -133
  765. package/tools/cli.js +0 -157
  766. package/tools/installer/README.md +0 -8
  767. package/tools/installer/bin/xiaoma.js +0 -477
  768. package/tools/installer/config/ide-agent-config.yaml +0 -58
  769. package/tools/installer/config/install.config.yaml +0 -164
  770. package/tools/installer/lib/config-loader.js +0 -286
  771. package/tools/installer/lib/file-manager.js +0 -446
  772. package/tools/installer/lib/ide-base-setup.js +0 -238
  773. package/tools/installer/lib/ide-setup.js +0 -2027
  774. package/tools/installer/lib/installer.js +0 -2333
  775. package/tools/installer/lib/memory-profiler.js +0 -235
  776. package/tools/installer/lib/module-manager.js +0 -116
  777. package/tools/installer/lib/resource-locator.js +0 -334
  778. package/tools/installer/package-lock.json +0 -715
  779. package/tools/installer/package.json +0 -44
  780. package/tools/lib/dependency-resolver.js +0 -186
  781. package/tools/lib/yaml-utils.js +0 -34
  782. package/tools/md-assets/web-agent-startup-instructions.md +0 -39
  783. package/tools/preview-release-notes.js +0 -74
  784. package/tools/setup-hooks.sh +0 -37
  785. package/tools/shared/bannerArt.js +0 -105
  786. package/tools/sync-installer-version.js +0 -41
  787. package/tools/sync-version.sh +0 -23
  788. package/tools/upgraders/v3-to-v4-upgrader.js +0 -753
  789. package/tools/version-bump.js +0 -100
  790. package/tools/workflow-coordinator/README.md +0 -38
  791. package/tools/workflow-coordinator/USAGE.md +0 -548
  792. package/tools/workflow-coordinator/package-lock.json +0 -4868
  793. package/tools/workflow-coordinator/package.json +0 -35
  794. package/tools/workflow-coordinator/src/api/server.js +0 -207
  795. package/tools/workflow-coordinator/src/controller/workflow-controller.js +0 -263
  796. package/tools/workflow-coordinator/src/index.js +0 -113
  797. package/tools/workflow-coordinator/src/parser/workflow-parser.js +0 -144
  798. package/tools/workflow-coordinator/src/utils/state-manager.js +0 -59
  799. package/tools/workflow-coordinator/src/utils/validator.js +0 -86
  800. package/tools/workflow-coordinator/test/integration-test.js +0 -266
  801. package/tools/workflow-coordinator/test/quick-test.js +0 -127
  802. package/xiaoma-core/agent-teams/team-all.yaml +0 -15
  803. package/xiaoma-core/agent-teams/team-fullstack-with-database.yaml +0 -27
  804. package/xiaoma-core/agent-teams/team-fullstack.yaml +0 -19
  805. package/xiaoma-core/agent-teams/team-ide-minimal.yaml +0 -11
  806. package/xiaoma-core/agent-teams/team-no-ui.yaml +0 -14
  807. package/xiaoma-core/agents/analyst.md +0 -91
  808. package/xiaoma-core/agents/architect.md +0 -88
  809. package/xiaoma-core/agents/automated-fix-validator.yaml +0 -579
  810. package/xiaoma-core/agents/automated-quality-validator.yaml +0 -549
  811. package/xiaoma-core/agents/automation-orchestrator.md +0 -353
  812. package/xiaoma-core/agents/dev.md +0 -144
  813. package/xiaoma-core/agents/enhanced-workflow-orchestrator.yaml +0 -304
  814. package/xiaoma-core/agents/full-requirement-orchestrator.md +0 -462
  815. package/xiaoma-core/agents/global-requirements-auditor.yaml +0 -520
  816. package/xiaoma-core/agents/intelligent-template-adapter.yaml +0 -389
  817. package/xiaoma-core/agents/issue-dispatcher.yaml +0 -627
  818. package/xiaoma-core/agents/master-execution-engine.yaml +0 -543
  819. package/xiaoma-core/agents/pm.md +0 -85
  820. package/xiaoma-core/agents/po.md +0 -77
  821. package/xiaoma-core/agents/qa.md +0 -88
  822. package/xiaoma-core/agents/requirements-coverage-auditor.yaml +0 -373
  823. package/xiaoma-core/agents/sm.md +0 -125
  824. package/xiaoma-core/agents/ux-expert.md +0 -67
  825. package/xiaoma-core/agents/workflow-executor.md +0 -1031
  826. package/xiaoma-core/agents/workflow-helper.md +0 -481
  827. package/xiaoma-core/agents/xiaoma-master.md +0 -108
  828. package/xiaoma-core/agents/xiaoma-orchestrator.md +0 -145
  829. package/xiaoma-core/checklists/architect-checklist.md +0 -440
  830. package/xiaoma-core/checklists/change-checklist.md +0 -184
  831. package/xiaoma-core/checklists/dev-completion-checklist.md +0 -324
  832. package/xiaoma-core/checklists/pm-checklist.md +0 -372
  833. package/xiaoma-core/checklists/po-master-checklist.md +0 -434
  834. package/xiaoma-core/checklists/po-story-validation-checklist.md +0 -219
  835. package/xiaoma-core/checklists/qa-approval-checklist.md +0 -393
  836. package/xiaoma-core/checklists/story-dod-checklist.md +0 -96
  837. package/xiaoma-core/checklists/story-draft-checklist.md +0 -155
  838. package/xiaoma-core/core-config.yaml +0 -23
  839. package/xiaoma-core/data/bmad-kb.md +0 -809
  840. package/xiaoma-core/data/brainstorming-techniques.md +0 -38
  841. package/xiaoma-core/data/elicitation-methods.md +0 -156
  842. package/xiaoma-core/data/technical-preferences.md +0 -5
  843. package/xiaoma-core/data/test-levels-framework.md +0 -148
  844. package/xiaoma-core/data/test-priorities-matrix.md +0 -174
  845. package/xiaoma-core/scripts/build-validation/pre-dev-validation.sh +0 -71
  846. package/xiaoma-core/scripts/build-validation/progressive-validation.sh +0 -88
  847. package/xiaoma-core/scripts/build-validation/quick-check.sh +0 -69
  848. package/xiaoma-core/tasks/advanced-elicitation.md +0 -119
  849. package/xiaoma-core/tasks/analyze-existing-database.md +0 -155
  850. package/xiaoma-core/tasks/apply-qa-fixes.md +0 -150
  851. package/xiaoma-core/tasks/automated-story-cycle.md +0 -370
  852. package/xiaoma-core/tasks/batch-story-generation.md +0 -354
  853. package/xiaoma-core/tasks/brownfield-create-epic.md +0 -162
  854. package/xiaoma-core/tasks/brownfield-create-story.md +0 -149
  855. package/xiaoma-core/tasks/correct-course.md +0 -72
  856. package/xiaoma-core/tasks/create-brownfield-story.md +0 -314
  857. package/xiaoma-core/tasks/create-database-design.md +0 -161
  858. package/xiaoma-core/tasks/create-deep-research-prompt.md +0 -280
  859. package/xiaoma-core/tasks/create-enhanced-story-with-database.md +0 -250
  860. package/xiaoma-core/tasks/create-incremental-architecture.md +0 -525
  861. package/xiaoma-core/tasks/create-next-story.md +0 -114
  862. package/xiaoma-core/tasks/create-prd-from-rag.md +0 -435
  863. package/xiaoma-core/tasks/create-story-with-rag.md +0 -559
  864. package/xiaoma-core/tasks/develop-story-with-rag.md +0 -536
  865. package/xiaoma-core/tasks/document-project.md +0 -345
  866. package/xiaoma-core/tasks/facilitate-brainstorming-session.md +0 -138
  867. package/xiaoma-core/tasks/generate-ai-frontend-prompt.md +0 -53
  868. package/xiaoma-core/tasks/generate-database-ddl.md +0 -240
  869. package/xiaoma-core/tasks/generate-database-entities.md +0 -501
  870. package/xiaoma-core/tasks/generate-rag-questions.md +0 -312
  871. package/xiaoma-core/tasks/index-docs.md +0 -175
  872. package/xiaoma-core/tasks/kb-mode-interaction.md +0 -77
  873. package/xiaoma-core/tasks/nfr-assess.md +0 -345
  874. package/xiaoma-core/tasks/project-integration-testing.md +0 -477
  875. package/xiaoma-core/tasks/qa-gate.md +0 -163
  876. package/xiaoma-core/tasks/requirement-analysis-with-rag.md +0 -1318
  877. package/xiaoma-core/tasks/requirements-coverage-audit.md +0 -198
  878. package/xiaoma-core/tasks/review-story.md +0 -316
  879. package/xiaoma-core/tasks/risk-profile.md +0 -355
  880. package/xiaoma-core/tasks/serial-development-orchestration.md +0 -426
  881. package/xiaoma-core/tasks/shard-doc.md +0 -187
  882. package/xiaoma-core/tasks/test-design.md +0 -176
  883. package/xiaoma-core/tasks/trace-requirements.md +0 -266
  884. package/xiaoma-core/tasks/validate-next-story.md +0 -136
  885. package/xiaoma-core/templates/api-design-tmpl.yaml +0 -704
  886. package/xiaoma-core/templates/architecture-tmpl.yaml +0 -650
  887. package/xiaoma-core/templates/brainstorming-output-tmpl.yaml +0 -156
  888. package/xiaoma-core/templates/brownfield-architecture-tmpl.yaml +0 -476
  889. package/xiaoma-core/templates/brownfield-prd-tmpl.yaml +0 -280
  890. package/xiaoma-core/templates/competitor-analysis-tmpl.yaml +0 -336
  891. package/xiaoma-core/templates/database-design-tmpl.yaml +0 -266
  892. package/xiaoma-core/templates/enhanced-story-with-database-tmpl.yaml +0 -428
  893. package/xiaoma-core/templates/front-end-architecture-tmpl.yaml +0 -272
  894. package/xiaoma-core/templates/front-end-spec-tmpl.yaml +0 -354
  895. package/xiaoma-core/templates/fullstack-architecture-tmpl.yaml +0 -925
  896. package/xiaoma-core/templates/global-qa-monitoring-tmpl.yaml +0 -443
  897. package/xiaoma-core/templates/incremental-architecture-tmpl.yaml +0 -601
  898. package/xiaoma-core/templates/market-research-tmpl.yaml +0 -252
  899. package/xiaoma-core/templates/maven-lombok-template.xml +0 -111
  900. package/xiaoma-core/templates/prd-from-rag-tmpl.yaml +0 -410
  901. package/xiaoma-core/templates/prd-tmpl.yaml +0 -202
  902. package/xiaoma-core/templates/project-brief-tmpl.yaml +0 -221
  903. package/xiaoma-core/templates/qa-gate-tmpl.yaml +0 -102
  904. package/xiaoma-core/templates/rag-knowledge-tmpl.yaml +0 -569
  905. package/xiaoma-core/templates/rag-questions-tmpl.yaml +0 -949
  906. package/xiaoma-core/templates/requirements-coverage-audit.yaml +0 -330
  907. package/xiaoma-core/templates/start-enhanced-workflow.yaml +0 -347
  908. package/xiaoma-core/templates/story-tmpl.yaml +0 -137
  909. package/xiaoma-core/templates/story-with-rag-tmpl.yaml +0 -360
  910. package/xiaoma-core/workflows/automated-requirements-analysis.yaml +0 -2149
  911. package/xiaoma-core/workflows/automated-requirements-development.yaml +0 -739
  912. package/xiaoma-core/workflows/automated-story-development.yaml +0 -1264
  913. package/xiaoma-core/workflows/brownfield-fullstack.yaml +0 -298
  914. package/xiaoma-core/workflows/brownfield-service.yaml +0 -188
  915. package/xiaoma-core/workflows/brownfield-ui.yaml +0 -198
  916. package/xiaoma-core/workflows/enhanced-fullstack-with-database.yaml +0 -427
  917. package/xiaoma-core/workflows/enhanced-fullstack-with-qa-loop.yaml +0 -766
  918. package/xiaoma-core/workflows/full-requirement-automation.yaml +0 -1305
  919. package/xiaoma-core/workflows/greenfield-fullstack.yaml +0 -241
  920. package/xiaoma-core/workflows/greenfield-service.yaml +0 -207
  921. package/xiaoma-core/workflows/greenfield-ui.yaml +0 -236
@@ -0,0 +1,2956 @@
1
+ /**
2
+ * File: tools/cli/installers/lib/core/installer.js
3
+ *
4
+ * XIAOMA Method - Business Model Agile Development Method
5
+ * Repository: https://github.com/paulpreibisch/XIAOMA-CLI
6
+ *
7
+ * Copyright (c) 2025 Paul Preibisch
8
+ * Licensed under the Apache License, Version 2.0
9
+ *
10
+ * ---
11
+ *
12
+ * @fileoverview Core XIAOMA installation orchestrator with AgentVibes injection point support
13
+ * @context Manages complete XIAOMA installation flow including core agents, modules, IDE configs, and optional TTS integration
14
+ * @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete XIAOMA installation
15
+ * @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js
16
+ * @entrypoints Called by install.js command via installer.install(config)
17
+ * @patterns Injection point processing (AgentVibes), placeholder replacement ({xiaoma_folder}), module dependency resolution
18
+ * @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement()
19
+ */
20
+
21
+ const path = require('node:path');
22
+ const fs = require('fs-extra');
23
+ const chalk = require('chalk');
24
+ const ora = require('ora');
25
+ const { Detector } = require('./detector');
26
+ const { Manifest } = require('./manifest');
27
+ const { ModuleManager } = require('../modules/manager');
28
+ const { IdeManager } = require('../ide/manager');
29
+ const { FileOps } = require('../../../lib/file-ops');
30
+ const { Config } = require('../../../lib/config');
31
+ const { XmlHandler } = require('../../../lib/xml-handler');
32
+ const { DependencyResolver } = require('./dependency-resolver');
33
+ const { ConfigCollector } = require('./config-collector');
34
+ // processInstallation no longer needed - LLMs understand {project-root}
35
+ const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
36
+ const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
37
+ const { CLIUtils } = require('../../../lib/cli-utils');
38
+ const { ManifestGenerator } = require('./manifest-generator');
39
+ const { IdeConfigManager } = require('./ide-config-manager');
40
+
41
+ class Installer {
42
+ constructor() {
43
+ this.detector = new Detector();
44
+ this.manifest = new Manifest();
45
+ this.moduleManager = new ModuleManager();
46
+ this.ideManager = new IdeManager();
47
+ this.fileOps = new FileOps();
48
+ this.config = new Config();
49
+ this.xmlHandler = new XmlHandler();
50
+ this.dependencyResolver = new DependencyResolver();
51
+ this.configCollector = new ConfigCollector();
52
+ this.ideConfigManager = new IdeConfigManager();
53
+ this.installedFiles = []; // Track all installed files
54
+ }
55
+
56
+ /**
57
+ * Find the xiaoma installation directory in a project
58
+ * V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
59
+ * @param {string} projectDir - Project directory
60
+ * @returns {Promise<string>} Path to xiaoma directory
61
+ */
62
+ async findBmadDir(projectDir) {
63
+ // Check if project directory exists
64
+ if (!(await fs.pathExists(projectDir))) {
65
+ // Project doesn't exist yet, return default
66
+ return path.join(projectDir, 'xiaoma');
67
+ }
68
+
69
+ // V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
70
+ // This is the definitive marker of a V6+ installation
71
+ try {
72
+ const entries = await fs.readdir(projectDir, { withFileTypes: true });
73
+ for (const entry of entries) {
74
+ if (entry.isDirectory()) {
75
+ const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
76
+ if (await fs.pathExists(manifestPath)) {
77
+ // Found a V6+ installation
78
+ return path.join(projectDir, entry.name);
79
+ }
80
+ }
81
+ }
82
+ } catch {
83
+ // Ignore errors, fall through to default
84
+ }
85
+
86
+ // No V6+ installation found, return default
87
+ // This will be used for new installations
88
+ return path.join(projectDir, 'xiaoma');
89
+ }
90
+
91
+ /**
92
+ * @function copyFileWithPlaceholderReplacement
93
+ * @intent Copy files from XIAOMA source to installation directory with dynamic content transformation
94
+ * @why Enables installation-time customization: {xiaoma_folder} replacement + optional AgentVibes TTS injection
95
+ * @param {string} sourcePath - Absolute path to source file in XIAOMA repository
96
+ * @param {string} targetPath - Absolute path to destination file in user's project
97
+ * @param {string} xiaomaFolderName - User's chosen xiaoma folder name (default: 'xiaoma')
98
+ * @returns {Promise<void>} Resolves when file copy and transformation complete
99
+ * @sideeffects Writes transformed file to targetPath, creates parent directories if needed
100
+ * @edgecases Binary files bypass transformation, falls back to raw copy if UTF-8 read fails
101
+ * @calledby installCore(), installModule(), IDE installers during file vendoring
102
+ * @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy()
103
+ *
104
+ * AI NOTE: This is the core transformation pipeline for ALL XIAOMA installation file copies.
105
+ * It performs two transformations in sequence:
106
+ * 1. {xiaoma_folder} → user's custom folder name (e.g., ".xiaoma" or "xiaoma")
107
+ * 2. <!-- TTS_INJECTION:* --> → TTS bash calls (if enabled) OR stripped (if disabled)
108
+ *
109
+ * The injection point processing enables loose coupling between XIAOMA and TTS providers:
110
+ * - XIAOMA source contains injection markers (not actual TTS code)
111
+ * - At install-time, markers are replaced OR removed based on user preference
112
+ * - Result: Clean installs for users without TTS, working TTS for users with it
113
+ *
114
+ * PATTERN: Adding New Injection Points
115
+ * =====================================
116
+ * 1. Add HTML comment marker in XIAOMA source file:
117
+ * <!-- TTS_INJECTION:feature-name -->
118
+ *
119
+ * 2. Add replacement logic in processTTSInjectionPoints():
120
+ * if (enableAgentVibes) {
121
+ * content = content.replace(/<!-- TTS_INJECTION:feature-name -->/g, 'actual code');
122
+ * } else {
123
+ * content = content.replace(/<!-- TTS_INJECTION:feature-name -->\n?/g, '');
124
+ * }
125
+ *
126
+ * 3. Document marker in instructions.md (if applicable)
127
+ */
128
+ async copyFileWithPlaceholderReplacement(sourcePath, targetPath, xiaomaFolderName) {
129
+ // List of text file extensions that should have placeholder replacement
130
+ const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv'];
131
+ const ext = path.extname(sourcePath).toLowerCase();
132
+
133
+ // Check if this is a text file that might contain placeholders
134
+ if (textExtensions.includes(ext)) {
135
+ try {
136
+ // Read the file content
137
+ let content = await fs.readFile(sourcePath, 'utf8');
138
+
139
+ // Replace {xiaoma_folder} placeholder with actual folder name
140
+ if (content.includes('{xiaoma_folder}')) {
141
+ content = content.replaceAll('{xiaoma_folder}', xiaomaFolderName);
142
+ }
143
+
144
+ // Process AgentVibes injection points
145
+ content = this.processTTSInjectionPoints(content);
146
+
147
+ // Write to target with replaced content
148
+ await fs.ensureDir(path.dirname(targetPath));
149
+ await fs.writeFile(targetPath, content, 'utf8');
150
+ } catch {
151
+ // If reading as text fails (might be binary despite extension), fall back to regular copy
152
+ await fs.copy(sourcePath, targetPath, { overwrite: true });
153
+ }
154
+ } else {
155
+ // Binary file or other file type - just copy directly
156
+ await fs.copy(sourcePath, targetPath, { overwrite: true });
157
+ }
158
+ }
159
+
160
+ /**
161
+ * @function processTTSInjectionPoints
162
+ * @intent Transform TTS injection markers based on user's installation choice
163
+ * @why Enables optional TTS integration without tight coupling between XIAOMA and TTS providers
164
+ * @param {string} content - Raw file content containing potential injection markers
165
+ * @returns {string} Transformed content with markers replaced (if enabled) or stripped (if disabled)
166
+ * @sideeffects None - pure transformation function
167
+ * @edgecases Returns content unchanged if no markers present, safe to call on all files
168
+ * @calledby copyFileWithPlaceholderReplacement() during every file copy operation
169
+ * @calls String.replace() with regex patterns for each injection point type
170
+ *
171
+ * AI NOTE: This implements the injection point pattern for TTS integration.
172
+ * Key architectural decisions:
173
+ *
174
+ * 1. **Why Injection Points vs Direct Integration?**
175
+ * - XIAOMA and TTS providers are separate projects with different maintainers
176
+ * - Users may install XIAOMA without TTS support (and vice versa)
177
+ * - Hard-coding TTS calls would break XIAOMA for non-TTS users
178
+ * - Injection points allow conditional feature inclusion at install-time
179
+ *
180
+ * 2. **How It Works:**
181
+ * - XIAOMA source contains markers: <!-- TTS_INJECTION:feature-name -->
182
+ * - During installation, user is prompted: "Enable AgentVibes TTS?"
183
+ * - If YES: markers → replaced with actual bash TTS calls
184
+ * - If NO: markers → stripped cleanly from installed files
185
+ *
186
+ * 3. **State Management:**
187
+ * - this.enableAgentVibes set in install() method from config.enableAgentVibes
188
+ * - config.enableAgentVibes comes from ui.promptAgentVibes() user choice
189
+ * - Flag persists for entire installation, all files get same treatment
190
+ *
191
+ * CURRENT INJECTION POINTS:
192
+ * ==========================
193
+ * - party-mode: Injects TTS calls after each agent speaks in party mode
194
+ * Location: src/core/workflows/party-mode/instructions.md
195
+ * Marker: <!-- TTS_INJECTION:party-mode -->
196
+ * Replacement: Bash call to .claude/hooks/xiaoma-speak.sh with agent name and dialogue
197
+ *
198
+ * - agent-tts: Injects TTS rule for individual agent conversations
199
+ * Location: src/modules/xmc/agents/*.md (all agent files)
200
+ * Marker: <!-- TTS_INJECTION:agent-tts -->
201
+ * Replacement: Rule instructing agent to call xiaoma-speak.sh with agent ID and response
202
+ *
203
+ * ADDING NEW INJECTION POINTS:
204
+ * =============================
205
+ * 1. Add new case in this function:
206
+ * content = content.replace(
207
+ * /<!-- TTS_INJECTION:new-feature -->/g,
208
+ * `code to inject when enabled`
209
+ * );
210
+ *
211
+ * 2. Add marker to XIAOMA source file at injection location
212
+ *
213
+ * 3. Test both enabled and disabled flows
214
+ *
215
+ * RELATED:
216
+ * ========
217
+ * - GitHub Issue: paulpreibisch/AgentVibes#36
218
+ * - User Prompt: tools/cli/lib/ui.js::promptAgentVibes()
219
+ * - Marker Locations:
220
+ * - src/core/workflows/party-mode/instructions.md:101
221
+ * - src/modules/xmc/agents/*.md (rules sections)
222
+ * - TTS Hook: .claude/hooks/xiaoma-speak.sh (in AgentVibes repo)
223
+ */
224
+ processTTSInjectionPoints(content) {
225
+ // Check if AgentVibes is enabled (set during installation configuration)
226
+ const enableAgentVibes = this.enableAgentVibes || false;
227
+
228
+ if (enableAgentVibes) {
229
+ // Replace party-mode injection marker with actual TTS call
230
+ // Use single quotes to prevent shell expansion of special chars like !
231
+ content = content.replaceAll(
232
+ '<!-- TTS_INJECTION:party-mode -->',
233
+ `<critical>IMPORTANT: Always use PROJECT hooks (.claude/hooks/), NEVER global hooks (~/.claude/hooks/)</critical>
234
+
235
+ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
236
+ - Use Bash tool: \`.claude/hooks/xiaoma-speak.sh '[Agent Name]' '[dialogue]'\`
237
+ - This speaks the dialogue with the agent's unique voice
238
+ - Run in background (&) to not block next agent`,
239
+ );
240
+
241
+ // Replace agent-tts injection marker with TTS rule for individual agents
242
+ content = content.replaceAll(
243
+ '<!-- TTS_INJECTION:agent-tts -->',
244
+ `- When responding to user messages, speak your responses using TTS:
245
+ Call: \`.claude/hooks/xiaoma-speak.sh '{agent-id}' '{response-text}'\` after each response
246
+ Replace {agent-id} with YOUR agent ID from <agent id="..."> tag at top of this file
247
+ Replace {response-text} with the text you just output to the user
248
+ IMPORTANT: Use single quotes as shown - do NOT escape special characters like ! or $ inside single quotes
249
+ Run in background (&) to avoid blocking`,
250
+ );
251
+ } else {
252
+ // Strip injection markers cleanly when AgentVibes is disabled
253
+ content = content.replaceAll(/<!-- TTS_INJECTION:party-mode -->\n?/g, '');
254
+ content = content.replaceAll(/<!-- TTS_INJECTION:agent-tts -->\n?/g, '');
255
+ }
256
+
257
+ return content;
258
+ }
259
+
260
+ /**
261
+ * Collect Tool/IDE configurations after module configuration
262
+ * @param {string} projectDir - Project directory
263
+ * @param {Array} selectedModules - Selected modules from configuration
264
+ * @param {boolean} isFullReinstall - Whether this is a full reinstall
265
+ * @param {Array} previousIdes - Previously configured IDEs (for reinstalls)
266
+ * @param {Array} preSelectedIdes - Pre-selected IDEs from early prompt (optional)
267
+ * @returns {Object} Tool/IDE selection and configurations
268
+ */
269
+ async collectToolConfigurations(projectDir, selectedModules, isFullReinstall = false, previousIdes = [], preSelectedIdes = null) {
270
+ // Use pre-selected IDEs if provided, otherwise prompt
271
+ let toolConfig;
272
+ if (preSelectedIdes === null) {
273
+ // Fallback: prompt for tool selection (backwards compatibility)
274
+ const { UI } = require('../../../lib/ui');
275
+ const ui = new UI();
276
+ toolConfig = await ui.promptToolSelection(projectDir, selectedModules);
277
+ } else {
278
+ // IDEs were already selected during initial prompts
279
+ toolConfig = {
280
+ ides: preSelectedIdes,
281
+ skipIde: !preSelectedIdes || preSelectedIdes.length === 0,
282
+ };
283
+ }
284
+
285
+ // Check for already configured IDEs
286
+ const { Detector } = require('./detector');
287
+ const detector = new Detector();
288
+ const xiaomaDir = path.join(projectDir, this.xiaomaFolderName || 'xiaoma');
289
+
290
+ // During full reinstall, use the saved previous IDEs since xiaoma dir was deleted
291
+ // Otherwise detect from existing installation
292
+ let previouslyConfiguredIdes;
293
+ if (isFullReinstall) {
294
+ // During reinstall, treat all IDEs as new (need configuration)
295
+ previouslyConfiguredIdes = [];
296
+ } else {
297
+ const existingInstall = await detector.detect(xiaomaDir);
298
+ previouslyConfiguredIdes = existingInstall.ides || [];
299
+ }
300
+
301
+ // Load saved IDE configurations for already-configured IDEs
302
+ const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(xiaomaDir);
303
+
304
+ // Collect IDE-specific configurations if any were selected
305
+ const ideConfigurations = {};
306
+
307
+ // First, add saved configs for already-configured IDEs
308
+ for (const ide of toolConfig.ides || []) {
309
+ if (previouslyConfiguredIdes.includes(ide) && savedIdeConfigs[ide]) {
310
+ ideConfigurations[ide] = savedIdeConfigs[ide];
311
+ }
312
+ }
313
+
314
+ if (!toolConfig.skipIde && toolConfig.ides && toolConfig.ides.length > 0) {
315
+ // Determine which IDEs are newly selected (not previously configured)
316
+ const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
317
+
318
+ if (newlySelectedIdes.length > 0) {
319
+ console.log('\n'); // Add spacing before IDE questions
320
+
321
+ for (const ide of newlySelectedIdes) {
322
+ // List of IDEs that have interactive prompts
323
+ const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes(
324
+ ide,
325
+ );
326
+
327
+ if (needsPrompts) {
328
+ // Get IDE handler and collect configuration
329
+ try {
330
+ // Dynamically load the IDE setup module
331
+ const ideModule = require(`../ide/${ide}`);
332
+
333
+ // Get the setup class (handle different export formats)
334
+ let SetupClass;
335
+ const className =
336
+ ide
337
+ .split('-')
338
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
339
+ .join('') + 'Setup';
340
+
341
+ if (ideModule[className]) {
342
+ SetupClass = ideModule[className];
343
+ } else if (ideModule.default) {
344
+ SetupClass = ideModule.default;
345
+ } else {
346
+ // Skip if no setup class found
347
+ continue;
348
+ }
349
+
350
+ const ideSetup = new SetupClass();
351
+
352
+ // Check if this IDE has a collectConfiguration method
353
+ if (typeof ideSetup.collectConfiguration === 'function') {
354
+ console.log(chalk.cyan(`\nConfiguring ${ide}...`));
355
+ ideConfigurations[ide] = await ideSetup.collectConfiguration({
356
+ selectedModules: selectedModules || [],
357
+ projectDir,
358
+ xiaomaDir,
359
+ });
360
+ }
361
+ } catch {
362
+ // IDE doesn't have a setup file or collectConfiguration method
363
+ console.warn(chalk.yellow(`Warning: Could not load configuration for ${ide}`));
364
+ }
365
+ }
366
+ }
367
+ }
368
+
369
+ // Log which IDEs are already configured and being kept
370
+ const keptIdes = toolConfig.ides.filter((ide) => previouslyConfiguredIdes.includes(ide));
371
+ if (keptIdes.length > 0) {
372
+ console.log(chalk.dim(`\nKeeping existing configuration for: ${keptIdes.join(', ')}`));
373
+ }
374
+ }
375
+
376
+ return {
377
+ ides: toolConfig.ides,
378
+ skipIde: toolConfig.skipIde,
379
+ configurations: ideConfigurations,
380
+ };
381
+ }
382
+
383
+ /**
384
+ * Main installation method
385
+ * @param {Object} config - Installation configuration
386
+ * @param {string} config.directory - Target directory
387
+ * @param {boolean} config.installCore - Whether to install core
388
+ * @param {string[]} config.modules - Modules to install
389
+ * @param {string[]} config.ides - IDEs to configure
390
+ * @param {boolean} config.skipIde - Skip IDE configuration
391
+ */
392
+ async install(config) {
393
+ // Display XIAOMA logo
394
+ CLIUtils.displayLogo();
395
+
396
+ // Display welcome message
397
+ CLIUtils.displaySection('XIAOMAā„¢ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
398
+
399
+ // Note: Legacy V4 detection now happens earlier in UI.promptInstall()
400
+ // before any config collection, so we don't need to check again here
401
+
402
+ const projectDir = path.resolve(config.directory);
403
+
404
+ // If core config was pre-collected (from interactive mode), use it
405
+ if (config.coreConfig) {
406
+ this.configCollector.collectedConfig.core = config.coreConfig;
407
+ // Also store in allAnswers for cross-referencing
408
+ this.configCollector.allAnswers = {};
409
+ for (const [key, value] of Object.entries(config.coreConfig)) {
410
+ this.configCollector.allAnswers[`core_${key}`] = value;
411
+ }
412
+ }
413
+
414
+ // Collect configurations for modules (skip if quick update already collected them)
415
+ let moduleConfigs;
416
+ if (config._quickUpdate) {
417
+ // Quick update already collected all configs, use them directly
418
+ moduleConfigs = this.configCollector.collectedConfig;
419
+ } else {
420
+ // Regular install - collect configurations (core was already collected in UI.promptInstall if interactive)
421
+ moduleConfigs = await this.configCollector.collectAllConfigurations(config.modules || [], path.resolve(config.directory));
422
+ }
423
+
424
+ // Get xiaoma_folder from config (default to 'xiaoma' for backwards compatibility)
425
+ const xiaomaFolderName = moduleConfigs.core && moduleConfigs.core.xiaoma_folder ? moduleConfigs.core.xiaoma_folder : 'xiaoma';
426
+ this.xiaomaFolderName = xiaomaFolderName; // Store for use in other methods
427
+
428
+ // Store AgentVibes configuration for injection point processing
429
+ this.enableAgentVibes = config.enableAgentVibes || false;
430
+
431
+ // Set xiaoma folder name on module manager and IDE manager for placeholder replacement
432
+ this.moduleManager.setBmadFolderName(xiaomaFolderName);
433
+ this.ideManager.setBmadFolderName(xiaomaFolderName);
434
+
435
+ // Tool selection will be collected after we determine if it's a reinstall/update/new install
436
+
437
+ const spinner = ora('Preparing installation...').start();
438
+
439
+ try {
440
+ // Resolve target directory (path.resolve handles platform differences)
441
+ const projectDir = path.resolve(config.directory);
442
+
443
+ // Check if xiaoma_folder has changed from existing installation (only if project dir exists)
444
+ let existingBmadDir = null;
445
+ let existingBmadFolderName = null;
446
+
447
+ if (await fs.pathExists(projectDir)) {
448
+ existingBmadDir = await this.findBmadDir(projectDir);
449
+ existingBmadFolderName = path.basename(existingBmadDir);
450
+ }
451
+
452
+ const targetBmadDir = path.join(projectDir, xiaomaFolderName);
453
+
454
+ // If xiaoma_folder changed during update/upgrade, back up old folder and do fresh install
455
+ if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== xiaomaFolderName) {
456
+ spinner.stop();
457
+ console.log(chalk.yellow(`\nāš ļø xiaoma_folder has changed: ${existingBmadFolderName} → ${xiaomaFolderName}`));
458
+ console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
459
+
460
+ const inquirer = require('inquirer');
461
+ const { confirmFreshInstall } = await inquirer.prompt([
462
+ {
463
+ type: 'confirm',
464
+ name: 'confirmFreshInstall',
465
+ message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
466
+ default: true,
467
+ },
468
+ ]);
469
+
470
+ if (!confirmFreshInstall) {
471
+ console.log(chalk.yellow('Installation cancelled.'));
472
+ return { success: false, cancelled: true };
473
+ }
474
+
475
+ spinner.start('Backing up existing installation...');
476
+
477
+ // Find a unique backup name
478
+ let backupDir = `${existingBmadDir}-bak`;
479
+ let counter = 1;
480
+ while (await fs.pathExists(backupDir)) {
481
+ backupDir = `${existingBmadDir}-bak-${counter}`;
482
+ counter++;
483
+ }
484
+
485
+ // Rename the old folder to backup
486
+ await fs.move(existingBmadDir, backupDir);
487
+
488
+ spinner.succeed(`Backed up ${existingBmadFolderName} → ${path.basename(backupDir)}`);
489
+ console.log(chalk.cyan('\nšŸ“‹ Important:'));
490
+ console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
491
+ console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
492
+ console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
493
+ console.log(chalk.dim(` - To the new location:`));
494
+ console.log(chalk.dim(` ${xiaomaFolderName}/_cfg/`));
495
+ console.log('');
496
+
497
+ spinner.start('Starting fresh installation...');
498
+ }
499
+
500
+ // Create a project directory if it doesn't exist (user already confirmed)
501
+ if (!(await fs.pathExists(projectDir))) {
502
+ spinner.text = 'Creating installation directory...';
503
+ try {
504
+ // fs.ensureDir handles platform-specific directory creation
505
+ // It will recursively create all necessary parent directories
506
+ await fs.ensureDir(projectDir);
507
+ } catch (error) {
508
+ spinner.fail('Failed to create installation directory');
509
+ console.error(chalk.red(`Error: ${error.message}`));
510
+ // More detailed error for common issues
511
+ if (error.code === 'EACCES') {
512
+ console.error(chalk.red('Permission denied. Check parent directory permissions.'));
513
+ } else if (error.code === 'ENOSPC') {
514
+ console.error(chalk.red('No space left on device.'));
515
+ }
516
+ throw new Error(`Cannot create directory: ${projectDir}`);
517
+ }
518
+ }
519
+
520
+ const xiaomaDir = path.join(projectDir, xiaomaFolderName);
521
+
522
+ // Check existing installation
523
+ spinner.text = 'Checking for existing installation...';
524
+ const existingInstall = await this.detector.detect(xiaomaDir);
525
+
526
+ if (existingInstall.installed && !config.force && !config._quickUpdate) {
527
+ spinner.stop();
528
+
529
+ // Check if user already decided what to do (from early menu in ui.js)
530
+ let action = null;
531
+ if (config._requestedReinstall) {
532
+ action = 'reinstall';
533
+ } else if (config.actionType === 'update') {
534
+ action = 'update';
535
+ } else {
536
+ // Fallback: Ask the user (backwards compatibility for other code paths)
537
+ console.log(chalk.yellow('\nāš ļø Existing XIAOMA installation detected'));
538
+ console.log(chalk.dim(` Location: ${xiaomaDir}`));
539
+ console.log(chalk.dim(` Version: ${existingInstall.version}`));
540
+
541
+ const promptResult = await this.promptUpdateAction();
542
+ action = promptResult.action;
543
+ }
544
+
545
+ if (action === 'cancel') {
546
+ console.log('Installation cancelled.');
547
+ return { success: false, cancelled: true };
548
+ }
549
+
550
+ if (action === 'reinstall') {
551
+ // Warn about destructive operation
552
+ console.log(chalk.red.bold('\nāš ļø WARNING: This is a destructive operation!'));
553
+ console.log(chalk.red('All custom files and modifications in the xiaoma directory will be lost.'));
554
+
555
+ const inquirer = require('inquirer');
556
+ const { confirmReinstall } = await inquirer.prompt([
557
+ {
558
+ type: 'confirm',
559
+ name: 'confirmReinstall',
560
+ message: chalk.yellow('Are you sure you want to delete and reinstall?'),
561
+ default: false,
562
+ },
563
+ ]);
564
+
565
+ if (!confirmReinstall) {
566
+ console.log('Installation cancelled.');
567
+ return { success: false, cancelled: true };
568
+ }
569
+
570
+ // Remember previously configured IDEs before deleting
571
+ config._previouslyConfiguredIdes = existingInstall.ides || [];
572
+
573
+ // Remove existing installation
574
+ await fs.remove(xiaomaDir);
575
+ console.log(chalk.green('āœ“ Removed existing installation\n'));
576
+
577
+ // Mark this as a full reinstall so we re-collect IDE configurations
578
+ config._isFullReinstall = true;
579
+ } else if (action === 'update') {
580
+ // Store that we're updating for later processing
581
+ config._isUpdate = true;
582
+ config._existingInstall = existingInstall;
583
+
584
+ // Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
585
+ const existingFilesManifest = await this.readFilesManifest(xiaomaDir);
586
+ console.log(chalk.dim(`DEBUG: Read ${existingFilesManifest.length} files from manifest`));
587
+ console.log(chalk.dim(`DEBUG: Manifest has hashes: ${existingFilesManifest.some((f) => f.hash)}`));
588
+
589
+ const { customFiles, modifiedFiles } = await this.detectCustomFiles(xiaomaDir, existingFilesManifest);
590
+
591
+ console.log(chalk.dim(`DEBUG: Found ${customFiles.length} custom files, ${modifiedFiles.length} modified files`));
592
+ if (modifiedFiles.length > 0) {
593
+ console.log(chalk.yellow('DEBUG: Modified files:'));
594
+ for (const f of modifiedFiles) console.log(chalk.dim(` - ${f.path}`));
595
+ }
596
+
597
+ config._customFiles = customFiles;
598
+ config._modifiedFiles = modifiedFiles;
599
+
600
+ // If there are custom files, back them up temporarily
601
+ if (customFiles.length > 0) {
602
+ const tempBackupDir = path.join(projectDir, '.xiaoma-custom-backup-temp');
603
+ await fs.ensureDir(tempBackupDir);
604
+
605
+ spinner.start(`Backing up ${customFiles.length} custom files...`);
606
+ for (const customFile of customFiles) {
607
+ const relativePath = path.relative(xiaomaDir, customFile);
608
+ const backupPath = path.join(tempBackupDir, relativePath);
609
+ await fs.ensureDir(path.dirname(backupPath));
610
+ await fs.copy(customFile, backupPath);
611
+ }
612
+ spinner.succeed(`Backed up ${customFiles.length} custom files`);
613
+
614
+ config._tempBackupDir = tempBackupDir;
615
+ }
616
+
617
+ // For modified files, back them up to temp directory (will be restored as .bak files after install)
618
+ if (modifiedFiles.length > 0) {
619
+ const tempModifiedBackupDir = path.join(projectDir, '.xiaoma-modified-backup-temp');
620
+ await fs.ensureDir(tempModifiedBackupDir);
621
+
622
+ console.log(chalk.yellow(`\nDEBUG: Backing up ${modifiedFiles.length} modified files to temp location`));
623
+ spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
624
+ for (const modifiedFile of modifiedFiles) {
625
+ const relativePath = path.relative(xiaomaDir, modifiedFile.path);
626
+ const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
627
+ console.log(chalk.dim(`DEBUG: Backing up ${relativePath} to temp`));
628
+ await fs.ensureDir(path.dirname(tempBackupPath));
629
+ await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
630
+ }
631
+ spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
632
+
633
+ config._tempModifiedBackupDir = tempModifiedBackupDir;
634
+ } else {
635
+ console.log(chalk.dim('DEBUG: No modified files detected'));
636
+ }
637
+ }
638
+ } else if (existingInstall.installed && config._quickUpdate) {
639
+ // Quick update mode - automatically treat as update without prompting
640
+ spinner.text = 'Preparing quick update...';
641
+ config._isUpdate = true;
642
+ config._existingInstall = existingInstall;
643
+
644
+ // Detect custom and modified files BEFORE updating
645
+ const existingFilesManifest = await this.readFilesManifest(xiaomaDir);
646
+ const { customFiles, modifiedFiles } = await this.detectCustomFiles(xiaomaDir, existingFilesManifest);
647
+
648
+ config._customFiles = customFiles;
649
+ config._modifiedFiles = modifiedFiles;
650
+
651
+ // Back up custom files
652
+ if (customFiles.length > 0) {
653
+ const tempBackupDir = path.join(projectDir, '.xiaoma-custom-backup-temp');
654
+ await fs.ensureDir(tempBackupDir);
655
+
656
+ spinner.start(`Backing up ${customFiles.length} custom files...`);
657
+ for (const customFile of customFiles) {
658
+ const relativePath = path.relative(xiaomaDir, customFile);
659
+ const backupPath = path.join(tempBackupDir, relativePath);
660
+ await fs.ensureDir(path.dirname(backupPath));
661
+ await fs.copy(customFile, backupPath);
662
+ }
663
+ spinner.succeed(`Backed up ${customFiles.length} custom files`);
664
+ config._tempBackupDir = tempBackupDir;
665
+ }
666
+
667
+ // Back up modified files
668
+ if (modifiedFiles.length > 0) {
669
+ const tempModifiedBackupDir = path.join(projectDir, '.xiaoma-modified-backup-temp');
670
+ await fs.ensureDir(tempModifiedBackupDir);
671
+
672
+ spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
673
+ for (const modifiedFile of modifiedFiles) {
674
+ const relativePath = path.relative(xiaomaDir, modifiedFile.path);
675
+ const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
676
+ await fs.ensureDir(path.dirname(tempBackupPath));
677
+ await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
678
+ }
679
+ spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
680
+ config._tempModifiedBackupDir = tempModifiedBackupDir;
681
+ }
682
+ }
683
+
684
+ // Now collect tool configurations after we know if it's a reinstall
685
+ // Skip for quick update since we already have the IDE list
686
+ spinner.stop();
687
+ let toolSelection;
688
+ if (config._quickUpdate) {
689
+ // Quick update already has IDEs configured, use saved configurations
690
+ const preConfiguredIdes = {};
691
+ const savedIdeConfigs = config._savedIdeConfigs || {};
692
+
693
+ for (const ide of config.ides || []) {
694
+ // Use saved config if available, otherwise mark as already configured (legacy)
695
+ if (savedIdeConfigs[ide]) {
696
+ preConfiguredIdes[ide] = savedIdeConfigs[ide];
697
+ } else {
698
+ preConfiguredIdes[ide] = { _alreadyConfigured: true };
699
+ }
700
+ }
701
+ toolSelection = {
702
+ ides: config.ides || [],
703
+ skipIde: !config.ides || config.ides.length === 0,
704
+ configurations: preConfiguredIdes,
705
+ };
706
+ } else {
707
+ // Pass pre-selected IDEs from early prompt (if available)
708
+ // This allows IDE selection to happen before file copying, improving UX
709
+ const preSelectedIdes = config.ides && config.ides.length > 0 ? config.ides : null;
710
+ toolSelection = await this.collectToolConfigurations(
711
+ path.resolve(config.directory),
712
+ config.modules,
713
+ config._isFullReinstall || false,
714
+ config._previouslyConfiguredIdes || [],
715
+ preSelectedIdes,
716
+ );
717
+ }
718
+
719
+ // Merge tool selection into config (for both quick update and regular flow)
720
+ config.ides = toolSelection.ides;
721
+ config.skipIde = toolSelection.skipIde;
722
+ const ideConfigurations = toolSelection.configurations;
723
+
724
+ // Check if spinner is already running (e.g., from folder name change scenario)
725
+ if (spinner.isSpinning) {
726
+ spinner.text = 'Continuing installation...';
727
+ } else {
728
+ spinner.start('Continuing installation...');
729
+ }
730
+
731
+ // Create xiaoma directory structure
732
+ spinner.text = 'Creating directory structure...';
733
+ await this.createDirectoryStructure(xiaomaDir);
734
+
735
+ // Resolve dependencies for selected modules
736
+ spinner.text = 'Resolving dependencies...';
737
+ const projectRoot = getProjectRoot();
738
+ const modulesToInstall = config.installCore ? ['core', ...config.modules] : config.modules;
739
+
740
+ // For dependency resolution, we need to pass the project root
741
+ const resolution = await this.dependencyResolver.resolve(projectRoot, config.modules || [], { verbose: config.verbose });
742
+
743
+ if (config.verbose) {
744
+ spinner.succeed('Dependencies resolved');
745
+ } else {
746
+ spinner.succeed('Dependencies resolved');
747
+ }
748
+
749
+ // Install core if requested or if dependencies require it
750
+ if (config.installCore || resolution.byModule.core) {
751
+ spinner.start('Installing XIAOMA core...');
752
+ await this.installCoreWithDependencies(xiaomaDir, resolution.byModule.core);
753
+ spinner.succeed('Core installed');
754
+ }
755
+
756
+ // Install modules with their dependencies
757
+ if (config.modules && config.modules.length > 0) {
758
+ for (const moduleName of config.modules) {
759
+ spinner.start(`Installing module: ${moduleName}...`);
760
+ await this.installModuleWithDependencies(moduleName, xiaomaDir, resolution.byModule[moduleName]);
761
+ spinner.succeed(`Module installed: ${moduleName}`);
762
+ }
763
+
764
+ // Install partial modules (only dependencies)
765
+ for (const [module, files] of Object.entries(resolution.byModule)) {
766
+ if (!config.modules.includes(module) && module !== 'core') {
767
+ const totalFiles =
768
+ files.agents.length +
769
+ files.tasks.length +
770
+ files.tools.length +
771
+ files.templates.length +
772
+ files.data.length +
773
+ files.other.length;
774
+ if (totalFiles > 0) {
775
+ spinner.start(`Installing ${module} dependencies...`);
776
+ await this.installPartialModule(module, xiaomaDir, files);
777
+ spinner.succeed(`${module} dependencies installed`);
778
+ }
779
+ }
780
+ }
781
+ }
782
+
783
+ // Generate clean config.yaml files for each installed module
784
+ spinner.start('Generating module configurations...');
785
+ await this.generateModuleConfigs(xiaomaDir, moduleConfigs);
786
+ spinner.succeed('Module configurations generated');
787
+
788
+ // Create agent configuration files
789
+ // Note: Legacy createAgentConfigs removed - using YAML customize system instead
790
+ // Customize templates are now created in processAgentFiles when building YAML agents
791
+
792
+ // Pre-register manifest files that will be created (except files-manifest.csv to avoid recursion)
793
+ const cfgDir = path.join(xiaomaDir, '_cfg');
794
+ this.installedFiles.push(
795
+ path.join(cfgDir, 'manifest.yaml'),
796
+ path.join(cfgDir, 'workflow-manifest.csv'),
797
+ path.join(cfgDir, 'agent-manifest.csv'),
798
+ path.join(cfgDir, 'task-manifest.csv'),
799
+ );
800
+
801
+ // Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes BEFORE IDE setup
802
+ spinner.start('Generating workflow and agent manifests...');
803
+ const manifestGen = new ManifestGenerator();
804
+
805
+ // Include preserved modules (from quick update) in the manifest
806
+ const allModulesToList = config._preserveModules ? [...(config.modules || []), ...config._preserveModules] : config.modules || [];
807
+
808
+ const manifestStats = await manifestGen.generateManifests(xiaomaDir, config.modules || [], this.installedFiles, {
809
+ ides: config.ides || [],
810
+ preservedModules: config._preserveModules || [], // Scan these from installed xiaoma/ dir
811
+ });
812
+
813
+ spinner.succeed(
814
+ `Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
815
+ );
816
+
817
+ // Configure IDEs and copy documentation
818
+ if (!config.skipIde && config.ides && config.ides.length > 0) {
819
+ // Filter out any undefined/null values from the IDE list
820
+ const validIdes = config.ides.filter((ide) => ide && typeof ide === 'string');
821
+
822
+ if (validIdes.length === 0) {
823
+ console.log(chalk.yellow('āš ļø No valid IDEs selected. Skipping IDE configuration.'));
824
+ } else {
825
+ // Check if any IDE might need prompting (no pre-collected config)
826
+ const needsPrompting = validIdes.some((ide) => !ideConfigurations[ide]);
827
+
828
+ if (!needsPrompting) {
829
+ spinner.start('Configuring IDEs...');
830
+ }
831
+
832
+ // Temporarily suppress console output if not verbose
833
+ const originalLog = console.log;
834
+ if (!config.verbose) {
835
+ console.log = () => {};
836
+ }
837
+
838
+ for (const ide of validIdes) {
839
+ // Only show spinner if we have pre-collected config (no prompts expected)
840
+ if (ideConfigurations[ide] && !needsPrompting) {
841
+ spinner.text = `Configuring ${ide}...`;
842
+ } else if (!ideConfigurations[ide]) {
843
+ // Stop spinner before prompting
844
+ if (spinner.isSpinning) {
845
+ spinner.stop();
846
+ }
847
+ console.log(chalk.cyan(`\nConfiguring ${ide}...`));
848
+ }
849
+
850
+ // Pass pre-collected configuration to avoid re-prompting
851
+ await this.ideManager.setup(ide, projectDir, xiaomaDir, {
852
+ selectedModules: config.modules || [],
853
+ preCollectedConfig: ideConfigurations[ide] || null,
854
+ verbose: config.verbose,
855
+ });
856
+
857
+ // Save IDE configuration for future updates
858
+ if (ideConfigurations[ide] && !ideConfigurations[ide]._alreadyConfigured) {
859
+ await this.ideConfigManager.saveIdeConfig(xiaomaDir, ide, ideConfigurations[ide]);
860
+ }
861
+
862
+ // Restart spinner if we stopped it
863
+ if (!ideConfigurations[ide] && !spinner.isSpinning) {
864
+ spinner.start('Configuring IDEs...');
865
+ }
866
+ }
867
+
868
+ // Restore console.log
869
+ console.log = originalLog;
870
+
871
+ if (spinner.isSpinning) {
872
+ spinner.succeed(`Configured ${validIdes.length} IDE${validIdes.length > 1 ? 's' : ''}`);
873
+ } else {
874
+ console.log(chalk.green(`āœ“ Configured ${validIdes.length} IDE${validIdes.length > 1 ? 's' : ''}`));
875
+ }
876
+ }
877
+
878
+ // Copy IDE-specific documentation (only for valid IDEs)
879
+ const validIdesForDocs = (config.ides || []).filter((ide) => ide && typeof ide === 'string');
880
+ if (validIdesForDocs.length > 0) {
881
+ spinner.start('Copying IDE documentation...');
882
+ await this.copyIdeDocumentation(validIdesForDocs, xiaomaDir);
883
+ spinner.succeed('IDE documentation copied');
884
+ }
885
+ }
886
+
887
+ // Run module-specific installers after IDE setup
888
+ spinner.start('Running module-specific installers...');
889
+
890
+ // Run core module installer if core was installed
891
+ if (config.installCore || resolution.byModule.core) {
892
+ spinner.text = 'Running core module installer...';
893
+
894
+ await this.moduleManager.runModuleInstaller('core', xiaomaDir, {
895
+ installedIDEs: config.ides || [],
896
+ moduleConfig: moduleConfigs.core || {},
897
+ logger: {
898
+ log: (msg) => console.log(msg),
899
+ error: (msg) => console.error(msg),
900
+ warn: (msg) => console.warn(msg),
901
+ },
902
+ });
903
+ }
904
+
905
+ // Run installers for user-selected modules
906
+ if (config.modules && config.modules.length > 0) {
907
+ for (const moduleName of config.modules) {
908
+ spinner.text = `Running ${moduleName} module installer...`;
909
+
910
+ // Pass installed IDEs and module config to module installer
911
+ await this.moduleManager.runModuleInstaller(moduleName, xiaomaDir, {
912
+ installedIDEs: config.ides || [],
913
+ moduleConfig: moduleConfigs[moduleName] || {},
914
+ logger: {
915
+ log: (msg) => console.log(msg),
916
+ error: (msg) => console.error(msg),
917
+ warn: (msg) => console.warn(msg),
918
+ },
919
+ });
920
+ }
921
+ }
922
+
923
+ spinner.succeed('Module-specific installers completed');
924
+
925
+ // Note: Manifest files are already created by ManifestGenerator above
926
+ // No need to create legacy manifest.csv anymore
927
+
928
+ // If this was an update, restore custom files
929
+ let customFiles = [];
930
+ let modifiedFiles = [];
931
+ if (config._isUpdate) {
932
+ if (config._customFiles && config._customFiles.length > 0) {
933
+ spinner.start(`Restoring ${config._customFiles.length} custom files...`);
934
+
935
+ for (const originalPath of config._customFiles) {
936
+ const relativePath = path.relative(xiaomaDir, originalPath);
937
+ const backupPath = path.join(config._tempBackupDir, relativePath);
938
+
939
+ if (await fs.pathExists(backupPath)) {
940
+ await fs.ensureDir(path.dirname(originalPath));
941
+ await fs.copy(backupPath, originalPath, { overwrite: true });
942
+ }
943
+ }
944
+
945
+ // Clean up temp backup
946
+ if (config._tempBackupDir && (await fs.pathExists(config._tempBackupDir))) {
947
+ await fs.remove(config._tempBackupDir);
948
+ }
949
+
950
+ spinner.succeed(`Restored ${config._customFiles.length} custom files`);
951
+ customFiles = config._customFiles;
952
+ }
953
+
954
+ if (config._modifiedFiles && config._modifiedFiles.length > 0) {
955
+ modifiedFiles = config._modifiedFiles;
956
+
957
+ // Restore modified files as .bak files
958
+ if (config._tempModifiedBackupDir && (await fs.pathExists(config._tempModifiedBackupDir))) {
959
+ spinner.start(`Restoring ${modifiedFiles.length} modified files as .bak...`);
960
+
961
+ for (const modifiedFile of modifiedFiles) {
962
+ const relativePath = path.relative(xiaomaDir, modifiedFile.path);
963
+ const tempBackupPath = path.join(config._tempModifiedBackupDir, relativePath);
964
+ const bakPath = modifiedFile.path + '.bak';
965
+
966
+ if (await fs.pathExists(tempBackupPath)) {
967
+ await fs.ensureDir(path.dirname(bakPath));
968
+ await fs.copy(tempBackupPath, bakPath, { overwrite: true });
969
+ }
970
+ }
971
+
972
+ // Clean up temp backup
973
+ await fs.remove(config._tempModifiedBackupDir);
974
+
975
+ spinner.succeed(`Restored ${modifiedFiles.length} modified files as .bak`);
976
+ }
977
+ }
978
+ }
979
+
980
+ spinner.stop();
981
+
982
+ // Report custom and modified files if any were found
983
+ if (customFiles.length > 0) {
984
+ console.log(chalk.cyan(`\nšŸ“ Custom files preserved: ${customFiles.length}`));
985
+ console.log(chalk.dim('The following custom files were found and restored:\n'));
986
+ for (const file of customFiles) {
987
+ console.log(chalk.dim(` - ${path.relative(xiaomaDir, file)}`));
988
+ }
989
+ console.log('');
990
+ }
991
+
992
+ if (modifiedFiles.length > 0) {
993
+ console.log(chalk.yellow(`\nāš ļø Modified files detected: ${modifiedFiles.length}`));
994
+ console.log(chalk.dim('The following files were modified and backed up with .bak extension:\n'));
995
+ for (const file of modifiedFiles) {
996
+ console.log(chalk.dim(` - ${file.relativePath} → ${file.relativePath}.bak`));
997
+ }
998
+ console.log(chalk.dim('\nThese files have been updated with the new version.'));
999
+ console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n'));
1000
+ }
1001
+
1002
+ // Reinstall custom agents from _cfg/custom/agents/ sources
1003
+ const customAgentResults = await this.reinstallCustomAgents(projectDir, xiaomaDir);
1004
+ if (customAgentResults.count > 0) {
1005
+ console.log(chalk.green(`\nāœ“ Reinstalled ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`));
1006
+ for (const agent of customAgentResults.agents) {
1007
+ console.log(chalk.dim(` - ${agent}`));
1008
+ }
1009
+ }
1010
+
1011
+ // Display completion message
1012
+ const { UI } = require('../../../lib/ui');
1013
+ const ui = new UI();
1014
+ ui.showInstallSummary({
1015
+ path: xiaomaDir,
1016
+ modules: config.modules,
1017
+ ides: config.ides,
1018
+ customFiles: customFiles.length > 0 ? customFiles : undefined,
1019
+ });
1020
+
1021
+ // Offer cleanup for legacy files (only for updates, not fresh installs, and only if not skipped)
1022
+ if (!config.skipCleanup && config._isUpdate) {
1023
+ try {
1024
+ const cleanupResult = await this.performCleanup(xiaomaDir, false);
1025
+ if (cleanupResult.deleted > 0) {
1026
+ console.log(chalk.green(`\nāœ“ Cleaned up ${cleanupResult.deleted} legacy file${cleanupResult.deleted > 1 ? 's' : ''}`));
1027
+ }
1028
+ if (cleanupResult.retained > 0) {
1029
+ console.log(chalk.dim(`Run 'xiaoma cleanup' anytime to manage retained files`));
1030
+ }
1031
+ } catch (cleanupError) {
1032
+ // Don't fail the installation for cleanup errors
1033
+ console.log(chalk.yellow(`\nāš ļø Cleanup warning: ${cleanupError.message}`));
1034
+ console.log(chalk.dim('Run "xiaoma cleanup" to manually clean up legacy files'));
1035
+ }
1036
+ }
1037
+
1038
+ return {
1039
+ success: true,
1040
+ path: xiaomaDir,
1041
+ modules: config.modules,
1042
+ ides: config.ides,
1043
+ needsAgentVibes: this.enableAgentVibes && !config.agentVibesInstalled,
1044
+ projectDir: projectDir,
1045
+ };
1046
+ } catch (error) {
1047
+ spinner.fail('Installation failed');
1048
+ throw error;
1049
+ }
1050
+ }
1051
+
1052
+ /**
1053
+ * Update existing installation
1054
+ */
1055
+ async update(config) {
1056
+ const spinner = ora('Checking installation...').start();
1057
+
1058
+ try {
1059
+ const projectDir = path.resolve(config.directory);
1060
+ const xiaomaDir = await this.findBmadDir(projectDir);
1061
+ const existingInstall = await this.detector.detect(xiaomaDir);
1062
+
1063
+ if (!existingInstall.installed) {
1064
+ spinner.fail('No XIAOMA installation found');
1065
+ throw new Error(`No XIAOMA installation found at ${xiaomaDir}`);
1066
+ }
1067
+
1068
+ spinner.text = 'Analyzing update requirements...';
1069
+
1070
+ // Compare versions and determine what needs updating
1071
+ const currentVersion = existingInstall.version;
1072
+ const newVersion = require(path.join(getProjectRoot(), 'package.json')).version;
1073
+
1074
+ if (config.dryRun) {
1075
+ spinner.stop();
1076
+ console.log(chalk.cyan('\nšŸ” Update Preview (Dry Run)\n'));
1077
+ console.log(chalk.bold('Current version:'), currentVersion);
1078
+ console.log(chalk.bold('New version:'), newVersion);
1079
+ console.log(chalk.bold('Core:'), existingInstall.hasCore ? 'Will be updated' : 'Not installed');
1080
+
1081
+ if (existingInstall.modules.length > 0) {
1082
+ console.log(chalk.bold('\nModules to update:'));
1083
+ for (const mod of existingInstall.modules) {
1084
+ console.log(` - ${mod.id}`);
1085
+ }
1086
+ }
1087
+ return;
1088
+ }
1089
+
1090
+ // Perform actual update
1091
+ if (existingInstall.hasCore) {
1092
+ spinner.text = 'Updating core...';
1093
+ await this.updateCore(xiaomaDir, config.force);
1094
+ }
1095
+
1096
+ for (const module of existingInstall.modules) {
1097
+ spinner.text = `Updating module: ${module.id}...`;
1098
+ await this.moduleManager.update(module.id, xiaomaDir, config.force);
1099
+ }
1100
+
1101
+ // Update manifest
1102
+ spinner.text = 'Updating manifest...';
1103
+ await this.manifest.update(xiaomaDir, {
1104
+ version: newVersion,
1105
+ updateDate: new Date().toISOString(),
1106
+ });
1107
+
1108
+ spinner.succeed('Update complete');
1109
+ return { success: true };
1110
+ } catch (error) {
1111
+ spinner.fail('Update failed');
1112
+ throw error;
1113
+ }
1114
+ }
1115
+
1116
+ /**
1117
+ * Get installation status
1118
+ */
1119
+ async getStatus(directory) {
1120
+ const projectDir = path.resolve(directory);
1121
+ const xiaomaDir = await this.findBmadDir(projectDir);
1122
+ return await this.detector.detect(xiaomaDir);
1123
+ }
1124
+
1125
+ /**
1126
+ * Get available modules
1127
+ */
1128
+ async getAvailableModules() {
1129
+ return await this.moduleManager.listAvailable();
1130
+ }
1131
+
1132
+ /**
1133
+ * Uninstall XIAOMA
1134
+ */
1135
+ async uninstall(directory) {
1136
+ const projectDir = path.resolve(directory);
1137
+ const xiaomaDir = await this.findBmadDir(projectDir);
1138
+
1139
+ if (await fs.pathExists(xiaomaDir)) {
1140
+ await fs.remove(xiaomaDir);
1141
+ }
1142
+
1143
+ // Clean up IDE configurations
1144
+ await this.ideManager.cleanup(projectDir);
1145
+
1146
+ return { success: true };
1147
+ }
1148
+
1149
+ /**
1150
+ * Private: Create directory structure
1151
+ */
1152
+ async createDirectoryStructure(xiaomaDir) {
1153
+ await fs.ensureDir(xiaomaDir);
1154
+ await fs.ensureDir(path.join(xiaomaDir, '_cfg'));
1155
+ await fs.ensureDir(path.join(xiaomaDir, '_cfg', 'agents'));
1156
+ }
1157
+
1158
+ /**
1159
+ * Generate clean config.yaml files for each installed module
1160
+ * @param {string} xiaomaDir - XIAOMA installation directory
1161
+ * @param {Object} moduleConfigs - Collected configuration values
1162
+ */
1163
+ async generateModuleConfigs(xiaomaDir, moduleConfigs) {
1164
+ const yaml = require('js-yaml');
1165
+
1166
+ // Extract core config values to share with other modules
1167
+ const coreConfig = moduleConfigs.core || {};
1168
+
1169
+ // Get all installed module directories
1170
+ const entries = await fs.readdir(xiaomaDir, { withFileTypes: true });
1171
+ const installedModules = entries
1172
+ .filter((entry) => entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs')
1173
+ .map((entry) => entry.name);
1174
+
1175
+ // Generate config.yaml for each installed module
1176
+ for (const moduleName of installedModules) {
1177
+ const modulePath = path.join(xiaomaDir, moduleName);
1178
+
1179
+ // Get module-specific config or use empty object if none
1180
+ const config = moduleConfigs[moduleName] || {};
1181
+
1182
+ if (await fs.pathExists(modulePath)) {
1183
+ const configPath = path.join(modulePath, 'config.yaml');
1184
+
1185
+ // Create header
1186
+ const packageJson = require(path.join(getProjectRoot(), 'package.json'));
1187
+ const header = `# ${moduleName.toUpperCase()} Module Configuration
1188
+ # Generated by XIAOMA installer
1189
+ # Version: ${packageJson.version}
1190
+ # Date: ${new Date().toISOString()}
1191
+
1192
+ `;
1193
+
1194
+ // For non-core modules, add core config values directly
1195
+ let finalConfig = { ...config };
1196
+ let coreSection = '';
1197
+
1198
+ if (moduleName !== 'core' && coreConfig && Object.keys(coreConfig).length > 0) {
1199
+ // Add core values directly to the module config
1200
+ // These will be available for reference in the module
1201
+ finalConfig = {
1202
+ ...config,
1203
+ ...coreConfig, // Spread core config values directly into the module config
1204
+ };
1205
+
1206
+ // Create a comment section to identify core values
1207
+ coreSection = '\n# Core Configuration Values\n';
1208
+ }
1209
+
1210
+ // Convert config to YAML
1211
+ let yamlContent = yaml.dump(finalConfig, {
1212
+ indent: 2,
1213
+ lineWidth: -1,
1214
+ noRefs: true,
1215
+ sortKeys: false,
1216
+ });
1217
+
1218
+ // If we have core values, reorganize the YAML to group them with their comment
1219
+ if (coreSection && moduleName !== 'core') {
1220
+ // Split the YAML into lines
1221
+ const lines = yamlContent.split('\n');
1222
+ const moduleConfigLines = [];
1223
+ const coreConfigLines = [];
1224
+
1225
+ // Separate module-specific and core config lines
1226
+ for (const line of lines) {
1227
+ const key = line.split(':')[0].trim();
1228
+ if (Object.prototype.hasOwnProperty.call(coreConfig, key)) {
1229
+ coreConfigLines.push(line);
1230
+ } else {
1231
+ moduleConfigLines.push(line);
1232
+ }
1233
+ }
1234
+
1235
+ // Rebuild YAML with module config first, then core config with comment
1236
+ yamlContent = moduleConfigLines.join('\n');
1237
+ if (coreConfigLines.length > 0) {
1238
+ yamlContent += coreSection + coreConfigLines.join('\n');
1239
+ }
1240
+ }
1241
+
1242
+ // Write the clean config file with POSIX-compliant final newline
1243
+ const content = header + yamlContent;
1244
+ await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
1245
+
1246
+ // Track the config file in installedFiles
1247
+ this.installedFiles.push(configPath);
1248
+ }
1249
+ }
1250
+ }
1251
+
1252
+ /**
1253
+ * Install core with resolved dependencies
1254
+ * @param {string} xiaomaDir - XIAOMA installation directory
1255
+ * @param {Object} coreFiles - Core files to install
1256
+ */
1257
+ async installCoreWithDependencies(xiaomaDir, coreFiles) {
1258
+ const sourcePath = getModulePath('core');
1259
+ const targetPath = path.join(xiaomaDir, 'core');
1260
+
1261
+ // Install full core
1262
+ await this.installCore(xiaomaDir);
1263
+
1264
+ // If there are specific dependency files, ensure they're included
1265
+ if (coreFiles) {
1266
+ // Already handled by installCore for core module
1267
+ }
1268
+ }
1269
+
1270
+ /**
1271
+ * Install module with resolved dependencies
1272
+ * @param {string} moduleName - Module name
1273
+ * @param {string} xiaomaDir - XIAOMA installation directory
1274
+ * @param {Object} moduleFiles - Module files to install
1275
+ */
1276
+ async installModuleWithDependencies(moduleName, xiaomaDir, moduleFiles) {
1277
+ // Get module configuration for conditional installation
1278
+ const moduleConfig = this.configCollector.collectedConfig[moduleName] || {};
1279
+
1280
+ // Use existing module manager for full installation with file tracking
1281
+ // Note: Module-specific installers are called separately after IDE setup
1282
+ await this.moduleManager.install(
1283
+ moduleName,
1284
+ xiaomaDir,
1285
+ (filePath) => {
1286
+ this.installedFiles.push(filePath);
1287
+ },
1288
+ {
1289
+ skipModuleInstaller: true, // We'll run it later after IDE setup
1290
+ moduleConfig: moduleConfig, // Pass module config for conditional filtering
1291
+ },
1292
+ );
1293
+
1294
+ // Process agent files to build YAML agents and create customize templates
1295
+ const modulePath = path.join(xiaomaDir, moduleName);
1296
+ await this.processAgentFiles(modulePath, moduleName);
1297
+
1298
+ // Dependencies are already included in full module install
1299
+ }
1300
+
1301
+ /**
1302
+ * Install partial module (only dependencies needed by other modules)
1303
+ */
1304
+ async installPartialModule(moduleName, xiaomaDir, files) {
1305
+ const sourceBase = getModulePath(moduleName);
1306
+ const targetBase = path.join(xiaomaDir, moduleName);
1307
+
1308
+ // Create module directory
1309
+ await fs.ensureDir(targetBase);
1310
+
1311
+ // Copy only the required dependency files
1312
+ if (files.agents && files.agents.length > 0) {
1313
+ const agentsDir = path.join(targetBase, 'agents');
1314
+ await fs.ensureDir(agentsDir);
1315
+
1316
+ for (const agentPath of files.agents) {
1317
+ const fileName = path.basename(agentPath);
1318
+ const sourcePath = path.join(sourceBase, 'agents', fileName);
1319
+ const targetPath = path.join(agentsDir, fileName);
1320
+
1321
+ if (await fs.pathExists(sourcePath)) {
1322
+ await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.xiaomaFolderName || 'xiaoma');
1323
+ this.installedFiles.push(targetPath);
1324
+ }
1325
+ }
1326
+ }
1327
+
1328
+ if (files.tasks && files.tasks.length > 0) {
1329
+ const tasksDir = path.join(targetBase, 'tasks');
1330
+ await fs.ensureDir(tasksDir);
1331
+
1332
+ for (const taskPath of files.tasks) {
1333
+ const fileName = path.basename(taskPath);
1334
+ const sourcePath = path.join(sourceBase, 'tasks', fileName);
1335
+ const targetPath = path.join(tasksDir, fileName);
1336
+
1337
+ if (await fs.pathExists(sourcePath)) {
1338
+ await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.xiaomaFolderName || 'xiaoma');
1339
+ this.installedFiles.push(targetPath);
1340
+ }
1341
+ }
1342
+ }
1343
+
1344
+ if (files.tools && files.tools.length > 0) {
1345
+ const toolsDir = path.join(targetBase, 'tools');
1346
+ await fs.ensureDir(toolsDir);
1347
+
1348
+ for (const toolPath of files.tools) {
1349
+ const fileName = path.basename(toolPath);
1350
+ const sourcePath = path.join(sourceBase, 'tools', fileName);
1351
+ const targetPath = path.join(toolsDir, fileName);
1352
+
1353
+ if (await fs.pathExists(sourcePath)) {
1354
+ await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.xiaomaFolderName || 'xiaoma');
1355
+ this.installedFiles.push(targetPath);
1356
+ }
1357
+ }
1358
+ }
1359
+
1360
+ if (files.templates && files.templates.length > 0) {
1361
+ const templatesDir = path.join(targetBase, 'templates');
1362
+ await fs.ensureDir(templatesDir);
1363
+
1364
+ for (const templatePath of files.templates) {
1365
+ const fileName = path.basename(templatePath);
1366
+ const sourcePath = path.join(sourceBase, 'templates', fileName);
1367
+ const targetPath = path.join(templatesDir, fileName);
1368
+
1369
+ if (await fs.pathExists(sourcePath)) {
1370
+ await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.xiaomaFolderName || 'xiaoma');
1371
+ this.installedFiles.push(targetPath);
1372
+ }
1373
+ }
1374
+ }
1375
+
1376
+ if (files.data && files.data.length > 0) {
1377
+ for (const dataPath of files.data) {
1378
+ // Preserve directory structure for data files
1379
+ const relative = path.relative(sourceBase, dataPath);
1380
+ const targetPath = path.join(targetBase, relative);
1381
+
1382
+ await fs.ensureDir(path.dirname(targetPath));
1383
+
1384
+ if (await fs.pathExists(dataPath)) {
1385
+ await this.copyFileWithPlaceholderReplacement(dataPath, targetPath, this.xiaomaFolderName || 'xiaoma');
1386
+ this.installedFiles.push(targetPath);
1387
+ }
1388
+ }
1389
+ }
1390
+
1391
+ // Create a marker file to indicate this is a partial installation
1392
+ const markerPath = path.join(targetBase, '.partial');
1393
+ await fs.writeFile(
1394
+ markerPath,
1395
+ `This module contains only dependencies required by other modules.\nInstalled: ${new Date().toISOString()}\n`,
1396
+ );
1397
+ }
1398
+
1399
+ /**
1400
+ * Private: Install core
1401
+ * @param {string} xiaomaDir - XIAOMA installation directory
1402
+ */
1403
+ async installCore(xiaomaDir) {
1404
+ const sourcePath = getModulePath('core');
1405
+ const targetPath = path.join(xiaomaDir, 'core');
1406
+
1407
+ // Copy core files with filtering for localskip agents
1408
+ await this.copyDirectoryWithFiltering(sourcePath, targetPath);
1409
+
1410
+ // Process agent files to inject activation block
1411
+ await this.processAgentFiles(targetPath, 'core');
1412
+ }
1413
+
1414
+ /**
1415
+ * Copy directory with filtering for localskip agents
1416
+ * @param {string} sourcePath - Source directory path
1417
+ * @param {string} targetPath - Target directory path
1418
+ */
1419
+ async copyDirectoryWithFiltering(sourcePath, targetPath) {
1420
+ // Get all files in source directory
1421
+ const files = await this.getFileList(sourcePath);
1422
+
1423
+ for (const file of files) {
1424
+ // Skip config.yaml templates - we'll generate clean ones with actual values
1425
+ if (file === 'config.yaml' || file.endsWith('/config.yaml')) {
1426
+ continue;
1427
+ }
1428
+
1429
+ const sourceFile = path.join(sourcePath, file);
1430
+ const targetFile = path.join(targetPath, file);
1431
+
1432
+ // Check if this is an agent file
1433
+ if (file.includes('agents/') && file.endsWith('.md')) {
1434
+ // Read the file to check for localskip
1435
+ const content = await fs.readFile(sourceFile, 'utf8');
1436
+
1437
+ // Check for localskip="true" in the agent tag
1438
+ const agentMatch = content.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
1439
+ if (agentMatch) {
1440
+ console.log(chalk.dim(` Skipping web-only agent: ${path.basename(file)}`));
1441
+ continue; // Skip this agent
1442
+ }
1443
+ }
1444
+
1445
+ // Copy the file with placeholder replacement
1446
+ await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile, this.xiaomaFolderName || 'xiaoma');
1447
+
1448
+ // Track the installed file
1449
+ this.installedFiles.push(targetFile);
1450
+ }
1451
+ }
1452
+
1453
+ /**
1454
+ * Get list of all files in a directory recursively
1455
+ * @param {string} dir - Directory path
1456
+ * @param {string} baseDir - Base directory for relative paths
1457
+ * @returns {Array} List of relative file paths
1458
+ */
1459
+ async getFileList(dir, baseDir = dir) {
1460
+ const files = [];
1461
+ const entries = await fs.readdir(dir, { withFileTypes: true });
1462
+
1463
+ for (const entry of entries) {
1464
+ const fullPath = path.join(dir, entry.name);
1465
+
1466
+ if (entry.isDirectory()) {
1467
+ // Skip _module-installer directories
1468
+ if (entry.name === '_module-installer') {
1469
+ continue;
1470
+ }
1471
+ const subFiles = await this.getFileList(fullPath, baseDir);
1472
+ files.push(...subFiles);
1473
+ } else {
1474
+ files.push(path.relative(baseDir, fullPath));
1475
+ }
1476
+ }
1477
+
1478
+ return files;
1479
+ }
1480
+
1481
+ /**
1482
+ * Process agent files to build YAML agents and inject activation blocks
1483
+ * @param {string} modulePath - Path to module in xiaoma/ installation
1484
+ * @param {string} moduleName - Module name
1485
+ */
1486
+ async processAgentFiles(modulePath, moduleName) {
1487
+ const agentsPath = path.join(modulePath, 'agents');
1488
+
1489
+ // Check if agents directory exists
1490
+ if (!(await fs.pathExists(agentsPath))) {
1491
+ return; // No agents to process
1492
+ }
1493
+
1494
+ // Determine project directory (parent of xiaoma/ directory)
1495
+ const xiaomaDir = path.dirname(modulePath);
1496
+ const projectDir = path.dirname(xiaomaDir);
1497
+ const cfgAgentsDir = path.join(xiaomaDir, '_cfg', 'agents');
1498
+
1499
+ // Ensure _cfg/agents directory exists
1500
+ await fs.ensureDir(cfgAgentsDir);
1501
+
1502
+ // Get all agent files
1503
+ const agentFiles = await fs.readdir(agentsPath);
1504
+
1505
+ for (const agentFile of agentFiles) {
1506
+ // Handle YAML agents - build them to .md
1507
+ if (agentFile.endsWith('.agent.yaml')) {
1508
+ const agentName = agentFile.replace('.agent.yaml', '');
1509
+ const yamlPath = path.join(agentsPath, agentFile);
1510
+ const mdPath = path.join(agentsPath, `${agentName}.md`);
1511
+ const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
1512
+
1513
+ // Create customize template if it doesn't exist
1514
+ if (!(await fs.pathExists(customizePath))) {
1515
+ const genericTemplatePath = getSourcePath('utility', 'templates', 'agent.customize.template.yaml');
1516
+ if (await fs.pathExists(genericTemplatePath)) {
1517
+ await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.xiaomaFolderName || 'xiaoma');
1518
+ console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
1519
+ }
1520
+ }
1521
+
1522
+ // Build YAML + customize to .md
1523
+ const customizeExists = await fs.pathExists(customizePath);
1524
+ const xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
1525
+ includeMetadata: true,
1526
+ });
1527
+
1528
+ // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
1529
+ // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
1530
+
1531
+ // Write the built .md file to xiaoma/{module}/agents/ with POSIX-compliant final newline
1532
+ const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
1533
+ await fs.writeFile(mdPath, content, 'utf8');
1534
+ this.installedFiles.push(mdPath);
1535
+
1536
+ // Remove the source YAML file - we can regenerate from installer source if needed
1537
+ await fs.remove(yamlPath);
1538
+
1539
+ console.log(chalk.dim(` Built agent: ${agentName}.md`));
1540
+ }
1541
+ // Handle legacy .md agents - inject activation if needed
1542
+ else if (agentFile.endsWith('.md')) {
1543
+ const agentPath = path.join(agentsPath, agentFile);
1544
+ let content = await fs.readFile(agentPath, 'utf8');
1545
+
1546
+ // Check if content has agent XML and no activation block
1547
+ if (content.includes('<agent') && !content.includes('<activation')) {
1548
+ // Inject the activation block using XML handler
1549
+ content = this.xmlHandler.injectActivationSimple(content);
1550
+ // Ensure POSIX-compliant final newline
1551
+ const finalContent = content.endsWith('\n') ? content : content + '\n';
1552
+ await fs.writeFile(agentPath, finalContent, 'utf8');
1553
+ }
1554
+ }
1555
+ }
1556
+ }
1557
+
1558
+ /**
1559
+ * Build standalone agents in xiaoma/agents/ directory
1560
+ * @param {string} xiaomaDir - Path to xiaoma directory
1561
+ * @param {string} projectDir - Path to project directory
1562
+ */
1563
+ async buildStandaloneAgents(xiaomaDir, projectDir) {
1564
+ const standaloneAgentsPath = path.join(xiaomaDir, 'agents');
1565
+ const cfgAgentsDir = path.join(xiaomaDir, '_cfg', 'agents');
1566
+
1567
+ // Check if standalone agents directory exists
1568
+ if (!(await fs.pathExists(standaloneAgentsPath))) {
1569
+ return;
1570
+ }
1571
+
1572
+ // Get all subdirectories in agents/
1573
+ const agentDirs = await fs.readdir(standaloneAgentsPath, { withFileTypes: true });
1574
+
1575
+ for (const agentDir of agentDirs) {
1576
+ if (!agentDir.isDirectory()) continue;
1577
+
1578
+ const agentDirPath = path.join(standaloneAgentsPath, agentDir.name);
1579
+
1580
+ // Find any .agent.yaml file in the directory
1581
+ const files = await fs.readdir(agentDirPath);
1582
+ const yamlFile = files.find((f) => f.endsWith('.agent.yaml'));
1583
+
1584
+ if (!yamlFile) continue;
1585
+
1586
+ const agentName = path.basename(yamlFile, '.agent.yaml');
1587
+ const sourceYamlPath = path.join(agentDirPath, yamlFile);
1588
+ const targetMdPath = path.join(agentDirPath, `${agentName}.md`);
1589
+ const customizePath = path.join(cfgAgentsDir, `${agentName}.customize.yaml`);
1590
+
1591
+ // Check for customizations
1592
+ const customizeExists = await fs.pathExists(customizePath);
1593
+ let customizedFields = [];
1594
+
1595
+ if (customizeExists) {
1596
+ const customizeContent = await fs.readFile(customizePath, 'utf8');
1597
+ const yaml = require('js-yaml');
1598
+ const customizeYaml = yaml.load(customizeContent);
1599
+
1600
+ // Detect what fields are customized (similar to rebuildAgentFiles)
1601
+ if (customizeYaml) {
1602
+ if (customizeYaml.persona) {
1603
+ for (const [key, value] of Object.entries(customizeYaml.persona)) {
1604
+ if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
1605
+ customizedFields.push(`persona.${key}`);
1606
+ }
1607
+ }
1608
+ }
1609
+ if (customizeYaml.agent?.metadata) {
1610
+ for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
1611
+ if (value !== '' && value !== null) {
1612
+ customizedFields.push(`metadata.${key}`);
1613
+ }
1614
+ }
1615
+ }
1616
+ if (customizeYaml.critical_actions && customizeYaml.critical_actions.length > 0) {
1617
+ customizedFields.push('critical_actions');
1618
+ }
1619
+ if (customizeYaml.menu && customizeYaml.menu.length > 0) {
1620
+ customizedFields.push('menu');
1621
+ }
1622
+ }
1623
+ }
1624
+
1625
+ // Build YAML to XML .md
1626
+ const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
1627
+ includeMetadata: true,
1628
+ });
1629
+
1630
+ // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
1631
+ // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
1632
+
1633
+ // Write the built .md file with POSIX-compliant final newline
1634
+ const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
1635
+ await fs.writeFile(targetMdPath, content, 'utf8');
1636
+
1637
+ // Display result
1638
+ if (customizedFields.length > 0) {
1639
+ console.log(chalk.dim(` Built standalone agent: ${agentName}.md `) + chalk.yellow(`(customized: ${customizedFields.join(', ')})`));
1640
+ } else {
1641
+ console.log(chalk.dim(` Built standalone agent: ${agentName}.md`));
1642
+ }
1643
+ }
1644
+ }
1645
+
1646
+ /**
1647
+ * Rebuild agent files from installer source (for compile command)
1648
+ * @param {string} modulePath - Path to module in xiaoma/ installation
1649
+ * @param {string} moduleName - Module name
1650
+ */
1651
+ async rebuildAgentFiles(modulePath, moduleName) {
1652
+ // Get source agents directory from installer
1653
+ const sourceAgentsPath =
1654
+ moduleName === 'core' ? path.join(getModulePath('core'), 'agents') : path.join(getSourcePath(`modules/${moduleName}`), 'agents');
1655
+
1656
+ if (!(await fs.pathExists(sourceAgentsPath))) {
1657
+ return; // No source agents to rebuild
1658
+ }
1659
+
1660
+ // Determine project directory (parent of xiaoma/ directory)
1661
+ const xiaomaDir = path.dirname(modulePath);
1662
+ const projectDir = path.dirname(xiaomaDir);
1663
+ const cfgAgentsDir = path.join(xiaomaDir, '_cfg', 'agents');
1664
+ const targetAgentsPath = path.join(modulePath, 'agents');
1665
+
1666
+ // Ensure target directory exists
1667
+ await fs.ensureDir(targetAgentsPath);
1668
+
1669
+ // Get all YAML agent files from source
1670
+ const sourceFiles = await fs.readdir(sourceAgentsPath);
1671
+
1672
+ for (const file of sourceFiles) {
1673
+ if (file.endsWith('.agent.yaml')) {
1674
+ const agentName = file.replace('.agent.yaml', '');
1675
+ const sourceYamlPath = path.join(sourceAgentsPath, file);
1676
+ const targetMdPath = path.join(targetAgentsPath, `${agentName}.md`);
1677
+ const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
1678
+
1679
+ // Check for customizations
1680
+ const customizeExists = await fs.pathExists(customizePath);
1681
+ let customizedFields = [];
1682
+
1683
+ if (customizeExists) {
1684
+ const customizeContent = await fs.readFile(customizePath, 'utf8');
1685
+ const yaml = require('js-yaml');
1686
+ const customizeYaml = yaml.load(customizeContent);
1687
+
1688
+ // Detect what fields are customized
1689
+ if (customizeYaml) {
1690
+ if (customizeYaml.persona) {
1691
+ for (const [key, value] of Object.entries(customizeYaml.persona)) {
1692
+ if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
1693
+ customizedFields.push(`persona.${key}`);
1694
+ }
1695
+ }
1696
+ }
1697
+ if (customizeYaml.agent?.metadata) {
1698
+ for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
1699
+ if (value !== '' && value !== null) {
1700
+ customizedFields.push(`metadata.${key}`);
1701
+ }
1702
+ }
1703
+ }
1704
+ if (customizeYaml.critical_actions && customizeYaml.critical_actions.length > 0) {
1705
+ customizedFields.push('critical_actions');
1706
+ }
1707
+ if (customizeYaml.memories && customizeYaml.memories.length > 0) {
1708
+ customizedFields.push('memories');
1709
+ }
1710
+ if (customizeYaml.menu && customizeYaml.menu.length > 0) {
1711
+ customizedFields.push('menu');
1712
+ }
1713
+ if (customizeYaml.prompts && customizeYaml.prompts.length > 0) {
1714
+ customizedFields.push('prompts');
1715
+ }
1716
+ }
1717
+ }
1718
+
1719
+ // Build YAML + customize to .md
1720
+ const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
1721
+ includeMetadata: true,
1722
+ });
1723
+
1724
+ // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
1725
+ // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
1726
+
1727
+ // Write the rebuilt .md file with POSIX-compliant final newline
1728
+ const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
1729
+ await fs.writeFile(targetMdPath, content, 'utf8');
1730
+
1731
+ // Display result with customizations if any
1732
+ if (customizedFields.length > 0) {
1733
+ console.log(chalk.dim(` Rebuilt agent: ${agentName}.md `) + chalk.yellow(`(customized: ${customizedFields.join(', ')})`));
1734
+ } else {
1735
+ console.log(chalk.dim(` Rebuilt agent: ${agentName}.md`));
1736
+ }
1737
+ }
1738
+ }
1739
+ }
1740
+
1741
+ /**
1742
+ * Compile/rebuild all agents and tasks for quick updates
1743
+ * @param {Object} config - Compilation configuration
1744
+ * @returns {Object} Compilation results
1745
+ */
1746
+ async compileAgents(config) {
1747
+ const ora = require('ora');
1748
+ const spinner = ora('Starting agent compilation...').start();
1749
+
1750
+ try {
1751
+ const projectDir = path.resolve(config.directory);
1752
+ const xiaomaDir = await this.findBmadDir(projectDir);
1753
+
1754
+ // Check if xiaoma directory exists
1755
+ if (!(await fs.pathExists(xiaomaDir))) {
1756
+ spinner.fail('No XIAOMA installation found');
1757
+ throw new Error(`XIAOMA not installed at ${xiaomaDir}`);
1758
+ }
1759
+
1760
+ let agentCount = 0;
1761
+ let taskCount = 0;
1762
+
1763
+ // Process all modules in xiaoma directory
1764
+ spinner.text = 'Rebuilding agent files...';
1765
+ const entries = await fs.readdir(xiaomaDir, { withFileTypes: true });
1766
+
1767
+ for (const entry of entries) {
1768
+ if (entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs') {
1769
+ const modulePath = path.join(xiaomaDir, entry.name);
1770
+
1771
+ // Special handling for standalone agents in xiaoma/agents/ directory
1772
+ if (entry.name === 'agents') {
1773
+ spinner.text = 'Building standalone agents...';
1774
+ await this.buildStandaloneAgents(xiaomaDir, projectDir);
1775
+
1776
+ // Count standalone agents
1777
+ const standaloneAgentsPath = path.join(xiaomaDir, 'agents');
1778
+ const standaloneAgentDirs = await fs.readdir(standaloneAgentsPath, { withFileTypes: true });
1779
+ for (const agentDir of standaloneAgentDirs) {
1780
+ if (agentDir.isDirectory()) {
1781
+ const agentDirPath = path.join(standaloneAgentsPath, agentDir.name);
1782
+ const agentFiles = await fs.readdir(agentDirPath);
1783
+ agentCount += agentFiles.filter((f) => f.endsWith('.md') && !f.endsWith('.agent.yaml')).length;
1784
+ }
1785
+ }
1786
+ } else {
1787
+ // Rebuild module agents from installer source
1788
+ const agentsPath = path.join(modulePath, 'agents');
1789
+ if (await fs.pathExists(agentsPath)) {
1790
+ await this.rebuildAgentFiles(modulePath, entry.name);
1791
+ const agentFiles = await fs.readdir(agentsPath);
1792
+ agentCount += agentFiles.filter((f) => f.endsWith('.md')).length;
1793
+ }
1794
+
1795
+ // Count tasks (already built)
1796
+ const tasksPath = path.join(modulePath, 'tasks');
1797
+ if (await fs.pathExists(tasksPath)) {
1798
+ const taskFiles = await fs.readdir(tasksPath);
1799
+ taskCount += taskFiles.filter((f) => f.endsWith('.md')).length;
1800
+ }
1801
+ }
1802
+ }
1803
+ }
1804
+
1805
+ // Reinstall custom agents from _cfg/custom/agents/ sources
1806
+ spinner.start('Rebuilding custom agents...');
1807
+ const customAgentResults = await this.reinstallCustomAgents(projectDir, xiaomaDir);
1808
+ if (customAgentResults.count > 0) {
1809
+ spinner.succeed(`Rebuilt ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`);
1810
+ agentCount += customAgentResults.count;
1811
+ } else {
1812
+ spinner.succeed('No custom agents found to rebuild');
1813
+ }
1814
+
1815
+ // Skip full manifest regeneration during compileAgents to preserve custom agents
1816
+ // Custom agents are already added to manifests during individual installation
1817
+ // Only regenerate YAML manifest for IDE updates if needed
1818
+ const existingManifestPath = path.join(xiaomaDir, '_cfg', 'manifest.yaml');
1819
+ let existingIdes = [];
1820
+ if (await fs.pathExists(existingManifestPath)) {
1821
+ const manifestContent = await fs.readFile(existingManifestPath, 'utf8');
1822
+ const yaml = require('js-yaml');
1823
+ const manifest = yaml.load(manifestContent);
1824
+ existingIdes = manifest.ides || [];
1825
+ }
1826
+
1827
+ // Update IDE configurations using the existing IDE list from manifest
1828
+ if (existingIdes && existingIdes.length > 0) {
1829
+ spinner.start('Updating IDE configurations...');
1830
+
1831
+ for (const ide of existingIdes) {
1832
+ spinner.text = `Updating ${ide}...`;
1833
+
1834
+ // Stop spinner before IDE setup to prevent blocking any potential prompts
1835
+ // However, we pass _alreadyConfigured to skip all prompts during compile
1836
+ spinner.stop();
1837
+
1838
+ await this.ideManager.setup(ide, projectDir, xiaomaDir, {
1839
+ selectedModules: installedModules,
1840
+ skipModuleInstall: true, // Skip module installation, just update IDE files
1841
+ verbose: config.verbose,
1842
+ preCollectedConfig: { _alreadyConfigured: true }, // Skip all interactive prompts during compile
1843
+ });
1844
+
1845
+ // Restart spinner for next IDE
1846
+ if (existingIdes.indexOf(ide) < existingIdes.length - 1) {
1847
+ spinner.start('Updating IDE configurations...');
1848
+ }
1849
+ }
1850
+
1851
+ console.log(chalk.green('āœ“ IDE configurations updated'));
1852
+ } else {
1853
+ console.log(chalk.yellow('āš ļø No IDEs configured. Skipping IDE update.'));
1854
+ }
1855
+
1856
+ return { agentCount, taskCount };
1857
+ } catch (error) {
1858
+ spinner.fail('Compilation failed');
1859
+ throw error;
1860
+ }
1861
+ }
1862
+
1863
+ /**
1864
+ * Private: Update core
1865
+ */
1866
+ async updateCore(xiaomaDir, force = false) {
1867
+ const sourcePath = getModulePath('core');
1868
+ const targetPath = path.join(xiaomaDir, 'core');
1869
+
1870
+ if (force) {
1871
+ await fs.remove(targetPath);
1872
+ await this.installCore(xiaomaDir);
1873
+ } else {
1874
+ // Selective update - preserve user modifications
1875
+ await this.fileOps.syncDirectory(sourcePath, targetPath);
1876
+ }
1877
+ }
1878
+
1879
+ /**
1880
+ * Quick update method - preserves all settings and only prompts for new config fields
1881
+ * @param {Object} config - Configuration with directory
1882
+ * @returns {Object} Update result
1883
+ */
1884
+ async quickUpdate(config) {
1885
+ const ora = require('ora');
1886
+ const spinner = ora('Starting quick update...').start();
1887
+
1888
+ try {
1889
+ const projectDir = path.resolve(config.directory);
1890
+ const xiaomaDir = await this.findBmadDir(projectDir);
1891
+
1892
+ // Check if xiaoma directory exists
1893
+ if (!(await fs.pathExists(xiaomaDir))) {
1894
+ spinner.fail('No XIAOMA installation found');
1895
+ throw new Error(`XIAOMA not installed at ${xiaomaDir}. Use regular install for first-time setup.`);
1896
+ }
1897
+
1898
+ spinner.text = 'Detecting installed modules and configuration...';
1899
+
1900
+ // Detect existing installation
1901
+ const existingInstall = await this.detector.detect(xiaomaDir);
1902
+ const installedModules = existingInstall.modules.map((m) => m.id);
1903
+ const configuredIdes = existingInstall.ides || [];
1904
+
1905
+ // Load saved IDE configurations
1906
+ const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(xiaomaDir);
1907
+
1908
+ // Get available modules (what we have source for)
1909
+ const availableModules = await this.moduleManager.listAvailable();
1910
+ const availableModuleIds = new Set(availableModules.map((m) => m.id));
1911
+
1912
+ // Only update modules that are BOTH installed AND available (we have source for)
1913
+ const modulesToUpdate = installedModules.filter((id) => availableModuleIds.has(id));
1914
+ const skippedModules = installedModules.filter((id) => !availableModuleIds.has(id));
1915
+
1916
+ spinner.succeed(`Found ${modulesToUpdate.length} module(s) to update and ${configuredIdes.length} configured tool(s)`);
1917
+
1918
+ if (skippedModules.length > 0) {
1919
+ console.log(chalk.yellow(`āš ļø Skipping ${skippedModules.length} module(s) - no source available: ${skippedModules.join(', ')}`));
1920
+ }
1921
+
1922
+ // Load existing configs and collect new fields (if any)
1923
+ console.log(chalk.cyan('\nšŸ“‹ Checking for new configuration options...'));
1924
+ await this.configCollector.loadExistingConfig(projectDir);
1925
+
1926
+ let promptedForNewFields = false;
1927
+
1928
+ // Check core config for new fields
1929
+ const corePrompted = await this.configCollector.collectModuleConfigQuick('core', projectDir, true);
1930
+ if (corePrompted) {
1931
+ promptedForNewFields = true;
1932
+ }
1933
+
1934
+ // Check each module we're updating for new fields (NOT skipped modules)
1935
+ for (const moduleName of modulesToUpdate) {
1936
+ const modulePrompted = await this.configCollector.collectModuleConfigQuick(moduleName, projectDir, true);
1937
+ if (modulePrompted) {
1938
+ promptedForNewFields = true;
1939
+ }
1940
+ }
1941
+
1942
+ if (!promptedForNewFields) {
1943
+ console.log(chalk.green('āœ“ All configuration is up to date, no new options to configure'));
1944
+ }
1945
+
1946
+ // Add metadata
1947
+ this.configCollector.collectedConfig._meta = {
1948
+ version: require(path.join(getProjectRoot(), 'package.json')).version,
1949
+ installDate: new Date().toISOString(),
1950
+ lastModified: new Date().toISOString(),
1951
+ };
1952
+
1953
+ // Check if xiaoma_folder has changed
1954
+ const existingBmadFolderName = path.basename(xiaomaDir);
1955
+ const newBmadFolderName = this.configCollector.collectedConfig.core?.xiaoma_folder || existingBmadFolderName;
1956
+
1957
+ if (existingBmadFolderName === newBmadFolderName) {
1958
+ // Normal quick update - start the spinner
1959
+ console.log(chalk.cyan('Updating XIAOMA installation...'));
1960
+ } else {
1961
+ // Folder name has changed - stop spinner and let install() handle it
1962
+ spinner.stop();
1963
+ console.log(chalk.yellow(`\nāš ļø Folder name will change: ${existingBmadFolderName} → ${newBmadFolderName}`));
1964
+ console.log(chalk.yellow('The installer will handle the folder migration.\n'));
1965
+ }
1966
+
1967
+ // Build the config object for the installer
1968
+ const installConfig = {
1969
+ directory: projectDir,
1970
+ installCore: true,
1971
+ modules: modulesToUpdate, // Only update modules we have source for
1972
+ ides: configuredIdes,
1973
+ skipIde: configuredIdes.length === 0,
1974
+ coreConfig: this.configCollector.collectedConfig.core,
1975
+ actionType: 'install', // Use regular install flow
1976
+ _quickUpdate: true, // Flag to skip certain prompts
1977
+ _preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
1978
+ _savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
1979
+ };
1980
+
1981
+ // Call the standard install method
1982
+ const result = await this.install(installConfig);
1983
+
1984
+ // Only succeed the spinner if it's still spinning
1985
+ // (install method might have stopped it if folder name changed)
1986
+ if (spinner.isSpinning) {
1987
+ spinner.succeed('Quick update complete!');
1988
+ }
1989
+
1990
+ return {
1991
+ success: true,
1992
+ moduleCount: modulesToUpdate.length + 1, // +1 for core
1993
+ hadNewFields: promptedForNewFields,
1994
+ modules: ['core', ...modulesToUpdate],
1995
+ skippedModules: skippedModules,
1996
+ ides: configuredIdes,
1997
+ };
1998
+ } catch (error) {
1999
+ spinner.fail('Quick update failed');
2000
+ throw error;
2001
+ }
2002
+ }
2003
+
2004
+ /**
2005
+ * Private: Prompt for update action
2006
+ */
2007
+ async promptUpdateAction() {
2008
+ const inquirer = require('inquirer');
2009
+ return await inquirer.prompt([
2010
+ {
2011
+ type: 'list',
2012
+ name: 'action',
2013
+ message: 'What would you like to do?',
2014
+ choices: [
2015
+ { name: 'Update existing installation', value: 'update' },
2016
+ { name: 'Remove and reinstall', value: 'reinstall' },
2017
+ { name: 'Cancel', value: 'cancel' },
2018
+ ],
2019
+ },
2020
+ ]);
2021
+ }
2022
+
2023
+ /**
2024
+ * Handle legacy XIAOMA v4 migration with automatic backup
2025
+ * @param {string} projectDir - Project directory
2026
+ * @param {Object} legacyV4 - Legacy V4 detection result with offenders array
2027
+ */
2028
+ async handleLegacyV4Migration(projectDir, legacyV4) {
2029
+ console.log(chalk.yellow.bold('\nāš ļø Legacy XIAOMA v4 detected'));
2030
+ console.log(chalk.dim('The installer found legacy artefacts in your project.\n'));
2031
+
2032
+ // Separate .xiaoma* folders (auto-backup) from other offending paths (manual cleanup)
2033
+ const xiaomaFolders = legacyV4.offenders.filter((p) => {
2034
+ const name = path.basename(p);
2035
+ return name.startsWith('.xiaoma'); // Only dot-prefixed folders get auto-backed up
2036
+ });
2037
+ const otherOffenders = legacyV4.offenders.filter((p) => {
2038
+ const name = path.basename(p);
2039
+ return !name.startsWith('.xiaoma'); // Everything else is manual cleanup
2040
+ });
2041
+
2042
+ const inquirer = require('inquirer');
2043
+
2044
+ // Show warning for other offending paths FIRST
2045
+ if (otherOffenders.length > 0) {
2046
+ console.log(chalk.yellow('āš ļø Recommended cleanup:'));
2047
+ console.log(chalk.dim('It is recommended to remove the following items before proceeding:\n'));
2048
+ for (const p of otherOffenders) console.log(chalk.dim(` - ${p}`));
2049
+
2050
+ console.log(chalk.cyan('\nCleanup commands you can copy/paste:'));
2051
+ console.log(chalk.dim('macOS/Linux:'));
2052
+ for (const p of otherOffenders) console.log(chalk.dim(` rm -rf '${p}'`));
2053
+ console.log(chalk.dim('Windows:'));
2054
+ for (const p of otherOffenders) console.log(chalk.dim(` rmdir /S /Q "${p}"`));
2055
+
2056
+ const { cleanedUp } = await inquirer.prompt([
2057
+ {
2058
+ type: 'confirm',
2059
+ name: 'cleanedUp',
2060
+ message: 'Have you completed the recommended cleanup? (You can proceed without it, but it is recommended)',
2061
+ default: false,
2062
+ },
2063
+ ]);
2064
+
2065
+ if (cleanedUp) {
2066
+ console.log(chalk.green('āœ“ Cleanup acknowledged\n'));
2067
+ } else {
2068
+ console.log(chalk.yellow('āš ļø Proceeding without recommended cleanup\n'));
2069
+ }
2070
+ }
2071
+
2072
+ // Handle .xiaoma* folders with automatic backup
2073
+ if (xiaomaFolders.length > 0) {
2074
+ console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:'));
2075
+ for (const p of xiaomaFolders) console.log(chalk.dim(` - ${p}`));
2076
+
2077
+ const { proceed } = await inquirer.prompt([
2078
+ {
2079
+ type: 'confirm',
2080
+ name: 'proceed',
2081
+ message: 'Proceed with backing up legacy v4 folders?',
2082
+ default: true,
2083
+ },
2084
+ ]);
2085
+
2086
+ if (proceed) {
2087
+ const backupDir = path.join(projectDir, 'v4-backup');
2088
+ await fs.ensureDir(backupDir);
2089
+
2090
+ for (const folder of xiaomaFolders) {
2091
+ const folderName = path.basename(folder);
2092
+ const backupPath = path.join(backupDir, folderName);
2093
+
2094
+ // If backup already exists, add timestamp
2095
+ let finalBackupPath = backupPath;
2096
+ if (await fs.pathExists(backupPath)) {
2097
+ const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').split('T')[0];
2098
+ finalBackupPath = path.join(backupDir, `${folderName}-${timestamp}`);
2099
+ }
2100
+
2101
+ await fs.move(folder, finalBackupPath, { overwrite: false });
2102
+ console.log(chalk.green(`āœ“ Moved ${folderName} to ${path.relative(projectDir, finalBackupPath)}`));
2103
+ }
2104
+ } else {
2105
+ throw new Error('Installation cancelled by user');
2106
+ }
2107
+ }
2108
+ }
2109
+
2110
+ /**
2111
+ * Read files-manifest.csv
2112
+ * @param {string} xiaomaDir - XIAOMA installation directory
2113
+ * @returns {Array} Array of file entries from files-manifest.csv
2114
+ */
2115
+ async readFilesManifest(xiaomaDir) {
2116
+ const filesManifestPath = path.join(xiaomaDir, '_cfg', 'files-manifest.csv');
2117
+ if (!(await fs.pathExists(filesManifestPath))) {
2118
+ return [];
2119
+ }
2120
+
2121
+ try {
2122
+ const content = await fs.readFile(filesManifestPath, 'utf8');
2123
+ const lines = content.split('\n');
2124
+ const files = [];
2125
+
2126
+ for (let i = 1; i < lines.length; i++) {
2127
+ // Skip header
2128
+ const line = lines[i].trim();
2129
+ if (!line) continue;
2130
+
2131
+ // Parse CSV line properly handling quoted values
2132
+ const parts = [];
2133
+ let current = '';
2134
+ let inQuotes = false;
2135
+
2136
+ for (const char of line) {
2137
+ if (char === '"') {
2138
+ inQuotes = !inQuotes;
2139
+ } else if (char === ',' && !inQuotes) {
2140
+ parts.push(current);
2141
+ current = '';
2142
+ } else {
2143
+ current += char;
2144
+ }
2145
+ }
2146
+ parts.push(current); // Add last part
2147
+
2148
+ if (parts.length >= 4) {
2149
+ files.push({
2150
+ type: parts[0],
2151
+ name: parts[1],
2152
+ module: parts[2],
2153
+ path: parts[3],
2154
+ hash: parts[4] || null, // Hash may not exist in old manifests
2155
+ });
2156
+ }
2157
+ }
2158
+
2159
+ return files;
2160
+ } catch (error) {
2161
+ console.warn('Warning: Could not read files-manifest.csv:', error.message);
2162
+ return [];
2163
+ }
2164
+ }
2165
+
2166
+ /**
2167
+ * Detect custom and modified files
2168
+ * @param {string} xiaomaDir - XIAOMA installation directory
2169
+ * @param {Array} existingFilesManifest - Previous files from files-manifest.csv
2170
+ * @returns {Object} Object with customFiles and modifiedFiles arrays
2171
+ */
2172
+ async detectCustomFiles(xiaomaDir, existingFilesManifest) {
2173
+ const customFiles = [];
2174
+ const modifiedFiles = [];
2175
+
2176
+ // Check if the manifest has hashes - if not, we can't detect modifications
2177
+ let manifestHasHashes = false;
2178
+ if (existingFilesManifest && existingFilesManifest.length > 0) {
2179
+ manifestHasHashes = existingFilesManifest.some((f) => f.hash);
2180
+ }
2181
+
2182
+ // Build map of previously installed files from files-manifest.csv with their hashes
2183
+ const installedFilesMap = new Map();
2184
+ for (const fileEntry of existingFilesManifest) {
2185
+ if (fileEntry.path) {
2186
+ // Files in manifest are stored as relative paths starting with 'xiaoma/'
2187
+ // Convert to absolute path
2188
+ const relativePath = fileEntry.path.startsWith('xiaoma/') ? fileEntry.path.slice(5) : fileEntry.path;
2189
+ const absolutePath = path.join(xiaomaDir, relativePath);
2190
+ installedFilesMap.set(path.normalize(absolutePath), {
2191
+ hash: fileEntry.hash,
2192
+ relativePath: relativePath,
2193
+ });
2194
+ }
2195
+ }
2196
+
2197
+ // Recursively scan xiaomaDir for all files
2198
+ const scanDirectory = async (dir) => {
2199
+ try {
2200
+ const entries = await fs.readdir(dir, { withFileTypes: true });
2201
+ for (const entry of entries) {
2202
+ const fullPath = path.join(dir, entry.name);
2203
+
2204
+ if (entry.isDirectory()) {
2205
+ // Skip certain directories
2206
+ if (entry.name === 'node_modules' || entry.name === '.git') {
2207
+ continue;
2208
+ }
2209
+ await scanDirectory(fullPath);
2210
+ } else if (entry.isFile()) {
2211
+ const normalizedPath = path.normalize(fullPath);
2212
+ const fileInfo = installedFilesMap.get(normalizedPath);
2213
+
2214
+ // Skip certain system files that are auto-generated
2215
+ const relativePath = path.relative(xiaomaDir, fullPath);
2216
+ const fileName = path.basename(fullPath);
2217
+
2218
+ // Skip _cfg directory - system files
2219
+ if (relativePath.startsWith('_cfg/') || relativePath.startsWith('_cfg\\')) {
2220
+ continue;
2221
+ }
2222
+
2223
+ // Skip config.yaml files - these are regenerated on each install/update
2224
+ // Users should use _cfg/agents/ override files instead
2225
+ if (fileName === 'config.yaml') {
2226
+ continue;
2227
+ }
2228
+
2229
+ if (!fileInfo) {
2230
+ // File not in manifest = custom file
2231
+ customFiles.push(fullPath);
2232
+ } else if (manifestHasHashes && fileInfo.hash) {
2233
+ // File in manifest with hash - check if it was modified
2234
+ const currentHash = await this.manifest.calculateFileHash(fullPath);
2235
+ if (currentHash && currentHash !== fileInfo.hash) {
2236
+ // Hash changed = file was modified
2237
+ modifiedFiles.push({
2238
+ path: fullPath,
2239
+ relativePath: fileInfo.relativePath,
2240
+ });
2241
+ }
2242
+ }
2243
+ // If manifest doesn't have hashes, we can't detect modifications
2244
+ // so we just skip files that are in the manifest
2245
+ }
2246
+ }
2247
+ } catch {
2248
+ // Ignore errors scanning directories
2249
+ }
2250
+ };
2251
+
2252
+ await scanDirectory(xiaomaDir);
2253
+ return { customFiles, modifiedFiles };
2254
+ }
2255
+
2256
+ /**
2257
+ * Private: Create agent configuration files
2258
+ * @param {string} xiaomaDir - XIAOMA installation directory
2259
+ * @param {Object} userInfo - User information including name and language
2260
+ */
2261
+ async createAgentConfigs(xiaomaDir, userInfo = null) {
2262
+ const agentConfigDir = path.join(xiaomaDir, '_cfg', 'agents');
2263
+ await fs.ensureDir(agentConfigDir);
2264
+
2265
+ // Get all agents from all modules
2266
+ const agents = [];
2267
+ const agentDetails = []; // For manifest generation
2268
+
2269
+ // Check modules for agents (including core)
2270
+ const entries = await fs.readdir(xiaomaDir, { withFileTypes: true });
2271
+ for (const entry of entries) {
2272
+ if (entry.isDirectory() && entry.name !== '_cfg') {
2273
+ const moduleAgentsPath = path.join(xiaomaDir, entry.name, 'agents');
2274
+ if (await fs.pathExists(moduleAgentsPath)) {
2275
+ const agentFiles = await fs.readdir(moduleAgentsPath);
2276
+ for (const agentFile of agentFiles) {
2277
+ if (agentFile.endsWith('.md')) {
2278
+ const agentPath = path.join(moduleAgentsPath, agentFile);
2279
+ const agentContent = await fs.readFile(agentPath, 'utf8');
2280
+
2281
+ // Skip agents with localskip="true"
2282
+ const hasLocalSkip = agentContent.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
2283
+ if (hasLocalSkip) {
2284
+ continue; // Skip this agent - it should not have been installed
2285
+ }
2286
+
2287
+ const agentName = path.basename(agentFile, '.md');
2288
+
2289
+ // Extract any nodes with agentConfig="true"
2290
+ const agentConfigNodes = this.extractAgentConfigNodes(agentContent);
2291
+
2292
+ agents.push({
2293
+ name: agentName,
2294
+ module: entry.name,
2295
+ agentConfigNodes: agentConfigNodes,
2296
+ });
2297
+
2298
+ // Use shared AgentPartyGenerator to extract details
2299
+ let details = AgentPartyGenerator.extractAgentDetails(agentContent, entry.name, agentName);
2300
+
2301
+ // Apply config overrides if they exist
2302
+ if (details) {
2303
+ const configPath = path.join(agentConfigDir, `${entry.name}-${agentName}.md`);
2304
+ if (await fs.pathExists(configPath)) {
2305
+ const configContent = await fs.readFile(configPath, 'utf8');
2306
+ details = AgentPartyGenerator.applyConfigOverrides(details, configContent);
2307
+ }
2308
+ agentDetails.push(details);
2309
+ }
2310
+ }
2311
+ }
2312
+ }
2313
+ }
2314
+ }
2315
+
2316
+ // Create config file for each agent
2317
+ let createdCount = 0;
2318
+ let skippedCount = 0;
2319
+
2320
+ // Load agent config template
2321
+ const templatePath = getSourcePath('utility', 'models', 'agent-config-template.md');
2322
+ const templateContent = await fs.readFile(templatePath, 'utf8');
2323
+
2324
+ for (const agent of agents) {
2325
+ const configPath = path.join(agentConfigDir, `${agent.module}-${agent.name}.md`);
2326
+
2327
+ // Skip if config file already exists (preserve custom configurations)
2328
+ if (await fs.pathExists(configPath)) {
2329
+ skippedCount++;
2330
+ continue;
2331
+ }
2332
+
2333
+ // Build config content header
2334
+ let configContent = `# Agent Config: ${agent.name}\n\n`;
2335
+
2336
+ // Process template and add agent-specific config nodes
2337
+ let processedTemplate = templateContent;
2338
+
2339
+ // Replace {core:user_name} placeholder with actual user name if available
2340
+ if (userInfo && userInfo.userName) {
2341
+ processedTemplate = processedTemplate.replaceAll('{core:user_name}', userInfo.userName);
2342
+ }
2343
+
2344
+ // Replace {core:communication_language} placeholder with actual language if available
2345
+ if (userInfo && userInfo.responseLanguage) {
2346
+ processedTemplate = processedTemplate.replaceAll('{core:communication_language}', userInfo.responseLanguage);
2347
+ }
2348
+
2349
+ // If this agent has agentConfig nodes, add them after the existing comment
2350
+ if (agent.agentConfigNodes && agent.agentConfigNodes.length > 0) {
2351
+ // Find the agent-specific configuration nodes comment
2352
+ const commentPattern = /(\s*<!-- Agent-specific configuration nodes -->)/;
2353
+ const commentMatch = processedTemplate.match(commentPattern);
2354
+
2355
+ if (commentMatch) {
2356
+ // Add nodes right after the comment
2357
+ let agentSpecificNodes = '';
2358
+ for (const node of agent.agentConfigNodes) {
2359
+ agentSpecificNodes += `\n ${node}`;
2360
+ }
2361
+
2362
+ processedTemplate = processedTemplate.replace(commentPattern, `$1${agentSpecificNodes}`);
2363
+ }
2364
+ }
2365
+
2366
+ configContent += processedTemplate;
2367
+
2368
+ // Ensure POSIX-compliant final newline
2369
+ if (!configContent.endsWith('\n')) {
2370
+ configContent += '\n';
2371
+ }
2372
+
2373
+ await fs.writeFile(configPath, configContent, 'utf8');
2374
+ this.installedFiles.push(configPath); // Track agent config files
2375
+ createdCount++;
2376
+ }
2377
+
2378
+ // Generate agent manifest with overrides applied
2379
+ await this.generateAgentManifest(xiaomaDir, agentDetails);
2380
+
2381
+ return { total: agents.length, created: createdCount, skipped: skippedCount };
2382
+ }
2383
+
2384
+ /**
2385
+ * Generate agent manifest XML file
2386
+ * @param {string} xiaomaDir - XIAOMA installation directory
2387
+ * @param {Array} agentDetails - Array of agent details
2388
+ */
2389
+ async generateAgentManifest(xiaomaDir, agentDetails) {
2390
+ const manifestPath = path.join(xiaomaDir, '_cfg', 'agent-manifest.csv');
2391
+ await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
2392
+ }
2393
+
2394
+ /**
2395
+ * Extract nodes with agentConfig="true" from agent content
2396
+ * @param {string} content - Agent file content
2397
+ * @returns {Array} Array of XML nodes that should be added to agent config
2398
+ */
2399
+ extractAgentConfigNodes(content) {
2400
+ const nodes = [];
2401
+
2402
+ try {
2403
+ // Find all XML nodes with agentConfig="true"
2404
+ // Match self-closing tags and tags with content
2405
+ const selfClosingPattern = /<([a-zA-Z][a-zA-Z0-9_-]*)\s+[^>]*agentConfig="true"[^>]*\/>/g;
2406
+ const withContentPattern = /<([a-zA-Z][a-zA-Z0-9_-]*)\s+[^>]*agentConfig="true"[^>]*>([\s\S]*?)<\/\1>/g;
2407
+
2408
+ // Extract self-closing tags
2409
+ let match;
2410
+ while ((match = selfClosingPattern.exec(content)) !== null) {
2411
+ // Extract just the tag without children (structure only)
2412
+ const tagMatch = match[0].match(/<([a-zA-Z][a-zA-Z0-9_-]*)([^>]*)\/>/);
2413
+ if (tagMatch) {
2414
+ const tagName = tagMatch[1];
2415
+ const attributes = tagMatch[2].replace(/\s*agentConfig="true"/, ''); // Remove agentConfig attribute
2416
+ nodes.push(`<${tagName}${attributes}></${tagName}>`);
2417
+ }
2418
+ }
2419
+
2420
+ // Extract tags with content
2421
+ while ((match = withContentPattern.exec(content)) !== null) {
2422
+ const fullMatch = match[0];
2423
+ const tagName = match[1];
2424
+
2425
+ // Extract opening tag with attributes (removing agentConfig="true")
2426
+ const openingTagMatch = fullMatch.match(new RegExp(`<${tagName}([^>]*)>`));
2427
+ if (openingTagMatch) {
2428
+ const attributes = openingTagMatch[1].replace(/\s*agentConfig="true"/, '');
2429
+ // Add empty node structure (no children)
2430
+ nodes.push(`<${tagName}${attributes}></${tagName}>`);
2431
+ }
2432
+ }
2433
+ } catch (error) {
2434
+ console.error('Error extracting agentConfig nodes:', error);
2435
+ }
2436
+
2437
+ return nodes;
2438
+ }
2439
+
2440
+ /**
2441
+ * Reinstall custom agents from backup and source locations
2442
+ * This preserves custom agents across quick updates/reinstalls
2443
+ * @param {string} projectDir - Project directory
2444
+ * @param {string} xiaomaDir - XIAOMA installation directory
2445
+ * @returns {Object} Result with count and agent names
2446
+ */
2447
+ async reinstallCustomAgents(projectDir, xiaomaDir) {
2448
+ const {
2449
+ discoverAgents,
2450
+ loadAgentConfig,
2451
+ extractManifestData,
2452
+ addToManifest,
2453
+ createIdeSlashCommands,
2454
+ updateManifestYaml,
2455
+ } = require('../../../lib/agent/installer');
2456
+ const { compileAgent } = require('../../../lib/agent/compiler');
2457
+
2458
+ const results = { count: 0, agents: [] };
2459
+
2460
+ // Check multiple locations for custom agents
2461
+ const sourceLocations = [
2462
+ path.join(xiaomaDir, '_cfg', 'custom', 'agents'), // Backup location
2463
+ path.join(xiaomaDir, 'custom', 'src', 'agents'), // XIAOMA folder source location
2464
+ path.join(projectDir, 'custom', 'src', 'agents'), // Project root source location
2465
+ ];
2466
+
2467
+ let foundAgents = [];
2468
+ let processedAgents = new Set(); // Track to avoid duplicates
2469
+
2470
+ // Discover agents from all locations
2471
+ for (const location of sourceLocations) {
2472
+ if (await fs.pathExists(location)) {
2473
+ const agents = discoverAgents(location);
2474
+ // Only add agents we haven't processed yet
2475
+ const newAgents = agents.filter((agent) => !processedAgents.has(agent.name));
2476
+ foundAgents.push(...newAgents);
2477
+ for (const agent of newAgents) processedAgents.add(agent.name);
2478
+ }
2479
+ }
2480
+
2481
+ if (foundAgents.length === 0) {
2482
+ return results;
2483
+ }
2484
+
2485
+ try {
2486
+ const customAgentsDir = path.join(xiaomaDir, 'custom', 'agents');
2487
+ await fs.ensureDir(customAgentsDir);
2488
+
2489
+ const manifestFile = path.join(xiaomaDir, '_cfg', 'agent-manifest.csv');
2490
+ const manifestYamlFile = path.join(xiaomaDir, '_cfg', 'manifest.yaml');
2491
+
2492
+ for (const agent of foundAgents) {
2493
+ try {
2494
+ const agentConfig = loadAgentConfig(agent.yamlFile);
2495
+ const finalAgentName = agent.name; // Already named correctly from save
2496
+
2497
+ // Determine agent type from the name (e.g., "fred-commit-poet" → "commit-poet")
2498
+ let agentType = finalAgentName;
2499
+ const parts = finalAgentName.split('-');
2500
+ if (parts.length >= 2) {
2501
+ // Try to extract type (last part or last two parts)
2502
+ // For "fred-commit-poet", we want "commit-poet"
2503
+ // This is heuristic - could be improved with metadata storage
2504
+ agentType = parts.slice(-2).join('-'); // Take last 2 parts as type
2505
+ }
2506
+
2507
+ // Create target directory
2508
+ const agentTargetDir = path.join(customAgentsDir, finalAgentName);
2509
+ await fs.ensureDir(agentTargetDir);
2510
+
2511
+ // Calculate paths
2512
+ const compiledFileName = `${finalAgentName}.md`;
2513
+ const compiledPath = path.join(agentTargetDir, compiledFileName);
2514
+ const relativePath = path.relative(projectDir, compiledPath);
2515
+
2516
+ // Compile with embedded defaults (answers are already in defaults section)
2517
+ const { xml, metadata } = compileAgent(
2518
+ await fs.readFile(agent.yamlFile, 'utf8'),
2519
+ agentConfig.defaults || {},
2520
+ finalAgentName,
2521
+ relativePath,
2522
+ );
2523
+
2524
+ // Write compiled agent
2525
+ await fs.writeFile(compiledPath, xml, 'utf8');
2526
+
2527
+ // Backup source YAML to _cfg/custom/agents if not already there
2528
+ const cfgAgentsBackupDir = path.join(xiaomaDir, '_cfg', 'custom', 'agents');
2529
+ await fs.ensureDir(cfgAgentsBackupDir);
2530
+ const backupYamlPath = path.join(cfgAgentsBackupDir, `${finalAgentName}.agent.yaml`);
2531
+
2532
+ // Only backup if source is not already in backup location
2533
+ if (agent.yamlFile !== backupYamlPath) {
2534
+ await fs.copy(agent.yamlFile, backupYamlPath);
2535
+ }
2536
+
2537
+ // Copy sidecar files if expert agent
2538
+ if (agent.hasSidecar && agent.type === 'expert') {
2539
+ const { copySidecarFiles } = require('../../../lib/agent/installer');
2540
+ copySidecarFiles(agent.path, agentTargetDir, agent.yamlFile);
2541
+ }
2542
+
2543
+ // Update manifest CSV
2544
+ if (await fs.pathExists(manifestFile)) {
2545
+ // Preserve YAML metadata for persona name, but override id for filename
2546
+ const manifestMetadata = {
2547
+ ...metadata,
2548
+ id: relativePath, // Use the compiled agent path for id
2549
+ name: metadata.name || finalAgentName, // Use YAML metadata.name (persona name) or fallback
2550
+ title: metadata.title, // Use YAML title
2551
+ icon: metadata.icon, // Use YAML icon
2552
+ };
2553
+ const manifestData = extractManifestData(xml, manifestMetadata, relativePath, 'custom');
2554
+ manifestData.name = finalAgentName; // Use filename for the name field
2555
+ manifestData.path = relativePath;
2556
+ addToManifest(manifestFile, manifestData);
2557
+ }
2558
+
2559
+ // Create IDE slash commands (async function)
2560
+ await createIdeSlashCommands(projectDir, finalAgentName, relativePath, metadata);
2561
+
2562
+ // Update manifest.yaml
2563
+ if (await fs.pathExists(manifestYamlFile)) {
2564
+ updateManifestYaml(manifestYamlFile, finalAgentName, agentType);
2565
+ }
2566
+
2567
+ results.count++;
2568
+ results.agents.push(finalAgentName);
2569
+ } catch (agentError) {
2570
+ console.log(chalk.yellow(` āš ļø Failed to reinstall ${agent.name}: ${agentError.message}`));
2571
+ }
2572
+ }
2573
+ } catch (error) {
2574
+ console.log(chalk.yellow(` āš ļø Error reinstalling custom agents: ${error.message}`));
2575
+ }
2576
+
2577
+ return results;
2578
+ }
2579
+
2580
+ /**
2581
+ * Copy IDE-specific documentation to XIAOMA docs
2582
+ * @param {Array} ides - List of selected IDEs
2583
+ * @param {string} xiaomaDir - XIAOMA installation directory
2584
+ */
2585
+ async copyIdeDocumentation(ides, xiaomaDir) {
2586
+ const docsDir = path.join(xiaomaDir, 'docs');
2587
+ await fs.ensureDir(docsDir);
2588
+
2589
+ for (const ide of ides) {
2590
+ const sourceDocPath = path.join(getProjectRoot(), 'docs', 'ide-info', `${ide}.md`);
2591
+ const targetDocPath = path.join(docsDir, `${ide}-instructions.md`);
2592
+
2593
+ if (await fs.pathExists(sourceDocPath)) {
2594
+ await this.copyFileWithPlaceholderReplacement(sourceDocPath, targetDocPath, this.xiaomaFolderName || 'xiaoma');
2595
+ }
2596
+ }
2597
+ }
2598
+
2599
+ /**
2600
+ * Scan for legacy/obsolete files in XIAOMA installation
2601
+ * @param {string} xiaomaDir - XIAOMA installation directory
2602
+ * @returns {Object} Categorized files for cleanup
2603
+ */
2604
+ async scanForLegacyFiles(xiaomaDir) {
2605
+ const legacyFiles = {
2606
+ backup: [],
2607
+ documentation: [],
2608
+ deprecated_task: [],
2609
+ unknown: [],
2610
+ };
2611
+
2612
+ try {
2613
+ // Load files manifest to understand what should exist
2614
+ const manifestPath = path.join(xiaomaDir, 'files-manifest.csv');
2615
+ const manifestFiles = new Set();
2616
+
2617
+ if (await fs.pathExists(manifestPath)) {
2618
+ const manifestContent = await fs.readFile(manifestPath, 'utf8');
2619
+ const lines = manifestContent.split('\n').slice(1); // Skip header
2620
+ for (const line of lines) {
2621
+ if (line.trim()) {
2622
+ const relativePath = line.split(',')[0];
2623
+ if (relativePath) {
2624
+ manifestFiles.add(relativePath);
2625
+ }
2626
+ }
2627
+ }
2628
+ }
2629
+
2630
+ // Scan all files recursively
2631
+ const allFiles = await this.getAllFiles(xiaomaDir);
2632
+
2633
+ for (const filePath of allFiles) {
2634
+ const relativePath = path.relative(xiaomaDir, filePath);
2635
+
2636
+ // Skip expected files
2637
+ if (this.isExpectedFile(relativePath, manifestFiles)) {
2638
+ continue;
2639
+ }
2640
+
2641
+ // Categorize legacy files
2642
+ if (relativePath.endsWith('.bak')) {
2643
+ legacyFiles.backup.push({
2644
+ path: filePath,
2645
+ relativePath: relativePath,
2646
+ size: (await fs.stat(filePath)).size,
2647
+ mtime: (await fs.stat(filePath)).mtime,
2648
+ });
2649
+ } else if (this.isDocumentationFile(relativePath)) {
2650
+ legacyFiles.documentation.push({
2651
+ path: filePath,
2652
+ relativePath: relativePath,
2653
+ size: (await fs.stat(filePath)).size,
2654
+ mtime: (await fs.stat(filePath)).mtime,
2655
+ });
2656
+ } else if (this.isDeprecatedTaskFile(relativePath)) {
2657
+ const suggestedAlternative = this.suggestAlternative(relativePath);
2658
+ legacyFiles.deprecated_task.push({
2659
+ path: filePath,
2660
+ relativePath: relativePath,
2661
+ size: (await fs.stat(filePath)).size,
2662
+ mtime: (await fs.stat(filePath)).mtime,
2663
+ suggestedAlternative,
2664
+ });
2665
+ } else {
2666
+ legacyFiles.unknown.push({
2667
+ path: filePath,
2668
+ relativePath: relativePath,
2669
+ size: (await fs.stat(filePath)).size,
2670
+ mtime: (await fs.stat(filePath)).mtime,
2671
+ });
2672
+ }
2673
+ }
2674
+ } catch (error) {
2675
+ console.warn(`Warning: Could not scan for legacy files: ${error.message}`);
2676
+ }
2677
+
2678
+ return legacyFiles;
2679
+ }
2680
+
2681
+ /**
2682
+ * Get all files in directory recursively
2683
+ * @param {string} dir - Directory to scan
2684
+ * @returns {Array} Array of file paths
2685
+ */
2686
+ async getAllFiles(dir) {
2687
+ const files = [];
2688
+
2689
+ async function scan(currentDir) {
2690
+ const entries = await fs.readdir(currentDir);
2691
+
2692
+ for (const entry of entries) {
2693
+ const fullPath = path.join(currentDir, entry);
2694
+ const stat = await fs.stat(fullPath);
2695
+
2696
+ if (stat.isDirectory()) {
2697
+ // Skip certain directories
2698
+ if (!['node_modules', '.git', 'dist', 'build'].includes(entry)) {
2699
+ await scan(fullPath);
2700
+ }
2701
+ } else {
2702
+ files.push(fullPath);
2703
+ }
2704
+ }
2705
+ }
2706
+
2707
+ await scan(dir);
2708
+ return files;
2709
+ }
2710
+
2711
+ /**
2712
+ * Check if file is expected in installation
2713
+ * @param {string} relativePath - Relative path from XIAOMA dir
2714
+ * @param {Set} manifestFiles - Files from manifest
2715
+ * @returns {boolean} True if expected file
2716
+ */
2717
+ isExpectedFile(relativePath, manifestFiles) {
2718
+ // Core files in manifest
2719
+ if (manifestFiles.has(relativePath)) {
2720
+ return true;
2721
+ }
2722
+
2723
+ // Configuration files
2724
+ if (relativePath.startsWith('_cfg/') || relativePath === 'config.yaml') {
2725
+ return true;
2726
+ }
2727
+
2728
+ // Custom files
2729
+ if (relativePath.startsWith('custom/') || relativePath === 'manifest.yaml') {
2730
+ return true;
2731
+ }
2732
+
2733
+ // Generated files
2734
+ if (relativePath === 'manifest.csv' || relativePath === 'files-manifest.csv') {
2735
+ return true;
2736
+ }
2737
+
2738
+ // IDE-specific files
2739
+ const ides = ['vscode', 'cursor', 'windsurf', 'claude-code', 'github-copilot', 'zsh', 'bash', 'fish'];
2740
+ if (ides.some((ide) => relativePath.includes(ide))) {
2741
+ return true;
2742
+ }
2743
+
2744
+ // XIAOMA MODULE STRUCTURES - recognize valid module content
2745
+ const modulePrefixes = ['xmb/', 'xmc/', 'cis/', 'core/'];
2746
+ const validExtensions = ['.yaml', '.yml', '.json', '.csv', '.md', '.xml', '.svg', '.png', '.jpg', '.gif', '.excalidraw', '.js'];
2747
+
2748
+ // Check if this file is in a recognized module directory
2749
+ for (const modulePrefix of modulePrefixes) {
2750
+ if (relativePath.startsWith(modulePrefix)) {
2751
+ // Check if it has a valid extension
2752
+ const hasValidExtension = validExtensions.some((ext) => relativePath.endsWith(ext));
2753
+ if (hasValidExtension) {
2754
+ return true;
2755
+ }
2756
+ }
2757
+ }
2758
+
2759
+ // Special case for core module resources
2760
+ if (relativePath.startsWith('core/resources/')) {
2761
+ return true;
2762
+ }
2763
+
2764
+ // Special case for docs directory
2765
+ if (relativePath.startsWith('docs/')) {
2766
+ return true;
2767
+ }
2768
+
2769
+ return false;
2770
+ }
2771
+
2772
+ /**
2773
+ * Check if file is documentation
2774
+ * @param {string} relativePath - Relative path
2775
+ * @returns {boolean} True if documentation
2776
+ */
2777
+ isDocumentationFile(relativePath) {
2778
+ const docExtensions = ['.md', '.txt', '.pdf'];
2779
+ const docPatterns = ['docs/', 'README', 'CHANGELOG', 'LICENSE'];
2780
+
2781
+ return docExtensions.some((ext) => relativePath.endsWith(ext)) || docPatterns.some((pattern) => relativePath.includes(pattern));
2782
+ }
2783
+
2784
+ /**
2785
+ * Check if file is deprecated task file
2786
+ * @param {string} relativePath - Relative path
2787
+ * @returns {boolean} True if deprecated
2788
+ */
2789
+ isDeprecatedTaskFile(relativePath) {
2790
+ // Known deprecated files
2791
+ const deprecatedFiles = ['adv-elicit-methods.csv', 'game-resources.json', 'ux-workflow.json'];
2792
+
2793
+ return deprecatedFiles.some((dep) => relativePath.includes(dep));
2794
+ }
2795
+
2796
+ /**
2797
+ * Suggest alternative for deprecated file
2798
+ * @param {string} relativePath - Deprecated file path
2799
+ * @returns {string} Suggested alternative
2800
+ */
2801
+ suggestAlternative(relativePath) {
2802
+ const alternatives = {
2803
+ 'adv-elicit-methods.csv': 'Use the new structured workflows in src/modules/',
2804
+ 'game-resources.json': 'Resources are now integrated into modules',
2805
+ 'ux-workflow.json': 'UX workflows are now in src/modules/xmc/workflows/',
2806
+ };
2807
+
2808
+ for (const [deprecated, alternative] of Object.entries(alternatives)) {
2809
+ if (relativePath.includes(deprecated)) {
2810
+ return alternative;
2811
+ }
2812
+ }
2813
+
2814
+ return 'Check src/modules/ for new alternatives';
2815
+ }
2816
+
2817
+ /**
2818
+ * Perform interactive cleanup of legacy files
2819
+ * @param {string} xiaomaDir - XIAOMA installation directory
2820
+ * @param {boolean} skipInteractive - Skip interactive prompts
2821
+ * @returns {Object} Cleanup results
2822
+ */
2823
+ async performCleanup(xiaomaDir, skipInteractive = false) {
2824
+ const inquirer = require('inquirer');
2825
+ const yaml = require('js-yaml');
2826
+
2827
+ // Load user retention preferences
2828
+ const retentionPath = path.join(xiaomaDir, '_cfg', 'user-retained-files.yaml');
2829
+ let retentionData = { retainedFiles: [], history: [] };
2830
+
2831
+ if (await fs.pathExists(retentionPath)) {
2832
+ const retentionContent = await fs.readFile(retentionPath, 'utf8');
2833
+ retentionData = yaml.load(retentionContent) || retentionData;
2834
+ }
2835
+
2836
+ // Scan for legacy files
2837
+ const legacyFiles = await this.scanForLegacyFiles(xiaomaDir);
2838
+ const allLegacyFiles = [...legacyFiles.backup, ...legacyFiles.documentation, ...legacyFiles.deprecated_task, ...legacyFiles.unknown];
2839
+
2840
+ if (allLegacyFiles.length === 0) {
2841
+ return { deleted: 0, retained: 0, message: 'No legacy files found' };
2842
+ }
2843
+
2844
+ let deletedCount = 0;
2845
+ let retainedCount = 0;
2846
+ const filesToDelete = [];
2847
+
2848
+ if (skipInteractive) {
2849
+ // Auto-delete all non-retained files
2850
+ for (const file of allLegacyFiles) {
2851
+ if (!retentionData.retainedFiles.includes(file.relativePath)) {
2852
+ filesToDelete.push(file);
2853
+ }
2854
+ }
2855
+ } else {
2856
+ // Interactive cleanup
2857
+ console.log(chalk.cyan('\n🧹 Legacy File Cleanup\n'));
2858
+ console.log(chalk.dim('The following obsolete files were found:\n'));
2859
+
2860
+ // Group files by category
2861
+ const categories = [];
2862
+ if (legacyFiles.backup.length > 0) {
2863
+ categories.push({ name: 'Backup Files (.bak)', files: legacyFiles.backup });
2864
+ }
2865
+ if (legacyFiles.documentation.length > 0) {
2866
+ categories.push({ name: 'Documentation', files: legacyFiles.documentation });
2867
+ }
2868
+ if (legacyFiles.deprecated_task.length > 0) {
2869
+ categories.push({ name: 'Deprecated Task Files', files: legacyFiles.deprecated_task });
2870
+ }
2871
+ if (legacyFiles.unknown.length > 0) {
2872
+ categories.push({ name: 'Unknown Files', files: legacyFiles.unknown });
2873
+ }
2874
+
2875
+ for (const category of categories) {
2876
+ console.log(chalk.yellow(`${category.name}:`));
2877
+ for (const file of category.files) {
2878
+ const size = (file.size / 1024).toFixed(1);
2879
+ const date = file.mtime.toLocaleDateString();
2880
+ let line = ` - ${file.relativePath} (${size}KB, ${date})`;
2881
+ if (file.suggestedAlternative) {
2882
+ line += chalk.dim(` → ${file.suggestedAlternative}`);
2883
+ }
2884
+ console.log(chalk.dim(line));
2885
+ }
2886
+ console.log();
2887
+ }
2888
+
2889
+ const prompt = await inquirer.prompt([
2890
+ {
2891
+ type: 'confirm',
2892
+ name: 'proceed',
2893
+ message: 'Would you like to review these files for cleanup?',
2894
+ default: true,
2895
+ },
2896
+ ]);
2897
+
2898
+ if (!prompt.proceed) {
2899
+ return { deleted: 0, retained: allLegacyFiles.length, message: 'Cleanup cancelled by user' };
2900
+ }
2901
+
2902
+ // Show selection interface
2903
+ const selectionPrompt = await inquirer.prompt([
2904
+ {
2905
+ type: 'checkbox',
2906
+ name: 'filesToDelete',
2907
+ message: 'Select files to delete (use SPACEBAR to select, ENTER to continue):',
2908
+ choices: allLegacyFiles.map((file) => {
2909
+ const isRetained = retentionData.retainedFiles.includes(file.relativePath);
2910
+ const description = `${file.relativePath} (${(file.size / 1024).toFixed(1)}KB)`;
2911
+ return {
2912
+ name: description,
2913
+ value: file,
2914
+ checked: !isRetained && !file.relativePath.includes('.bak'),
2915
+ };
2916
+ }),
2917
+ pageSize: Math.min(allLegacyFiles.length, 15),
2918
+ },
2919
+ ]);
2920
+
2921
+ filesToDelete.push(...selectionPrompt.filesToDelete);
2922
+ }
2923
+
2924
+ // Delete selected files
2925
+ for (const file of filesToDelete) {
2926
+ try {
2927
+ await fs.remove(file.path);
2928
+ deletedCount++;
2929
+ } catch (error) {
2930
+ console.warn(`Warning: Could not delete ${file.relativePath}: ${error.message}`);
2931
+ }
2932
+ }
2933
+
2934
+ // Count retained files
2935
+ retainedCount = allLegacyFiles.length - deletedCount;
2936
+
2937
+ // Update retention data
2938
+ const newlyRetained = allLegacyFiles.filter((f) => !filesToDelete.includes(f)).map((f) => f.relativePath);
2939
+
2940
+ retentionData.retainedFiles = [...new Set([...retentionData.retainedFiles, ...newlyRetained])];
2941
+ retentionData.history.push({
2942
+ date: new Date().toISOString(),
2943
+ deleted: deletedCount,
2944
+ retained: retainedCount,
2945
+ files: filesToDelete.map((f) => f.relativePath),
2946
+ });
2947
+
2948
+ // Save retention data
2949
+ await fs.ensureDir(path.dirname(retentionPath));
2950
+ await fs.writeFile(retentionPath, yaml.dump(retentionData), 'utf8');
2951
+
2952
+ return { deleted: deletedCount, retained: retainedCount };
2953
+ }
2954
+ }
2955
+
2956
+ module.exports = { Installer };