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