bmad-method 6.0.0-alpha.14 → 6.0.0-alpha.16
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/.coderabbit.yaml +36 -0
- package/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +4 -4
- package/CHANGELOG.md +168 -409
- package/README.md +4 -1
- package/docs/agent-customization-guide.md +7 -7
- package/docs/custom-content-installation.md +245 -0
- 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 +1 -1
- package/docs/index.md +2 -2
- package/docs/installers-bundlers/ide-injections.md +2 -2
- package/docs/installers-bundlers/installers-modules-platforms-reference.md +29 -28
- package/docs/v4-to-v6-upgrade.md +10 -10
- package/docs/web-bundles-gemini-gpt-guide.md +2 -2
- package/package.json +1 -1
- package/src/core/_module-installer/installer.js +1 -1
- package/src/core/agents/bmad-master.agent.yaml +5 -5
- package/src/core/agents/bmad-web-orchestrator.agent.xml +6 -6
- package/src/core/{_module-installer/install-config.yaml → module.yaml} +0 -7
- package/src/core/resources/excalidraw/README.md +4 -4
- package/src/core/tasks/advanced-elicitation.xml +3 -3
- package/src/core/tasks/index-docs.xml +1 -1
- package/src/core/tasks/validate-workflow.xml +1 -1
- package/src/core/tasks/workflow.xml +4 -4
- package/src/core/tools/shard-doc.xml +1 -1
- package/src/core/workflows/brainstorming/workflow.md +2 -2
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +2 -2
- package/src/core/workflows/party-mode/workflow.md +4 -4
- package/src/modules/bmb/README.md +2 -2
- package/src/modules/bmb/_module-installer/installer.js +1 -1
- package/src/modules/bmb/agents/bmad-builder.agent.yaml +15 -15
- package/src/modules/bmb/docs/agents/agent-compilation.md +2 -2
- package/src/modules/bmb/docs/agents/agent-menu-patterns.md +22 -22
- package/src/modules/bmb/docs/agents/expert-agent-architecture.md +1 -1
- package/src/modules/bmb/docs/agents/index.md +2 -2
- package/src/modules/bmb/docs/agents/module-agent-architecture.md +50 -51
- package/src/modules/bmb/docs/agents/understanding-agent-types.md +3 -3
- package/src/modules/bmb/docs/workflows/architecture.md +1 -1
- package/src/modules/bmb/docs/workflows/common-workflow-tools.csv +3 -3
- package/src/modules/bmb/docs/workflows/templates/step-01-init-continuable-template.md +1 -1
- package/src/modules/bmb/docs/workflows/templates/step-1b-template.md +1 -1
- package/src/modules/bmb/docs/workflows/templates/step-file.md +3 -3
- package/src/modules/bmb/docs/workflows/templates/step-template.md +3 -3
- package/src/modules/bmb/docs/workflows/templates/workflow-template.md +2 -2
- package/src/modules/bmb/docs/workflows/templates/workflow.md +1 -1
- package/src/modules/bmb/{_module-installer/install-config.yaml → module.yaml} +0 -1
- package/src/modules/bmb/reference/agents/module-examples/README.md +1 -1
- package/src/modules/bmb/reference/agents/module-examples/security-engineer.agent.yaml +5 -5
- package/src/modules/bmb/reference/agents/module-examples/trend-analyst.agent.yaml +7 -7
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +1 -1
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +1 -1
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +3 -3
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +3 -3
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +5 -5
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +5 -5
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +5 -5
- package/src/modules/bmb/reference/workflows/meal-prep-nutrition/workflow.md +2 -2
- package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/README.md +1 -1
- package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/security-engineer.agent.yaml +6 -6
- package/src/modules/bmb/workflows/create-agent/data/reference/agents/module-examples/trend-analyst.agent.yaml +7 -7
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +1 -1
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +1 -1
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +3 -3
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +3 -3
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +5 -5
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +5 -5
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +5 -5
- package/src/modules/bmb/workflows/create-agent/data/reference/workflows/meal-prep-nutrition/workflow.md +2 -2
- package/src/modules/bmb/workflows/create-agent/data/validation-complete.md +5 -5
- package/src/modules/bmb/workflows/create-agent/steps/step-01-brainstorm.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-02-discover.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-03-persona.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-04-commands.md +7 -7
- package/src/modules/bmb/workflows/create-agent/steps/step-05-name.md +3 -3
- package/src/modules/bmb/workflows/create-agent/steps/step-06-build.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-07-validate.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-08-setup.md +3 -3
- package/src/modules/bmb/workflows/create-agent/steps/step-09-customize.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-10-build-tools.md +3 -3
- package/src/modules/bmb/workflows/create-agent/steps/step-11-celebrate.md +3 -3
- package/src/modules/bmb/workflows/create-agent/workflow.md +13 -13
- package/src/modules/bmb/workflows/create-module/steps/step-01-init.md +2 -2
- package/src/modules/bmb/workflows/create-module/steps/step-02-concept.md +4 -4
- package/src/modules/bmb/workflows/create-module/steps/step-03-components.md +4 -4
- package/src/modules/bmb/workflows/create-module/steps/step-04-structure.md +6 -6
- package/src/modules/bmb/workflows/create-module/steps/step-05-config.md +5 -5
- package/src/modules/bmb/workflows/create-module/steps/step-06-agents.md +6 -6
- package/src/modules/bmb/workflows/create-module/steps/step-07-workflows.md +3 -3
- package/src/modules/bmb/workflows/create-module/steps/step-08-installer.md +12 -12
- package/src/modules/bmb/workflows/create-module/steps/step-09-documentation.md +6 -5
- package/src/modules/bmb/workflows/create-module/steps/step-10-roadmap.md +6 -5
- package/src/modules/bmb/workflows/create-module/steps/step-11-validate.md +7 -7
- package/src/modules/bmb/workflows/create-module/templates/agent.template.md +7 -7
- package/src/modules/bmb/workflows/create-module/templates/installer.template.js +1 -1
- package/src/modules/bmb/workflows/create-module/templates/{install-config.template.yaml → module.template.yaml} +1 -1
- package/src/modules/bmb/workflows/create-module/validation.md +3 -3
- package/src/modules/bmb/workflows/create-module/workflow.md +2 -2
- package/src/modules/bmb/workflows/create-workflow/steps/step-01-init.md +3 -3
- package/src/modules/bmb/workflows/create-workflow/steps/step-02-gather.md +5 -5
- package/src/modules/bmb/workflows/create-workflow/steps/step-03-tools-configuration.md +4 -4
- package/src/modules/bmb/workflows/create-workflow/steps/step-04-plan-review.md +3 -3
- package/src/modules/bmb/workflows/create-workflow/steps/step-05-output-format-design.md +3 -3
- package/src/modules/bmb/workflows/create-workflow/steps/step-06-design.md +10 -10
- package/src/modules/bmb/workflows/create-workflow/steps/step-07-build.md +13 -13
- package/src/modules/bmb/workflows/create-workflow/steps/step-08-review.md +3 -3
- package/src/modules/bmb/workflows/create-workflow/steps/step-09-complete.md +1 -1
- package/src/modules/bmb/workflows/create-workflow/workflow.md +1 -1
- package/src/modules/bmb/workflows/edit-agent/steps/step-01-discover-intent.md +3 -3
- package/src/modules/bmb/workflows/edit-agent/steps/step-02-analyze-agent.md +13 -13
- package/src/modules/bmb/workflows/edit-agent/steps/step-03-propose-changes.md +5 -5
- package/src/modules/bmb/workflows/edit-agent/steps/step-04-apply-changes.md +3 -3
- package/src/modules/bmb/workflows/edit-agent/steps/step-05-validate.md +5 -5
- package/src/modules/bmb/workflows/edit-agent/workflow.md +1 -1
- package/src/modules/bmb/workflows/edit-workflow/steps/step-01-analyze.md +4 -4
- package/src/modules/bmb/workflows/edit-workflow/steps/step-02-discover.md +3 -3
- package/src/modules/bmb/workflows/edit-workflow/steps/step-03-improve.md +6 -6
- package/src/modules/bmb/workflows/edit-workflow/steps/step-04-validate.md +3 -3
- package/src/modules/bmb/workflows/edit-workflow/steps/step-05-compliance-check.md +3 -3
- package/src/modules/bmb/workflows/edit-workflow/workflow.md +1 -1
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-01-validate-goal.md +3 -3
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-02-workflow-validation.md +5 -5
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-03-step-validation.md +6 -6
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-04-file-validation.md +4 -4
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-05-intent-spectrum-validation.md +4 -4
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-06-web-subprocess-validation.md +4 -4
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-07-holistic-analysis.md +4 -4
- package/src/modules/bmb/workflows/workflow-compliance-check/steps/step-08-generate-report.md +3 -3
- package/src/modules/bmb/workflows/workflow-compliance-check/workflow.md +1 -1
- package/src/modules/bmb/workflows-legacy/edit-module/README.md +2 -2
- package/src/modules/bmb/workflows-legacy/edit-module/checklist.md +2 -2
- package/src/modules/bmb/workflows-legacy/edit-module/instructions.md +4 -4
- package/src/modules/bmb/workflows-legacy/edit-module/workflow.yaml +10 -10
- package/src/modules/bmb/workflows-legacy/module-brief/README.md +2 -2
- package/src/modules/bmb/workflows-legacy/module-brief/instructions.md +2 -2
- package/src/modules/bmb/workflows-legacy/module-brief/workflow.yaml +4 -4
- package/src/modules/bmgd/README.md +3 -2
- package/src/modules/bmgd/agents/game-architect.agent.yaml +6 -6
- package/src/modules/bmgd/agents/game-designer.agent.yaml +7 -7
- package/src/modules/bmgd/agents/game-dev.agent.yaml +9 -9
- package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +21 -21
- package/src/modules/bmgd/{_module-installer/install-config.yaml → module.yaml} +0 -1
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/instructions.md +1 -1
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.yaml +9 -9
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/instructions.md +1 -1
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.yaml +8 -8
- package/src/modules/bmgd/workflows/2-design/gdd/instructions-gdd.md +4 -4
- package/src/modules/bmgd/workflows/2-design/gdd/workflow.yaml +30 -30
- package/src/modules/bmgd/workflows/2-design/narrative/instructions-narrative.md +1 -1
- package/src/modules/bmgd/workflows/2-design/narrative/workflow.yaml +5 -5
- package/src/modules/bmgd/workflows/3-technical/game-architecture/instructions.md +1 -1
- package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/code-review/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/code-review/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/correct-course/checklist.md +1 -1
- package/src/modules/bmgd/workflows/4-production/correct-course/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/correct-course/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/create-story/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/create-story/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/dev-story/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/dev-story/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/checklist.md +1 -1
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/retrospective/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/retrospective/workflow.yaml +3 -3
- package/src/modules/bmgd/workflows/4-production/sprint-planning/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/sprint-planning/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/story-context/checklist.md +1 -1
- package/src/modules/bmgd/workflows/4-production/story-context/context-template.xml +1 -1
- package/src/modules/bmgd/workflows/4-production/story-context/instructions.md +2 -2
- package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/story-done/instructions.md +1 -1
- package/src/modules/bmgd/workflows/4-production/story-done/workflow.yaml +2 -2
- package/src/modules/bmgd/workflows/4-production/story-ready/instructions.md +1 -1
- package/src/modules/bmgd/workflows/4-production/story-ready/workflow.yaml +2 -2
- package/src/modules/bmm/_module-installer/installer.js +1 -1
- 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 +8 -8
- package/src/modules/bmm/agents/architect.agent.yaml +8 -8
- package/src/modules/bmm/agents/dev.agent.yaml +3 -3
- package/src/modules/bmm/agents/pm.agent.yaml +8 -8
- package/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml +5 -5
- package/src/modules/bmm/agents/sm.agent.yaml +9 -9
- package/src/modules/bmm/agents/tea.agent.yaml +13 -13
- package/src/modules/bmm/agents/tech-writer.agent.yaml +9 -9
- package/src/modules/bmm/agents/ux-designer.agent.yaml +6 -6
- package/src/modules/bmm/docs/README.md +0 -25
- package/src/modules/bmm/docs/agents-guide.md +4 -4
- package/src/modules/bmm/docs/brownfield-guide.md +4 -4
- package/src/modules/bmm/docs/enterprise-agentic-development.md +3 -3
- package/src/modules/bmm/docs/faq.md +1 -1
- package/src/modules/bmm/docs/party-mode.md +3 -3
- package/src/modules/bmm/docs/quick-start.md +1 -1
- package/src/modules/bmm/docs/test-architecture.md +3 -3
- package/src/modules/bmm/docs/workflow-document-project-reference.md +1 -1
- package/src/modules/bmm/docs/workflows-implementation.md +0 -100
- package/src/modules/bmm/{_module-installer/install-config.yaml → module.yaml} +1 -2
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-01-init.md +1 -1
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-01b-continue.md +1 -1
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-02-vision.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-03-users.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-04-metrics.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-05-scope.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/product-brief/steps/step-06-complete.md +5 -3
- package/src/modules/bmm/workflows/1-analysis/product-brief/workflow.md +2 -2
- package/src/modules/bmm/workflows/1-analysis/research/workflow.md +2 -2
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +4 -4
- package/src/modules/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +2 -2
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-01-init.md +1 -1
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-01b-continue.md +1 -1
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-02-discovery.md +3 -3
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-03-success.md +7 -7
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-04-journeys.md +8 -8
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-05-domain.md +12 -12
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-06-innovation.md +12 -12
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-07-project-type.md +10 -10
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-08-scoping.md +7 -7
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-09-functional.md +9 -9
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-10-nonfunctional.md +9 -9
- package/src/modules/bmm/workflows/2-plan-workflows/prd/steps/step-11-complete.md +1 -1
- package/src/modules/bmm/workflows/2-plan-workflows/prd/workflow.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-02-context.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-03-starter.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-04-decisions.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-05-patterns.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-06-structure.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-07-validation.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/architecture/steps/step-08-complete.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/architecture/workflow.md +2 -2
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +2 -2
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-01-document-discovery.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-02-prd-analysis.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-03-epic-coverage-validation.md +6 -6
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-04-ux-alignment.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-05-epic-quality-review.md +2 -2
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/steps/step-06-final-assessment.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/implementation-readiness/workflow.md +1 -1
- package/src/modules/bmm/workflows/4-implementation/code-review/instructions.xml +1 -1
- package/src/modules/bmm/workflows/4-implementation/code-review/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/correct-course/checklist.md +1 -1
- package/src/modules/bmm/workflows/4-implementation/correct-course/instructions.md +2 -2
- package/src/modules/bmm/workflows/4-implementation/correct-course/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/create-story/checklist.md +2 -2
- package/src/modules/bmm/workflows/4-implementation/create-story/instructions.xml +2 -2
- package/src/modules/bmm/workflows/4-implementation/create-story/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/dev-story/instructions.xml +1 -1
- package/src/modules/bmm/workflows/4-implementation/dev-story/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/retrospective/instructions.md +2 -2
- package/src/modules/bmm/workflows/4-implementation/retrospective/workflow.yaml +3 -3
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/instructions.md +2 -2
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/sprint-status/instructions.md +2 -2
- package/src/modules/bmm/workflows/4-implementation/sprint-status/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/bmad-quick-flow/create-tech-spec/workflow.yaml +5 -5
- package/src/modules/bmm/workflows/bmad-quick-flow/quick-dev/workflow.yaml +7 -7
- package/src/modules/bmm/workflows/diagrams/create-dataflow/instructions.md +1 -1
- package/src/modules/bmm/workflows/diagrams/create-dataflow/workflow.yaml +5 -5
- package/src/modules/bmm/workflows/diagrams/create-diagram/instructions.md +2 -2
- package/src/modules/bmm/workflows/diagrams/create-diagram/workflow.yaml +5 -5
- package/src/modules/bmm/workflows/diagrams/create-flowchart/instructions.md +2 -2
- package/src/modules/bmm/workflows/diagrams/create-flowchart/workflow.yaml +5 -5
- package/src/modules/bmm/workflows/diagrams/create-wireframe/instructions.md +1 -1
- package/src/modules/bmm/workflows/diagrams/create-wireframe/workflow.yaml +5 -5
- package/src/modules/bmm/workflows/document-project/instructions.md +5 -5
- package/src/modules/bmm/workflows/document-project/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/document-project/workflows/deep-dive.yaml +5 -5
- package/src/modules/bmm/workflows/document-project/workflows/full-scan.yaml +5 -5
- package/src/modules/bmm/workflows/generate-project-context/steps/step-02-generate.md +2 -2
- package/src/modules/bmm/workflows/generate-project-context/workflow.md +2 -2
- package/src/modules/bmm/workflows/testarch/atdd/instructions.md +2 -2
- package/src/modules/bmm/workflows/testarch/atdd/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/automate/instructions.md +2 -2
- package/src/modules/bmm/workflows/testarch/automate/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/ci/instructions.md +1 -1
- package/src/modules/bmm/workflows/testarch/ci/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/framework/instructions.md +3 -3
- package/src/modules/bmm/workflows/testarch/framework/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/nfr-assess/instructions.md +1 -1
- package/src/modules/bmm/workflows/testarch/nfr-assess/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/test-design/instructions.md +3 -3
- package/src/modules/bmm/workflows/testarch/test-design/test-design-template.md +1 -1
- package/src/modules/bmm/workflows/testarch/test-design/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/test-review/instructions.md +1 -1
- package/src/modules/bmm/workflows/testarch/test-review/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/testarch/trace/instructions.md +6 -6
- package/src/modules/bmm/workflows/testarch/trace/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/workflow-status/init/instructions.md +1 -1
- package/src/modules/bmm/workflows/workflow-status/init/workflow.yaml +4 -4
- package/src/modules/bmm/workflows/workflow-status/instructions.md +3 -3
- package/src/modules/bmm/workflows/workflow-status/project-levels.yaml +1 -1
- package/src/modules/bmm/workflows/workflow-status/workflow.yaml +2 -2
- package/src/modules/cis/README.md +1 -1
- 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 +4 -4
- package/src/modules/cis/agents/creative-problem-solver.agent.yaml +4 -4
- package/src/modules/cis/agents/design-thinking-coach.agent.yaml +4 -4
- package/src/modules/cis/agents/innovation-strategist.agent.yaml +4 -4
- package/src/modules/cis/agents/presentation-master.agent.yaml +3 -3
- package/src/modules/cis/agents/storyteller.agent.yaml +4 -4
- package/src/modules/cis/{_module-installer/install-config.yaml → module.yaml} +0 -1
- package/src/modules/cis/workflows/README.md +1 -1
- package/src/modules/cis/workflows/design-thinking/instructions.md +2 -2
- package/src/modules/cis/workflows/design-thinking/workflow.yaml +7 -7
- package/src/modules/cis/workflows/innovation-strategy/instructions.md +2 -2
- package/src/modules/cis/workflows/innovation-strategy/workflow.yaml +7 -7
- package/src/modules/cis/workflows/problem-solving/instructions.md +2 -2
- package/src/modules/cis/workflows/problem-solving/workflow.yaml +7 -7
- package/src/modules/cis/workflows/storytelling/instructions.md +2 -2
- package/src/modules/cis/workflows/storytelling/workflow.yaml +7 -7
- package/src/utility/models/agent-activation-ide.xml +3 -3
- package/src/utility/models/agent-activation-web.xml +3 -3
- package/src/utility/models/fragments/activation-steps.xml +1 -1
- package/src/utility/models/fragments/handler-validate-workflow.xml +1 -1
- package/src/utility/models/fragments/handler-workflow.xml +1 -1
- package/src/utility/models/fragments/web-bundle-activation-steps.xml +3 -3
- package/tools/cli/README.md +23 -23
- package/tools/cli/bundlers/web-bundler.js +23 -33
- package/tools/cli/commands/build.js +5 -5
- package/tools/cli/installers/lib/core/config-collector.js +33 -20
- package/tools/cli/installers/lib/core/custom-module-cache.js +251 -0
- package/tools/cli/installers/lib/core/detector.js +8 -4
- package/tools/cli/installers/lib/core/installer.js +885 -98
- package/tools/cli/installers/lib/core/manifest-generator.js +177 -14
- package/tools/cli/installers/lib/core/manifest.js +47 -0
- package/tools/cli/installers/lib/custom/handler.js +147 -20
- package/tools/cli/installers/lib/ide/_base-ide.js +14 -14
- package/tools/cli/installers/lib/ide/gemini.js +4 -4
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +3 -3
- package/tools/cli/installers/lib/ide/templates/agent-command-template.md +1 -1
- package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +3 -3
- package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +3 -3
- package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +1 -1
- package/tools/cli/installers/lib/modules/manager.js +99 -53
- package/tools/cli/lib/agent/compiler.js +6 -14
- package/tools/cli/lib/agent/installer.js +4 -3
- package/tools/cli/lib/cli-utils.js +21 -4
- package/tools/cli/lib/ui.js +496 -12
- package/tools/cli/regenerate-manifests.js +3 -4
- package/tools/maintainer/review-pr-README.md +55 -0
- package/tools/maintainer/review-pr.md +242 -0
- package/tools/migrate-custom-module-paths.js +124 -0
- package/bmad-method-6.0.0-alpha.14.tgz +0 -0
- package/docs/custom-agent-installation.md +0 -137
- package/example-custom-content/README.md +0 -4
- package/example-custom-content/agents/commit-poet/commit-poet.agent.yaml +0 -129
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/instructions.md +0 -70
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/bundlers.md +0 -111
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/deploy.md +0 -70
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/docs.md +0 -114
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +0 -134
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +0 -161
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/knowledge/tests.md +0 -103
- package/example-custom-content/agents/toolsmith/toolsmith-sidecar/memories.md +0 -17
- package/example-custom-content/agents/toolsmith/toolsmith.agent.yaml +0 -109
- package/example-custom-content/custom.yaml +0 -3
- package/example-custom-content/workflows/quiz-master/steps/step-01-init.md +0 -168
- package/example-custom-content/workflows/quiz-master/steps/step-02-q1.md +0 -155
- package/example-custom-content/workflows/quiz-master/steps/step-03-q2.md +0 -89
- package/example-custom-content/workflows/quiz-master/steps/step-04-q3.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-05-q4.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-06-q5.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-07-q6.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-08-q7.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-09-q8.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-10-q9.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-11-q10.md +0 -36
- package/example-custom-content/workflows/quiz-master/steps/step-12-results.md +0 -150
- package/example-custom-content/workflows/quiz-master/templates/csv-headers.template +0 -1
- package/example-custom-content/workflows/quiz-master/workflow-plan-quiz-master.md +0 -269
- package/example-custom-content/workflows/quiz-master/workflow.md +0 -54
- package/example-custom-content/workflows/wassup/workflow.md +0 -26
- package/example-custom-module/mwm/README.md +0 -4
- package/example-custom-module/mwm/_module-installer/install-config.yaml +0 -27
- package/example-custom-module/mwm/agents/cbt-coach/cbt-coach-sidecar/cognitive-distortions.md +0 -47
- package/example-custom-module/mwm/agents/cbt-coach/cbt-coach-sidecar/thought-records.md +0 -17
- package/example-custom-module/mwm/agents/cbt-coach/cbt-coach.agent.yaml +0 -150
- package/example-custom-module/mwm/agents/crisis-navigator.agent.yaml +0 -137
- package/example-custom-module/mwm/agents/meditation-guide.agent.yaml +0 -137
- package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/insights.md +0 -13
- package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/instructions.md +0 -30
- package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/memories.md +0 -13
- package/example-custom-module/mwm/agents/wellness-companion/wellness-companion-sidecar/patterns.md +0 -17
- package/example-custom-module/mwm/agents/wellness-companion/wellness-companion.agent.yaml +0 -124
- package/example-custom-module/mwm/workflows/cbt-thought-record/README.md +0 -31
- package/example-custom-module/mwm/workflows/cbt-thought-record/workflow.md +0 -45
- package/example-custom-module/mwm/workflows/crisis-support/README.md +0 -31
- package/example-custom-module/mwm/workflows/crisis-support/workflow.md +0 -45
- package/example-custom-module/mwm/workflows/daily-checkin/README.md +0 -32
- package/example-custom-module/mwm/workflows/daily-checkin/workflow.md +0 -45
- package/example-custom-module/mwm/workflows/guided-meditation/README.md +0 -31
- package/example-custom-module/mwm/workflows/guided-meditation/workflow.md +0 -45
- package/example-custom-module/mwm/workflows/wellness-journal/README.md +0 -31
- package/example-custom-module/mwm/workflows/wellness-journal/workflow.md +0 -45
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete BMAD installation
|
|
15
15
|
* @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js
|
|
16
16
|
* @entrypoints Called by install.js command via installer.install(config)
|
|
17
|
-
* @patterns Injection point processing (AgentVibes), placeholder replacement (
|
|
17
|
+
* @patterns Injection point processing (AgentVibes), placeholder replacement (.bmad), module dependency resolution
|
|
18
18
|
* @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement()
|
|
19
19
|
*/
|
|
20
20
|
|
|
@@ -22,6 +22,7 @@ const path = require('node:path');
|
|
|
22
22
|
const fs = require('fs-extra');
|
|
23
23
|
const chalk = require('chalk');
|
|
24
24
|
const ora = require('ora');
|
|
25
|
+
const inquirer = require('inquirer');
|
|
25
26
|
const { Detector } = require('./detector');
|
|
26
27
|
const { Manifest } = require('./manifest');
|
|
27
28
|
const { ModuleManager } = require('../modules/manager');
|
|
@@ -38,6 +39,7 @@ const { CLIUtils } = require('../../../lib/cli-utils');
|
|
|
38
39
|
const { ManifestGenerator } = require('./manifest-generator');
|
|
39
40
|
const { IdeConfigManager } = require('./ide-config-manager');
|
|
40
41
|
const { replaceAgentSidecarFolders } = require('./post-install-sidecar-replacement');
|
|
42
|
+
const { CustomHandler } = require('../custom/handler');
|
|
41
43
|
|
|
42
44
|
class Installer {
|
|
43
45
|
constructor() {
|
|
@@ -65,7 +67,7 @@ class Installer {
|
|
|
65
67
|
// Check if project directory exists
|
|
66
68
|
if (!(await fs.pathExists(projectDir))) {
|
|
67
69
|
// Project doesn't exist yet, return default
|
|
68
|
-
return path.join(projectDir, 'bmad');
|
|
70
|
+
return path.join(projectDir, '.bmad');
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
|
|
@@ -87,13 +89,13 @@ class Installer {
|
|
|
87
89
|
|
|
88
90
|
// No V6+ installation found, return default
|
|
89
91
|
// This will be used for new installations
|
|
90
|
-
return path.join(projectDir, 'bmad');
|
|
92
|
+
return path.join(projectDir, '.bmad');
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
/**
|
|
94
96
|
* @function copyFileWithPlaceholderReplacement
|
|
95
97
|
* @intent Copy files from BMAD source to installation directory with dynamic content transformation
|
|
96
|
-
* @why Enables installation-time customization:
|
|
98
|
+
* @why Enables installation-time customization: .bmad replacement + optional AgentVibes TTS injection
|
|
97
99
|
* @param {string} sourcePath - Absolute path to source file in BMAD repository
|
|
98
100
|
* @param {string} targetPath - Absolute path to destination file in user's project
|
|
99
101
|
* @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad')
|
|
@@ -103,11 +105,6 @@ class Installer {
|
|
|
103
105
|
* @calledby installCore(), installModule(), IDE installers during file vendoring
|
|
104
106
|
* @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy()
|
|
105
107
|
*
|
|
106
|
-
* AI NOTE: This is the core transformation pipeline for ALL BMAD installation file copies.
|
|
107
|
-
* It performs two transformations in sequence:
|
|
108
|
-
* 1. {bmad_folder} → user's custom folder name (e.g., ".bmad" or "bmad")
|
|
109
|
-
* 2. <!-- TTS_INJECTION:* --> → TTS bash calls (if enabled) OR stripped (if disabled)
|
|
110
|
-
*
|
|
111
108
|
* The injection point processing enables loose coupling between BMAD and TTS providers:
|
|
112
109
|
* - BMAD source contains injection markers (not actual TTS code)
|
|
113
110
|
* - At install-time, markers are replaced OR removed based on user preference
|
|
@@ -129,7 +126,7 @@ class Installer {
|
|
|
129
126
|
*/
|
|
130
127
|
async copyFileWithPlaceholderReplacement(sourcePath, targetPath, bmadFolderName) {
|
|
131
128
|
// List of text file extensions that should have placeholder replacement
|
|
132
|
-
const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv'];
|
|
129
|
+
const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv', '.xml'];
|
|
133
130
|
const ext = path.extname(sourcePath).toLowerCase();
|
|
134
131
|
|
|
135
132
|
// Check if this is a text file that might contain placeholders
|
|
@@ -138,16 +135,6 @@ class Installer {
|
|
|
138
135
|
// Read the file content
|
|
139
136
|
let content = await fs.readFile(sourcePath, 'utf8');
|
|
140
137
|
|
|
141
|
-
// Replace {bmad_folder} placeholder with actual folder name
|
|
142
|
-
if (content.includes('{bmad_folder}')) {
|
|
143
|
-
content = content.replaceAll('{bmad_folder}', bmadFolderName);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
|
|
147
|
-
if (content.includes('{*bmad_folder*}')) {
|
|
148
|
-
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
138
|
// Process AgentVibes injection points (pass targetPath for tracking)
|
|
152
139
|
content = this.processTTSInjectionPoints(content, targetPath);
|
|
153
140
|
|
|
@@ -406,7 +393,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
406
393
|
* @param {string[]} config.ides - IDEs to configure
|
|
407
394
|
* @param {boolean} config.skipIde - Skip IDE configuration
|
|
408
395
|
*/
|
|
409
|
-
async install(
|
|
396
|
+
async install(originalConfig) {
|
|
397
|
+
// Clone config to avoid mutating the caller's object
|
|
398
|
+
const config = { ...originalConfig };
|
|
399
|
+
|
|
410
400
|
// Display BMAD logo
|
|
411
401
|
CLIUtils.displayLogo();
|
|
412
402
|
|
|
@@ -434,12 +424,56 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
434
424
|
// Quick update already collected all configs, use them directly
|
|
435
425
|
moduleConfigs = this.configCollector.collectedConfig;
|
|
436
426
|
} else {
|
|
427
|
+
// Build custom module paths map from customContent
|
|
428
|
+
const customModulePaths = new Map();
|
|
429
|
+
|
|
430
|
+
// Handle selectedFiles (from existing install path or manual directory input)
|
|
431
|
+
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
|
432
|
+
const customHandler = new CustomHandler();
|
|
433
|
+
for (const customFile of config.customContent.selectedFiles) {
|
|
434
|
+
const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory));
|
|
435
|
+
if (customInfo && customInfo.id) {
|
|
436
|
+
customModulePaths.set(customInfo.id, customInfo.path);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Handle cachedModules (from new install path where modules are cached)
|
|
442
|
+
// Only include modules that were actually selected for installation
|
|
443
|
+
if (config.customContent && config.customContent.cachedModules) {
|
|
444
|
+
// Get selected cached module IDs (if available)
|
|
445
|
+
const selectedCachedIds = config.customContent.selectedCachedModules || [];
|
|
446
|
+
// If no selection info, include all cached modules (for backward compatibility)
|
|
447
|
+
const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected;
|
|
448
|
+
|
|
449
|
+
for (const cachedModule of config.customContent.cachedModules) {
|
|
450
|
+
// For cached modules, the path is the cachePath which contains the module.yaml
|
|
451
|
+
if (
|
|
452
|
+
cachedModule.id &&
|
|
453
|
+
cachedModule.cachePath && // Include if selected or if we should include all
|
|
454
|
+
(shouldIncludeAll || selectedCachedIds.includes(cachedModule.id))
|
|
455
|
+
) {
|
|
456
|
+
customModulePaths.set(cachedModule.id, cachedModule.cachePath);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Get list of all modules including custom modules
|
|
462
|
+
const allModulesForConfig = [...(config.modules || [])];
|
|
463
|
+
for (const [moduleId] of customModulePaths) {
|
|
464
|
+
if (!allModulesForConfig.includes(moduleId)) {
|
|
465
|
+
allModulesForConfig.push(moduleId);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
437
469
|
// Regular install - collect configurations (core was already collected in UI.promptInstall if interactive)
|
|
438
|
-
moduleConfigs = await this.configCollector.collectAllConfigurations(
|
|
470
|
+
moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
|
|
471
|
+
customModulePaths,
|
|
472
|
+
});
|
|
439
473
|
}
|
|
440
474
|
|
|
441
|
-
//
|
|
442
|
-
const bmadFolderName =
|
|
475
|
+
// Always use .bmad as the folder name
|
|
476
|
+
const bmadFolderName = '.bmad';
|
|
443
477
|
this.bmadFolderName = bmadFolderName; // Store for use in other methods
|
|
444
478
|
|
|
445
479
|
// Store AgentVibes configuration for injection point processing
|
|
@@ -458,7 +492,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
458
492
|
// Resolve target directory (path.resolve handles platform differences)
|
|
459
493
|
const projectDir = path.resolve(config.directory);
|
|
460
494
|
|
|
461
|
-
// Check if bmad_folder has changed from existing installation (only if project dir exists)
|
|
462
495
|
let existingBmadDir = null;
|
|
463
496
|
let existingBmadFolderName = null;
|
|
464
497
|
|
|
@@ -467,54 +500,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
467
500
|
existingBmadFolderName = path.basename(existingBmadDir);
|
|
468
501
|
}
|
|
469
502
|
|
|
470
|
-
const targetBmadDir = path.join(projectDir, bmadFolderName);
|
|
471
|
-
|
|
472
|
-
// If bmad_folder changed during update/upgrade, back up old folder and do fresh install
|
|
473
|
-
if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== bmadFolderName) {
|
|
474
|
-
spinner.stop();
|
|
475
|
-
console.log(chalk.yellow(`\n⚠️ bmad_folder has changed: ${existingBmadFolderName} → ${bmadFolderName}`));
|
|
476
|
-
console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
|
|
477
|
-
|
|
478
|
-
const inquirer = require('inquirer');
|
|
479
|
-
const { confirmFreshInstall } = await inquirer.prompt([
|
|
480
|
-
{
|
|
481
|
-
type: 'confirm',
|
|
482
|
-
name: 'confirmFreshInstall',
|
|
483
|
-
message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
|
|
484
|
-
default: true,
|
|
485
|
-
},
|
|
486
|
-
]);
|
|
487
|
-
|
|
488
|
-
if (!confirmFreshInstall) {
|
|
489
|
-
console.log(chalk.yellow('Installation cancelled.'));
|
|
490
|
-
return { success: false, cancelled: true };
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
spinner.start('Backing up existing installation...');
|
|
494
|
-
|
|
495
|
-
// Find a unique backup name
|
|
496
|
-
let backupDir = `${existingBmadDir}-bak`;
|
|
497
|
-
let counter = 1;
|
|
498
|
-
while (await fs.pathExists(backupDir)) {
|
|
499
|
-
backupDir = `${existingBmadDir}-bak-${counter}`;
|
|
500
|
-
counter++;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Rename the old folder to backup
|
|
504
|
-
await fs.move(existingBmadDir, backupDir);
|
|
505
|
-
|
|
506
|
-
spinner.succeed(`Backed up ${existingBmadFolderName} → ${path.basename(backupDir)}`);
|
|
507
|
-
console.log(chalk.cyan('\n📋 Important:'));
|
|
508
|
-
console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
|
|
509
|
-
console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
|
|
510
|
-
console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
|
|
511
|
-
console.log(chalk.dim(` - To the new location:`));
|
|
512
|
-
console.log(chalk.dim(` ${bmadFolderName}/_cfg/`));
|
|
513
|
-
console.log('');
|
|
514
|
-
|
|
515
|
-
spinner.start('Starting fresh installation...');
|
|
516
|
-
}
|
|
517
|
-
|
|
518
503
|
// Create a project directory if it doesn't exist (user already confirmed)
|
|
519
504
|
if (!(await fs.pathExists(projectDir))) {
|
|
520
505
|
spinner.text = 'Creating installation directory...';
|
|
@@ -750,13 +735,80 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
750
735
|
spinner.text = 'Creating directory structure...';
|
|
751
736
|
await this.createDirectoryStructure(bmadDir);
|
|
752
737
|
|
|
753
|
-
//
|
|
754
|
-
spinner.text = 'Resolving dependencies...';
|
|
738
|
+
// Get project root
|
|
755
739
|
const projectRoot = getProjectRoot();
|
|
756
|
-
|
|
740
|
+
|
|
741
|
+
// Step 1: Install core module first (if requested)
|
|
742
|
+
if (config.installCore) {
|
|
743
|
+
spinner.start('Installing BMAD core...');
|
|
744
|
+
await this.installCoreWithDependencies(bmadDir, { core: {} });
|
|
745
|
+
spinner.succeed('Core installed');
|
|
746
|
+
|
|
747
|
+
// Generate core config file
|
|
748
|
+
await this.generateModuleConfigs(bmadDir, { core: config.coreConfig || {} });
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Custom content is already handled in UI before module selection
|
|
752
|
+
let finalCustomContent = config.customContent;
|
|
753
|
+
|
|
754
|
+
// Step 3: Prepare modules list including cached custom modules
|
|
755
|
+
let allModules = [...(config.modules || [])];
|
|
756
|
+
|
|
757
|
+
// During quick update, we might have custom module sources from the manifest
|
|
758
|
+
if (config._customModuleSources) {
|
|
759
|
+
// Add custom modules from stored sources
|
|
760
|
+
for (const [moduleId, customInfo] of config._customModuleSources) {
|
|
761
|
+
if (!allModules.includes(moduleId) && (await fs.pathExists(customInfo.sourcePath))) {
|
|
762
|
+
allModules.push(moduleId);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Add cached custom modules
|
|
768
|
+
if (finalCustomContent && finalCustomContent.cachedModules) {
|
|
769
|
+
for (const cachedModule of finalCustomContent.cachedModules) {
|
|
770
|
+
if (!allModules.includes(cachedModule.id)) {
|
|
771
|
+
allModules.push(cachedModule.id);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Regular custom content from user input (non-cached)
|
|
777
|
+
if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
|
778
|
+
// Add custom modules to the installation list
|
|
779
|
+
const customHandler = new CustomHandler();
|
|
780
|
+
for (const customFile of finalCustomContent.selectedFiles) {
|
|
781
|
+
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
782
|
+
if (customInfo && customInfo.id) {
|
|
783
|
+
allModules.push(customInfo.id);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Don't include core again if already installed
|
|
789
|
+
if (config.installCore) {
|
|
790
|
+
allModules = allModules.filter((m) => m !== 'core');
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const modulesToInstall = allModules;
|
|
757
794
|
|
|
758
795
|
// For dependency resolution, we need to pass the project root
|
|
759
|
-
|
|
796
|
+
// Create a temporary module manager that knows about custom content locations
|
|
797
|
+
const tempModuleManager = new ModuleManager({
|
|
798
|
+
scanProjectForModules: true,
|
|
799
|
+
bmadDir: bmadDir, // Pass bmadDir so we can check cache
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
// Make sure custom modules are discoverable
|
|
803
|
+
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
|
804
|
+
// The dependency resolver needs to know about these modules
|
|
805
|
+
// We'll handle custom modules separately in the installation loop
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
|
|
809
|
+
verbose: config.verbose,
|
|
810
|
+
moduleManager: tempModuleManager,
|
|
811
|
+
});
|
|
760
812
|
|
|
761
813
|
if (config.verbose) {
|
|
762
814
|
spinner.succeed('Dependencies resolved');
|
|
@@ -764,24 +816,182 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
764
816
|
spinner.succeed('Dependencies resolved');
|
|
765
817
|
}
|
|
766
818
|
|
|
767
|
-
//
|
|
768
|
-
if (config.installCore || resolution.byModule.core) {
|
|
769
|
-
spinner.start('Installing BMAD core...');
|
|
770
|
-
await this.installCoreWithDependencies(bmadDir, resolution.byModule.core);
|
|
771
|
-
spinner.succeed('Core installed');
|
|
772
|
-
}
|
|
819
|
+
// Core is already installed above, skip if included in resolution
|
|
773
820
|
|
|
774
821
|
// Install modules with their dependencies
|
|
775
|
-
if (
|
|
776
|
-
|
|
822
|
+
if (allModules && allModules.length > 0) {
|
|
823
|
+
const installedModuleNames = new Set();
|
|
824
|
+
|
|
825
|
+
for (const moduleName of allModules) {
|
|
826
|
+
// Skip if already installed
|
|
827
|
+
if (installedModuleNames.has(moduleName)) {
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
installedModuleNames.add(moduleName);
|
|
831
|
+
|
|
777
832
|
spinner.start(`Installing module: ${moduleName}...`);
|
|
778
|
-
|
|
833
|
+
|
|
834
|
+
// Check if this is a custom module
|
|
835
|
+
let isCustomModule = false;
|
|
836
|
+
let customInfo = null;
|
|
837
|
+
let useCache = false;
|
|
838
|
+
|
|
839
|
+
// First check if we have a cached version
|
|
840
|
+
if (finalCustomContent && finalCustomContent.cachedModules) {
|
|
841
|
+
const cachedModule = finalCustomContent.cachedModules.find((m) => m.id === moduleName);
|
|
842
|
+
if (cachedModule) {
|
|
843
|
+
isCustomModule = true;
|
|
844
|
+
customInfo = {
|
|
845
|
+
id: moduleName,
|
|
846
|
+
path: cachedModule.cachePath,
|
|
847
|
+
config: {},
|
|
848
|
+
};
|
|
849
|
+
useCache = true;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// Then check if we have custom module sources from the manifest (for quick update)
|
|
854
|
+
if (!isCustomModule && config._customModuleSources && config._customModuleSources.has(moduleName)) {
|
|
855
|
+
customInfo = config._customModuleSources.get(moduleName);
|
|
856
|
+
isCustomModule = true;
|
|
857
|
+
|
|
858
|
+
// Check if this is a cached module (source path starts with _cfg)
|
|
859
|
+
if (customInfo.sourcePath && (customInfo.sourcePath.startsWith('_cfg') || customInfo.sourcePath.includes('_cfg/custom'))) {
|
|
860
|
+
useCache = true;
|
|
861
|
+
// Make sure we have the right path structure
|
|
862
|
+
if (!customInfo.path) {
|
|
863
|
+
customInfo.path = customInfo.sourcePath;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Finally check regular custom content
|
|
869
|
+
if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
|
870
|
+
const customHandler = new CustomHandler();
|
|
871
|
+
for (const customFile of finalCustomContent.selectedFiles) {
|
|
872
|
+
const info = await customHandler.getCustomInfo(customFile, projectDir);
|
|
873
|
+
if (info && info.id === moduleName) {
|
|
874
|
+
isCustomModule = true;
|
|
875
|
+
customInfo = info;
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (isCustomModule && customInfo) {
|
|
882
|
+
// Install custom module using CustomHandler but as a proper module
|
|
883
|
+
const customHandler = new CustomHandler();
|
|
884
|
+
|
|
885
|
+
// Install to module directory instead of custom directory
|
|
886
|
+
const moduleTargetPath = path.join(bmadDir, moduleName);
|
|
887
|
+
await fs.ensureDir(moduleTargetPath);
|
|
888
|
+
|
|
889
|
+
// Get collected config for this custom module (from module.yaml prompts)
|
|
890
|
+
const collectedModuleConfig = moduleConfigs[moduleName] || {};
|
|
891
|
+
|
|
892
|
+
const result = await customHandler.install(
|
|
893
|
+
customInfo.path,
|
|
894
|
+
path.join(bmadDir, 'temp-custom'),
|
|
895
|
+
{ ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig, _bmadDir: bmadDir },
|
|
896
|
+
(filePath) => {
|
|
897
|
+
// Track installed files with correct path
|
|
898
|
+
const relativePath = path.relative(path.join(bmadDir, 'temp-custom'), filePath);
|
|
899
|
+
const finalPath = path.join(moduleTargetPath, relativePath);
|
|
900
|
+
this.installedFiles.push(finalPath);
|
|
901
|
+
},
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
// Move from temp-custom to actual module directory
|
|
905
|
+
const tempCustomPath = path.join(bmadDir, 'temp-custom');
|
|
906
|
+
if (await fs.pathExists(tempCustomPath)) {
|
|
907
|
+
const customDir = path.join(tempCustomPath, 'custom');
|
|
908
|
+
if (await fs.pathExists(customDir)) {
|
|
909
|
+
// Move contents to module directory
|
|
910
|
+
const items = await fs.readdir(customDir);
|
|
911
|
+
const movedItems = [];
|
|
912
|
+
try {
|
|
913
|
+
for (const item of items) {
|
|
914
|
+
const srcPath = path.join(customDir, item);
|
|
915
|
+
const destPath = path.join(moduleTargetPath, item);
|
|
916
|
+
|
|
917
|
+
// If destination exists, remove it first (or we could merge)
|
|
918
|
+
if (await fs.pathExists(destPath)) {
|
|
919
|
+
await fs.remove(destPath);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
await fs.move(srcPath, destPath);
|
|
923
|
+
movedItems.push({ src: srcPath, dest: destPath });
|
|
924
|
+
}
|
|
925
|
+
} catch (moveError) {
|
|
926
|
+
// Rollback: restore any successfully moved items
|
|
927
|
+
for (const moved of movedItems) {
|
|
928
|
+
try {
|
|
929
|
+
await fs.move(moved.dest, moved.src);
|
|
930
|
+
} catch {
|
|
931
|
+
// Best-effort rollback - log if it fails
|
|
932
|
+
console.error(`Failed to rollback ${moved.dest} during cleanup`);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
throw new Error(`Failed to move custom module files: ${moveError.message}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
await fs.remove(tempCustomPath);
|
|
940
|
+
} catch (cleanupError) {
|
|
941
|
+
// Non-fatal: temp directory cleanup failed but files were moved successfully
|
|
942
|
+
console.warn(`Warning: Could not clean up temp directory: ${cleanupError.message}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Create module config (include collected config from module.yaml prompts)
|
|
947
|
+
await this.generateModuleConfigs(bmadDir, {
|
|
948
|
+
[moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig },
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
// Store custom module info for later manifest update
|
|
952
|
+
if (!config._customModulesToTrack) {
|
|
953
|
+
config._customModulesToTrack = [];
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// For cached modules, use appropriate path handling
|
|
957
|
+
let sourcePath;
|
|
958
|
+
if (useCache) {
|
|
959
|
+
// Check if we have cached modules info (from initial install)
|
|
960
|
+
if (finalCustomContent && finalCustomContent.cachedModules) {
|
|
961
|
+
sourcePath = finalCustomContent.cachedModules.find((m) => m.id === moduleName)?.relativePath;
|
|
962
|
+
} else {
|
|
963
|
+
// During update, the sourcePath is already cache-relative if it starts with _cfg
|
|
964
|
+
sourcePath =
|
|
965
|
+
customInfo.sourcePath && customInfo.sourcePath.startsWith('_cfg')
|
|
966
|
+
? customInfo.sourcePath
|
|
967
|
+
: path.relative(bmadDir, customInfo.path || customInfo.sourcePath);
|
|
968
|
+
}
|
|
969
|
+
} else {
|
|
970
|
+
sourcePath = path.resolve(customInfo.path || customInfo.sourcePath);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
config._customModulesToTrack.push({
|
|
974
|
+
id: customInfo.id,
|
|
975
|
+
name: customInfo.name,
|
|
976
|
+
sourcePath: sourcePath,
|
|
977
|
+
installDate: new Date().toISOString(),
|
|
978
|
+
});
|
|
979
|
+
} else {
|
|
980
|
+
// Regular module installation
|
|
981
|
+
// Special case for core module
|
|
982
|
+
if (moduleName === 'core') {
|
|
983
|
+
await this.installCoreWithDependencies(bmadDir, resolution.byModule[moduleName]);
|
|
984
|
+
} else {
|
|
985
|
+
await this.installModuleWithDependencies(moduleName, bmadDir, resolution.byModule[moduleName]);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
779
989
|
spinner.succeed(`Module installed: ${moduleName}`);
|
|
780
990
|
}
|
|
781
991
|
|
|
782
992
|
// Install partial modules (only dependencies)
|
|
783
993
|
for (const [module, files] of Object.entries(resolution.byModule)) {
|
|
784
|
-
if (!
|
|
994
|
+
if (!allModules.includes(module) && module !== 'core') {
|
|
785
995
|
const totalFiles =
|
|
786
996
|
files.agents.length +
|
|
787
997
|
files.tasks.length +
|
|
@@ -798,6 +1008,70 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
798
1008
|
}
|
|
799
1009
|
}
|
|
800
1010
|
|
|
1011
|
+
// Install custom content if provided AND selected
|
|
1012
|
+
// Process custom content that wasn't installed as modules
|
|
1013
|
+
// This is now handled in the module installation loop above
|
|
1014
|
+
// This section is kept for backward compatibility with any custom content
|
|
1015
|
+
// that doesn't have a module structure
|
|
1016
|
+
const remainingCustomContent = [];
|
|
1017
|
+
if (
|
|
1018
|
+
config.customContent &&
|
|
1019
|
+
config.customContent.hasCustomContent &&
|
|
1020
|
+
config.customContent.customPath &&
|
|
1021
|
+
config.customContent.selected &&
|
|
1022
|
+
config.customContent.selectedFiles
|
|
1023
|
+
) {
|
|
1024
|
+
// Filter out custom modules that were already installed
|
|
1025
|
+
const customHandler = new CustomHandler();
|
|
1026
|
+
for (const customFile of config.customContent.selectedFiles) {
|
|
1027
|
+
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
1028
|
+
|
|
1029
|
+
// Skip if this was installed as a module
|
|
1030
|
+
if (!customInfo || !customInfo.id || !allModules.includes(customInfo.id)) {
|
|
1031
|
+
remainingCustomContent.push(customFile);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (remainingCustomContent.length > 0) {
|
|
1037
|
+
spinner.start('Installing remaining custom content...');
|
|
1038
|
+
const customHandler = new CustomHandler();
|
|
1039
|
+
|
|
1040
|
+
// Use the remaining files
|
|
1041
|
+
const customFiles = remainingCustomContent;
|
|
1042
|
+
|
|
1043
|
+
if (customFiles.length > 0) {
|
|
1044
|
+
console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
|
|
1045
|
+
for (const customFile of customFiles) {
|
|
1046
|
+
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
1047
|
+
if (customInfo) {
|
|
1048
|
+
console.log(chalk.dim(` • ${customInfo.name} (${customInfo.relativePath})`));
|
|
1049
|
+
|
|
1050
|
+
// Install the custom content
|
|
1051
|
+
const result = await customHandler.install(
|
|
1052
|
+
customInfo.path,
|
|
1053
|
+
bmadDir,
|
|
1054
|
+
{ ...config.coreConfig, ...customInfo.config },
|
|
1055
|
+
(filePath) => {
|
|
1056
|
+
// Track installed files
|
|
1057
|
+
this.installedFiles.push(filePath);
|
|
1058
|
+
},
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
if (result.errors.length > 0) {
|
|
1062
|
+
console.log(chalk.yellow(` ⚠️ ${result.errors.length} error(s) occurred`));
|
|
1063
|
+
for (const error of result.errors) {
|
|
1064
|
+
console.log(chalk.dim(` - ${error}`));
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
console.log(chalk.green(` ✓ Installed ${result.agentsInstalled} agents, ${result.workflowsInstalled} workflows`));
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
spinner.succeed('Custom content installed');
|
|
1073
|
+
}
|
|
1074
|
+
|
|
801
1075
|
// Generate clean config.yaml files for each installed module
|
|
802
1076
|
spinner.start('Generating module configurations...');
|
|
803
1077
|
await this.generateModuleConfigs(bmadDir, moduleConfigs);
|
|
@@ -820,14 +1094,37 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
820
1094
|
spinner.start('Generating workflow and agent manifests...');
|
|
821
1095
|
const manifestGen = new ManifestGenerator();
|
|
822
1096
|
|
|
823
|
-
//
|
|
824
|
-
|
|
1097
|
+
// For quick update, we need ALL installed modules in the manifest
|
|
1098
|
+
// Not just the ones being updated
|
|
1099
|
+
const allModulesForManifest = config._quickUpdate
|
|
1100
|
+
? config._existingModules || allModules || []
|
|
1101
|
+
: config._preserveModules
|
|
1102
|
+
? [...allModules, ...config._preserveModules]
|
|
1103
|
+
: allModules || [];
|
|
1104
|
+
|
|
1105
|
+
// For regular installs (including when called from quick update), use what we have
|
|
1106
|
+
let modulesForCsvPreserve;
|
|
1107
|
+
if (config._quickUpdate) {
|
|
1108
|
+
// Quick update - use existing modules or fall back to modules being updated
|
|
1109
|
+
modulesForCsvPreserve = config._existingModules || allModules || [];
|
|
1110
|
+
} else {
|
|
1111
|
+
// Regular install - use the modules we're installing plus any preserved ones
|
|
1112
|
+
modulesForCsvPreserve = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules;
|
|
1113
|
+
}
|
|
825
1114
|
|
|
826
|
-
const manifestStats = await manifestGen.generateManifests(bmadDir,
|
|
1115
|
+
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, this.installedFiles, {
|
|
827
1116
|
ides: config.ides || [],
|
|
828
|
-
preservedModules:
|
|
1117
|
+
preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
|
|
829
1118
|
});
|
|
830
1119
|
|
|
1120
|
+
// Add custom modules to manifest (now that it exists)
|
|
1121
|
+
if (config._customModulesToTrack && config._customModulesToTrack.length > 0) {
|
|
1122
|
+
spinner.text = 'Storing custom module sources...';
|
|
1123
|
+
for (const customModule of config._customModulesToTrack) {
|
|
1124
|
+
await this.manifest.addCustomModule(bmadDir, customModule);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
831
1128
|
spinner.succeed(
|
|
832
1129
|
`Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
|
|
833
1130
|
);
|
|
@@ -1090,6 +1387,30 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1090
1387
|
const currentVersion = existingInstall.version;
|
|
1091
1388
|
const newVersion = require(path.join(getProjectRoot(), 'package.json')).version;
|
|
1092
1389
|
|
|
1390
|
+
// Check for custom modules with missing sources before update
|
|
1391
|
+
const customModuleSources = new Map();
|
|
1392
|
+
if (existingInstall.customModules) {
|
|
1393
|
+
for (const customModule of existingInstall.customModules) {
|
|
1394
|
+
customModuleSources.set(customModule.id, customModule);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (customModuleSources.size > 0) {
|
|
1399
|
+
spinner.stop();
|
|
1400
|
+
console.log(chalk.yellow('\nChecking custom module sources before update...'));
|
|
1401
|
+
|
|
1402
|
+
const projectRoot = getProjectRoot();
|
|
1403
|
+
await this.handleMissingCustomSources(
|
|
1404
|
+
customModuleSources,
|
|
1405
|
+
bmadDir,
|
|
1406
|
+
projectRoot,
|
|
1407
|
+
'update',
|
|
1408
|
+
existingInstall.modules.map((m) => m.id),
|
|
1409
|
+
);
|
|
1410
|
+
|
|
1411
|
+
spinner.start('Preparing update...');
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1093
1414
|
if (config.dryRun) {
|
|
1094
1415
|
spinner.stop();
|
|
1095
1416
|
console.log(chalk.cyan('\n🔍 Update Preview (Dry Run)\n'));
|
|
@@ -1547,6 +1868,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1547
1868
|
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1548
1869
|
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
1549
1870
|
|
|
1871
|
+
// Replace .bmad with actual folder name
|
|
1872
|
+
xmlContent = xmlContent.replaceAll('.bmad', this.bmadFolderName || 'bmad');
|
|
1873
|
+
|
|
1550
1874
|
// Replace {agent_sidecar_folder} if configured
|
|
1551
1875
|
const coreConfig = this.configCollector.collectedConfig.core || {};
|
|
1552
1876
|
if (coreConfig.agent_sidecar_folder && xmlContent.includes('{agent_sidecar_folder}')) {
|
|
@@ -1592,7 +1916,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1592
1916
|
// Resolve path variables
|
|
1593
1917
|
const resolvedSidecarFolder = agentSidecarFolder
|
|
1594
1918
|
.replaceAll('{project-root}', projectDir)
|
|
1595
|
-
.replaceAll('
|
|
1919
|
+
.replaceAll('.bmad', this.bmadFolderName || 'bmad');
|
|
1596
1920
|
|
|
1597
1921
|
// Create sidecar directory for this agent
|
|
1598
1922
|
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
|
|
@@ -1858,6 +2182,24 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1858
2182
|
throw new Error(`BMAD not installed at ${bmadDir}`);
|
|
1859
2183
|
}
|
|
1860
2184
|
|
|
2185
|
+
// Check for custom modules with missing sources
|
|
2186
|
+
const manifest = await this.manifest.read(bmadDir);
|
|
2187
|
+
if (manifest && manifest.customModules && manifest.customModules.length > 0) {
|
|
2188
|
+
spinner.stop();
|
|
2189
|
+
console.log(chalk.yellow('\nChecking custom module sources before compilation...'));
|
|
2190
|
+
|
|
2191
|
+
const customModuleSources = new Map();
|
|
2192
|
+
for (const customModule of manifest.customModules) {
|
|
2193
|
+
customModuleSources.set(customModule.id, customModule);
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
const projectRoot = getProjectRoot();
|
|
2197
|
+
const installedModules = manifest.modules || [];
|
|
2198
|
+
await this.handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, 'compile-agents', installedModules);
|
|
2199
|
+
|
|
2200
|
+
spinner.start('Rebuilding agent files...');
|
|
2201
|
+
}
|
|
2202
|
+
|
|
1861
2203
|
let agentCount = 0;
|
|
1862
2204
|
let taskCount = 0;
|
|
1863
2205
|
|
|
@@ -2002,17 +2344,234 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2002
2344
|
const existingInstall = await this.detector.detect(bmadDir);
|
|
2003
2345
|
const installedModules = existingInstall.modules.map((m) => m.id);
|
|
2004
2346
|
const configuredIdes = existingInstall.ides || [];
|
|
2347
|
+
const projectRoot = path.dirname(bmadDir);
|
|
2348
|
+
|
|
2349
|
+
// Get custom module sources from manifest
|
|
2350
|
+
const customModuleSources = new Map();
|
|
2351
|
+
if (existingInstall.customModules) {
|
|
2352
|
+
for (const customModule of existingInstall.customModules) {
|
|
2353
|
+
// Ensure we have an absolute sourcePath
|
|
2354
|
+
let absoluteSourcePath = customModule.sourcePath;
|
|
2355
|
+
|
|
2356
|
+
// Check if sourcePath is a cache-relative path (starts with _cfg/)
|
|
2357
|
+
if (absoluteSourcePath && absoluteSourcePath.startsWith('_cfg')) {
|
|
2358
|
+
// Convert cache-relative path to absolute path
|
|
2359
|
+
absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
|
|
2360
|
+
}
|
|
2361
|
+
// If no sourcePath but we have relativePath, convert it
|
|
2362
|
+
else if (!absoluteSourcePath && customModule.relativePath) {
|
|
2363
|
+
// relativePath is relative to the project root (parent of bmad dir)
|
|
2364
|
+
absoluteSourcePath = path.resolve(projectRoot, customModule.relativePath);
|
|
2365
|
+
}
|
|
2366
|
+
// Ensure sourcePath is absolute for anything else
|
|
2367
|
+
else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
|
|
2368
|
+
absoluteSourcePath = path.resolve(absoluteSourcePath);
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// Update the custom module object with the absolute path
|
|
2372
|
+
const updatedModule = {
|
|
2373
|
+
...customModule,
|
|
2374
|
+
sourcePath: absoluteSourcePath,
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2377
|
+
customModuleSources.set(customModule.id, updatedModule);
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2005
2380
|
|
|
2006
2381
|
// Load saved IDE configurations
|
|
2007
2382
|
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
|
2008
2383
|
|
|
2009
2384
|
// Get available modules (what we have source for)
|
|
2010
|
-
const
|
|
2011
|
-
const
|
|
2385
|
+
const availableModulesData = await this.moduleManager.listAvailable();
|
|
2386
|
+
const availableModules = [...availableModulesData.modules, ...availableModulesData.customModules];
|
|
2387
|
+
|
|
2388
|
+
// Add custom modules from manifest if their sources exist
|
|
2389
|
+
for (const [moduleId, customModule] of customModuleSources) {
|
|
2390
|
+
// Use the absolute sourcePath
|
|
2391
|
+
const sourcePath = customModule.sourcePath;
|
|
2392
|
+
|
|
2393
|
+
// Check if source exists at the recorded path
|
|
2394
|
+
if (
|
|
2395
|
+
sourcePath &&
|
|
2396
|
+
(await fs.pathExists(sourcePath)) && // Add to available modules if not already there
|
|
2397
|
+
!availableModules.some((m) => m.id === moduleId)
|
|
2398
|
+
) {
|
|
2399
|
+
availableModules.push({
|
|
2400
|
+
id: moduleId,
|
|
2401
|
+
name: customModule.name || moduleId,
|
|
2402
|
+
path: sourcePath,
|
|
2403
|
+
isCustom: true,
|
|
2404
|
+
fromManifest: true,
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
// Check for untracked custom modules (installed but not in manifest)
|
|
2410
|
+
const untrackedCustomModules = [];
|
|
2411
|
+
for (const installedModule of installedModules) {
|
|
2412
|
+
// Skip standard modules and core
|
|
2413
|
+
const standardModuleIds = ['bmb', 'bmgd', 'bmm', 'cis', 'core'];
|
|
2414
|
+
if (standardModuleIds.includes(installedModule)) {
|
|
2415
|
+
continue;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// Check if this installed module is not tracked in customModules
|
|
2419
|
+
if (!customModuleSources.has(installedModule)) {
|
|
2420
|
+
const modulePath = path.join(bmadDir, installedModule);
|
|
2421
|
+
if (await fs.pathExists(modulePath)) {
|
|
2422
|
+
untrackedCustomModules.push({
|
|
2423
|
+
id: installedModule,
|
|
2424
|
+
name: installedModule, // We don't have the original name
|
|
2425
|
+
path: modulePath,
|
|
2426
|
+
untracked: true,
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
// If we found untracked custom modules, offer to track them
|
|
2433
|
+
if (untrackedCustomModules.length > 0) {
|
|
2434
|
+
spinner.stop();
|
|
2435
|
+
console.log(chalk.yellow(`\n⚠️ Found ${untrackedCustomModules.length} custom module(s) not tracked in manifest:`));
|
|
2436
|
+
|
|
2437
|
+
for (const untracked of untrackedCustomModules) {
|
|
2438
|
+
console.log(chalk.dim(` • ${untracked.id} (installed at ${path.relative(projectRoot, untracked.path)})`));
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
const { trackModules } = await inquirer.prompt([
|
|
2442
|
+
{
|
|
2443
|
+
type: 'confirm',
|
|
2444
|
+
name: 'trackModules',
|
|
2445
|
+
message: chalk.cyan('Would you like to scan for their source locations?'),
|
|
2446
|
+
default: true,
|
|
2447
|
+
},
|
|
2448
|
+
]);
|
|
2449
|
+
|
|
2450
|
+
if (trackModules) {
|
|
2451
|
+
const { scanDirectory } = await inquirer.prompt([
|
|
2452
|
+
{
|
|
2453
|
+
type: 'input',
|
|
2454
|
+
name: 'scanDirectory',
|
|
2455
|
+
message: 'Enter directory to scan for custom module sources (or leave blank to skip):',
|
|
2456
|
+
default: projectRoot,
|
|
2457
|
+
validate: async (input) => {
|
|
2458
|
+
if (input && input.trim() !== '') {
|
|
2459
|
+
const expandedPath = path.resolve(input.trim());
|
|
2460
|
+
if (!(await fs.pathExists(expandedPath))) {
|
|
2461
|
+
return 'Directory does not exist';
|
|
2462
|
+
}
|
|
2463
|
+
const stats = await fs.stat(expandedPath);
|
|
2464
|
+
if (!stats.isDirectory()) {
|
|
2465
|
+
return 'Path must be a directory';
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
return true;
|
|
2469
|
+
},
|
|
2470
|
+
},
|
|
2471
|
+
]);
|
|
2472
|
+
|
|
2473
|
+
if (scanDirectory && scanDirectory.trim() !== '') {
|
|
2474
|
+
console.log(chalk.dim('\nScanning for custom module sources...'));
|
|
2475
|
+
|
|
2476
|
+
// Scan for all module.yaml files
|
|
2477
|
+
const allModulePaths = await this.moduleManager.findModulesInProject(scanDirectory);
|
|
2478
|
+
const { ModuleManager } = require('../modules/manager');
|
|
2479
|
+
const mm = new ModuleManager({ scanProjectForModules: true });
|
|
2480
|
+
|
|
2481
|
+
for (const untracked of untrackedCustomModules) {
|
|
2482
|
+
let foundSource = null;
|
|
2483
|
+
|
|
2484
|
+
// Try to find by module ID
|
|
2485
|
+
for (const modulePath of allModulePaths) {
|
|
2486
|
+
try {
|
|
2487
|
+
const moduleInfo = await mm.getModuleInfo(modulePath);
|
|
2488
|
+
if (moduleInfo && moduleInfo.id === untracked.id) {
|
|
2489
|
+
foundSource = {
|
|
2490
|
+
path: modulePath,
|
|
2491
|
+
info: moduleInfo,
|
|
2492
|
+
};
|
|
2493
|
+
break;
|
|
2494
|
+
}
|
|
2495
|
+
} catch {
|
|
2496
|
+
// Continue searching
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
if (foundSource) {
|
|
2501
|
+
console.log(chalk.green(` ✓ Found source for ${untracked.id}: ${path.relative(projectRoot, foundSource.path)}`));
|
|
2502
|
+
|
|
2503
|
+
// Add to manifest
|
|
2504
|
+
await this.manifest.addCustomModule(bmadDir, {
|
|
2505
|
+
id: untracked.id,
|
|
2506
|
+
name: foundSource.info.name || untracked.name,
|
|
2507
|
+
sourcePath: path.resolve(foundSource.path),
|
|
2508
|
+
installDate: new Date().toISOString(),
|
|
2509
|
+
tracked: true,
|
|
2510
|
+
});
|
|
2511
|
+
|
|
2512
|
+
// Add to customModuleSources for processing
|
|
2513
|
+
customModuleSources.set(untracked.id, {
|
|
2514
|
+
id: untracked.id,
|
|
2515
|
+
name: foundSource.info.name || untracked.name,
|
|
2516
|
+
sourcePath: path.resolve(foundSource.path),
|
|
2517
|
+
});
|
|
2518
|
+
} else {
|
|
2519
|
+
console.log(chalk.yellow(` ⚠ Could not find source for ${untracked.id}`));
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
console.log(chalk.dim('\nUntracked custom modules will remain installed but cannot be updated without their source.'));
|
|
2526
|
+
spinner.start('Preparing update...');
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
// Handle missing custom module sources using shared method
|
|
2530
|
+
const customModuleResult = await this.handleMissingCustomSources(
|
|
2531
|
+
customModuleSources,
|
|
2532
|
+
bmadDir,
|
|
2533
|
+
projectRoot,
|
|
2534
|
+
'update',
|
|
2535
|
+
installedModules,
|
|
2536
|
+
);
|
|
2537
|
+
|
|
2538
|
+
const { validCustomModules, keptModulesWithoutSources } = customModuleResult;
|
|
2539
|
+
|
|
2540
|
+
const customModulesFromManifest = validCustomModules.map((m) => ({
|
|
2541
|
+
...m,
|
|
2542
|
+
isCustom: true,
|
|
2543
|
+
hasUpdate: true,
|
|
2544
|
+
}));
|
|
2545
|
+
|
|
2546
|
+
// Add untracked modules to the update list but mark them as untrackable
|
|
2547
|
+
for (const untracked of untrackedCustomModules) {
|
|
2548
|
+
if (!customModuleSources.has(untracked.id)) {
|
|
2549
|
+
customModulesFromManifest.push({
|
|
2550
|
+
...untracked,
|
|
2551
|
+
isCustom: true,
|
|
2552
|
+
hasUpdate: false, // Can't update without source
|
|
2553
|
+
untracked: true,
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
const allAvailableModules = [...availableModules, ...customModulesFromManifest];
|
|
2559
|
+
const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
|
|
2560
|
+
|
|
2561
|
+
// Core module is special - never include it in update flow
|
|
2562
|
+
const nonCoreInstalledModules = installedModules.filter((id) => id !== 'core');
|
|
2012
2563
|
|
|
2013
2564
|
// Only update modules that are BOTH installed AND available (we have source for)
|
|
2014
|
-
const modulesToUpdate =
|
|
2015
|
-
const skippedModules =
|
|
2565
|
+
const modulesToUpdate = nonCoreInstalledModules.filter((id) => availableModuleIds.has(id));
|
|
2566
|
+
const skippedModules = nonCoreInstalledModules.filter((id) => !availableModuleIds.has(id));
|
|
2567
|
+
|
|
2568
|
+
// Add custom modules that were kept without sources to the skipped modules
|
|
2569
|
+
// This ensures their agents are preserved in the manifest
|
|
2570
|
+
for (const keptModule of keptModulesWithoutSources) {
|
|
2571
|
+
if (!skippedModules.includes(keptModule)) {
|
|
2572
|
+
skippedModules.push(keptModule);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2016
2575
|
|
|
2017
2576
|
spinner.succeed(`Found ${modulesToUpdate.length} module(s) to update and ${configuredIdes.length} configured tool(s)`);
|
|
2018
2577
|
|
|
@@ -2051,7 +2610,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2051
2610
|
lastModified: new Date().toISOString(),
|
|
2052
2611
|
};
|
|
2053
2612
|
|
|
2054
|
-
// Check if bmad_folder has changed
|
|
2055
2613
|
const existingBmadFolderName = path.basename(bmadDir);
|
|
2056
2614
|
const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
|
|
2057
2615
|
|
|
@@ -2077,6 +2635,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2077
2635
|
_quickUpdate: true, // Flag to skip certain prompts
|
|
2078
2636
|
_preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
|
|
2079
2637
|
_savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
|
|
2638
|
+
_customModuleSources: customModuleSources, // Pass custom module sources for updates
|
|
2639
|
+
_existingModules: installedModules, // Pass all installed modules for manifest generation
|
|
2080
2640
|
};
|
|
2081
2641
|
|
|
2082
2642
|
// Call the standard install method
|
|
@@ -2647,7 +3207,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2647
3207
|
const agentSidecarFolder = config.coreConfig?.agent_sidecar_folder;
|
|
2648
3208
|
|
|
2649
3209
|
// Resolve path variables
|
|
2650
|
-
const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('
|
|
3210
|
+
const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('.bmad', bmadDir);
|
|
2651
3211
|
|
|
2652
3212
|
// Create sidecar directory for this agent
|
|
2653
3213
|
const agentSidecarDir = path.join(resolvedSidecarFolder, finalAgentName);
|
|
@@ -2716,6 +3276,233 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2716
3276
|
}
|
|
2717
3277
|
}
|
|
2718
3278
|
}
|
|
3279
|
+
|
|
3280
|
+
/**
|
|
3281
|
+
* Handle missing custom module sources interactively
|
|
3282
|
+
* @param {Map} customModuleSources - Map of custom module ID to info
|
|
3283
|
+
* @param {string} bmadDir - BMAD directory
|
|
3284
|
+
* @param {string} projectRoot - Project root directory
|
|
3285
|
+
* @param {string} operation - Current operation ('update', 'compile', etc.)
|
|
3286
|
+
* @param {Array} installedModules - Array of installed module IDs (will be modified)
|
|
3287
|
+
* @returns {Object} Object with validCustomModules array and keptModulesWithoutSources array
|
|
3288
|
+
*/
|
|
3289
|
+
async handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, operation, installedModules) {
|
|
3290
|
+
const validCustomModules = [];
|
|
3291
|
+
const keptModulesWithoutSources = []; // Track modules kept without sources
|
|
3292
|
+
const customModulesWithMissingSources = [];
|
|
3293
|
+
|
|
3294
|
+
// Check which sources exist
|
|
3295
|
+
for (const [moduleId, customInfo] of customModuleSources) {
|
|
3296
|
+
if (await fs.pathExists(customInfo.sourcePath)) {
|
|
3297
|
+
validCustomModules.push({
|
|
3298
|
+
id: moduleId,
|
|
3299
|
+
name: customInfo.name,
|
|
3300
|
+
path: customInfo.sourcePath,
|
|
3301
|
+
info: customInfo,
|
|
3302
|
+
});
|
|
3303
|
+
} else {
|
|
3304
|
+
customModulesWithMissingSources.push({
|
|
3305
|
+
id: moduleId,
|
|
3306
|
+
name: customInfo.name,
|
|
3307
|
+
sourcePath: customInfo.sourcePath,
|
|
3308
|
+
relativePath: customInfo.relativePath,
|
|
3309
|
+
info: customInfo,
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
// If no missing sources, return immediately
|
|
3315
|
+
if (customModulesWithMissingSources.length === 0) {
|
|
3316
|
+
return {
|
|
3317
|
+
validCustomModules,
|
|
3318
|
+
keptModulesWithoutSources: [],
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
// Stop any spinner for interactive prompts
|
|
3323
|
+
const currentSpinner = ora();
|
|
3324
|
+
if (currentSpinner.isSpinning) {
|
|
3325
|
+
currentSpinner.stop();
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
console.log(chalk.yellow(`\n⚠️ Found ${customModulesWithMissingSources.length} custom module(s) with missing sources:`));
|
|
3329
|
+
|
|
3330
|
+
const inquirer = require('inquirer');
|
|
3331
|
+
let keptCount = 0;
|
|
3332
|
+
let updatedCount = 0;
|
|
3333
|
+
let removedCount = 0;
|
|
3334
|
+
|
|
3335
|
+
for (const missing of customModulesWithMissingSources) {
|
|
3336
|
+
console.log(chalk.dim(` • ${missing.name} (${missing.id})`));
|
|
3337
|
+
console.log(chalk.dim(` Original source: ${missing.relativePath}`));
|
|
3338
|
+
console.log(chalk.dim(` Full path: ${missing.sourcePath}`));
|
|
3339
|
+
|
|
3340
|
+
const choices = [
|
|
3341
|
+
{
|
|
3342
|
+
name: 'Keep installed (will not be processed)',
|
|
3343
|
+
value: 'keep',
|
|
3344
|
+
short: 'Keep',
|
|
3345
|
+
},
|
|
3346
|
+
{
|
|
3347
|
+
name: 'Specify new source location',
|
|
3348
|
+
value: 'update',
|
|
3349
|
+
short: 'Update',
|
|
3350
|
+
},
|
|
3351
|
+
];
|
|
3352
|
+
|
|
3353
|
+
// Only add remove option if not just compiling agents
|
|
3354
|
+
if (operation !== 'compile-agents') {
|
|
3355
|
+
choices.push({
|
|
3356
|
+
name: '⚠️ REMOVE module completely (destructive!)',
|
|
3357
|
+
value: 'remove',
|
|
3358
|
+
short: 'Remove',
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
const { action } = await inquirer.prompt([
|
|
3363
|
+
{
|
|
3364
|
+
type: 'list',
|
|
3365
|
+
name: 'action',
|
|
3366
|
+
message: `How would you like to handle "${missing.name}"?`,
|
|
3367
|
+
choices,
|
|
3368
|
+
},
|
|
3369
|
+
]);
|
|
3370
|
+
|
|
3371
|
+
switch (action) {
|
|
3372
|
+
case 'update': {
|
|
3373
|
+
const { newSourcePath } = await inquirer.prompt([
|
|
3374
|
+
{
|
|
3375
|
+
type: 'input',
|
|
3376
|
+
name: 'newSourcePath',
|
|
3377
|
+
message: 'Enter the new path to the custom module:',
|
|
3378
|
+
default: missing.sourcePath,
|
|
3379
|
+
validate: async (input) => {
|
|
3380
|
+
if (!input || input.trim() === '') {
|
|
3381
|
+
return 'Please enter a path';
|
|
3382
|
+
}
|
|
3383
|
+
const expandedPath = path.resolve(input.trim());
|
|
3384
|
+
if (!(await fs.pathExists(expandedPath))) {
|
|
3385
|
+
return 'Path does not exist';
|
|
3386
|
+
}
|
|
3387
|
+
// Check if it looks like a valid module
|
|
3388
|
+
const moduleYamlPath = path.join(expandedPath, 'module.yaml');
|
|
3389
|
+
const agentsPath = path.join(expandedPath, 'agents');
|
|
3390
|
+
const workflowsPath = path.join(expandedPath, 'workflows');
|
|
3391
|
+
|
|
3392
|
+
if (!(await fs.pathExists(moduleYamlPath)) && !(await fs.pathExists(agentsPath)) && !(await fs.pathExists(workflowsPath))) {
|
|
3393
|
+
return 'Path does not appear to contain a valid custom module';
|
|
3394
|
+
}
|
|
3395
|
+
return true;
|
|
3396
|
+
},
|
|
3397
|
+
},
|
|
3398
|
+
]);
|
|
3399
|
+
|
|
3400
|
+
// Update the source in manifest
|
|
3401
|
+
const resolvedPath = path.resolve(newSourcePath.trim());
|
|
3402
|
+
missing.info.sourcePath = resolvedPath;
|
|
3403
|
+
// Remove relativePath - we only store absolute sourcePath now
|
|
3404
|
+
delete missing.info.relativePath;
|
|
3405
|
+
await this.manifest.addCustomModule(bmadDir, missing.info);
|
|
3406
|
+
|
|
3407
|
+
validCustomModules.push({
|
|
3408
|
+
id: moduleId,
|
|
3409
|
+
name: missing.name,
|
|
3410
|
+
path: resolvedPath,
|
|
3411
|
+
info: missing.info,
|
|
3412
|
+
});
|
|
3413
|
+
|
|
3414
|
+
updatedCount++;
|
|
3415
|
+
console.log(chalk.green(`✓ Updated source location`));
|
|
3416
|
+
|
|
3417
|
+
break;
|
|
3418
|
+
}
|
|
3419
|
+
case 'remove': {
|
|
3420
|
+
// Extra confirmation for destructive remove
|
|
3421
|
+
console.log(chalk.red.bold(`\n⚠️ WARNING: This will PERMANENTLY DELETE "${missing.name}" and all its files!`));
|
|
3422
|
+
console.log(chalk.red(` Module location: ${path.join(bmadDir, moduleId)}`));
|
|
3423
|
+
|
|
3424
|
+
const { confirm } = await inquirer.prompt([
|
|
3425
|
+
{
|
|
3426
|
+
type: 'confirm',
|
|
3427
|
+
name: 'confirm',
|
|
3428
|
+
message: chalk.red.bold('Are you absolutely sure you want to delete this module?'),
|
|
3429
|
+
default: false,
|
|
3430
|
+
},
|
|
3431
|
+
]);
|
|
3432
|
+
|
|
3433
|
+
if (confirm) {
|
|
3434
|
+
const { typedConfirm } = await inquirer.prompt([
|
|
3435
|
+
{
|
|
3436
|
+
type: 'input',
|
|
3437
|
+
name: 'typedConfirm',
|
|
3438
|
+
message: chalk.red.bold('Type "DELETE" to confirm permanent deletion:'),
|
|
3439
|
+
validate: (input) => {
|
|
3440
|
+
if (input !== 'DELETE') {
|
|
3441
|
+
return chalk.red('You must type "DELETE" exactly to proceed');
|
|
3442
|
+
}
|
|
3443
|
+
return true;
|
|
3444
|
+
},
|
|
3445
|
+
},
|
|
3446
|
+
]);
|
|
3447
|
+
|
|
3448
|
+
if (typedConfirm === 'DELETE') {
|
|
3449
|
+
// Remove the module from filesystem and manifest
|
|
3450
|
+
const modulePath = path.join(bmadDir, moduleId);
|
|
3451
|
+
if (await fs.pathExists(modulePath)) {
|
|
3452
|
+
const fsExtra = require('fs-extra');
|
|
3453
|
+
await fsExtra.remove(modulePath);
|
|
3454
|
+
console.log(chalk.yellow(` ✓ Deleted module directory: ${path.relative(projectRoot, modulePath)}`));
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
await this.manifest.removeModule(bmadDir, moduleId);
|
|
3458
|
+
await this.manifest.removeCustomModule(bmadDir, moduleId);
|
|
3459
|
+
console.log(chalk.yellow(` ✓ Removed from manifest`));
|
|
3460
|
+
|
|
3461
|
+
// Also remove from installedModules list
|
|
3462
|
+
if (installedModules && installedModules.includes(moduleId)) {
|
|
3463
|
+
const index = installedModules.indexOf(moduleId);
|
|
3464
|
+
if (index !== -1) {
|
|
3465
|
+
installedModules.splice(index, 1);
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3469
|
+
removedCount++;
|
|
3470
|
+
console.log(chalk.red.bold(`✓ "${missing.name}" has been permanently removed`));
|
|
3471
|
+
} else {
|
|
3472
|
+
console.log(chalk.dim(' Removal cancelled - module will be kept'));
|
|
3473
|
+
keptCount++;
|
|
3474
|
+
}
|
|
3475
|
+
} else {
|
|
3476
|
+
console.log(chalk.dim(' Removal cancelled - module will be kept'));
|
|
3477
|
+
keptCount++;
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
break;
|
|
3481
|
+
}
|
|
3482
|
+
case 'keep': {
|
|
3483
|
+
keptCount++;
|
|
3484
|
+
keptModulesWithoutSources.push(moduleId);
|
|
3485
|
+
console.log(chalk.dim(` Module will be kept as-is`));
|
|
3486
|
+
|
|
3487
|
+
break;
|
|
3488
|
+
}
|
|
3489
|
+
// No default
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
// Show summary
|
|
3494
|
+
if (keptCount > 0 || updatedCount > 0 || removedCount > 0) {
|
|
3495
|
+
console.log(chalk.dim(`\nSummary for custom modules with missing sources:`));
|
|
3496
|
+
if (keptCount > 0) console.log(chalk.dim(` • ${keptCount} module(s) kept as-is`));
|
|
3497
|
+
if (updatedCount > 0) console.log(chalk.dim(` • ${updatedCount} module(s) updated with new sources`));
|
|
3498
|
+
if (removedCount > 0) console.log(chalk.red(` • ${removedCount} module(s) permanently deleted`));
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
return {
|
|
3502
|
+
validCustomModules,
|
|
3503
|
+
keptModulesWithoutSources,
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
2719
3506
|
}
|
|
2720
3507
|
|
|
2721
3508
|
module.exports = { Installer };
|