bmad-method 6.0.0-alpha.0 → 6.0.0-alpha.10
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/.github/ISSUE_TEMPLATE/config.yaml +4 -0
- package/.github/ISSUE_TEMPLATE/idea_submission.md +3 -3
- package/.github/workflows/bundle-latest.yaml +277 -0
- package/.github/workflows/manual-release.yaml +64 -17
- package/.github/workflows/quality.yaml +78 -0
- package/.husky/pre-commit +4 -0
- package/.prettierignore +6 -0
- package/.vscode/settings.json +3 -1
- package/CHANGELOG.md +1211 -2
- package/CONTRIBUTING.md +2 -13
- package/README.md +442 -0
- package/docs/BUNDLE_DISTRIBUTION_SETUP.md +95 -0
- package/docs/agent-customization-guide.md +208 -0
- package/docs/document-sharding-guide.md +449 -0
- package/docs/ide-info/auggie.md +2 -2
- package/docs/ide-info/claude-code.md +4 -4
- package/docs/ide-info/codex.md +9 -20
- package/docs/ide-info/crush.md +1 -1
- package/docs/ide-info/cursor.md +7 -7
- package/docs/ide-info/iflow.md +3 -3
- package/docs/ide-info/opencode.md +24 -0
- package/docs/index.md +227 -0
- package/docs/installers-bundlers/ide-injections.md +2 -12
- package/docs/installers-bundlers/installers-modules-platforms-reference.md +100 -67
- package/docs/v4-to-v6-upgrade.md +227 -0
- package/docs/web-bundles-gemini-gpt-guide.md +473 -0
- package/eslint.config.mjs +6 -2
- package/package.json +13 -6
- package/src/core/_module-installer/install-config.yaml +35 -0
- package/src/core/_module-installer/installer.js +1 -9
- package/src/core/agents/bmad-master.agent.yaml +39 -0
- package/src/core/agents/bmad-web-orchestrator.agent.xml +113 -0
- package/src/core/tasks/advanced-elicitation-methods.csv +21 -0
- package/src/core/tasks/{adv-elicit.md → advanced-elicitation.xml} +11 -14
- package/src/core/tasks/{index-docs.md → index-docs.xml} +6 -10
- package/src/core/tasks/{validate-workflow.md → validate-workflow.xml} +4 -7
- package/src/core/tasks/workflow.xml +270 -0
- package/src/core/tools/shard-doc.xml +109 -0
- package/src/{modules/cis → core}/workflows/brainstorming/README.md +8 -18
- package/src/{modules/cis → core}/workflows/brainstorming/instructions.md +29 -24
- package/src/{modules/cis → core}/workflows/brainstorming/template.md +6 -2
- package/src/{modules/cis → core}/workflows/brainstorming/workflow.yaml +17 -9
- package/src/core/workflows/party-mode/instructions.md +27 -25
- package/src/core/workflows/party-mode/workflow.yaml +14 -10
- package/src/modules/bmb/README.md +194 -0
- package/src/modules/bmb/_module-installer/install-config.yaml +31 -0
- package/src/modules/bmb/agents/bmad-builder.agent.yaml +57 -0
- package/src/modules/bmb/workflows/audit-workflow/checklist.md +142 -0
- package/src/modules/bmb/workflows/audit-workflow/instructions.md +341 -0
- package/src/modules/bmb/workflows/audit-workflow/template.md +118 -0
- package/src/modules/bmb/workflows/audit-workflow/workflow.yaml +25 -0
- package/src/modules/bmb/workflows/convert-legacy/README.md +34 -34
- package/src/modules/bmb/workflows/convert-legacy/checklist.md +22 -21
- package/src/modules/bmb/workflows/convert-legacy/instructions.md +135 -86
- package/src/modules/bmb/workflows/convert-legacy/workflow.yaml +10 -15
- package/src/modules/bmb/workflows/create-agent/README.md +144 -209
- package/src/modules/bmb/workflows/create-agent/agent-architecture.md +68 -65
- package/src/modules/bmb/workflows/create-agent/agent-command-patterns.md +174 -172
- package/src/modules/bmb/workflows/create-agent/agent-types.md +139 -24
- package/src/modules/bmb/workflows/create-agent/brainstorm-context.md +4 -4
- package/src/modules/bmb/workflows/create-agent/checklist.md +30 -102
- package/src/modules/bmb/workflows/create-agent/communication-styles.md +22 -60
- package/src/modules/bmb/workflows/create-agent/instructions.md +339 -221
- package/src/modules/bmb/workflows/create-agent/workflow.yaml +28 -19
- package/src/modules/bmb/workflows/create-module/README.md +164 -153
- package/src/modules/bmb/workflows/create-module/brainstorm-context.md +1 -1
- package/src/modules/bmb/workflows/create-module/checklist.md +21 -31
- package/src/modules/bmb/workflows/create-module/installer-templates/install-config.yaml +92 -0
- package/src/modules/bmb/workflows/create-module/installer-templates/installer.js +1 -1
- package/src/modules/bmb/workflows/create-module/instructions.md +306 -238
- package/src/modules/bmb/workflows/create-module/module-structure.md +135 -45
- package/src/modules/bmb/workflows/create-module/workflow.yaml +32 -27
- package/src/modules/bmb/workflows/create-workflow/README.md +70 -9
- package/src/modules/bmb/workflows/create-workflow/brainstorm-context.md +4 -4
- package/src/modules/bmb/workflows/create-workflow/checklist.md +22 -0
- package/src/modules/bmb/workflows/create-workflow/instructions.md +505 -47
- package/src/modules/bmb/workflows/create-workflow/workflow-creation-guide.md +939 -68
- package/src/modules/bmb/workflows/create-workflow/workflow-template/instructions.md +6 -3
- package/src/modules/bmb/workflows/create-workflow/workflow-template/workflow.yaml +31 -5
- package/src/modules/bmb/workflows/create-workflow/workflow.yaml +16 -17
- package/src/modules/bmb/workflows/edit-agent/README.md +112 -0
- package/src/modules/bmb/workflows/edit-agent/checklist.md +112 -0
- package/src/modules/bmb/workflows/edit-agent/instructions.md +290 -0
- package/src/modules/bmb/workflows/edit-agent/workflow.yaml +33 -0
- package/src/modules/bmb/workflows/edit-module/README.md +187 -0
- package/src/modules/bmb/workflows/edit-module/checklist.md +165 -0
- package/src/modules/bmb/workflows/edit-module/instructions.md +341 -0
- package/src/modules/bmb/workflows/edit-module/workflow.yaml +34 -0
- package/src/modules/bmb/workflows/edit-workflow/README.md +58 -2
- package/src/modules/bmb/workflows/edit-workflow/instructions.md +290 -118
- package/src/modules/bmb/workflows/edit-workflow/workflow.yaml +9 -16
- package/src/modules/bmb/workflows/module-brief/README.md +17 -17
- package/src/modules/bmb/workflows/module-brief/checklist.md +6 -6
- package/src/modules/bmb/workflows/module-brief/instructions.md +10 -7
- package/src/modules/bmb/workflows/module-brief/template.md +9 -9
- package/src/modules/bmb/workflows/module-brief/workflow.yaml +16 -10
- package/src/modules/bmb/workflows/redoc/README.md +5 -5
- package/src/modules/bmb/workflows/redoc/checklist.md +2 -2
- package/src/modules/bmb/workflows/redoc/instructions.md +20 -10
- package/src/modules/bmb/workflows/redoc/workflow.yaml +6 -5
- package/src/modules/bmgd/README.md +208 -0
- package/src/modules/bmgd/_module-installer/install-config.yaml +54 -0
- package/src/modules/bmgd/agents/game-architect.agent.yaml +33 -0
- package/src/modules/bmgd/agents/game-designer.agent.yaml +40 -0
- package/src/modules/bmgd/agents/game-dev.agent.yaml +40 -0
- package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +75 -0
- package/src/modules/bmgd/teams/default-party.csv +10 -0
- package/src/modules/bmgd/teams/team-gamedev.yaml +18 -0
- package/src/modules/{bmm/workflows/1-analysis → bmgd/workflows/1-preproduction}/brainstorm-game/game-context.md +3 -3
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/instructions.md +130 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.yaml +41 -0
- package/src/modules/{bmm/workflows/1-analysis → bmgd/workflows/1-preproduction}/game-brief/checklist.md +2 -2
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/instructions.md +373 -0
- package/src/modules/{bmm/workflows/1-analysis → bmgd/workflows/1-preproduction}/game-brief/template.md +2 -2
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.yaml +36 -0
- package/src/modules/bmgd/workflows/2-design/gdd/checklist.md +148 -0
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/action-platformer.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/adventure.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/card-game.md +2 -2
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/fighting.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/horror.md +2 -2
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/idle-incremental.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/moba.md +4 -4
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/party-game.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/racing.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/rhythm.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/roguelike.md +2 -2
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/rpg.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/sandbox.md +4 -4
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/shooter.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/simulation.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/sports.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/strategy.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/survival.md +2 -2
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/text-based.md +3 -3
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/tower-defense.md +5 -5
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/turn-based-tactics.md +4 -4
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/visual-novel.md +1 -1
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/gdd-template.md +6 -12
- package/src/modules/bmgd/workflows/2-design/gdd/instructions-gdd.md +502 -0
- package/src/modules/bmgd/workflows/2-design/gdd/workflow.yaml +81 -0
- package/src/modules/bmgd/workflows/2-design/narrative/checklist.md +139 -0
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/narrative/instructions-narrative.md +99 -12
- package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/narrative/narrative-template.md +8 -8
- package/src/modules/bmgd/workflows/2-design/narrative/workflow.yaml +32 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/architecture-patterns.yaml +321 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/architecture-template.md +103 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/checklist.md +240 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/decision-catalog.yaml +222 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/instructions.md +701 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/pattern-categories.csv +13 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.yaml +69 -0
- package/src/modules/bmgd/workflows/4-production/code-review/instructions.md +398 -0
- package/src/modules/bmgd/workflows/4-production/code-review/workflow.yaml +61 -0
- package/src/modules/bmgd/workflows/4-production/correct-course/checklist.md +279 -0
- package/src/modules/bmgd/workflows/4-production/correct-course/instructions.md +206 -0
- package/src/modules/bmgd/workflows/4-production/correct-course/workflow.yaml +58 -0
- package/src/modules/bmgd/workflows/4-production/create-story/checklist.md +240 -0
- package/src/modules/bmgd/workflows/4-production/create-story/instructions.md +256 -0
- package/src/modules/bmgd/workflows/4-production/create-story/template.md +51 -0
- package/src/modules/bmgd/workflows/4-production/create-story/workflow.yaml +74 -0
- package/src/modules/bmgd/workflows/4-production/dev-story/checklist.md +38 -0
- package/src/modules/bmgd/workflows/4-production/dev-story/instructions.md +267 -0
- package/src/modules/bmgd/workflows/4-production/dev-story/workflow.yaml +58 -0
- package/src/modules/{bmm/workflows/3-solutioning/tech-spec → bmgd/workflows/4-production/epic-tech-context}/checklist.md +1 -1
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/instructions.md +164 -0
- package/src/modules/{bmm/workflows/3-solutioning/tech-spec → bmgd/workflows/4-production/epic-tech-context}/template.md +1 -1
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/workflow.yaml +58 -0
- package/src/modules/bmgd/workflows/4-production/retrospective/instructions.md +1443 -0
- package/src/modules/bmgd/workflows/4-production/retrospective/workflow.yaml +57 -0
- package/src/modules/bmgd/workflows/4-production/sprint-planning/checklist.md +33 -0
- package/src/modules/bmgd/workflows/4-production/sprint-planning/instructions.md +234 -0
- package/src/modules/bmgd/workflows/4-production/sprint-planning/sprint-status-template.yaml +55 -0
- package/src/modules/bmgd/workflows/4-production/sprint-planning/workflow.yaml +51 -0
- package/src/modules/bmgd/workflows/4-production/story-context/checklist.md +16 -0
- package/src/modules/bmgd/workflows/4-production/story-context/context-template.xml +34 -0
- package/src/modules/bmgd/workflows/4-production/story-context/instructions.md +209 -0
- package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +63 -0
- package/src/modules/bmgd/workflows/4-production/story-done/instructions.md +111 -0
- package/src/modules/bmgd/workflows/4-production/story-done/workflow.yaml +28 -0
- package/src/modules/bmgd/workflows/4-production/story-ready/instructions.md +117 -0
- package/src/modules/bmgd/workflows/4-production/story-ready/workflow.yaml +25 -0
- package/src/modules/bmm/README.md +128 -0
- package/src/modules/bmm/_module-installer/install-config.yaml +60 -0
- package/src/modules/bmm/_module-installer/installer.js +3 -3
- package/src/modules/bmm/_module-installer/platform-specifics/claude-code.js +1 -1
- package/src/modules/bmm/_module-installer/platform-specifics/windsurf.js +1 -1
- package/src/modules/bmm/agents/analyst.agent.yaml +49 -0
- package/src/modules/bmm/agents/architect.agent.yaml +42 -0
- package/src/modules/bmm/agents/dev.agent.yaml +40 -0
- package/src/modules/bmm/agents/frame-expert.agent.yaml +42 -0
- package/src/modules/bmm/agents/pm.agent.yaml +64 -0
- package/src/modules/bmm/agents/sm.agent.yaml +73 -0
- package/src/modules/bmm/agents/tea.agent.yaml +67 -0
- package/src/modules/bmm/agents/tech-writer.agent.yaml +68 -0
- package/src/modules/bmm/agents/ux-designer.agent.yaml +39 -0
- package/src/modules/bmm/docs/README.md +236 -0
- package/src/modules/bmm/docs/agents-guide.md +1058 -0
- package/src/modules/bmm/docs/brownfield-guide.md +762 -0
- package/src/modules/bmm/docs/enterprise-agentic-development.md +686 -0
- package/src/modules/bmm/docs/faq.md +588 -0
- package/src/modules/bmm/docs/glossary.md +320 -0
- package/src/modules/bmm/docs/party-mode.md +224 -0
- package/src/modules/bmm/docs/quick-spec-flow.md +652 -0
- package/src/modules/bmm/docs/quick-start.md +376 -0
- package/src/modules/bmm/docs/scale-adaptive-system.md +612 -0
- package/src/modules/bmm/docs/test-architecture.md +396 -0
- package/src/modules/bmm/docs/workflow-architecture-reference.md +366 -0
- package/src/modules/bmm/docs/workflow-document-project-reference.md +489 -0
- package/src/modules/bmm/docs/workflows-analysis.md +370 -0
- package/src/modules/bmm/docs/workflows-implementation.md +286 -0
- package/src/modules/bmm/docs/workflows-planning.md +612 -0
- package/src/modules/bmm/docs/workflows-solutioning.md +554 -0
- package/src/modules/bmm/teams/default-party.csv +20 -0
- package/src/modules/bmm/teams/team-fullstack.yaml +13 -0
- package/src/modules/bmm/testarch/knowledge/ci-burn-in.md +675 -0
- package/src/modules/bmm/testarch/knowledge/component-tdd.md +486 -0
- package/src/modules/bmm/testarch/knowledge/contract-testing.md +957 -0
- package/src/modules/bmm/testarch/knowledge/data-factories.md +500 -0
- package/src/modules/bmm/testarch/knowledge/email-auth.md +721 -0
- package/src/modules/bmm/testarch/knowledge/error-handling.md +725 -0
- package/src/modules/bmm/testarch/knowledge/feature-flags.md +750 -0
- package/src/modules/bmm/testarch/knowledge/fixture-architecture.md +401 -0
- package/src/modules/bmm/testarch/knowledge/network-first.md +486 -0
- package/src/modules/bmm/testarch/knowledge/nfr-criteria.md +670 -0
- package/src/modules/bmm/testarch/knowledge/playwright-config.md +730 -0
- package/src/modules/bmm/testarch/knowledge/probability-impact.md +601 -0
- package/src/modules/bmm/testarch/knowledge/risk-governance.md +615 -0
- package/src/modules/bmm/testarch/knowledge/selective-testing.md +732 -0
- package/src/modules/bmm/testarch/knowledge/selector-resilience.md +527 -0
- package/src/modules/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
- package/src/modules/bmm/testarch/knowledge/test-levels-framework.md +473 -0
- package/src/modules/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
- package/src/modules/bmm/testarch/knowledge/test-quality.md +664 -0
- package/src/modules/bmm/testarch/knowledge/timing-debugging.md +372 -0
- package/src/modules/bmm/testarch/knowledge/visual-debugging.md +524 -0
- package/src/modules/bmm/testarch/tea-index.csv +22 -0
- package/src/modules/bmm/workflows/1-analysis/brainstorm-project/instructions.md +82 -8
- package/src/modules/bmm/workflows/1-analysis/brainstorm-project/project-context.md +4 -4
- package/src/modules/bmm/workflows/1-analysis/brainstorm-project/workflow.yaml +23 -5
- package/src/modules/bmm/workflows/1-analysis/domain-research/instructions.md +425 -0
- package/src/modules/bmm/workflows/1-analysis/domain-research/template.md +180 -0
- package/src/modules/bmm/workflows/1-analysis/domain-research/workflow.yaml +56 -0
- package/src/modules/bmm/workflows/1-analysis/product-brief/checklist.md +2 -2
- package/src/modules/bmm/workflows/1-analysis/product-brief/instructions.md +418 -247
- package/src/modules/bmm/workflows/1-analysis/product-brief/template.md +93 -77
- package/src/modules/bmm/workflows/1-analysis/product-brief/workflow.yaml +42 -14
- package/src/modules/bmm/workflows/1-analysis/research/checklist-deep-prompt.md +144 -0
- package/src/modules/bmm/workflows/1-analysis/research/checklist-technical.md +249 -0
- package/src/modules/bmm/workflows/1-analysis/research/checklist.md +156 -59
- package/src/modules/bmm/workflows/1-analysis/research/instructions-deep-prompt.md +115 -47
- package/src/modules/bmm/workflows/1-analysis/research/instructions-market.md +236 -114
- package/src/modules/bmm/workflows/1-analysis/research/instructions-router.md +109 -66
- package/src/modules/bmm/workflows/1-analysis/research/instructions-technical.md +147 -55
- package/src/modules/bmm/workflows/1-analysis/research/template-deep-prompt.md +1 -1
- package/src/modules/bmm/workflows/1-analysis/research/template-market.md +38 -2
- package/src/modules/bmm/workflows/1-analysis/research/template-technical.md +36 -1
- package/src/modules/bmm/workflows/1-analysis/research/workflow.yaml +36 -123
- package/src/modules/bmm/workflows/2-plan-workflows/create-epics-and-stories/epics-template.md +80 -0
- package/src/modules/bmm/workflows/2-plan-workflows/create-epics-and-stories/instructions.md +616 -0
- package/src/modules/bmm/workflows/2-plan-workflows/create-epics-and-stories/workflow.yaml +63 -0
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/checklist.md +310 -0
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/instructions.md +1308 -0
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +145 -0
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/workflow.yaml +93 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/checklist.md +346 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/domain-complexity.csv +13 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/instructions.md +703 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/prd-template.md +204 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/project-types.csv +11 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/workflow.yaml +78 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/checklist.md +217 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/epics-template.md +74 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/instructions-generate-stories.md +436 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/instructions.md +980 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/tech-spec-template.md +181 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/user-story-template.md +90 -0
- package/src/modules/bmm/workflows/2-plan-workflows/tech-spec/workflow.yaml +60 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/architecture-patterns.yaml +321 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/architecture-template.md +103 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/checklist.md +240 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/decision-catalog.yaml +222 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/instructions.md +768 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/pattern-categories.csv +13 -0
- package/src/modules/bmm/workflows/3-solutioning/architecture/workflow.yaml +100 -0
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/checklist.md +169 -0
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/instructions.md +332 -0
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/template.md +146 -0
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/workflow.yaml +64 -0
- package/src/modules/bmm/workflows/4-implementation/code-review/backlog_template.md +12 -0
- package/src/modules/bmm/workflows/4-implementation/code-review/checklist.md +22 -0
- package/src/modules/bmm/workflows/4-implementation/code-review/instructions.md +398 -0
- package/src/modules/bmm/workflows/4-implementation/code-review/workflow.yaml +61 -0
- package/src/modules/bmm/workflows/4-implementation/correct-course/checklist.md +6 -6
- package/src/modules/bmm/workflows/4-implementation/correct-course/instructions.md +70 -54
- package/src/modules/bmm/workflows/4-implementation/correct-course/workflow.yaml +44 -21
- package/src/modules/bmm/workflows/4-implementation/create-story/checklist.md +240 -39
- package/src/modules/bmm/workflows/4-implementation/create-story/instructions.md +203 -28
- package/src/modules/bmm/workflows/4-implementation/create-story/template.md +2 -8
- package/src/modules/bmm/workflows/4-implementation/create-story/workflow.yaml +43 -42
- package/src/modules/bmm/workflows/4-implementation/dev-story/checklist.md +1 -1
- package/src/modules/bmm/workflows/4-implementation/dev-story/instructions.md +220 -40
- package/src/modules/bmm/workflows/4-implementation/dev-story/workflow.yaml +43 -38
- package/src/modules/bmm/workflows/4-implementation/epic-tech-context/checklist.md +17 -0
- package/src/modules/bmm/workflows/4-implementation/epic-tech-context/instructions.md +164 -0
- package/src/modules/bmm/workflows/4-implementation/epic-tech-context/template.md +76 -0
- package/src/modules/bmm/workflows/4-implementation/epic-tech-context/workflow.yaml +58 -0
- package/src/modules/bmm/workflows/4-implementation/retrospective/instructions.md +1280 -228
- package/src/modules/bmm/workflows/4-implementation/retrospective/workflow.yaml +41 -25
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/instructions.md +234 -0
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +51 -0
- package/src/modules/bmm/workflows/4-implementation/story-context/checklist.md +1 -1
- package/src/modules/bmm/workflows/4-implementation/story-context/context-template.xml +1 -1
- package/src/modules/bmm/workflows/4-implementation/story-context/instructions.md +155 -22
- package/src/modules/bmm/workflows/4-implementation/story-context/workflow.yaml +43 -36
- package/src/modules/bmm/workflows/4-implementation/story-done/instructions.md +111 -0
- package/src/modules/bmm/workflows/4-implementation/story-done/workflow.yaml +28 -0
- package/src/modules/bmm/workflows/4-implementation/story-ready/instructions.md +117 -0
- package/src/modules/bmm/workflows/4-implementation/story-ready/workflow.yaml +25 -0
- package/src/modules/bmm/workflows/document-project/checklist.md +245 -0
- package/src/modules/bmm/workflows/document-project/documentation-requirements.csv +12 -0
- package/src/modules/bmm/workflows/document-project/instructions.md +222 -0
- package/src/modules/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
- package/src/modules/bmm/workflows/document-project/templates/index-template.md +169 -0
- package/src/modules/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
- package/src/modules/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
- package/src/modules/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
- package/src/modules/bmm/workflows/document-project/workflow.yaml +31 -0
- package/src/modules/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
- package/src/modules/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
- package/src/modules/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
- package/src/modules/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
- package/src/modules/bmm/workflows/frame-expert/_shared/excalidraw-helpers.md +127 -0
- package/src/modules/bmm/workflows/frame-expert/_shared/excalidraw-library.json +90 -0
- package/src/modules/bmm/workflows/frame-expert/_shared/excalidraw-templates.yaml +127 -0
- package/src/modules/bmm/workflows/frame-expert/_shared/validate-json-instructions.md +79 -0
- package/src/modules/bmm/workflows/frame-expert/create-dataflow/checklist.md +39 -0
- package/src/modules/bmm/workflows/frame-expert/create-dataflow/instructions.md +131 -0
- package/src/modules/bmm/workflows/frame-expert/create-dataflow/workflow.yaml +24 -0
- package/src/modules/bmm/workflows/frame-expert/create-diagram/checklist.md +43 -0
- package/src/modules/bmm/workflows/frame-expert/create-diagram/instructions.md +142 -0
- package/src/modules/bmm/workflows/frame-expert/create-diagram/workflow.yaml +25 -0
- package/src/modules/bmm/workflows/frame-expert/create-flowchart/checklist.md +49 -0
- package/src/modules/bmm/workflows/frame-expert/create-flowchart/instructions.md +242 -0
- package/src/modules/bmm/workflows/frame-expert/create-flowchart/workflow.yaml +28 -0
- package/src/modules/bmm/workflows/frame-expert/create-wireframe/checklist.md +38 -0
- package/src/modules/bmm/workflows/frame-expert/create-wireframe/instructions.md +133 -0
- package/src/modules/bmm/workflows/frame-expert/create-wireframe/workflow.yaml +24 -0
- package/src/modules/bmm/workflows/techdoc/documentation-standards.md +262 -0
- package/src/modules/bmm/workflows/testarch/atdd/atdd-checklist-template.md +363 -0
- package/src/modules/bmm/workflows/testarch/atdd/checklist.md +373 -0
- package/src/modules/bmm/workflows/testarch/atdd/instructions.md +785 -0
- package/src/modules/bmm/workflows/testarch/atdd/workflow.yaml +47 -0
- package/src/modules/bmm/workflows/testarch/automate/checklist.md +580 -0
- package/src/modules/bmm/workflows/testarch/automate/instructions.md +1303 -0
- package/src/modules/bmm/workflows/testarch/automate/workflow.yaml +54 -0
- package/src/modules/bmm/workflows/testarch/ci/checklist.md +246 -0
- package/src/modules/bmm/workflows/testarch/ci/github-actions-template.yaml +165 -0
- package/src/modules/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +128 -0
- package/src/modules/bmm/workflows/testarch/ci/instructions.md +517 -0
- package/src/modules/bmm/workflows/testarch/ci/workflow.yaml +47 -0
- package/src/modules/bmm/workflows/testarch/framework/checklist.md +321 -0
- package/src/modules/bmm/workflows/testarch/framework/instructions.md +455 -0
- package/src/modules/bmm/workflows/testarch/framework/workflow.yaml +49 -0
- package/src/modules/bmm/workflows/testarch/nfr-assess/checklist.md +405 -0
- package/src/modules/bmm/workflows/testarch/nfr-assess/instructions.md +722 -0
- package/src/modules/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +443 -0
- package/src/modules/bmm/workflows/testarch/nfr-assess/workflow.yaml +49 -0
- package/src/modules/bmm/workflows/testarch/test-design/checklist.md +234 -0
- package/src/modules/bmm/workflows/testarch/test-design/instructions.md +782 -0
- package/src/modules/bmm/workflows/testarch/test-design/test-design-template.md +285 -0
- package/src/modules/bmm/workflows/testarch/test-design/workflow.yaml +50 -0
- package/src/modules/bmm/workflows/testarch/test-review/checklist.md +470 -0
- package/src/modules/bmm/workflows/testarch/test-review/instructions.md +608 -0
- package/src/modules/bmm/workflows/testarch/test-review/test-review-template.md +388 -0
- package/src/modules/bmm/workflows/testarch/test-review/workflow.yaml +48 -0
- package/src/modules/bmm/workflows/testarch/trace/checklist.md +654 -0
- package/src/modules/bmm/workflows/testarch/trace/instructions.md +1045 -0
- package/src/modules/bmm/workflows/testarch/trace/trace-template.md +673 -0
- package/src/modules/bmm/workflows/testarch/trace/workflow.yaml +57 -0
- package/src/modules/bmm/workflows/workflow-status/init/instructions.md +334 -0
- package/src/modules/bmm/workflows/workflow-status/init/workflow.yaml +29 -0
- package/src/modules/bmm/workflows/workflow-status/instructions.md +388 -0
- package/src/modules/bmm/workflows/workflow-status/paths/enterprise-brownfield.yaml +138 -0
- package/src/modules/bmm/workflows/workflow-status/paths/enterprise-greenfield.yaml +126 -0
- package/src/modules/bmm/workflows/workflow-status/paths/game-design.yaml +52 -0
- package/src/modules/bmm/workflows/workflow-status/paths/method-brownfield.yaml +122 -0
- package/src/modules/bmm/workflows/workflow-status/paths/method-greenfield.yaml +113 -0
- package/src/modules/bmm/workflows/workflow-status/paths/quick-flow-brownfield.yaml +58 -0
- package/src/modules/bmm/workflows/workflow-status/paths/quick-flow-greenfield.yaml +47 -0
- package/src/modules/bmm/workflows/workflow-status/project-levels.yaml +59 -0
- package/src/modules/bmm/workflows/workflow-status/workflow-status-template.yaml +24 -0
- package/src/modules/bmm/workflows/workflow-status/workflow.yaml +30 -0
- package/src/modules/cis/README.md +153 -0
- package/src/modules/cis/_module-installer/{install-menu-config.yaml → install-config.yaml} +6 -4
- package/src/modules/cis/_module-installer/installer.js +1 -1
- package/src/modules/cis/agents/README.md +1 -1
- package/src/modules/cis/agents/brainstorming-coach.agent.yaml +28 -0
- package/src/modules/cis/agents/creative-problem-solver.agent.yaml +28 -0
- package/src/modules/cis/agents/design-thinking-coach.agent.yaml +28 -0
- package/src/modules/cis/agents/innovation-strategist.agent.yaml +28 -0
- package/src/modules/cis/agents/storyteller.agent.yaml +28 -0
- package/src/modules/cis/teams/creative-squad.yaml +1 -0
- package/src/modules/cis/teams/default-party.csv +11 -0
- package/src/modules/cis/workflows/README.md +102 -30
- package/src/modules/cis/workflows/design-thinking/design-methods.csv +1 -1
- package/src/modules/cis/workflows/design-thinking/instructions.md +4 -2
- package/src/modules/cis/workflows/design-thinking/workflow.yaml +16 -7
- package/src/modules/cis/workflows/innovation-strategy/innovation-frameworks.csv +2 -2
- package/src/modules/cis/workflows/innovation-strategy/instructions.md +7 -5
- package/src/modules/cis/workflows/innovation-strategy/template.md +6 -6
- package/src/modules/cis/workflows/innovation-strategy/workflow.yaml +16 -7
- package/src/modules/cis/workflows/problem-solving/instructions.md +4 -2
- package/src/modules/cis/workflows/problem-solving/solving-methods.csv +6 -6
- package/src/modules/cis/workflows/problem-solving/template.md +3 -3
- package/src/modules/cis/workflows/problem-solving/workflow.yaml +16 -7
- package/src/modules/cis/workflows/storytelling/instructions.md +48 -30
- package/src/modules/cis/workflows/storytelling/template.md +2 -2
- package/src/modules/cis/workflows/storytelling/workflow.yaml +16 -7
- package/src/utility/models/action-command-header.md +0 -0
- package/src/utility/models/agent-activation-ide.xml +4 -4
- package/src/utility/models/agent-activation-web.xml +60 -0
- package/src/utility/models/agent-command-header.md +1 -0
- package/src/utility/models/agent-in-team-activation.xml +3 -0
- package/src/utility/models/fragments/activation-rules.xml +8 -0
- package/src/utility/models/fragments/activation-steps.xml +16 -0
- package/src/utility/models/fragments/handler-action.xml +4 -0
- package/src/utility/models/fragments/handler-data.xml +5 -0
- package/src/utility/models/fragments/handler-exec.xml +5 -0
- package/src/utility/models/fragments/handler-tmpl.xml +5 -0
- package/src/utility/models/fragments/handler-validate-workflow.xml +7 -0
- package/src/utility/models/fragments/handler-workflow.xml +9 -0
- package/src/utility/models/fragments/menu-handlers.xml +6 -0
- package/src/utility/models/fragments/web-bundle-activation-steps.xml +32 -0
- package/src/utility/templates/agent.customize.template.yaml +42 -0
- package/test/README.md +295 -0
- package/test/fixtures/agent-schema/invalid/critical-actions/actions-as-string.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/critical-actions/empty-string-in-actions.agent.yaml +29 -0
- package/test/fixtures/agent-schema/invalid/menu/empty-menu.agent.yaml +21 -0
- package/test/fixtures/agent-schema/invalid/menu/missing-menu.agent.yaml +19 -0
- package/test/fixtures/agent-schema/invalid/menu-commands/empty-command-target.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/menu-commands/no-command-target.agent.yaml +23 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/camel-case.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/duplicate-triggers.agent.yaml +30 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/empty-trigger.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/leading-asterisk.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/snake-case.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/trigger-with-spaces.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/metadata/core-agent-with-module.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/metadata/empty-module-string.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/metadata/empty-name.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/metadata/extra-metadata-fields.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/metadata/missing-id.agent.yaml +23 -0
- package/test/fixtures/agent-schema/invalid/metadata/module-agent-missing-module.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/metadata/wrong-module-value.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/persona/empty-principles-array.agent.yaml +23 -0
- package/test/fixtures/agent-schema/invalid/persona/empty-string-in-principles.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/persona/extra-persona-fields.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/persona/missing-role.agent.yaml +23 -0
- package/test/fixtures/agent-schema/invalid/prompts/empty-content.agent.yaml +28 -0
- package/test/fixtures/agent-schema/invalid/prompts/extra-prompt-fields.agent.yaml +30 -0
- package/test/fixtures/agent-schema/invalid/prompts/missing-content.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/prompts/missing-id.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/top-level/empty-file.agent.yaml +5 -0
- package/test/fixtures/agent-schema/invalid/top-level/extra-top-level-keys.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/top-level/missing-agent-key.agent.yaml +11 -0
- package/test/fixtures/agent-schema/invalid/yaml-errors/invalid-indentation.agent.yaml +19 -0
- package/test/fixtures/agent-schema/invalid/yaml-errors/malformed-yaml.agent.yaml +18 -0
- package/test/fixtures/agent-schema/valid/critical-actions/empty-critical-actions.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/critical-actions/no-critical-actions.agent.yaml +21 -0
- package/test/fixtures/agent-schema/valid/critical-actions/valid-critical-actions.agent.yaml +26 -0
- package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +30 -0
- package/test/fixtures/agent-schema/valid/menu/single-menu-item.agent.yaml +21 -0
- package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +39 -0
- package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/menu-triggers/kebab-case-triggers.agent.yaml +33 -0
- package/test/fixtures/agent-schema/valid/metadata/empty-module-name-in-path.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/metadata/malformed-path-treated-as-core.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/metadata/module-agent-correct.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/persona/complete-persona.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/prompts/empty-prompts.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/prompts/no-prompts.agent.yaml +21 -0
- package/test/fixtures/agent-schema/valid/prompts/valid-prompts-minimal.agent.yaml +27 -0
- package/test/fixtures/agent-schema/valid/prompts/valid-prompts-with-description.agent.yaml +29 -0
- package/test/fixtures/agent-schema/valid/top-level/minimal-core-agent.agent.yaml +23 -0
- package/test/test-agent-schema.js +387 -0
- package/test/test-cli-integration.sh +159 -0
- package/test/test-installation-components.js +214 -0
- package/test/unit-test-schema.js +133 -0
- package/tools/bmad-npx-wrapper.js +38 -0
- package/tools/cli/README.md +608 -0
- package/tools/cli/bmad-cli.js +0 -2
- package/tools/cli/bundlers/bundle-web.js +24 -2
- package/tools/cli/bundlers/test-bundler.js +1 -1
- package/tools/cli/bundlers/web-bundler.js +944 -49
- package/tools/cli/commands/build.js +458 -0
- package/tools/cli/commands/install.js +46 -8
- package/tools/cli/installers/lib/core/config-collector.js +490 -43
- package/tools/cli/installers/lib/core/dependency-resolver.js +5 -1
- package/tools/cli/installers/lib/core/detector.js +134 -13
- package/tools/cli/installers/lib/core/ide-config-manager.js +154 -0
- package/tools/cli/installers/lib/core/installer.js +1365 -167
- package/tools/cli/installers/lib/core/manifest-generator.js +388 -81
- package/tools/cli/installers/lib/core/manifest.js +100 -44
- package/tools/cli/installers/lib/ide/_base-ide.js +368 -23
- package/tools/cli/installers/lib/ide/auggie.js +100 -192
- package/tools/cli/installers/lib/ide/claude-code.js +167 -321
- package/tools/cli/installers/lib/ide/cline.js +146 -227
- package/tools/cli/installers/lib/ide/codex.js +138 -202
- package/tools/cli/installers/lib/ide/crush.js +108 -72
- package/tools/cli/installers/lib/ide/cursor.js +148 -20
- package/tools/cli/installers/lib/ide/gemini.js +109 -63
- package/tools/cli/installers/lib/ide/github-copilot.js +60 -47
- package/tools/cli/installers/lib/ide/iflow.js +13 -30
- package/tools/cli/installers/lib/ide/kilo.js +20 -16
- package/tools/cli/installers/lib/ide/manager.js +41 -35
- package/tools/cli/installers/lib/ide/opencode.js +212 -0
- package/tools/cli/installers/lib/ide/qwen.js +198 -68
- package/tools/cli/installers/lib/ide/roo.js +27 -62
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +90 -0
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +143 -0
- package/tools/cli/installers/lib/ide/shared/module-injections.js +133 -0
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +119 -0
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +237 -0
- package/tools/cli/installers/lib/ide/templates/agent-command-template.md +14 -0
- package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +14 -0
- package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +12 -0
- package/tools/cli/installers/lib/ide/{workflow-command-template.md → templates/workflow-command-template.md} +7 -5
- package/tools/cli/installers/lib/ide/trae.js +138 -54
- package/tools/cli/installers/lib/ide/windsurf.js +104 -42
- package/tools/cli/installers/lib/modules/manager.js +332 -10
- package/tools/cli/lib/activation-builder.js +168 -0
- package/tools/cli/lib/agent-analyzer.js +81 -0
- package/tools/cli/lib/agent-party-generator.js +3 -3
- package/tools/cli/lib/cli-utils.js +52 -50
- package/tools/cli/lib/config.js +3 -1
- package/tools/cli/lib/ui.js +106 -14
- package/tools/cli/lib/xml-handler.js +47 -1
- package/tools/cli/lib/yaml-format.js +2 -1
- package/tools/cli/lib/yaml-xml-builder.js +507 -0
- package/tools/cli/regenerate-manifests.js +1 -1
- package/tools/cli/test-yaml-builder.js +43 -0
- package/tools/format-workflow-md.js +263 -0
- package/tools/platform-codes.yaml +6 -0
- package/tools/schema/agent.js +240 -0
- package/tools/validate-agent-schema.js +110 -0
- package/v6-open-items.md +17 -0
- package/.github/workflows/format-check.yaml +0 -43
- package/docs/codebase-flattener.md +0 -19
- package/readme.md +0 -216
- package/src/core/_module-installer/install-menu-config.yaml +0 -24
- package/src/core/agents/bmad-master.md +0 -27
- package/src/core/agents/bmad-web-orchestrator.md +0 -71
- package/src/core/tasks/shard-doc.md +0 -57
- package/src/core/tasks/workflow.md +0 -141
- package/src/core/workflows/bmad-init/instructions.md +0 -79
- package/src/core/workflows/bmad-init/workflow.yaml +0 -24
- package/src/modules/bmb/_module-installer/install-menu-config.yaml +0 -16
- package/src/modules/bmb/agents/bmad-builder.md +0 -30
- package/src/modules/bmb/workflows/create-module/installer-templates/install-module-config.yaml +0 -132
- package/src/modules/bmm/_module-installer/install-menu-config.yaml +0 -49
- package/src/modules/bmm/agents/analyst.md +0 -26
- package/src/modules/bmm/agents/architect.md +0 -29
- package/src/modules/bmm/agents/dev.md +0 -61
- package/src/modules/bmm/agents/game-architect.md +0 -26
- package/src/modules/bmm/agents/game-designer.md +0 -27
- package/src/modules/bmm/agents/game-dev.md +0 -28
- package/src/modules/bmm/agents/pm.md +0 -26
- package/src/modules/bmm/agents/po.md +0 -25
- package/src/modules/bmm/agents/sm.md +0 -29
- package/src/modules/bmm/agents/tea.md +0 -32
- package/src/modules/bmm/agents/ux-expert.md +0 -24
- package/src/modules/bmm/sub-modules/claude-code/config.yaml +0 -5
- package/src/modules/bmm/sub-modules/claude-code/injections.yaml +0 -242
- package/src/modules/bmm/sub-modules/claude-code/readme.md +0 -87
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/api-documenter.md +0 -85
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/codebase-analyzer.md +0 -64
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/data-analyst.md +0 -84
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/dependency-mapper.md +0 -67
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/document-reviewer.md +0 -85
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/epic-optimizer.md +0 -66
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/market-researcher.md +0 -34
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/pattern-detector.md +0 -67
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/requirements-analyst.md +0 -61
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/tech-debt-auditor.md +0 -89
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/technical-decisions-curator.md +0 -146
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/technical-evaluator.md +0 -51
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/test-coverage-analyzer.md +0 -91
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/trend-spotter.md +0 -99
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/user-journey-mapper.md +0 -101
- package/src/modules/bmm/sub-modules/claude-code/sub-agents/user-researcher.md +0 -56
- package/src/modules/bmm/tasks/daily-standup.md +0 -91
- package/src/modules/bmm/tasks/retrospective.md +0 -110
- package/src/modules/bmm/teams/team-all.yaml +0 -7
- package/src/modules/bmm/teams/team-dev.yaml +0 -14
- package/src/modules/bmm/teams/team-gamedev.yaml +0 -9
- package/src/modules/bmm/testarch/atdd.md +0 -40
- package/src/modules/bmm/testarch/automate.md +0 -38
- package/src/modules/bmm/testarch/ci.md +0 -39
- package/src/modules/bmm/testarch/framework.md +0 -41
- package/src/modules/bmm/testarch/nfr-assess.md +0 -38
- package/src/modules/bmm/testarch/risk-profile.md +0 -38
- package/src/modules/bmm/testarch/tea-commands.csv +0 -11
- package/src/modules/bmm/testarch/tea-gate.md +0 -38
- package/src/modules/bmm/testarch/tea-knowledge.md +0 -275
- package/src/modules/bmm/testarch/test-design.md +0 -39
- package/src/modules/bmm/testarch/test-levels-framework.md +0 -148
- package/src/modules/bmm/testarch/test-priorities-matrix.md +0 -174
- package/src/modules/bmm/testarch/trace-requirements.md +0 -38
- package/src/modules/bmm/workflows/1-analysis/brainstorm-game/instructions.md +0 -47
- package/src/modules/bmm/workflows/1-analysis/brainstorm-game/workflow.yaml +0 -22
- package/src/modules/bmm/workflows/1-analysis/game-brief/README.md +0 -221
- package/src/modules/bmm/workflows/1-analysis/game-brief/instructions.md +0 -517
- package/src/modules/bmm/workflows/1-analysis/game-brief/workflow.yaml +0 -34
- package/src/modules/bmm/workflows/1-analysis/product-brief/README.md +0 -180
- package/src/modules/bmm/workflows/1-analysis/research/README.md +0 -454
- package/src/modules/bmm/workflows/1-analysis/research/claude-code/sub-agents/bmm-competitor-analyzer.md +0 -259
- package/src/modules/bmm/workflows/1-analysis/research/claude-code/sub-agents/bmm-data-analyst.md +0 -190
- package/src/modules/bmm/workflows/1-analysis/research/claude-code/sub-agents/bmm-market-researcher.md +0 -337
- package/src/modules/bmm/workflows/1-analysis/research/claude-code/sub-agents/bmm-trend-spotter.md +0 -107
- package/src/modules/bmm/workflows/1-analysis/research/claude-code/sub-agents/bmm-user-researcher.md +0 -329
- package/src/modules/bmm/workflows/2-plan/README.md +0 -203
- package/src/modules/bmm/workflows/2-plan/checklist.md +0 -369
- package/src/modules/bmm/workflows/2-plan/gdd/README.md +0 -222
- package/src/modules/bmm/workflows/2-plan/gdd/instructions-gdd.md +0 -480
- package/src/modules/bmm/workflows/2-plan/instructions-router.md +0 -222
- package/src/modules/bmm/workflows/2-plan/prd/analysis-template.md +0 -53
- package/src/modules/bmm/workflows/2-plan/prd/epics-template.md +0 -18
- package/src/modules/bmm/workflows/2-plan/prd/instructions-lg.md +0 -267
- package/src/modules/bmm/workflows/2-plan/prd/instructions-med.md +0 -251
- package/src/modules/bmm/workflows/2-plan/prd/prd-template.md +0 -73
- package/src/modules/bmm/workflows/2-plan/tech-spec/instructions-sm.md +0 -137
- package/src/modules/bmm/workflows/2-plan/tech-spec/tech-spec-template.md +0 -59
- package/src/modules/bmm/workflows/2-plan/ux/instructions-ux.md +0 -360
- package/src/modules/bmm/workflows/2-plan/ux/ux-spec-template.md +0 -162
- package/src/modules/bmm/workflows/2-plan/workflow.yaml +0 -60
- package/src/modules/bmm/workflows/3-solutioning/ADR-template.md +0 -74
- package/src/modules/bmm/workflows/3-solutioning/README.md +0 -565
- package/src/modules/bmm/workflows/3-solutioning/checklist.md +0 -170
- package/src/modules/bmm/workflows/3-solutioning/instructions.md +0 -661
- package/src/modules/bmm/workflows/3-solutioning/project-types/backend-questions.md +0 -490
- package/src/modules/bmm/workflows/3-solutioning/project-types/cli-questions.md +0 -337
- package/src/modules/bmm/workflows/3-solutioning/project-types/data-questions.md +0 -472
- package/src/modules/bmm/workflows/3-solutioning/project-types/desktop-questions.md +0 -299
- package/src/modules/bmm/workflows/3-solutioning/project-types/embedded-questions.md +0 -118
- package/src/modules/bmm/workflows/3-solutioning/project-types/extension-questions.md +0 -374
- package/src/modules/bmm/workflows/3-solutioning/project-types/game-questions.md +0 -133
- package/src/modules/bmm/workflows/3-solutioning/project-types/infra-questions.md +0 -484
- package/src/modules/bmm/workflows/3-solutioning/project-types/library-questions.md +0 -146
- package/src/modules/bmm/workflows/3-solutioning/project-types/mobile-questions.md +0 -110
- package/src/modules/bmm/workflows/3-solutioning/project-types/project-types.csv +0 -12
- package/src/modules/bmm/workflows/3-solutioning/project-types/web-questions.md +0 -136
- package/src/modules/bmm/workflows/3-solutioning/tech-spec/README.md +0 -195
- package/src/modules/bmm/workflows/3-solutioning/tech-spec/instructions.md +0 -73
- package/src/modules/bmm/workflows/3-solutioning/tech-spec/workflow.yaml +0 -51
- package/src/modules/bmm/workflows/3-solutioning/templates/backend-service-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/cli-tool-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/data-pipeline-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/desktop-app-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/embedded-firmware-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/game-engine-architecture.md +0 -244
- package/src/modules/bmm/workflows/3-solutioning/templates/game-engine-godot-guide.md +0 -428
- package/src/modules/bmm/workflows/3-solutioning/templates/game-engine-unity-guide.md +0 -333
- package/src/modules/bmm/workflows/3-solutioning/templates/game-engine-web-guide.md +0 -528
- package/src/modules/bmm/workflows/3-solutioning/templates/infrastructure-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/library-package-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/mobile-app-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/registry.csv +0 -172
- package/src/modules/bmm/workflows/3-solutioning/templates/web-api-architecture.md +0 -66
- package/src/modules/bmm/workflows/3-solutioning/templates/web-fullstack-architecture.md +0 -277
- package/src/modules/bmm/workflows/3-solutioning/workflow.yaml +0 -65
- package/src/modules/bmm/workflows/4-implementation/create-story/README.md +0 -42
- package/src/modules/bmm/workflows/4-implementation/dev-story/README.md +0 -84
- package/src/modules/bmm/workflows/4-implementation/review-story/README.md +0 -72
- package/src/modules/bmm/workflows/4-implementation/review-story/instructions.md +0 -176
- package/src/modules/bmm/workflows/4-implementation/review-story/workflow.yaml +0 -99
- package/src/modules/bmm/workflows/4-implementation/story-context/README.md +0 -234
- package/src/modules/cis/agents/brainstorming-coach.md +0 -24
- package/src/modules/cis/agents/creative-problem-solver.md +0 -24
- package/src/modules/cis/agents/design-thinking-coach.md +0 -24
- package/src/modules/cis/agents/innovation-strategist.md +0 -24
- package/src/modules/cis/agents/storyteller.md +0 -24
- package/src/modules/cis/readme.md +0 -86
- package/tools/cli/installers/lib/ide/workflow-command-generator.js +0 -162
- package/tools/test-agents/captain-kirk-commander.md +0 -110
- package/tools/test-agents/data-operations-android.md +0 -123
- package/tools/test-agents/geordi-chief-engineer.md +0 -135
- package/tools/test-agents/isabella-martinez-ethicist.md +0 -109
- package/tools/test-agents/marcus-thompson-security.md +0 -109
- package/tools/test-agents/maya-patel-pragmatist.md +0 -82
- package/tools/test-agents/picard-diplomat-captain.md +0 -134
- package/tools/test-agents/spock-science-officer.md +0 -124
- package/tools/test-agents/william-smithers-technocrat.md +0 -71
- package/tools/test-agents/zara-chen-designer.md +0 -94
- /package/src/{modules/cis → core}/workflows/brainstorming/brain-methods.csv +0 -0
- /package/src/modules/{bmm/workflows/1-analysis → bmgd/workflows/1-preproduction}/brainstorm-game/game-brain-methods.csv +0 -0
- /package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/metroidvania.md +0 -0
- /package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types/puzzle.md +0 -0
- /package/src/modules/{bmm/workflows/2-plan → bmgd/workflows/2-design}/gdd/game-types.csv +0 -0
- /package/src/modules/{bmm/workflows/4-implementation/review-story → bmgd/workflows/4-production/code-review}/backlog_template.md +0 -0
- /package/src/modules/{bmm/workflows/4-implementation/review-story → bmgd/workflows/4-production/code-review}/checklist.md +0 -0
- /package/src/modules/bmm/_module-installer/assets/{technical-decisions-template.md → technical-decisions.md} +0 -0
|
@@ -16,6 +16,7 @@ const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/p
|
|
|
16
16
|
const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
|
|
17
17
|
const { CLIUtils } = require('../../../lib/cli-utils');
|
|
18
18
|
const { ManifestGenerator } = require('./manifest-generator');
|
|
19
|
+
const { IdeConfigManager } = require('./ide-config-manager');
|
|
19
20
|
|
|
20
21
|
class Installer {
|
|
21
22
|
constructor() {
|
|
@@ -28,72 +29,192 @@ class Installer {
|
|
|
28
29
|
this.xmlHandler = new XmlHandler();
|
|
29
30
|
this.dependencyResolver = new DependencyResolver();
|
|
30
31
|
this.configCollector = new ConfigCollector();
|
|
32
|
+
this.ideConfigManager = new IdeConfigManager();
|
|
31
33
|
this.installedFiles = []; // Track all installed files
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Find the bmad installation directory in a project
|
|
38
|
+
* V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
|
|
39
|
+
* @param {string} projectDir - Project directory
|
|
40
|
+
* @returns {Promise<string>} Path to bmad directory
|
|
41
|
+
*/
|
|
42
|
+
async findBmadDir(projectDir) {
|
|
43
|
+
// Check if project directory exists
|
|
44
|
+
if (!(await fs.pathExists(projectDir))) {
|
|
45
|
+
// Project doesn't exist yet, return default
|
|
46
|
+
return path.join(projectDir, 'bmad');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
|
|
50
|
+
// This is the definitive marker of a V6+ installation
|
|
51
|
+
try {
|
|
52
|
+
const entries = await fs.readdir(projectDir, { withFileTypes: true });
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
|
|
56
|
+
if (await fs.pathExists(manifestPath)) {
|
|
57
|
+
// Found a V6+ installation
|
|
58
|
+
return path.join(projectDir, entry.name);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore errors, fall through to default
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// No V6+ installation found, return default
|
|
67
|
+
// This will be used for new installations
|
|
68
|
+
return path.join(projectDir, 'bmad');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Copy a file and replace {bmad_folder} placeholder with actual folder name
|
|
73
|
+
* @param {string} sourcePath - Source file path
|
|
74
|
+
* @param {string} targetPath - Target file path
|
|
75
|
+
* @param {string} bmadFolderName - The bmad folder name to use for replacement
|
|
76
|
+
*/
|
|
77
|
+
async copyFileWithPlaceholderReplacement(sourcePath, targetPath, bmadFolderName) {
|
|
78
|
+
// List of text file extensions that should have placeholder replacement
|
|
79
|
+
const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv'];
|
|
80
|
+
const ext = path.extname(sourcePath).toLowerCase();
|
|
81
|
+
|
|
82
|
+
// Check if this is a text file that might contain placeholders
|
|
83
|
+
if (textExtensions.includes(ext)) {
|
|
84
|
+
try {
|
|
85
|
+
// Read the file content
|
|
86
|
+
let content = await fs.readFile(sourcePath, 'utf8');
|
|
87
|
+
|
|
88
|
+
// Replace {bmad_folder} placeholder with actual folder name
|
|
89
|
+
if (content.includes('{bmad_folder}')) {
|
|
90
|
+
content = content.replaceAll('{bmad_folder}', bmadFolderName);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Write to target with replaced content
|
|
94
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
95
|
+
await fs.writeFile(targetPath, content, 'utf8');
|
|
96
|
+
} catch {
|
|
97
|
+
// If reading as text fails (might be binary despite extension), fall back to regular copy
|
|
98
|
+
await fs.copy(sourcePath, targetPath, { overwrite: true });
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// Binary file or other file type - just copy directly
|
|
102
|
+
await fs.copy(sourcePath, targetPath, { overwrite: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
34
106
|
/**
|
|
35
107
|
* Collect Tool/IDE configurations after module configuration
|
|
36
108
|
* @param {string} projectDir - Project directory
|
|
37
109
|
* @param {Array} selectedModules - Selected modules from configuration
|
|
110
|
+
* @param {boolean} isFullReinstall - Whether this is a full reinstall
|
|
111
|
+
* @param {Array} previousIdes - Previously configured IDEs (for reinstalls)
|
|
112
|
+
* @param {Array} preSelectedIdes - Pre-selected IDEs from early prompt (optional)
|
|
38
113
|
* @returns {Object} Tool/IDE selection and configurations
|
|
39
114
|
*/
|
|
40
|
-
async collectToolConfigurations(projectDir, selectedModules) {
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
115
|
+
async collectToolConfigurations(projectDir, selectedModules, isFullReinstall = false, previousIdes = [], preSelectedIdes = null) {
|
|
116
|
+
// Use pre-selected IDEs if provided, otherwise prompt
|
|
117
|
+
let toolConfig;
|
|
118
|
+
if (preSelectedIdes === null) {
|
|
119
|
+
// Fallback: prompt for tool selection (backwards compatibility)
|
|
120
|
+
const { UI } = require('../../../lib/ui');
|
|
121
|
+
const ui = new UI();
|
|
122
|
+
toolConfig = await ui.promptToolSelection(projectDir, selectedModules);
|
|
123
|
+
} else {
|
|
124
|
+
// IDEs were already selected during initial prompts
|
|
125
|
+
toolConfig = {
|
|
126
|
+
ides: preSelectedIdes,
|
|
127
|
+
skipIde: !preSelectedIdes || preSelectedIdes.length === 0,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check for already configured IDEs
|
|
132
|
+
const { Detector } = require('./detector');
|
|
133
|
+
const detector = new Detector();
|
|
134
|
+
const bmadDir = path.join(projectDir, this.bmadFolderName || 'bmad');
|
|
135
|
+
|
|
136
|
+
// During full reinstall, use the saved previous IDEs since bmad dir was deleted
|
|
137
|
+
// Otherwise detect from existing installation
|
|
138
|
+
let previouslyConfiguredIdes;
|
|
139
|
+
if (isFullReinstall) {
|
|
140
|
+
// During reinstall, treat all IDEs as new (need configuration)
|
|
141
|
+
previouslyConfiguredIdes = [];
|
|
142
|
+
} else {
|
|
143
|
+
const existingInstall = await detector.detect(bmadDir);
|
|
144
|
+
previouslyConfiguredIdes = existingInstall.ides || [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Load saved IDE configurations for already-configured IDEs
|
|
148
|
+
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
|
45
149
|
|
|
46
150
|
// Collect IDE-specific configurations if any were selected
|
|
47
151
|
const ideConfigurations = {};
|
|
48
|
-
|
|
152
|
+
|
|
153
|
+
// First, add saved configs for already-configured IDEs
|
|
154
|
+
for (const ide of toolConfig.ides || []) {
|
|
155
|
+
if (previouslyConfiguredIdes.includes(ide) && savedIdeConfigs[ide]) {
|
|
156
|
+
ideConfigurations[ide] = savedIdeConfigs[ide];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
49
159
|
|
|
50
160
|
if (!toolConfig.skipIde && toolConfig.ides && toolConfig.ides.length > 0) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
161
|
+
// Determine which IDEs are newly selected (not previously configured)
|
|
162
|
+
const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
|
|
163
|
+
|
|
164
|
+
if (newlySelectedIdes.length > 0) {
|
|
165
|
+
console.log('\n'); // Add spacing before IDE questions
|
|
166
|
+
|
|
167
|
+
for (const ide of newlySelectedIdes) {
|
|
168
|
+
// List of IDEs that have interactive prompts
|
|
169
|
+
const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini'].includes(ide);
|
|
170
|
+
|
|
171
|
+
if (needsPrompts) {
|
|
172
|
+
// Get IDE handler and collect configuration
|
|
173
|
+
try {
|
|
174
|
+
// Dynamically load the IDE setup module
|
|
175
|
+
const ideModule = require(`../ide/${ide}`);
|
|
176
|
+
|
|
177
|
+
// Get the setup class (handle different export formats)
|
|
178
|
+
let SetupClass;
|
|
179
|
+
const className =
|
|
180
|
+
ide
|
|
181
|
+
.split('-')
|
|
182
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
183
|
+
.join('') + 'Setup';
|
|
184
|
+
|
|
185
|
+
if (ideModule[className]) {
|
|
186
|
+
SetupClass = ideModule[className];
|
|
187
|
+
} else if (ideModule.default) {
|
|
188
|
+
SetupClass = ideModule.default;
|
|
189
|
+
} else {
|
|
190
|
+
// Skip if no setup class found
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
79
193
|
|
|
80
|
-
|
|
194
|
+
const ideSetup = new SetupClass();
|
|
81
195
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
196
|
+
// Check if this IDE has a collectConfiguration method
|
|
197
|
+
if (typeof ideSetup.collectConfiguration === 'function') {
|
|
198
|
+
console.log(chalk.cyan(`\nConfiguring ${ide}...`));
|
|
199
|
+
ideConfigurations[ide] = await ideSetup.collectConfiguration({
|
|
200
|
+
selectedModules: selectedModules || [],
|
|
201
|
+
projectDir,
|
|
202
|
+
bmadDir,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
// IDE doesn't have a setup file or collectConfiguration method
|
|
207
|
+
console.warn(chalk.yellow(`Warning: Could not load configuration for ${ide}`));
|
|
90
208
|
}
|
|
91
|
-
} catch {
|
|
92
|
-
// IDE doesn't have a setup file or collectConfiguration method
|
|
93
|
-
console.warn(chalk.yellow(`Warning: Could not load configuration for ${ide}`));
|
|
94
209
|
}
|
|
95
210
|
}
|
|
96
211
|
}
|
|
212
|
+
|
|
213
|
+
// Log which IDEs are already configured and being kept
|
|
214
|
+
const keptIdes = toolConfig.ides.filter((ide) => previouslyConfiguredIdes.includes(ide));
|
|
215
|
+
if (keptIdes.length > 0) {
|
|
216
|
+
console.log(chalk.dim(`\nKeeping existing configuration for: ${keptIdes.join(', ')}`));
|
|
217
|
+
}
|
|
97
218
|
}
|
|
98
219
|
|
|
99
220
|
return {
|
|
@@ -119,13 +240,10 @@ class Installer {
|
|
|
119
240
|
// Display welcome message
|
|
120
241
|
CLIUtils.displaySection('BMAD™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
|
|
121
242
|
|
|
122
|
-
//
|
|
243
|
+
// Note: Legacy V4 detection now happens earlier in UI.promptInstall()
|
|
244
|
+
// before any config collection, so we don't need to check again here
|
|
245
|
+
|
|
123
246
|
const projectDir = path.resolve(config.directory);
|
|
124
|
-
const legacyV4 = await this.detector.detectLegacyV4(projectDir);
|
|
125
|
-
if (legacyV4.hasLegacyV4) {
|
|
126
|
-
const error = this.createLegacyV4Error(legacyV4);
|
|
127
|
-
throw error;
|
|
128
|
-
}
|
|
129
247
|
|
|
130
248
|
// If core config was pre-collected (from interactive mode), use it
|
|
131
249
|
if (config.coreConfig) {
|
|
@@ -137,15 +255,25 @@ class Installer {
|
|
|
137
255
|
}
|
|
138
256
|
}
|
|
139
257
|
|
|
140
|
-
// Collect configurations for modules (
|
|
141
|
-
|
|
258
|
+
// Collect configurations for modules (skip if quick update already collected them)
|
|
259
|
+
let moduleConfigs;
|
|
260
|
+
if (config._quickUpdate) {
|
|
261
|
+
// Quick update already collected all configs, use them directly
|
|
262
|
+
moduleConfigs = this.configCollector.collectedConfig;
|
|
263
|
+
} else {
|
|
264
|
+
// Regular install - collect configurations (core was already collected in UI.promptInstall if interactive)
|
|
265
|
+
moduleConfigs = await this.configCollector.collectAllConfigurations(config.modules || [], path.resolve(config.directory));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Get bmad_folder from config (default to 'bmad' for backwards compatibility)
|
|
269
|
+
const bmadFolderName = moduleConfigs.core && moduleConfigs.core.bmad_folder ? moduleConfigs.core.bmad_folder : 'bmad';
|
|
270
|
+
this.bmadFolderName = bmadFolderName; // Store for use in other methods
|
|
142
271
|
|
|
143
|
-
|
|
272
|
+
// Set bmad folder name on module manager and IDE manager for placeholder replacement
|
|
273
|
+
this.moduleManager.setBmadFolderName(bmadFolderName);
|
|
274
|
+
this.ideManager.setBmadFolderName(bmadFolderName);
|
|
144
275
|
|
|
145
|
-
//
|
|
146
|
-
config.ides = toolSelection.ides;
|
|
147
|
-
config.skipIde = toolSelection.skipIde;
|
|
148
|
-
const ideConfigurations = toolSelection.configurations;
|
|
276
|
+
// Tool selection will be collected after we determine if it's a reinstall/update/new install
|
|
149
277
|
|
|
150
278
|
const spinner = ora('Preparing installation...').start();
|
|
151
279
|
|
|
@@ -153,6 +281,63 @@ class Installer {
|
|
|
153
281
|
// Resolve target directory (path.resolve handles platform differences)
|
|
154
282
|
const projectDir = path.resolve(config.directory);
|
|
155
283
|
|
|
284
|
+
// Check if bmad_folder has changed from existing installation (only if project dir exists)
|
|
285
|
+
let existingBmadDir = null;
|
|
286
|
+
let existingBmadFolderName = null;
|
|
287
|
+
|
|
288
|
+
if (await fs.pathExists(projectDir)) {
|
|
289
|
+
existingBmadDir = await this.findBmadDir(projectDir);
|
|
290
|
+
existingBmadFolderName = path.basename(existingBmadDir);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const targetBmadDir = path.join(projectDir, bmadFolderName);
|
|
294
|
+
|
|
295
|
+
// If bmad_folder changed during update/upgrade, back up old folder and do fresh install
|
|
296
|
+
if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== bmadFolderName) {
|
|
297
|
+
spinner.stop();
|
|
298
|
+
console.log(chalk.yellow(`\n⚠️ bmad_folder has changed: ${existingBmadFolderName} → ${bmadFolderName}`));
|
|
299
|
+
console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
|
|
300
|
+
|
|
301
|
+
const inquirer = require('inquirer');
|
|
302
|
+
const { confirmFreshInstall } = await inquirer.prompt([
|
|
303
|
+
{
|
|
304
|
+
type: 'confirm',
|
|
305
|
+
name: 'confirmFreshInstall',
|
|
306
|
+
message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
|
|
307
|
+
default: true,
|
|
308
|
+
},
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
if (!confirmFreshInstall) {
|
|
312
|
+
console.log(chalk.yellow('Installation cancelled.'));
|
|
313
|
+
return { success: false, cancelled: true };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
spinner.start('Backing up existing installation...');
|
|
317
|
+
|
|
318
|
+
// Find a unique backup name
|
|
319
|
+
let backupDir = `${existingBmadDir}-bak`;
|
|
320
|
+
let counter = 1;
|
|
321
|
+
while (await fs.pathExists(backupDir)) {
|
|
322
|
+
backupDir = `${existingBmadDir}-bak-${counter}`;
|
|
323
|
+
counter++;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Rename the old folder to backup
|
|
327
|
+
await fs.move(existingBmadDir, backupDir);
|
|
328
|
+
|
|
329
|
+
spinner.succeed(`Backed up ${existingBmadFolderName} → ${path.basename(backupDir)}`);
|
|
330
|
+
console.log(chalk.cyan('\n📋 Important:'));
|
|
331
|
+
console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
|
|
332
|
+
console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
|
|
333
|
+
console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
|
|
334
|
+
console.log(chalk.dim(` - To the new location:`));
|
|
335
|
+
console.log(chalk.dim(` ${bmadFolderName}/_cfg/`));
|
|
336
|
+
console.log('');
|
|
337
|
+
|
|
338
|
+
spinner.start('Starting fresh installation...');
|
|
339
|
+
}
|
|
340
|
+
|
|
156
341
|
// Create a project directory if it doesn't exist (user already confirmed)
|
|
157
342
|
if (!(await fs.pathExists(projectDir))) {
|
|
158
343
|
spinner.text = 'Creating installation directory...';
|
|
@@ -173,27 +358,217 @@ class Installer {
|
|
|
173
358
|
}
|
|
174
359
|
}
|
|
175
360
|
|
|
176
|
-
const bmadDir = path.join(projectDir,
|
|
361
|
+
const bmadDir = path.join(projectDir, bmadFolderName);
|
|
177
362
|
|
|
178
363
|
// Check existing installation
|
|
179
364
|
spinner.text = 'Checking for existing installation...';
|
|
180
365
|
const existingInstall = await this.detector.detect(bmadDir);
|
|
181
366
|
|
|
182
|
-
if (existingInstall.installed && !config.force) {
|
|
367
|
+
if (existingInstall.installed && !config.force && !config._quickUpdate) {
|
|
183
368
|
spinner.stop();
|
|
184
369
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
370
|
+
// Check if user already decided what to do (from early menu in ui.js)
|
|
371
|
+
let action = null;
|
|
372
|
+
if (config._requestedReinstall) {
|
|
373
|
+
action = 'reinstall';
|
|
374
|
+
} else if (config.actionType === 'update') {
|
|
375
|
+
action = 'update';
|
|
376
|
+
} else {
|
|
377
|
+
// Fallback: Ask the user (backwards compatibility for other code paths)
|
|
378
|
+
console.log(chalk.yellow('\n⚠️ Existing BMAD installation detected'));
|
|
379
|
+
console.log(chalk.dim(` Location: ${bmadDir}`));
|
|
380
|
+
console.log(chalk.dim(` Version: ${existingInstall.version}`));
|
|
381
|
+
|
|
382
|
+
const promptResult = await this.promptUpdateAction();
|
|
383
|
+
action = promptResult.action;
|
|
384
|
+
}
|
|
188
385
|
|
|
189
|
-
// TODO: Handle update scenario
|
|
190
|
-
const { action } = await this.promptUpdateAction();
|
|
191
386
|
if (action === 'cancel') {
|
|
192
387
|
console.log('Installation cancelled.');
|
|
193
|
-
return;
|
|
388
|
+
return { success: false, cancelled: true };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (action === 'reinstall') {
|
|
392
|
+
// Warn about destructive operation
|
|
393
|
+
console.log(chalk.red.bold('\n⚠️ WARNING: This is a destructive operation!'));
|
|
394
|
+
console.log(chalk.red('All custom files and modifications in the bmad directory will be lost.'));
|
|
395
|
+
|
|
396
|
+
const inquirer = require('inquirer');
|
|
397
|
+
const { confirmReinstall } = await inquirer.prompt([
|
|
398
|
+
{
|
|
399
|
+
type: 'confirm',
|
|
400
|
+
name: 'confirmReinstall',
|
|
401
|
+
message: chalk.yellow('Are you sure you want to delete and reinstall?'),
|
|
402
|
+
default: false,
|
|
403
|
+
},
|
|
404
|
+
]);
|
|
405
|
+
|
|
406
|
+
if (!confirmReinstall) {
|
|
407
|
+
console.log('Installation cancelled.');
|
|
408
|
+
return { success: false, cancelled: true };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Remember previously configured IDEs before deleting
|
|
412
|
+
config._previouslyConfiguredIdes = existingInstall.ides || [];
|
|
413
|
+
|
|
414
|
+
// Remove existing installation
|
|
415
|
+
await fs.remove(bmadDir);
|
|
416
|
+
console.log(chalk.green('✓ Removed existing installation\n'));
|
|
417
|
+
|
|
418
|
+
// Mark this as a full reinstall so we re-collect IDE configurations
|
|
419
|
+
config._isFullReinstall = true;
|
|
420
|
+
} else if (action === 'update') {
|
|
421
|
+
// Store that we're updating for later processing
|
|
422
|
+
config._isUpdate = true;
|
|
423
|
+
config._existingInstall = existingInstall;
|
|
424
|
+
|
|
425
|
+
// Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
|
|
426
|
+
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
|
427
|
+
console.log(chalk.dim(`DEBUG: Read ${existingFilesManifest.length} files from manifest`));
|
|
428
|
+
console.log(chalk.dim(`DEBUG: Manifest has hashes: ${existingFilesManifest.some((f) => f.hash)}`));
|
|
429
|
+
|
|
430
|
+
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
|
431
|
+
|
|
432
|
+
console.log(chalk.dim(`DEBUG: Found ${customFiles.length} custom files, ${modifiedFiles.length} modified files`));
|
|
433
|
+
if (modifiedFiles.length > 0) {
|
|
434
|
+
console.log(chalk.yellow('DEBUG: Modified files:'));
|
|
435
|
+
for (const f of modifiedFiles) console.log(chalk.dim(` - ${f.path}`));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
config._customFiles = customFiles;
|
|
439
|
+
config._modifiedFiles = modifiedFiles;
|
|
440
|
+
|
|
441
|
+
// If there are custom files, back them up temporarily
|
|
442
|
+
if (customFiles.length > 0) {
|
|
443
|
+
const tempBackupDir = path.join(projectDir, '.bmad-custom-backup-temp');
|
|
444
|
+
await fs.ensureDir(tempBackupDir);
|
|
445
|
+
|
|
446
|
+
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
|
447
|
+
for (const customFile of customFiles) {
|
|
448
|
+
const relativePath = path.relative(bmadDir, customFile);
|
|
449
|
+
const backupPath = path.join(tempBackupDir, relativePath);
|
|
450
|
+
await fs.ensureDir(path.dirname(backupPath));
|
|
451
|
+
await fs.copy(customFile, backupPath);
|
|
452
|
+
}
|
|
453
|
+
spinner.succeed(`Backed up ${customFiles.length} custom files`);
|
|
454
|
+
|
|
455
|
+
config._tempBackupDir = tempBackupDir;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// For modified files, back them up to temp directory (will be restored as .bak files after install)
|
|
459
|
+
if (modifiedFiles.length > 0) {
|
|
460
|
+
const tempModifiedBackupDir = path.join(projectDir, '.bmad-modified-backup-temp');
|
|
461
|
+
await fs.ensureDir(tempModifiedBackupDir);
|
|
462
|
+
|
|
463
|
+
console.log(chalk.yellow(`\nDEBUG: Backing up ${modifiedFiles.length} modified files to temp location`));
|
|
464
|
+
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
|
465
|
+
for (const modifiedFile of modifiedFiles) {
|
|
466
|
+
const relativePath = path.relative(bmadDir, modifiedFile.path);
|
|
467
|
+
const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
|
|
468
|
+
console.log(chalk.dim(`DEBUG: Backing up ${relativePath} to temp`));
|
|
469
|
+
await fs.ensureDir(path.dirname(tempBackupPath));
|
|
470
|
+
await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
|
|
471
|
+
}
|
|
472
|
+
spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
|
|
473
|
+
|
|
474
|
+
config._tempModifiedBackupDir = tempModifiedBackupDir;
|
|
475
|
+
} else {
|
|
476
|
+
console.log(chalk.dim('DEBUG: No modified files detected'));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} else if (existingInstall.installed && config._quickUpdate) {
|
|
480
|
+
// Quick update mode - automatically treat as update without prompting
|
|
481
|
+
spinner.text = 'Preparing quick update...';
|
|
482
|
+
config._isUpdate = true;
|
|
483
|
+
config._existingInstall = existingInstall;
|
|
484
|
+
|
|
485
|
+
// Detect custom and modified files BEFORE updating
|
|
486
|
+
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
|
487
|
+
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
|
488
|
+
|
|
489
|
+
config._customFiles = customFiles;
|
|
490
|
+
config._modifiedFiles = modifiedFiles;
|
|
491
|
+
|
|
492
|
+
// Back up custom files
|
|
493
|
+
if (customFiles.length > 0) {
|
|
494
|
+
const tempBackupDir = path.join(projectDir, '.bmad-custom-backup-temp');
|
|
495
|
+
await fs.ensureDir(tempBackupDir);
|
|
496
|
+
|
|
497
|
+
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
|
498
|
+
for (const customFile of customFiles) {
|
|
499
|
+
const relativePath = path.relative(bmadDir, customFile);
|
|
500
|
+
const backupPath = path.join(tempBackupDir, relativePath);
|
|
501
|
+
await fs.ensureDir(path.dirname(backupPath));
|
|
502
|
+
await fs.copy(customFile, backupPath);
|
|
503
|
+
}
|
|
504
|
+
spinner.succeed(`Backed up ${customFiles.length} custom files`);
|
|
505
|
+
config._tempBackupDir = tempBackupDir;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Back up modified files
|
|
509
|
+
if (modifiedFiles.length > 0) {
|
|
510
|
+
const tempModifiedBackupDir = path.join(projectDir, '.bmad-modified-backup-temp');
|
|
511
|
+
await fs.ensureDir(tempModifiedBackupDir);
|
|
512
|
+
|
|
513
|
+
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
|
514
|
+
for (const modifiedFile of modifiedFiles) {
|
|
515
|
+
const relativePath = path.relative(bmadDir, modifiedFile.path);
|
|
516
|
+
const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
|
|
517
|
+
await fs.ensureDir(path.dirname(tempBackupPath));
|
|
518
|
+
await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
|
|
519
|
+
}
|
|
520
|
+
spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
|
|
521
|
+
config._tempModifiedBackupDir = tempModifiedBackupDir;
|
|
194
522
|
}
|
|
195
523
|
}
|
|
196
524
|
|
|
525
|
+
// Now collect tool configurations after we know if it's a reinstall
|
|
526
|
+
// Skip for quick update since we already have the IDE list
|
|
527
|
+
spinner.stop();
|
|
528
|
+
let toolSelection;
|
|
529
|
+
if (config._quickUpdate) {
|
|
530
|
+
// Quick update already has IDEs configured, use saved configurations
|
|
531
|
+
const preConfiguredIdes = {};
|
|
532
|
+
const savedIdeConfigs = config._savedIdeConfigs || {};
|
|
533
|
+
|
|
534
|
+
for (const ide of config.ides || []) {
|
|
535
|
+
// Use saved config if available, otherwise mark as already configured (legacy)
|
|
536
|
+
if (savedIdeConfigs[ide]) {
|
|
537
|
+
preConfiguredIdes[ide] = savedIdeConfigs[ide];
|
|
538
|
+
} else {
|
|
539
|
+
preConfiguredIdes[ide] = { _alreadyConfigured: true };
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
toolSelection = {
|
|
543
|
+
ides: config.ides || [],
|
|
544
|
+
skipIde: !config.ides || config.ides.length === 0,
|
|
545
|
+
configurations: preConfiguredIdes,
|
|
546
|
+
};
|
|
547
|
+
} else {
|
|
548
|
+
// Pass pre-selected IDEs from early prompt (if available)
|
|
549
|
+
// This allows IDE selection to happen before file copying, improving UX
|
|
550
|
+
const preSelectedIdes = config.ides && config.ides.length > 0 ? config.ides : null;
|
|
551
|
+
toolSelection = await this.collectToolConfigurations(
|
|
552
|
+
path.resolve(config.directory),
|
|
553
|
+
config.modules,
|
|
554
|
+
config._isFullReinstall || false,
|
|
555
|
+
config._previouslyConfiguredIdes || [],
|
|
556
|
+
preSelectedIdes,
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Merge tool selection into config (for both quick update and regular flow)
|
|
561
|
+
config.ides = toolSelection.ides;
|
|
562
|
+
config.skipIde = toolSelection.skipIde;
|
|
563
|
+
const ideConfigurations = toolSelection.configurations;
|
|
564
|
+
|
|
565
|
+
// Check if spinner is already running (e.g., from folder name change scenario)
|
|
566
|
+
if (spinner.isSpinning) {
|
|
567
|
+
spinner.text = 'Continuing installation...';
|
|
568
|
+
} else {
|
|
569
|
+
spinner.start('Continuing installation...');
|
|
570
|
+
}
|
|
571
|
+
|
|
197
572
|
// Create bmad directory structure
|
|
198
573
|
spinner.text = 'Creating directory structure...';
|
|
199
574
|
await this.createDirectoryStructure(bmadDir);
|
|
@@ -230,7 +605,13 @@ class Installer {
|
|
|
230
605
|
// Install partial modules (only dependencies)
|
|
231
606
|
for (const [module, files] of Object.entries(resolution.byModule)) {
|
|
232
607
|
if (!config.modules.includes(module) && module !== 'core') {
|
|
233
|
-
const totalFiles =
|
|
608
|
+
const totalFiles =
|
|
609
|
+
files.agents.length +
|
|
610
|
+
files.tasks.length +
|
|
611
|
+
files.tools.length +
|
|
612
|
+
files.templates.length +
|
|
613
|
+
files.data.length +
|
|
614
|
+
files.other.length;
|
|
234
615
|
if (totalFiles > 0) {
|
|
235
616
|
spinner.start(`Installing ${module} dependencies...`);
|
|
236
617
|
await this.installPartialModule(module, bmadDir, files);
|
|
@@ -246,57 +627,102 @@ class Installer {
|
|
|
246
627
|
spinner.succeed('Module configurations generated');
|
|
247
628
|
|
|
248
629
|
// Create agent configuration files
|
|
249
|
-
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
630
|
+
// Note: Legacy createAgentConfigs removed - using YAML customize system instead
|
|
631
|
+
// Customize templates are now created in processAgentFiles when building YAML agents
|
|
632
|
+
|
|
633
|
+
// Pre-register manifest files that will be created (except files-manifest.csv to avoid recursion)
|
|
634
|
+
const cfgDir = path.join(bmadDir, '_cfg');
|
|
635
|
+
this.installedFiles.push(
|
|
636
|
+
path.join(cfgDir, 'manifest.yaml'),
|
|
637
|
+
path.join(cfgDir, 'workflow-manifest.csv'),
|
|
638
|
+
path.join(cfgDir, 'agent-manifest.csv'),
|
|
639
|
+
path.join(cfgDir, 'task-manifest.csv'),
|
|
640
|
+
);
|
|
261
641
|
|
|
262
|
-
// Generate CSV manifests for workflows, agents,
|
|
642
|
+
// Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes BEFORE IDE setup
|
|
263
643
|
spinner.start('Generating workflow and agent manifests...');
|
|
264
644
|
const manifestGen = new ManifestGenerator();
|
|
265
|
-
|
|
645
|
+
|
|
646
|
+
// Include preserved modules (from quick update) in the manifest
|
|
647
|
+
const allModulesToList = config._preserveModules ? [...(config.modules || []), ...config._preserveModules] : config.modules || [];
|
|
648
|
+
|
|
649
|
+
const manifestStats = await manifestGen.generateManifests(bmadDir, config.modules || [], this.installedFiles, {
|
|
650
|
+
ides: config.ides || [],
|
|
651
|
+
preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir
|
|
652
|
+
});
|
|
653
|
+
|
|
266
654
|
spinner.succeed(
|
|
267
|
-
`Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks`,
|
|
655
|
+
`Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
|
|
268
656
|
);
|
|
269
657
|
|
|
270
658
|
// Configure IDEs and copy documentation
|
|
271
659
|
if (!config.skipIde && config.ides && config.ides.length > 0) {
|
|
272
|
-
|
|
660
|
+
// Filter out any undefined/null values from the IDE list
|
|
661
|
+
const validIdes = config.ides.filter((ide) => ide && typeof ide === 'string');
|
|
273
662
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
663
|
+
if (validIdes.length === 0) {
|
|
664
|
+
console.log(chalk.yellow('⚠️ No valid IDEs selected. Skipping IDE configuration.'));
|
|
665
|
+
} else {
|
|
666
|
+
// Check if any IDE might need prompting (no pre-collected config)
|
|
667
|
+
const needsPrompting = validIdes.some((ide) => !ideConfigurations[ide]);
|
|
279
668
|
|
|
280
|
-
|
|
281
|
-
|
|
669
|
+
if (!needsPrompting) {
|
|
670
|
+
spinner.start('Configuring IDEs...');
|
|
671
|
+
}
|
|
282
672
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
673
|
+
// Temporarily suppress console output if not verbose
|
|
674
|
+
const originalLog = console.log;
|
|
675
|
+
if (!config.verbose) {
|
|
676
|
+
console.log = () => {};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
for (const ide of validIdes) {
|
|
680
|
+
// Only show spinner if we have pre-collected config (no prompts expected)
|
|
681
|
+
if (ideConfigurations[ide] && !needsPrompting) {
|
|
682
|
+
spinner.text = `Configuring ${ide}...`;
|
|
683
|
+
} else if (!ideConfigurations[ide]) {
|
|
684
|
+
// Stop spinner before prompting
|
|
685
|
+
if (spinner.isSpinning) {
|
|
686
|
+
spinner.stop();
|
|
687
|
+
}
|
|
688
|
+
console.log(chalk.cyan(`\nConfiguring ${ide}...`));
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Pass pre-collected configuration to avoid re-prompting
|
|
692
|
+
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
693
|
+
selectedModules: config.modules || [],
|
|
694
|
+
preCollectedConfig: ideConfigurations[ide] || null,
|
|
695
|
+
verbose: config.verbose,
|
|
696
|
+
});
|
|
290
697
|
|
|
291
|
-
|
|
292
|
-
|
|
698
|
+
// Save IDE configuration for future updates
|
|
699
|
+
if (ideConfigurations[ide] && !ideConfigurations[ide]._alreadyConfigured) {
|
|
700
|
+
await this.ideConfigManager.saveIdeConfig(bmadDir, ide, ideConfigurations[ide]);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Restart spinner if we stopped it
|
|
704
|
+
if (!ideConfigurations[ide] && !spinner.isSpinning) {
|
|
705
|
+
spinner.start('Configuring IDEs...');
|
|
706
|
+
}
|
|
707
|
+
}
|
|
293
708
|
|
|
294
|
-
|
|
709
|
+
// Restore console.log
|
|
710
|
+
console.log = originalLog;
|
|
711
|
+
|
|
712
|
+
if (spinner.isSpinning) {
|
|
713
|
+
spinner.succeed(`Configured ${validIdes.length} IDE${validIdes.length > 1 ? 's' : ''}`);
|
|
714
|
+
} else {
|
|
715
|
+
console.log(chalk.green(`✓ Configured ${validIdes.length} IDE${validIdes.length > 1 ? 's' : ''}`));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
295
718
|
|
|
296
|
-
// Copy IDE-specific documentation
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
719
|
+
// Copy IDE-specific documentation (only for valid IDEs)
|
|
720
|
+
const validIdesForDocs = (config.ides || []).filter((ide) => ide && typeof ide === 'string');
|
|
721
|
+
if (validIdesForDocs.length > 0) {
|
|
722
|
+
spinner.start('Copying IDE documentation...');
|
|
723
|
+
await this.copyIdeDocumentation(validIdesForDocs, bmadDir);
|
|
724
|
+
spinner.succeed('IDE documentation copied');
|
|
725
|
+
}
|
|
300
726
|
}
|
|
301
727
|
|
|
302
728
|
// Run module-specific installers after IDE setup
|
|
@@ -337,24 +763,83 @@ class Installer {
|
|
|
337
763
|
|
|
338
764
|
spinner.succeed('Module-specific installers completed');
|
|
339
765
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
766
|
+
// Note: Manifest files are already created by ManifestGenerator above
|
|
767
|
+
// No need to create legacy manifest.csv anymore
|
|
768
|
+
|
|
769
|
+
// If this was an update, restore custom files
|
|
770
|
+
let customFiles = [];
|
|
771
|
+
let modifiedFiles = [];
|
|
772
|
+
if (config._isUpdate) {
|
|
773
|
+
if (config._customFiles && config._customFiles.length > 0) {
|
|
774
|
+
spinner.start(`Restoring ${config._customFiles.length} custom files...`);
|
|
775
|
+
|
|
776
|
+
for (const originalPath of config._customFiles) {
|
|
777
|
+
const relativePath = path.relative(bmadDir, originalPath);
|
|
778
|
+
const backupPath = path.join(config._tempBackupDir, relativePath);
|
|
779
|
+
|
|
780
|
+
if (await fs.pathExists(backupPath)) {
|
|
781
|
+
await fs.ensureDir(path.dirname(originalPath));
|
|
782
|
+
await fs.copy(backupPath, originalPath, { overwrite: true });
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Clean up temp backup
|
|
787
|
+
if (config._tempBackupDir && (await fs.pathExists(config._tempBackupDir))) {
|
|
788
|
+
await fs.remove(config._tempBackupDir);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
spinner.succeed(`Restored ${config._customFiles.length} custom files`);
|
|
792
|
+
customFiles = config._customFiles;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (config._modifiedFiles && config._modifiedFiles.length > 0) {
|
|
796
|
+
modifiedFiles = config._modifiedFiles;
|
|
797
|
+
|
|
798
|
+
// Restore modified files as .bak files
|
|
799
|
+
if (config._tempModifiedBackupDir && (await fs.pathExists(config._tempModifiedBackupDir))) {
|
|
800
|
+
spinner.start(`Restoring ${modifiedFiles.length} modified files as .bak...`);
|
|
801
|
+
|
|
802
|
+
for (const modifiedFile of modifiedFiles) {
|
|
803
|
+
const relativePath = path.relative(bmadDir, modifiedFile.path);
|
|
804
|
+
const tempBackupPath = path.join(config._tempModifiedBackupDir, relativePath);
|
|
805
|
+
const bakPath = modifiedFile.path + '.bak';
|
|
806
|
+
|
|
807
|
+
if (await fs.pathExists(tempBackupPath)) {
|
|
808
|
+
await fs.ensureDir(path.dirname(bakPath));
|
|
809
|
+
await fs.copy(tempBackupPath, bakPath, { overwrite: true });
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Clean up temp backup
|
|
814
|
+
await fs.remove(config._tempModifiedBackupDir);
|
|
815
|
+
|
|
816
|
+
spinner.succeed(`Restored ${modifiedFiles.length} modified files as .bak`);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
355
820
|
|
|
356
821
|
spinner.stop();
|
|
357
822
|
|
|
823
|
+
// Report custom and modified files if any were found
|
|
824
|
+
if (customFiles.length > 0) {
|
|
825
|
+
console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`));
|
|
826
|
+
console.log(chalk.dim('The following custom files were found and restored:\n'));
|
|
827
|
+
for (const file of customFiles) {
|
|
828
|
+
console.log(chalk.dim(` - ${path.relative(bmadDir, file)}`));
|
|
829
|
+
}
|
|
830
|
+
console.log('');
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
if (modifiedFiles.length > 0) {
|
|
834
|
+
console.log(chalk.yellow(`\n⚠️ Modified files detected: ${modifiedFiles.length}`));
|
|
835
|
+
console.log(chalk.dim('The following files were modified and backed up with .bak extension:\n'));
|
|
836
|
+
for (const file of modifiedFiles) {
|
|
837
|
+
console.log(chalk.dim(` - ${file.relativePath} → ${file.relativePath}.bak`));
|
|
838
|
+
}
|
|
839
|
+
console.log(chalk.dim('\nThese files have been updated with the new version.'));
|
|
840
|
+
console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n'));
|
|
841
|
+
}
|
|
842
|
+
|
|
358
843
|
// Display completion message
|
|
359
844
|
const { UI } = require('../../../lib/ui');
|
|
360
845
|
const ui = new UI();
|
|
@@ -362,6 +847,7 @@ class Installer {
|
|
|
362
847
|
path: bmadDir,
|
|
363
848
|
modules: config.modules,
|
|
364
849
|
ides: config.ides,
|
|
850
|
+
customFiles: customFiles.length > 0 ? customFiles : undefined,
|
|
365
851
|
});
|
|
366
852
|
|
|
367
853
|
return { success: true, path: bmadDir, modules: config.modules, ides: config.ides };
|
|
@@ -378,7 +864,8 @@ class Installer {
|
|
|
378
864
|
const spinner = ora('Checking installation...').start();
|
|
379
865
|
|
|
380
866
|
try {
|
|
381
|
-
const
|
|
867
|
+
const projectDir = path.resolve(config.directory);
|
|
868
|
+
const bmadDir = await this.findBmadDir(projectDir);
|
|
382
869
|
const existingInstall = await this.detector.detect(bmadDir);
|
|
383
870
|
|
|
384
871
|
if (!existingInstall.installed) {
|
|
@@ -438,7 +925,8 @@ class Installer {
|
|
|
438
925
|
* Get installation status
|
|
439
926
|
*/
|
|
440
927
|
async getStatus(directory) {
|
|
441
|
-
const
|
|
928
|
+
const projectDir = path.resolve(directory);
|
|
929
|
+
const bmadDir = await this.findBmadDir(projectDir);
|
|
442
930
|
return await this.detector.detect(bmadDir);
|
|
443
931
|
}
|
|
444
932
|
|
|
@@ -453,14 +941,15 @@ class Installer {
|
|
|
453
941
|
* Uninstall BMAD
|
|
454
942
|
*/
|
|
455
943
|
async uninstall(directory) {
|
|
456
|
-
const
|
|
944
|
+
const projectDir = path.resolve(directory);
|
|
945
|
+
const bmadDir = await this.findBmadDir(projectDir);
|
|
457
946
|
|
|
458
947
|
if (await fs.pathExists(bmadDir)) {
|
|
459
948
|
await fs.remove(bmadDir);
|
|
460
949
|
}
|
|
461
950
|
|
|
462
951
|
// Clean up IDE configurations
|
|
463
|
-
await this.ideManager.cleanup(
|
|
952
|
+
await this.ideManager.cleanup(projectDir);
|
|
464
953
|
|
|
465
954
|
return { success: true };
|
|
466
955
|
}
|
|
@@ -558,8 +1047,12 @@ class Installer {
|
|
|
558
1047
|
}
|
|
559
1048
|
}
|
|
560
1049
|
|
|
561
|
-
// Write the clean config file
|
|
562
|
-
|
|
1050
|
+
// Write the clean config file with POSIX-compliant final newline
|
|
1051
|
+
const content = header + yamlContent;
|
|
1052
|
+
await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
|
|
1053
|
+
|
|
1054
|
+
// Track the config file in installedFiles
|
|
1055
|
+
this.installedFiles.push(configPath);
|
|
563
1056
|
}
|
|
564
1057
|
}
|
|
565
1058
|
}
|
|
@@ -589,6 +1082,9 @@ class Installer {
|
|
|
589
1082
|
* @param {Object} moduleFiles - Module files to install
|
|
590
1083
|
*/
|
|
591
1084
|
async installModuleWithDependencies(moduleName, bmadDir, moduleFiles) {
|
|
1085
|
+
// Get module configuration for conditional installation
|
|
1086
|
+
const moduleConfig = this.configCollector.collectedConfig[moduleName] || {};
|
|
1087
|
+
|
|
592
1088
|
// Use existing module manager for full installation with file tracking
|
|
593
1089
|
// Note: Module-specific installers are called separately after IDE setup
|
|
594
1090
|
await this.moduleManager.install(
|
|
@@ -599,9 +1095,14 @@ class Installer {
|
|
|
599
1095
|
},
|
|
600
1096
|
{
|
|
601
1097
|
skipModuleInstaller: true, // We'll run it later after IDE setup
|
|
1098
|
+
moduleConfig: moduleConfig, // Pass module config for conditional filtering
|
|
602
1099
|
},
|
|
603
1100
|
);
|
|
604
1101
|
|
|
1102
|
+
// Process agent files to build YAML agents and create customize templates
|
|
1103
|
+
const modulePath = path.join(bmadDir, moduleName);
|
|
1104
|
+
await this.processAgentFiles(modulePath, moduleName);
|
|
1105
|
+
|
|
605
1106
|
// Dependencies are already included in full module install
|
|
606
1107
|
}
|
|
607
1108
|
|
|
@@ -626,7 +1127,7 @@ class Installer {
|
|
|
626
1127
|
const targetPath = path.join(agentsDir, fileName);
|
|
627
1128
|
|
|
628
1129
|
if (await fs.pathExists(sourcePath)) {
|
|
629
|
-
await
|
|
1130
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
630
1131
|
this.installedFiles.push(targetPath);
|
|
631
1132
|
}
|
|
632
1133
|
}
|
|
@@ -642,7 +1143,23 @@ class Installer {
|
|
|
642
1143
|
const targetPath = path.join(tasksDir, fileName);
|
|
643
1144
|
|
|
644
1145
|
if (await fs.pathExists(sourcePath)) {
|
|
645
|
-
await
|
|
1146
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1147
|
+
this.installedFiles.push(targetPath);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (files.tools && files.tools.length > 0) {
|
|
1153
|
+
const toolsDir = path.join(targetBase, 'tools');
|
|
1154
|
+
await fs.ensureDir(toolsDir);
|
|
1155
|
+
|
|
1156
|
+
for (const toolPath of files.tools) {
|
|
1157
|
+
const fileName = path.basename(toolPath);
|
|
1158
|
+
const sourcePath = path.join(sourceBase, 'tools', fileName);
|
|
1159
|
+
const targetPath = path.join(toolsDir, fileName);
|
|
1160
|
+
|
|
1161
|
+
if (await fs.pathExists(sourcePath)) {
|
|
1162
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
646
1163
|
this.installedFiles.push(targetPath);
|
|
647
1164
|
}
|
|
648
1165
|
}
|
|
@@ -658,7 +1175,7 @@ class Installer {
|
|
|
658
1175
|
const targetPath = path.join(templatesDir, fileName);
|
|
659
1176
|
|
|
660
1177
|
if (await fs.pathExists(sourcePath)) {
|
|
661
|
-
await
|
|
1178
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
662
1179
|
this.installedFiles.push(targetPath);
|
|
663
1180
|
}
|
|
664
1181
|
}
|
|
@@ -673,7 +1190,7 @@ class Installer {
|
|
|
673
1190
|
await fs.ensureDir(path.dirname(targetPath));
|
|
674
1191
|
|
|
675
1192
|
if (await fs.pathExists(dataPath)) {
|
|
676
|
-
await
|
|
1193
|
+
await this.copyFileWithPlaceholderReplacement(dataPath, targetPath, this.bmadFolderName || 'bmad');
|
|
677
1194
|
this.installedFiles.push(targetPath);
|
|
678
1195
|
}
|
|
679
1196
|
}
|
|
@@ -733,9 +1250,8 @@ class Installer {
|
|
|
733
1250
|
}
|
|
734
1251
|
}
|
|
735
1252
|
|
|
736
|
-
// Copy the file
|
|
737
|
-
await
|
|
738
|
-
await fs.copy(sourceFile, targetFile, { overwrite: true });
|
|
1253
|
+
// Copy the file with placeholder replacement
|
|
1254
|
+
await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile, this.bmadFolderName || 'bmad');
|
|
739
1255
|
|
|
740
1256
|
// Track the installed file
|
|
741
1257
|
this.installedFiles.push(targetFile);
|
|
@@ -771,8 +1287,8 @@ class Installer {
|
|
|
771
1287
|
}
|
|
772
1288
|
|
|
773
1289
|
/**
|
|
774
|
-
* Process agent files to inject activation
|
|
775
|
-
* @param {string} modulePath - Path to module
|
|
1290
|
+
* Process agent files to build YAML agents and inject activation blocks
|
|
1291
|
+
* @param {string} modulePath - Path to module in bmad/ installation
|
|
776
1292
|
* @param {string} moduleName - Module name
|
|
777
1293
|
*/
|
|
778
1294
|
async processAgentFiles(modulePath, moduleName) {
|
|
@@ -783,24 +1299,375 @@ class Installer {
|
|
|
783
1299
|
return; // No agents to process
|
|
784
1300
|
}
|
|
785
1301
|
|
|
1302
|
+
// Determine project directory (parent of bmad/ directory)
|
|
1303
|
+
const bmadDir = path.dirname(modulePath);
|
|
1304
|
+
const projectDir = path.dirname(bmadDir);
|
|
1305
|
+
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
|
|
1306
|
+
|
|
1307
|
+
// Ensure _cfg/agents directory exists
|
|
1308
|
+
await fs.ensureDir(cfgAgentsDir);
|
|
1309
|
+
|
|
786
1310
|
// Get all agent files
|
|
787
1311
|
const agentFiles = await fs.readdir(agentsPath);
|
|
788
1312
|
|
|
789
1313
|
for (const agentFile of agentFiles) {
|
|
790
|
-
|
|
1314
|
+
// Handle YAML agents - build them to .md
|
|
1315
|
+
if (agentFile.endsWith('.agent.yaml')) {
|
|
1316
|
+
const agentName = agentFile.replace('.agent.yaml', '');
|
|
1317
|
+
const yamlPath = path.join(agentsPath, agentFile);
|
|
1318
|
+
const mdPath = path.join(agentsPath, `${agentName}.md`);
|
|
1319
|
+
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1320
|
+
|
|
1321
|
+
// Create customize template if it doesn't exist
|
|
1322
|
+
if (!(await fs.pathExists(customizePath))) {
|
|
1323
|
+
const genericTemplatePath = getSourcePath('utility', 'templates', 'agent.customize.template.yaml');
|
|
1324
|
+
if (await fs.pathExists(genericTemplatePath)) {
|
|
1325
|
+
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
|
|
1326
|
+
console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// Build YAML + customize to .md
|
|
1331
|
+
const customizeExists = await fs.pathExists(customizePath);
|
|
1332
|
+
const xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
|
|
1333
|
+
includeMetadata: true,
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1337
|
+
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
791
1338
|
|
|
792
|
-
|
|
793
|
-
|
|
1339
|
+
// Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
|
|
1340
|
+
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
|
1341
|
+
await fs.writeFile(mdPath, content, 'utf8');
|
|
1342
|
+
this.installedFiles.push(mdPath);
|
|
794
1343
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1344
|
+
// Remove the source YAML file - we can regenerate from installer source if needed
|
|
1345
|
+
await fs.remove(yamlPath);
|
|
1346
|
+
|
|
1347
|
+
console.log(chalk.dim(` Built agent: ${agentName}.md`));
|
|
1348
|
+
}
|
|
1349
|
+
// Handle legacy .md agents - inject activation if needed
|
|
1350
|
+
else if (agentFile.endsWith('.md')) {
|
|
1351
|
+
const agentPath = path.join(agentsPath, agentFile);
|
|
1352
|
+
let content = await fs.readFile(agentPath, 'utf8');
|
|
1353
|
+
|
|
1354
|
+
// Check if content has agent XML and no activation block
|
|
1355
|
+
if (content.includes('<agent') && !content.includes('<activation')) {
|
|
1356
|
+
// Inject the activation block using XML handler
|
|
1357
|
+
content = this.xmlHandler.injectActivationSimple(content);
|
|
1358
|
+
// Ensure POSIX-compliant final newline
|
|
1359
|
+
const finalContent = content.endsWith('\n') ? content : content + '\n';
|
|
1360
|
+
await fs.writeFile(agentPath, finalContent, 'utf8');
|
|
1361
|
+
}
|
|
800
1362
|
}
|
|
801
1363
|
}
|
|
802
1364
|
}
|
|
803
1365
|
|
|
1366
|
+
/**
|
|
1367
|
+
* Build standalone agents in bmad/agents/ directory
|
|
1368
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
1369
|
+
* @param {string} projectDir - Path to project directory
|
|
1370
|
+
*/
|
|
1371
|
+
async buildStandaloneAgents(bmadDir, projectDir) {
|
|
1372
|
+
const standaloneAgentsPath = path.join(bmadDir, 'agents');
|
|
1373
|
+
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
|
|
1374
|
+
|
|
1375
|
+
// Check if standalone agents directory exists
|
|
1376
|
+
if (!(await fs.pathExists(standaloneAgentsPath))) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// Get all subdirectories in agents/
|
|
1381
|
+
const agentDirs = await fs.readdir(standaloneAgentsPath, { withFileTypes: true });
|
|
1382
|
+
|
|
1383
|
+
for (const agentDir of agentDirs) {
|
|
1384
|
+
if (!agentDir.isDirectory()) continue;
|
|
1385
|
+
|
|
1386
|
+
const agentDirPath = path.join(standaloneAgentsPath, agentDir.name);
|
|
1387
|
+
|
|
1388
|
+
// Find any .agent.yaml file in the directory
|
|
1389
|
+
const files = await fs.readdir(agentDirPath);
|
|
1390
|
+
const yamlFile = files.find((f) => f.endsWith('.agent.yaml'));
|
|
1391
|
+
|
|
1392
|
+
if (!yamlFile) continue;
|
|
1393
|
+
|
|
1394
|
+
const agentName = path.basename(yamlFile, '.agent.yaml');
|
|
1395
|
+
const sourceYamlPath = path.join(agentDirPath, yamlFile);
|
|
1396
|
+
const targetMdPath = path.join(agentDirPath, `${agentName}.md`);
|
|
1397
|
+
const customizePath = path.join(cfgAgentsDir, `${agentName}.customize.yaml`);
|
|
1398
|
+
|
|
1399
|
+
// Check for customizations
|
|
1400
|
+
const customizeExists = await fs.pathExists(customizePath);
|
|
1401
|
+
let customizedFields = [];
|
|
1402
|
+
|
|
1403
|
+
if (customizeExists) {
|
|
1404
|
+
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
1405
|
+
const yaml = require('js-yaml');
|
|
1406
|
+
const customizeYaml = yaml.load(customizeContent);
|
|
1407
|
+
|
|
1408
|
+
// Detect what fields are customized (similar to rebuildAgentFiles)
|
|
1409
|
+
if (customizeYaml) {
|
|
1410
|
+
if (customizeYaml.persona) {
|
|
1411
|
+
for (const [key, value] of Object.entries(customizeYaml.persona)) {
|
|
1412
|
+
if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
|
|
1413
|
+
customizedFields.push(`persona.${key}`);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (customizeYaml.agent?.metadata) {
|
|
1418
|
+
for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
|
|
1419
|
+
if (value !== '' && value !== null) {
|
|
1420
|
+
customizedFields.push(`metadata.${key}`);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
if (customizeYaml.critical_actions && customizeYaml.critical_actions.length > 0) {
|
|
1425
|
+
customizedFields.push('critical_actions');
|
|
1426
|
+
}
|
|
1427
|
+
if (customizeYaml.menu && customizeYaml.menu.length > 0) {
|
|
1428
|
+
customizedFields.push('menu');
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// Build YAML to XML .md
|
|
1434
|
+
const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
|
1435
|
+
includeMetadata: true,
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1439
|
+
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
1440
|
+
|
|
1441
|
+
// Write the built .md file with POSIX-compliant final newline
|
|
1442
|
+
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
|
1443
|
+
await fs.writeFile(targetMdPath, content, 'utf8');
|
|
1444
|
+
|
|
1445
|
+
// Display result
|
|
1446
|
+
if (customizedFields.length > 0) {
|
|
1447
|
+
console.log(chalk.dim(` Built standalone agent: ${agentName}.md `) + chalk.yellow(`(customized: ${customizedFields.join(', ')})`));
|
|
1448
|
+
} else {
|
|
1449
|
+
console.log(chalk.dim(` Built standalone agent: ${agentName}.md`));
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* Rebuild agent files from installer source (for compile command)
|
|
1456
|
+
* @param {string} modulePath - Path to module in bmad/ installation
|
|
1457
|
+
* @param {string} moduleName - Module name
|
|
1458
|
+
*/
|
|
1459
|
+
async rebuildAgentFiles(modulePath, moduleName) {
|
|
1460
|
+
// Get source agents directory from installer
|
|
1461
|
+
const sourceAgentsPath =
|
|
1462
|
+
moduleName === 'core' ? path.join(getModulePath('core'), 'agents') : path.join(getSourcePath(`modules/${moduleName}`), 'agents');
|
|
1463
|
+
|
|
1464
|
+
if (!(await fs.pathExists(sourceAgentsPath))) {
|
|
1465
|
+
return; // No source agents to rebuild
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// Determine project directory (parent of bmad/ directory)
|
|
1469
|
+
const bmadDir = path.dirname(modulePath);
|
|
1470
|
+
const projectDir = path.dirname(bmadDir);
|
|
1471
|
+
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
|
|
1472
|
+
const targetAgentsPath = path.join(modulePath, 'agents');
|
|
1473
|
+
|
|
1474
|
+
// Ensure target directory exists
|
|
1475
|
+
await fs.ensureDir(targetAgentsPath);
|
|
1476
|
+
|
|
1477
|
+
// Get all YAML agent files from source
|
|
1478
|
+
const sourceFiles = await fs.readdir(sourceAgentsPath);
|
|
1479
|
+
|
|
1480
|
+
for (const file of sourceFiles) {
|
|
1481
|
+
if (file.endsWith('.agent.yaml')) {
|
|
1482
|
+
const agentName = file.replace('.agent.yaml', '');
|
|
1483
|
+
const sourceYamlPath = path.join(sourceAgentsPath, file);
|
|
1484
|
+
const targetMdPath = path.join(targetAgentsPath, `${agentName}.md`);
|
|
1485
|
+
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1486
|
+
|
|
1487
|
+
// Check for customizations
|
|
1488
|
+
const customizeExists = await fs.pathExists(customizePath);
|
|
1489
|
+
let customizedFields = [];
|
|
1490
|
+
|
|
1491
|
+
if (customizeExists) {
|
|
1492
|
+
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
1493
|
+
const yaml = require('js-yaml');
|
|
1494
|
+
const customizeYaml = yaml.load(customizeContent);
|
|
1495
|
+
|
|
1496
|
+
// Detect what fields are customized
|
|
1497
|
+
if (customizeYaml) {
|
|
1498
|
+
if (customizeYaml.persona) {
|
|
1499
|
+
for (const [key, value] of Object.entries(customizeYaml.persona)) {
|
|
1500
|
+
if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
|
|
1501
|
+
customizedFields.push(`persona.${key}`);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
if (customizeYaml.agent?.metadata) {
|
|
1506
|
+
for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
|
|
1507
|
+
if (value !== '' && value !== null) {
|
|
1508
|
+
customizedFields.push(`metadata.${key}`);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (customizeYaml.critical_actions && customizeYaml.critical_actions.length > 0) {
|
|
1513
|
+
customizedFields.push('critical_actions');
|
|
1514
|
+
}
|
|
1515
|
+
if (customizeYaml.memories && customizeYaml.memories.length > 0) {
|
|
1516
|
+
customizedFields.push('memories');
|
|
1517
|
+
}
|
|
1518
|
+
if (customizeYaml.menu && customizeYaml.menu.length > 0) {
|
|
1519
|
+
customizedFields.push('menu');
|
|
1520
|
+
}
|
|
1521
|
+
if (customizeYaml.prompts && customizeYaml.prompts.length > 0) {
|
|
1522
|
+
customizedFields.push('prompts');
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// Build YAML + customize to .md
|
|
1528
|
+
const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
|
1529
|
+
includeMetadata: true,
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1533
|
+
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
1534
|
+
|
|
1535
|
+
// Write the rebuilt .md file with POSIX-compliant final newline
|
|
1536
|
+
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
|
1537
|
+
await fs.writeFile(targetMdPath, content, 'utf8');
|
|
1538
|
+
|
|
1539
|
+
// Display result with customizations if any
|
|
1540
|
+
if (customizedFields.length > 0) {
|
|
1541
|
+
console.log(chalk.dim(` Rebuilt agent: ${agentName}.md `) + chalk.yellow(`(customized: ${customizedFields.join(', ')})`));
|
|
1542
|
+
} else {
|
|
1543
|
+
console.log(chalk.dim(` Rebuilt agent: ${agentName}.md`));
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* Compile/rebuild all agents and tasks for quick updates
|
|
1551
|
+
* @param {Object} config - Compilation configuration
|
|
1552
|
+
* @returns {Object} Compilation results
|
|
1553
|
+
*/
|
|
1554
|
+
async compileAgents(config) {
|
|
1555
|
+
const ora = require('ora');
|
|
1556
|
+
const spinner = ora('Starting agent compilation...').start();
|
|
1557
|
+
|
|
1558
|
+
try {
|
|
1559
|
+
const projectDir = path.resolve(config.directory);
|
|
1560
|
+
const bmadDir = await this.findBmadDir(projectDir);
|
|
1561
|
+
|
|
1562
|
+
// Check if bmad directory exists
|
|
1563
|
+
if (!(await fs.pathExists(bmadDir))) {
|
|
1564
|
+
spinner.fail('No BMAD installation found');
|
|
1565
|
+
throw new Error(`BMAD not installed at ${bmadDir}`);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
let agentCount = 0;
|
|
1569
|
+
let taskCount = 0;
|
|
1570
|
+
|
|
1571
|
+
// Process all modules in bmad directory
|
|
1572
|
+
spinner.text = 'Rebuilding agent files...';
|
|
1573
|
+
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
1574
|
+
|
|
1575
|
+
for (const entry of entries) {
|
|
1576
|
+
if (entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs') {
|
|
1577
|
+
const modulePath = path.join(bmadDir, entry.name);
|
|
1578
|
+
|
|
1579
|
+
// Special handling for standalone agents in bmad/agents/ directory
|
|
1580
|
+
if (entry.name === 'agents') {
|
|
1581
|
+
spinner.text = 'Building standalone agents...';
|
|
1582
|
+
await this.buildStandaloneAgents(bmadDir, projectDir);
|
|
1583
|
+
|
|
1584
|
+
// Count standalone agents
|
|
1585
|
+
const standaloneAgentsPath = path.join(bmadDir, 'agents');
|
|
1586
|
+
const standaloneAgentDirs = await fs.readdir(standaloneAgentsPath, { withFileTypes: true });
|
|
1587
|
+
for (const agentDir of standaloneAgentDirs) {
|
|
1588
|
+
if (agentDir.isDirectory()) {
|
|
1589
|
+
const agentDirPath = path.join(standaloneAgentsPath, agentDir.name);
|
|
1590
|
+
const agentFiles = await fs.readdir(agentDirPath);
|
|
1591
|
+
agentCount += agentFiles.filter((f) => f.endsWith('.md') && !f.endsWith('.agent.yaml')).length;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
} else {
|
|
1595
|
+
// Rebuild module agents from installer source
|
|
1596
|
+
const agentsPath = path.join(modulePath, 'agents');
|
|
1597
|
+
if (await fs.pathExists(agentsPath)) {
|
|
1598
|
+
await this.rebuildAgentFiles(modulePath, entry.name);
|
|
1599
|
+
const agentFiles = await fs.readdir(agentsPath);
|
|
1600
|
+
agentCount += agentFiles.filter((f) => f.endsWith('.md')).length;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// Count tasks (already built)
|
|
1604
|
+
const tasksPath = path.join(modulePath, 'tasks');
|
|
1605
|
+
if (await fs.pathExists(tasksPath)) {
|
|
1606
|
+
const taskFiles = await fs.readdir(tasksPath);
|
|
1607
|
+
taskCount += taskFiles.filter((f) => f.endsWith('.md')).length;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// Regenerate manifests after compilation
|
|
1614
|
+
spinner.start('Regenerating manifests...');
|
|
1615
|
+
const installedModules = entries
|
|
1616
|
+
.filter((e) => e.isDirectory() && e.name !== '_cfg' && e.name !== 'docs' && e.name !== 'agents' && e.name !== 'core')
|
|
1617
|
+
.map((e) => e.name);
|
|
1618
|
+
const manifestGen = new ManifestGenerator();
|
|
1619
|
+
|
|
1620
|
+
// Get existing IDE list from manifest
|
|
1621
|
+
const existingManifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
|
1622
|
+
let existingIdes = [];
|
|
1623
|
+
if (await fs.pathExists(existingManifestPath)) {
|
|
1624
|
+
const manifestContent = await fs.readFile(existingManifestPath, 'utf8');
|
|
1625
|
+
const yaml = require('js-yaml');
|
|
1626
|
+
const manifest = yaml.load(manifestContent);
|
|
1627
|
+
existingIdes = manifest.ides || [];
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
await manifestGen.generateManifests(bmadDir, installedModules, [], {
|
|
1631
|
+
ides: existingIdes,
|
|
1632
|
+
});
|
|
1633
|
+
spinner.succeed('Manifests regenerated');
|
|
1634
|
+
|
|
1635
|
+
// Update IDE configurations using the existing IDE list from manifest
|
|
1636
|
+
if (existingIdes && existingIdes.length > 0) {
|
|
1637
|
+
spinner.start('Updating IDE configurations...');
|
|
1638
|
+
|
|
1639
|
+
for (const ide of existingIdes) {
|
|
1640
|
+
spinner.text = `Updating ${ide}...`;
|
|
1641
|
+
|
|
1642
|
+
// Stop spinner before IDE setup to prevent blocking any potential prompts
|
|
1643
|
+
// However, we pass _alreadyConfigured to skip all prompts during compile
|
|
1644
|
+
spinner.stop();
|
|
1645
|
+
|
|
1646
|
+
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
1647
|
+
selectedModules: installedModules,
|
|
1648
|
+
skipModuleInstall: true, // Skip module installation, just update IDE files
|
|
1649
|
+
verbose: config.verbose,
|
|
1650
|
+
preCollectedConfig: { _alreadyConfigured: true }, // Skip all interactive prompts during compile
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
// Restart spinner for next IDE
|
|
1654
|
+
if (existingIdes.indexOf(ide) < existingIdes.length - 1) {
|
|
1655
|
+
spinner.start('Updating IDE configurations...');
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
console.log(chalk.green('✓ IDE configurations updated'));
|
|
1660
|
+
} else {
|
|
1661
|
+
console.log(chalk.yellow('⚠️ No IDEs configured. Skipping IDE update.'));
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
return { agentCount, taskCount };
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
spinner.fail('Compilation failed');
|
|
1667
|
+
throw error;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
|
|
804
1671
|
/**
|
|
805
1672
|
* Private: Update core
|
|
806
1673
|
*/
|
|
@@ -817,6 +1684,131 @@ class Installer {
|
|
|
817
1684
|
}
|
|
818
1685
|
}
|
|
819
1686
|
|
|
1687
|
+
/**
|
|
1688
|
+
* Quick update method - preserves all settings and only prompts for new config fields
|
|
1689
|
+
* @param {Object} config - Configuration with directory
|
|
1690
|
+
* @returns {Object} Update result
|
|
1691
|
+
*/
|
|
1692
|
+
async quickUpdate(config) {
|
|
1693
|
+
const ora = require('ora');
|
|
1694
|
+
const spinner = ora('Starting quick update...').start();
|
|
1695
|
+
|
|
1696
|
+
try {
|
|
1697
|
+
const projectDir = path.resolve(config.directory);
|
|
1698
|
+
const bmadDir = await this.findBmadDir(projectDir);
|
|
1699
|
+
|
|
1700
|
+
// Check if bmad directory exists
|
|
1701
|
+
if (!(await fs.pathExists(bmadDir))) {
|
|
1702
|
+
spinner.fail('No BMAD installation found');
|
|
1703
|
+
throw new Error(`BMAD not installed at ${bmadDir}. Use regular install for first-time setup.`);
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
spinner.text = 'Detecting installed modules and configuration...';
|
|
1707
|
+
|
|
1708
|
+
// Detect existing installation
|
|
1709
|
+
const existingInstall = await this.detector.detect(bmadDir);
|
|
1710
|
+
const installedModules = existingInstall.modules.map((m) => m.id);
|
|
1711
|
+
const configuredIdes = existingInstall.ides || [];
|
|
1712
|
+
|
|
1713
|
+
// Load saved IDE configurations
|
|
1714
|
+
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
|
1715
|
+
|
|
1716
|
+
// Get available modules (what we have source for)
|
|
1717
|
+
const availableModules = await this.moduleManager.listAvailable();
|
|
1718
|
+
const availableModuleIds = new Set(availableModules.map((m) => m.id));
|
|
1719
|
+
|
|
1720
|
+
// Only update modules that are BOTH installed AND available (we have source for)
|
|
1721
|
+
const modulesToUpdate = installedModules.filter((id) => availableModuleIds.has(id));
|
|
1722
|
+
const skippedModules = installedModules.filter((id) => !availableModuleIds.has(id));
|
|
1723
|
+
|
|
1724
|
+
spinner.succeed(`Found ${modulesToUpdate.length} module(s) to update and ${configuredIdes.length} configured tool(s)`);
|
|
1725
|
+
|
|
1726
|
+
if (skippedModules.length > 0) {
|
|
1727
|
+
console.log(chalk.yellow(`⚠️ Skipping ${skippedModules.length} module(s) - no source available: ${skippedModules.join(', ')}`));
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// Load existing configs and collect new fields (if any)
|
|
1731
|
+
console.log(chalk.cyan('\n📋 Checking for new configuration options...'));
|
|
1732
|
+
await this.configCollector.loadExistingConfig(projectDir);
|
|
1733
|
+
|
|
1734
|
+
let promptedForNewFields = false;
|
|
1735
|
+
|
|
1736
|
+
// Check core config for new fields
|
|
1737
|
+
const corePrompted = await this.configCollector.collectModuleConfigQuick('core', projectDir, true);
|
|
1738
|
+
if (corePrompted) {
|
|
1739
|
+
promptedForNewFields = true;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
// Check each module we're updating for new fields (NOT skipped modules)
|
|
1743
|
+
for (const moduleName of modulesToUpdate) {
|
|
1744
|
+
const modulePrompted = await this.configCollector.collectModuleConfigQuick(moduleName, projectDir, true);
|
|
1745
|
+
if (modulePrompted) {
|
|
1746
|
+
promptedForNewFields = true;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
if (!promptedForNewFields) {
|
|
1751
|
+
console.log(chalk.green('✓ All configuration is up to date, no new options to configure'));
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// Add metadata
|
|
1755
|
+
this.configCollector.collectedConfig._meta = {
|
|
1756
|
+
version: require(path.join(getProjectRoot(), 'package.json')).version,
|
|
1757
|
+
installDate: new Date().toISOString(),
|
|
1758
|
+
lastModified: new Date().toISOString(),
|
|
1759
|
+
};
|
|
1760
|
+
|
|
1761
|
+
// Check if bmad_folder has changed
|
|
1762
|
+
const existingBmadFolderName = path.basename(bmadDir);
|
|
1763
|
+
const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
|
|
1764
|
+
|
|
1765
|
+
if (existingBmadFolderName === newBmadFolderName) {
|
|
1766
|
+
// Normal quick update - start the spinner
|
|
1767
|
+
spinner.start('Updating BMAD installation...');
|
|
1768
|
+
} else {
|
|
1769
|
+
// Folder name has changed - stop spinner and let install() handle it
|
|
1770
|
+
spinner.stop();
|
|
1771
|
+
console.log(chalk.yellow(`\n⚠️ Folder name will change: ${existingBmadFolderName} → ${newBmadFolderName}`));
|
|
1772
|
+
console.log(chalk.yellow('The installer will handle the folder migration.\n'));
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// Build the config object for the installer
|
|
1776
|
+
const installConfig = {
|
|
1777
|
+
directory: projectDir,
|
|
1778
|
+
installCore: true,
|
|
1779
|
+
modules: modulesToUpdate, // Only update modules we have source for
|
|
1780
|
+
ides: configuredIdes,
|
|
1781
|
+
skipIde: configuredIdes.length === 0,
|
|
1782
|
+
coreConfig: this.configCollector.collectedConfig.core,
|
|
1783
|
+
actionType: 'install', // Use regular install flow
|
|
1784
|
+
_quickUpdate: true, // Flag to skip certain prompts
|
|
1785
|
+
_preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
|
|
1786
|
+
_savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
// Call the standard install method
|
|
1790
|
+
const result = await this.install(installConfig);
|
|
1791
|
+
|
|
1792
|
+
// Only succeed the spinner if it's still spinning
|
|
1793
|
+
// (install method might have stopped it if folder name changed)
|
|
1794
|
+
if (spinner.isSpinning) {
|
|
1795
|
+
spinner.succeed('Quick update complete!');
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
return {
|
|
1799
|
+
success: true,
|
|
1800
|
+
moduleCount: modulesToUpdate.length + 1, // +1 for core
|
|
1801
|
+
hadNewFields: promptedForNewFields,
|
|
1802
|
+
modules: ['core', ...modulesToUpdate],
|
|
1803
|
+
skippedModules: skippedModules,
|
|
1804
|
+
ides: configuredIdes,
|
|
1805
|
+
};
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
spinner.fail('Quick update failed');
|
|
1808
|
+
throw error;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
|
|
820
1812
|
/**
|
|
821
1813
|
* Private: Prompt for update action
|
|
822
1814
|
*/
|
|
@@ -837,36 +1829,236 @@ class Installer {
|
|
|
837
1829
|
}
|
|
838
1830
|
|
|
839
1831
|
/**
|
|
840
|
-
*
|
|
1832
|
+
* Handle legacy BMAD v4 migration with automatic backup
|
|
1833
|
+
* @param {string} projectDir - Project directory
|
|
841
1834
|
* @param {Object} legacyV4 - Legacy V4 detection result with offenders array
|
|
842
|
-
* @returns {Error} Formatted error with fullMessage property
|
|
843
1835
|
*/
|
|
844
|
-
|
|
845
|
-
|
|
1836
|
+
async handleLegacyV4Migration(projectDir, legacyV4) {
|
|
1837
|
+
console.log(chalk.yellow.bold('\n⚠️ Legacy BMAD v4 detected'));
|
|
1838
|
+
console.log(chalk.dim('The installer found legacy artefacts in your project.\n'));
|
|
1839
|
+
|
|
1840
|
+
// Separate .bmad* folders (auto-backup) from other offending paths (manual cleanup)
|
|
1841
|
+
const bmadFolders = legacyV4.offenders.filter((p) => {
|
|
1842
|
+
const name = path.basename(p);
|
|
1843
|
+
return name.startsWith('.bmad'); // Only dot-prefixed folders get auto-backed up
|
|
1844
|
+
});
|
|
1845
|
+
const otherOffenders = legacyV4.offenders.filter((p) => {
|
|
1846
|
+
const name = path.basename(p);
|
|
1847
|
+
return !name.startsWith('.bmad'); // Everything else is manual cleanup
|
|
1848
|
+
});
|
|
846
1849
|
|
|
847
|
-
|
|
848
|
-
const headerMessage = `
|
|
849
|
-
${chalk.red.bold('Blocked: Legacy BMAD v4 detected')}
|
|
850
|
-
The installer found legacy artefacts in your project.`;
|
|
1850
|
+
const inquirer = require('inquirer');
|
|
851
1851
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1852
|
+
// Show warning for other offending paths FIRST
|
|
1853
|
+
if (otherOffenders.length > 0) {
|
|
1854
|
+
console.log(chalk.yellow('⚠️ Recommended cleanup:'));
|
|
1855
|
+
console.log(chalk.dim('It is recommended to remove the following items before proceeding:\n'));
|
|
1856
|
+
for (const p of otherOffenders) console.log(chalk.dim(` - ${p}`));
|
|
855
1857
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1858
|
+
console.log(chalk.cyan('\nCleanup commands you can copy/paste:'));
|
|
1859
|
+
console.log(chalk.dim('macOS/Linux:'));
|
|
1860
|
+
for (const p of otherOffenders) console.log(chalk.dim(` rm -rf '${p}'`));
|
|
1861
|
+
console.log(chalk.dim('Windows:'));
|
|
1862
|
+
for (const p of otherOffenders) console.log(chalk.dim(` rmdir /S /Q "${p}"`));
|
|
861
1863
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1864
|
+
const { cleanedUp } = await inquirer.prompt([
|
|
1865
|
+
{
|
|
1866
|
+
type: 'confirm',
|
|
1867
|
+
name: 'cleanedUp',
|
|
1868
|
+
message: 'Have you completed the recommended cleanup? (You can proceed without it, but it is recommended)',
|
|
1869
|
+
default: false,
|
|
1870
|
+
},
|
|
1871
|
+
]);
|
|
865
1872
|
|
|
866
|
-
|
|
867
|
-
|
|
1873
|
+
if (cleanedUp) {
|
|
1874
|
+
console.log(chalk.green('✓ Cleanup acknowledged\n'));
|
|
1875
|
+
} else {
|
|
1876
|
+
console.log(chalk.yellow('⚠️ Proceeding without recommended cleanup\n'));
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// Handle .bmad* folders with automatic backup
|
|
1881
|
+
if (bmadFolders.length > 0) {
|
|
1882
|
+
console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:'));
|
|
1883
|
+
for (const p of bmadFolders) console.log(chalk.dim(` - ${p}`));
|
|
1884
|
+
|
|
1885
|
+
const { proceed } = await inquirer.prompt([
|
|
1886
|
+
{
|
|
1887
|
+
type: 'confirm',
|
|
1888
|
+
name: 'proceed',
|
|
1889
|
+
message: 'Proceed with backing up legacy v4 folders?',
|
|
1890
|
+
default: true,
|
|
1891
|
+
},
|
|
1892
|
+
]);
|
|
868
1893
|
|
|
869
|
-
|
|
1894
|
+
if (proceed) {
|
|
1895
|
+
const backupDir = path.join(projectDir, 'v4-backup');
|
|
1896
|
+
await fs.ensureDir(backupDir);
|
|
1897
|
+
|
|
1898
|
+
for (const folder of bmadFolders) {
|
|
1899
|
+
const folderName = path.basename(folder);
|
|
1900
|
+
const backupPath = path.join(backupDir, folderName);
|
|
1901
|
+
|
|
1902
|
+
// If backup already exists, add timestamp
|
|
1903
|
+
let finalBackupPath = backupPath;
|
|
1904
|
+
if (await fs.pathExists(backupPath)) {
|
|
1905
|
+
const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').split('T')[0];
|
|
1906
|
+
finalBackupPath = path.join(backupDir, `${folderName}-${timestamp}`);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
await fs.move(folder, finalBackupPath, { overwrite: false });
|
|
1910
|
+
console.log(chalk.green(`✓ Moved ${folderName} to ${path.relative(projectDir, finalBackupPath)}`));
|
|
1911
|
+
}
|
|
1912
|
+
} else {
|
|
1913
|
+
throw new Error('Installation cancelled by user');
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* Read files-manifest.csv
|
|
1920
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
1921
|
+
* @returns {Array} Array of file entries from files-manifest.csv
|
|
1922
|
+
*/
|
|
1923
|
+
async readFilesManifest(bmadDir) {
|
|
1924
|
+
const filesManifestPath = path.join(bmadDir, '_cfg', 'files-manifest.csv');
|
|
1925
|
+
if (!(await fs.pathExists(filesManifestPath))) {
|
|
1926
|
+
return [];
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
try {
|
|
1930
|
+
const content = await fs.readFile(filesManifestPath, 'utf8');
|
|
1931
|
+
const lines = content.split('\n');
|
|
1932
|
+
const files = [];
|
|
1933
|
+
|
|
1934
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1935
|
+
// Skip header
|
|
1936
|
+
const line = lines[i].trim();
|
|
1937
|
+
if (!line) continue;
|
|
1938
|
+
|
|
1939
|
+
// Parse CSV line properly handling quoted values
|
|
1940
|
+
const parts = [];
|
|
1941
|
+
let current = '';
|
|
1942
|
+
let inQuotes = false;
|
|
1943
|
+
|
|
1944
|
+
for (const char of line) {
|
|
1945
|
+
if (char === '"') {
|
|
1946
|
+
inQuotes = !inQuotes;
|
|
1947
|
+
} else if (char === ',' && !inQuotes) {
|
|
1948
|
+
parts.push(current);
|
|
1949
|
+
current = '';
|
|
1950
|
+
} else {
|
|
1951
|
+
current += char;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
parts.push(current); // Add last part
|
|
1955
|
+
|
|
1956
|
+
if (parts.length >= 4) {
|
|
1957
|
+
files.push({
|
|
1958
|
+
type: parts[0],
|
|
1959
|
+
name: parts[1],
|
|
1960
|
+
module: parts[2],
|
|
1961
|
+
path: parts[3],
|
|
1962
|
+
hash: parts[4] || null, // Hash may not exist in old manifests
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
return files;
|
|
1968
|
+
} catch (error) {
|
|
1969
|
+
console.warn('Warning: Could not read files-manifest.csv:', error.message);
|
|
1970
|
+
return [];
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
/**
|
|
1975
|
+
* Detect custom and modified files
|
|
1976
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
1977
|
+
* @param {Array} existingFilesManifest - Previous files from files-manifest.csv
|
|
1978
|
+
* @returns {Object} Object with customFiles and modifiedFiles arrays
|
|
1979
|
+
*/
|
|
1980
|
+
async detectCustomFiles(bmadDir, existingFilesManifest) {
|
|
1981
|
+
const customFiles = [];
|
|
1982
|
+
const modifiedFiles = [];
|
|
1983
|
+
|
|
1984
|
+
// Check if the manifest has hashes - if not, we can't detect modifications
|
|
1985
|
+
let manifestHasHashes = false;
|
|
1986
|
+
if (existingFilesManifest && existingFilesManifest.length > 0) {
|
|
1987
|
+
manifestHasHashes = existingFilesManifest.some((f) => f.hash);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// Build map of previously installed files from files-manifest.csv with their hashes
|
|
1991
|
+
const installedFilesMap = new Map();
|
|
1992
|
+
for (const fileEntry of existingFilesManifest) {
|
|
1993
|
+
if (fileEntry.path) {
|
|
1994
|
+
// Files in manifest are stored as relative paths starting with 'bmad/'
|
|
1995
|
+
// Convert to absolute path
|
|
1996
|
+
const relativePath = fileEntry.path.startsWith('bmad/') ? fileEntry.path.slice(5) : fileEntry.path;
|
|
1997
|
+
const absolutePath = path.join(bmadDir, relativePath);
|
|
1998
|
+
installedFilesMap.set(path.normalize(absolutePath), {
|
|
1999
|
+
hash: fileEntry.hash,
|
|
2000
|
+
relativePath: relativePath,
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// Recursively scan bmadDir for all files
|
|
2006
|
+
const scanDirectory = async (dir) => {
|
|
2007
|
+
try {
|
|
2008
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
2009
|
+
for (const entry of entries) {
|
|
2010
|
+
const fullPath = path.join(dir, entry.name);
|
|
2011
|
+
|
|
2012
|
+
if (entry.isDirectory()) {
|
|
2013
|
+
// Skip certain directories
|
|
2014
|
+
if (entry.name === 'node_modules' || entry.name === '.git') {
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
await scanDirectory(fullPath);
|
|
2018
|
+
} else if (entry.isFile()) {
|
|
2019
|
+
const normalizedPath = path.normalize(fullPath);
|
|
2020
|
+
const fileInfo = installedFilesMap.get(normalizedPath);
|
|
2021
|
+
|
|
2022
|
+
// Skip certain system files that are auto-generated
|
|
2023
|
+
const relativePath = path.relative(bmadDir, fullPath);
|
|
2024
|
+
const fileName = path.basename(fullPath);
|
|
2025
|
+
|
|
2026
|
+
// Skip _cfg directory - system files
|
|
2027
|
+
if (relativePath.startsWith('_cfg/') || relativePath.startsWith('_cfg\\')) {
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// Skip config.yaml files - these are regenerated on each install/update
|
|
2032
|
+
// Users should use _cfg/agents/ override files instead
|
|
2033
|
+
if (fileName === 'config.yaml') {
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
if (!fileInfo) {
|
|
2038
|
+
// File not in manifest = custom file
|
|
2039
|
+
customFiles.push(fullPath);
|
|
2040
|
+
} else if (manifestHasHashes && fileInfo.hash) {
|
|
2041
|
+
// File in manifest with hash - check if it was modified
|
|
2042
|
+
const currentHash = await this.manifest.calculateFileHash(fullPath);
|
|
2043
|
+
if (currentHash && currentHash !== fileInfo.hash) {
|
|
2044
|
+
// Hash changed = file was modified
|
|
2045
|
+
modifiedFiles.push({
|
|
2046
|
+
path: fullPath,
|
|
2047
|
+
relativePath: fileInfo.relativePath,
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
// If manifest doesn't have hashes, we can't detect modifications
|
|
2052
|
+
// so we just skip files that are in the manifest
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
} catch {
|
|
2056
|
+
// Ignore errors scanning directories
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
|
|
2060
|
+
await scanDirectory(bmadDir);
|
|
2061
|
+
return { customFiles, modifiedFiles };
|
|
870
2062
|
}
|
|
871
2063
|
|
|
872
2064
|
/**
|
|
@@ -981,7 +2173,13 @@ Note: You may also want to remove other BMAD-related v4 files/folders left over
|
|
|
981
2173
|
|
|
982
2174
|
configContent += processedTemplate;
|
|
983
2175
|
|
|
2176
|
+
// Ensure POSIX-compliant final newline
|
|
2177
|
+
if (!configContent.endsWith('\n')) {
|
|
2178
|
+
configContent += '\n';
|
|
2179
|
+
}
|
|
2180
|
+
|
|
984
2181
|
await fs.writeFile(configPath, configContent, 'utf8');
|
|
2182
|
+
this.installedFiles.push(configPath); // Track agent config files
|
|
985
2183
|
createdCount++;
|
|
986
2184
|
}
|
|
987
2185
|
|
|
@@ -997,7 +2195,7 @@ Note: You may also want to remove other BMAD-related v4 files/folders left over
|
|
|
997
2195
|
* @param {Array} agentDetails - Array of agent details
|
|
998
2196
|
*/
|
|
999
2197
|
async generateAgentManifest(bmadDir, agentDetails) {
|
|
1000
|
-
const manifestPath = path.join(bmadDir, '_cfg', 'agent-
|
|
2198
|
+
const manifestPath = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
|
|
1001
2199
|
await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
|
|
1002
2200
|
}
|
|
1003
2201
|
|
|
@@ -1061,7 +2259,7 @@ Note: You may also want to remove other BMAD-related v4 files/folders left over
|
|
|
1061
2259
|
const targetDocPath = path.join(docsDir, `${ide}-instructions.md`);
|
|
1062
2260
|
|
|
1063
2261
|
if (await fs.pathExists(sourceDocPath)) {
|
|
1064
|
-
await
|
|
2262
|
+
await this.copyFileWithPlaceholderReplacement(sourceDocPath, targetDocPath, this.bmadFolderName || 'bmad');
|
|
1065
2263
|
}
|
|
1066
2264
|
}
|
|
1067
2265
|
}
|