@zeyue0329/xiaoma-cli 1.12.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.
- package/package.json +24 -14
- package/src/core-skills/module-help.csv +13 -0
- package/src/{core → core-skills}/module.yaml +8 -0
- package/src/{core/skills/xiaoma-advanced-elicitation/workflow.md → core-skills/xiaoma-advanced-elicitation/SKILL.md} +10 -3
- package/src/core-skills/xiaoma-advanced-elicitation/methods.csv +70 -0
- package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-03-technique-execution.md +6 -4
- package/src/{core/skills → core-skills}/xiaoma-brainstorming/workflow.md +1 -1
- package/src/core-skills/xiaoma-customize/SKILL.md +111 -0
- package/src/core-skills/xiaoma-customize/scripts/list_customizable_skills.js +172 -0
- package/src/{core/skills → core-skills}/xiaoma-distillator/resources/distillate-format-reference.md +1 -1
- package/src/{core/skills → core-skills}/xiaoma-distillator/scripts/analyze_sources.py +2 -2
- package/src/{core/skills/xiaoma-editorial-review-prose/workflow.md → core-skills/xiaoma-editorial-review-prose/SKILL.md} +5 -0
- package/src/{core/skills/xiaoma-editorial-review-structure/workflow.md → core-skills/xiaoma-editorial-review-structure/SKILL.md} +5 -0
- package/src/core-skills/xiaoma-help/SKILL.md +75 -0
- package/src/{core/skills/xiaoma-index-docs/workflow.md → core-skills/xiaoma-index-docs/SKILL.md} +5 -0
- package/src/core-skills/xiaoma-party-mode/SKILL.md +128 -0
- package/src/{core/skills/xiaoma-review-adversarial-general/workflow.md → core-skills/xiaoma-review-adversarial-general/SKILL.md} +5 -0
- package/src/{core/skills/xiaoma-review-edge-case-hunter/workflow.md → core-skills/xiaoma-review-edge-case-hunter/SKILL.md} +5 -0
- package/src/{core/skills/xiaoma-shard-doc/workflow.md → core-skills/xiaoma-shard-doc/SKILL.md} +5 -0
- package/src/core-skills/xiaoma-spec/SKILL.md +129 -0
- package/src/core-skills/xiaoma-spec/assets/headless-schemas.md +33 -0
- package/src/core-skills/xiaoma-spec/assets/spec-template.md +49 -0
- package/src/core-skills/xiaoma-spec/customize.toml +53 -0
- package/src/scripts/resolve_config.js +163 -0
- package/src/scripts/resolve_customization.js +188 -0
- package/src/scripts/toml.js +338 -0
- package/src/xmc-skills/1-analysis/research/xiaoma-domain-research/SKILL.md +96 -0
- package/src/xmc-skills/1-analysis/research/xiaoma-domain-research/customize.toml +41 -0
- package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-06-research-synthesis.md +6 -0
- package/src/xmc-skills/1-analysis/research/xiaoma-market-research/SKILL.md +96 -0
- package/src/xmc-skills/1-analysis/research/xiaoma-market-research/customize.toml +41 -0
- package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-06-research-completion.md +6 -0
- package/src/xmc-skills/1-analysis/research/xiaoma-technical-research/SKILL.md +96 -0
- package/src/xmc-skills/1-analysis/research/xiaoma-technical-research/customize.toml +41 -0
- package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-06-research-synthesis.md +6 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-analyst/SKILL.md +76 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-analyst/customize.toml +90 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/SKILL.md +76 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/customize.toml +81 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/explain-concept.md +20 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/mermaid-gen.md +20 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/validate-doc.md +19 -0
- package/src/xmc-skills/1-analysis/xiaoma-agent-tech-writer/write-document.md +20 -0
- package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/checklist.md +5 -2
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/src/xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline/steps/step-08-finalize.md +184 -0
- package/src/xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline/workflow.md +140 -0
- package/src/xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline/xiaoma-skill-manifest.yaml +24 -0
- package/src/xmc-skills/1-analysis/xiaoma-document-project/SKILL.md +62 -0
- package/src/xmc-skills/1-analysis/xiaoma-document-project/customize.toml +41 -0
- package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/deep-dive-instructions.md +1 -0
- package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/full-scan-instructions.md +1 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/SKILL.md +135 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/agents/artifact-analyzer.md +60 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/agents/web-researcher.md +49 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/assets/prfaq-template.md +62 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/customize.toml +41 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/customer-faq.md +55 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/internal-faq.md +51 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/press-release.md +60 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/references/verdict.md +83 -0
- package/src/xmc-skills/1-analysis/xiaoma-prfaq/xiaoma-manifest.json +16 -0
- package/src/xmc-skills/1-analysis/xiaoma-product-brief/SKILL.md +91 -0
- package/src/xmc-skills/1-analysis/xiaoma-product-brief/assets/brief-template.md +41 -0
- package/src/xmc-skills/1-analysis/xiaoma-product-brief/customize.toml +99 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-agent-pm/SKILL.md +76 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-agent-pm/customize.toml +75 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-agent-ux-designer/SKILL.md +76 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-agent-ux-designer/customize.toml +60 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-create-prd/SKILL.md +30 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-create-prd/customize.toml +41 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-edit-prd/SKILL.md +30 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-edit-prd/customize.toml +42 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/SKILL.md +92 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/headless-schemas.md +76 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/prd-template.md +165 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/prd-validation-checklist.md +217 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/assets/validation-report-template.html +325 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/customize.toml +147 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/references/headless.md +39 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-prd/references/validate.md +97 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/SKILL.md +90 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/color-themes.md +9 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-directions.md +9 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-example-editorial.md +158 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-example-mobile.md +93 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/design-example-shadcn.md +109 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/excalidraw-wireframe.md +19 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/experience-example-mobile.md +112 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/experience-example-shadcn.md +133 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/headless-schemas.md +84 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/key-screens.md +29 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/assets/validation-report-template.html +319 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/customize.toml +100 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/creative-tools.md +19 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/design-md-spec.md +50 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/headless.md +37 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-ux/references/validate.md +115 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-validate-prd/SKILL.md +30 -0
- package/src/xmc-skills/2-plan-workflows/xiaoma-validate-prd/customize.toml +31 -0
- package/src/xmc-skills/3-solutioning/xiaoma-agent-architect/SKILL.md +76 -0
- package/src/xmc-skills/3-solutioning/xiaoma-agent-architect/customize.toml +65 -0
- package/src/xmc-skills/3-solutioning/xiaoma-check-implementation-readiness/SKILL.md +91 -0
- package/src/xmc-skills/3-solutioning/xiaoma-check-implementation-readiness/customize.toml +41 -0
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-01-document-discovery.md +1 -1
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-02-prd-analysis.md +1 -1
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +1 -1
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-06-final-assessment.md +6 -0
- package/src/xmc-skills/3-solutioning/xiaoma-create-architecture/SKILL.md +74 -0
- package/src/xmc-skills/3-solutioning/xiaoma-create-architecture/customize.toml +41 -0
- package/src/xmc-skills/3-solutioning/xiaoma-create-architecture/references/headless.md +37 -0
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-07-validation.md +23 -21
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-08-complete.md +6 -0
- package/src/xmc-skills/3-solutioning/xiaoma-create-epics-and-stories/SKILL.md +93 -0
- package/src/xmc-skills/3-solutioning/xiaoma-create-epics-and-stories/customize.toml +41 -0
- package/src/xmc-skills/3-solutioning/xiaoma-create-epics-and-stories/references/headless.md +35 -0
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-02-design-epics.md +34 -4
- package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-04-final-validation.md +12 -0
- package/src/xmc-skills/3-solutioning/xiaoma-generate-project-context/SKILL.md +81 -0
- package/src/xmc-skills/3-solutioning/xiaoma-generate-project-context/customize.toml +41 -0
- package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/steps/step-03-complete.md +6 -0
- package/src/xmc-skills/4-implementation/xiaoma-agent-dev/SKILL.md +76 -0
- package/src/xmc-skills/4-implementation/xiaoma-agent-dev/customize.toml +131 -0
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/checklist.md +29 -0
- 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
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-02-create-story.md +111 -0
- 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
- 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
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-05-code-review.md +99 -0
- 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
- 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
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-08-complete-story.md +174 -0
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/steps/step-09-finalize.md +145 -0
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/workflow.md +127 -0
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline/xiaoma-skill-manifest.yaml +27 -0
- package/src/{xmc/workflows/4-implementation/auto-story-pipeline-batch → xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch}/SKILL.md +2 -2
- package/src/xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch/checklist.md +45 -0
- package/src/{xmc/workflows/4-implementation/auto-story-pipeline-batch → xmc-skills/4-implementation/xiaoma-auto-story-pipeline-batch}/workflow.md +150 -7
- 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
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/SKILL.md +68 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/generate-trail.md +38 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-01-orientation.md +105 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-02-walkthrough.md +89 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-03-detail-pass.md +106 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-04-testing.md +74 -0
- package/src/xmc-skills/4-implementation/xiaoma-checkpoint-preview/step-05-wrapup.md +30 -0
- package/src/xmc-skills/4-implementation/xiaoma-code-review/SKILL.md +90 -0
- package/src/xmc-skills/4-implementation/xiaoma-code-review/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-code-review/steps/step-01-gather-context.md +85 -0
- package/src/xmc-skills/4-implementation/xiaoma-code-review/steps/step-02-review.md +35 -0
- package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-code-review/steps/step-03-triage.md +7 -8
- package/src/xmc-skills/4-implementation/xiaoma-code-review/steps/step-04-present.md +132 -0
- package/src/{xmc/workflows/4-implementation/xiaoma-correct-course/workflow.md → xmc-skills/4-implementation/xiaoma-correct-course/SKILL.md} +65 -31
- package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-correct-course/checklist.md +2 -2
- package/src/xmc-skills/4-implementation/xiaoma-correct-course/customize.toml +41 -0
- package/src/{xmc/workflows/4-implementation/xiaoma-create-story/workflow.md → xmc-skills/4-implementation/xiaoma-create-story/SKILL.md} +60 -11
- package/src/xmc-skills/4-implementation/xiaoma-create-story/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-create-story/references/headless.md +32 -0
- package/src/{xmc/workflows/4-implementation/xiaoma-dev-story/workflow.md → xmc-skills/4-implementation/xiaoma-dev-story/SKILL.md} +70 -20
- package/src/xmc-skills/4-implementation/xiaoma-dev-story/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-investigate/SKILL.md +196 -0
- package/src/xmc-skills/4-implementation/xiaoma-investigate/customize.toml +62 -0
- package/src/xmc-skills/4-implementation/xiaoma-investigate/references/case-file-template.md +127 -0
- package/src/{xmc/workflows/xiaoma-qa-generate-e2e-tests/workflow.md → xmc-skills/4-implementation/xiaoma-qa-generate-e2e-tests/SKILL.md} +51 -23
- package/src/{xmc/workflows → xmc-skills/4-implementation}/xiaoma-qa-generate-e2e-tests/checklist.md +1 -5
- package/src/xmc-skills/4-implementation/xiaoma-qa-generate-e2e-tests/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/SKILL.md +111 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/compile-epic-context.md +62 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/spec-template.md +88 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-01-clarify-and-route.md +100 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-02-plan.md +47 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-03-implement.md +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-04-review.md +50 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-05-present.md +78 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/step-oneshot.md +71 -0
- package/src/xmc-skills/4-implementation/xiaoma-quick-dev/sync-sprint-status.md +19 -0
- package/src/{xmc/workflows/4-implementation/xiaoma-retrospective/workflow.md → xmc-skills/4-implementation/xiaoma-retrospective/SKILL.md} +185 -152
- package/src/xmc-skills/4-implementation/xiaoma-retrospective/customize.toml +41 -0
- package/src/{xmc/workflows/4-implementation/xiaoma-sprint-planning/workflow.md → xmc-skills/4-implementation/xiaoma-sprint-planning/SKILL.md} +59 -15
- package/src/xmc-skills/4-implementation/xiaoma-sprint-planning/customize.toml +41 -0
- package/src/xmc-skills/4-implementation/xiaoma-sprint-planning/references/headless.md +28 -0
- package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-sprint-planning/sprint-status-template.yaml +3 -3
- package/src/{xmc/workflows/4-implementation/xiaoma-sprint-status/workflow.md → xmc-skills/4-implementation/xiaoma-sprint-status/SKILL.md} +57 -20
- package/src/xmc-skills/4-implementation/xiaoma-sprint-status/customize.toml +41 -0
- package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/checklist.md +6 -0
- 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
- 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
- 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
- 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
- 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
- package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/workflow.md +7 -8
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/SKILL.md +6 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/checklist.md +47 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/steps/step-01-init-and-validate.md +156 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/steps/step-02-create-epics.md +157 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/steps/step-03-bridge-sprint-planning.md +197 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/steps/step-04-batch-create-stories.md +310 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/steps/step-05-finalize.md +351 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/workflow.md +104 -0
- package/src/xmc-skills/5-full-pipeline/xiaoma-auto-prd-to-stories/xiaoma-skill-manifest.yaml +3 -0
- package/src/xmc-skills/module-help.csv +32 -0
- package/src/xmc-skills/module.yaml +95 -0
- package/src/xpm-skills/module-help.csv +3 -0
- package/src/xpm-skills/module.yaml +36 -0
- package/src/xpm-skills/xiaoma-agent-patent-advisor/SKILL.md +75 -0
- package/src/xpm-skills/xiaoma-agent-patent-advisor/customize.toml +46 -0
- package/src/xpm-skills/xiaoma-patent-mining/SKILL.md +6 -0
- package/src/xpm-skills/xiaoma-patent-mining/references/docx-format-spec.md +183 -0
- package/src/xpm-skills/xiaoma-patent-mining/scripts/md2docx.js +777 -0
- package/src/xpm-skills/xiaoma-patent-mining/steps/step-01-project-analysis.md +65 -0
- package/src/xpm-skills/xiaoma-patent-mining/steps/step-02-patent-mining.md +87 -0
- package/src/xpm-skills/xiaoma-patent-mining/steps/step-03-disclosure-writing.md +110 -0
- package/src/xpm-skills/xiaoma-patent-mining/steps/step-04-ai-taste-removal.md +85 -0
- package/src/xpm-skills/xiaoma-patent-mining/steps/step-05-docx-generation.md +111 -0
- package/src/xpm-skills/xiaoma-patent-mining/workflow.md +94 -0
- package/tools/format-workflow-md.js +263 -0
- package/tools/{cli → installer}/README.md +2 -2
- package/tools/installer/cli-utils.js +57 -0
- package/tools/installer/commands/install.js +146 -0
- package/tools/{cli → installer}/commands/status.js +15 -7
- package/tools/{cli → installer}/commands/uninstall.js +7 -7
- package/tools/installer/core/config.js +73 -0
- package/tools/installer/core/existing-install.js +121 -0
- package/tools/installer/core/install-paths.js +132 -0
- package/tools/installer/core/installer.js +1624 -0
- package/tools/installer/core/legacy-warnings.js +156 -0
- package/tools/installer/core/manifest-generator.js +859 -0
- package/tools/installer/core/manifest.js +434 -0
- package/tools/{cli/lib → installer}/file-ops.js +1 -1
- package/tools/installer/fs-native.js +116 -0
- package/tools/installer/ide/_config-driven.js +972 -0
- package/tools/{cli/installers/lib → installer}/ide/manager.js +82 -62
- package/tools/installer/ide/platform-codes.js +80 -0
- package/tools/installer/ide/platform-codes.yaml +322 -0
- package/tools/installer/ide/shared/installed-skills.js +50 -0
- package/tools/{cli/installers/lib → installer}/ide/shared/path-utils.js +0 -145
- package/tools/{cli/installers/lib → installer}/ide/shared/skill-manifest.js +3 -36
- package/tools/installer/list-options.js +210 -0
- package/tools/{cli/installers/lib → installer}/message-loader.js +3 -3
- package/tools/installer/modules/channel-plan.js +203 -0
- package/tools/installer/modules/channel-resolver.js +241 -0
- package/tools/installer/modules/custom-module-manager.js +912 -0
- package/tools/installer/modules/external-manager.js +533 -0
- package/tools/installer/modules/module-help-schema.js +13 -0
- package/tools/{cli/installers/lib/core/config-collector.js → installer/modules/official-modules.js} +1052 -110
- package/tools/installer/modules/plugin-resolver.js +398 -0
- package/tools/installer/modules/version-resolver.js +336 -0
- package/tools/installer/project-root.js +230 -0
- package/tools/{cli/lib → installer}/prompts.js +143 -100
- package/tools/installer/set-overrides.js +330 -0
- package/tools/installer/ui.js +2078 -0
- package/tools/{cli → installer}/xiaoma-cli.js +9 -10
- package/tools/{cli/lib → installer}/yaml-format.js +1 -1
- package/tools/migrate-custom-module-paths.js +124 -0
- package/tools/schema/step.js +855 -0
- package/tools/skill-validator.md +323 -0
- package/tools/validate-file-refs.js +566 -0
- package/tools/validate-frontmatter-prose-routing.js +334 -0
- package/tools/validate-skills.js +702 -0
- package/tools/validate-step-schemas.js +401 -0
- package/tools/validate-svg-changes.sh +1 -1
- package/tools/validate-trigger-column-vs-emits.js +375 -0
- package/tools/validate-warnings-samples.js +261 -0
- package/tools/xiaoma/rebrand.mjs +0 -0
- package/tools/xiaoma-npx-wrapper.js +2 -2
- package/CLAUDE.md +0 -110
- package/README.md +0 -128
- package/demo/xiaoma-bug-circle-resolve/SKILL.md +0 -6
- package/demo/xiaoma-bug-circle-resolve/workflow.md +0 -212
- package/demo/xiaoma-bug-resolve/SKILL.md +0 -6
- package/demo/xiaoma-bug-resolve/workflow.md +0 -269
- 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
- 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
- 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
- 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
- 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
- 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
- package/patent-disclosure-optimized/SKILL.md +0 -298
- package/src/core/module-help.csv +0 -11
- package/src/core/skills/xiaoma-advanced-elicitation/SKILL.md +0 -6
- package/src/core/skills/xiaoma-advanced-elicitation/methods.csv +0 -51
- package/src/core/skills/xiaoma-editorial-review-prose/SKILL.md +0 -6
- package/src/core/skills/xiaoma-editorial-review-structure/SKILL.md +0 -6
- package/src/core/skills/xiaoma-help/SKILL.md +0 -6
- package/src/core/skills/xiaoma-help/workflow.md +0 -88
- package/src/core/skills/xiaoma-help/xiaoma-skill-manifest.yaml +0 -1
- package/src/core/skills/xiaoma-index-docs/SKILL.md +0 -6
- package/src/core/skills/xiaoma-index-docs/xiaoma-skill-manifest.yaml +0 -1
- package/src/core/skills/xiaoma-party-mode/SKILL.md +0 -6
- package/src/core/skills/xiaoma-party-mode/steps/step-01-agent-loading.md +0 -138
- package/src/core/skills/xiaoma-party-mode/steps/step-02-discussion-orchestration.md +0 -187
- package/src/core/skills/xiaoma-party-mode/steps/step-03-graceful-exit.md +0 -167
- package/src/core/skills/xiaoma-party-mode/workflow.md +0 -190
- package/src/core/skills/xiaoma-party-mode/xiaoma-skill-manifest.yaml +0 -1
- package/src/core/skills/xiaoma-review-adversarial-general/SKILL.md +0 -6
- package/src/core/skills/xiaoma-review-adversarial-general/xiaoma-skill-manifest.yaml +0 -1
- package/src/core/skills/xiaoma-review-edge-case-hunter/SKILL.md +0 -6
- package/src/core/skills/xiaoma-review-edge-case-hunter/xiaoma-skill-manifest.yaml +0 -1
- package/src/core/skills/xiaoma-shard-doc/SKILL.md +0 -6
- package/src/core/skills/xiaoma-shard-doc/xiaoma-skill-manifest.yaml +0 -1
- package/src/core/tasks/xiaoma-create-prd/SKILL.md +0 -6
- package/src/core/tasks/xiaoma-create-prd/data/prd-purpose.md +0 -197
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-01-init.md +0 -178
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-01b-continue.md +0 -161
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-02-discovery.md +0 -208
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-02b-vision.md +0 -142
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-02c-executive-summary.md +0 -158
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-03-success.md +0 -214
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-04-journeys.md +0 -201
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-05-domain.md +0 -194
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-06-innovation.md +0 -211
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-07-project-type.md +0 -222
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-08-scoping.md +0 -216
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-09-functional.md +0 -219
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-10-nonfunctional.md +0 -230
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-11-polish.md +0 -221
- package/src/core/tasks/xiaoma-create-prd/steps-c/step-12-complete.md +0 -115
- package/src/core/tasks/xiaoma-create-prd/templates/prd-template.md +0 -10
- package/src/core/tasks/xiaoma-create-prd/workflow.md +0 -62
- package/src/core/tasks/xiaoma-create-prd/xiaoma-skill-manifest.yaml +0 -1
- package/src/utility/agent-components/activation-rules.txt +0 -6
- package/src/utility/agent-components/activation-steps.txt +0 -14
- package/src/utility/agent-components/agent-command-header.md +0 -1
- package/src/utility/agent-components/agent.customize.template.yaml +0 -41
- package/src/utility/agent-components/handler-action.txt +0 -4
- package/src/utility/agent-components/handler-data.txt +0 -5
- package/src/utility/agent-components/handler-exec.txt +0 -6
- package/src/utility/agent-components/handler-multi.txt +0 -13
- package/src/utility/agent-components/handler-tmpl.txt +0 -5
- package/src/utility/agent-components/menu-handlers.txt +0 -6
- package/src/xmc/agents/analyst.agent.yaml +0 -47
- package/src/xmc/agents/architect.agent.yaml +0 -29
- package/src/xmc/agents/dev.agent.yaml +0 -38
- package/src/xmc/agents/pm.agent.yaml +0 -44
- package/src/xmc/agents/qa.agent.yaml +0 -58
- package/src/xmc/agents/quick-flow-solo-dev.agent.yaml +0 -36
- package/src/xmc/agents/sm.agent.yaml +0 -49
- package/src/xmc/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +0 -224
- package/src/xmc/agents/tech-writer/tech-writer.agent.yaml +0 -46
- package/src/xmc/agents/tech-writer/xiaoma-skill-manifest.yaml +0 -3
- package/src/xmc/agents/ux-designer.agent.yaml +0 -27
- package/src/xmc/agents/xiaoma-skill-manifest.yaml +0 -39
- package/src/xmc/data/project-context-template.md +0 -26
- package/src/xmc/module-help.csv +0 -32
- package/src/xmc/module.yaml +0 -50
- package/src/xmc/teams/default-party.csv +0 -20
- package/src/xmc/teams/team-fullstack.yaml +0 -12
- package/src/xmc/workflows/1-analysis/auto-requirements-pipeline/steps/step-08-finalize.md +0 -124
- package/src/xmc/workflows/1-analysis/auto-requirements-pipeline/workflow.md +0 -107
- package/src/xmc/workflows/1-analysis/auto-requirements-pipeline/xiaoma-skill-manifest.yaml +0 -3
- package/src/xmc/workflows/1-analysis/research/market-steps/step-01-init.md +0 -182
- package/src/xmc/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +0 -237
- package/src/xmc/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +0 -249
- package/src/xmc/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +0 -259
- package/src/xmc/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +0 -177
- package/src/xmc/workflows/1-analysis/research/market-steps/step-06-research-completion.md +0 -476
- package/src/xmc/workflows/1-analysis/research/xiaoma-domain-research/SKILL.md +0 -6
- package/src/xmc/workflows/1-analysis/research/xiaoma-domain-research/workflow.md +0 -49
- package/src/xmc/workflows/1-analysis/research/xiaoma-domain-research/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/1-analysis/research/xiaoma-market-research/SKILL.md +0 -6
- package/src/xmc/workflows/1-analysis/research/xiaoma-market-research/workflow.md +0 -49
- package/src/xmc/workflows/1-analysis/research/xiaoma-market-research/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/SKILL.md +0 -6
- package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/research.template.md +0 -29
- package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/workflow.md +0 -50
- package/src/xmc/workflows/1-analysis/research/xiaoma-technical-research/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/SKILL.md +0 -6
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/product-brief.template.md +0 -10
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-01-init.md +0 -170
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-01b-continue.md +0 -158
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-02-vision.md +0 -193
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-03-users.md +0 -196
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-04-metrics.md +0 -199
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-05-scope.md +0 -213
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/steps/step-06-complete.md +0 -159
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/workflow.md +0 -55
- package/src/xmc/workflows/1-analysis/xiaoma-create-product-brief/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/1-analysis/xiaoma-product-brief-preview/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +0 -15
- package/src/xmc/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +0 -197
- package/src/xmc/workflows/2-plan-workflows/create-prd/data/project-types.csv +0 -11
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +0 -224
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +0 -191
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +0 -209
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +0 -174
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -214
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +0 -228
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +0 -217
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -205
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -243
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +0 -263
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +0 -209
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -264
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +0 -242
- package/src/xmc/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +0 -232
- package/src/xmc/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +0 -65
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/SKILL.md +0 -6
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-01-init.md +0 -135
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-01b-continue.md +0 -127
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-02-discovery.md +0 -190
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-03-core-experience.md +0 -217
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-04-emotional-response.md +0 -220
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-05-inspiration.md +0 -235
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-06-design-system.md +0 -253
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-07-defining-experience.md +0 -255
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-08-visual-foundation.md +0 -225
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-09-design-directions.md +0 -225
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-10-user-journeys.md +0 -242
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-11-component-strategy.md +0 -249
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-12-ux-patterns.md +0 -238
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-13-responsive-accessibility.md +0 -265
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/steps/step-14-complete.md +0 -171
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/ux-design-template.md +0 -13
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/workflow.md +0 -36
- package/src/xmc/workflows/2-plan-workflows/xiaoma-create-ux-design/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/SKILL.md +0 -6
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-01-discovery.md +0 -242
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-01b-legacy-conversion.md +0 -204
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-02-review.md +0 -245
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-03-edit.md +0 -250
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/steps-e/step-e-04-complete.md +0 -165
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/workflow.md +0 -63
- package/src/xmc/workflows/2-plan-workflows/xiaoma-edit-prd/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/SKILL.md +0 -6
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/domain-complexity.csv +0 -15
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/prd-purpose.md +0 -197
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/data/project-types.csv +0 -11
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-01-discovery.md +0 -221
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-02-format-detection.md +0 -188
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-02b-parity-check.md +0 -206
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-03-density-validation.md +0 -171
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -211
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-05-measurability-validation.md +0 -225
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-06-traceability-validation.md +0 -214
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -202
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -240
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-09-project-type-validation.md +0 -260
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-10-smart-validation.md +0 -206
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -261
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-12-completeness-validation.md +0 -239
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/steps-v/step-v-13-report-complete.md +0 -229
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/workflow.md +0 -62
- package/src/xmc/workflows/2-plan-workflows/xiaoma-validate-prd/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/3-solutioning/xiaoma-check-implementation-readiness/SKILL.md +0 -6
- package/src/xmc/workflows/3-solutioning/xiaoma-check-implementation-readiness/workflow.md +0 -49
- package/src/xmc/workflows/3-solutioning/xiaoma-check-implementation-readiness/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/3-solutioning/xiaoma-create-architecture/SKILL.md +0 -6
- package/src/xmc/workflows/3-solutioning/xiaoma-create-architecture/workflow.md +0 -38
- package/src/xmc/workflows/3-solutioning/xiaoma-create-architecture/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/3-solutioning/xiaoma-create-epics-and-stories/SKILL.md +0 -6
- package/src/xmc/workflows/3-solutioning/xiaoma-create-epics-and-stories/workflow.md +0 -53
- package/src/xmc/workflows/3-solutioning/xiaoma-create-epics-and-stories/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/checklist.md +0 -22
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-02-create-story.md +0 -102
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-05-code-review.md +0 -95
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-08-complete-story.md +0 -114
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/steps/step-09-finalize.md +0 -69
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/workflow.md +0 -89
- package/src/xmc/workflows/4-implementation/auto-story-pipeline/xiaoma-skill-manifest.yaml +0 -3
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/checklist.md +0 -23
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/steps/step-01-gather-context.md +0 -61
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/steps/step-02-review.md +0 -41
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/steps/step-04-present.md +0 -38
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/workflow.md +0 -54
- package/src/xmc/workflows/4-implementation/xiaoma-code-review/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/xiaoma-correct-course/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-correct-course/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/xiaoma-create-story/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-create-story/discover-inputs.md +0 -88
- package/src/xmc/workflows/4-implementation/xiaoma-create-story/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/xiaoma-dev-story/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-dev-story/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/xiaoma-retrospective/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-retrospective/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/xiaoma-sprint-planning/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-sprint-planning/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/4-implementation/xiaoma-sprint-status/SKILL.md +0 -6
- package/src/xmc/workflows/4-implementation/xiaoma-sprint-status/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/xiaoma-document-project/SKILL.md +0 -6
- package/src/xmc/workflows/xiaoma-document-project/workflow.md +0 -27
- package/src/xmc/workflows/xiaoma-document-project/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/xiaoma-generate-project-context/SKILL.md +0 -6
- package/src/xmc/workflows/xiaoma-generate-project-context/workflow.md +0 -43
- package/src/xmc/workflows/xiaoma-generate-project-context/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/xiaoma-qa-generate-e2e-tests/SKILL.md +0 -6
- package/src/xmc/workflows/xiaoma-qa-generate-e2e-tests/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/SKILL.md +0 -6
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-01-mode-detection.md +0 -169
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-02-context-gathering.md +0 -114
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-03-execute.md +0 -107
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-04-self-check.md +0 -107
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-05-adversarial-review.md +0 -94
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/steps/step-06-resolve-findings.md +0 -144
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/workflow.md +0 -38
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-dev-new-preview/xiaoma-skill-manifest.yaml +0 -1
- package/src/xmc/workflows/xiaoma-quick-flow/xiaoma-quick-spec/xiaoma-skill-manifest.yaml +0 -1
- package/tools/cli/commands/install.js +0 -87
- package/tools/cli/external-official-modules.yaml +0 -4
- package/tools/cli/installers/lib/core/custom-module-cache.js +0 -260
- package/tools/cli/installers/lib/core/dependency-resolver.js +0 -743
- package/tools/cli/installers/lib/core/detector.js +0 -223
- package/tools/cli/installers/lib/core/ide-config-manager.js +0 -157
- package/tools/cli/installers/lib/core/installer.js +0 -3212
- package/tools/cli/installers/lib/core/manifest-generator.js +0 -1374
- package/tools/cli/installers/lib/core/manifest.js +0 -1040
- package/tools/cli/installers/lib/custom/handler.js +0 -358
- package/tools/cli/installers/lib/ide/_base-ide.js +0 -673
- package/tools/cli/installers/lib/ide/_config-driven.js +0 -1058
- package/tools/cli/installers/lib/ide/platform-codes.js +0 -100
- package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -321
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +0 -181
- package/tools/cli/installers/lib/ide/shared/module-injections.js +0 -136
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +0 -368
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +0 -179
- package/tools/cli/installers/lib/ide/shared/xiaoma-artifacts.js +0 -181
- package/tools/cli/installers/lib/ide/templates/agent-command-template.md +0 -14
- package/tools/cli/installers/lib/ide/templates/combined/antigravity.md +0 -8
- package/tools/cli/installers/lib/ide/templates/combined/default-agent.md +0 -15
- package/tools/cli/installers/lib/ide/templates/combined/default-task.md +0 -10
- package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +0 -10
- package/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +0 -6
- package/tools/cli/installers/lib/ide/templates/combined/gemini-agent.toml +0 -14
- package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +0 -11
- package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +0 -11
- package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow-yaml.toml +0 -16
- package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow.toml +0 -14
- package/tools/cli/installers/lib/ide/templates/combined/kiro-agent.md +0 -16
- package/tools/cli/installers/lib/ide/templates/combined/kiro-task.md +0 -9
- package/tools/cli/installers/lib/ide/templates/combined/kiro-tool.md +0 -9
- package/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md +0 -7
- package/tools/cli/installers/lib/ide/templates/combined/opencode-agent.md +0 -15
- package/tools/cli/installers/lib/ide/templates/combined/opencode-task.md +0 -13
- package/tools/cli/installers/lib/ide/templates/combined/opencode-tool.md +0 -13
- package/tools/cli/installers/lib/ide/templates/combined/opencode-workflow-yaml.md +0 -16
- package/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md +0 -16
- package/tools/cli/installers/lib/ide/templates/combined/rovodev.md +0 -9
- package/tools/cli/installers/lib/ide/templates/combined/trae.md +0 -9
- package/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +0 -10
- package/tools/cli/installers/lib/ide/templates/split/.gitkeep +0 -0
- package/tools/cli/installers/lib/modules/external-manager.js +0 -136
- package/tools/cli/installers/lib/modules/manager.js +0 -1382
- package/tools/cli/lib/activation-builder.js +0 -165
- package/tools/cli/lib/agent/compiler.js +0 -516
- package/tools/cli/lib/agent/installer.js +0 -680
- package/tools/cli/lib/agent/template-engine.js +0 -152
- package/tools/cli/lib/agent-analyzer.js +0 -97
- package/tools/cli/lib/agent-party-generator.js +0 -194
- package/tools/cli/lib/cli-utils.js +0 -182
- package/tools/cli/lib/config.js +0 -213
- package/tools/cli/lib/platform-codes.js +0 -116
- package/tools/cli/lib/project-root.js +0 -77
- package/tools/cli/lib/ui.js +0 -1960
- package/tools/cli/lib/xml-handler.js +0 -177
- package/tools/cli/lib/xml-to-markdown.js +0 -82
- package/tools/cli/lib/yaml-xml-builder.js +0 -570
- package/tools/platform-codes.yaml +0 -157
- package/tools/schema/agent.js +0 -489
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/SKILL.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/brain-methods.csv +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-01-session-setup.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-01b-continue.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02a-user-selected.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02b-ai-recommended.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02c-random-selection.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-02d-progressive-flow.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/steps/step-04-idea-organization.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-brainstorming/template.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/SKILL.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/agents/distillate-compressor.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/agents/round-trip-reconstructor.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/resources/compression-rules.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/resources/splitting-strategy.md +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/scripts/tests/test_analyze_sources.py +0 -0
- /package/src/{core/skills → core-skills}/xiaoma-distillator/xiaoma-skill-manifest.yaml +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-01-init.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-02-domain-analysis.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-03-competitive-landscape.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-04-regulatory-focus.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-domain-research/domain-steps/step-05-technical-trends.md +0 -0
- /package/src/{xmc/workflows/1-analysis/research → xmc-skills/1-analysis/research/xiaoma-domain-research}/research.template.md +0 -0
- /package/src/{xmc/workflows/1-analysis/research/xiaoma-domain-research → xmc-skills/1-analysis/research/xiaoma-market-research}/research.template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-01-init.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-02-customer-behavior.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-03-customer-pain-points.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-04-customer-decisions.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-market-research/steps/step-05-competitive-analysis.md +0 -0
- /package/src/{xmc/workflows/1-analysis/research/xiaoma-market-research → xmc-skills/1-analysis/research/xiaoma-technical-research}/research.template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-01-init.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-02-technical-overview.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-03-integration-patterns.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-04-architectural-patterns.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/research/xiaoma-technical-research/technical-steps/step-05-implementation-research.md +0 -0
- /package/src/{xmc/workflows/1-analysis/auto-requirements-pipeline → xmc-skills/1-analysis/xiaoma-auto-requirements-pipeline}/SKILL.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/checklist.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/documentation-requirements.csv +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/instructions.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/deep-dive-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/index-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/project-overview-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/project-scan-report-schema.json +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/templates/source-tree-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/deep-dive-workflow.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/1-analysis}/xiaoma-document-project/workflows/full-scan-workflow.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/SKILL.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/artifact-analyzer.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/opportunity-reviewer.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/skeptic-reviewer.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/agents/web-researcher.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/contextual-discovery.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/draft-and-review.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/finalize.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/prompts/guided-elicitation.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/resources/brief-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/1-analysis/xiaoma-product-brief-preview/xiaoma-manifest.json +0 -0
- /package/src/{core/skills/xiaoma-advanced-elicitation → xmc-skills/1-analysis/xiaoma-product-brief-preview}/xiaoma-skill-manifest.yaml +0 -0
- /package/src/{core/tasks/xiaoma-create-prd/data → xmc-skills/2-plan-workflows/xiaoma-prd/assets}/domain-complexity.csv +0 -0
- /package/src/{core/tasks/xiaoma-create-prd/data → xmc-skills/2-plan-workflows/xiaoma-prd/assets}/project-types.csv +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-04-ux-alignment.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/steps/step-05-epic-quality-review.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-check-implementation-readiness/templates/readiness-report-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/architecture-decision-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/data/domain-complexity.csv +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/data/project-types.csv +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-01-init.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-01b-continue.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-02-context.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-03-starter.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-04-decisions.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-05-patterns.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-architecture/steps/step-06-structure.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-01-validate-prerequisites.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/steps/step-03-create-stories.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/3-solutioning/xiaoma-create-epics-and-stories/templates/epics-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/project-context-template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/steps/step-01-discover.md +0 -0
- /package/src/{xmc/workflows → xmc-skills/3-solutioning}/xiaoma-generate-project-context/steps/step-02-generate.md +0 -0
- /package/src/{xmc/workflows/4-implementation/auto-story-pipeline → xmc-skills/4-implementation/xiaoma-auto-story-pipeline}/SKILL.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-create-story/checklist.md +0 -0
- /package/src/{xmc/workflows/4-implementation/xiaoma-code-review → xmc-skills/4-implementation/xiaoma-create-story}/discover-inputs.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-create-story/template.md +0 -0
- /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-dev-story/checklist.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/SKILL.md +0 -0
- /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
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-02-plan.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-03-implement.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-04-review.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/steps/step-05-present.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/tech-spec-template.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-dev-new-preview/workflow.md +0 -0
- /package/src/{core/skills/xiaoma-brainstorming → xmc-skills/4-implementation/xiaoma-quick-dev-new-preview}/xiaoma-skill-manifest.yaml +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/SKILL.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-01-understand.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-02-investigate.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-03-generate.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/steps/step-04-review.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/tech-spec-template.md +0 -0
- /package/src/{xmc/workflows/xiaoma-quick-flow → xmc-skills/4-implementation}/xiaoma-quick-spec/workflow.md +0 -0
- /package/src/{core/skills/xiaoma-editorial-review-prose → xmc-skills/4-implementation/xiaoma-quick-spec}/xiaoma-skill-manifest.yaml +0 -0
- /package/src/{xmc/workflows → xmc-skills}/4-implementation/xiaoma-sprint-planning/checklist.md +0 -0
- /package/src/{xmc/workflows/5-full-pipeline/auto-full-pipeline → xmc-skills/5-full-pipeline/xiaoma-auto-full-pipeline}/SKILL.md +0 -0
- /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
- /package/{patent-disclosure-optimized → src/xpm-skills/xiaoma-patent-mining}/references/disclosure-template.md +0 -0
- /package/{patent-disclosure-optimized → src/xpm-skills/xiaoma-patent-mining}/references/mining-principles.md +0 -0
- /package/src/{core/skills/xiaoma-editorial-review-structure → xpm-skills/xiaoma-patent-mining}/xiaoma-skill-manifest.yaml +0 -0
- /package/tools/{cli/installers → installer}/install-messages.yaml +0 -0
|
@@ -0,0 +1,2078 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const os = require('node:os');
|
|
3
|
+
const semver = require('semver');
|
|
4
|
+
const fs = require('./fs-native');
|
|
5
|
+
const installerPackageJson = require('../../package.json');
|
|
6
|
+
const { CLIUtils } = require('./cli-utils');
|
|
7
|
+
const { ExternalModuleManager } = require('./modules/external-manager');
|
|
8
|
+
const { resolveModuleVersion } = require('./modules/version-resolver');
|
|
9
|
+
const { Manifest } = require('./core/manifest');
|
|
10
|
+
const {
|
|
11
|
+
parseChannelOptions,
|
|
12
|
+
buildPlan,
|
|
13
|
+
decideChannelForModule,
|
|
14
|
+
orphanPinWarnings,
|
|
15
|
+
bundledTargetWarnings,
|
|
16
|
+
} = require('./modules/channel-plan');
|
|
17
|
+
const channelResolver = require('./modules/channel-resolver');
|
|
18
|
+
const prompts = require('./prompts');
|
|
19
|
+
const { parseSetEntries } = require('./set-overrides');
|
|
20
|
+
|
|
21
|
+
const manifest = new Manifest();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Format a resolved version for display in installer labels.
|
|
25
|
+
* Semver-like values are normalized to a single leading "v".
|
|
26
|
+
* @param {string|null|undefined} version
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
29
|
+
function formatDisplayVersion(version) {
|
|
30
|
+
const trimmed = typeof version === 'string' ? version.trim() : '';
|
|
31
|
+
if (!trimmed) return '';
|
|
32
|
+
|
|
33
|
+
const normalized = semver.valid(semver.coerce(trimmed));
|
|
34
|
+
if (normalized) {
|
|
35
|
+
return `v${normalized}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return trimmed;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Build the display label for a module, showing an upgrade arrow when an
|
|
43
|
+
* installed semver differs from the latest resolvable semver.
|
|
44
|
+
* @param {string} name
|
|
45
|
+
* @param {string} latestVersion
|
|
46
|
+
* @param {string} installedVersion
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
function buildModuleLabel(name, latestVersion, installedVersion = '') {
|
|
50
|
+
const latestDisplay = formatDisplayVersion(latestVersion);
|
|
51
|
+
if (!latestDisplay) return name;
|
|
52
|
+
|
|
53
|
+
const installedDisplay = formatDisplayVersion(installedVersion);
|
|
54
|
+
const latestSemver = semver.valid(semver.coerce(latestVersion || ''));
|
|
55
|
+
const installedSemver = semver.valid(semver.coerce(installedVersion || ''));
|
|
56
|
+
|
|
57
|
+
if (installedDisplay && latestSemver && installedSemver && semver.neq(installedSemver, latestSemver)) {
|
|
58
|
+
return `${name} (${installedDisplay} → ${latestDisplay})`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return `${name} (${latestDisplay})`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the version to show for a module picker entry. External modules use
|
|
66
|
+
* the same channel/tag resolver as installs; bundled modules fall back to local
|
|
67
|
+
* source metadata.
|
|
68
|
+
* @param {string} moduleCode - Module code (e.g., 'core', 'xmc', 'cis')
|
|
69
|
+
* @param {Object} options
|
|
70
|
+
* @param {string|null} [options.repoUrl] - Module repository URL for tag resolution
|
|
71
|
+
* @param {string|null} [options.registryDefault] - Registry default channel
|
|
72
|
+
* @param {Object|null} [options.channelOptions] - Parsed installer channel options
|
|
73
|
+
* @returns {Promise<{version: string, lookupAttempted: boolean, lookupSucceeded: boolean}>}
|
|
74
|
+
*/
|
|
75
|
+
async function getModuleVersion(moduleCode, { repoUrl = null, registryDefault = null, channelOptions = null } = {}) {
|
|
76
|
+
if (repoUrl) {
|
|
77
|
+
const plan = decideChannelForModule({
|
|
78
|
+
code: moduleCode,
|
|
79
|
+
channelOptions,
|
|
80
|
+
registryDefault,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const resolved = await channelResolver.resolveChannel({
|
|
85
|
+
channel: plan.channel,
|
|
86
|
+
pin: plan.pin,
|
|
87
|
+
repoUrl,
|
|
88
|
+
});
|
|
89
|
+
if (resolved?.version) {
|
|
90
|
+
return {
|
|
91
|
+
version: resolved.version,
|
|
92
|
+
lookupAttempted: plan.channel === 'stable',
|
|
93
|
+
lookupSucceeded: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// Fall back to local metadata when tag resolution is unavailable.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const versionInfo = await resolveModuleVersion(moduleCode);
|
|
102
|
+
return {
|
|
103
|
+
version: versionInfo.version || '',
|
|
104
|
+
lookupAttempted: !!repoUrl,
|
|
105
|
+
lookupSucceeded: false,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* UI utilities for the installer
|
|
111
|
+
*/
|
|
112
|
+
class UI {
|
|
113
|
+
async _retainUnavailableInstalledModules(selectedModules, installedModuleIds, xiaomaDir, options = {}) {
|
|
114
|
+
const { OfficialModules } = require('./modules/official-modules');
|
|
115
|
+
const officialCodes = new Set(['core']);
|
|
116
|
+
|
|
117
|
+
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
|
|
118
|
+
for (const mod of builtInModules) {
|
|
119
|
+
officialCodes.add(mod.id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const externalManager = new ExternalModuleManager();
|
|
123
|
+
const registryModules = await externalManager.listAvailable();
|
|
124
|
+
for (const mod of registryModules) {
|
|
125
|
+
officialCodes.add(mod.code);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const { CustomModuleManager } = require('./modules/custom-module-manager');
|
|
129
|
+
const customMgr = new CustomModuleManager();
|
|
130
|
+
const selectedSet = new Set(selectedModules);
|
|
131
|
+
const preserveModules = [];
|
|
132
|
+
|
|
133
|
+
for (const moduleId of installedModuleIds) {
|
|
134
|
+
if (moduleId === 'core') continue;
|
|
135
|
+
if (!selectedSet.has(moduleId) && !options.preserveUnselected) continue;
|
|
136
|
+
if (officialCodes.has(moduleId)) continue;
|
|
137
|
+
|
|
138
|
+
const customSource = await customMgr.findModuleSourceByCode(moduleId, { xiaomaDir });
|
|
139
|
+
if (!customSource) {
|
|
140
|
+
preserveModules.push(moduleId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const preservedSet = new Set(preserveModules);
|
|
145
|
+
return {
|
|
146
|
+
selectedModules: selectedModules.filter((moduleId) => !preservedSet.has(moduleId)),
|
|
147
|
+
preserveModules,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Prompt for installation configuration
|
|
153
|
+
* @param {Object} options - Command-line options from install command
|
|
154
|
+
* @returns {Object} Installation configuration
|
|
155
|
+
*/
|
|
156
|
+
async promptInstall(options = {}) {
|
|
157
|
+
await CLIUtils.displayLogo();
|
|
158
|
+
|
|
159
|
+
// Display version-specific start message from install-messages.yaml
|
|
160
|
+
const { MessageLoader } = require('./message-loader');
|
|
161
|
+
const messageLoader = new MessageLoader();
|
|
162
|
+
await messageLoader.displayStartMessage();
|
|
163
|
+
|
|
164
|
+
// Parse channel flags (--channel/--all-*/--next=/--pin) once. Warnings
|
|
165
|
+
// are surfaced immediately so the user sees them before any git ops run.
|
|
166
|
+
const channelOptions = parseChannelOptions(options);
|
|
167
|
+
for (const warning of channelOptions.warnings) {
|
|
168
|
+
await prompts.log.warn(warning);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// When the user launched the installer from a prerelease (npx @zeyue0329/xiaoma-cli@next),
|
|
172
|
+
// mirror that intent for external modules: seed the global channel to 'next' so
|
|
173
|
+
// the module picker's version labels resolve from main HEAD (matching what
|
|
174
|
+
// actually gets installed) and the interactive channel gate skips — the user
|
|
175
|
+
// already declared "next" intent by typing @next. Explicit channel flags
|
|
176
|
+
// override this seed.
|
|
177
|
+
if (
|
|
178
|
+
semver.prerelease(installerPackageJson.version) !== null &&
|
|
179
|
+
!channelOptions.global &&
|
|
180
|
+
channelOptions.nextSet.size === 0 &&
|
|
181
|
+
channelOptions.pins.size === 0
|
|
182
|
+
) {
|
|
183
|
+
channelOptions.global = 'next';
|
|
184
|
+
await prompts.log.info(
|
|
185
|
+
'Launched from a prerelease — installing all external modules from main HEAD (next channel). Pass --all-stable or --pin to override.',
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Get directory from options or prompt
|
|
190
|
+
let confirmedDirectory;
|
|
191
|
+
if (options.directory) {
|
|
192
|
+
// Use provided directory from command-line
|
|
193
|
+
const expandedDir = this.expandUserPath(options.directory);
|
|
194
|
+
const validation = this.validateDirectorySync(expandedDir);
|
|
195
|
+
if (validation) {
|
|
196
|
+
throw new Error(`Invalid directory: ${validation}`);
|
|
197
|
+
}
|
|
198
|
+
confirmedDirectory = expandedDir;
|
|
199
|
+
await prompts.log.info(`Using directory from command-line: ${confirmedDirectory}`);
|
|
200
|
+
} else {
|
|
201
|
+
confirmedDirectory = await this.getConfirmedDirectory();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const { Installer } = require('./core/installer');
|
|
205
|
+
const installer = new Installer();
|
|
206
|
+
const { xiaomaDir } = await installer.findXiaomaDir(confirmedDirectory);
|
|
207
|
+
|
|
208
|
+
// Check if there's an existing XiaoMa installation
|
|
209
|
+
const hasExistingInstall = await fs.pathExists(xiaomaDir);
|
|
210
|
+
|
|
211
|
+
// Track action type (only set if there's an existing installation)
|
|
212
|
+
let actionType;
|
|
213
|
+
|
|
214
|
+
// Only show action menu if there's an existing installation
|
|
215
|
+
if (hasExistingInstall) {
|
|
216
|
+
// Get version information
|
|
217
|
+
const { existingInstall, xiaomaDir } = await this.getExistingInstallation(confirmedDirectory);
|
|
218
|
+
|
|
219
|
+
// Build menu choices dynamically
|
|
220
|
+
const choices = [];
|
|
221
|
+
|
|
222
|
+
// Always show Quick Update first (allows refreshing installation even on same version)
|
|
223
|
+
if (existingInstall.installed) {
|
|
224
|
+
choices.push({
|
|
225
|
+
name: 'Quick Update',
|
|
226
|
+
value: 'quick-update',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Common actions
|
|
231
|
+
choices.push({ name: 'Modify XiaoMa Installation', value: 'update' });
|
|
232
|
+
|
|
233
|
+
// Check if action is provided via command-line
|
|
234
|
+
if (options.action) {
|
|
235
|
+
const validActions = choices.map((c) => c.value);
|
|
236
|
+
if (!validActions.includes(options.action)) {
|
|
237
|
+
throw new Error(`Invalid action: ${options.action}. Valid actions: ${validActions.join(', ')}`);
|
|
238
|
+
}
|
|
239
|
+
actionType = options.action;
|
|
240
|
+
await prompts.log.info(`Using action from command-line: ${actionType}`);
|
|
241
|
+
} else if (options.yes) {
|
|
242
|
+
// Default to quick-update if available, unless flags that require the
|
|
243
|
+
// full update path are present (e.g. --custom-source which re-clones
|
|
244
|
+
// modules at a new version — quick-update skips that entirely).
|
|
245
|
+
if (choices.length === 0) {
|
|
246
|
+
throw new Error('No valid actions available for this installation');
|
|
247
|
+
}
|
|
248
|
+
const hasQuickUpdate = choices.some((c) => c.value === 'quick-update');
|
|
249
|
+
const needsFullUpdate = !!options.customSource;
|
|
250
|
+
actionType = hasQuickUpdate && !needsFullUpdate ? 'quick-update' : (choices.find((c) => c.value === 'update') || choices[0]).value;
|
|
251
|
+
await prompts.log.info(`Non-interactive mode (--yes): defaulting to ${actionType}`);
|
|
252
|
+
} else {
|
|
253
|
+
actionType = await prompts.select({
|
|
254
|
+
message: 'How would you like to proceed?',
|
|
255
|
+
choices: choices,
|
|
256
|
+
default: choices[0].value,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Handle quick update separately
|
|
261
|
+
if (actionType === 'quick-update') {
|
|
262
|
+
return {
|
|
263
|
+
actionType: 'quick-update',
|
|
264
|
+
directory: confirmedDirectory,
|
|
265
|
+
skipPrompts: options.yes || false,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// If actionType === 'update', handle it with the new flow
|
|
270
|
+
// Return early with modify configuration
|
|
271
|
+
if (actionType === 'update') {
|
|
272
|
+
// Get existing installation info
|
|
273
|
+
const { installedModuleIds, installedModuleVersions } = await this.getExistingInstallation(confirmedDirectory);
|
|
274
|
+
|
|
275
|
+
await prompts.log.message(`Found existing modules: ${[...installedModuleIds].join(', ')}`);
|
|
276
|
+
|
|
277
|
+
// Unified module selection - all modules in one grouped multiselect
|
|
278
|
+
let selectedModules;
|
|
279
|
+
if (options.modules) {
|
|
280
|
+
// Use modules from command-line
|
|
281
|
+
selectedModules = options.modules
|
|
282
|
+
.split(',')
|
|
283
|
+
.map((m) => m.trim())
|
|
284
|
+
.filter(Boolean);
|
|
285
|
+
await prompts.log.info(`Using modules from command-line: ${selectedModules.join(', ')}`);
|
|
286
|
+
} else if (options.customSource && !options.yes) {
|
|
287
|
+
// Custom source without --modules or --yes: start with empty list
|
|
288
|
+
// (only custom source modules + core will be installed).
|
|
289
|
+
// When --yes is also set, fall through to the --yes branch so all
|
|
290
|
+
// installed modules are included alongside the custom source modules.
|
|
291
|
+
selectedModules = [];
|
|
292
|
+
} else if (options.yes) {
|
|
293
|
+
selectedModules = await this.getDefaultModules(installedModuleIds);
|
|
294
|
+
await prompts.log.info(
|
|
295
|
+
`Non-interactive mode (--yes): using default modules (installed + defaults): ${selectedModules.join(', ')}`,
|
|
296
|
+
);
|
|
297
|
+
} else {
|
|
298
|
+
selectedModules = await this.selectAllModules(installedModuleIds, installedModuleVersions, channelOptions);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Resolve custom sources from --custom-source flag
|
|
302
|
+
if (options.customSource) {
|
|
303
|
+
const customCodes = await this._resolveCustomSourcesCli(options.customSource);
|
|
304
|
+
for (const code of customCodes) {
|
|
305
|
+
if (!selectedModules.includes(code)) selectedModules.push(code);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Ensure core is in the modules list
|
|
310
|
+
if (!selectedModules.includes('core')) {
|
|
311
|
+
selectedModules.unshift('core');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const retainedModuleResult = await this._retainUnavailableInstalledModules(selectedModules, installedModuleIds, xiaomaDir, {
|
|
315
|
+
preserveUnselected: options.yes && !options.modules,
|
|
316
|
+
});
|
|
317
|
+
selectedModules = retainedModuleResult.selectedModules;
|
|
318
|
+
const preservedModules = retainedModuleResult.preserveModules;
|
|
319
|
+
|
|
320
|
+
if (preservedModules.length > 0) {
|
|
321
|
+
await prompts.log.warn(
|
|
322
|
+
`Retaining ${preservedModules.length} installed module(s) with no available source: ${preservedModules.join(', ')}`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// For existing installs, resolve per-module update decisions BEFORE
|
|
327
|
+
// we clone anything. Reads the existing manifest's recorded channel
|
|
328
|
+
// per module and prompts the user on available upgrades (patch/minor
|
|
329
|
+
// default Y, major default N). Legacy entries with no channel are
|
|
330
|
+
// migrated here too. Mutates channelOptions.pins to lock rejections.
|
|
331
|
+
await this._resolveUpdateChannels({
|
|
332
|
+
xiaomaDir,
|
|
333
|
+
selectedModules,
|
|
334
|
+
channelOptions,
|
|
335
|
+
yes: options.yes || false,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Get tool selection
|
|
339
|
+
const toolSelection = await this.promptToolSelection(confirmedDirectory, options);
|
|
340
|
+
|
|
341
|
+
const { moduleConfigs, setOverrides } = await this.collectModuleConfigs(confirmedDirectory, selectedModules, {
|
|
342
|
+
...options,
|
|
343
|
+
channelOptions,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Warn about --pin/--next flags that refer to modules the user didn't
|
|
347
|
+
// select, or that target bundled modules (core/xmc) where channel
|
|
348
|
+
// flags don't apply.
|
|
349
|
+
{
|
|
350
|
+
const bundledCodes = await this._bundledModuleCodes();
|
|
351
|
+
for (const warning of [
|
|
352
|
+
...orphanPinWarnings(channelOptions, selectedModules),
|
|
353
|
+
...bundledTargetWarnings(channelOptions, bundledCodes),
|
|
354
|
+
]) {
|
|
355
|
+
await prompts.log.warn(warning);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
actionType: 'update',
|
|
361
|
+
directory: confirmedDirectory,
|
|
362
|
+
modules: selectedModules,
|
|
363
|
+
ides: toolSelection.ides,
|
|
364
|
+
skipIde: toolSelection.skipIde,
|
|
365
|
+
coreConfig: moduleConfigs.core || {},
|
|
366
|
+
moduleConfigs: moduleConfigs,
|
|
367
|
+
setOverrides,
|
|
368
|
+
skipPrompts: options.yes || false,
|
|
369
|
+
channelOptions,
|
|
370
|
+
_preserveModules: preservedModules,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// This section is only for new installations (update returns early above)
|
|
376
|
+
const { installedModuleIds, installedModuleVersions } = await this.getExistingInstallation(confirmedDirectory);
|
|
377
|
+
|
|
378
|
+
// Unified module selection - all modules in one grouped multiselect
|
|
379
|
+
let selectedModules;
|
|
380
|
+
if (options.modules) {
|
|
381
|
+
// Use modules from command-line
|
|
382
|
+
selectedModules = options.modules
|
|
383
|
+
.split(',')
|
|
384
|
+
.map((m) => m.trim())
|
|
385
|
+
.filter(Boolean);
|
|
386
|
+
await prompts.log.info(`Using modules from command-line: ${selectedModules.join(', ')}`);
|
|
387
|
+
} else if (options.customSource) {
|
|
388
|
+
// Custom source without --modules: start with empty list (core added below)
|
|
389
|
+
selectedModules = [];
|
|
390
|
+
} else if (options.yes) {
|
|
391
|
+
// Use default modules when --yes flag is set
|
|
392
|
+
selectedModules = await this.getDefaultModules(installedModuleIds);
|
|
393
|
+
await prompts.log.info(`Using default modules (--yes flag): ${selectedModules.join(', ')}`);
|
|
394
|
+
} else {
|
|
395
|
+
selectedModules = await this.selectAllModules(installedModuleIds, installedModuleVersions, channelOptions);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Resolve custom sources from --custom-source flag
|
|
399
|
+
if (options.customSource) {
|
|
400
|
+
const customCodes = await this._resolveCustomSourcesCli(options.customSource);
|
|
401
|
+
for (const code of customCodes) {
|
|
402
|
+
if (!selectedModules.includes(code)) selectedModules.push(code);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Ensure core is in the modules list
|
|
407
|
+
if (!selectedModules.includes('core')) {
|
|
408
|
+
selectedModules.unshift('core');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Interactive channel gate: "Ready to install (all stable)? [Y/n]"
|
|
412
|
+
// Only shown for fresh installs with no channel flags and an external module
|
|
413
|
+
// selected. Skipped for prerelease launches because channelOptions.global
|
|
414
|
+
// was already seeded to 'next' upstream. Non-interactive installs skip this
|
|
415
|
+
// and fall through to the registry default (stable) or whatever flags were
|
|
416
|
+
// supplied.
|
|
417
|
+
await this._interactiveChannelGate({ options, channelOptions, selectedModules });
|
|
418
|
+
|
|
419
|
+
let toolSelection = await this.promptToolSelection(confirmedDirectory, options);
|
|
420
|
+
const { moduleConfigs, setOverrides } = await this.collectModuleConfigs(confirmedDirectory, selectedModules, {
|
|
421
|
+
...options,
|
|
422
|
+
channelOptions,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Warn about --pin/--next flags that refer to modules the user didn't
|
|
426
|
+
// select, or that target bundled modules (core/xmc) where channel
|
|
427
|
+
// flags don't apply.
|
|
428
|
+
{
|
|
429
|
+
const bundledCodes = await this._bundledModuleCodes();
|
|
430
|
+
for (const warning of [
|
|
431
|
+
...orphanPinWarnings(channelOptions, selectedModules),
|
|
432
|
+
...bundledTargetWarnings(channelOptions, bundledCodes),
|
|
433
|
+
]) {
|
|
434
|
+
await prompts.log.warn(warning);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
actionType: 'install',
|
|
440
|
+
directory: confirmedDirectory,
|
|
441
|
+
modules: selectedModules,
|
|
442
|
+
ides: toolSelection.ides,
|
|
443
|
+
skipIde: toolSelection.skipIde,
|
|
444
|
+
coreConfig: moduleConfigs.core || {},
|
|
445
|
+
moduleConfigs: moduleConfigs,
|
|
446
|
+
setOverrides,
|
|
447
|
+
skipPrompts: options.yes || false,
|
|
448
|
+
channelOptions,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Prompt for tool/IDE selection (called after module configuration)
|
|
454
|
+
* Uses a split prompt approach:
|
|
455
|
+
* 1. Recommended tools - standard multiselect for preferred tools
|
|
456
|
+
* 2. Additional tools - autocompleteMultiselect with search capability
|
|
457
|
+
* @param {string} projectDir - Project directory to check for existing IDEs
|
|
458
|
+
* @param {Object} options - Command-line options
|
|
459
|
+
* @returns {Object} Tool configuration
|
|
460
|
+
*/
|
|
461
|
+
_parseToolsFlag(toolsArg, allKnownValues) {
|
|
462
|
+
const selectedIdes = toolsArg
|
|
463
|
+
.split(',')
|
|
464
|
+
.map((t) => t.trim())
|
|
465
|
+
.filter(Boolean);
|
|
466
|
+
|
|
467
|
+
if (selectedIdes.length === 0) {
|
|
468
|
+
const err = new Error(
|
|
469
|
+
'--tools was passed empty. Provide at least one tool ID (e.g. --tools claude-code) or run with --list-tools to see valid IDs.',
|
|
470
|
+
);
|
|
471
|
+
err.expected = true;
|
|
472
|
+
throw err;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const unknown = selectedIdes.filter((id) => !allKnownValues.has(id));
|
|
476
|
+
if (unknown.length > 0) {
|
|
477
|
+
const err = new Error(
|
|
478
|
+
[
|
|
479
|
+
`Unknown tool ID${unknown.length === 1 ? '' : 's'}: ${unknown.join(', ')}`,
|
|
480
|
+
'',
|
|
481
|
+
'Run with --list-tools to see all valid IDs.',
|
|
482
|
+
'Common: claude-code, cursor, copilot, windsurf, cline',
|
|
483
|
+
].join('\n'),
|
|
484
|
+
);
|
|
485
|
+
err.expected = true;
|
|
486
|
+
throw err;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return selectedIdes;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async promptToolSelection(projectDir, options = {}) {
|
|
493
|
+
const { ExistingInstall } = require('./core/existing-install');
|
|
494
|
+
const { Installer } = require('./core/installer');
|
|
495
|
+
const installer = new Installer();
|
|
496
|
+
const { xiaomaDir } = await installer.findXiaomaDir(projectDir || process.cwd());
|
|
497
|
+
const existingInstall = await ExistingInstall.detect(xiaomaDir);
|
|
498
|
+
const configuredIdes = existingInstall.ides;
|
|
499
|
+
|
|
500
|
+
// Get IDE manager to fetch available IDEs dynamically
|
|
501
|
+
const { IdeManager } = require('./ide/manager');
|
|
502
|
+
const ideManager = new IdeManager();
|
|
503
|
+
await ideManager.ensureInitialized(); // IMPORTANT: Must initialize before getting IDEs
|
|
504
|
+
|
|
505
|
+
const preferredIdes = ideManager.getPreferredIdes();
|
|
506
|
+
const otherIdes = ideManager.getOtherIdes();
|
|
507
|
+
|
|
508
|
+
// Determine which configured IDEs are in "preferred" vs "other" categories
|
|
509
|
+
const configuredPreferred = configuredIdes.filter((id) => preferredIdes.some((ide) => ide.value === id));
|
|
510
|
+
const configuredOther = configuredIdes.filter((id) => otherIdes.some((ide) => ide.value === id));
|
|
511
|
+
|
|
512
|
+
// Warn about previously configured tools that are no longer available
|
|
513
|
+
const allKnownValues = new Set([...preferredIdes, ...otherIdes].map((ide) => ide.value));
|
|
514
|
+
const unknownTools = configuredIdes.filter((id) => id && typeof id === 'string' && !allKnownValues.has(id));
|
|
515
|
+
if (unknownTools.length > 0) {
|
|
516
|
+
await prompts.log.warn(`Previously configured tools are no longer available: ${unknownTools.join(', ')}`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
520
|
+
// UPGRADE PATH: If tools already configured, show all tools with configured at top
|
|
521
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
522
|
+
if (configuredIdes.length > 0) {
|
|
523
|
+
const allTools = [...preferredIdes, ...otherIdes];
|
|
524
|
+
|
|
525
|
+
// Non-interactive: handle --tools and --yes flags before interactive prompt
|
|
526
|
+
// Use !== undefined so an explicit --tools "" falls through to _parseToolsFlag and
|
|
527
|
+
// gets a specific "passed empty" error instead of being silently ignored.
|
|
528
|
+
if (options.tools !== undefined) {
|
|
529
|
+
const selectedIdes = this._parseToolsFlag(options.tools, allKnownValues);
|
|
530
|
+
await prompts.log.info(`Using tools from command-line: ${selectedIdes.join(', ')}`);
|
|
531
|
+
await this.displaySelectedTools(selectedIdes, preferredIdes, allTools);
|
|
532
|
+
return { ides: selectedIdes, skipIde: false };
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (options.yes) {
|
|
536
|
+
await prompts.log.info(`Non-interactive mode (--yes): keeping configured tools: ${configuredIdes.join(', ')}`);
|
|
537
|
+
await this.displaySelectedTools(configuredIdes, preferredIdes, allTools);
|
|
538
|
+
return { ides: configuredIdes, skipIde: false };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Sort: configured tools first, then preferred, then others
|
|
542
|
+
const sortedTools = [
|
|
543
|
+
...allTools.filter((ide) => configuredIdes.includes(ide.value)),
|
|
544
|
+
...allTools.filter((ide) => !configuredIdes.includes(ide.value)),
|
|
545
|
+
];
|
|
546
|
+
|
|
547
|
+
const upgradeOptions = sortedTools.map((ide) => {
|
|
548
|
+
const isConfigured = configuredIdes.includes(ide.value);
|
|
549
|
+
const isPreferred = preferredIdes.some((p) => p.value === ide.value);
|
|
550
|
+
let label = ide.name;
|
|
551
|
+
if (isPreferred) label += ' ⭐';
|
|
552
|
+
if (isConfigured) label += ' ✅';
|
|
553
|
+
return { label, value: ide.value };
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Sort initialValues to match display order
|
|
557
|
+
const sortedInitialValues = sortedTools.filter((ide) => configuredIdes.includes(ide.value)).map((ide) => ide.value);
|
|
558
|
+
|
|
559
|
+
const upgradeSelected = await prompts.autocompleteMultiselect({
|
|
560
|
+
message: 'Integrate with',
|
|
561
|
+
options: upgradeOptions,
|
|
562
|
+
initialValues: sortedInitialValues,
|
|
563
|
+
required: false,
|
|
564
|
+
maxItems: 8,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
const selectedIdes = upgradeSelected || [];
|
|
568
|
+
|
|
569
|
+
if (selectedIdes.length === 0) {
|
|
570
|
+
const confirmNoTools = await prompts.confirm({
|
|
571
|
+
message: 'No tools selected. Continue without installing any tools?',
|
|
572
|
+
default: false,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
if (!confirmNoTools) {
|
|
576
|
+
return this.promptToolSelection(projectDir, options);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return { ides: [], skipIde: true };
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Display selected tools
|
|
583
|
+
await this.displaySelectedTools(selectedIdes, preferredIdes, allTools);
|
|
584
|
+
|
|
585
|
+
return { ides: selectedIdes, skipIde: false };
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
589
|
+
// NEW INSTALL: Show all tools with search
|
|
590
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
591
|
+
const allTools = [...preferredIdes, ...otherIdes];
|
|
592
|
+
|
|
593
|
+
const allToolOptions = allTools.map((ide) => {
|
|
594
|
+
const isPreferred = preferredIdes.some((p) => p.value === ide.value);
|
|
595
|
+
let label = ide.name;
|
|
596
|
+
if (isPreferred) label += ' ⭐';
|
|
597
|
+
return {
|
|
598
|
+
label,
|
|
599
|
+
value: ide.value,
|
|
600
|
+
};
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
let selectedIdes = [];
|
|
604
|
+
|
|
605
|
+
// Check if tools are provided via command-line.
|
|
606
|
+
// Use !== undefined so an explicit --tools "" still hits _parseToolsFlag's empty-value error.
|
|
607
|
+
if (options.tools !== undefined) {
|
|
608
|
+
selectedIdes = this._parseToolsFlag(options.tools, allKnownValues);
|
|
609
|
+
await prompts.log.info(`Using tools from command-line: ${selectedIdes.join(', ')}`);
|
|
610
|
+
await this.displaySelectedTools(selectedIdes, preferredIdes, allTools);
|
|
611
|
+
return { ides: selectedIdes, skipIde: false };
|
|
612
|
+
} else if (options.yes) {
|
|
613
|
+
// If --yes flag is set, skip tool prompt and use previously configured tools or empty
|
|
614
|
+
if (configuredIdes.length > 0) {
|
|
615
|
+
await prompts.log.info(`Using previously configured tools (--yes flag): ${configuredIdes.join(', ')}`);
|
|
616
|
+
await this.displaySelectedTools(configuredIdes, preferredIdes, allTools);
|
|
617
|
+
return { ides: configuredIdes, skipIde: false };
|
|
618
|
+
} else {
|
|
619
|
+
const err = new Error(
|
|
620
|
+
[
|
|
621
|
+
'--tools is required for non-interactive install (--yes / -y) when no tools are previously configured.',
|
|
622
|
+
'',
|
|
623
|
+
'Common: claude-code, cursor, copilot, windsurf, cline',
|
|
624
|
+
'See all supported tools: npx @zeyue0329/xiaoma-cli install --list-tools',
|
|
625
|
+
'',
|
|
626
|
+
'Example: npx @zeyue0329/xiaoma-cli install --modules xmc --tools claude-code -y',
|
|
627
|
+
].join('\n'),
|
|
628
|
+
);
|
|
629
|
+
err.expected = true;
|
|
630
|
+
throw err;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Interactive mode
|
|
635
|
+
const interactiveSelectedIdes = await prompts.autocompleteMultiselect({
|
|
636
|
+
message: 'Integrate with:',
|
|
637
|
+
options: allToolOptions,
|
|
638
|
+
initialValues: configuredIdes.length > 0 ? configuredIdes : undefined,
|
|
639
|
+
required: false,
|
|
640
|
+
maxItems: 8,
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
selectedIdes = interactiveSelectedIdes || [];
|
|
644
|
+
|
|
645
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
646
|
+
// STEP 3: Confirm if no tools selected
|
|
647
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
648
|
+
if (selectedIdes.length === 0) {
|
|
649
|
+
const confirmNoTools = await prompts.confirm({
|
|
650
|
+
message: 'No tools selected. Continue without installing any tools?',
|
|
651
|
+
default: false,
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
if (!confirmNoTools) {
|
|
655
|
+
// User wants to select tools - recurse
|
|
656
|
+
return this.promptToolSelection(projectDir, options);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
ides: [],
|
|
661
|
+
skipIde: true,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Display selected tools
|
|
666
|
+
await this.displaySelectedTools(selectedIdes, preferredIdes, allTools);
|
|
667
|
+
|
|
668
|
+
return {
|
|
669
|
+
ides: selectedIdes,
|
|
670
|
+
skipIde: selectedIdes.length === 0,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Prompt for update configuration
|
|
676
|
+
* @returns {Object} Update configuration
|
|
677
|
+
*/
|
|
678
|
+
async promptUpdate() {
|
|
679
|
+
const backupFirst = await prompts.confirm({
|
|
680
|
+
message: 'Create backup before updating?',
|
|
681
|
+
default: true,
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
const preserveCustomizations = await prompts.confirm({
|
|
685
|
+
message: 'Preserve local customizations?',
|
|
686
|
+
default: true,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
return { backupFirst, preserveCustomizations };
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Confirm action
|
|
694
|
+
* @param {string} message - Confirmation message
|
|
695
|
+
* @param {boolean} defaultValue - Default value
|
|
696
|
+
* @returns {boolean} User confirmation
|
|
697
|
+
*/
|
|
698
|
+
async confirm(message, defaultValue = false) {
|
|
699
|
+
return await prompts.confirm({
|
|
700
|
+
message,
|
|
701
|
+
default: defaultValue,
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Get confirmed directory from user
|
|
707
|
+
* @returns {string} Confirmed directory path
|
|
708
|
+
*/
|
|
709
|
+
async getConfirmedDirectory() {
|
|
710
|
+
let confirmedDirectory = null;
|
|
711
|
+
while (!confirmedDirectory) {
|
|
712
|
+
const directoryAnswer = await this.promptForDirectory();
|
|
713
|
+
await this.displayDirectoryInfo(directoryAnswer.directory);
|
|
714
|
+
|
|
715
|
+
if (await this.confirmDirectory(directoryAnswer.directory)) {
|
|
716
|
+
confirmedDirectory = directoryAnswer.directory;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return confirmedDirectory;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Get existing installation info and installed modules
|
|
724
|
+
* @param {string} directory - Installation directory
|
|
725
|
+
* @returns {Object} Object with existingInstall, installedModuleIds, installedModuleVersions, and xiaomaDir
|
|
726
|
+
*/
|
|
727
|
+
async getExistingInstallation(directory) {
|
|
728
|
+
const { ExistingInstall } = require('./core/existing-install');
|
|
729
|
+
const { Installer } = require('./core/installer');
|
|
730
|
+
const installer = new Installer();
|
|
731
|
+
const { xiaomaDir } = await installer.findXiaomaDir(directory);
|
|
732
|
+
const existingInstall = await ExistingInstall.detect(xiaomaDir);
|
|
733
|
+
const installedModuleIds = new Set(existingInstall.moduleIds);
|
|
734
|
+
const installedModuleVersions = new Map();
|
|
735
|
+
const manifestModules = await manifest.getAllModuleVersions(xiaomaDir);
|
|
736
|
+
|
|
737
|
+
for (const module of manifestModules) {
|
|
738
|
+
if (module?.name && module.version) {
|
|
739
|
+
installedModuleVersions.set(module.name, module.version);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
for (const module of existingInstall.modules) {
|
|
744
|
+
if (module?.id && module.version && module.version !== 'unknown' && !installedModuleVersions.has(module.id)) {
|
|
745
|
+
installedModuleVersions.set(module.id, module.version);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (existingInstall.hasCore && existingInstall.version && !installedModuleVersions.has('core')) {
|
|
750
|
+
installedModuleVersions.set('core', existingInstall.version);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return { existingInstall, installedModuleIds, installedModuleVersions, xiaomaDir };
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Collect all module configurations (core + selected modules).
|
|
758
|
+
* All interactive prompting happens here in the UI layer.
|
|
759
|
+
* @param {string} directory - Installation directory
|
|
760
|
+
* @param {string[]} modules - Modules to configure (including 'core')
|
|
761
|
+
* @param {Object} options - Command-line options
|
|
762
|
+
* @returns {Object} Collected module configurations keyed by module name
|
|
763
|
+
*/
|
|
764
|
+
async collectModuleConfigs(directory, modules, options = {}) {
|
|
765
|
+
const { OfficialModules } = require('./modules/official-modules');
|
|
766
|
+
|
|
767
|
+
// Parse --set up front purely to surface user-error before the install
|
|
768
|
+
// burns time on the network / filesystem. The actual application happens
|
|
769
|
+
// in installer.install() as a post-write TOML patch — see
|
|
770
|
+
// `tools/installer/set-overrides.js`. We also warn about overrides
|
|
771
|
+
// targeting modules the user didn't include, since those will silently
|
|
772
|
+
// miss the file the patch step looks for.
|
|
773
|
+
let setOverrides = {};
|
|
774
|
+
try {
|
|
775
|
+
setOverrides = parseSetEntries(options.set || []);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
// install.js validated already; rethrow as-is for the user.
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
// Drop overrides for modules that aren't in the install set so the
|
|
781
|
+
// post-install patch step doesn't create orphan sections in config.toml
|
|
782
|
+
// for modules that were never installed.
|
|
783
|
+
const selectedModuleSet = new Set(['core', ...modules]);
|
|
784
|
+
for (const moduleCode of Object.keys(setOverrides)) {
|
|
785
|
+
if (!selectedModuleSet.has(moduleCode)) {
|
|
786
|
+
await prompts.log.warn(
|
|
787
|
+
`--set ${moduleCode}.* — module '${moduleCode}' is not in the install set; values will be ignored. Add it to --modules to apply.`,
|
|
788
|
+
);
|
|
789
|
+
delete setOverrides[moduleCode];
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const configCollector = new OfficialModules({ channelOptions: options.channelOptions });
|
|
794
|
+
|
|
795
|
+
// Seed core config from CLI options and/or --yes defaults.
|
|
796
|
+
const hasCoreFlags = Boolean(
|
|
797
|
+
options.userName || options.communicationLanguage || options.documentOutputLanguage || options.outputFolder,
|
|
798
|
+
);
|
|
799
|
+
if (hasCoreFlags || options.yes) {
|
|
800
|
+
const coreConfig = {};
|
|
801
|
+
if (options.userName) {
|
|
802
|
+
coreConfig.user_name = options.userName;
|
|
803
|
+
await prompts.log.info(`Using user name from command-line: ${options.userName}`);
|
|
804
|
+
}
|
|
805
|
+
if (options.communicationLanguage) {
|
|
806
|
+
coreConfig.communication_language = options.communicationLanguage;
|
|
807
|
+
await prompts.log.info(`Using communication language from command-line: ${options.communicationLanguage}`);
|
|
808
|
+
}
|
|
809
|
+
if (options.documentOutputLanguage) {
|
|
810
|
+
coreConfig.document_output_language = options.documentOutputLanguage;
|
|
811
|
+
await prompts.log.info(`Using document output language from command-line: ${options.documentOutputLanguage}`);
|
|
812
|
+
}
|
|
813
|
+
if (options.outputFolder) {
|
|
814
|
+
coreConfig.output_folder = options.outputFolder;
|
|
815
|
+
await prompts.log.info(`Using output folder from command-line: ${options.outputFolder}`);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Load existing config to merge with provided options
|
|
819
|
+
await configCollector.loadExistingConfig(directory);
|
|
820
|
+
const existingConfig = configCollector.collectedConfig.core || {};
|
|
821
|
+
|
|
822
|
+
if (options.yes) {
|
|
823
|
+
// Non-interactive: EVERY core key that wasn't supplied via flag (or carried
|
|
824
|
+
// over from a prior install) must still receive its default. Otherwise core
|
|
825
|
+
// cross-references like {output_folder} have nothing to resolve against and
|
|
826
|
+
// leak the literal placeholder into module configs (and create a dir named
|
|
827
|
+
// "{output_folder}"). Precedence: flag > existing install > default.
|
|
828
|
+
let safeUsername;
|
|
829
|
+
try {
|
|
830
|
+
safeUsername = os.userInfo().username;
|
|
831
|
+
} catch {
|
|
832
|
+
safeUsername = process.env.USER || process.env.USERNAME || 'User';
|
|
833
|
+
}
|
|
834
|
+
const defaultUsername = safeUsername.charAt(0).toUpperCase() + safeUsername.slice(1);
|
|
835
|
+
const coreDefaults = {
|
|
836
|
+
user_name: defaultUsername,
|
|
837
|
+
// {directory_name} default per src/core-skills/module.yaml — matches what the
|
|
838
|
+
// interactive flow resolves via buildQuestion()'s {directory_name} placeholder.
|
|
839
|
+
project_name: path.basename(directory),
|
|
840
|
+
communication_language: 'English',
|
|
841
|
+
document_output_language: 'English',
|
|
842
|
+
output_folder: '_xiaoma-output',
|
|
843
|
+
};
|
|
844
|
+
configCollector.collectedConfig.core = { ...coreDefaults, ...existingConfig, ...coreConfig };
|
|
845
|
+
// existingConfig can carry a corrupt user_name (empty or the literal
|
|
846
|
+
// "[USER_NAME]" placeholder from a legacy/hand-edited install). Because core
|
|
847
|
+
// is seeded directly here, collectAllConfigurations skips it and the normal
|
|
848
|
+
// [USER_NAME] sanitizer never runs — so guard it explicitly.
|
|
849
|
+
const seededUserName = configCollector.collectedConfig.core.user_name;
|
|
850
|
+
if (!seededUserName || seededUserName === '[USER_NAME]') {
|
|
851
|
+
configCollector.collectedConfig.core.user_name = defaultUsername;
|
|
852
|
+
}
|
|
853
|
+
await prompts.log.info('Using default configuration for unspecified core settings (--yes flag)');
|
|
854
|
+
} else {
|
|
855
|
+
configCollector.collectedConfig.core = { ...existingConfig, ...coreConfig };
|
|
856
|
+
// Interactive: collect any core keys the user didn't pass as flags.
|
|
857
|
+
if (!options.userName || !options.communicationLanguage || !options.documentOutputLanguage || !options.outputFolder) {
|
|
858
|
+
await configCollector.collectModuleConfig('core', directory, false, true);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Collect all module configs — core is skipped if already seeded above
|
|
864
|
+
await configCollector.collectAllConfigurations(modules, directory, {
|
|
865
|
+
skipPrompts: options.yes || false,
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
return { moduleConfigs: configCollector.collectedConfig, setOverrides };
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Select all modules across three tiers: official, community, and custom URL.
|
|
873
|
+
* @param {Set} installedModuleIds - Currently installed module IDs
|
|
874
|
+
* @param {Map<string, string>} installedModuleVersions - Installed module versions from the local manifest
|
|
875
|
+
* @param {Object|null} channelOptions - Parsed installer channel options
|
|
876
|
+
* @returns {Array} Selected module codes (excluding core)
|
|
877
|
+
*/
|
|
878
|
+
async selectAllModules(installedModuleIds = new Set(), installedModuleVersions = new Map(), channelOptions = null) {
|
|
879
|
+
// Phase 1: Official modules
|
|
880
|
+
const officialSelected = await this._selectOfficialModules(installedModuleIds, installedModuleVersions, channelOptions);
|
|
881
|
+
|
|
882
|
+
// Identify installed modules that aren't official (previously installed
|
|
883
|
+
// community modules or custom-source modules). Preserve them on update;
|
|
884
|
+
// they can be managed via --custom-source, uninstall, or a dedicated installer.
|
|
885
|
+
const externalManager = new ExternalModuleManager();
|
|
886
|
+
const registryModules = await externalManager.listAvailable();
|
|
887
|
+
const officialRegistryCodes = new Set(['core', 'xmc', 'xpm', ...registryModules.map((m) => m.code)]);
|
|
888
|
+
const installedNonOfficial = [...installedModuleIds].filter((id) => !officialRegistryCodes.has(id));
|
|
889
|
+
|
|
890
|
+
// Phase 2: Custom URL modules
|
|
891
|
+
const customSelected = await this._addCustomUrlModules(installedModuleIds);
|
|
892
|
+
|
|
893
|
+
const allSelected = new Set([...officialSelected, ...customSelected, ...installedNonOfficial]);
|
|
894
|
+
|
|
895
|
+
return [...allSelected];
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Select official modules using autocompleteMultiselect.
|
|
900
|
+
* Extracted from the original selectAllModules - unchanged behavior.
|
|
901
|
+
* @param {Set} installedModuleIds - Currently installed module IDs
|
|
902
|
+
* @param {Map<string, string>} installedModuleVersions - Installed module versions from the local manifest
|
|
903
|
+
* @param {Object|null} channelOptions - Parsed installer channel options
|
|
904
|
+
* @returns {Array} Selected official module codes
|
|
905
|
+
*/
|
|
906
|
+
async _selectOfficialModules(installedModuleIds = new Set(), installedModuleVersions = new Map(), channelOptions = null) {
|
|
907
|
+
// Built-in modules (core, xmc) come from local source, not the registry
|
|
908
|
+
const { OfficialModules } = require('./modules/official-modules');
|
|
909
|
+
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
|
|
910
|
+
|
|
911
|
+
// External modules come from the registry (with fallback)
|
|
912
|
+
const externalManager = new ExternalModuleManager();
|
|
913
|
+
const registryModules = await externalManager.listAvailable();
|
|
914
|
+
|
|
915
|
+
const allOptions = [];
|
|
916
|
+
const initialValues = [];
|
|
917
|
+
const lockedValues = ['core'];
|
|
918
|
+
|
|
919
|
+
const buildModuleEntry = async (code, name, description, isDefault, repoUrl = null, registryDefault = null) => {
|
|
920
|
+
const isInstalled = installedModuleIds.has(code);
|
|
921
|
+
const installedVersion = installedModuleVersions.get(code) || '';
|
|
922
|
+
const versionState = await getModuleVersion(code, { repoUrl, registryDefault, channelOptions });
|
|
923
|
+
const label = buildModuleLabel(name, versionState.version, installedVersion);
|
|
924
|
+
return {
|
|
925
|
+
label,
|
|
926
|
+
value: code,
|
|
927
|
+
hint: description,
|
|
928
|
+
selected: isInstalled || isDefault,
|
|
929
|
+
lookupAttempted: versionState.lookupAttempted,
|
|
930
|
+
lookupSucceeded: versionState.lookupSucceeded,
|
|
931
|
+
};
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// Add built-in modules first (always available regardless of network)
|
|
935
|
+
const builtInCodes = new Set();
|
|
936
|
+
for (const mod of builtInModules) {
|
|
937
|
+
const code = mod.id;
|
|
938
|
+
builtInCodes.add(code);
|
|
939
|
+
const entry = await buildModuleEntry(code, mod.name, mod.description, mod.defaultSelected);
|
|
940
|
+
allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint });
|
|
941
|
+
if (entry.selected) {
|
|
942
|
+
initialValues.push(code);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Add external registry modules (skip built-in duplicates)
|
|
947
|
+
const externalRegistryModules = registryModules.filter((mod) => !mod.builtIn && !builtInCodes.has(mod.code));
|
|
948
|
+
let externalRegistryEntries = [];
|
|
949
|
+
if (externalRegistryModules.length > 0) {
|
|
950
|
+
const spinner = await prompts.spinner();
|
|
951
|
+
spinner.start('Checking latest module versions...');
|
|
952
|
+
|
|
953
|
+
externalRegistryEntries = await Promise.all(
|
|
954
|
+
externalRegistryModules.map(async (mod) => ({
|
|
955
|
+
code: mod.code,
|
|
956
|
+
entry: await buildModuleEntry(
|
|
957
|
+
mod.code,
|
|
958
|
+
mod.name,
|
|
959
|
+
mod.description,
|
|
960
|
+
mod.defaultSelected,
|
|
961
|
+
mod.url || null,
|
|
962
|
+
mod.defaultChannel || null,
|
|
963
|
+
),
|
|
964
|
+
})),
|
|
965
|
+
);
|
|
966
|
+
|
|
967
|
+
spinner.stop('Checked latest module versions.');
|
|
968
|
+
|
|
969
|
+
const attemptedLookups = externalRegistryEntries.filter(({ entry }) => entry.lookupAttempted).length;
|
|
970
|
+
const successfulLookups = externalRegistryEntries.filter(({ entry }) => entry.lookupSucceeded).length;
|
|
971
|
+
if (attemptedLookups > 0 && successfulLookups === 0) {
|
|
972
|
+
await prompts.log.warn('Could not check latest module versions; showing cached/local versions.');
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
for (const { code, entry } of externalRegistryEntries) {
|
|
976
|
+
allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint });
|
|
977
|
+
if (entry.selected) {
|
|
978
|
+
initialValues.push(code);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const selected = await prompts.autocompleteMultiselect({
|
|
983
|
+
message: 'Select official modules to install:',
|
|
984
|
+
options: allOptions,
|
|
985
|
+
initialValues: initialValues.length > 0 ? initialValues : undefined,
|
|
986
|
+
lockedValues,
|
|
987
|
+
required: true,
|
|
988
|
+
maxItems: allOptions.length,
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
const result = selected ? [...selected] : [];
|
|
992
|
+
|
|
993
|
+
if (result.length > 0) {
|
|
994
|
+
const moduleLines = result.map((moduleId) => {
|
|
995
|
+
const opt = allOptions.find((o) => o.value === moduleId);
|
|
996
|
+
return ` \u2022 ${opt?.label || moduleId}`;
|
|
997
|
+
});
|
|
998
|
+
await prompts.log.message('Selected official modules:\n' + moduleLines.join('\n'));
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Prompt user to install modules from custom sources (Git URLs or local paths).
|
|
1006
|
+
* @param {Set} installedModuleIds - Currently installed module IDs
|
|
1007
|
+
* @returns {Array} Selected custom module code strings
|
|
1008
|
+
*/
|
|
1009
|
+
async _addCustomUrlModules(installedModuleIds = new Set()) {
|
|
1010
|
+
const addCustom = await prompts.confirm({
|
|
1011
|
+
message: 'Do you want to install custom or community modules (Git URL or local path)?',
|
|
1012
|
+
default: false,
|
|
1013
|
+
});
|
|
1014
|
+
if (!addCustom) return [];
|
|
1015
|
+
|
|
1016
|
+
const { CustomModuleManager } = require('./modules/custom-module-manager');
|
|
1017
|
+
const customMgr = new CustomModuleManager();
|
|
1018
|
+
const selectedModules = [];
|
|
1019
|
+
|
|
1020
|
+
let addMore = true;
|
|
1021
|
+
while (addMore) {
|
|
1022
|
+
const sourceInput = await prompts.text({
|
|
1023
|
+
message: 'Git URL or local path:',
|
|
1024
|
+
placeholder: 'https://github.com/owner/repo or /path/to/module',
|
|
1025
|
+
validate: (input) => {
|
|
1026
|
+
if (!input || input.trim() === '') return 'Source is required';
|
|
1027
|
+
const result = customMgr.parseSource(input.trim());
|
|
1028
|
+
return result.isValid ? undefined : result.error;
|
|
1029
|
+
},
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
const s = await prompts.spinner();
|
|
1033
|
+
s.start('Resolving source...');
|
|
1034
|
+
|
|
1035
|
+
let sourceResult;
|
|
1036
|
+
try {
|
|
1037
|
+
sourceResult = await customMgr.resolveSource(sourceInput.trim(), { skipInstall: true, silent: true });
|
|
1038
|
+
s.stop(sourceResult.parsed.type === 'local' ? 'Local source resolved' : 'Repository cloned');
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
s.error('Failed to resolve source');
|
|
1041
|
+
await prompts.log.error(` ${error.message}`);
|
|
1042
|
+
addMore = await prompts.confirm({ message: 'Try another source?', default: false });
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (sourceResult.parsed.type === 'local') {
|
|
1047
|
+
await prompts.log.info('LOCAL MODULE: Pointing directly at local source (changes take effect on reinstall).');
|
|
1048
|
+
} else {
|
|
1049
|
+
await prompts.log.warn(
|
|
1050
|
+
'UNVERIFIED MODULE: This module has not been reviewed by the XiaoMa team.\n' + ' Only install modules from sources you trust.',
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Resolve plugins based on discovery mode vs direct mode
|
|
1055
|
+
s.start('Analyzing plugin structure...');
|
|
1056
|
+
const allResolved = [];
|
|
1057
|
+
const localPath = sourceResult.parsed.type === 'local' ? sourceResult.rootDir : null;
|
|
1058
|
+
|
|
1059
|
+
if (sourceResult.mode === 'discovery') {
|
|
1060
|
+
// Discovery mode: marketplace.json found, list available plugins
|
|
1061
|
+
let plugins;
|
|
1062
|
+
try {
|
|
1063
|
+
plugins = await customMgr.discoverModules(sourceResult.marketplace, sourceResult.sourceUrl);
|
|
1064
|
+
} catch (discoverError) {
|
|
1065
|
+
s.error('Failed to discover modules');
|
|
1066
|
+
await prompts.log.error(` ${discoverError.message}`);
|
|
1067
|
+
addMore = await prompts.confirm({ message: 'Try another source?', default: false });
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const effectiveRepoPath = sourceResult.repoPath || sourceResult.rootDir;
|
|
1072
|
+
for (const plugin of plugins) {
|
|
1073
|
+
try {
|
|
1074
|
+
const resolved = await customMgr.resolvePlugin(effectiveRepoPath, plugin.rawPlugin, sourceResult.sourceUrl, localPath);
|
|
1075
|
+
if (resolved.length > 0) {
|
|
1076
|
+
allResolved.push(...resolved);
|
|
1077
|
+
} else {
|
|
1078
|
+
// No skills array or empty - use plugin metadata as-is (legacy)
|
|
1079
|
+
allResolved.push({
|
|
1080
|
+
code: plugin.code,
|
|
1081
|
+
name: plugin.displayName || plugin.name,
|
|
1082
|
+
version: plugin.version,
|
|
1083
|
+
description: plugin.description,
|
|
1084
|
+
strategy: 0,
|
|
1085
|
+
pluginName: plugin.name,
|
|
1086
|
+
skillPaths: [],
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
} catch (resolveError) {
|
|
1090
|
+
await prompts.log.warn(` Could not resolve ${plugin.name}: ${resolveError.message}`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
} else {
|
|
1094
|
+
// Direct mode: no marketplace.json, scan directory for skills and resolve
|
|
1095
|
+
const directPlugin = {
|
|
1096
|
+
name: sourceResult.parsed.displayName || path.basename(sourceResult.rootDir),
|
|
1097
|
+
source: '.',
|
|
1098
|
+
skills: [],
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
// Scan for SKILL.md directories to populate skills array
|
|
1102
|
+
try {
|
|
1103
|
+
const entries = await fs.readdir(sourceResult.rootDir, { withFileTypes: true });
|
|
1104
|
+
for (const entry of entries) {
|
|
1105
|
+
if (entry.isDirectory()) {
|
|
1106
|
+
const skillMd = path.join(sourceResult.rootDir, entry.name, 'SKILL.md');
|
|
1107
|
+
if (await fs.pathExists(skillMd)) {
|
|
1108
|
+
directPlugin.skills.push(entry.name);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
} catch (scanError) {
|
|
1113
|
+
s.error('Failed to scan directory');
|
|
1114
|
+
await prompts.log.error(` ${scanError.message}`);
|
|
1115
|
+
addMore = await prompts.confirm({ message: 'Try another source?', default: false });
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (directPlugin.skills.length > 0) {
|
|
1120
|
+
try {
|
|
1121
|
+
const resolved = await customMgr.resolvePlugin(sourceResult.rootDir, directPlugin, sourceResult.sourceUrl, localPath);
|
|
1122
|
+
allResolved.push(...resolved);
|
|
1123
|
+
} catch (resolveError) {
|
|
1124
|
+
await prompts.log.warn(` Could not resolve: ${resolveError.message}`);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
s.stop(`Found ${allResolved.length} installable module${allResolved.length === 1 ? '' : 's'}`);
|
|
1129
|
+
|
|
1130
|
+
if (allResolved.length === 0) {
|
|
1131
|
+
await prompts.log.warn('No installable modules found in this source.');
|
|
1132
|
+
addMore = await prompts.confirm({ message: 'Try another source?', default: false });
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Build multiselect choices
|
|
1137
|
+
// Already-installed modules are pre-checked (update). New modules are unchecked (opt-in).
|
|
1138
|
+
// Unchecking an installed module means "skip update" - removal is handled elsewhere.
|
|
1139
|
+
const choices = allResolved.map((mod) => {
|
|
1140
|
+
const versionStr = mod.version ? ` v${mod.version}` : '';
|
|
1141
|
+
const skillCount = mod.skillPaths ? mod.skillPaths.length : 0;
|
|
1142
|
+
const skillStr = skillCount > 0 ? ` (${skillCount} skill${skillCount === 1 ? '' : 's'})` : '';
|
|
1143
|
+
const alreadyInstalled = installedModuleIds.has(mod.code);
|
|
1144
|
+
const hint = alreadyInstalled ? 'update' : undefined;
|
|
1145
|
+
|
|
1146
|
+
return {
|
|
1147
|
+
name: `${mod.name}${versionStr}${skillStr}`,
|
|
1148
|
+
value: mod.code,
|
|
1149
|
+
hint,
|
|
1150
|
+
checked: alreadyInstalled,
|
|
1151
|
+
};
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
// Show descriptions before the multiselect
|
|
1155
|
+
for (const mod of allResolved) {
|
|
1156
|
+
const versionStr = mod.version ? ` v${mod.version}` : '';
|
|
1157
|
+
await prompts.log.info(` ${mod.name}${versionStr}\n ${mod.description}`);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const selected = await prompts.multiselect({
|
|
1161
|
+
message: 'Select modules to install:',
|
|
1162
|
+
choices,
|
|
1163
|
+
required: false,
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
if (selected && selected.length > 0) {
|
|
1167
|
+
for (const code of selected) {
|
|
1168
|
+
selectedModules.push(code);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
addMore = await prompts.confirm({
|
|
1173
|
+
message: 'Add another custom source?',
|
|
1174
|
+
default: false,
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
if (selectedModules.length > 0) {
|
|
1179
|
+
await prompts.log.message('Selected custom modules:\n' + selectedModules.map((c) => ` \u2022 ${c}`).join('\n'));
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
return selectedModules;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* Resolve custom sources from --custom-source CLI flag (non-interactive).
|
|
1187
|
+
* Auto-selects all discovered modules from each source.
|
|
1188
|
+
* @param {string} sourcesArg - Comma-separated Git URLs or local paths
|
|
1189
|
+
* @returns {Array} Module codes from all resolved sources
|
|
1190
|
+
*/
|
|
1191
|
+
async _resolveCustomSourcesCli(sourcesArg) {
|
|
1192
|
+
const { CustomModuleManager } = require('./modules/custom-module-manager');
|
|
1193
|
+
const customMgr = new CustomModuleManager();
|
|
1194
|
+
const allCodes = [];
|
|
1195
|
+
|
|
1196
|
+
const sources = sourcesArg
|
|
1197
|
+
.split(',')
|
|
1198
|
+
.map((s) => s.trim())
|
|
1199
|
+
.filter(Boolean);
|
|
1200
|
+
|
|
1201
|
+
for (const source of sources) {
|
|
1202
|
+
const s = await prompts.spinner();
|
|
1203
|
+
s.start(`Resolving ${source}...`);
|
|
1204
|
+
|
|
1205
|
+
let sourceResult;
|
|
1206
|
+
try {
|
|
1207
|
+
sourceResult = await customMgr.resolveSource(source, { skipInstall: true, silent: true });
|
|
1208
|
+
s.stop(sourceResult.parsed.type === 'local' ? 'Local source resolved' : 'Repository cloned');
|
|
1209
|
+
} catch (error) {
|
|
1210
|
+
s.error(`Failed to resolve ${source}`);
|
|
1211
|
+
await prompts.log.error(` ${error.message}`);
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const s2 = await prompts.spinner();
|
|
1216
|
+
s2.start('Analyzing plugin structure...');
|
|
1217
|
+
const allResolved = [];
|
|
1218
|
+
const localPath = sourceResult.parsed.type === 'local' ? sourceResult.rootDir : null;
|
|
1219
|
+
|
|
1220
|
+
if (sourceResult.mode === 'discovery') {
|
|
1221
|
+
try {
|
|
1222
|
+
const plugins = await customMgr.discoverModules(sourceResult.marketplace, sourceResult.sourceUrl);
|
|
1223
|
+
const effectiveRepoPath = sourceResult.repoPath || sourceResult.rootDir;
|
|
1224
|
+
for (const plugin of plugins) {
|
|
1225
|
+
try {
|
|
1226
|
+
const resolved = await customMgr.resolvePlugin(effectiveRepoPath, plugin.rawPlugin, sourceResult.sourceUrl, localPath);
|
|
1227
|
+
if (resolved.length > 0) {
|
|
1228
|
+
allResolved.push(...resolved);
|
|
1229
|
+
}
|
|
1230
|
+
} catch {
|
|
1231
|
+
// Skip unresolvable plugins
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
} catch (discoverError) {
|
|
1235
|
+
s2.error('Failed to discover modules');
|
|
1236
|
+
await prompts.log.error(` ${discoverError.message}`);
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
} else {
|
|
1240
|
+
// Direct mode: scan for SKILL.md directories
|
|
1241
|
+
const directPlugin = {
|
|
1242
|
+
name: sourceResult.parsed.displayName || path.basename(sourceResult.rootDir),
|
|
1243
|
+
source: '.',
|
|
1244
|
+
skills: [],
|
|
1245
|
+
};
|
|
1246
|
+
try {
|
|
1247
|
+
const entries = await fs.readdir(sourceResult.rootDir, { withFileTypes: true });
|
|
1248
|
+
for (const entry of entries) {
|
|
1249
|
+
if (entry.isDirectory()) {
|
|
1250
|
+
const skillMd = path.join(sourceResult.rootDir, entry.name, 'SKILL.md');
|
|
1251
|
+
if (await fs.pathExists(skillMd)) {
|
|
1252
|
+
directPlugin.skills.push(entry.name);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
} catch {
|
|
1257
|
+
// Skip unreadable directories
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
if (directPlugin.skills.length > 0) {
|
|
1261
|
+
try {
|
|
1262
|
+
const resolved = await customMgr.resolvePlugin(sourceResult.rootDir, directPlugin, sourceResult.sourceUrl, localPath);
|
|
1263
|
+
allResolved.push(...resolved);
|
|
1264
|
+
} catch {
|
|
1265
|
+
// Skip unresolvable
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
s2.stop(`Found ${allResolved.length} module${allResolved.length === 1 ? '' : 's'}`);
|
|
1270
|
+
|
|
1271
|
+
for (const mod of allResolved) {
|
|
1272
|
+
allCodes.push(mod.code);
|
|
1273
|
+
const versionStr = mod.version ? ` v${mod.version}` : '';
|
|
1274
|
+
await prompts.log.info(` Custom module: ${mod.name}${versionStr}`);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
return allCodes;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Get default modules for non-interactive mode
|
|
1283
|
+
* @param {Set} installedModuleIds - Already installed module IDs
|
|
1284
|
+
* @returns {Array} Default module codes
|
|
1285
|
+
*/
|
|
1286
|
+
async getDefaultModules(installedModuleIds = new Set()) {
|
|
1287
|
+
// Built-in modules with default_selected come from local source
|
|
1288
|
+
const { OfficialModules } = require('./modules/official-modules');
|
|
1289
|
+
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
|
|
1290
|
+
|
|
1291
|
+
const defaultModules = [];
|
|
1292
|
+
const seen = new Set();
|
|
1293
|
+
|
|
1294
|
+
for (const mod of builtInModules) {
|
|
1295
|
+
if (mod.defaultSelected || installedModuleIds.has(mod.id)) {
|
|
1296
|
+
defaultModules.push(mod.id);
|
|
1297
|
+
seen.add(mod.id);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// Add external registry defaults
|
|
1302
|
+
const externalManager = new ExternalModuleManager();
|
|
1303
|
+
const registryModules = await externalManager.listAvailable();
|
|
1304
|
+
|
|
1305
|
+
for (const mod of registryModules) {
|
|
1306
|
+
if (mod.builtIn || seen.has(mod.code)) continue;
|
|
1307
|
+
if (mod.defaultSelected || installedModuleIds.has(mod.code)) {
|
|
1308
|
+
defaultModules.push(mod.code);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// If no defaults found, use 'xmc' as the fallback default
|
|
1313
|
+
if (defaultModules.length === 0) {
|
|
1314
|
+
defaultModules.push('xmc');
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
return defaultModules;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Prompt for directory selection
|
|
1322
|
+
* @returns {Object} Directory answer from prompt
|
|
1323
|
+
*/
|
|
1324
|
+
async promptForDirectory() {
|
|
1325
|
+
// Use sync validation because @clack/prompts doesn't support async validate
|
|
1326
|
+
const directory = await prompts.directory({
|
|
1327
|
+
message: 'Installation directory:',
|
|
1328
|
+
default: process.cwd(),
|
|
1329
|
+
placeholder: process.cwd(),
|
|
1330
|
+
validate: (input) => this.validateDirectorySync(input),
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
// Apply filter logic
|
|
1334
|
+
let filteredDir = directory;
|
|
1335
|
+
if (!filteredDir || filteredDir.trim() === '') {
|
|
1336
|
+
filteredDir = process.cwd();
|
|
1337
|
+
} else {
|
|
1338
|
+
filteredDir = this.expandUserPath(filteredDir);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
return { directory: filteredDir };
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Display directory information
|
|
1346
|
+
* @param {string} directory - The directory path
|
|
1347
|
+
*/
|
|
1348
|
+
async displayDirectoryInfo(directory) {
|
|
1349
|
+
await prompts.log.info(`Resolved installation path: ${directory}`);
|
|
1350
|
+
|
|
1351
|
+
const dirExists = await fs.pathExists(directory);
|
|
1352
|
+
if (dirExists) {
|
|
1353
|
+
// Show helpful context about the existing path
|
|
1354
|
+
const stats = await fs.stat(directory);
|
|
1355
|
+
if (stats.isDirectory()) {
|
|
1356
|
+
const files = await fs.readdir(directory);
|
|
1357
|
+
if (files.length > 0) {
|
|
1358
|
+
// Check for any xiaoma installation (any folder with _config/manifest.yaml)
|
|
1359
|
+
const { Installer } = require('./core/installer');
|
|
1360
|
+
const installer = new Installer();
|
|
1361
|
+
const xiaomaResult = await installer.findXiaomaDir(directory);
|
|
1362
|
+
const hasXiaomaInstall =
|
|
1363
|
+
(await fs.pathExists(xiaomaResult.xiaomaDir)) &&
|
|
1364
|
+
(await fs.pathExists(path.join(xiaomaResult.xiaomaDir, '_config', 'manifest.yaml')));
|
|
1365
|
+
|
|
1366
|
+
const xiaomaNote = hasXiaomaInstall ? ` including existing XiaoMa installation (${path.basename(xiaomaResult.xiaomaDir)})` : '';
|
|
1367
|
+
await prompts.log.message(`Directory exists and contains ${files.length} item(s)${xiaomaNote}`);
|
|
1368
|
+
} else {
|
|
1369
|
+
await prompts.log.message('Directory exists and is empty');
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Confirm directory selection
|
|
1377
|
+
* @param {string} directory - The directory path
|
|
1378
|
+
* @returns {boolean} Whether user confirmed
|
|
1379
|
+
*/
|
|
1380
|
+
async confirmDirectory(directory) {
|
|
1381
|
+
const dirExists = await fs.pathExists(directory);
|
|
1382
|
+
|
|
1383
|
+
if (dirExists) {
|
|
1384
|
+
const proceed = await prompts.confirm({
|
|
1385
|
+
message: 'Install to this directory?',
|
|
1386
|
+
default: true,
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1389
|
+
if (!proceed) {
|
|
1390
|
+
await prompts.log.warn("Let's try again with a different path.");
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
return proceed;
|
|
1394
|
+
} else {
|
|
1395
|
+
// Ask for confirmation to create the directory
|
|
1396
|
+
const create = await prompts.confirm({
|
|
1397
|
+
message: `Create directory: ${directory}?`,
|
|
1398
|
+
default: false,
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
if (!create) {
|
|
1402
|
+
await prompts.log.warn("Let's try again with a different path.");
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
return create;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Validate directory path for installation (sync version for clack prompts)
|
|
1411
|
+
* @param {string} input - User input path
|
|
1412
|
+
* @returns {string|undefined} Error message or undefined if valid
|
|
1413
|
+
*/
|
|
1414
|
+
validateDirectorySync(input) {
|
|
1415
|
+
// Allow empty input to use the default
|
|
1416
|
+
if (!input || input.trim() === '') {
|
|
1417
|
+
return; // Empty means use default, undefined = valid for clack
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
let expandedPath;
|
|
1421
|
+
try {
|
|
1422
|
+
expandedPath = this.expandUserPath(input.trim());
|
|
1423
|
+
} catch (error) {
|
|
1424
|
+
return error.message;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// Check if the path exists
|
|
1428
|
+
const pathExists = fs.pathExistsSync(expandedPath);
|
|
1429
|
+
|
|
1430
|
+
if (!pathExists) {
|
|
1431
|
+
// Find the first existing parent directory
|
|
1432
|
+
const existingParent = this.findExistingParentSync(expandedPath);
|
|
1433
|
+
|
|
1434
|
+
if (!existingParent) {
|
|
1435
|
+
return 'Cannot create directory: no existing parent directory found';
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// Check if the existing parent is writable
|
|
1439
|
+
try {
|
|
1440
|
+
fs.accessSync(existingParent, fs.constants.W_OK);
|
|
1441
|
+
// Path doesn't exist but can be created - will prompt for confirmation later
|
|
1442
|
+
return;
|
|
1443
|
+
} catch {
|
|
1444
|
+
// Provide a detailed error message explaining both issues
|
|
1445
|
+
return `Directory '${expandedPath}' does not exist and cannot be created: parent directory '${existingParent}' is not writable`;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// If it exists, validate it's a directory and writable
|
|
1450
|
+
const stat = fs.statSync(expandedPath);
|
|
1451
|
+
if (!stat.isDirectory()) {
|
|
1452
|
+
return `Path exists but is not a directory: ${expandedPath}`;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// Check write permissions
|
|
1456
|
+
try {
|
|
1457
|
+
fs.accessSync(expandedPath, fs.constants.W_OK);
|
|
1458
|
+
} catch {
|
|
1459
|
+
return `Directory is not writable: ${expandedPath}`;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
* Validate directory path for installation (async version)
|
|
1467
|
+
* @param {string} input - User input path
|
|
1468
|
+
* @returns {string|true} Error message or true if valid
|
|
1469
|
+
*/
|
|
1470
|
+
async validateDirectory(input) {
|
|
1471
|
+
// Allow empty input to use the default
|
|
1472
|
+
if (!input || input.trim() === '') {
|
|
1473
|
+
return true; // Empty means use default
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
let expandedPath;
|
|
1477
|
+
try {
|
|
1478
|
+
expandedPath = this.expandUserPath(input.trim());
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
return error.message;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// Check if the path exists
|
|
1484
|
+
const pathExists = await fs.pathExists(expandedPath);
|
|
1485
|
+
|
|
1486
|
+
if (!pathExists) {
|
|
1487
|
+
// Find the first existing parent directory
|
|
1488
|
+
const existingParent = await this.findExistingParent(expandedPath);
|
|
1489
|
+
|
|
1490
|
+
if (!existingParent) {
|
|
1491
|
+
return 'Cannot create directory: no existing parent directory found';
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// Check if the existing parent is writable
|
|
1495
|
+
try {
|
|
1496
|
+
await fs.access(existingParent, fs.constants.W_OK);
|
|
1497
|
+
// Path doesn't exist but can be created - will prompt for confirmation later
|
|
1498
|
+
return true;
|
|
1499
|
+
} catch {
|
|
1500
|
+
// Provide a detailed error message explaining both issues
|
|
1501
|
+
return `Directory '${expandedPath}' does not exist and cannot be created: parent directory '${existingParent}' is not writable`;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// If it exists, validate it's a directory and writable
|
|
1506
|
+
const stat = await fs.stat(expandedPath);
|
|
1507
|
+
if (!stat.isDirectory()) {
|
|
1508
|
+
return `Path exists but is not a directory: ${expandedPath}`;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// Check write permissions
|
|
1512
|
+
try {
|
|
1513
|
+
await fs.access(expandedPath, fs.constants.W_OK);
|
|
1514
|
+
} catch {
|
|
1515
|
+
return `Directory is not writable: ${expandedPath}`;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
return true;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/**
|
|
1522
|
+
* Find the first existing parent directory (sync version)
|
|
1523
|
+
* @param {string} targetPath - The path to check
|
|
1524
|
+
* @returns {string|null} The first existing parent directory, or null if none found
|
|
1525
|
+
*/
|
|
1526
|
+
findExistingParentSync(targetPath) {
|
|
1527
|
+
let currentPath = path.resolve(targetPath);
|
|
1528
|
+
|
|
1529
|
+
// Walk up the directory tree until we find an existing directory
|
|
1530
|
+
while (currentPath !== path.dirname(currentPath)) {
|
|
1531
|
+
// Stop at root
|
|
1532
|
+
const parent = path.dirname(currentPath);
|
|
1533
|
+
if (fs.pathExistsSync(parent)) {
|
|
1534
|
+
return parent;
|
|
1535
|
+
}
|
|
1536
|
+
currentPath = parent;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
return null; // No existing parent found (shouldn't happen in practice)
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* Find the first existing parent directory (async version)
|
|
1544
|
+
* @param {string} targetPath - The path to check
|
|
1545
|
+
* @returns {string|null} The first existing parent directory, or null if none found
|
|
1546
|
+
*/
|
|
1547
|
+
async findExistingParent(targetPath) {
|
|
1548
|
+
let currentPath = path.resolve(targetPath);
|
|
1549
|
+
|
|
1550
|
+
// Walk up the directory tree until we find an existing directory
|
|
1551
|
+
while (currentPath !== path.dirname(currentPath)) {
|
|
1552
|
+
// Stop at root
|
|
1553
|
+
const parent = path.dirname(currentPath);
|
|
1554
|
+
if (await fs.pathExists(parent)) {
|
|
1555
|
+
return parent;
|
|
1556
|
+
}
|
|
1557
|
+
currentPath = parent;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
return null; // No existing parent found (shouldn't happen in practice)
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Expands the user-provided path: handles ~ and resolves to absolute.
|
|
1565
|
+
* @param {string} inputPath - User input path.
|
|
1566
|
+
* @returns {string} Absolute expanded path.
|
|
1567
|
+
*/
|
|
1568
|
+
expandUserPath(inputPath) {
|
|
1569
|
+
if (typeof inputPath !== 'string') {
|
|
1570
|
+
throw new TypeError('Path must be a string.');
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
let expanded = inputPath.trim();
|
|
1574
|
+
|
|
1575
|
+
// Handle tilde expansion
|
|
1576
|
+
if (expanded.startsWith('~')) {
|
|
1577
|
+
if (expanded === '~') {
|
|
1578
|
+
expanded = os.homedir();
|
|
1579
|
+
} else if (expanded.startsWith('~' + path.sep)) {
|
|
1580
|
+
const pathAfterHome = expanded.slice(2); // Remove ~/ or ~\
|
|
1581
|
+
expanded = path.join(os.homedir(), pathAfterHome);
|
|
1582
|
+
} else {
|
|
1583
|
+
const restOfPath = expanded.slice(1);
|
|
1584
|
+
const separatorIndex = restOfPath.indexOf(path.sep);
|
|
1585
|
+
const username = separatorIndex === -1 ? restOfPath : restOfPath.slice(0, separatorIndex);
|
|
1586
|
+
if (username) {
|
|
1587
|
+
throw new Error(`Path expansion for ~${username} is not supported. Please use an absolute path or ~${path.sep}`);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// Resolve to the absolute path relative to the current working directory
|
|
1593
|
+
return path.resolve(expanded);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Get configured IDEs from existing installation
|
|
1598
|
+
* @param {string} directory - Installation directory
|
|
1599
|
+
* @returns {Array} List of configured IDEs
|
|
1600
|
+
*/
|
|
1601
|
+
async getConfiguredIdes(directory) {
|
|
1602
|
+
const { ExistingInstall } = require('./core/existing-install');
|
|
1603
|
+
const { Installer } = require('./core/installer');
|
|
1604
|
+
const installer = new Installer();
|
|
1605
|
+
const { xiaomaDir } = await installer.findXiaomaDir(directory);
|
|
1606
|
+
const existingInstall = await ExistingInstall.detect(xiaomaDir);
|
|
1607
|
+
return existingInstall.ides;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
/**
|
|
1611
|
+
* Display module versions with update availability
|
|
1612
|
+
* @param {Array} modules - Array of module info objects with version info
|
|
1613
|
+
* @param {Array} availableUpdates - Array of available updates
|
|
1614
|
+
*/
|
|
1615
|
+
async displayModuleVersions(modules, availableUpdates = []) {
|
|
1616
|
+
// Group modules by source
|
|
1617
|
+
const builtIn = modules.filter((m) => m.source === 'built-in');
|
|
1618
|
+
const external = modules.filter((m) => m.source === 'external');
|
|
1619
|
+
const community = modules.filter((m) => m.source === 'community');
|
|
1620
|
+
const custom = modules.filter((m) => m.source === 'custom');
|
|
1621
|
+
const unknown = modules.filter((m) => m.source === 'unknown');
|
|
1622
|
+
|
|
1623
|
+
const lines = [];
|
|
1624
|
+
const formatGroup = (group, title) => {
|
|
1625
|
+
if (group.length === 0) return;
|
|
1626
|
+
lines.push(title);
|
|
1627
|
+
for (const mod of group) {
|
|
1628
|
+
const updateInfo = availableUpdates.find((u) => u.name === mod.name);
|
|
1629
|
+
const versionDisplay = mod.version || 'unknown';
|
|
1630
|
+
if (updateInfo) {
|
|
1631
|
+
lines.push(` ${mod.name.padEnd(20)} ${versionDisplay} \u2192 ${updateInfo.latestVersion} \u2191`);
|
|
1632
|
+
} else {
|
|
1633
|
+
lines.push(` ${mod.name.padEnd(20)} ${versionDisplay} \u2713`);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
|
|
1638
|
+
formatGroup(builtIn, 'Built-in Modules');
|
|
1639
|
+
formatGroup(external, 'External Modules (Official)');
|
|
1640
|
+
formatGroup(community, 'Community Modules');
|
|
1641
|
+
formatGroup(custom, 'Custom Modules');
|
|
1642
|
+
formatGroup(unknown, 'Other Modules');
|
|
1643
|
+
|
|
1644
|
+
await prompts.note(lines.join('\n'), 'Module Versions');
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Prompt user to select which modules to update
|
|
1649
|
+
* @param {Array} availableUpdates - Array of available updates
|
|
1650
|
+
* @returns {Array} Selected module names to update
|
|
1651
|
+
*/
|
|
1652
|
+
async promptUpdateSelection(availableUpdates) {
|
|
1653
|
+
if (availableUpdates.length === 0) {
|
|
1654
|
+
return [];
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
await prompts.log.info('Available Updates');
|
|
1658
|
+
|
|
1659
|
+
const choices = availableUpdates.map((update) => ({
|
|
1660
|
+
name: `${update.name} (v${update.installedVersion} \u2192 v${update.latestVersion})`,
|
|
1661
|
+
value: update.name,
|
|
1662
|
+
checked: true, // Default to selecting all updates
|
|
1663
|
+
}));
|
|
1664
|
+
|
|
1665
|
+
// Add "Update All" and "Cancel" options
|
|
1666
|
+
const action = await prompts.select({
|
|
1667
|
+
message: 'How would you like to proceed?',
|
|
1668
|
+
choices: [
|
|
1669
|
+
{ name: 'Update all available modules', value: 'all' },
|
|
1670
|
+
{ name: 'Select specific modules to update', value: 'select' },
|
|
1671
|
+
{ name: 'Skip updates for now', value: 'skip' },
|
|
1672
|
+
],
|
|
1673
|
+
default: 'all',
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
if (action === 'all') {
|
|
1677
|
+
return availableUpdates.map((u) => u.name);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
if (action === 'skip') {
|
|
1681
|
+
return [];
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// Allow specific selection
|
|
1685
|
+
const selected = await prompts.multiselect({
|
|
1686
|
+
message: 'Select modules to update (use arrow keys, space to toggle):',
|
|
1687
|
+
choices: choices,
|
|
1688
|
+
required: true,
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
return selected || [];
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/**
|
|
1695
|
+
* Display status of all installed modules
|
|
1696
|
+
* @param {Object} statusData - Status data with modules, installation info, and available updates
|
|
1697
|
+
*/
|
|
1698
|
+
async displayStatus(statusData) {
|
|
1699
|
+
const { installation, modules, availableUpdates, xiaomaDir } = statusData;
|
|
1700
|
+
|
|
1701
|
+
// Installation info
|
|
1702
|
+
const infoLines = [
|
|
1703
|
+
`Version: ${installation.version || 'unknown'}`,
|
|
1704
|
+
`Location: ${xiaomaDir}`,
|
|
1705
|
+
`Installed: ${new Date(installation.installDate).toLocaleDateString()}`,
|
|
1706
|
+
`Last Updated: ${installation.lastUpdated ? new Date(installation.lastUpdated).toLocaleDateString() : 'unknown'}`,
|
|
1707
|
+
];
|
|
1708
|
+
|
|
1709
|
+
await prompts.note(infoLines.join('\n'), 'XiaoMa Status');
|
|
1710
|
+
|
|
1711
|
+
// Module versions
|
|
1712
|
+
await this.displayModuleVersions(modules, availableUpdates);
|
|
1713
|
+
|
|
1714
|
+
// Update summary
|
|
1715
|
+
if (availableUpdates.length > 0) {
|
|
1716
|
+
await prompts.log.warn(`${availableUpdates.length} update(s) available`);
|
|
1717
|
+
await prompts.log.message('Run \'xiaoma install\' and select "Quick Update" to update');
|
|
1718
|
+
} else {
|
|
1719
|
+
await prompts.log.success('All modules are up to date');
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* Display list of selected tools after IDE selection
|
|
1725
|
+
* @param {Array} selectedIdes - Array of selected IDE values
|
|
1726
|
+
* @param {Array} preferredIdes - Array of preferred IDE objects
|
|
1727
|
+
* @param {Array} allTools - Array of all tool objects
|
|
1728
|
+
*/
|
|
1729
|
+
async displaySelectedTools(selectedIdes, preferredIdes, allTools) {
|
|
1730
|
+
if (selectedIdes.length === 0) return;
|
|
1731
|
+
|
|
1732
|
+
const preferredValues = new Set(preferredIdes.map((ide) => ide.value));
|
|
1733
|
+
const toolLines = selectedIdes.map((ideValue) => {
|
|
1734
|
+
const tool = allTools.find((t) => t.value === ideValue);
|
|
1735
|
+
const name = tool?.name || ideValue;
|
|
1736
|
+
const marker = preferredValues.has(ideValue) ? ' \u2B50' : '';
|
|
1737
|
+
return ` \u2022 ${name}${marker}`;
|
|
1738
|
+
});
|
|
1739
|
+
await prompts.log.message('Selected tools:\n' + toolLines.join('\n'));
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* Return the set of module codes the registry marks as built-in (core, xmc).
|
|
1744
|
+
* These ship with the installer binary and have no per-module channel.
|
|
1745
|
+
*/
|
|
1746
|
+
async _bundledModuleCodes() {
|
|
1747
|
+
const externalManager = new ExternalModuleManager();
|
|
1748
|
+
try {
|
|
1749
|
+
const modules = await externalManager.listAvailable();
|
|
1750
|
+
return modules.filter((m) => m.builtIn).map((m) => m.code);
|
|
1751
|
+
} catch {
|
|
1752
|
+
// Registry unreachable — fall back to the known bundled codes.
|
|
1753
|
+
return ['core', 'xmc'];
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
/**
|
|
1758
|
+
* Fast-path channel gate: confirm "all stable" or open the per-module picker.
|
|
1759
|
+
*
|
|
1760
|
+
* Skipped when:
|
|
1761
|
+
* - running non-interactively (--yes)
|
|
1762
|
+
* - the user already passed channel flags (--channel / --pin / --next), OR
|
|
1763
|
+
* the installer was launched from a prerelease (which seeds
|
|
1764
|
+
* channelOptions.global = 'next' upstream in promptInstall)
|
|
1765
|
+
* - no externals/community modules are selected
|
|
1766
|
+
*
|
|
1767
|
+
* Mutates channelOptions.pins and channelOptions.nextSet to reflect picker choices.
|
|
1768
|
+
*/
|
|
1769
|
+
async _interactiveChannelGate({ options, channelOptions, selectedModules }) {
|
|
1770
|
+
if (options.yes) return;
|
|
1771
|
+
// If the user already declared their channel intent via flags, trust them
|
|
1772
|
+
// and skip the gate.
|
|
1773
|
+
const haveFlagIntent = channelOptions.global || channelOptions.nextSet.size > 0 || channelOptions.pins.size > 0;
|
|
1774
|
+
if (haveFlagIntent) return;
|
|
1775
|
+
|
|
1776
|
+
// Figure out which selected modules actually get a channel (externals only).
|
|
1777
|
+
// Bundled core/xmc and custom modules skip the picker.
|
|
1778
|
+
const externalManager = new ExternalModuleManager();
|
|
1779
|
+
const externals = await externalManager.listAvailable();
|
|
1780
|
+
const externalByCode = new Map(externals.map((m) => [m.code, m]));
|
|
1781
|
+
|
|
1782
|
+
const channelSelectable = selectedModules.filter((code) => {
|
|
1783
|
+
const info = externalByCode.get(code);
|
|
1784
|
+
return info && !info.builtIn;
|
|
1785
|
+
});
|
|
1786
|
+
if (channelSelectable.length === 0) return;
|
|
1787
|
+
|
|
1788
|
+
const fastPath = await prompts.confirm({
|
|
1789
|
+
message: `Ready to install (all stable)? Pick "n" to customize channels or pin versions.`,
|
|
1790
|
+
default: true,
|
|
1791
|
+
});
|
|
1792
|
+
if (fastPath) return; // stable for all, registry default applies
|
|
1793
|
+
|
|
1794
|
+
// Customize path: per-module picker.
|
|
1795
|
+
const { fetchStableTags, parseGitHubRepo } = require('./modules/channel-resolver');
|
|
1796
|
+
|
|
1797
|
+
for (const code of channelSelectable) {
|
|
1798
|
+
const info = externalByCode.get(code);
|
|
1799
|
+
const repoUrl = info.url;
|
|
1800
|
+
|
|
1801
|
+
// Try to pre-resolve the top stable tag so we can surface it in the picker.
|
|
1802
|
+
let stableLabel = 'stable (released version)';
|
|
1803
|
+
try {
|
|
1804
|
+
const parsed = repoUrl ? parseGitHubRepo(repoUrl) : null;
|
|
1805
|
+
if (parsed) {
|
|
1806
|
+
const tags = await fetchStableTags(parsed.owner, parsed.repo);
|
|
1807
|
+
if (tags.length > 0) {
|
|
1808
|
+
stableLabel = `stable ${tags[0].tag} (released version)`;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
} catch {
|
|
1812
|
+
// fall through with the generic label
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const choice = await prompts.select({
|
|
1816
|
+
message: `${code}: choose a channel`,
|
|
1817
|
+
choices: [
|
|
1818
|
+
{ name: stableLabel, value: 'stable' },
|
|
1819
|
+
{ name: 'next (main HEAD \u2014 current development)', value: 'next' },
|
|
1820
|
+
{ name: 'pin (specific version)', value: 'pin' },
|
|
1821
|
+
],
|
|
1822
|
+
default: 'stable',
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
if (choice === 'next') {
|
|
1826
|
+
channelOptions.nextSet.add(code);
|
|
1827
|
+
} else if (choice === 'pin') {
|
|
1828
|
+
const pinValue = await prompts.text({
|
|
1829
|
+
message: `Enter a version tag for '${code}' (e.g. v1.6.0):`,
|
|
1830
|
+
validate: (value) => {
|
|
1831
|
+
if (!value || !/^[\w.\-+/]+$/.test(String(value).trim())) {
|
|
1832
|
+
return 'Must be a non-empty tag name (letters, digits, dots, hyphens).';
|
|
1833
|
+
}
|
|
1834
|
+
},
|
|
1835
|
+
});
|
|
1836
|
+
channelOptions.pins.set(code, String(pinValue).trim());
|
|
1837
|
+
}
|
|
1838
|
+
// 'stable' is the default; nothing to record.
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/**
|
|
1843
|
+
* Resolve channel decisions for an update over an existing install.
|
|
1844
|
+
*
|
|
1845
|
+
* For each selected external/community module:
|
|
1846
|
+
* - Read the recorded channel from the existing manifest.
|
|
1847
|
+
* - On `stable`: query tags; if a newer stable exists, classify the diff
|
|
1848
|
+
* and prompt. Patch/minor default Y; major defaults N. `--yes` accepts
|
|
1849
|
+
* defaults (patches/minors) but NOT majors — a major under --yes stays
|
|
1850
|
+
* frozen unless the user also passes `--pin CODE=NEW_TAG`.
|
|
1851
|
+
* - On `next`: no prompt (pull HEAD).
|
|
1852
|
+
* - On `pinned`: no prompt (stays pinned).
|
|
1853
|
+
* - No channel recorded and `version: null`: one-time migration prompt
|
|
1854
|
+
* ("Switch to stable / Keep on next").
|
|
1855
|
+
*
|
|
1856
|
+
* Decisions that freeze the current version are applied by adding a pin to
|
|
1857
|
+
* `channelOptions.pins` so downstream clone logic honors them.
|
|
1858
|
+
*/
|
|
1859
|
+
async _resolveUpdateChannels({ xiaomaDir, selectedModules, channelOptions, yes }) {
|
|
1860
|
+
const { Manifest } = require('./core/manifest');
|
|
1861
|
+
const manifestObj = new Manifest();
|
|
1862
|
+
const manifest = await manifestObj.read(xiaomaDir);
|
|
1863
|
+
const existingByName = new Map();
|
|
1864
|
+
for (const m of manifest?.modulesDetailed || []) {
|
|
1865
|
+
if (m?.name) existingByName.set(m.name, m);
|
|
1866
|
+
}
|
|
1867
|
+
if (existingByName.size === 0) return;
|
|
1868
|
+
|
|
1869
|
+
const externalManager = new ExternalModuleManager();
|
|
1870
|
+
const externals = await externalManager.listAvailable();
|
|
1871
|
+
const externalByCode = new Map(externals.map((m) => [m.code, m]));
|
|
1872
|
+
|
|
1873
|
+
const { fetchStableTags, classifyUpgrade, releaseNotesUrl } = require('./modules/channel-resolver');
|
|
1874
|
+
const { parseGitHubRepo } = require('./modules/channel-resolver');
|
|
1875
|
+
|
|
1876
|
+
// Interactive-only: offer a one-time gate to review / switch channels for
|
|
1877
|
+
// selected modules that are already installed. Default N so normal Modify
|
|
1878
|
+
// flows (add/remove modules) aren't interrupted.
|
|
1879
|
+
let reviewChannels = false;
|
|
1880
|
+
if (!yes) {
|
|
1881
|
+
const existingWithChannel = selectedModules.filter((code) => {
|
|
1882
|
+
const prev = existingByName.get(code);
|
|
1883
|
+
if (!prev) return false;
|
|
1884
|
+
const info = externalByCode.get(code);
|
|
1885
|
+
return info && !info.builtIn;
|
|
1886
|
+
});
|
|
1887
|
+
if (existingWithChannel.length > 0) {
|
|
1888
|
+
reviewChannels = await prompts.confirm({
|
|
1889
|
+
message: 'Review channel assignments (stable / next / pin) for your existing modules?',
|
|
1890
|
+
default: false,
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
for (const code of selectedModules) {
|
|
1896
|
+
const prev = existingByName.get(code);
|
|
1897
|
+
if (!prev) continue;
|
|
1898
|
+
|
|
1899
|
+
const info = externalByCode.get(code);
|
|
1900
|
+
if (!info) continue;
|
|
1901
|
+
// Bundled modules (core/xmc) ship with the installer binary itself —
|
|
1902
|
+
// their version is stapled to the CLI version, not a git tag. Skip
|
|
1903
|
+
// tag-API lookups for them; the "upgrade" mechanism is `npx xiaoma@X install`.
|
|
1904
|
+
if (info.builtIn) continue;
|
|
1905
|
+
|
|
1906
|
+
const repoUrl = info.url;
|
|
1907
|
+
const parsed = repoUrl ? parseGitHubRepo(repoUrl) : null;
|
|
1908
|
+
|
|
1909
|
+
// Legacy migration: manifest carries no channel and a null/empty
|
|
1910
|
+
// version. Offer the one-time pick between stable and next.
|
|
1911
|
+
const recordedChannel = prev.channel || null;
|
|
1912
|
+
const needsMigration = !recordedChannel && (prev.version == null || prev.version === '');
|
|
1913
|
+
if (needsMigration) {
|
|
1914
|
+
if (yes) {
|
|
1915
|
+
// Conservative headless default: stable.
|
|
1916
|
+
continue;
|
|
1917
|
+
}
|
|
1918
|
+
const chosen = await prompts.select({
|
|
1919
|
+
message: `${code}: your existing install tracks the main branch. Switch to stable releases (recommended for production), or keep on main?`,
|
|
1920
|
+
choices: [
|
|
1921
|
+
{ name: 'Switch to stable', value: 'stable' },
|
|
1922
|
+
{ name: 'Keep on main (next)', value: 'next' },
|
|
1923
|
+
],
|
|
1924
|
+
default: 'stable',
|
|
1925
|
+
});
|
|
1926
|
+
if (chosen === 'next') channelOptions.nextSet.add(code);
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// Optional channel-switch offer. Fires only when the user opted in via
|
|
1931
|
+
// the gate above. 'keep' falls through to the existing per-channel
|
|
1932
|
+
// logic (which runs upgrade classification for stable). Any switch
|
|
1933
|
+
// records the new intent into channelOptions and skips upgrade prompts.
|
|
1934
|
+
if (reviewChannels && recordedChannel) {
|
|
1935
|
+
const switchChoices = [
|
|
1936
|
+
{
|
|
1937
|
+
name: `Keep on '${recordedChannel}'${prev.version ? ` @ ${prev.version}` : ''}`,
|
|
1938
|
+
value: 'keep',
|
|
1939
|
+
},
|
|
1940
|
+
];
|
|
1941
|
+
if (recordedChannel !== 'stable') {
|
|
1942
|
+
switchChoices.push({ name: 'Switch to stable (released version)', value: 'stable' });
|
|
1943
|
+
}
|
|
1944
|
+
if (recordedChannel !== 'next') {
|
|
1945
|
+
switchChoices.push({ name: 'Switch to next (main HEAD)', value: 'next' });
|
|
1946
|
+
}
|
|
1947
|
+
switchChoices.push({ name: 'Pin to a specific version tag', value: 'pin' });
|
|
1948
|
+
|
|
1949
|
+
const choice = await prompts.select({
|
|
1950
|
+
message: `${code} channel:`,
|
|
1951
|
+
choices: switchChoices,
|
|
1952
|
+
default: 'keep',
|
|
1953
|
+
});
|
|
1954
|
+
|
|
1955
|
+
if (choice === 'next') {
|
|
1956
|
+
channelOptions.nextSet.add(code);
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
if (choice === 'pin') {
|
|
1960
|
+
const pinValue = await prompts.text({
|
|
1961
|
+
message: `Enter a version tag for '${code}' (e.g. v1.6.0):`,
|
|
1962
|
+
validate: (value) => {
|
|
1963
|
+
if (!value || !/^[\w.\-+/]+$/.test(String(value).trim())) {
|
|
1964
|
+
return 'Must be a non-empty tag name (letters, digits, dots, hyphens).';
|
|
1965
|
+
}
|
|
1966
|
+
},
|
|
1967
|
+
});
|
|
1968
|
+
channelOptions.pins.set(code, String(pinValue).trim());
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
if (choice === 'stable') {
|
|
1972
|
+
// Switch to stable: install at the top stable tag without an
|
|
1973
|
+
// upgrade-classification prompt (the user explicitly opted in).
|
|
1974
|
+
// Also warm the tag cache here so the actual clone step doesn't
|
|
1975
|
+
// need a second GitHub API call (can hit rate limits).
|
|
1976
|
+
if (parsed) {
|
|
1977
|
+
try {
|
|
1978
|
+
await fetchStableTags(parsed.owner, parsed.repo);
|
|
1979
|
+
} catch {
|
|
1980
|
+
// best effort; clone step will surface any failure
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
// 'keep' → fall through with recordedChannel below.
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
if (recordedChannel === 'pinned' || recordedChannel === 'next') {
|
|
1989
|
+
// Respect any explicit channel intent the user already expressed via
|
|
1990
|
+
// CLI flags (--channel / --all-* / --next=CODE / --pin CODE=TAG) or
|
|
1991
|
+
// via the interactive review gate above. Only auto-re-assert the
|
|
1992
|
+
// recorded channel when the user hasn't opted into anything else —
|
|
1993
|
+
// otherwise --all-stable (or a review "switch to stable") would be
|
|
1994
|
+
// silently clobbered by the prior channel.
|
|
1995
|
+
const alreadyDecided = channelOptions.global || channelOptions.nextSet.has(code) || channelOptions.pins.has(code);
|
|
1996
|
+
if (!alreadyDecided) {
|
|
1997
|
+
if (recordedChannel === 'pinned' && prev.version) {
|
|
1998
|
+
channelOptions.pins.set(code, prev.version);
|
|
1999
|
+
} else if (recordedChannel === 'next') {
|
|
2000
|
+
channelOptions.nextSet.add(code);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
continue;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
// Stable channel: check for a newer released tag.
|
|
2007
|
+
if (!parsed) continue;
|
|
2008
|
+
// Respect explicit CLI intent (--pin / --next=CODE / --all-*) and any
|
|
2009
|
+
// choice the user already made in the earlier review gate. Without this
|
|
2010
|
+
// guard the upgrade classifier below would unconditionally call
|
|
2011
|
+
// `channelOptions.pins.set(code, prev.version)` on decline/major-refuse/
|
|
2012
|
+
// fetch-error, silently clobbering the user's override.
|
|
2013
|
+
const alreadyDecided = channelOptions.global || channelOptions.nextSet.has(code) || channelOptions.pins.has(code);
|
|
2014
|
+
if (alreadyDecided) continue;
|
|
2015
|
+
let tags;
|
|
2016
|
+
try {
|
|
2017
|
+
tags = await fetchStableTags(parsed.owner, parsed.repo);
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
await prompts.log.warn(`Could not check for updates on ${code} (${error.message}). Leaving at ${prev.version}.`);
|
|
2020
|
+
if (prev.version) channelOptions.pins.set(code, prev.version);
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
if (!tags || tags.length === 0) continue;
|
|
2024
|
+
const topTag = tags[0].tag; // e.g. "v1.7.0"
|
|
2025
|
+
const currentTag = prev.version || '';
|
|
2026
|
+
const diffClass = classifyUpgrade(currentTag, topTag);
|
|
2027
|
+
|
|
2028
|
+
if (diffClass === 'none') continue; // already at or above top tag
|
|
2029
|
+
|
|
2030
|
+
const notes = releaseNotesUrl(repoUrl, topTag);
|
|
2031
|
+
let accept;
|
|
2032
|
+
if (diffClass === 'major') {
|
|
2033
|
+
if (yes) {
|
|
2034
|
+
// Major under --yes is refused by design.
|
|
2035
|
+
await prompts.log.warn(
|
|
2036
|
+
`${code} ${currentTag} → ${topTag} is a new major release; staying on ${currentTag}. ` +
|
|
2037
|
+
`To accept, rerun with --pin ${code}=${topTag}.`,
|
|
2038
|
+
);
|
|
2039
|
+
channelOptions.pins.set(code, currentTag);
|
|
2040
|
+
continue;
|
|
2041
|
+
}
|
|
2042
|
+
accept = await prompts.confirm({
|
|
2043
|
+
message:
|
|
2044
|
+
`${code} ${topTag} available — new major release (may change behavior).` +
|
|
2045
|
+
(notes ? ` Release notes: ${notes}.` : '') +
|
|
2046
|
+
' Upgrade?',
|
|
2047
|
+
default: false,
|
|
2048
|
+
});
|
|
2049
|
+
} else if (diffClass === 'minor') {
|
|
2050
|
+
if (yes) {
|
|
2051
|
+
accept = true;
|
|
2052
|
+
} else {
|
|
2053
|
+
accept = await prompts.confirm({
|
|
2054
|
+
message: `${code} ${topTag} available (new features).` + (notes ? ` Release notes: ${notes}.` : '') + ' Upgrade?',
|
|
2055
|
+
default: true,
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
} else {
|
|
2059
|
+
// patch
|
|
2060
|
+
if (yes) {
|
|
2061
|
+
accept = true;
|
|
2062
|
+
} else {
|
|
2063
|
+
accept = await prompts.confirm({
|
|
2064
|
+
message: `${code} ${topTag} available. Upgrade?`,
|
|
2065
|
+
default: true,
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
if (!accept && currentTag) {
|
|
2071
|
+
// Freeze the current version by pinning it for this run.
|
|
2072
|
+
channelOptions.pins.set(code, currentTag);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
module.exports = { UI };
|