@zeyue0329/xiaoma-cli 1.13.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (712) hide show
  1. package/package.json +24 -14
  2. package/src/core-skills/module-help.csv +13 -0
  3. package/src/{core → core-skills}/module.yaml +8 -0
  4. package/src/{core/skills/xiaoma-advanced-elicitation/workflow.md → core-skills/xiaoma-advanced-elicitation/SKILL.md} +10 -3
  5. package/src/core-skills/xiaoma-advanced-elicitation/methods.csv +70 -0
  6. package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-03-technique-execution.md +6 -4
  7. package/src/{core/skills → core-skills}/xiaoma-brainstorming/workflow.md +1 -1
  8. package/src/core-skills/xiaoma-customize/SKILL.md +111 -0
  9. package/src/core-skills/xiaoma-customize/scripts/list_customizable_skills.js +172 -0
  10. package/src/{core/skills → core-skills}/xiaoma-distillator/resources/distillate-format-reference.md +1 -1
  11. package/src/{core/skills → core-skills}/xiaoma-distillator/scripts/analyze_sources.py +2 -2
  12. package/src/{core/skills/xiaoma-editorial-review-prose/workflow.md → core-skills/xiaoma-editorial-review-prose/SKILL.md} +5 -0
  13. package/src/{core/skills/xiaoma-editorial-review-structure/workflow.md → core-skills/xiaoma-editorial-review-structure/SKILL.md} +5 -0
  14. package/src/core-skills/xiaoma-help/SKILL.md +75 -0
  15. package/src/{core/skills/xiaoma-index-docs/workflow.md → core-skills/xiaoma-index-docs/SKILL.md} +5 -0
  16. package/src/core-skills/xiaoma-party-mode/SKILL.md +128 -0
  17. package/src/{core/skills/xiaoma-review-adversarial-general/workflow.md → core-skills/xiaoma-review-adversarial-general/SKILL.md} +5 -0
  18. package/src/{core/skills/xiaoma-review-edge-case-hunter/workflow.md → core-skills/xiaoma-review-edge-case-hunter/SKILL.md} +5 -0
  19. package/src/{core/skills/xiaoma-shard-doc/workflow.md → core-skills/xiaoma-shard-doc/SKILL.md} +5 -0
  20. package/src/core-skills/xiaoma-spec/SKILL.md +129 -0
  21. package/src/core-skills/xiaoma-spec/assets/headless-schemas.md +33 -0
  22. package/src/core-skills/xiaoma-spec/assets/spec-template.md +49 -0
  23. package/src/core-skills/xiaoma-spec/customize.toml +53 -0
  24. package/src/scripts/resolve_config.js +163 -0
  25. package/src/scripts/resolve_customization.js +188 -0
  26. package/src/scripts/toml.js +338 -0
  27. package/src/xmc-skills/1-analysis/research/xiaoma-domain-research/SKILL.md +96 -0
  28. package/src/xmc-skills/1-analysis/research/xiaoma-domain-research/customize.toml +41 -0
  29. package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-06-research-synthesis.md +6 -0
  30. package/src/xmc-skills/1-analysis/research/xiaoma-market-research/SKILL.md +96 -0
  31. package/src/xmc-skills/1-analysis/research/xiaoma-market-research/customize.toml +41 -0
  32. package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-06-research-completion.md +6 -0
  33. package/src/xmc-skills/1-analysis/research/xiaoma-technical-research/SKILL.md +96 -0
  34. package/src/xmc-skills/1-analysis/research/xiaoma-technical-research/customize.toml +41 -0
  35. package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-06-research-synthesis.md +6 -0
  36. package/src/xmc-skills/1-analysis/xiaoma-agent-analyst/SKILL.md +76 -0
  37. package/src/xmc-skills/1-analysis/xiaoma-agent-analyst/customize.toml +90 -0
  38. package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/SKILL.md +76 -0
  39. package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/customize.toml +81 -0
  40. package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/explain-concept.md +20 -0
  41. package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/mermaid-gen.md +20 -0
  42. package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/validate-doc.md +19 -0
  43. package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/write-document.md +20 -0
  44. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/checklist.md +5 -2
  45. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-01-init-and-validate.md +18 -1
  46. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-02-requirements-analysis.md +3 -1
  47. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-03-architecture-analysis.md +5 -3
  48. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-04-create-prd.md +12 -14
  49. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-05-validate-prd.md +18 -15
  50. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-06-create-epics.md +9 -5
  51. package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/steps/step-07-create-architecture.md +10 -7
  52. package/src/xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline/steps/step-08-finalize.md +184 -0
  53. package/src/xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline/workflow.md +140 -0
  54. package/src/xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline/xiaoma-skill-manifest.yaml +24 -0
  55. package/src/xmc-skills/1-analysis/xiaoma-document-project/SKILL.md +62 -0
  56. package/src/xmc-skills/1-analysis/xiaoma-document-project/customize.toml +41 -0
  57. package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/deep-dive-instructions.md +1 -0
  58. package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/full-scan-instructions.md +1 -0
  59. package/src/xmc-skills/1-analysis/xiaoma-prfaq/SKILL.md +135 -0
  60. package/src/xmc-skills/1-analysis/xiaoma-prfaq/agents/artifact-analyzer.md +60 -0
  61. package/src/xmc-skills/1-analysis/xiaoma-prfaq/agents/web-researcher.md +49 -0
  62. package/src/xmc-skills/1-analysis/xiaoma-prfaq/assets/prfaq-template.md +62 -0
  63. package/src/xmc-skills/1-analysis/xiaoma-prfaq/customize.toml +41 -0
  64. package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/customer-faq.md +55 -0
  65. package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/internal-faq.md +51 -0
  66. package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/press-release.md +60 -0
  67. package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/verdict.md +83 -0
  68. package/src/xmc-skills/1-analysis/xiaoma-prfaq/xiaoma-manifest.json +16 -0
  69. package/src/xmc-skills/1-analysis/xiaoma-product-brief/SKILL.md +91 -0
  70. package/src/xmc-skills/1-analysis/xiaoma-product-brief/assets/brief-template.md +41 -0
  71. package/src/xmc-skills/1-analysis/xiaoma-product-brief/customize.toml +99 -0
  72. package/src/xmc-skills/2-plan-workflows/xiaoma-agent-pm/SKILL.md +76 -0
  73. package/src/xmc-skills/2-plan-workflows/xiaoma-agent-pm/customize.toml +75 -0
  74. package/src/xmc-skills/2-plan-workflows/xiaoma-agent-ux-designer/SKILL.md +76 -0
  75. package/src/xmc-skills/2-plan-workflows/xiaoma-agent-ux-designer/customize.toml +60 -0
  76. package/src/xmc-skills/2-plan-workflows/xiaoma-create-prd/SKILL.md +30 -0
  77. package/src/xmc-skills/2-plan-workflows/xiaoma-create-prd/customize.toml +41 -0
  78. package/src/xmc-skills/2-plan-workflows/xiaoma-edit-prd/SKILL.md +30 -0
  79. package/src/xmc-skills/2-plan-workflows/xiaoma-edit-prd/customize.toml +42 -0
  80. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/SKILL.md +92 -0
  81. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/headless-schemas.md +76 -0
  82. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/prd-template.md +165 -0
  83. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/prd-validation-checklist.md +217 -0
  84. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/validation-report-template.html +325 -0
  85. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/customize.toml +147 -0
  86. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/references/headless.md +39 -0
  87. package/src/xmc-skills/2-plan-workflows/xiaoma-prd/references/validate.md +97 -0
  88. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/SKILL.md +90 -0
  89. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/color-themes.md +9 -0
  90. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-directions.md +9 -0
  91. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-example-editorial.md +158 -0
  92. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-example-mobile.md +93 -0
  93. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-example-shadcn.md +109 -0
  94. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/excalidraw-wireframe.md +19 -0
  95. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/experience-example-mobile.md +112 -0
  96. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/experience-example-shadcn.md +133 -0
  97. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/headless-schemas.md +84 -0
  98. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/key-screens.md +29 -0
  99. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/validation-report-template.html +319 -0
  100. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/customize.toml +100 -0
  101. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/creative-tools.md +19 -0
  102. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/design-md-spec.md +50 -0
  103. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/headless.md +37 -0
  104. package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/validate.md +115 -0
  105. package/src/xmc-skills/2-plan-workflows/xiaoma-validate-prd/SKILL.md +30 -0
  106. package/src/xmc-skills/2-plan-workflows/xiaoma-validate-prd/customize.toml +31 -0
  107. package/src/xmc-skills/3-solutioning/xiaoma-agent-architect/SKILL.md +76 -0
  108. package/src/xmc-skills/3-solutioning/xiaoma-agent-architect/customize.toml +65 -0
  109. package/src/xmc-skills/3-solutioning/xiaoma-check-implementation-readiness/SKILL.md +91 -0
  110. package/src/xmc-skills/3-solutioning/xiaoma-check-implementation-readiness/customize.toml +41 -0
  111. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-01-document-discovery.md +1 -1
  112. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-02-prd-analysis.md +1 -1
  113. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +1 -1
  114. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-06-final-assessment.md +6 -0
  115. package/src/xmc-skills/3-solutioning/xiaoma-create-architecture/SKILL.md +74 -0
  116. package/src/xmc-skills/3-solutioning/xiaoma-create-architecture/customize.toml +41 -0
  117. package/src/xmc-skills/3-solutioning/xiaoma-create-architecture/references/headless.md +37 -0
  118. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-07-validation.md +23 -21
  119. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-08-complete.md +6 -0
  120. package/src/xmc-skills/3-solutioning/xiaoma-create-epics-and-stories/SKILL.md +93 -0
  121. package/src/xmc-skills/3-solutioning/xiaoma-create-epics-and-stories/customize.toml +41 -0
  122. package/src/xmc-skills/3-solutioning/xiaoma-create-epics-and-stories/references/headless.md +35 -0
  123. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-02-design-epics.md +34 -4
  124. package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-04-final-validation.md +12 -0
  125. package/src/xmc-skills/3-solutioning/xiaoma-generate-project-context/SKILL.md +81 -0
  126. package/src/xmc-skills/3-solutioning/xiaoma-generate-project-context/customize.toml +41 -0
  127. package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/steps/step-03-complete.md +6 -0
  128. package/src/xmc-skills/4-implementation/xiaoma-agent-dev/SKILL.md +76 -0
  129. package/src/xmc-skills/4-implementation/xiaoma-agent-dev/customize.toml +131 -0
  130. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/checklist.md +29 -0
  131. package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/steps/step-01-init-and-validate.md +16 -8
  132. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-02-create-story.md +111 -0
  133. package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/steps/step-03-validate-story.md +4 -2
  134. package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/steps/step-04-develop-story.md +10 -6
  135. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-05-code-review.md +99 -0
  136. package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/steps/step-06-test-story.md +25 -12
  137. package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/steps/step-07-fix-and-retest.md +28 -13
  138. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-08-complete-story.md +174 -0
  139. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-09-finalize.md +145 -0
  140. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/workflow.md +127 -0
  141. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/xiaoma-skill-manifest.yaml +27 -0
  142. package/src/{xmc/workflows/4-implementation/auto-story-pipeline-batch → xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch}/SKILL.md +2 -2
  143. package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch/checklist.md +45 -0
  144. package/src/{xmc/workflows/4-implementation/auto-story-pipeline-batch → xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch}/workflow.md +150 -7
  145. package/src/{xmc/workflows/4-implementation/auto-story-pipeline-batch → xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch}/xiaoma-skill-manifest.yaml +2 -2
  146. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/SKILL.md +68 -0
  147. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/customize.toml +41 -0
  148. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/generate-trail.md +38 -0
  149. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-01-orientation.md +105 -0
  150. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-02-walkthrough.md +89 -0
  151. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-03-detail-pass.md +106 -0
  152. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-04-testing.md +74 -0
  153. package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-05-wrapup.md +30 -0
  154. package/src/xmc-skills/4-implementation/xiaoma-code-review/SKILL.md +90 -0
  155. package/src/xmc-skills/4-implementation/xiaoma-code-review/customize.toml +41 -0
  156. package/src/xmc-skills/4-implementation/xiaoma-code-review/steps/step-01-gather-context.md +85 -0
  157. package/src/xmc-skills/4-implementation/xiaoma-code-review/steps/step-02-review.md +35 -0
  158. package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-code-review/steps/step-03-triage.md +7 -8
  159. package/src/xmc-skills/4-implementation/xiaoma-code-review/steps/step-04-present.md +132 -0
  160. package/src/{xmc/workflows/4-implementation/xiaoma-correct-course/workflow.md → xmc-skills/4-implementation/xiaoma-correct-course/SKILL.md} +65 -31
  161. package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-correct-course/checklist.md +2 -2
  162. package/src/xmc-skills/4-implementation/xiaoma-correct-course/customize.toml +41 -0
  163. package/src/{xmc/workflows/4-implementation/xiaoma-create-story/workflow.md → xmc-skills/4-implementation/xiaoma-create-story/SKILL.md} +60 -11
  164. package/src/xmc-skills/4-implementation/xiaoma-create-story/customize.toml +41 -0
  165. package/src/xmc-skills/4-implementation/xiaoma-create-story/references/headless.md +32 -0
  166. package/src/{xmc/workflows/4-implementation/xiaoma-dev-story/workflow.md → xmc-skills/4-implementation/xiaoma-dev-story/SKILL.md} +70 -20
  167. package/src/xmc-skills/4-implementation/xiaoma-dev-story/customize.toml +41 -0
  168. package/src/xmc-skills/4-implementation/xiaoma-investigate/SKILL.md +196 -0
  169. package/src/xmc-skills/4-implementation/xiaoma-investigate/customize.toml +62 -0
  170. package/src/xmc-skills/4-implementation/xiaoma-investigate/references/case-file-template.md +127 -0
  171. package/src/{xmc/workflows/xiaoma-qa-generate-e2e-tests/workflow.md → xmc-skills/4-implementation/xiaoma-qa-generate-e2e-tests/SKILL.md} +51 -23
  172. package/src/{xmc/workflows → xmc-skills/4-implementation}/xiaoma-qa-generate-e2e-tests/checklist.md +1 -5
  173. package/src/xmc-skills/4-implementation/xiaoma-qa-generate-e2e-tests/customize.toml +41 -0
  174. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/SKILL.md +111 -0
  175. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/compile-epic-context.md +62 -0
  176. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/customize.toml +41 -0
  177. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/spec-template.md +88 -0
  178. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-01-clarify-and-route.md +100 -0
  179. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-02-plan.md +47 -0
  180. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-03-implement.md +41 -0
  181. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-04-review.md +50 -0
  182. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-05-present.md +78 -0
  183. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-oneshot.md +71 -0
  184. package/src/xmc-skills/4-implementation/xiaoma-quick-dev/sync-sprint-status.md +19 -0
  185. package/src/{xmc/workflows/4-implementation/xiaoma-retrospective/workflow.md → xmc-skills/4-implementation/xiaoma-retrospective/SKILL.md} +185 -152
  186. package/src/xmc-skills/4-implementation/xiaoma-retrospective/customize.toml +41 -0
  187. package/src/{xmc/workflows/4-implementation/xiaoma-sprint-planning/workflow.md → xmc-skills/4-implementation/xiaoma-sprint-planning/SKILL.md} +59 -15
  188. package/src/xmc-skills/4-implementation/xiaoma-sprint-planning/customize.toml +41 -0
  189. package/src/xmc-skills/4-implementation/xiaoma-sprint-planning/references/headless.md +28 -0
  190. package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-sprint-planning/sprint-status-template.yaml +3 -3
  191. package/src/{xmc/workflows/4-implementation/xiaoma-sprint-status/workflow.md → xmc-skills/4-implementation/xiaoma-sprint-status/SKILL.md} +57 -20
  192. package/src/xmc-skills/4-implementation/xiaoma-sprint-status/customize.toml +41 -0
  193. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/checklist.md +6 -0
  194. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/steps/step-01-init-and-validate.md +28 -4
  195. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/steps/step-02-run-requirements-pipeline.md +2 -1
  196. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/steps/step-03-bridge-sprint-planning.md +63 -9
  197. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/steps/step-04-run-story-pipeline.md +2 -1
  198. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/steps/step-05-finalize.md +30 -3
  199. package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/workflow.md +7 -8
  200. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/checklist.md +4 -0
  201. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/steps/step-01-init-and-validate.md +8 -7
  202. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/steps/step-02-create-epics.md +3 -2
  203. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/steps/step-03-bridge-sprint-planning.md +68 -14
  204. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/steps/step-04-batch-create-stories.md +6 -5
  205. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/steps/step-05-finalize.md +48 -8
  206. package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/workflow.md +7 -8
  207. package/src/xmc-skills/module-help.csv +32 -0
  208. package/src/xmc-skills/module.yaml +95 -0
  209. package/src/xpm-skills/module-help.csv +3 -0
  210. package/src/xpm-skills/module.yaml +36 -0
  211. package/src/xpm-skills/xiaoma-agent-patent-advisor/SKILL.md +75 -0
  212. package/src/xpm-skills/xiaoma-agent-patent-advisor/customize.toml +46 -0
  213. package/src/xpm-skills/xiaoma-patent-mining/SKILL.md +6 -0
  214. package/src/xpm-skills/xiaoma-patent-mining/steps/step-01-project-analysis.md +65 -0
  215. package/src/xpm-skills/xiaoma-patent-mining/steps/step-02-patent-mining.md +87 -0
  216. package/src/xpm-skills/xiaoma-patent-mining/steps/step-03-disclosure-writing.md +110 -0
  217. package/src/xpm-skills/xiaoma-patent-mining/steps/step-04-ai-taste-removal.md +85 -0
  218. package/src/xpm-skills/xiaoma-patent-mining/steps/step-05-docx-generation.md +111 -0
  219. package/src/xpm-skills/xiaoma-patent-mining/workflow.md +94 -0
  220. package/tools/format-workflow-md.js +263 -0
  221. package/tools/{cli → installer}/README.md +2 -2
  222. package/tools/installer/cli-utils.js +57 -0
  223. package/tools/installer/commands/install.js +146 -0
  224. package/tools/{cli → installer}/commands/status.js +15 -7
  225. package/tools/{cli → installer}/commands/uninstall.js +7 -7
  226. package/tools/installer/core/config.js +73 -0
  227. package/tools/installer/core/existing-install.js +121 -0
  228. package/tools/installer/core/install-paths.js +132 -0
  229. package/tools/installer/core/installer.js +1624 -0
  230. package/tools/installer/core/legacy-warnings.js +156 -0
  231. package/tools/installer/core/manifest-generator.js +859 -0
  232. package/tools/installer/core/manifest.js +434 -0
  233. package/tools/{cli/lib → installer}/file-ops.js +1 -1
  234. package/tools/installer/fs-native.js +116 -0
  235. package/tools/installer/ide/_config-driven.js +972 -0
  236. package/tools/{cli/installers/lib → installer}/ide/manager.js +82 -62
  237. package/tools/installer/ide/platform-codes.js +80 -0
  238. package/tools/installer/ide/platform-codes.yaml +322 -0
  239. package/tools/installer/ide/shared/installed-skills.js +50 -0
  240. package/tools/{cli/installers/lib → installer}/ide/shared/path-utils.js +0 -145
  241. package/tools/{cli/installers/lib → installer}/ide/shared/skill-manifest.js +3 -36
  242. package/tools/installer/list-options.js +210 -0
  243. package/tools/{cli/installers/lib → installer}/message-loader.js +3 -3
  244. package/tools/installer/modules/channel-plan.js +203 -0
  245. package/tools/installer/modules/channel-resolver.js +241 -0
  246. package/tools/installer/modules/custom-module-manager.js +912 -0
  247. package/tools/installer/modules/external-manager.js +533 -0
  248. package/tools/installer/modules/module-help-schema.js +13 -0
  249. package/tools/{cli/installers/lib/core/config-collector.js → installer/modules/official-modules.js} +1052 -110
  250. package/tools/installer/modules/plugin-resolver.js +398 -0
  251. package/tools/installer/modules/version-resolver.js +336 -0
  252. package/tools/installer/project-root.js +230 -0
  253. package/tools/{cli/lib → installer}/prompts.js +143 -100
  254. package/tools/installer/set-overrides.js +330 -0
  255. package/tools/installer/ui.js +2078 -0
  256. package/tools/{cli → installer}/xiaoma-cli.js +9 -10
  257. package/tools/{cli/lib → installer}/yaml-format.js +1 -1
  258. package/tools/migrate-custom-module-paths.js +124 -0
  259. package/tools/schema/step.js +855 -0
  260. package/tools/skill-validator.md +323 -0
  261. package/tools/validate-file-refs.js +566 -0
  262. package/tools/validate-frontmatter-prose-routing.js +334 -0
  263. package/tools/validate-skills.js +702 -0
  264. package/tools/validate-step-schemas.js +401 -0
  265. package/tools/validate-svg-changes.sh +1 -1
  266. package/tools/validate-trigger-column-vs-emits.js +375 -0
  267. package/tools/validate-warnings-samples.js +261 -0
  268. package/tools/xiaoma/rebrand.mjs +0 -0
  269. package/tools/xiaoma-npx-wrapper.js +2 -2
  270. package/.playwright-cli/console-2026-05-13T06-36-26-793Z.log +0 -2
  271. package/.playwright-cli/page-2026-05-13T06-36-27-725Z.yml +0 -1
  272. package/CLAUDE.md +0 -111
  273. package/README.md +0 -128
  274. package/XiaoMa-CLI-2026H2-/350/277/255/344/273/243/350/247/204/345/210/222.pptx +0 -0
  275. package/demo/xiaoma-bug-circle-resolve/SKILL.md +0 -6
  276. package/demo/xiaoma-bug-circle-resolve/workflow.md +0 -254
  277. package/demo/xiaoma-bug-resolve/SKILL.md +0 -6
  278. package/demo/xiaoma-bug-resolve/workflow.md +0 -269
  279. package/demo/xiaoma-prd-saas-zh/README.md +0 -57
  280. package/demo/xiaoma-prd-saas-zh/domain-research.md +0 -128
  281. package/demo/xiaoma-prd-saas-zh/epics.md +0 -303
  282. package/demo/xiaoma-prd-saas-zh/market-research-2026-q1.md +0 -183
  283. package/demo/xiaoma-prd-saas-zh/prd-bad-examples.md +0 -268
  284. package/demo/xiaoma-prd-saas-zh/prd.md +0 -409
  285. package/demo/xiaoma-prd-saas-zh/product-brief.md +0 -97
  286. package/demo/xiaoma-prd-saas-zh/validation-report.md +0 -279
  287. package/docs/roadshow/01-/351/241/271/347/233/256/346/246/202/350/247/210/344/270/216/346/236/266/346/236/204.md +0 -189
  288. package/docs/roadshow/02-/346/231/272/350/203/275/344/275/223/347/263/273/347/273/237/350/257/246/350/247/243.md +0 -464
  289. package/docs/roadshow/03-/346/231/272/350/203/275/344/275/223/344/272/244/344/272/222/346/265/201/347/250/213/345/233/276.md +0 -334
  290. package/docs/roadshow/04-/345/267/245/344/275/234/346/265/201/346/211/247/350/241/214/350/257/246/350/247/243.md +0 -1038
  291. package/docs/roadshow/05-/346/212/200/346/234/257/345/256/236/347/216/260/344/270/216/345/210/233/346/226/260/344/272/256/347/202/271.md +0 -205
  292. package/docs/roadshow/06-/350/267/257/346/274/224/346/200/273/347/273/223/344/270/216/346/274/224/347/244/272/345/273/272/350/256/256.md +0 -167
  293. package/media/doc1_fig1.png +0 -0
  294. package/media/doc1_fig2.png +0 -0
  295. package/media/doc1_fig3.png +0 -0
  296. package/media/doc1_fig4.png +0 -0
  297. package/media/doc2_fig1.png +0 -0
  298. package/media/doc2_fig2.png +0 -0
  299. package/media/doc2_fig3.png +0 -0
  300. package/media/doc2_fig4.png +0 -0
  301. package/media/doc3_fig1.png +0 -0
  302. package/media/doc3_fig2.png +0 -0
  303. package/media/doc3_fig3.png +0 -0
  304. package/media/doc3_fig4.png +0 -0
  305. package/media/doc4_fig1.png +0 -0
  306. package/media/doc4_fig2.png +0 -0
  307. package/media/doc4_fig3.png +0 -0
  308. package/media/doc5_fig1.png +0 -0
  309. package/media/doc5_fig2.png +0 -0
  310. package/media/doc5_fig3.png +0 -0
  311. package/patent-disclosure-optimized/SKILL.md +0 -416
  312. package/src/core/module-help.csv +0 -11
  313. package/src/core/skills/xiaoma-advanced-elicitation/SKILL.md +0 -6
  314. package/src/core/skills/xiaoma-advanced-elicitation/methods.csv +0 -51
  315. package/src/core/skills/xiaoma-editorial-review-prose/SKILL.md +0 -6
  316. package/src/core/skills/xiaoma-editorial-review-structure/SKILL.md +0 -6
  317. package/src/core/skills/xiaoma-help/SKILL.md +0 -6
  318. package/src/core/skills/xiaoma-help/workflow.md +0 -88
  319. package/src/core/skills/xiaoma-help/xiaoma-skill-manifest.yaml +0 -1
  320. package/src/core/skills/xiaoma-index-docs/SKILL.md +0 -6
  321. package/src/core/skills/xiaoma-index-docs/xiaoma-skill-manifest.yaml +0 -1
  322. package/src/core/skills/xiaoma-party-mode/SKILL.md +0 -6
  323. package/src/core/skills/xiaoma-party-mode/steps/step-01-agent-loading.md +0 -138
  324. package/src/core/skills/xiaoma-party-mode/steps/step-02-discussion-orchestration.md +0 -187
  325. package/src/core/skills/xiaoma-party-mode/steps/step-03-graceful-exit.md +0 -167
  326. package/src/core/skills/xiaoma-party-mode/workflow.md +0 -190
  327. package/src/core/skills/xiaoma-party-mode/xiaoma-skill-manifest.yaml +0 -1
  328. package/src/core/skills/xiaoma-review-adversarial-general/SKILL.md +0 -6
  329. package/src/core/skills/xiaoma-review-adversarial-general/xiaoma-skill-manifest.yaml +0 -1
  330. package/src/core/skills/xiaoma-review-edge-case-hunter/SKILL.md +0 -6
  331. package/src/core/skills/xiaoma-review-edge-case-hunter/xiaoma-skill-manifest.yaml +0 -1
  332. package/src/core/skills/xiaoma-shard-doc/SKILL.md +0 -6
  333. package/src/core/skills/xiaoma-shard-doc/xiaoma-skill-manifest.yaml +0 -1
  334. package/src/core/tasks/xiaoma-create-prd/SKILL.md +0 -6
  335. package/src/core/tasks/xiaoma-create-prd/data/prd-purpose.md +0 -354
  336. package/src/core/tasks/xiaoma-create-prd/data/upstream-input-contract.md +0 -168
  337. package/src/core/tasks/xiaoma-create-prd/steps-c/step-01-init.md +0 -178
  338. package/src/core/tasks/xiaoma-create-prd/steps-c/step-01b-continue.md +0 -161
  339. package/src/core/tasks/xiaoma-create-prd/steps-c/step-02-discovery.md +0 -208
  340. package/src/core/tasks/xiaoma-create-prd/steps-c/step-02b-vision.md +0 -142
  341. package/src/core/tasks/xiaoma-create-prd/steps-c/step-02c-executive-summary.md +0 -158
  342. package/src/core/tasks/xiaoma-create-prd/steps-c/step-03-success.md +0 -214
  343. package/src/core/tasks/xiaoma-create-prd/steps-c/step-04-journeys.md +0 -201
  344. package/src/core/tasks/xiaoma-create-prd/steps-c/step-05-domain.md +0 -194
  345. package/src/core/tasks/xiaoma-create-prd/steps-c/step-06-innovation.md +0 -211
  346. package/src/core/tasks/xiaoma-create-prd/steps-c/step-07-project-type.md +0 -222
  347. package/src/core/tasks/xiaoma-create-prd/steps-c/step-08-scoping.md +0 -216
  348. package/src/core/tasks/xiaoma-create-prd/steps-c/step-09-functional.md +0 -219
  349. package/src/core/tasks/xiaoma-create-prd/steps-c/step-10-nonfunctional.md +0 -230
  350. package/src/core/tasks/xiaoma-create-prd/steps-c/step-11-polish.md +0 -221
  351. package/src/core/tasks/xiaoma-create-prd/steps-c/step-12-complete.md +0 -115
  352. package/src/core/tasks/xiaoma-create-prd/templates/prd-skeleton-reference.md +0 -428
  353. package/src/core/tasks/xiaoma-create-prd/templates/prd-template.md +0 -108
  354. package/src/core/tasks/xiaoma-create-prd/workflow.md +0 -62
  355. package/src/core/tasks/xiaoma-create-prd/xiaoma-skill-manifest.yaml +0 -1
  356. package/src/utility/agent-components/activation-rules.txt +0 -6
  357. package/src/utility/agent-components/activation-steps.txt +0 -14
  358. package/src/utility/agent-components/agent-command-header.md +0 -1
  359. package/src/utility/agent-components/agent.customize.template.yaml +0 -41
  360. package/src/utility/agent-components/handler-action.txt +0 -4
  361. package/src/utility/agent-components/handler-data.txt +0 -5
  362. package/src/utility/agent-components/handler-exec.txt +0 -6
  363. package/src/utility/agent-components/handler-multi.txt +0 -13
  364. package/src/utility/agent-components/handler-tmpl.txt +0 -5
  365. package/src/utility/agent-components/menu-handlers.txt +0 -6
  366. package/src/xmc/agents/analyst.agent.yaml +0 -47
  367. package/src/xmc/agents/architect.agent.yaml +0 -29
  368. package/src/xmc/agents/dev.agent.yaml +0 -38
  369. package/src/xmc/agents/pm.agent.yaml +0 -44
  370. package/src/xmc/agents/qa.agent.yaml +0 -58
  371. package/src/xmc/agents/quick-flow-solo-dev.agent.yaml +0 -36
  372. package/src/xmc/agents/sm.agent.yaml +0 -53
  373. package/src/xmc/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +0 -224
  374. package/src/xmc/agents/tech-writer/tech-writer.agent.yaml +0 -46
  375. package/src/xmc/agents/tech-writer/xiaoma-skill-manifest.yaml +0 -3
  376. package/src/xmc/agents/ux-designer.agent.yaml +0 -27
  377. package/src/xmc/agents/xiaoma-skill-manifest.yaml +0 -39
  378. package/src/xmc/data/project-context-template.md +0 -26
  379. package/src/xmc/module-help.csv +0 -32
  380. package/src/xmc/module.yaml +0 -50
  381. package/src/xmc/teams/default-party.csv +0 -20
  382. package/src/xmc/teams/team-fullstack.yaml +0 -12
  383. package/src/xmc/workflows/1-analysis/auto-requirements-pipeline/steps/step-08-finalize.md +0 -124
  384. package/src/xmc/workflows/1-analysis/auto-requirements-pipeline/workflow.md +0 -107
  385. package/src/xmc/workflows/1-analysis/auto-requirements-pipeline/xiaoma-skill-manifest.yaml +0 -3
  386. package/src/xmc/workflows/1-analysis/research/market-steps/step-01-init.md +0 -182
  387. package/src/xmc/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +0 -237
  388. package/src/xmc/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +0 -249
  389. package/src/xmc/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +0 -259
  390. package/src/xmc/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +0 -177
  391. package/src/xmc/workflows/1-analysis/research/market-steps/step-06-research-completion.md +0 -476
  392. package/src/xmc/workflows/1-analysis/research/xiaoma-domain-research/SKILL.md +0 -6
  393. package/src/xmc/workflows/1-analysis/research/xiaoma-domain-research/workflow.md +0 -49
  394. package/src/xmc/workflows/1-analysis/research/xiaoma-domain-research/xiaoma-skill-manifest.yaml +0 -1
  395. package/src/xmc/workflows/1-analysis/research/xiaoma-market-research/SKILL.md +0 -6
  396. package/src/xmc/workflows/1-analysis/research/xiaoma-market-research/workflow.md +0 -49
  397. package/src/xmc/workflows/1-analysis/research/xiaoma-market-research/xiaoma-skill-manifest.yaml +0 -1
  398. package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/SKILL.md +0 -6
  399. package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/research.template.md +0 -29
  400. package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/workflow.md +0 -50
  401. package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/xiaoma-skill-manifest.yaml +0 -1
  402. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/SKILL.md +0 -6
  403. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/product-brief.template.md +0 -10
  404. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-01-init.md +0 -170
  405. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-01b-continue.md +0 -158
  406. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-02-vision.md +0 -193
  407. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-03-users.md +0 -196
  408. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-04-metrics.md +0 -199
  409. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-05-scope.md +0 -213
  410. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-06-complete.md +0 -159
  411. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/workflow.md +0 -55
  412. package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/xiaoma-skill-manifest.yaml +0 -1
  413. package/src/xmc/workflows/1-analysis/xiaoma-product-brief-preview/xiaoma-skill-manifest.yaml +0 -1
  414. package/src/xmc/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +0 -15
  415. package/src/xmc/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +0 -197
  416. package/src/xmc/workflows/2-plan-workflows/create-prd/data/project-types.csv +0 -11
  417. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +0 -224
  418. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +0 -191
  419. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +0 -209
  420. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +0 -174
  421. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -214
  422. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +0 -228
  423. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +0 -217
  424. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -205
  425. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -243
  426. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +0 -263
  427. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +0 -209
  428. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -264
  429. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +0 -242
  430. package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +0 -232
  431. package/src/xmc/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +0 -65
  432. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/SKILL.md +0 -6
  433. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-01-init.md +0 -135
  434. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-01b-continue.md +0 -127
  435. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-02-discovery.md +0 -190
  436. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-03-core-experience.md +0 -217
  437. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-04-emotional-response.md +0 -220
  438. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-05-inspiration.md +0 -235
  439. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-06-design-system.md +0 -253
  440. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-07-defining-experience.md +0 -255
  441. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-08-visual-foundation.md +0 -225
  442. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-09-design-directions.md +0 -225
  443. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-10-user-journeys.md +0 -242
  444. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-11-component-strategy.md +0 -249
  445. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-12-ux-patterns.md +0 -238
  446. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-13-responsive-accessibility.md +0 -265
  447. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-14-complete.md +0 -171
  448. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/ux-design-template.md +0 -13
  449. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/workflow.md +0 -36
  450. package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/xiaoma-skill-manifest.yaml +0 -1
  451. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/SKILL.md +0 -6
  452. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-01-discovery.md +0 -242
  453. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-01b-legacy-conversion.md +0 -204
  454. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-02-review.md +0 -245
  455. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-03-edit.md +0 -250
  456. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-04-complete.md +0 -165
  457. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/workflow.md +0 -63
  458. package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/xiaoma-skill-manifest.yaml +0 -1
  459. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/SKILL.md +0 -6
  460. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/domain-complexity.csv +0 -15
  461. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/prd-purpose.md +0 -197
  462. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/prd-quality-rubric.csv +0 -14
  463. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/project-types.csv +0 -11
  464. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-01-discovery.md +0 -221
  465. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-02-format-detection.md +0 -188
  466. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-02b-parity-check.md +0 -206
  467. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-03-density-validation.md +0 -171
  468. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -211
  469. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-05-measurability-validation.md +0 -225
  470. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-06-traceability-validation.md +0 -214
  471. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -202
  472. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -240
  473. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-09-project-type-validation.md +0 -260
  474. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-10-smart-validation.md +0 -206
  475. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -261
  476. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-12-completeness-validation.md +0 -239
  477. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-13-report-complete.md +0 -229
  478. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/workflow.md +0 -62
  479. package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/xiaoma-skill-manifest.yaml +0 -1
  480. package/src/xmc/workflows/3-solutioning/xiaoma-check-implementation-readiness/SKILL.md +0 -6
  481. package/src/xmc/workflows/3-solutioning/xiaoma-check-implementation-readiness/workflow.md +0 -49
  482. package/src/xmc/workflows/3-solutioning/xiaoma-check-implementation-readiness/xiaoma-skill-manifest.yaml +0 -1
  483. package/src/xmc/workflows/3-solutioning/xiaoma-create-architecture/SKILL.md +0 -6
  484. package/src/xmc/workflows/3-solutioning/xiaoma-create-architecture/workflow.md +0 -38
  485. package/src/xmc/workflows/3-solutioning/xiaoma-create-architecture/xiaoma-skill-manifest.yaml +0 -1
  486. package/src/xmc/workflows/3-solutioning/xiaoma-create-epics-and-stories/SKILL.md +0 -6
  487. package/src/xmc/workflows/3-solutioning/xiaoma-create-epics-and-stories/workflow.md +0 -53
  488. package/src/xmc/workflows/3-solutioning/xiaoma-create-epics-and-stories/xiaoma-skill-manifest.yaml +0 -1
  489. package/src/xmc/workflows/4-implementation/auto-story-pipeline/checklist.md +0 -22
  490. package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-02-create-story.md +0 -102
  491. package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-05-code-review.md +0 -95
  492. package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-08-complete-story.md +0 -114
  493. package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-09-finalize.md +0 -69
  494. package/src/xmc/workflows/4-implementation/auto-story-pipeline/workflow.md +0 -89
  495. package/src/xmc/workflows/4-implementation/auto-story-pipeline/xiaoma-skill-manifest.yaml +0 -3
  496. package/src/xmc/workflows/4-implementation/xiaoma-code-review/SKILL.md +0 -6
  497. package/src/xmc/workflows/4-implementation/xiaoma-code-review/checklist.md +0 -23
  498. package/src/xmc/workflows/4-implementation/xiaoma-code-review/steps/step-01-gather-context.md +0 -61
  499. package/src/xmc/workflows/4-implementation/xiaoma-code-review/steps/step-02-review.md +0 -41
  500. package/src/xmc/workflows/4-implementation/xiaoma-code-review/steps/step-04-present.md +0 -38
  501. package/src/xmc/workflows/4-implementation/xiaoma-code-review/workflow.md +0 -54
  502. package/src/xmc/workflows/4-implementation/xiaoma-code-review/xiaoma-skill-manifest.yaml +0 -1
  503. package/src/xmc/workflows/4-implementation/xiaoma-correct-course/SKILL.md +0 -6
  504. package/src/xmc/workflows/4-implementation/xiaoma-correct-course/xiaoma-skill-manifest.yaml +0 -1
  505. package/src/xmc/workflows/4-implementation/xiaoma-create-story/SKILL.md +0 -6
  506. package/src/xmc/workflows/4-implementation/xiaoma-create-story/discover-inputs.md +0 -88
  507. package/src/xmc/workflows/4-implementation/xiaoma-create-story/xiaoma-skill-manifest.yaml +0 -1
  508. package/src/xmc/workflows/4-implementation/xiaoma-dev-story/SKILL.md +0 -6
  509. package/src/xmc/workflows/4-implementation/xiaoma-dev-story/xiaoma-skill-manifest.yaml +0 -1
  510. package/src/xmc/workflows/4-implementation/xiaoma-retrospective/SKILL.md +0 -6
  511. package/src/xmc/workflows/4-implementation/xiaoma-retrospective/xiaoma-skill-manifest.yaml +0 -1
  512. package/src/xmc/workflows/4-implementation/xiaoma-sprint-planning/SKILL.md +0 -6
  513. package/src/xmc/workflows/4-implementation/xiaoma-sprint-planning/xiaoma-skill-manifest.yaml +0 -1
  514. package/src/xmc/workflows/4-implementation/xiaoma-sprint-status/SKILL.md +0 -6
  515. package/src/xmc/workflows/4-implementation/xiaoma-sprint-status/xiaoma-skill-manifest.yaml +0 -1
  516. package/src/xmc/workflows/xiaoma-document-project/SKILL.md +0 -6
  517. package/src/xmc/workflows/xiaoma-document-project/workflow.md +0 -27
  518. package/src/xmc/workflows/xiaoma-document-project/xiaoma-skill-manifest.yaml +0 -1
  519. package/src/xmc/workflows/xiaoma-generate-project-context/SKILL.md +0 -6
  520. package/src/xmc/workflows/xiaoma-generate-project-context/workflow.md +0 -43
  521. package/src/xmc/workflows/xiaoma-generate-project-context/xiaoma-skill-manifest.yaml +0 -1
  522. package/src/xmc/workflows/xiaoma-qa-generate-e2e-tests/SKILL.md +0 -6
  523. package/src/xmc/workflows/xiaoma-qa-generate-e2e-tests/xiaoma-skill-manifest.yaml +0 -1
  524. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/SKILL.md +0 -6
  525. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-01-mode-detection.md +0 -169
  526. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-02-context-gathering.md +0 -114
  527. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-03-execute.md +0 -107
  528. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-04-self-check.md +0 -107
  529. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-05-adversarial-review.md +0 -94
  530. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-06-resolve-findings.md +0 -144
  531. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/workflow.md +0 -38
  532. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/xiaoma-skill-manifest.yaml +0 -1
  533. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev-new-preview/xiaoma-skill-manifest.yaml +0 -1
  534. package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-spec/xiaoma-skill-manifest.yaml +0 -1
  535. package/tools/cli/commands/install.js +0 -87
  536. package/tools/cli/external-official-modules.yaml +0 -4
  537. package/tools/cli/installers/lib/core/custom-module-cache.js +0 -260
  538. package/tools/cli/installers/lib/core/dependency-resolver.js +0 -743
  539. package/tools/cli/installers/lib/core/detector.js +0 -223
  540. package/tools/cli/installers/lib/core/ide-config-manager.js +0 -157
  541. package/tools/cli/installers/lib/core/installer.js +0 -3212
  542. package/tools/cli/installers/lib/core/manifest-generator.js +0 -1374
  543. package/tools/cli/installers/lib/core/manifest.js +0 -1040
  544. package/tools/cli/installers/lib/custom/handler.js +0 -358
  545. package/tools/cli/installers/lib/ide/_base-ide.js +0 -673
  546. package/tools/cli/installers/lib/ide/_config-driven.js +0 -1058
  547. package/tools/cli/installers/lib/ide/platform-codes.js +0 -100
  548. package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -321
  549. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +0 -181
  550. package/tools/cli/installers/lib/ide/shared/module-injections.js +0 -136
  551. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +0 -368
  552. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +0 -179
  553. package/tools/cli/installers/lib/ide/shared/xiaoma-artifacts.js +0 -181
  554. package/tools/cli/installers/lib/ide/templates/agent-command-template.md +0 -14
  555. package/tools/cli/installers/lib/ide/templates/combined/antigravity.md +0 -8
  556. package/tools/cli/installers/lib/ide/templates/combined/default-agent.md +0 -15
  557. package/tools/cli/installers/lib/ide/templates/combined/default-task.md +0 -10
  558. package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +0 -10
  559. package/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +0 -6
  560. package/tools/cli/installers/lib/ide/templates/combined/gemini-agent.toml +0 -14
  561. package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +0 -11
  562. package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +0 -11
  563. package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow-yaml.toml +0 -16
  564. package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow.toml +0 -14
  565. package/tools/cli/installers/lib/ide/templates/combined/kiro-agent.md +0 -16
  566. package/tools/cli/installers/lib/ide/templates/combined/kiro-task.md +0 -9
  567. package/tools/cli/installers/lib/ide/templates/combined/kiro-tool.md +0 -9
  568. package/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md +0 -7
  569. package/tools/cli/installers/lib/ide/templates/combined/opencode-agent.md +0 -15
  570. package/tools/cli/installers/lib/ide/templates/combined/opencode-task.md +0 -13
  571. package/tools/cli/installers/lib/ide/templates/combined/opencode-tool.md +0 -13
  572. package/tools/cli/installers/lib/ide/templates/combined/opencode-workflow-yaml.md +0 -16
  573. package/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md +0 -16
  574. package/tools/cli/installers/lib/ide/templates/combined/rovodev.md +0 -9
  575. package/tools/cli/installers/lib/ide/templates/combined/trae.md +0 -9
  576. package/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +0 -10
  577. package/tools/cli/installers/lib/ide/templates/split/.gitkeep +0 -0
  578. package/tools/cli/installers/lib/modules/external-manager.js +0 -136
  579. package/tools/cli/installers/lib/modules/manager.js +0 -1382
  580. package/tools/cli/lib/activation-builder.js +0 -165
  581. package/tools/cli/lib/agent/compiler.js +0 -516
  582. package/tools/cli/lib/agent/installer.js +0 -680
  583. package/tools/cli/lib/agent/template-engine.js +0 -152
  584. package/tools/cli/lib/agent-analyzer.js +0 -97
  585. package/tools/cli/lib/agent-party-generator.js +0 -194
  586. package/tools/cli/lib/cli-utils.js +0 -182
  587. package/tools/cli/lib/config.js +0 -213
  588. package/tools/cli/lib/platform-codes.js +0 -116
  589. package/tools/cli/lib/project-root.js +0 -77
  590. package/tools/cli/lib/ui.js +0 -1960
  591. package/tools/cli/lib/xml-handler.js +0 -177
  592. package/tools/cli/lib/xml-to-markdown.js +0 -82
  593. package/tools/cli/lib/yaml-xml-builder.js +0 -570
  594. package/tools/platform-codes.yaml +0 -157
  595. package/tools/schema/agent.js +0 -489
  596. package//344/270/223/345/210/251/344/272/244/345/272/225/344/271/246_1_/351/235/242/345/220/221AI/346/231/272/350/203/275/344/275/223/347/232/204/345/244/232/351/200/232/351/201/223/344/276/235/350/265/226_20260318.md +0 -483
  597. package//344/270/223/345/210/251/344/272/244/345/272/225/344/271/246_2_/345/237/272/344/272/216/351/205/215/347/275/256/351/251/261/345/212/250/347/232/204/350/267/250/345/271/263/345/217/260IDE/346/231/272/350/203/275_20260318.md +0 -592
  598. package//344/270/223/345/210/251/344/272/244/345/272/225/344/271/246_3_AI/346/231/272/350/203/275/344/275/223/345/243/260/346/230/216/345/274/217/345/256/232/344/271/211/347/232/204/347/274/226/350/257/221/346/265/201/346/260/264_20260318.md +0 -624
  599. package//344/270/223/345/210/251/344/272/244/345/272/225/344/271/246_4_/345/237/272/344/272/216/345/223/210/345/270/214/346/214/207/347/272/271/347/232/204/346/231/272/350/203/275/344/275/223/351/231/204/345/261/236/350/265/204/346/272/220/351/200/211_20260318.md +0 -628
  600. package//344/270/223/345/210/251/344/272/244/345/272/225/344/271/246_5_AI/346/231/272/350/203/275/344/275/223/350/247/246/345/217/221/346/214/207/344/273/244/347/232/204/345/244/215/345/220/210/346/240/274/345/274/217/346/240/241_20260318.md +0 -652
  601. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/SKILL.md +0 -0
  602. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/brain-methods.csv +0 -0
  603. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-01-session-setup.md +0 -0
  604. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-01b-continue.md +0 -0
  605. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02a-user-selected.md +0 -0
  606. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02b-ai-recommended.md +0 -0
  607. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02c-random-selection.md +0 -0
  608. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02d-progressive-flow.md +0 -0
  609. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-04-idea-organization.md +0 -0
  610. /package/src/{core/skills → core-skills}/xiaoma-brainstorming/template.md +0 -0
  611. /package/src/{core/skills → core-skills}/xiaoma-distillator/SKILL.md +0 -0
  612. /package/src/{core/skills → core-skills}/xiaoma-distillator/agents/distillate-compressor.md +0 -0
  613. /package/src/{core/skills → core-skills}/xiaoma-distillator/agents/round-trip-reconstructor.md +0 -0
  614. /package/src/{core/skills → core-skills}/xiaoma-distillator/resources/compression-rules.md +0 -0
  615. /package/src/{core/skills → core-skills}/xiaoma-distillator/resources/splitting-strategy.md +0 -0
  616. /package/src/{core/skills → core-skills}/xiaoma-distillator/scripts/tests/test_analyze_sources.py +0 -0
  617. /package/src/{core/skills → core-skills}/xiaoma-distillator/xiaoma-skill-manifest.yaml +0 -0
  618. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-01-init.md +0 -0
  619. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-02-domain-analysis.md +0 -0
  620. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-03-competitive-landscape.md +0 -0
  621. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-04-regulatory-focus.md +0 -0
  622. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-05-technical-trends.md +0 -0
  623. /package/src/{xmc/workflows/1-analysis/research → xmc-skills/1-analysis/research/xiaoma-domain-research}/research.template.md +0 -0
  624. /package/src/{xmc/workflows/1-analysis/research/xiaoma-domain-research → xmc-skills/1-analysis/research/xiaoma-market-research}/research.template.md +0 -0
  625. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-01-init.md +0 -0
  626. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-02-customer-behavior.md +0 -0
  627. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-03-customer-pain-points.md +0 -0
  628. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-04-customer-decisions.md +0 -0
  629. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-05-competitive-analysis.md +0 -0
  630. /package/src/{xmc/workflows/1-analysis/research/xiaoma-market-research → xmc-skills/1-analysis/research/xiaoma-technical-research}/research.template.md +0 -0
  631. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-01-init.md +0 -0
  632. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-02-technical-overview.md +0 -0
  633. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-03-integration-patterns.md +0 -0
  634. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-04-architectural-patterns.md +0 -0
  635. /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-05-implementation-research.md +0 -0
  636. /package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/SKILL.md +0 -0
  637. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/checklist.md +0 -0
  638. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/documentation-requirements.csv +0 -0
  639. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/instructions.md +0 -0
  640. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/deep-dive-template.md +0 -0
  641. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/index-template.md +0 -0
  642. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/project-overview-template.md +0 -0
  643. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/project-scan-report-schema.json +0 -0
  644. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/source-tree-template.md +0 -0
  645. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/deep-dive-workflow.md +0 -0
  646. /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/full-scan-workflow.md +0 -0
  647. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/SKILL.md +0 -0
  648. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/artifact-analyzer.md +0 -0
  649. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/opportunity-reviewer.md +0 -0
  650. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/skeptic-reviewer.md +0 -0
  651. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/web-researcher.md +0 -0
  652. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/contextual-discovery.md +0 -0
  653. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/draft-and-review.md +0 -0
  654. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/finalize.md +0 -0
  655. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/guided-elicitation.md +0 -0
  656. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/resources/brief-template.md +0 -0
  657. /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/xiaoma-manifest.json +0 -0
  658. /package/src/{core/skills/xiaoma-advanced-elicitation → xmc-skills/1-analysis/xiaoma-product-brief-preview}/xiaoma-skill-manifest.yaml +0 -0
  659. /package/src/{core/tasks/xiaoma-create-prd/data → xmc-skills/2-plan-workflows/xiaoma-prd/assets}/domain-complexity.csv +0 -0
  660. /package/src/{core/tasks/xiaoma-create-prd/data → xmc-skills/2-plan-workflows/xiaoma-prd/assets}/project-types.csv +0 -0
  661. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-04-ux-alignment.md +0 -0
  662. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-05-epic-quality-review.md +0 -0
  663. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/templates/readiness-report-template.md +0 -0
  664. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/architecture-decision-template.md +0 -0
  665. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/data/domain-complexity.csv +0 -0
  666. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/data/project-types.csv +0 -0
  667. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-01-init.md +0 -0
  668. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-01b-continue.md +0 -0
  669. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-02-context.md +0 -0
  670. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-03-starter.md +0 -0
  671. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-04-decisions.md +0 -0
  672. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-05-patterns.md +0 -0
  673. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-06-structure.md +0 -0
  674. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-01-validate-prerequisites.md +0 -0
  675. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-03-create-stories.md +0 -0
  676. /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/templates/epics-template.md +0 -0
  677. /package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/project-context-template.md +0 -0
  678. /package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/steps/step-01-discover.md +0 -0
  679. /package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/steps/step-02-generate.md +0 -0
  680. /package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/SKILL.md +0 -0
  681. /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-create-story/checklist.md +0 -0
  682. /package/src/{xmc/workflows/4-implementation/xiaoma-code-review → xmc-skills/4-implementation/xiaoma-create-story}/discover-inputs.md +0 -0
  683. /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-create-story/template.md +0 -0
  684. /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-dev-story/checklist.md +0 -0
  685. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/SKILL.md +0 -0
  686. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-01-clarify-and-route.md +0 -0
  687. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-02-plan.md +0 -0
  688. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-03-implement.md +0 -0
  689. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-04-review.md +0 -0
  690. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-05-present.md +0 -0
  691. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/tech-spec-template.md +0 -0
  692. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/workflow.md +0 -0
  693. /package/src/{core/skills/xiaoma-brainstorming → xmc-skills/4-implementation/xiaoma-quick-dev-new-preview}/xiaoma-skill-manifest.yaml +0 -0
  694. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/SKILL.md +0 -0
  695. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-01-understand.md +0 -0
  696. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-02-investigate.md +0 -0
  697. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-03-generate.md +0 -0
  698. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-04-review.md +0 -0
  699. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/tech-spec-template.md +0 -0
  700. /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/workflow.md +0 -0
  701. /package/src/{core/skills/xiaoma-editorial-review-prose → xmc-skills/4-implementation/xiaoma-quick-spec}/xiaoma-skill-manifest.yaml +0 -0
  702. /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-sprint-planning/checklist.md +0 -0
  703. /package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/SKILL.md +0 -0
  704. /package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/xiaoma-skill-manifest.yaml +0 -0
  705. /package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/SKILL.md +0 -0
  706. /package/src/{xmc/workflows/5-full-pipeline/auto-prd-to-stories → xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories}/xiaoma-skill-manifest.yaml +0 -0
  707. /package/{patent-disclosure-optimized → src/xpm-skills/xiaoma-patent-mining}/references/disclosure-template.md +0 -0
  708. /package/{patent-disclosure-optimized → src/xpm-skills/xiaoma-patent-mining}/references/docx-format-spec.md +0 -0
  709. /package/{patent-disclosure-optimized → src/xpm-skills/xiaoma-patent-mining}/references/mining-principles.md +0 -0
  710. /package/{patent-disclosure-optimized → src/xpm-skills/xiaoma-patent-mining}/scripts/md2docx.js +0 -0
  711. /package/src/{core/skills/xiaoma-editorial-review-structure → xpm-skills/xiaoma-patent-mining}/xiaoma-skill-manifest.yaml +0 -0
  712. /package/tools/{cli/installers → installer}/install-messages.yaml +0 -0
@@ -1,3212 +0,0 @@
1
- const path = require('node:path');
2
- const fs = require('fs-extra');
3
- const { Detector } = require('./detector');
4
- const { Manifest } = require('./manifest');
5
- const { ModuleManager } = require('../modules/manager');
6
- const { IdeManager } = require('../ide/manager');
7
- const { FileOps } = require('../../../lib/file-ops');
8
- const { Config } = require('../../../lib/config');
9
- const { XmlHandler } = require('../../../lib/xml-handler');
10
- const { DependencyResolver } = require('./dependency-resolver');
11
- const { ConfigCollector } = require('./config-collector');
12
- const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
13
- const { CLIUtils } = require('../../../lib/cli-utils');
14
- const { ManifestGenerator } = require('./manifest-generator');
15
- const { IdeConfigManager } = require('./ide-config-manager');
16
- const { CustomHandler } = require('../custom/handler');
17
- const prompts = require('../../../lib/prompts');
18
- const { XiaoMa_FOLDER_NAME } = require('../ide/shared/path-utils');
19
-
20
- class Installer {
21
- constructor() {
22
- this.detector = new Detector();
23
- this.manifest = new Manifest();
24
- this.moduleManager = new ModuleManager();
25
- this.ideManager = new IdeManager();
26
- this.fileOps = new FileOps();
27
- this.config = new Config();
28
- this.xmlHandler = new XmlHandler();
29
- this.dependencyResolver = new DependencyResolver();
30
- this.configCollector = new ConfigCollector();
31
- this.ideConfigManager = new IdeConfigManager();
32
- this.installedFiles = new Set(); // Track all installed files
33
- this.xiaomaFolderName = XiaoMa_FOLDER_NAME;
34
- }
35
-
36
- /**
37
- * Find the xiaoma installation directory in a project
38
- * Always uses the standard _xiaoma folder name
39
- * Also checks for legacy _cfg folder for migration
40
- * @param {string} projectDir - Project directory
41
- * @returns {Promise<Object>} { xiaomaDir: string, hasLegacyCfg: boolean }
42
- */
43
- async findXiaomaDir(projectDir) {
44
- const xiaomaDir = path.join(projectDir, XiaoMa_FOLDER_NAME);
45
-
46
- // Check if project directory exists
47
- if (!(await fs.pathExists(projectDir))) {
48
- // Project doesn't exist yet, return default
49
- return { xiaomaDir, hasLegacyCfg: false };
50
- }
51
-
52
- // Check for legacy _cfg folder if xiaoma directory exists
53
- let hasLegacyCfg = false;
54
- if (await fs.pathExists(xiaomaDir)) {
55
- const legacyCfgPath = path.join(xiaomaDir, '_cfg');
56
- if (await fs.pathExists(legacyCfgPath)) {
57
- hasLegacyCfg = true;
58
- }
59
- }
60
-
61
- return { xiaomaDir, hasLegacyCfg };
62
- }
63
-
64
- /**
65
- * @function copyFileWithPlaceholderReplacement
66
- * @intent Copy files from XiaoMa source to installation directory with dynamic content transformation
67
- * @why Enables installation-time customization: _xiaoma replacement
68
- * @param {string} sourcePath - Absolute path to source file in XiaoMa repository
69
- * @param {string} targetPath - Absolute path to destination file in user's project
70
- * @param {string} xiaomaFolderName - User's chosen xiaoma folder name (default: 'xiaoma')
71
- * @returns {Promise<void>} Resolves when file copy and transformation complete
72
- * @sideeffects Writes transformed file to targetPath, creates parent directories if needed
73
- * @edgecases Binary files bypass transformation, falls back to raw copy if UTF-8 read fails
74
- * @calledby installCore(), installModule(), IDE installers during file vendoring
75
- * @calls fs.readFile(), fs.writeFile(), fs.copy()
76
- *
77
-
78
- *
79
- * 3. Document marker in instructions.md (if applicable)
80
- */
81
- async copyFileWithPlaceholderReplacement(sourcePath, targetPath) {
82
- // List of text file extensions that should have placeholder replacement
83
- const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv', '.xml'];
84
- const ext = path.extname(sourcePath).toLowerCase();
85
-
86
- // Check if this is a text file that might contain placeholders
87
- if (textExtensions.includes(ext)) {
88
- try {
89
- // Read the file content
90
- let content = await fs.readFile(sourcePath, 'utf8');
91
-
92
- // Write to target with replaced content
93
- await fs.ensureDir(path.dirname(targetPath));
94
- await fs.writeFile(targetPath, content, 'utf8');
95
- } catch {
96
- // If reading as text fails (might be binary despite extension), fall back to regular copy
97
- await fs.copy(sourcePath, targetPath, { overwrite: true });
98
- }
99
- } else {
100
- // Binary file or other file type - just copy directly
101
- await fs.copy(sourcePath, targetPath, { overwrite: true });
102
- }
103
- }
104
-
105
- /**
106
- * Collect Tool/IDE configurations after module configuration
107
- * @param {string} projectDir - Project directory
108
- * @param {Array} selectedModules - Selected modules from configuration
109
- * @param {boolean} isFullReinstall - Whether this is a full reinstall
110
- * @param {Array} previousIdes - Previously configured IDEs (for reinstalls)
111
- * @param {Array} preSelectedIdes - Pre-selected IDEs from early prompt (optional)
112
- * @param {boolean} skipPrompts - Skip prompts and use defaults (for --yes flag)
113
- * @returns {Object} Tool/IDE selection and configurations
114
- */
115
- async collectToolConfigurations(
116
- projectDir,
117
- selectedModules,
118
- isFullReinstall = false,
119
- previousIdes = [],
120
- preSelectedIdes = null,
121
- skipPrompts = false,
122
- ) {
123
- // Use pre-selected IDEs if provided, otherwise prompt
124
- let toolConfig;
125
- if (preSelectedIdes === null) {
126
- // Fallback: prompt for tool selection (backwards compatibility)
127
- const { UI } = require('../../../lib/ui');
128
- const ui = new UI();
129
- toolConfig = await ui.promptToolSelection(projectDir);
130
- } else {
131
- // IDEs were already selected during initial prompts
132
- toolConfig = {
133
- ides: preSelectedIdes,
134
- skipIde: !preSelectedIdes || preSelectedIdes.length === 0,
135
- };
136
- }
137
-
138
- // Check for already configured IDEs
139
- const { Detector } = require('./detector');
140
- const detector = new Detector();
141
- const xiaomaDir = path.join(projectDir, XiaoMa_FOLDER_NAME);
142
-
143
- // During full reinstall, use the saved previous IDEs since xiaoma dir was deleted
144
- // Otherwise detect from existing installation
145
- let previouslyConfiguredIdes;
146
- if (isFullReinstall) {
147
- // During reinstall, treat all IDEs as new (need configuration)
148
- previouslyConfiguredIdes = [];
149
- } else {
150
- const existingInstall = await detector.detect(xiaomaDir);
151
- previouslyConfiguredIdes = existingInstall.ides || [];
152
- }
153
-
154
- // Load saved IDE configurations for already-configured IDEs
155
- const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(xiaomaDir);
156
-
157
- // Collect IDE-specific configurations if any were selected
158
- const ideConfigurations = {};
159
-
160
- // First, add saved configs for already-configured IDEs
161
- for (const ide of toolConfig.ides || []) {
162
- if (previouslyConfiguredIdes.includes(ide) && savedIdeConfigs[ide]) {
163
- ideConfigurations[ide] = savedIdeConfigs[ide];
164
- }
165
- }
166
-
167
- if (!toolConfig.skipIde && toolConfig.ides && toolConfig.ides.length > 0) {
168
- // Ensure IDE manager is initialized
169
- await this.ideManager.ensureInitialized();
170
-
171
- // Determine which IDEs are newly selected (not previously configured)
172
- const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
173
-
174
- if (newlySelectedIdes.length > 0) {
175
- // Collect configuration for IDEs that support it
176
- for (const ide of newlySelectedIdes) {
177
- try {
178
- const handler = this.ideManager.handlers.get(ide);
179
-
180
- if (!handler) {
181
- await prompts.log.warn(`Warning: IDE '${ide}' handler not found`);
182
- continue;
183
- }
184
-
185
- // Check if this IDE handler has a collectConfiguration method
186
- // (custom installers like Codex, Kilo may have this)
187
- if (typeof handler.collectConfiguration === 'function') {
188
- await prompts.log.info(`Configuring ${ide}...`);
189
- ideConfigurations[ide] = await handler.collectConfiguration({
190
- selectedModules: selectedModules || [],
191
- projectDir,
192
- xiaomaDir,
193
- skipPrompts,
194
- });
195
- } else {
196
- // Config-driven IDEs don't need configuration - mark as ready
197
- ideConfigurations[ide] = { _noConfigNeeded: true };
198
- }
199
- } catch (error) {
200
- // IDE doesn't support configuration or has an error
201
- await prompts.log.warn(`Warning: Could not load configuration for ${ide}: ${error.message}`);
202
- }
203
- }
204
- }
205
-
206
- // Log which IDEs are already configured and being kept
207
- const keptIdes = toolConfig.ides.filter((ide) => previouslyConfiguredIdes.includes(ide));
208
- if (keptIdes.length > 0) {
209
- await prompts.log.message(`Keeping existing configuration for: ${keptIdes.join(', ')}`);
210
- }
211
- }
212
-
213
- return {
214
- ides: toolConfig.ides,
215
- skipIde: toolConfig.skipIde,
216
- configurations: ideConfigurations,
217
- };
218
- }
219
-
220
- /**
221
- * Main installation method
222
- * @param {Object} config - Installation configuration
223
- * @param {string} config.directory - Target directory
224
- * @param {boolean} config.installCore - Whether to install core
225
- * @param {string[]} config.modules - Modules to install
226
- * @param {string[]} config.ides - IDEs to configure
227
- * @param {boolean} config.skipIde - Skip IDE configuration
228
- */
229
- async install(originalConfig) {
230
- // Clone config to avoid mutating the caller's object
231
- const config = { ...originalConfig };
232
-
233
- // Check if core config was already collected in UI
234
- const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
235
-
236
- // Only display logo if core config wasn't already collected (meaning we're not continuing from UI)
237
- if (!hasCoreConfig) {
238
- // Display XiaoMa logo
239
- await CLIUtils.displayLogo();
240
-
241
- // Display welcome message
242
- await CLIUtils.displaySection('XiaoMa™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
243
- }
244
-
245
- // Note: Legacy V4 detection now happens earlier in UI.promptInstall()
246
- // before any config collection, so we don't need to check again here
247
-
248
- const projectDir = path.resolve(config.directory);
249
- const xiaomaDir = path.join(projectDir, XiaoMa_FOLDER_NAME);
250
-
251
- // If core config was pre-collected (from interactive mode), use it
252
- if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
253
- this.configCollector.collectedConfig.core = config.coreConfig;
254
- // Also store in allAnswers for cross-referencing
255
- this.configCollector.allAnswers = {};
256
- for (const [key, value] of Object.entries(config.coreConfig)) {
257
- this.configCollector.allAnswers[`core_${key}`] = value;
258
- }
259
- }
260
-
261
- // Collect configurations for modules (skip if quick update already collected them)
262
- let moduleConfigs;
263
- let customModulePaths = new Map();
264
-
265
- if (config._quickUpdate) {
266
- // Quick update already collected all configs, use them directly
267
- moduleConfigs = this.configCollector.collectedConfig;
268
-
269
- // For quick update, populate customModulePaths from _customModuleSources
270
- if (config._customModuleSources) {
271
- for (const [moduleId, customInfo] of config._customModuleSources) {
272
- customModulePaths.set(moduleId, customInfo.sourcePath);
273
- }
274
- }
275
- } else {
276
- // For regular updates (modify flow), check manifest for custom module sources
277
- if (config._isUpdate && config._existingInstall && config._existingInstall.customModules) {
278
- for (const customModule of config._existingInstall.customModules) {
279
- // Ensure we have an absolute sourcePath
280
- let absoluteSourcePath = customModule.sourcePath;
281
-
282
- // Check if sourcePath is a cache-relative path (starts with _config)
283
- if (absoluteSourcePath && absoluteSourcePath.startsWith('_config')) {
284
- // Convert cache-relative path to absolute path
285
- absoluteSourcePath = path.join(xiaomaDir, absoluteSourcePath);
286
- }
287
- // If no sourcePath but we have relativePath, convert it
288
- else if (!absoluteSourcePath && customModule.relativePath) {
289
- // relativePath is relative to the project root (parent of xiaoma dir)
290
- absoluteSourcePath = path.resolve(projectDir, customModule.relativePath);
291
- }
292
- // Ensure sourcePath is absolute for anything else
293
- else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
294
- absoluteSourcePath = path.resolve(absoluteSourcePath);
295
- }
296
-
297
- if (absoluteSourcePath) {
298
- customModulePaths.set(customModule.id, absoluteSourcePath);
299
- }
300
- }
301
- }
302
-
303
- // Build custom module paths map from customContent
304
-
305
- // Handle selectedFiles (from existing install path or manual directory input)
306
- if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
307
- const customHandler = new CustomHandler();
308
- for (const customFile of config.customContent.selectedFiles) {
309
- const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory));
310
- if (customInfo && customInfo.id) {
311
- customModulePaths.set(customInfo.id, customInfo.path);
312
- }
313
- }
314
- }
315
-
316
- // Handle new custom content sources from UI
317
- if (config.customContent && config.customContent.sources) {
318
- for (const source of config.customContent.sources) {
319
- customModulePaths.set(source.id, source.path);
320
- }
321
- }
322
-
323
- // Handle cachedModules (from new install path where modules are cached)
324
- // Only include modules that were actually selected for installation
325
- if (config.customContent && config.customContent.cachedModules) {
326
- // Get selected cached module IDs (if available)
327
- const selectedCachedIds = config.customContent.selectedCachedModules || [];
328
- // If no selection info, include all cached modules (for backward compatibility)
329
- const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected;
330
-
331
- for (const cachedModule of config.customContent.cachedModules) {
332
- // For cached modules, the path is the cachePath which contains the module.yaml
333
- if (
334
- cachedModule.id &&
335
- cachedModule.cachePath && // Include if selected or if we should include all
336
- (shouldIncludeAll || selectedCachedIds.includes(cachedModule.id))
337
- ) {
338
- customModulePaths.set(cachedModule.id, cachedModule.cachePath);
339
- }
340
- }
341
- }
342
-
343
- // Get list of all modules including custom modules
344
- // Order: core first, then official modules, then custom modules
345
- const allModulesForConfig = ['core'];
346
-
347
- // Add official modules (excluding core and any custom modules)
348
- const officialModules = (config.modules || []).filter((m) => m !== 'core' && !customModulePaths.has(m));
349
- allModulesForConfig.push(...officialModules);
350
-
351
- // Add custom modules at the end
352
- for (const [moduleId] of customModulePaths) {
353
- if (!allModulesForConfig.includes(moduleId)) {
354
- allModulesForConfig.push(moduleId);
355
- }
356
- }
357
-
358
- // Check if core was already collected in UI
359
- if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
360
- // Core already collected, skip it in config collection
361
- const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
362
- moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, path.resolve(config.directory), {
363
- customModulePaths,
364
- skipPrompts: config.skipPrompts,
365
- });
366
- } else {
367
- // Core not collected yet, include it
368
- moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
369
- customModulePaths,
370
- skipPrompts: config.skipPrompts,
371
- });
372
- }
373
- }
374
-
375
- // Set xiaoma folder name on module manager and IDE manager for placeholder replacement
376
- this.moduleManager.setXiaomaFolderName(XiaoMa_FOLDER_NAME);
377
- this.moduleManager.setCoreConfig(moduleConfigs.core || {});
378
- this.moduleManager.setCustomModulePaths(customModulePaths);
379
- this.ideManager.setXiaomaFolderName(XiaoMa_FOLDER_NAME);
380
-
381
- // Tool selection will be collected after we determine if it's a reinstall/update/new install
382
-
383
- const spinner = await prompts.spinner();
384
- spinner.start('Preparing installation...');
385
-
386
- try {
387
- // Create a project directory if it doesn't exist (user already confirmed)
388
- if (!(await fs.pathExists(projectDir))) {
389
- spinner.message('Creating installation directory...');
390
- try {
391
- // fs.ensureDir handles platform-specific directory creation
392
- // It will recursively create all necessary parent directories
393
- await fs.ensureDir(projectDir);
394
- } catch (error) {
395
- spinner.error('Failed to create installation directory');
396
- await prompts.log.error(`Error: ${error.message}`);
397
- // More detailed error for common issues
398
- if (error.code === 'EACCES') {
399
- await prompts.log.error('Permission denied. Check parent directory permissions.');
400
- } else if (error.code === 'ENOSPC') {
401
- await prompts.log.error('No space left on device.');
402
- }
403
- throw new Error(`Cannot create directory: ${projectDir}`);
404
- }
405
- }
406
-
407
- // Check existing installation
408
- spinner.message('Checking for existing installation...');
409
- const existingInstall = await this.detector.detect(xiaomaDir);
410
-
411
- if (existingInstall.installed && !config.force && !config._quickUpdate) {
412
- spinner.stop('Existing installation detected');
413
-
414
- // Check if user already decided what to do (from early menu in ui.js)
415
- let action = null;
416
- if (config.actionType === 'update') {
417
- action = 'update';
418
- } else if (config.skipPrompts) {
419
- // Non-interactive mode: default to update
420
- action = 'update';
421
- } else {
422
- // Fallback: Ask the user (backwards compatibility for other code paths)
423
- await prompts.log.warn('Existing XiaoMa installation detected');
424
- await prompts.log.message(` Location: ${xiaomaDir}`);
425
- await prompts.log.message(` Version: ${existingInstall.version}`);
426
-
427
- const promptResult = await this.promptUpdateAction();
428
- action = promptResult.action;
429
- }
430
-
431
- if (action === 'update') {
432
- // Store that we're updating for later processing
433
- config._isUpdate = true;
434
- config._existingInstall = existingInstall;
435
-
436
- // Detect modules that were previously installed but are NOT in the new selection (to be removed)
437
- const previouslyInstalledModules = new Set(existingInstall.modules.map((m) => m.id));
438
- const newlySelectedModules = new Set(config.modules || []);
439
-
440
- // Find modules to remove (installed but not in new selection)
441
- // Exclude 'core' from being removable
442
- const modulesToRemove = [...previouslyInstalledModules].filter((m) => !newlySelectedModules.has(m) && m !== 'core');
443
-
444
- // If there are modules to remove, ask for confirmation
445
- if (modulesToRemove.length > 0) {
446
- if (config.skipPrompts) {
447
- // Non-interactive mode: preserve modules (matches prompt default: false)
448
- for (const moduleId of modulesToRemove) {
449
- if (!config.modules) config.modules = [];
450
- config.modules.push(moduleId);
451
- }
452
- spinner.start('Preparing update...');
453
- } else {
454
- if (spinner.isSpinning) {
455
- spinner.stop('Module changes reviewed');
456
- }
457
-
458
- await prompts.log.warn('Modules to be removed:');
459
- for (const moduleId of modulesToRemove) {
460
- const moduleInfo = existingInstall.modules.find((m) => m.id === moduleId);
461
- const displayName = moduleInfo?.name || moduleId;
462
- const modulePath = path.join(xiaomaDir, moduleId);
463
- await prompts.log.error(` - ${displayName} (${modulePath})`);
464
- }
465
-
466
- const confirmRemoval = await prompts.confirm({
467
- message: `Remove ${modulesToRemove.length} module(s) from XiaoMa installation?`,
468
- default: false,
469
- });
470
-
471
- if (confirmRemoval) {
472
- // Remove module folders
473
- for (const moduleId of modulesToRemove) {
474
- const modulePath = path.join(xiaomaDir, moduleId);
475
- try {
476
- if (await fs.pathExists(modulePath)) {
477
- await fs.remove(modulePath);
478
- await prompts.log.message(` Removed: ${moduleId}`);
479
- }
480
- } catch (error) {
481
- await prompts.log.warn(` Warning: Failed to remove ${moduleId}: ${error.message}`);
482
- }
483
- }
484
- await prompts.log.success(` Removed ${modulesToRemove.length} module(s)`);
485
- } else {
486
- await prompts.log.message(' Module removal cancelled');
487
- // Add the modules back to the selection since user cancelled removal
488
- for (const moduleId of modulesToRemove) {
489
- if (!config.modules) config.modules = [];
490
- config.modules.push(moduleId);
491
- }
492
- }
493
-
494
- spinner.start('Preparing update...');
495
- }
496
- }
497
-
498
- // Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
499
- const existingFilesManifest = await this.readFilesManifest(xiaomaDir);
500
- const { customFiles, modifiedFiles } = await this.detectCustomFiles(xiaomaDir, existingFilesManifest);
501
-
502
- config._customFiles = customFiles;
503
- config._modifiedFiles = modifiedFiles;
504
-
505
- // Preserve existing core configuration during updates
506
- // Read the current core config.yaml to maintain user's settings
507
- const coreConfigPath = path.join(xiaomaDir, 'core', 'config.yaml');
508
- if ((await fs.pathExists(coreConfigPath)) && (!config.coreConfig || Object.keys(config.coreConfig).length === 0)) {
509
- try {
510
- const yaml = require('yaml');
511
- const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
512
- const existingCoreConfig = yaml.parse(coreConfigContent);
513
-
514
- // Store in config.coreConfig so it's preserved through the installation
515
- config.coreConfig = existingCoreConfig;
516
-
517
- // Also store in configCollector for use during config collection
518
- this.configCollector.collectedConfig.core = existingCoreConfig;
519
- } catch (error) {
520
- await prompts.log.warn(`Warning: Could not read existing core config: ${error.message}`);
521
- }
522
- }
523
-
524
- // Also check cache directory for custom modules (like quick update does)
525
- const cacheDir = path.join(xiaomaDir, '_config', 'custom');
526
- if (await fs.pathExists(cacheDir)) {
527
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
528
-
529
- for (const cachedModule of cachedModules) {
530
- const moduleId = cachedModule.name;
531
- const cachedPath = path.join(cacheDir, moduleId);
532
-
533
- // Skip if path doesn't exist (broken symlink, deleted dir) - avoids lstat ENOENT
534
- if (!(await fs.pathExists(cachedPath)) || !cachedModule.isDirectory()) {
535
- continue;
536
- }
537
-
538
- // Skip if we already have this module from manifest
539
- if (customModulePaths.has(moduleId)) {
540
- continue;
541
- }
542
-
543
- // Check if this is an external official module - skip cache for those
544
- const isExternal = await this.moduleManager.isExternalModule(moduleId);
545
- if (isExternal) {
546
- // External modules are handled via cloneExternalModule, not from cache
547
- continue;
548
- }
549
-
550
- // Check if this is actually a custom module (has module.yaml)
551
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
552
- if (await fs.pathExists(moduleYamlPath)) {
553
- customModulePaths.set(moduleId, cachedPath);
554
- }
555
- }
556
-
557
- // Update module manager with the new custom module paths from cache
558
- this.moduleManager.setCustomModulePaths(customModulePaths);
559
- }
560
-
561
- // If there are custom files, back them up temporarily
562
- if (customFiles.length > 0) {
563
- const tempBackupDir = path.join(projectDir, '_xiaoma-custom-backup-temp');
564
- await fs.ensureDir(tempBackupDir);
565
-
566
- spinner.start(`Backing up ${customFiles.length} custom files...`);
567
- for (const customFile of customFiles) {
568
- const relativePath = path.relative(xiaomaDir, customFile);
569
- const backupPath = path.join(tempBackupDir, relativePath);
570
- await fs.ensureDir(path.dirname(backupPath));
571
- await fs.copy(customFile, backupPath);
572
- }
573
- spinner.stop(`Backed up ${customFiles.length} custom files`);
574
-
575
- config._tempBackupDir = tempBackupDir;
576
- }
577
-
578
- // For modified files, back them up to temp directory (will be restored as .bak files after install)
579
- if (modifiedFiles.length > 0) {
580
- const tempModifiedBackupDir = path.join(projectDir, '_xiaoma-modified-backup-temp');
581
- await fs.ensureDir(tempModifiedBackupDir);
582
-
583
- spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
584
- for (const modifiedFile of modifiedFiles) {
585
- const relativePath = path.relative(xiaomaDir, modifiedFile.path);
586
- const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
587
- await fs.ensureDir(path.dirname(tempBackupPath));
588
- await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
589
- }
590
- spinner.stop(`Backed up ${modifiedFiles.length} modified files`);
591
-
592
- config._tempModifiedBackupDir = tempModifiedBackupDir;
593
- }
594
- }
595
- } else if (existingInstall.installed && config._quickUpdate) {
596
- // Quick update mode - automatically treat as update without prompting
597
- spinner.message('Preparing quick update...');
598
- config._isUpdate = true;
599
- config._existingInstall = existingInstall;
600
-
601
- // Detect custom and modified files BEFORE updating
602
- const existingFilesManifest = await this.readFilesManifest(xiaomaDir);
603
- const { customFiles, modifiedFiles } = await this.detectCustomFiles(xiaomaDir, existingFilesManifest);
604
-
605
- config._customFiles = customFiles;
606
- config._modifiedFiles = modifiedFiles;
607
-
608
- // Also check cache directory for custom modules (like quick update does)
609
- const cacheDir = path.join(xiaomaDir, '_config', 'custom');
610
- if (await fs.pathExists(cacheDir)) {
611
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
612
-
613
- for (const cachedModule of cachedModules) {
614
- const moduleId = cachedModule.name;
615
- const cachedPath = path.join(cacheDir, moduleId);
616
-
617
- // Skip if path doesn't exist (broken symlink, deleted dir) - avoids lstat ENOENT
618
- if (!(await fs.pathExists(cachedPath)) || !cachedModule.isDirectory()) {
619
- continue;
620
- }
621
-
622
- // Skip if we already have this module from manifest
623
- if (customModulePaths.has(moduleId)) {
624
- continue;
625
- }
626
-
627
- // Check if this is an external official module - skip cache for those
628
- const isExternal = await this.moduleManager.isExternalModule(moduleId);
629
- if (isExternal) {
630
- // External modules are handled via cloneExternalModule, not from cache
631
- continue;
632
- }
633
-
634
- // Check if this is actually a custom module (has module.yaml)
635
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
636
- if (await fs.pathExists(moduleYamlPath)) {
637
- customModulePaths.set(moduleId, cachedPath);
638
- }
639
- }
640
-
641
- // Update module manager with the new custom module paths from cache
642
- this.moduleManager.setCustomModulePaths(customModulePaths);
643
- }
644
-
645
- // Back up custom files
646
- if (customFiles.length > 0) {
647
- const tempBackupDir = path.join(projectDir, '_xiaoma-custom-backup-temp');
648
- await fs.ensureDir(tempBackupDir);
649
-
650
- spinner.start(`Backing up ${customFiles.length} custom files...`);
651
- for (const customFile of customFiles) {
652
- const relativePath = path.relative(xiaomaDir, customFile);
653
- const backupPath = path.join(tempBackupDir, relativePath);
654
- await fs.ensureDir(path.dirname(backupPath));
655
- await fs.copy(customFile, backupPath);
656
- }
657
- spinner.stop(`Backed up ${customFiles.length} custom files`);
658
- config._tempBackupDir = tempBackupDir;
659
- }
660
-
661
- // Back up modified files
662
- if (modifiedFiles.length > 0) {
663
- const tempModifiedBackupDir = path.join(projectDir, '_xiaoma-modified-backup-temp');
664
- await fs.ensureDir(tempModifiedBackupDir);
665
-
666
- spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
667
- for (const modifiedFile of modifiedFiles) {
668
- const relativePath = path.relative(xiaomaDir, modifiedFile.path);
669
- const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
670
- await fs.ensureDir(path.dirname(tempBackupPath));
671
- await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
672
- }
673
- spinner.stop(`Backed up ${modifiedFiles.length} modified files`);
674
- config._tempModifiedBackupDir = tempModifiedBackupDir;
675
- }
676
- }
677
-
678
- // Now collect tool configurations after we know if it's a reinstall
679
- // Skip for quick update since we already have the IDE list
680
- spinner.stop('Pre-checks complete');
681
- let toolSelection;
682
- if (config._quickUpdate) {
683
- // Quick update already has IDEs configured, use saved configurations
684
- const preConfiguredIdes = {};
685
- const savedIdeConfigs = config._savedIdeConfigs || {};
686
-
687
- for (const ide of config.ides || []) {
688
- // Use saved config if available, otherwise mark as already configured (legacy)
689
- if (savedIdeConfigs[ide]) {
690
- preConfiguredIdes[ide] = savedIdeConfigs[ide];
691
- } else {
692
- preConfiguredIdes[ide] = { _alreadyConfigured: true };
693
- }
694
- }
695
- toolSelection = {
696
- ides: config.ides || [],
697
- skipIde: !config.ides || config.ides.length === 0,
698
- configurations: preConfiguredIdes,
699
- };
700
- } else {
701
- // Pass pre-selected IDEs from early prompt (if available)
702
- // This allows IDE selection to happen before file copying, improving UX
703
- // Use config.ides if it's an array (even if empty), null means prompt
704
- const preSelectedIdes = Array.isArray(config.ides) ? config.ides : null;
705
- toolSelection = await this.collectToolConfigurations(
706
- path.resolve(config.directory),
707
- config.modules,
708
- config._isFullReinstall || false,
709
- config._previouslyConfiguredIdes || [],
710
- preSelectedIdes,
711
- config.skipPrompts || false,
712
- );
713
- }
714
-
715
- // Merge tool selection into config (for both quick update and regular flow)
716
- // Normalize IDE keys to lowercase so they match handler map keys consistently
717
- config.ides = (toolSelection.ides || []).map((ide) => ide.toLowerCase());
718
- config.skipIde = toolSelection.skipIde;
719
- const ideConfigurations = toolSelection.configurations;
720
-
721
- // Early check: fail fast if ALL selected IDEs are suspended
722
- if (config.ides && config.ides.length > 0) {
723
- await this.ideManager.ensureInitialized();
724
- const suspendedIdes = config.ides.filter((ide) => {
725
- const handler = this.ideManager.handlers.get(ide);
726
- return handler?.platformConfig?.suspended;
727
- });
728
-
729
- if (suspendedIdes.length > 0 && suspendedIdes.length === config.ides.length) {
730
- for (const ide of suspendedIdes) {
731
- const handler = this.ideManager.handlers.get(ide);
732
- await prompts.log.error(`${handler.displayName || ide}: ${handler.platformConfig.suspended}`);
733
- }
734
- throw new Error(
735
- `All selected tool(s) are suspended: ${suspendedIdes.join(', ')}. Installation aborted to prevent upgrading _xiaoma/ without a working IDE configuration.`,
736
- );
737
- }
738
- }
739
-
740
- // Detect IDEs that were previously installed but are NOT in the new selection (to be removed)
741
- if (config._isUpdate && config._existingInstall) {
742
- const previouslyInstalledIdes = new Set(config._existingInstall.ides || []);
743
- const newlySelectedIdes = new Set(config.ides || []);
744
-
745
- const idesToRemove = [...previouslyInstalledIdes].filter((ide) => !newlySelectedIdes.has(ide));
746
-
747
- if (idesToRemove.length > 0) {
748
- if (config.skipPrompts) {
749
- // Non-interactive mode: silently preserve existing IDE configs
750
- if (!config.ides) config.ides = [];
751
- const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(xiaomaDir);
752
- for (const ide of idesToRemove) {
753
- config.ides.push(ide);
754
- if (savedIdeConfigs[ide] && !ideConfigurations[ide]) {
755
- ideConfigurations[ide] = savedIdeConfigs[ide];
756
- }
757
- }
758
- } else {
759
- if (spinner.isSpinning) {
760
- spinner.stop('IDE changes reviewed');
761
- }
762
-
763
- await prompts.log.warn('IDEs to be removed:');
764
- for (const ide of idesToRemove) {
765
- await prompts.log.error(` - ${ide}`);
766
- }
767
-
768
- const confirmRemoval = await prompts.confirm({
769
- message: `Remove XiaoMa configuration for ${idesToRemove.length} IDE(s)?`,
770
- default: false,
771
- });
772
-
773
- if (confirmRemoval) {
774
- await this.ideManager.ensureInitialized();
775
- for (const ide of idesToRemove) {
776
- try {
777
- const handler = this.ideManager.handlers.get(ide);
778
- if (handler) {
779
- await handler.cleanup(projectDir);
780
- }
781
- await this.ideConfigManager.deleteIdeConfig(xiaomaDir, ide);
782
- await prompts.log.message(` Removed: ${ide}`);
783
- } catch (error) {
784
- await prompts.log.warn(` Warning: Failed to remove ${ide}: ${error.message}`);
785
- }
786
- }
787
- await prompts.log.success(` Removed ${idesToRemove.length} IDE(s)`);
788
- } else {
789
- await prompts.log.message(' IDE removal cancelled');
790
- // Add IDEs back to selection and restore their saved configurations
791
- if (!config.ides) config.ides = [];
792
- const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(xiaomaDir);
793
- for (const ide of idesToRemove) {
794
- config.ides.push(ide);
795
- if (savedIdeConfigs[ide] && !ideConfigurations[ide]) {
796
- ideConfigurations[ide] = savedIdeConfigs[ide];
797
- }
798
- }
799
- }
800
-
801
- spinner.start('Preparing installation...');
802
- }
803
- }
804
- }
805
-
806
- // Results collector for consolidated summary
807
- const results = [];
808
- const addResult = (step, status, detail = '') => results.push({ step, status, detail });
809
-
810
- if (spinner.isSpinning) {
811
- spinner.message('Preparing installation...');
812
- } else {
813
- spinner.start('Preparing installation...');
814
- }
815
-
816
- // Create xiaoma directory structure
817
- spinner.message('Creating directory structure...');
818
- await this.createDirectoryStructure(xiaomaDir);
819
-
820
- // Cache custom modules if any
821
- if (customModulePaths && customModulePaths.size > 0) {
822
- spinner.message('Caching custom modules...');
823
- const { CustomModuleCache } = require('./custom-module-cache');
824
- const customCache = new CustomModuleCache(xiaomaDir);
825
-
826
- for (const [moduleId, sourcePath] of customModulePaths) {
827
- const cachedInfo = await customCache.cacheModule(moduleId, sourcePath, {
828
- sourcePath: sourcePath, // Store original path for updates
829
- });
830
-
831
- // Update the customModulePaths to use the cached location
832
- customModulePaths.set(moduleId, cachedInfo.cachePath);
833
- }
834
-
835
- // Update module manager with the cached paths
836
- this.moduleManager.setCustomModulePaths(customModulePaths);
837
- addResult('Custom modules cached', 'ok');
838
- }
839
-
840
- const projectRoot = getProjectRoot();
841
-
842
- // Custom content is already handled in UI before module selection
843
- const finalCustomContent = config.customContent;
844
-
845
- // Prepare modules list including cached custom modules
846
- let allModules = [...(config.modules || [])];
847
-
848
- // During quick update, we might have custom module sources from the manifest
849
- if (config._customModuleSources) {
850
- // Add custom modules from stored sources
851
- for (const [moduleId, customInfo] of config._customModuleSources) {
852
- if (!allModules.includes(moduleId) && (await fs.pathExists(customInfo.sourcePath))) {
853
- allModules.push(moduleId);
854
- }
855
- }
856
- }
857
-
858
- // Add cached custom modules
859
- if (finalCustomContent && finalCustomContent.cachedModules) {
860
- for (const cachedModule of finalCustomContent.cachedModules) {
861
- if (!allModules.includes(cachedModule.id)) {
862
- allModules.push(cachedModule.id);
863
- }
864
- }
865
- }
866
-
867
- // Regular custom content from user input (non-cached)
868
- if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
869
- // Add custom modules to the installation list
870
- const customHandler = new CustomHandler();
871
- for (const customFile of finalCustomContent.selectedFiles) {
872
- const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
873
- if (customInfo && customInfo.id) {
874
- allModules.push(customInfo.id);
875
- }
876
- }
877
- }
878
-
879
- // Don't include core again if already installed
880
- if (config.installCore) {
881
- allModules = allModules.filter((m) => m !== 'core');
882
- }
883
-
884
- // For dependency resolution, we only need regular modules (not custom modules)
885
- // Custom modules are already installed in _xiaoma and don't need dependency resolution from source
886
- const regularModulesForResolution = allModules.filter((module) => {
887
- // Check if this is a custom module
888
- const isCustom =
889
- customModulePaths.has(module) ||
890
- (finalCustomContent && finalCustomContent.cachedModules && finalCustomContent.cachedModules.some((cm) => cm.id === module)) ||
891
- (finalCustomContent &&
892
- finalCustomContent.selected &&
893
- finalCustomContent.selectedFiles &&
894
- finalCustomContent.selectedFiles.some((f) => f.includes(module)));
895
- return !isCustom;
896
- });
897
-
898
- // Stop spinner before tasks() takes over progress display
899
- spinner.stop('Preparation complete');
900
-
901
- // ─────────────────────────────────────────────────────────────────────────
902
- // FIRST TASKS BLOCK: Core installation through manifests (non-interactive)
903
- // ─────────────────────────────────────────────────────────────────────────
904
- const isQuickUpdate = config._quickUpdate || false;
905
-
906
- // Shared resolution result across task callbacks (closure-scoped, not on `this`)
907
- let taskResolution;
908
-
909
- // Collect directory creation results for output after tasks() completes
910
- const dirResults = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
911
-
912
- // Build task list conditionally
913
- const installTasks = [];
914
-
915
- // Core installation task
916
- if (config.installCore) {
917
- installTasks.push({
918
- title: isQuickUpdate ? 'Updating XiaoMa core' : 'Installing XiaoMa core',
919
- task: async (message) => {
920
- await this.installCoreWithDependencies(xiaomaDir, { core: {} });
921
- addResult('Core', 'ok', isQuickUpdate ? 'updated' : 'installed');
922
- await this.generateModuleConfigs(xiaomaDir, { core: config.coreConfig || {} });
923
- return isQuickUpdate ? 'Core updated' : 'Core installed';
924
- },
925
- });
926
- }
927
-
928
- // Dependency resolution task
929
- installTasks.push({
930
- title: 'Resolving dependencies',
931
- task: async (message) => {
932
- // Create a temporary module manager that knows about custom content locations
933
- const tempModuleManager = new ModuleManager({
934
- xiaomaDir: xiaomaDir,
935
- });
936
-
937
- taskResolution = await this.dependencyResolver.resolve(projectRoot, regularModulesForResolution, {
938
- verbose: config.verbose,
939
- moduleManager: tempModuleManager,
940
- });
941
- return 'Dependencies resolved';
942
- },
943
- });
944
-
945
- // Module installation task
946
- if (allModules && allModules.length > 0) {
947
- installTasks.push({
948
- title: isQuickUpdate ? `Updating ${allModules.length} module(s)` : `Installing ${allModules.length} module(s)`,
949
- task: async (message) => {
950
- const resolution = taskResolution;
951
- const installedModuleNames = new Set();
952
-
953
- for (const moduleName of allModules) {
954
- if (installedModuleNames.has(moduleName)) continue;
955
- installedModuleNames.add(moduleName);
956
-
957
- message(`${isQuickUpdate ? 'Updating' : 'Installing'} ${moduleName}...`);
958
-
959
- // Check if this is a custom module
960
- let isCustomModule = false;
961
- let customInfo = null;
962
-
963
- // First check if we have a cached version
964
- if (finalCustomContent && finalCustomContent.cachedModules) {
965
- const cachedModule = finalCustomContent.cachedModules.find((m) => m.id === moduleName);
966
- if (cachedModule) {
967
- isCustomModule = true;
968
- customInfo = { id: moduleName, path: cachedModule.cachePath, config: {} };
969
- }
970
- }
971
-
972
- // Then check custom module sources from manifest (for quick update)
973
- if (!isCustomModule && config._customModuleSources && config._customModuleSources.has(moduleName)) {
974
- customInfo = config._customModuleSources.get(moduleName);
975
- isCustomModule = true;
976
- if (customInfo.sourcePath && !customInfo.path) {
977
- customInfo.path = path.isAbsolute(customInfo.sourcePath)
978
- ? customInfo.sourcePath
979
- : path.join(xiaomaDir, customInfo.sourcePath);
980
- }
981
- }
982
-
983
- // Finally check regular custom content
984
- if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
985
- const customHandler = new CustomHandler();
986
- for (const customFile of finalCustomContent.selectedFiles) {
987
- const info = await customHandler.getCustomInfo(customFile, projectDir);
988
- if (info && info.id === moduleName) {
989
- isCustomModule = true;
990
- customInfo = info;
991
- break;
992
- }
993
- }
994
- }
995
-
996
- if (isCustomModule && customInfo) {
997
- if (!customModulePaths.has(moduleName) && customInfo.path) {
998
- customModulePaths.set(moduleName, customInfo.path);
999
- this.moduleManager.setCustomModulePaths(customModulePaths);
1000
- }
1001
-
1002
- const collectedModuleConfig = moduleConfigs[moduleName] || {};
1003
- await this.moduleManager.install(
1004
- moduleName,
1005
- xiaomaDir,
1006
- (filePath) => {
1007
- this.installedFiles.add(filePath);
1008
- },
1009
- {
1010
- isCustom: true,
1011
- moduleConfig: collectedModuleConfig,
1012
- isQuickUpdate: isQuickUpdate,
1013
- installer: this,
1014
- silent: true,
1015
- },
1016
- );
1017
- await this.generateModuleConfigs(xiaomaDir, {
1018
- [moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig },
1019
- });
1020
- } else {
1021
- if (!resolution || !resolution.byModule) {
1022
- addResult(`Module: ${moduleName}`, 'warn', 'skipped (no resolution data)');
1023
- continue;
1024
- }
1025
- if (moduleName === 'core') {
1026
- await this.installCoreWithDependencies(xiaomaDir, resolution.byModule[moduleName]);
1027
- } else {
1028
- await this.installModuleWithDependencies(moduleName, xiaomaDir, resolution.byModule[moduleName]);
1029
- }
1030
- }
1031
-
1032
- addResult(`Module: ${moduleName}`, 'ok', isQuickUpdate ? 'updated' : 'installed');
1033
- }
1034
-
1035
- // Install partial modules (only dependencies)
1036
- if (!resolution || !resolution.byModule) {
1037
- return `${allModules.length} module(s) ${isQuickUpdate ? 'updated' : 'installed'}`;
1038
- }
1039
- for (const [module, files] of Object.entries(resolution.byModule)) {
1040
- if (!allModules.includes(module) && module !== 'core') {
1041
- const totalFiles =
1042
- files.agents.length +
1043
- files.tasks.length +
1044
- files.tools.length +
1045
- files.templates.length +
1046
- files.data.length +
1047
- files.other.length;
1048
- if (totalFiles > 0) {
1049
- message(`Installing ${module} dependencies...`);
1050
- await this.installPartialModule(module, xiaomaDir, files);
1051
- }
1052
- }
1053
- }
1054
-
1055
- return `${allModules.length} module(s) ${isQuickUpdate ? 'updated' : 'installed'}`;
1056
- },
1057
- });
1058
- }
1059
-
1060
- // Module directory creation task
1061
- installTasks.push({
1062
- title: 'Creating module directories',
1063
- task: async (message) => {
1064
- const resolution = taskResolution;
1065
- if (!resolution || !resolution.byModule) {
1066
- addResult('Module directories', 'warn', 'no resolution data');
1067
- return 'Module directories skipped (no resolution data)';
1068
- }
1069
- const verboseMode = process.env.XiaoMa_VERBOSE_INSTALL === 'true' || config.verbose;
1070
- const moduleLogger = {
1071
- log: async (msg) => (verboseMode ? await prompts.log.message(msg) : undefined),
1072
- error: async (msg) => await prompts.log.error(msg),
1073
- warn: async (msg) => await prompts.log.warn(msg),
1074
- };
1075
-
1076
- // Core module directories
1077
- if (config.installCore || resolution.byModule.core) {
1078
- const result = await this.moduleManager.createModuleDirectories('core', xiaomaDir, {
1079
- installedIDEs: config.ides || [],
1080
- moduleConfig: moduleConfigs.core || {},
1081
- existingModuleConfig: this.configCollector.existingConfig?.core || {},
1082
- coreConfig: moduleConfigs.core || {},
1083
- logger: moduleLogger,
1084
- silent: true,
1085
- });
1086
- if (result) {
1087
- dirResults.createdDirs.push(...result.createdDirs);
1088
- dirResults.movedDirs.push(...(result.movedDirs || []));
1089
- dirResults.createdWdsFolders.push(...result.createdWdsFolders);
1090
- }
1091
- }
1092
-
1093
- // User-selected module directories
1094
- if (config.modules && config.modules.length > 0) {
1095
- for (const moduleName of config.modules) {
1096
- message(`Setting up ${moduleName}...`);
1097
- const result = await this.moduleManager.createModuleDirectories(moduleName, xiaomaDir, {
1098
- installedIDEs: config.ides || [],
1099
- moduleConfig: moduleConfigs[moduleName] || {},
1100
- existingModuleConfig: this.configCollector.existingConfig?.[moduleName] || {},
1101
- coreConfig: moduleConfigs.core || {},
1102
- logger: moduleLogger,
1103
- silent: true,
1104
- });
1105
- if (result) {
1106
- dirResults.createdDirs.push(...result.createdDirs);
1107
- dirResults.movedDirs.push(...(result.movedDirs || []));
1108
- dirResults.createdWdsFolders.push(...result.createdWdsFolders);
1109
- }
1110
- }
1111
- }
1112
-
1113
- addResult('Module directories', 'ok');
1114
- return 'Module directories created';
1115
- },
1116
- });
1117
-
1118
- // Configuration generation task (stored as named reference for deferred execution)
1119
- const configTask = {
1120
- title: 'Generating configurations',
1121
- task: async (message) => {
1122
- // Generate clean config.yaml files for each installed module
1123
- await this.generateModuleConfigs(xiaomaDir, moduleConfigs);
1124
- addResult('Configurations', 'ok', 'generated');
1125
-
1126
- // Pre-register manifest files
1127
- const cfgDir = path.join(xiaomaDir, '_config');
1128
- this.installedFiles.add(path.join(cfgDir, 'manifest.yaml'));
1129
- this.installedFiles.add(path.join(cfgDir, 'workflow-manifest.csv'));
1130
- this.installedFiles.add(path.join(cfgDir, 'agent-manifest.csv'));
1131
- this.installedFiles.add(path.join(cfgDir, 'task-manifest.csv'));
1132
-
1133
- // Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes
1134
- // This must happen BEFORE mergeModuleHelpCatalogs because it depends on agent-manifest.csv
1135
- message('Generating manifests...');
1136
- const manifestGen = new ManifestGenerator();
1137
-
1138
- const allModulesForManifest = config._quickUpdate
1139
- ? config._existingModules || allModules || []
1140
- : config._preserveModules
1141
- ? [...allModules, ...config._preserveModules]
1142
- : allModules || [];
1143
-
1144
- let modulesForCsvPreserve;
1145
- if (config._quickUpdate) {
1146
- modulesForCsvPreserve = config._existingModules || allModules || [];
1147
- } else {
1148
- modulesForCsvPreserve = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules;
1149
- }
1150
-
1151
- const manifestStats = await manifestGen.generateManifests(xiaomaDir, allModulesForManifest, [...this.installedFiles], {
1152
- ides: config.ides || [],
1153
- preservedModules: modulesForCsvPreserve,
1154
- });
1155
-
1156
- // Merge help catalogs
1157
- message('Generating help catalog...');
1158
- await this.mergeModuleHelpCatalogs(xiaomaDir);
1159
- addResult('Help catalog', 'ok');
1160
-
1161
- return 'Configurations generated';
1162
- },
1163
- };
1164
- installTasks.push(configTask);
1165
-
1166
- // Run all tasks except config (which runs after directory output)
1167
- const mainTasks = installTasks.filter((t) => t !== configTask);
1168
- await prompts.tasks(mainTasks);
1169
-
1170
- // Render directory creation output right after directory task
1171
- const color = await prompts.getColor();
1172
- if (dirResults.movedDirs.length > 0) {
1173
- const lines = dirResults.movedDirs.map((d) => ` ${d}`).join('\n');
1174
- await prompts.log.message(color.cyan(`Moved directories:\n${lines}`));
1175
- }
1176
- if (dirResults.createdDirs.length > 0) {
1177
- const lines = dirResults.createdDirs.map((d) => ` ${d}`).join('\n');
1178
- await prompts.log.message(color.yellow(`Created directories:\n${lines}`));
1179
- }
1180
- if (dirResults.createdWdsFolders.length > 0) {
1181
- const lines = dirResults.createdWdsFolders.map((f) => color.dim(` \u2713 ${f}/`)).join('\n');
1182
- await prompts.log.message(color.cyan(`Created WDS folder structure:\n${lines}`));
1183
- }
1184
-
1185
- // Now run configuration generation
1186
- await prompts.tasks([configTask]);
1187
-
1188
- // Resolution is now available via closure-scoped taskResolution
1189
- const resolution = taskResolution;
1190
-
1191
- // ─────────────────────────────────────────────────────────────────────────
1192
- // IDE SETUP: Keep as spinner since it may prompt for user input
1193
- // ─────────────────────────────────────────────────────────────────────────
1194
- if (!config.skipIde && config.ides && config.ides.length > 0) {
1195
- await this.ideManager.ensureInitialized();
1196
- const validIdes = config.ides.filter((ide) => ide && typeof ide === 'string');
1197
-
1198
- if (validIdes.length === 0) {
1199
- addResult('IDE configuration', 'warn', 'no valid IDEs selected');
1200
- } else {
1201
- const needsPrompting = validIdes.some((ide) => !ideConfigurations[ide]);
1202
- const ideSpinner = await prompts.spinner();
1203
- ideSpinner.start('Configuring tools...');
1204
-
1205
- try {
1206
- for (const ide of validIdes) {
1207
- if (!needsPrompting || ideConfigurations[ide]) {
1208
- ideSpinner.message(`Configuring ${ide}...`);
1209
- } else {
1210
- if (ideSpinner.isSpinning) {
1211
- ideSpinner.stop('Ready for IDE configuration');
1212
- }
1213
- }
1214
-
1215
- // Suppress stray console output for pre-configured IDEs (no user interaction)
1216
- const ideHasConfig = Boolean(ideConfigurations[ide]);
1217
- const originalLog = console.log;
1218
- if (!config.verbose && ideHasConfig) {
1219
- console.log = () => {};
1220
- }
1221
- try {
1222
- const setupResult = await this.ideManager.setup(ide, projectDir, xiaomaDir, {
1223
- selectedModules: allModules || [],
1224
- preCollectedConfig: ideConfigurations[ide] || null,
1225
- verbose: config.verbose,
1226
- silent: ideHasConfig,
1227
- });
1228
-
1229
- if (ideConfigurations[ide] && !ideConfigurations[ide]._alreadyConfigured) {
1230
- await this.ideConfigManager.saveIdeConfig(xiaomaDir, ide, ideConfigurations[ide]);
1231
- }
1232
-
1233
- if (setupResult.success) {
1234
- addResult(ide, 'ok', setupResult.detail || '');
1235
- } else {
1236
- addResult(ide, 'error', setupResult.error || 'failed');
1237
- }
1238
- } finally {
1239
- console.log = originalLog;
1240
- }
1241
-
1242
- if (needsPrompting && !ideSpinner.isSpinning) {
1243
- ideSpinner.start('Configuring tools...');
1244
- }
1245
- }
1246
- } finally {
1247
- if (ideSpinner.isSpinning) {
1248
- ideSpinner.stop('Tool configuration complete');
1249
- }
1250
- }
1251
- }
1252
- }
1253
-
1254
- // ─────────────────────────────────────────────────────────────────────────
1255
- // SECOND TASKS BLOCK: Post-IDE operations (non-interactive)
1256
- // ─────────────────────────────────────────────────────────────────────────
1257
- const postIdeTasks = [];
1258
-
1259
- // File restoration task (only for updates)
1260
- if (
1261
- config._isUpdate &&
1262
- ((config._customFiles && config._customFiles.length > 0) || (config._modifiedFiles && config._modifiedFiles.length > 0))
1263
- ) {
1264
- postIdeTasks.push({
1265
- title: 'Finalizing installation',
1266
- task: async (message) => {
1267
- let customFiles = [];
1268
- let modifiedFiles = [];
1269
-
1270
- if (config._customFiles && config._customFiles.length > 0) {
1271
- message(`Restoring ${config._customFiles.length} custom files...`);
1272
-
1273
- for (const originalPath of config._customFiles) {
1274
- const relativePath = path.relative(xiaomaDir, originalPath);
1275
- const backupPath = path.join(config._tempBackupDir, relativePath);
1276
-
1277
- if (await fs.pathExists(backupPath)) {
1278
- await fs.ensureDir(path.dirname(originalPath));
1279
- await fs.copy(backupPath, originalPath, { overwrite: true });
1280
- }
1281
- }
1282
-
1283
- if (config._tempBackupDir && (await fs.pathExists(config._tempBackupDir))) {
1284
- await fs.remove(config._tempBackupDir);
1285
- }
1286
-
1287
- customFiles = config._customFiles;
1288
- }
1289
-
1290
- if (config._modifiedFiles && config._modifiedFiles.length > 0) {
1291
- modifiedFiles = config._modifiedFiles;
1292
-
1293
- if (config._tempModifiedBackupDir && (await fs.pathExists(config._tempModifiedBackupDir))) {
1294
- message(`Restoring ${modifiedFiles.length} modified files as .bak...`);
1295
-
1296
- for (const modifiedFile of modifiedFiles) {
1297
- const relativePath = path.relative(xiaomaDir, modifiedFile.path);
1298
- const tempBackupPath = path.join(config._tempModifiedBackupDir, relativePath);
1299
- const bakPath = modifiedFile.path + '.bak';
1300
-
1301
- if (await fs.pathExists(tempBackupPath)) {
1302
- await fs.ensureDir(path.dirname(bakPath));
1303
- await fs.copy(tempBackupPath, bakPath, { overwrite: true });
1304
- }
1305
- }
1306
-
1307
- await fs.remove(config._tempModifiedBackupDir);
1308
- }
1309
- }
1310
-
1311
- // Store for summary access
1312
- config._restoredCustomFiles = customFiles;
1313
- config._restoredModifiedFiles = modifiedFiles;
1314
-
1315
- return 'Installation finalized';
1316
- },
1317
- });
1318
- }
1319
-
1320
- await prompts.tasks(postIdeTasks);
1321
-
1322
- // Retrieve restored file info for summary
1323
- const customFiles = config._restoredCustomFiles || [];
1324
- const modifiedFiles = config._restoredModifiedFiles || [];
1325
-
1326
- // Render consolidated summary
1327
- await this.renderInstallSummary(results, {
1328
- xiaomaDir,
1329
- modules: config.modules,
1330
- ides: config.ides,
1331
- customFiles: customFiles.length > 0 ? customFiles : undefined,
1332
- modifiedFiles: modifiedFiles.length > 0 ? modifiedFiles : undefined,
1333
- });
1334
-
1335
- return {
1336
- success: true,
1337
- path: xiaomaDir,
1338
- modules: config.modules,
1339
- ides: config.ides,
1340
- projectDir: projectDir,
1341
- };
1342
- } catch (error) {
1343
- try {
1344
- if (spinner.isSpinning) {
1345
- spinner.error('Installation failed');
1346
- } else {
1347
- await prompts.log.error('Installation failed');
1348
- }
1349
- } catch {
1350
- // Ensure the original error is never swallowed by a logging failure
1351
- }
1352
-
1353
- // Clean up any temp backup directories that were created before the failure
1354
- try {
1355
- if (config._tempBackupDir && (await fs.pathExists(config._tempBackupDir))) {
1356
- await fs.remove(config._tempBackupDir);
1357
- }
1358
- if (config._tempModifiedBackupDir && (await fs.pathExists(config._tempModifiedBackupDir))) {
1359
- await fs.remove(config._tempModifiedBackupDir);
1360
- }
1361
- } catch {
1362
- // Best-effort cleanup — don't mask the original error
1363
- }
1364
-
1365
- throw error;
1366
- }
1367
- }
1368
-
1369
- /**
1370
- * Render a consolidated install summary using prompts.note()
1371
- * @param {Array} results - Array of {step, status: 'ok'|'error'|'warn', detail}
1372
- * @param {Object} context - {xiaomaDir, modules, ides, customFiles, modifiedFiles}
1373
- */
1374
- async renderInstallSummary(results, context = {}) {
1375
- const color = await prompts.getColor();
1376
- const selectedIdes = new Set((context.ides || []).map((ide) => String(ide).toLowerCase()));
1377
-
1378
- // Build step lines with status indicators
1379
- const lines = [];
1380
- for (const r of results) {
1381
- let stepLabel = null;
1382
-
1383
- if (r.status !== 'ok') {
1384
- stepLabel = r.step;
1385
- } else if (r.step === 'Core') {
1386
- stepLabel = 'XiaoMa';
1387
- } else if (r.step.startsWith('Module: ')) {
1388
- stepLabel = r.step;
1389
- } else if (selectedIdes.has(String(r.step).toLowerCase())) {
1390
- stepLabel = r.step;
1391
- }
1392
-
1393
- if (!stepLabel) {
1394
- continue;
1395
- }
1396
-
1397
- let icon;
1398
- if (r.status === 'ok') {
1399
- icon = color.green('\u2713');
1400
- } else if (r.status === 'warn') {
1401
- icon = color.yellow('!');
1402
- } else {
1403
- icon = color.red('\u2717');
1404
- }
1405
- const detail = r.detail ? color.dim(` (${r.detail})`) : '';
1406
- lines.push(` ${icon} ${stepLabel}${detail}`);
1407
- }
1408
-
1409
- if ((context.ides || []).length === 0) {
1410
- lines.push(` ${color.green('\u2713')} No IDE selected ${color.dim('(installed in _xiaoma only)')}`);
1411
- }
1412
-
1413
- // Context and warnings
1414
- lines.push('');
1415
- if (context.xiaomaDir) {
1416
- lines.push(` Installed to: ${color.dim(context.xiaomaDir)}`);
1417
- }
1418
- if (context.customFiles && context.customFiles.length > 0) {
1419
- lines.push(` ${color.cyan(`Custom files preserved: ${context.customFiles.length}`)}`);
1420
- }
1421
- if (context.modifiedFiles && context.modifiedFiles.length > 0) {
1422
- lines.push(` ${color.yellow(`Modified files backed up (.bak): ${context.modifiedFiles.length}`)}`);
1423
- }
1424
-
1425
- // Next steps
1426
- lines.push(
1427
- '',
1428
- ' Next steps:',
1429
- ` Read our new Docs Site: ${color.dim('https://docs.xiaoma-cli.org/')}`,
1430
- ` Join our Discord: ${color.dim('https://discord.gg/gk8jAdXWmj')}`,
1431
- ` Star us on GitHub: ${color.dim('https://github.com/xiaoma-code-org/XiaoMa-CLI/')}`,
1432
- ` Subscribe on YouTube: ${color.dim('https://github.com/zqyl-xiaoma/xiaoma-cli-release')}`,
1433
- );
1434
- if (context.ides && context.ides.length > 0) {
1435
- lines.push(` Invoke the ${color.cyan('xiaoma-help')} skill in your IDE Agent to get started`);
1436
- }
1437
-
1438
- await prompts.note(lines.join('\n'), 'XiaoMa is ready to use!');
1439
- }
1440
-
1441
- /**
1442
- * Update existing installation
1443
- */
1444
- async update(config) {
1445
- const spinner = await prompts.spinner();
1446
- spinner.start('Checking installation...');
1447
-
1448
- try {
1449
- const projectDir = path.resolve(config.directory);
1450
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
1451
- const existingInstall = await this.detector.detect(xiaomaDir);
1452
-
1453
- if (!existingInstall.installed) {
1454
- spinner.stop('No XiaoMa installation found');
1455
- throw new Error(`No XiaoMa installation found at ${xiaomaDir}`);
1456
- }
1457
-
1458
- spinner.message('Analyzing update requirements...');
1459
-
1460
- // Compare versions and determine what needs updating
1461
- const currentVersion = existingInstall.version;
1462
- const newVersion = require(path.join(getProjectRoot(), 'package.json')).version;
1463
-
1464
- // Check for custom modules with missing sources before update
1465
- const customModuleSources = new Map();
1466
-
1467
- // Check manifest for backward compatibility
1468
- if (existingInstall.customModules) {
1469
- for (const customModule of existingInstall.customModules) {
1470
- customModuleSources.set(customModule.id, customModule);
1471
- }
1472
- }
1473
-
1474
- // Also check cache directory
1475
- const cacheDir = path.join(xiaomaDir, '_config', 'custom');
1476
- if (await fs.pathExists(cacheDir)) {
1477
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
1478
-
1479
- for (const cachedModule of cachedModules) {
1480
- if (cachedModule.isDirectory()) {
1481
- const moduleId = cachedModule.name;
1482
-
1483
- // Skip if we already have this module
1484
- if (customModuleSources.has(moduleId)) {
1485
- continue;
1486
- }
1487
-
1488
- // Check if this is an external official module - skip cache for those
1489
- const isExternal = await this.moduleManager.isExternalModule(moduleId);
1490
- if (isExternal) {
1491
- // External modules are handled via cloneExternalModule, not from cache
1492
- continue;
1493
- }
1494
-
1495
- const cachedPath = path.join(cacheDir, moduleId);
1496
-
1497
- // Check if this is actually a custom module (has module.yaml)
1498
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
1499
- if (await fs.pathExists(moduleYamlPath)) {
1500
- customModuleSources.set(moduleId, {
1501
- id: moduleId,
1502
- name: moduleId,
1503
- sourcePath: path.join('_config', 'custom', moduleId), // Relative path
1504
- cached: true,
1505
- });
1506
- }
1507
- }
1508
- }
1509
- }
1510
-
1511
- if (customModuleSources.size > 0) {
1512
- spinner.stop('Update analysis complete');
1513
- await prompts.log.warn('Checking custom module sources before update...');
1514
-
1515
- const projectRoot = getProjectRoot();
1516
- await this.handleMissingCustomSources(
1517
- customModuleSources,
1518
- xiaomaDir,
1519
- projectRoot,
1520
- 'update',
1521
- existingInstall.modules.map((m) => m.id),
1522
- config.skipPrompts || false,
1523
- );
1524
-
1525
- spinner.start('Preparing update...');
1526
- }
1527
-
1528
- if (config.dryRun) {
1529
- spinner.stop('Dry run analysis complete');
1530
- let dryRunContent = `Current version: ${currentVersion}\n`;
1531
- dryRunContent += `New version: ${newVersion}\n`;
1532
- dryRunContent += `Core: ${existingInstall.hasCore ? 'Will be updated' : 'Not installed'}`;
1533
-
1534
- if (existingInstall.modules.length > 0) {
1535
- dryRunContent += '\n\nModules to update:';
1536
- for (const mod of existingInstall.modules) {
1537
- dryRunContent += `\n - ${mod.id}`;
1538
- }
1539
- }
1540
- await prompts.note(dryRunContent, 'Update Preview (Dry Run)');
1541
- return;
1542
- }
1543
-
1544
- // Perform actual update
1545
- if (existingInstall.hasCore) {
1546
- spinner.message('Updating core...');
1547
- await this.updateCore(xiaomaDir, config.force);
1548
- }
1549
-
1550
- for (const module of existingInstall.modules) {
1551
- spinner.message(`Updating module: ${module.id}...`);
1552
- await this.moduleManager.update(module.id, xiaomaDir, config.force, { installer: this });
1553
- }
1554
-
1555
- // Update manifest
1556
- spinner.message('Updating manifest...');
1557
- await this.manifest.update(xiaomaDir, {
1558
- version: newVersion,
1559
- updateDate: new Date().toISOString(),
1560
- });
1561
-
1562
- spinner.stop('Update complete');
1563
- return { success: true };
1564
- } catch (error) {
1565
- spinner.error('Update failed');
1566
- throw error;
1567
- }
1568
- }
1569
-
1570
- /**
1571
- * Get installation status
1572
- */
1573
- async getStatus(directory) {
1574
- const projectDir = path.resolve(directory);
1575
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
1576
- return await this.detector.detect(xiaomaDir);
1577
- }
1578
-
1579
- /**
1580
- * Get available modules
1581
- */
1582
- async getAvailableModules() {
1583
- return await this.moduleManager.listAvailable();
1584
- }
1585
-
1586
- /**
1587
- * Uninstall XiaoMa with selective removal options
1588
- * @param {string} directory - Project directory
1589
- * @param {Object} options - Uninstall options
1590
- * @param {boolean} [options.removeModules=true] - Remove _xiaoma/ directory
1591
- * @param {boolean} [options.removeIdeConfigs=true] - Remove IDE configurations
1592
- * @param {boolean} [options.removeOutputFolder=false] - Remove user artifacts output folder
1593
- * @returns {Object} Result with success status and removed components
1594
- */
1595
- async uninstall(directory, options = {}) {
1596
- const projectDir = path.resolve(directory);
1597
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
1598
-
1599
- if (!(await fs.pathExists(xiaomaDir))) {
1600
- return { success: false, reason: 'not-installed' };
1601
- }
1602
-
1603
- // 1. DETECT: Read state BEFORE deleting anything
1604
- const existingInstall = await this.detector.detect(xiaomaDir);
1605
- const outputFolder = await this._readOutputFolder(xiaomaDir);
1606
-
1607
- const removed = { modules: false, ideConfigs: false, outputFolder: false };
1608
-
1609
- // 2. IDE CLEANUP (before _xiaoma/ deletion so configs are accessible)
1610
- if (options.removeIdeConfigs !== false) {
1611
- await this.uninstallIdeConfigs(projectDir, existingInstall, { silent: options.silent });
1612
- removed.ideConfigs = true;
1613
- }
1614
-
1615
- // 3. OUTPUT FOLDER (only if explicitly requested)
1616
- if (options.removeOutputFolder === true && outputFolder) {
1617
- removed.outputFolder = await this.uninstallOutputFolder(projectDir, outputFolder);
1618
- }
1619
-
1620
- // 4. XiaoMa DIRECTORY (last, after everything that needs it)
1621
- if (options.removeModules !== false) {
1622
- removed.modules = await this.uninstallModules(projectDir);
1623
- }
1624
-
1625
- return { success: true, removed, version: existingInstall.version };
1626
- }
1627
-
1628
- /**
1629
- * Uninstall IDE configurations only
1630
- * @param {string} projectDir - Project directory
1631
- * @param {Object} existingInstall - Detection result from detector.detect()
1632
- * @param {Object} [options] - Options (e.g. { silent: true })
1633
- * @returns {Promise<Object>} Results from IDE cleanup
1634
- */
1635
- async uninstallIdeConfigs(projectDir, existingInstall, options = {}) {
1636
- await this.ideManager.ensureInitialized();
1637
- const cleanupOptions = { isUninstall: true, silent: options.silent };
1638
- const ideList = existingInstall.ides || [];
1639
- if (ideList.length > 0) {
1640
- return this.ideManager.cleanupByList(projectDir, ideList, cleanupOptions);
1641
- }
1642
- return this.ideManager.cleanup(projectDir, cleanupOptions);
1643
- }
1644
-
1645
- /**
1646
- * Remove user artifacts output folder
1647
- * @param {string} projectDir - Project directory
1648
- * @param {string} outputFolder - Output folder name (relative)
1649
- * @returns {Promise<boolean>} Whether the folder was removed
1650
- */
1651
- async uninstallOutputFolder(projectDir, outputFolder) {
1652
- if (!outputFolder) return false;
1653
- const resolvedProject = path.resolve(projectDir);
1654
- const outputPath = path.resolve(resolvedProject, outputFolder);
1655
- if (!outputPath.startsWith(resolvedProject + path.sep)) {
1656
- return false;
1657
- }
1658
- if (await fs.pathExists(outputPath)) {
1659
- await fs.remove(outputPath);
1660
- return true;
1661
- }
1662
- return false;
1663
- }
1664
-
1665
- /**
1666
- * Remove the _xiaoma/ directory
1667
- * @param {string} projectDir - Project directory
1668
- * @returns {Promise<boolean>} Whether the directory was removed
1669
- */
1670
- async uninstallModules(projectDir) {
1671
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
1672
- if (await fs.pathExists(xiaomaDir)) {
1673
- await fs.remove(xiaomaDir);
1674
- return true;
1675
- }
1676
- return false;
1677
- }
1678
-
1679
- /**
1680
- * Get the configured output folder name for a project
1681
- * Resolves xiaomaDir internally from projectDir
1682
- * @param {string} projectDir - Project directory
1683
- * @returns {string} Output folder name (relative, default: '_xiaoma-output')
1684
- */
1685
- async getOutputFolder(projectDir) {
1686
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
1687
- return this._readOutputFolder(xiaomaDir);
1688
- }
1689
-
1690
- /**
1691
- * Read the output_folder setting from module config files
1692
- * Checks xmc/config.yaml first, then other module configs
1693
- * @param {string} xiaomaDir - XiaoMa installation directory
1694
- * @returns {string} Output folder path or default
1695
- */
1696
- async _readOutputFolder(xiaomaDir) {
1697
- const yaml = require('yaml');
1698
-
1699
- // Check xmc/config.yaml first (most common)
1700
- const xmcConfigPath = path.join(xiaomaDir, 'xmc', 'config.yaml');
1701
- if (await fs.pathExists(xmcConfigPath)) {
1702
- try {
1703
- const content = await fs.readFile(xmcConfigPath, 'utf8');
1704
- const config = yaml.parse(content);
1705
- if (config && config.output_folder) {
1706
- // Strip {project-root}/ prefix if present
1707
- return config.output_folder.replace(/^\{project-root\}[/\\]/, '');
1708
- }
1709
- } catch {
1710
- // Fall through to other modules
1711
- }
1712
- }
1713
-
1714
- // Scan other module config.yaml files
1715
- try {
1716
- const entries = await fs.readdir(xiaomaDir, { withFileTypes: true });
1717
- for (const entry of entries) {
1718
- if (!entry.isDirectory() || entry.name === 'xmc' || entry.name.startsWith('_')) continue;
1719
- const configPath = path.join(xiaomaDir, entry.name, 'config.yaml');
1720
- if (await fs.pathExists(configPath)) {
1721
- try {
1722
- const content = await fs.readFile(configPath, 'utf8');
1723
- const config = yaml.parse(content);
1724
- if (config && config.output_folder) {
1725
- return config.output_folder.replace(/^\{project-root\}[/\\]/, '');
1726
- }
1727
- } catch {
1728
- // Continue scanning
1729
- }
1730
- }
1731
- }
1732
- } catch {
1733
- // Directory scan failed
1734
- }
1735
-
1736
- // Default fallback
1737
- return '_xiaoma-output';
1738
- }
1739
-
1740
- /**
1741
- * Private: Create directory structure
1742
- */
1743
- /**
1744
- * Merge all module-help.csv files into a single xiaoma-help.csv
1745
- * Scans all installed modules for module-help.csv and merges them
1746
- * Enriches agent info from agent-manifest.csv
1747
- * Output is written to _xiaoma/_config/xiaoma-help.csv
1748
- * @param {string} xiaomaDir - XiaoMa installation directory
1749
- */
1750
- async mergeModuleHelpCatalogs(xiaomaDir) {
1751
- const allRows = [];
1752
- const headerRow =
1753
- 'module,phase,name,code,sequence,workflow-file,command,required,agent-name,agent-command,agent-display-name,agent-title,options,description,output-location,outputs';
1754
-
1755
- // Load agent manifest for agent info lookup
1756
- const agentManifestPath = path.join(xiaomaDir, '_config', 'agent-manifest.csv');
1757
- const agentInfo = new Map(); // agent-name -> {command, displayName, title+icon}
1758
-
1759
- if (await fs.pathExists(agentManifestPath)) {
1760
- const manifestContent = await fs.readFile(agentManifestPath, 'utf8');
1761
- const lines = manifestContent.split('\n').filter((line) => line.trim());
1762
-
1763
- for (const line of lines) {
1764
- if (line.startsWith('name,')) continue; // Skip header
1765
-
1766
- const cols = line.split(',');
1767
- if (cols.length >= 4) {
1768
- const agentName = cols[0].replaceAll('"', '').trim();
1769
- const displayName = cols[1].replaceAll('"', '').trim();
1770
- const title = cols[2].replaceAll('"', '').trim();
1771
- const icon = cols[3].replaceAll('"', '').trim();
1772
- const module = cols[10] ? cols[10].replaceAll('"', '').trim() : '';
1773
-
1774
- // Build agent command: xiaoma:module:agent:name
1775
- const agentCommand = module ? `xiaoma:${module}:agent:${agentName}` : `xiaoma:agent:${agentName}`;
1776
-
1777
- agentInfo.set(agentName, {
1778
- command: agentCommand,
1779
- displayName: displayName || agentName,
1780
- title: icon && title ? `${icon} ${title}` : title || agentName,
1781
- });
1782
- }
1783
- }
1784
- }
1785
-
1786
- // Get all installed module directories
1787
- const entries = await fs.readdir(xiaomaDir, { withFileTypes: true });
1788
- const installedModules = entries
1789
- .filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs' && entry.name !== '_memory')
1790
- .map((entry) => entry.name);
1791
-
1792
- // Add core module to scan (it's installed at root level as _config, but we check src/core)
1793
- const coreModulePath = getSourcePath('core');
1794
- const modulePaths = new Map();
1795
-
1796
- // Map all module source paths
1797
- if (await fs.pathExists(coreModulePath)) {
1798
- modulePaths.set('core', coreModulePath);
1799
- }
1800
-
1801
- // Map installed module paths
1802
- for (const moduleName of installedModules) {
1803
- const modulePath = path.join(xiaomaDir, moduleName);
1804
- modulePaths.set(moduleName, modulePath);
1805
- }
1806
-
1807
- // Scan each module for module-help.csv
1808
- for (const [moduleName, modulePath] of modulePaths) {
1809
- const helpFilePath = path.join(modulePath, 'module-help.csv');
1810
-
1811
- if (await fs.pathExists(helpFilePath)) {
1812
- try {
1813
- const content = await fs.readFile(helpFilePath, 'utf8');
1814
- const lines = content.split('\n').filter((line) => line.trim() && !line.startsWith('#'));
1815
-
1816
- for (const line of lines) {
1817
- // Skip header row
1818
- if (line.startsWith('module,')) {
1819
- continue;
1820
- }
1821
-
1822
- // Parse the line - handle quoted fields with commas
1823
- const columns = this.parseCSVLine(line);
1824
- if (columns.length >= 12) {
1825
- // Map old schema to new schema
1826
- // Old: module,phase,name,code,sequence,workflow-file,command,required,agent,options,description,output-location,outputs
1827
- // New: module,phase,name,code,sequence,workflow-file,command,required,agent-name,agent-command,agent-display-name,agent-title,options,description,output-location,outputs
1828
-
1829
- const [
1830
- module,
1831
- phase,
1832
- name,
1833
- code,
1834
- sequence,
1835
- workflowFile,
1836
- command,
1837
- required,
1838
- agentName,
1839
- options,
1840
- description,
1841
- outputLocation,
1842
- outputs,
1843
- ] = columns;
1844
-
1845
- // If module column is empty, set it to this module's name (except for core which stays empty for universal tools)
1846
- const finalModule = (!module || module.trim() === '') && moduleName !== 'core' ? moduleName : module || '';
1847
-
1848
- // Lookup agent info
1849
- const cleanAgentName = agentName ? agentName.trim() : '';
1850
- const agentData = agentInfo.get(cleanAgentName) || { command: '', displayName: '', title: '' };
1851
-
1852
- // Build new row with agent info
1853
- const newRow = [
1854
- finalModule,
1855
- phase || '',
1856
- name || '',
1857
- code || '',
1858
- sequence || '',
1859
- workflowFile || '',
1860
- command || '',
1861
- required || 'false',
1862
- cleanAgentName,
1863
- agentData.command,
1864
- agentData.displayName,
1865
- agentData.title,
1866
- options || '',
1867
- description || '',
1868
- outputLocation || '',
1869
- outputs || '',
1870
- ];
1871
-
1872
- allRows.push(newRow.map((c) => this.escapeCSVField(c)).join(','));
1873
- }
1874
- }
1875
-
1876
- if (process.env.XiaoMa_VERBOSE_INSTALL === 'true') {
1877
- await prompts.log.message(` Merged module-help from: ${moduleName}`);
1878
- }
1879
- } catch (error) {
1880
- await prompts.log.warn(` Warning: Failed to read module-help.csv from ${moduleName}: ${error.message}`);
1881
- }
1882
- }
1883
- }
1884
-
1885
- // Sort by module, then phase, then sequence
1886
- allRows.sort((a, b) => {
1887
- const colsA = this.parseCSVLine(a);
1888
- const colsB = this.parseCSVLine(b);
1889
-
1890
- // Module comparison (empty module/universal tools come first)
1891
- const moduleA = (colsA[0] || '').toLowerCase();
1892
- const moduleB = (colsB[0] || '').toLowerCase();
1893
- if (moduleA !== moduleB) {
1894
- return moduleA.localeCompare(moduleB);
1895
- }
1896
-
1897
- // Phase comparison
1898
- const phaseA = colsA[1] || '';
1899
- const phaseB = colsB[1] || '';
1900
- if (phaseA !== phaseB) {
1901
- return phaseA.localeCompare(phaseB);
1902
- }
1903
-
1904
- // Sequence comparison
1905
- const seqA = parseInt(colsA[4] || '0', 10);
1906
- const seqB = parseInt(colsB[4] || '0', 10);
1907
- return seqA - seqB;
1908
- });
1909
-
1910
- // Write merged catalog
1911
- const outputDir = path.join(xiaomaDir, '_config');
1912
- await fs.ensureDir(outputDir);
1913
- const outputPath = path.join(outputDir, 'xiaoma-help.csv');
1914
-
1915
- const mergedContent = [headerRow, ...allRows].join('\n');
1916
- await fs.writeFile(outputPath, mergedContent, 'utf8');
1917
-
1918
- // Track the installed file
1919
- this.installedFiles.add(outputPath);
1920
-
1921
- if (process.env.XiaoMa_VERBOSE_INSTALL === 'true') {
1922
- await prompts.log.message(` Generated xiaoma-help.csv: ${allRows.length} workflows`);
1923
- }
1924
- }
1925
-
1926
- /**
1927
- * Parse a CSV line, handling quoted fields
1928
- * @param {string} line - CSV line to parse
1929
- * @returns {Array} Array of field values
1930
- */
1931
- parseCSVLine(line) {
1932
- const result = [];
1933
- let current = '';
1934
- let inQuotes = false;
1935
-
1936
- for (let i = 0; i < line.length; i++) {
1937
- const char = line[i];
1938
- const nextChar = line[i + 1];
1939
-
1940
- if (char === '"') {
1941
- if (inQuotes && nextChar === '"') {
1942
- // Escaped quote
1943
- current += '"';
1944
- i++; // Skip next quote
1945
- } else {
1946
- // Toggle quote mode
1947
- inQuotes = !inQuotes;
1948
- }
1949
- } else if (char === ',' && !inQuotes) {
1950
- result.push(current);
1951
- current = '';
1952
- } else {
1953
- current += char;
1954
- }
1955
- }
1956
- result.push(current);
1957
- return result;
1958
- }
1959
-
1960
- /**
1961
- * Escape a CSV field if it contains special characters
1962
- * @param {string} field - Field value to escape
1963
- * @returns {string} Escaped field
1964
- */
1965
- escapeCSVField(field) {
1966
- if (field === null || field === undefined) {
1967
- return '';
1968
- }
1969
- const str = String(field);
1970
- // If field contains comma, quote, or newline, wrap in quotes and escape inner quotes
1971
- if (str.includes(',') || str.includes('"') || str.includes('\n')) {
1972
- return `"${str.replaceAll('"', '""')}"`;
1973
- }
1974
- return str;
1975
- }
1976
-
1977
- async createDirectoryStructure(xiaomaDir) {
1978
- await fs.ensureDir(xiaomaDir);
1979
- await fs.ensureDir(path.join(xiaomaDir, '_config'));
1980
- await fs.ensureDir(path.join(xiaomaDir, '_config', 'agents'));
1981
- await fs.ensureDir(path.join(xiaomaDir, '_config', 'custom'));
1982
- }
1983
-
1984
- /**
1985
- * Generate clean config.yaml files for each installed module
1986
- * @param {string} xiaomaDir - XiaoMa installation directory
1987
- * @param {Object} moduleConfigs - Collected configuration values
1988
- */
1989
- async generateModuleConfigs(xiaomaDir, moduleConfigs) {
1990
- const yaml = require('yaml');
1991
-
1992
- // Extract core config values to share with other modules
1993
- const coreConfig = moduleConfigs.core || {};
1994
-
1995
- // Get all installed module directories
1996
- const entries = await fs.readdir(xiaomaDir, { withFileTypes: true });
1997
- const installedModules = entries
1998
- .filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs')
1999
- .map((entry) => entry.name);
2000
-
2001
- // Generate config.yaml for each installed module
2002
- for (const moduleName of installedModules) {
2003
- const modulePath = path.join(xiaomaDir, moduleName);
2004
-
2005
- // Get module-specific config or use empty object if none
2006
- const config = moduleConfigs[moduleName] || {};
2007
-
2008
- if (await fs.pathExists(modulePath)) {
2009
- const configPath = path.join(modulePath, 'config.yaml');
2010
-
2011
- // Create header
2012
- const packageJson = require(path.join(getProjectRoot(), 'package.json'));
2013
- const header = `# ${moduleName.toUpperCase()} Module Configuration
2014
- # Generated by XiaoMa installer
2015
- # Version: ${packageJson.version}
2016
- # Date: ${new Date().toISOString()}
2017
-
2018
- `;
2019
-
2020
- // For non-core modules, add core config values directly
2021
- let finalConfig = { ...config };
2022
- let coreSection = '';
2023
-
2024
- if (moduleName !== 'core' && coreConfig && Object.keys(coreConfig).length > 0) {
2025
- // Add core values directly to the module config
2026
- // These will be available for reference in the module
2027
- finalConfig = {
2028
- ...config,
2029
- ...coreConfig, // Spread core config values directly into the module config
2030
- };
2031
-
2032
- // Create a comment section to identify core values
2033
- coreSection = '\n# Core Configuration Values\n';
2034
- }
2035
-
2036
- // Clean the config to remove any non-serializable values (like functions)
2037
- const cleanConfig = structuredClone(finalConfig);
2038
-
2039
- // Convert config to YAML
2040
- let yamlContent = yaml.stringify(cleanConfig, {
2041
- indent: 2,
2042
- lineWidth: 0,
2043
- minContentWidth: 0,
2044
- });
2045
-
2046
- // If we have core values, reorganize the YAML to group them with their comment
2047
- if (coreSection && moduleName !== 'core') {
2048
- // Split the YAML into lines
2049
- const lines = yamlContent.split('\n');
2050
- const moduleConfigLines = [];
2051
- const coreConfigLines = [];
2052
-
2053
- // Separate module-specific and core config lines
2054
- for (const line of lines) {
2055
- const key = line.split(':')[0].trim();
2056
- if (Object.prototype.hasOwnProperty.call(coreConfig, key)) {
2057
- coreConfigLines.push(line);
2058
- } else {
2059
- moduleConfigLines.push(line);
2060
- }
2061
- }
2062
-
2063
- // Rebuild YAML with module config first, then core config with comment
2064
- yamlContent = moduleConfigLines.join('\n');
2065
- if (coreConfigLines.length > 0) {
2066
- yamlContent += coreSection + coreConfigLines.join('\n');
2067
- }
2068
- }
2069
-
2070
- // Write the clean config file with POSIX-compliant final newline
2071
- const content = header + yamlContent;
2072
- await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
2073
-
2074
- // Track the config file in installedFiles
2075
- this.installedFiles.add(configPath);
2076
- }
2077
- }
2078
- }
2079
-
2080
- /**
2081
- * Install core with resolved dependencies
2082
- * @param {string} xiaomaDir - XiaoMa installation directory
2083
- * @param {Object} coreFiles - Core files to install
2084
- */
2085
- async installCoreWithDependencies(xiaomaDir, coreFiles) {
2086
- const sourcePath = getModulePath('core');
2087
- const targetPath = path.join(xiaomaDir, 'core');
2088
- await this.installCore(xiaomaDir);
2089
- }
2090
-
2091
- /**
2092
- * Install module with resolved dependencies
2093
- * @param {string} moduleName - Module name
2094
- * @param {string} xiaomaDir - XiaoMa installation directory
2095
- * @param {Object} moduleFiles - Module files to install
2096
- */
2097
- async installModuleWithDependencies(moduleName, xiaomaDir, moduleFiles) {
2098
- // Get module configuration for conditional installation
2099
- const moduleConfig = this.configCollector.collectedConfig[moduleName] || {};
2100
-
2101
- // Use existing module manager for full installation with file tracking
2102
- // Note: Module-specific installers are called separately after IDE setup
2103
- await this.moduleManager.install(
2104
- moduleName,
2105
- xiaomaDir,
2106
- (filePath) => {
2107
- this.installedFiles.add(filePath);
2108
- },
2109
- {
2110
- skipModuleInstaller: true, // We'll run it later after IDE setup
2111
- moduleConfig: moduleConfig, // Pass module config for conditional filtering
2112
- installer: this,
2113
- silent: true,
2114
- },
2115
- );
2116
-
2117
- // Process agent files to build YAML agents and create customize templates
2118
- const modulePath = path.join(xiaomaDir, moduleName);
2119
- await this.processAgentFiles(modulePath, moduleName);
2120
-
2121
- // Dependencies are already included in full module install
2122
- }
2123
-
2124
- /**
2125
- * Install partial module (only dependencies needed by other modules)
2126
- */
2127
- async installPartialModule(moduleName, xiaomaDir, files) {
2128
- const sourceBase = getModulePath(moduleName);
2129
- const targetBase = path.join(xiaomaDir, moduleName);
2130
-
2131
- // Create module directory
2132
- await fs.ensureDir(targetBase);
2133
-
2134
- // Copy only the required dependency files
2135
- if (files.agents && files.agents.length > 0) {
2136
- const agentsDir = path.join(targetBase, 'agents');
2137
- await fs.ensureDir(agentsDir);
2138
-
2139
- for (const agentPath of files.agents) {
2140
- const fileName = path.basename(agentPath);
2141
- const sourcePath = path.join(sourceBase, 'agents', fileName);
2142
- const targetPath = path.join(agentsDir, fileName);
2143
-
2144
- if (await fs.pathExists(sourcePath)) {
2145
- await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
2146
- this.installedFiles.add(targetPath);
2147
- }
2148
- }
2149
- }
2150
-
2151
- if (files.tasks && files.tasks.length > 0) {
2152
- const tasksDir = path.join(targetBase, 'tasks');
2153
- await fs.ensureDir(tasksDir);
2154
-
2155
- for (const taskPath of files.tasks) {
2156
- const fileName = path.basename(taskPath);
2157
- const sourcePath = path.join(sourceBase, 'tasks', fileName);
2158
- const targetPath = path.join(tasksDir, fileName);
2159
-
2160
- if (await fs.pathExists(sourcePath)) {
2161
- await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
2162
- this.installedFiles.add(targetPath);
2163
- }
2164
- }
2165
- }
2166
-
2167
- if (files.tools && files.tools.length > 0) {
2168
- const toolsDir = path.join(targetBase, 'tools');
2169
- await fs.ensureDir(toolsDir);
2170
-
2171
- for (const toolPath of files.tools) {
2172
- const fileName = path.basename(toolPath);
2173
- const sourcePath = path.join(sourceBase, 'tools', fileName);
2174
- const targetPath = path.join(toolsDir, fileName);
2175
-
2176
- if (await fs.pathExists(sourcePath)) {
2177
- await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
2178
- this.installedFiles.add(targetPath);
2179
- }
2180
- }
2181
- }
2182
-
2183
- if (files.templates && files.templates.length > 0) {
2184
- const templatesDir = path.join(targetBase, 'templates');
2185
- await fs.ensureDir(templatesDir);
2186
-
2187
- for (const templatePath of files.templates) {
2188
- const fileName = path.basename(templatePath);
2189
- const sourcePath = path.join(sourceBase, 'templates', fileName);
2190
- const targetPath = path.join(templatesDir, fileName);
2191
-
2192
- if (await fs.pathExists(sourcePath)) {
2193
- await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
2194
- this.installedFiles.add(targetPath);
2195
- }
2196
- }
2197
- }
2198
-
2199
- if (files.data && files.data.length > 0) {
2200
- for (const dataPath of files.data) {
2201
- // Preserve directory structure for data files
2202
- const relative = path.relative(sourceBase, dataPath);
2203
- const targetPath = path.join(targetBase, relative);
2204
-
2205
- await fs.ensureDir(path.dirname(targetPath));
2206
-
2207
- if (await fs.pathExists(dataPath)) {
2208
- await this.copyFileWithPlaceholderReplacement(dataPath, targetPath);
2209
- this.installedFiles.add(targetPath);
2210
- }
2211
- }
2212
- }
2213
-
2214
- // Create a marker file to indicate this is a partial installation
2215
- const markerPath = path.join(targetBase, '.partial');
2216
- await fs.writeFile(
2217
- markerPath,
2218
- `This module contains only dependencies required by other modules.\nInstalled: ${new Date().toISOString()}\n`,
2219
- );
2220
- }
2221
-
2222
- /**
2223
- * Private: Install core
2224
- * @param {string} xiaomaDir - XiaoMa installation directory
2225
- */
2226
- async installCore(xiaomaDir) {
2227
- const sourcePath = getModulePath('core');
2228
- const targetPath = path.join(xiaomaDir, 'core');
2229
-
2230
- // Copy core files (skip .agent.yaml files like modules do)
2231
- await this.copyCoreFiles(sourcePath, targetPath);
2232
-
2233
- // Compile agents using the same compiler as modules
2234
- const { ModuleManager } = require('../modules/manager');
2235
- const moduleManager = new ModuleManager();
2236
- await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', xiaomaDir, this);
2237
-
2238
- // Process agent files to inject activation block
2239
- await this.processAgentFiles(targetPath, 'core');
2240
- }
2241
-
2242
- /**
2243
- * Copy core files (similar to copyModuleWithFiltering but for core)
2244
- * @param {string} sourcePath - Source path
2245
- * @param {string} targetPath - Target path
2246
- */
2247
- async copyCoreFiles(sourcePath, targetPath) {
2248
- // Get all files in source
2249
- const files = await this.getFileList(sourcePath);
2250
-
2251
- for (const file of files) {
2252
- // Skip sub-modules directory - these are IDE-specific and handled separately
2253
- if (file.startsWith('sub-modules/')) {
2254
- continue;
2255
- }
2256
-
2257
- // Skip sidecar directories - they are handled separately during agent compilation
2258
- if (
2259
- path
2260
- .dirname(file)
2261
- .split('/')
2262
- .some((dir) => dir.toLowerCase().includes('sidecar'))
2263
- ) {
2264
- continue;
2265
- }
2266
-
2267
- // Skip module.yaml at root - it's only needed at install time
2268
- if (file === 'module.yaml') {
2269
- continue;
2270
- }
2271
-
2272
- // Skip config.yaml templates - we'll generate clean ones with actual values
2273
- if (file === 'config.yaml' || file.endsWith('/config.yaml') || file === 'custom.yaml' || file.endsWith('/custom.yaml')) {
2274
- continue;
2275
- }
2276
-
2277
- // Skip .agent.yaml files - they will be compiled separately
2278
- if (file.endsWith('.agent.yaml')) {
2279
- continue;
2280
- }
2281
-
2282
- const sourceFile = path.join(sourcePath, file);
2283
- const targetFile = path.join(targetPath, file);
2284
-
2285
- // Check if this is an agent file
2286
- if (file.startsWith('agents/') && file.endsWith('.md')) {
2287
- // Read the file to check for localskip
2288
- const content = await fs.readFile(sourceFile, 'utf8');
2289
-
2290
- // Check for localskip="true" in the agent tag
2291
- const agentMatch = content.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
2292
- if (agentMatch) {
2293
- await prompts.log.message(` Skipping web-only agent: ${path.basename(file)}`);
2294
- continue; // Skip this agent
2295
- }
2296
- }
2297
-
2298
- // Copy the file with placeholder replacement
2299
- await fs.ensureDir(path.dirname(targetFile));
2300
- await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile);
2301
-
2302
- // Track the installed file
2303
- this.installedFiles.add(targetFile);
2304
- }
2305
- }
2306
-
2307
- /**
2308
- * Get list of all files in a directory recursively
2309
- * @param {string} dir - Directory path
2310
- * @param {string} baseDir - Base directory for relative paths
2311
- * @returns {Array} List of relative file paths
2312
- */
2313
- async getFileList(dir, baseDir = dir) {
2314
- const files = [];
2315
- const entries = await fs.readdir(dir, { withFileTypes: true });
2316
-
2317
- for (const entry of entries) {
2318
- const fullPath = path.join(dir, entry.name);
2319
-
2320
- if (entry.isDirectory()) {
2321
- const subFiles = await this.getFileList(fullPath, baseDir);
2322
- files.push(...subFiles);
2323
- } else {
2324
- files.push(path.relative(baseDir, fullPath));
2325
- }
2326
- }
2327
-
2328
- return files;
2329
- }
2330
-
2331
- /**
2332
- * Process agent files to build YAML agents and inject activation blocks
2333
- * @param {string} modulePath - Path to module in xiaoma/ installation
2334
- * @param {string} moduleName - Module name
2335
- */
2336
- async processAgentFiles(modulePath, moduleName) {
2337
- const agentsPath = path.join(modulePath, 'agents');
2338
-
2339
- // Check if agents directory exists
2340
- if (!(await fs.pathExists(agentsPath))) {
2341
- return; // No agents to process
2342
- }
2343
-
2344
- // Determine project directory (parent of xiaoma/ directory)
2345
- const xiaomaDir = path.dirname(modulePath);
2346
- const cfgAgentsDir = path.join(xiaomaDir, '_config', 'agents');
2347
-
2348
- // Ensure _config/agents directory exists
2349
- await fs.ensureDir(cfgAgentsDir);
2350
-
2351
- // Get all agent files
2352
- const agentFiles = await fs.readdir(agentsPath);
2353
-
2354
- for (const agentFile of agentFiles) {
2355
- // Skip .agent.yaml files - they should already be compiled by compileModuleAgents
2356
- if (agentFile.endsWith('.agent.yaml')) {
2357
- continue;
2358
- }
2359
-
2360
- // Only process .md files (already compiled from YAML)
2361
- if (!agentFile.endsWith('.md')) {
2362
- continue;
2363
- }
2364
-
2365
- const agentName = agentFile.replace('.md', '');
2366
- const mdPath = path.join(agentsPath, agentFile);
2367
- const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
2368
-
2369
- // For .md files that are already compiled, we don't need to do much
2370
- // Just ensure the customize template exists
2371
- if (!(await fs.pathExists(customizePath))) {
2372
- const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
2373
- if (await fs.pathExists(genericTemplatePath)) {
2374
- await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath);
2375
- if (process.env.XiaoMa_VERBOSE_INSTALL === 'true') {
2376
- await prompts.log.message(` Created customize: ${moduleName}-${agentName}.customize.yaml`);
2377
- }
2378
- }
2379
- }
2380
- }
2381
- }
2382
-
2383
- /**
2384
- * Private: Update core
2385
- */
2386
- async updateCore(xiaomaDir, force = false) {
2387
- const sourcePath = getModulePath('core');
2388
- const targetPath = path.join(xiaomaDir, 'core');
2389
-
2390
- if (force) {
2391
- await fs.remove(targetPath);
2392
- await this.installCore(xiaomaDir);
2393
- } else {
2394
- // Selective update - preserve user modifications
2395
- await this.fileOps.syncDirectory(sourcePath, targetPath);
2396
-
2397
- // Recompile agents (#1133)
2398
- const { ModuleManager } = require('../modules/manager');
2399
- const moduleManager = new ModuleManager();
2400
- await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', xiaomaDir, this);
2401
- await this.processAgentFiles(targetPath, 'core');
2402
- }
2403
- }
2404
-
2405
- /**
2406
- * Quick update method - preserves all settings and only prompts for new config fields
2407
- * @param {Object} config - Configuration with directory
2408
- * @returns {Object} Update result
2409
- */
2410
- async quickUpdate(config) {
2411
- const spinner = await prompts.spinner();
2412
- spinner.start('Starting quick update...');
2413
-
2414
- try {
2415
- const projectDir = path.resolve(config.directory);
2416
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
2417
-
2418
- // Check if xiaoma directory exists
2419
- if (!(await fs.pathExists(xiaomaDir))) {
2420
- spinner.stop('No XiaoMa installation found');
2421
- throw new Error(`XiaoMa not installed at ${xiaomaDir}. Use regular install for first-time setup.`);
2422
- }
2423
-
2424
- spinner.message('Detecting installed modules and configuration...');
2425
-
2426
- // Detect existing installation
2427
- const existingInstall = await this.detector.detect(xiaomaDir);
2428
- const installedModules = existingInstall.modules.map((m) => m.id);
2429
- const configuredIdes = existingInstall.ides || [];
2430
- const projectRoot = path.dirname(xiaomaDir);
2431
-
2432
- // Get custom module sources: first from --custom-content (re-cache from source), then from cache
2433
- const customModuleSources = new Map();
2434
- if (config.customContent?.sources?.length > 0) {
2435
- for (const source of config.customContent.sources) {
2436
- if (source.id && source.path && (await fs.pathExists(source.path))) {
2437
- customModuleSources.set(source.id, {
2438
- id: source.id,
2439
- name: source.name || source.id,
2440
- sourcePath: source.path,
2441
- cached: false, // From CLI, will be re-cached
2442
- });
2443
- }
2444
- }
2445
- }
2446
- const cacheDir = path.join(xiaomaDir, '_config', 'custom');
2447
- if (await fs.pathExists(cacheDir)) {
2448
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
2449
-
2450
- for (const cachedModule of cachedModules) {
2451
- const moduleId = cachedModule.name;
2452
- const cachedPath = path.join(cacheDir, moduleId);
2453
-
2454
- // Skip if path doesn't exist (broken symlink, deleted dir) - avoids lstat ENOENT
2455
- if (!(await fs.pathExists(cachedPath))) {
2456
- continue;
2457
- }
2458
- if (!cachedModule.isDirectory()) {
2459
- continue;
2460
- }
2461
-
2462
- // Skip if we already have this module from manifest
2463
- if (customModuleSources.has(moduleId)) {
2464
- continue;
2465
- }
2466
-
2467
- // Check if this is an external official module - skip cache for those
2468
- const isExternal = await this.moduleManager.isExternalModule(moduleId);
2469
- if (isExternal) {
2470
- // External modules are handled via cloneExternalModule, not from cache
2471
- continue;
2472
- }
2473
-
2474
- // Check if this is actually a custom module (has module.yaml)
2475
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
2476
- if (await fs.pathExists(moduleYamlPath)) {
2477
- // For quick update, we always rebuild from cache
2478
- customModuleSources.set(moduleId, {
2479
- id: moduleId,
2480
- name: moduleId, // We'll read the actual name if needed
2481
- sourcePath: cachedPath,
2482
- cached: true, // Flag to indicate this is from cache
2483
- });
2484
- }
2485
- }
2486
- }
2487
-
2488
- // Load saved IDE configurations
2489
- const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(xiaomaDir);
2490
-
2491
- // Get available modules (what we have source for)
2492
- const availableModulesData = await this.moduleManager.listAvailable();
2493
- const availableModules = [...availableModulesData.modules, ...availableModulesData.customModules];
2494
-
2495
- // Add external official modules to available modules
2496
- // These can always be obtained by cloning from their remote URLs
2497
- const { ExternalModuleManager } = require('../modules/external-manager');
2498
- const externalManager = new ExternalModuleManager();
2499
- const externalModules = await externalManager.listAvailable();
2500
- for (const externalModule of externalModules) {
2501
- // Only add if not already in the list and is installed
2502
- if (installedModules.includes(externalModule.code) && !availableModules.some((m) => m.id === externalModule.code)) {
2503
- availableModules.push({
2504
- id: externalModule.code,
2505
- name: externalModule.name,
2506
- isExternal: true,
2507
- fromExternal: true,
2508
- });
2509
- }
2510
- }
2511
-
2512
- // Add custom modules from manifest if their sources exist
2513
- for (const [moduleId, customModule] of customModuleSources) {
2514
- // Use the absolute sourcePath
2515
- const sourcePath = customModule.sourcePath;
2516
-
2517
- // Check if source exists at the recorded path
2518
- if (
2519
- sourcePath &&
2520
- (await fs.pathExists(sourcePath)) && // Add to available modules if not already there
2521
- !availableModules.some((m) => m.id === moduleId)
2522
- ) {
2523
- availableModules.push({
2524
- id: moduleId,
2525
- name: customModule.name || moduleId,
2526
- path: sourcePath,
2527
- isCustom: true,
2528
- fromManifest: true,
2529
- });
2530
- }
2531
- }
2532
-
2533
- // Handle missing custom module sources using shared method
2534
- const customModuleResult = await this.handleMissingCustomSources(
2535
- customModuleSources,
2536
- xiaomaDir,
2537
- projectRoot,
2538
- 'update',
2539
- installedModules,
2540
- config.skipPrompts || false,
2541
- );
2542
-
2543
- const { validCustomModules, keptModulesWithoutSources } = customModuleResult;
2544
-
2545
- const customModulesFromManifest = validCustomModules.map((m) => ({
2546
- ...m,
2547
- isCustom: true,
2548
- hasUpdate: true,
2549
- }));
2550
-
2551
- const allAvailableModules = [...availableModules, ...customModulesFromManifest];
2552
- const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
2553
-
2554
- // Core module is special - never include it in update flow
2555
- const nonCoreInstalledModules = installedModules.filter((id) => id !== 'core');
2556
-
2557
- // Only update modules that are BOTH installed AND available (we have source for)
2558
- const modulesToUpdate = nonCoreInstalledModules.filter((id) => availableModuleIds.has(id));
2559
- const skippedModules = nonCoreInstalledModules.filter((id) => !availableModuleIds.has(id));
2560
-
2561
- // Add custom modules that were kept without sources to the skipped modules
2562
- // This ensures their agents are preserved in the manifest
2563
- for (const keptModule of keptModulesWithoutSources) {
2564
- if (!skippedModules.includes(keptModule)) {
2565
- skippedModules.push(keptModule);
2566
- }
2567
- }
2568
-
2569
- spinner.stop(`Found ${modulesToUpdate.length} module(s) to update and ${configuredIdes.length} configured tool(s)`);
2570
-
2571
- if (skippedModules.length > 0) {
2572
- await prompts.log.warn(`Skipping ${skippedModules.length} module(s) - no source available: ${skippedModules.join(', ')}`);
2573
- }
2574
-
2575
- // Load existing configs and collect new fields (if any)
2576
- await prompts.log.info('Checking for new configuration options...');
2577
- await this.configCollector.loadExistingConfig(projectDir);
2578
-
2579
- let promptedForNewFields = false;
2580
-
2581
- // Check core config for new fields
2582
- const corePrompted = await this.configCollector.collectModuleConfigQuick('core', projectDir, true);
2583
- if (corePrompted) {
2584
- promptedForNewFields = true;
2585
- }
2586
-
2587
- // Check each module we're updating for new fields (NOT skipped modules)
2588
- for (const moduleName of modulesToUpdate) {
2589
- const modulePrompted = await this.configCollector.collectModuleConfigQuick(moduleName, projectDir, true);
2590
- if (modulePrompted) {
2591
- promptedForNewFields = true;
2592
- }
2593
- }
2594
-
2595
- if (!promptedForNewFields) {
2596
- await prompts.log.success('All configuration is up to date, no new options to configure');
2597
- }
2598
-
2599
- // Add metadata
2600
- this.configCollector.collectedConfig._meta = {
2601
- version: require(path.join(getProjectRoot(), 'package.json')).version,
2602
- installDate: new Date().toISOString(),
2603
- lastModified: new Date().toISOString(),
2604
- };
2605
-
2606
- // Build the config object for the installer
2607
- const installConfig = {
2608
- directory: projectDir,
2609
- installCore: true,
2610
- modules: modulesToUpdate, // Only update modules we have source for
2611
- ides: configuredIdes,
2612
- skipIde: configuredIdes.length === 0,
2613
- coreConfig: this.configCollector.collectedConfig.core,
2614
- actionType: 'install', // Use regular install flow
2615
- _quickUpdate: true, // Flag to skip certain prompts
2616
- _preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
2617
- _savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
2618
- _customModuleSources: customModuleSources, // Pass custom module sources for updates
2619
- _existingModules: installedModules, // Pass all installed modules for manifest generation
2620
- customContent: config.customContent, // Pass through for re-caching from source
2621
- };
2622
-
2623
- // Call the standard install method
2624
- const result = await this.install(installConfig);
2625
-
2626
- // Only succeed the spinner if it's still spinning
2627
- // (install method might have stopped it if folder name changed)
2628
- if (spinner.isSpinning) {
2629
- spinner.stop('Quick update complete!');
2630
- }
2631
-
2632
- return {
2633
- success: true,
2634
- moduleCount: modulesToUpdate.length + 1, // +1 for core
2635
- hadNewFields: promptedForNewFields,
2636
- modules: ['core', ...modulesToUpdate],
2637
- skippedModules: skippedModules,
2638
- ides: configuredIdes,
2639
- };
2640
- } catch (error) {
2641
- spinner.error('Quick update failed');
2642
- throw error;
2643
- }
2644
- }
2645
-
2646
- /**
2647
- * Compile agents with customizations only
2648
- * @param {Object} config - Configuration with directory
2649
- * @returns {Object} Compilation result
2650
- */
2651
- async compileAgents(config) {
2652
- // Using @clack prompts
2653
- const { ModuleManager } = require('../modules/manager');
2654
- const { getSourcePath } = require('../../../lib/project-root');
2655
-
2656
- const spinner = await prompts.spinner();
2657
- spinner.start('Recompiling agents with customizations...');
2658
-
2659
- try {
2660
- const projectDir = path.resolve(config.directory);
2661
- const { xiaomaDir } = await this.findXiaomaDir(projectDir);
2662
-
2663
- // Check if xiaoma directory exists
2664
- if (!(await fs.pathExists(xiaomaDir))) {
2665
- spinner.stop('No XiaoMa installation found');
2666
- throw new Error(`XiaoMa not installed at ${xiaomaDir}. Use regular install for first-time setup.`);
2667
- }
2668
-
2669
- // Detect existing installation
2670
- const existingInstall = await this.detector.detect(xiaomaDir);
2671
- const installedModules = existingInstall.modules.map((m) => m.id);
2672
-
2673
- // Initialize module manager
2674
- const moduleManager = new ModuleManager();
2675
- moduleManager.setXiaomaFolderName(path.basename(xiaomaDir));
2676
-
2677
- let totalAgentCount = 0;
2678
-
2679
- // Get custom module sources from cache
2680
- const customModuleSources = new Map();
2681
- const cacheDir = path.join(xiaomaDir, '_config', 'custom');
2682
- if (await fs.pathExists(cacheDir)) {
2683
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
2684
-
2685
- for (const cachedModule of cachedModules) {
2686
- if (cachedModule.isDirectory()) {
2687
- const moduleId = cachedModule.name;
2688
- const cachedPath = path.join(cacheDir, moduleId);
2689
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
2690
-
2691
- // Check if this is actually a custom module
2692
- if (await fs.pathExists(moduleYamlPath)) {
2693
- // Check if this is an external official module - skip cache for those
2694
- const isExternal = await this.moduleManager.isExternalModule(moduleId);
2695
- if (isExternal) {
2696
- // External modules are handled via cloneExternalModule, not from cache
2697
- continue;
2698
- }
2699
- customModuleSources.set(moduleId, cachedPath);
2700
- }
2701
- }
2702
- }
2703
- }
2704
-
2705
- // Process each installed module
2706
- for (const moduleId of installedModules) {
2707
- spinner.message(`Recompiling agents in ${moduleId}...`);
2708
-
2709
- // Get source path
2710
- let sourcePath;
2711
- if (moduleId === 'core') {
2712
- sourcePath = getSourcePath('core');
2713
- } else {
2714
- // First check if it's in the custom cache
2715
- if (customModuleSources.has(moduleId)) {
2716
- sourcePath = customModuleSources.get(moduleId);
2717
- } else {
2718
- sourcePath = await moduleManager.findModuleSource(moduleId);
2719
- }
2720
- }
2721
-
2722
- if (!sourcePath) {
2723
- await prompts.log.warn(`Source not found for module ${moduleId}, skipping...`);
2724
- continue;
2725
- }
2726
-
2727
- const targetPath = path.join(xiaomaDir, moduleId);
2728
-
2729
- // Compile agents for this module
2730
- await moduleManager.compileModuleAgents(sourcePath, targetPath, moduleId, xiaomaDir, this);
2731
-
2732
- // Count agents (rough estimate based on files)
2733
- const agentsPath = path.join(targetPath, 'agents');
2734
- if (await fs.pathExists(agentsPath)) {
2735
- const agentFiles = await fs.readdir(agentsPath);
2736
- const agentCount = agentFiles.filter((f) => f.endsWith('.md')).length;
2737
- totalAgentCount += agentCount;
2738
- }
2739
- }
2740
-
2741
- spinner.stop('Agent recompilation complete!');
2742
-
2743
- return {
2744
- success: true,
2745
- agentCount: totalAgentCount,
2746
- modules: installedModules,
2747
- };
2748
- } catch (error) {
2749
- spinner.error('Agent recompilation failed');
2750
- throw error;
2751
- }
2752
- }
2753
-
2754
- /**
2755
- * Private: Prompt for update action
2756
- */
2757
- async promptUpdateAction() {
2758
- const action = await prompts.select({
2759
- message: 'What would you like to do?',
2760
- choices: [{ name: 'Update existing installation', value: 'update' }],
2761
- });
2762
- return { action };
2763
- }
2764
-
2765
- /**
2766
- * Handle legacy XiaoMa v4 detection with simple warning
2767
- * @param {string} _projectDir - Project directory (unused in simplified version)
2768
- * @param {Object} _legacyV4 - Legacy V4 detection result (unused in simplified version)
2769
- */
2770
- async handleLegacyV4Migration(_projectDir, _legacyV4) {
2771
- await prompts.note(
2772
- 'Found .xiaoma-cli folder from XiaoMa v4 installation.\n\n' +
2773
- 'Before continuing with installation, we recommend:\n' +
2774
- ' 1. Remove the .xiaoma-cli folder, OR\n' +
2775
- ' 2. Back it up by renaming it to another name (e.g., xiaoma-cli-backup)\n\n' +
2776
- 'If your v4 installation set up rules or commands, you should remove those as well.',
2777
- 'Legacy XiaoMa v4 detected',
2778
- );
2779
-
2780
- const proceed = await prompts.select({
2781
- message: 'What would you like to do?',
2782
- choices: [
2783
- {
2784
- name: 'Exit and clean up manually (recommended)',
2785
- value: 'exit',
2786
- hint: 'Exit installation',
2787
- },
2788
- {
2789
- name: 'Continue with installation anyway',
2790
- value: 'continue',
2791
- hint: 'Continue',
2792
- },
2793
- ],
2794
- default: 'exit',
2795
- });
2796
-
2797
- if (proceed === 'exit') {
2798
- await prompts.log.info('Please remove the .xiaoma-cli folder and any v4 rules/commands, then run the installer again.');
2799
- // Allow event loop to flush pending I/O before exit
2800
- setImmediate(() => process.exit(0));
2801
- return;
2802
- }
2803
-
2804
- await prompts.log.warn('Proceeding with installation despite legacy v4 folder');
2805
- }
2806
-
2807
- /**
2808
- * Read files-manifest.csv
2809
- * @param {string} xiaomaDir - XiaoMa installation directory
2810
- * @returns {Array} Array of file entries from files-manifest.csv
2811
- */
2812
- async readFilesManifest(xiaomaDir) {
2813
- const filesManifestPath = path.join(xiaomaDir, '_config', 'files-manifest.csv');
2814
- if (!(await fs.pathExists(filesManifestPath))) {
2815
- return [];
2816
- }
2817
-
2818
- try {
2819
- const content = await fs.readFile(filesManifestPath, 'utf8');
2820
- const lines = content.split('\n');
2821
- const files = [];
2822
-
2823
- for (let i = 1; i < lines.length; i++) {
2824
- // Skip header
2825
- const line = lines[i].trim();
2826
- if (!line) continue;
2827
-
2828
- // Parse CSV line properly handling quoted values
2829
- const parts = [];
2830
- let current = '';
2831
- let inQuotes = false;
2832
-
2833
- for (const char of line) {
2834
- if (char === '"') {
2835
- inQuotes = !inQuotes;
2836
- } else if (char === ',' && !inQuotes) {
2837
- parts.push(current);
2838
- current = '';
2839
- } else {
2840
- current += char;
2841
- }
2842
- }
2843
- parts.push(current); // Add last part
2844
-
2845
- if (parts.length >= 4) {
2846
- files.push({
2847
- type: parts[0],
2848
- name: parts[1],
2849
- module: parts[2],
2850
- path: parts[3],
2851
- hash: parts[4] || null, // Hash may not exist in old manifests
2852
- });
2853
- }
2854
- }
2855
-
2856
- return files;
2857
- } catch (error) {
2858
- await prompts.log.warn('Could not read files-manifest.csv: ' + error.message);
2859
- return [];
2860
- }
2861
- }
2862
-
2863
- /**
2864
- * Detect custom and modified files
2865
- * @param {string} xiaomaDir - XiaoMa installation directory
2866
- * @param {Array} existingFilesManifest - Previous files from files-manifest.csv
2867
- * @returns {Object} Object with customFiles and modifiedFiles arrays
2868
- */
2869
- async detectCustomFiles(xiaomaDir, existingFilesManifest) {
2870
- const customFiles = [];
2871
- const modifiedFiles = [];
2872
-
2873
- // Memory is always in _xiaoma/_memory
2874
- const xiaomaMemoryPath = '_memory';
2875
-
2876
- // Check if the manifest has hashes - if not, we can't detect modifications
2877
- let manifestHasHashes = false;
2878
- if (existingFilesManifest && existingFilesManifest.length > 0) {
2879
- manifestHasHashes = existingFilesManifest.some((f) => f.hash);
2880
- }
2881
-
2882
- // Build map of previously installed files from files-manifest.csv with their hashes
2883
- const installedFilesMap = new Map();
2884
- for (const fileEntry of existingFilesManifest) {
2885
- if (fileEntry.path) {
2886
- const absolutePath = path.join(xiaomaDir, fileEntry.path);
2887
- installedFilesMap.set(path.normalize(absolutePath), {
2888
- hash: fileEntry.hash,
2889
- relativePath: fileEntry.path,
2890
- });
2891
- }
2892
- }
2893
-
2894
- // Recursively scan xiaomaDir for all files
2895
- const scanDirectory = async (dir) => {
2896
- try {
2897
- const entries = await fs.readdir(dir, { withFileTypes: true });
2898
- for (const entry of entries) {
2899
- const fullPath = path.join(dir, entry.name);
2900
-
2901
- if (entry.isDirectory()) {
2902
- // Skip certain directories
2903
- if (entry.name === 'node_modules' || entry.name === '.git') {
2904
- continue;
2905
- }
2906
- await scanDirectory(fullPath);
2907
- } else if (entry.isFile()) {
2908
- const normalizedPath = path.normalize(fullPath);
2909
- const fileInfo = installedFilesMap.get(normalizedPath);
2910
-
2911
- // Skip certain system files that are auto-generated
2912
- const relativePath = path.relative(xiaomaDir, fullPath);
2913
- const fileName = path.basename(fullPath);
2914
-
2915
- // Skip _config directory EXCEPT for modified agent customizations
2916
- if (relativePath.startsWith('_config/') || relativePath.startsWith('_config\\')) {
2917
- // Special handling for .customize.yaml files - only preserve if modified
2918
- if (relativePath.includes('/agents/') && fileName.endsWith('.customize.yaml')) {
2919
- // Check if the customization file has been modified from manifest
2920
- const manifestPath = path.join(xiaomaDir, '_config', 'manifest.yaml');
2921
- if (await fs.pathExists(manifestPath)) {
2922
- const crypto = require('node:crypto');
2923
- const currentContent = await fs.readFile(fullPath, 'utf8');
2924
- const currentHash = crypto.createHash('sha256').update(currentContent).digest('hex');
2925
-
2926
- const yaml = require('yaml');
2927
- const manifestContent = await fs.readFile(manifestPath, 'utf8');
2928
- const manifestData = yaml.parse(manifestContent);
2929
- const originalHash = manifestData.agentCustomizations?.[relativePath];
2930
-
2931
- // Only add to customFiles if hash differs (user modified)
2932
- if (originalHash && currentHash !== originalHash) {
2933
- customFiles.push(fullPath);
2934
- }
2935
- }
2936
- }
2937
- continue;
2938
- }
2939
-
2940
- if (relativePath.startsWith(xiaomaMemoryPath + '/') && path.dirname(relativePath).includes('-sidecar')) {
2941
- continue;
2942
- }
2943
-
2944
- // Skip config.yaml files - these are regenerated on each install/update
2945
- if (fileName === 'config.yaml') {
2946
- continue;
2947
- }
2948
-
2949
- if (!fileInfo) {
2950
- // File not in manifest = custom file
2951
- // EXCEPT: Agent .md files in module folders are generated files, not custom
2952
- // Only treat .md files under _config/agents/ as custom
2953
- if (!(fileName.endsWith('.md') && relativePath.includes('/agents/') && !relativePath.startsWith('_config/'))) {
2954
- customFiles.push(fullPath);
2955
- }
2956
- } else if (manifestHasHashes && fileInfo.hash) {
2957
- // File in manifest with hash - check if it was modified
2958
- const currentHash = await this.manifest.calculateFileHash(fullPath);
2959
- if (currentHash && currentHash !== fileInfo.hash) {
2960
- // Hash changed = file was modified
2961
- modifiedFiles.push({
2962
- path: fullPath,
2963
- relativePath: fileInfo.relativePath,
2964
- });
2965
- }
2966
- }
2967
- }
2968
- }
2969
- } catch {
2970
- // Ignore errors scanning directories
2971
- }
2972
- };
2973
-
2974
- await scanDirectory(xiaomaDir);
2975
- return { customFiles, modifiedFiles };
2976
- }
2977
-
2978
- /**
2979
- * Handle missing custom module sources interactively
2980
- * @param {Map} customModuleSources - Map of custom module ID to info
2981
- * @param {string} xiaomaDir - XiaoMa directory
2982
- * @param {string} projectRoot - Project root directory
2983
- * @param {string} operation - Current operation ('update', 'compile', etc.)
2984
- * @param {Array} installedModules - Array of installed module IDs (will be modified)
2985
- * @param {boolean} [skipPrompts=false] - Skip interactive prompts and keep all modules with missing sources
2986
- * @returns {Object} Object with validCustomModules array and keptModulesWithoutSources array
2987
- */
2988
- async handleMissingCustomSources(customModuleSources, xiaomaDir, projectRoot, operation, installedModules, skipPrompts = false) {
2989
- const validCustomModules = [];
2990
- const keptModulesWithoutSources = []; // Track modules kept without sources
2991
- const customModulesWithMissingSources = [];
2992
-
2993
- // Check which sources exist
2994
- for (const [moduleId, customInfo] of customModuleSources) {
2995
- if (await fs.pathExists(customInfo.sourcePath)) {
2996
- validCustomModules.push({
2997
- id: moduleId,
2998
- name: customInfo.name,
2999
- path: customInfo.sourcePath,
3000
- info: customInfo,
3001
- });
3002
- } else {
3003
- // For cached modules that are missing, we just skip them without prompting
3004
- if (customInfo.cached) {
3005
- // Skip cached modules without prompting
3006
- keptModulesWithoutSources.push({
3007
- id: moduleId,
3008
- name: customInfo.name,
3009
- cached: true,
3010
- });
3011
- } else {
3012
- customModulesWithMissingSources.push({
3013
- id: moduleId,
3014
- name: customInfo.name,
3015
- sourcePath: customInfo.sourcePath,
3016
- relativePath: customInfo.relativePath,
3017
- info: customInfo,
3018
- });
3019
- }
3020
- }
3021
- }
3022
-
3023
- // If no missing sources, return immediately
3024
- if (customModulesWithMissingSources.length === 0) {
3025
- return {
3026
- validCustomModules,
3027
- keptModulesWithoutSources: [],
3028
- };
3029
- }
3030
-
3031
- // Non-interactive mode: keep all modules with missing sources
3032
- if (skipPrompts) {
3033
- for (const missing of customModulesWithMissingSources) {
3034
- keptModulesWithoutSources.push(missing.id);
3035
- }
3036
- return { validCustomModules, keptModulesWithoutSources };
3037
- }
3038
-
3039
- await prompts.log.warn(`Found ${customModulesWithMissingSources.length} custom module(s) with missing sources:`);
3040
-
3041
- let keptCount = 0;
3042
- let updatedCount = 0;
3043
- let removedCount = 0;
3044
-
3045
- for (const missing of customModulesWithMissingSources) {
3046
- await prompts.log.message(
3047
- `${missing.name} (${missing.id})\n Original source: ${missing.relativePath}\n Full path: ${missing.sourcePath}`,
3048
- );
3049
-
3050
- const choices = [
3051
- {
3052
- name: 'Keep installed (will not be processed)',
3053
- value: 'keep',
3054
- hint: 'Keep',
3055
- },
3056
- {
3057
- name: 'Specify new source location',
3058
- value: 'update',
3059
- hint: 'Update',
3060
- },
3061
- ];
3062
-
3063
- // Only add remove option if not just compiling agents
3064
- if (operation !== 'compile-agents') {
3065
- choices.push({
3066
- name: '⚠️ REMOVE module completely (destructive!)',
3067
- value: 'remove',
3068
- hint: 'Remove',
3069
- });
3070
- }
3071
-
3072
- const action = await prompts.select({
3073
- message: `How would you like to handle "${missing.name}"?`,
3074
- choices,
3075
- });
3076
-
3077
- switch (action) {
3078
- case 'update': {
3079
- // Use sync validation because @clack/prompts doesn't support async validate
3080
- const newSourcePath = await prompts.text({
3081
- message: 'Enter the new path to the custom module:',
3082
- default: missing.sourcePath,
3083
- validate: (input) => {
3084
- if (!input || input.trim() === '') {
3085
- return 'Please enter a path';
3086
- }
3087
- const expandedPath = path.resolve(input.trim());
3088
- if (!fs.pathExistsSync(expandedPath)) {
3089
- return 'Path does not exist';
3090
- }
3091
- // Check if it looks like a valid module
3092
- const moduleYamlPath = path.join(expandedPath, 'module.yaml');
3093
- const agentsPath = path.join(expandedPath, 'agents');
3094
- const workflowsPath = path.join(expandedPath, 'workflows');
3095
-
3096
- if (!fs.pathExistsSync(moduleYamlPath) && !fs.pathExistsSync(agentsPath) && !fs.pathExistsSync(workflowsPath)) {
3097
- return 'Path does not appear to contain a valid custom module';
3098
- }
3099
- return; // clack expects undefined for valid input
3100
- },
3101
- });
3102
-
3103
- // Defensive: handleCancel should have exited, but guard against symbol propagation
3104
- if (typeof newSourcePath !== 'string') {
3105
- keptCount++;
3106
- keptModulesWithoutSources.push(missing.id);
3107
- continue;
3108
- }
3109
-
3110
- // Update the source in manifest
3111
- const resolvedPath = path.resolve(newSourcePath.trim());
3112
- missing.info.sourcePath = resolvedPath;
3113
- // Remove relativePath - we only store absolute sourcePath now
3114
- delete missing.info.relativePath;
3115
- await this.manifest.addCustomModule(xiaomaDir, missing.info);
3116
-
3117
- validCustomModules.push({
3118
- id: missing.id,
3119
- name: missing.name,
3120
- path: resolvedPath,
3121
- info: missing.info,
3122
- });
3123
-
3124
- updatedCount++;
3125
- await prompts.log.success('Updated source location');
3126
-
3127
- break;
3128
- }
3129
- case 'remove': {
3130
- // Extra confirmation for destructive remove
3131
- await prompts.log.error(
3132
- `WARNING: This will PERMANENTLY DELETE "${missing.name}" and all its files!\n Module location: ${path.join(xiaomaDir, missing.id)}`,
3133
- );
3134
-
3135
- const confirmDelete = await prompts.confirm({
3136
- message: 'Are you absolutely sure you want to delete this module?',
3137
- default: false,
3138
- });
3139
-
3140
- if (confirmDelete) {
3141
- const typedConfirm = await prompts.text({
3142
- message: 'Type "DELETE" to confirm permanent deletion:',
3143
- validate: (input) => {
3144
- if (input !== 'DELETE') {
3145
- return 'You must type "DELETE" exactly to proceed';
3146
- }
3147
- return; // clack expects undefined for valid input
3148
- },
3149
- });
3150
-
3151
- if (typedConfirm === 'DELETE') {
3152
- // Remove the module from filesystem and manifest
3153
- const modulePath = path.join(xiaomaDir, missing.id);
3154
- if (await fs.pathExists(modulePath)) {
3155
- const fsExtra = require('fs-extra');
3156
- await fsExtra.remove(modulePath);
3157
- await prompts.log.warn(`Deleted module directory: ${path.relative(projectRoot, modulePath)}`);
3158
- }
3159
-
3160
- await this.manifest.removeModule(xiaomaDir, missing.id);
3161
- await this.manifest.removeCustomModule(xiaomaDir, missing.id);
3162
- await prompts.log.warn('Removed from manifest');
3163
-
3164
- // Also remove from installedModules list
3165
- if (installedModules && installedModules.includes(missing.id)) {
3166
- const index = installedModules.indexOf(missing.id);
3167
- if (index !== -1) {
3168
- installedModules.splice(index, 1);
3169
- }
3170
- }
3171
-
3172
- removedCount++;
3173
- await prompts.log.error(`"${missing.name}" has been permanently removed`);
3174
- } else {
3175
- await prompts.log.message('Removal cancelled - module will be kept');
3176
- keptCount++;
3177
- }
3178
- } else {
3179
- await prompts.log.message('Removal cancelled - module will be kept');
3180
- keptCount++;
3181
- }
3182
-
3183
- break;
3184
- }
3185
- case 'keep': {
3186
- keptCount++;
3187
- keptModulesWithoutSources.push(missing.id);
3188
- await prompts.log.message('Module will be kept as-is');
3189
-
3190
- break;
3191
- }
3192
- // No default
3193
- }
3194
- }
3195
-
3196
- // Show summary
3197
- if (keptCount > 0 || updatedCount > 0 || removedCount > 0) {
3198
- let summary = 'Summary for custom modules with missing sources:';
3199
- if (keptCount > 0) summary += `\n • ${keptCount} module(s) kept as-is`;
3200
- if (updatedCount > 0) summary += `\n • ${updatedCount} module(s) updated with new sources`;
3201
- if (removedCount > 0) summary += `\n • ${removedCount} module(s) permanently deleted`;
3202
- await prompts.log.message(summary);
3203
- }
3204
-
3205
- return {
3206
- validCustomModules,
3207
- keptModulesWithoutSources,
3208
- };
3209
- }
3210
- }
3211
-
3212
- module.exports = { Installer };