bmad-method 6.0.0-alpha.15 → 6.0.0-alpha.17
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 +8 -4
- package/.github/scripts/discord-helpers.sh +21 -2
- package/.github/workflows/discord.yaml +31 -7
- package/.github/workflows/manual-release.yaml +12 -42
- package/.markdownlint-cli2.yaml +2 -2
- package/.prettierignore +2 -2
- package/.vscode/settings.json +1 -1
- package/CHANGELOG.md +126 -1
- package/LICENSE +1 -1
- package/README.md +29 -2
- package/docs/agent-customization-guide.md +10 -10
- package/docs/custom-content-installation.md +100 -196
- package/docs/custom-content.md +122 -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/ide-info/rovo-dev.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 +25 -25
- package/docs/sample-custom-modules/README.md +11 -0
- package/docs/sample-custom-modules/sample-unitary-module/README.md +8 -0
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/commit-poet/commit-poet.agent.yaml +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith.agent.yaml +14 -14
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/module.yaml +5 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-01-init.md +2 -2
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-02-q1.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-03-q2.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-04-q3.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-05-q4.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-06-q5.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-07-q6.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-08-q7.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-09-q8.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-10-q9.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-11-q10.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/steps/step-12-results.md +1 -1
- package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/workflow.md +1 -1
- package/docs/sample-custom-modules/sample-wellness-module/README.md +6 -0
- package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/meditation-guide.agent.yaml +37 -39
- package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/foo.md +3 -0
- package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion-sidecar/addition1.md +1 -0
- package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion.agent.yaml +8 -13
- package/docs/sample-custom-modules/sample-wellness-module/module.yaml +17 -0
- package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/wellness-journal/workflow.md +3 -3
- package/docs/v4-to-v6-upgrade.md +27 -27
- package/docs/web-bundles-gemini-gpt-guide.md +6 -458
- package/eslint.config.mjs +2 -2
- package/package.json +1 -1
- package/src/core/agents/bmad-master.agent.yaml +6 -11
- package/src/core/module.yaml +10 -24
- package/src/core/resources/excalidraw/README.md +6 -6
- 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 +12 -12
- package/src/core/workflows/brainstorming/workflow.md +3 -3
- 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/agents/bmad-builder.agent.yaml +15 -15
- package/src/modules/bmb/{README.md → docs/README.md} +15 -27
- package/src/modules/bmb/docs/agents/agent-compilation.md +3 -3
- package/src/modules/bmb/docs/agents/agent-menu-patterns.md +23 -24
- package/src/modules/bmb/docs/agents/expert-agent-architecture.md +21 -22
- package/src/modules/bmb/docs/agents/index.md +1 -1
- package/src/modules/bmb/docs/agents/simple-agent-architecture.md +17 -52
- package/src/modules/bmb/docs/agents/understanding-agent-types.md +6 -6
- 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.yaml +6 -16
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper.agent.yaml +7 -7
- package/src/modules/bmb/reference/agents/module-examples/README.md +3 -4
- 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/agents/simple-examples/README.md +1 -1
- package/src/modules/bmb/reference/agents/simple-examples/commit-poet.agent.yaml +1 -1
- 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 -3
- 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/agents/simple-examples/README.md +1 -1
- package/src/modules/bmb/workflows/create-agent/data/reference/agents/simple-examples/commit-poet.agent.yaml +1 -1
- 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 +5 -5
- package/src/modules/bmb/workflows/create-agent/steps/step-02-discover.md +7 -11
- package/src/modules/bmb/workflows/create-agent/steps/step-03-persona.md +7 -7
- package/src/modules/bmb/workflows/create-agent/steps/step-04-commands.md +10 -10
- package/src/modules/bmb/workflows/create-agent/steps/step-05-name.md +7 -6
- package/src/modules/bmb/workflows/create-agent/steps/step-06-build.md +20 -58
- package/src/modules/bmb/workflows/create-agent/steps/step-07-validate.md +6 -6
- package/src/modules/bmb/workflows/create-agent/steps/{step-11-celebrate.md → step-08-celebrate.md} +3 -7
- package/src/modules/bmb/workflows/create-agent/templates/agent-plan.template.md +3 -0
- package/src/modules/bmb/workflows/create-agent/templates/expert-agent.template.md +364 -0
- package/src/modules/bmb/workflows/create-agent/templates/simple-agent.template.md +257 -0
- package/src/modules/bmb/workflows/create-agent/workflow.md +2 -35
- package/src/modules/bmb/workflows/create-module/steps/step-01-init.md +4 -4
- package/src/modules/bmb/workflows/create-module/steps/step-01b-continue.md +2 -2
- package/src/modules/bmb/workflows/create-module/steps/step-02-concept.md +5 -5
- package/src/modules/bmb/workflows/create-module/steps/step-03-components.md +5 -5
- 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 +9 -9
- package/src/modules/bmb/workflows/create-module/steps/step-07-workflows.md +5 -5
- package/src/modules/bmb/workflows/create-module/steps/step-08-installer.md +8 -8
- package/src/modules/bmb/workflows/create-module/steps/step-09-documentation.md +7 -7
- package/src/modules/bmb/workflows/create-module/steps/step-10-roadmap.md +7 -7
- package/src/modules/bmb/workflows/create-module/steps/step-11-validate.md +6 -6
- package/src/modules/bmb/workflows/create-module/templates/agent.template.md +15 -19
- package/src/modules/bmb/workflows/create-module/templates/module.template.yaml +1 -1
- package/src/modules/bmb/workflows/create-module/workflow.md +3 -3
- package/src/modules/bmb/workflows/create-workflow/steps/step-01-init.md +4 -4
- package/src/modules/bmb/workflows/create-workflow/steps/step-02-gather.md +6 -6
- package/src/modules/bmb/workflows/create-workflow/steps/step-03-tools-configuration.md +5 -5
- package/src/modules/bmb/workflows/create-workflow/steps/step-04-plan-review.md +4 -4
- package/src/modules/bmb/workflows/create-workflow/steps/step-05-output-format-design.md +4 -4
- package/src/modules/bmb/workflows/create-workflow/steps/step-06-design.md +11 -11
- package/src/modules/bmb/workflows/create-workflow/steps/step-07-build.md +14 -14
- package/src/modules/bmb/workflows/create-workflow/steps/step-08-review.md +4 -4
- package/src/modules/bmb/workflows/create-workflow/steps/step-09-complete.md +2 -2
- package/src/modules/bmb/workflows/create-workflow/workflow.md +2 -2
- 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 +1 -17
- package/src/modules/bmb/workflows-legacy/edit-module/checklist.md +3 -4
- package/src/modules/bmb/workflows-legacy/edit-module/instructions.md +4 -5
- 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 +1 -1
- 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 +10 -10
- package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +21 -21
- package/src/modules/bmgd/module.yaml +2 -8
- 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 +4 -4
- 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 +2 -2
- 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/README.md +1 -1
- package/src/modules/bmm/agents/analyst.agent.yaml +9 -9
- package/src/modules/bmm/agents/architect.agent.yaml +8 -8
- package/src/modules/bmm/agents/dev.agent.yaml +5 -5
- 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 +11 -11
- 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 +16 -14
- package/src/modules/bmm/docs/brownfield-guide.md +6 -6
- package/src/modules/bmm/docs/enterprise-agentic-development.md +3 -3
- package/src/modules/bmm/docs/faq.md +5 -18
- package/src/modules/bmm/docs/glossary.md +3 -4
- package/src/modules/bmm/docs/images/README.md +1 -1
- package/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw +8 -8
- package/src/modules/bmm/docs/party-mode.md +3 -3
- package/src/modules/bmm/docs/quick-flow-solo-dev.md +5 -5
- package/src/modules/bmm/docs/quick-spec-flow.md +2 -16
- package/src/modules/bmm/docs/quick-start.md +3 -3
- package/src/modules/bmm/docs/test-architecture.md +15 -23
- package/src/modules/bmm/docs/troubleshooting.md +6 -25
- package/src/modules/bmm/docs/workflow-document-project-reference.md +1 -1
- package/src/modules/bmm/docs/workflows-implementation.md +3 -104
- package/src/modules/bmm/module.yaml +25 -24
- package/src/modules/bmm/testarch/knowledge/overview.md +0 -1
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-01-init.md +1 -1
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-01b-continue.md +1 -1
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-02-vision.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-03-users.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-04-metrics.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-05-scope.md +3 -3
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/steps/step-06-complete.md +5 -3
- package/src/modules/bmm/workflows/1-analysis/{product-brief → create-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 +2 -2
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-01-document-discovery.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-02-prd-analysis.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-03-epic-coverage-validation.md +6 -6
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-04-ux-alignment.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-05-epic-quality-review.md +2 -2
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/steps/step-06-final-assessment.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/workflow.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-01-init.md +1 -1
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-02-context.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-03-starter.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-04-decisions.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-05-patterns.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-06-structure.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-07-validation.md +4 -4
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-08-complete.md +3 -3
- package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/workflow.md +2 -2
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +3 -3
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +3 -3
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +3 -3
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +3 -3
- package/src/modules/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +3 -3
- package/src/modules/bmm/workflows/4-implementation/code-review/instructions.xml +4 -3
- 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 +5 -5
- package/src/modules/bmm/workflows/4-implementation/create-story/template.md +3 -5
- package/src/modules/bmm/workflows/4-implementation/create-story/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/dev-story/checklist.md +2 -2
- package/src/modules/bmm/workflows/4-implementation/dev-story/instructions.xml +11 -8
- package/src/modules/bmm/workflows/4-implementation/dev-story/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/retrospective/instructions.md +3 -3
- package/src/modules/bmm/workflows/4-implementation/retrospective/workflow.yaml +3 -3
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/instructions.md +15 -22
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +4 -5
- package/src/modules/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +2 -2
- package/src/modules/bmm/workflows/4-implementation/sprint-status/instructions.md +90 -35
- 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/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/{diagrams → excalidraw-diagrams}/create-dataflow/instructions.md +1 -1
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-dataflow/workflow.yaml +6 -6
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-diagram/instructions.md +2 -2
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-diagram/workflow.yaml +6 -6
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-flowchart/instructions.md +2 -2
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-flowchart/workflow.yaml +6 -6
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-wireframe/instructions.md +1 -1
- package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-wireframe/workflow.yaml +6 -6
- package/src/modules/bmm/workflows/generate-project-context/steps/step-01-discover.md +2 -2
- package/src/modules/bmm/workflows/generate-project-context/steps/step-02-generate.md +3 -3
- package/src/modules/bmm/workflows/generate-project-context/steps/step-03-complete.md +2 -2
- package/src/modules/bmm/workflows/generate-project-context/workflow.md +5 -5
- 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 +10 -4
- 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/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/storyteller-sidecar/stories-told.md +7 -0
- package/src/modules/cis/agents/storyteller/storyteller-sidecar/story-preferences.md +7 -0
- package/src/modules/cis/agents/{storyteller.agent.yaml → storyteller/storyteller.agent.yaml} +9 -4
- package/src/modules/cis/module.yaml +3 -8
- package/src/modules/cis/{README.md → readme.md} +1 -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/agent-components/activation-rules.txt +7 -0
- package/src/utility/agent-components/activation-steps.txt +13 -0
- package/src/utility/agent-components/agent-command-header.md +1 -0
- package/src/utility/{templates → agent-components}/agent.customize.template.yaml +0 -1
- package/src/utility/agent-components/handler-action.txt +4 -0
- package/src/utility/agent-components/handler-exec.txt +6 -0
- package/src/utility/agent-components/handler-multi.txt +14 -0
- package/src/utility/agent-components/handler-tmpl.txt +5 -0
- package/src/utility/agent-components/handler-validate-workflow.txt +7 -0
- package/src/utility/agent-components/handler-workflow.txt +10 -0
- package/src/utility/agent-components/menu-handlers.txt +6 -0
- package/test/README.md +1 -1
- package/test/test-agent-schema.js +2 -2
- package/tools/cli/README.md +1 -607
- package/tools/cli/commands/build.js +7 -7
- package/tools/cli/commands/install.js +9 -20
- package/tools/cli/commands/list.js +13 -1
- package/tools/cli/installers/lib/core/config-collector.js +243 -73
- package/tools/cli/installers/lib/core/custom-module-cache.js +36 -16
- package/tools/cli/installers/lib/core/dependency-resolver.js +2 -2
- package/tools/cli/installers/lib/core/detector.js +16 -16
- package/tools/cli/installers/lib/core/ide-config-manager.js +9 -7
- package/tools/cli/installers/lib/core/installer.js +595 -1006
- package/tools/cli/installers/lib/core/manifest-generator.js +43 -40
- package/tools/cli/installers/lib/core/manifest.js +23 -20
- package/tools/cli/installers/lib/custom/handler.js +16 -49
- package/tools/cli/installers/lib/ide/_base-ide.js +26 -33
- package/tools/cli/installers/lib/ide/antigravity.js +3 -3
- package/tools/cli/installers/lib/ide/claude-code.js +3 -3
- package/tools/cli/installers/lib/ide/codex.js +2 -2
- package/tools/cli/installers/lib/ide/gemini.js +6 -6
- package/tools/cli/installers/lib/ide/kiro-cli.js +2 -2
- package/tools/cli/installers/lib/ide/opencode.js +2 -2
- package/tools/cli/installers/lib/ide/roo.js +15 -5
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/shared/module-injections.js +2 -2
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +4 -4
- 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 +304 -313
- package/tools/cli/lib/activation-builder.js +8 -13
- package/tools/cli/lib/agent/compiler.js +68 -168
- package/tools/cli/lib/agent/installer.js +8 -128
- package/tools/cli/lib/agent-analyzer.js +2 -2
- package/tools/cli/lib/agent-party-generator.js +5 -17
- package/tools/cli/lib/config.js +2 -2
- package/tools/cli/lib/platform-codes.js +2 -2
- package/tools/cli/lib/ui.js +610 -551
- package/tools/cli/lib/xml-handler.js +3 -55
- package/tools/cli/lib/yaml-format.js +4 -6
- package/tools/cli/lib/yaml-xml-builder.js +14 -26
- package/tools/flattener/ignoreRules.js +1 -1
- package/tools/flattener/xml.js +1 -7
- package/tools/lib/xml-utils.js +13 -0
- package/tools/migrate-custom-module-paths.js +2 -2
- package/tools/validate-agent-schema.js +2 -2
- package/docs/v6-open-items.md +0 -17
- package/example-custom-content/README.md +0 -8
- package/example-custom-module/mwm/README.md +0 -9
- 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 -151
- package/example-custom-module/mwm/agents/crisis-navigator.agent.yaml +0 -138
- package/example-custom-module/mwm/module.yaml +0 -28
- 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/src/core/agents/bmad-web-orchestrator.agent.xml +0 -113
- package/src/modules/bmb/_module-installer/installer.js +0 -76
- package/src/modules/bmb/docs/agents/module-agent-architecture.md +0 -367
- package/src/modules/bmb/workflows/create-agent/steps/step-08-setup.md +0 -179
- package/src/modules/bmb/workflows/create-agent/steps/step-09-customize.md +0 -197
- package/src/modules/bmb/workflows/create-agent/steps/step-10-build-tools.md +0 -180
- package/src/modules/bmb/workflows/create-agent/templates/agent_commands.md +0 -21
- package/src/modules/bmb/workflows/create-agent/templates/agent_persona.md +0 -25
- package/src/modules/bmb/workflows/create-agent/templates/agent_purpose_and_type.md +0 -23
- package/src/modules/bmm/tasks/daily-standup.xml +0 -85
- package/src/modules/cis/agents/README.md +0 -104
- package/src/utility/models/action-command-header.md +0 -0
- package/src/utility/models/agent-activation-ide.xml +0 -51
- package/src/utility/models/agent-activation-web.xml +0 -50
- package/src/utility/models/agent-command-header.md +0 -1
- package/src/utility/models/agent-config-template.md +0 -23
- package/src/utility/models/agent-in-team-activation.xml +0 -3
- package/src/utility/models/fragments/activation-rules.xml +0 -7
- package/src/utility/models/fragments/activation-steps.xml +0 -16
- package/src/utility/models/fragments/handler-action.xml +0 -4
- package/src/utility/models/fragments/handler-exec.xml +0 -6
- package/src/utility/models/fragments/handler-multi.xml +0 -14
- package/src/utility/models/fragments/handler-tmpl.xml +0 -5
- package/src/utility/models/fragments/handler-validate-workflow.xml +0 -7
- package/src/utility/models/fragments/handler-workflow.xml +0 -9
- package/src/utility/models/fragments/menu-handlers.xml +0 -6
- package/src/utility/models/fragments/web-bundle-activation-steps.xml +0 -32
- package/tools/cli/bundlers/bundle-web.js +0 -179
- package/tools/cli/bundlers/test-analyst.js +0 -28
- package/tools/cli/bundlers/test-bundler.js +0 -118
- package/tools/cli/bundlers/web-bundler.js +0 -1764
- package/tools/cli/installers/lib/core/post-install-sidecar-replacement.js +0 -79
- package/tools/cli/lib/replace-project-root.js +0 -239
- package/tools/cli/regenerate-manifests.js +0 -28
- package/tools/cli/test-yaml-builder.js +0 -43
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/instructions.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/bundlers.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/deploy.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/docs.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/knowledge/tests.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/agents/toolsmith/toolsmith-sidecar/memories.md +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/quiz-master/templates/csv-headers.template +0 -0
- /package/{example-custom-content → docs/sample-custom-modules/sample-unitary-module}/workflows/wassup/workflow.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/insights.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/instructions.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/memories.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/agents/wellness-companion/wellness-companion-sidecar/patterns.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/daily-checkin/README.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/daily-checkin/workflow.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/guided-meditation/README.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/guided-meditation/workflow.md +0 -0
- /package/{example-custom-module/mwm → docs/sample-custom-modules/sample-wellness-module}/workflows/wellness-journal/README.md +0 -0
- /package/src/modules/bmgd/workflows/4-production/code-review/{backlog_template.md → backlog-template.md} +0 -0
- /package/src/modules/bmm/workflows/1-analysis/{product-brief → create-product-brief}/product-brief.template.md +0 -0
- /package/src/modules/bmm/workflows/3-solutioning/{implementation-readiness → check-implementation-readiness}/templates/readiness-report-template.md +0 -0
- /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/architecture-decision-template.md +0 -0
- /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/data/domain-complexity.csv +0 -0
- /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/data/project-types.csv +0 -0
- /package/src/modules/bmm/workflows/3-solutioning/{architecture → create-architecture}/steps/step-01b-continue.md +0 -0
- /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/_shared/excalidraw-library.json +0 -0
- /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/_shared/excalidraw-templates.yaml +0 -0
- /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-dataflow/checklist.md +0 -0
- /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-diagram/checklist.md +0 -0
- /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-flowchart/checklist.md +0 -0
- /package/src/modules/bmm/workflows/{diagrams → excalidraw-diagrams}/create-wireframe/checklist.md +0 -0
- /package/src/utility/{models/fragments/handler-data.xml → agent-components/handler-data.txt} +0 -0
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File: tools/cli/installers/lib/core/installer.js
|
|
3
|
-
*
|
|
4
|
-
* BMAD Method - Business Model Agile Development Method
|
|
5
|
-
* Repository: https://github.com/paulpreibisch/BMAD-METHOD
|
|
6
|
-
*
|
|
7
|
-
* Copyright (c) 2025 Paul Preibisch
|
|
8
|
-
* Licensed under the Apache License, Version 2.0
|
|
9
|
-
*
|
|
10
|
-
* ---
|
|
11
|
-
*
|
|
12
|
-
* @fileoverview Core BMAD installation orchestrator with AgentVibes injection point support
|
|
13
|
-
* @context Manages complete BMAD installation flow including core agents, modules, IDE configs, and optional TTS integration
|
|
14
|
-
* @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete BMAD installation
|
|
15
|
-
* @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js
|
|
16
|
-
* @entrypoints Called by install.js command via installer.install(config)
|
|
17
|
-
* @patterns Injection point processing (AgentVibes), placeholder replacement ({bmad_folder}), module dependency resolution
|
|
18
|
-
* @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement()
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
1
|
const path = require('node:path');
|
|
22
2
|
const fs = require('fs-extra');
|
|
23
3
|
const chalk = require('chalk');
|
|
@@ -32,13 +12,13 @@ const { Config } = require('../../../lib/config');
|
|
|
32
12
|
const { XmlHandler } = require('../../../lib/xml-handler');
|
|
33
13
|
const { DependencyResolver } = require('./dependency-resolver');
|
|
34
14
|
const { ConfigCollector } = require('./config-collector');
|
|
35
|
-
// processInstallation no longer needed - LLMs understand {project-root}
|
|
36
15
|
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
|
|
37
16
|
const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
|
|
38
17
|
const { CLIUtils } = require('../../../lib/cli-utils');
|
|
39
18
|
const { ManifestGenerator } = require('./manifest-generator');
|
|
40
19
|
const { IdeConfigManager } = require('./ide-config-manager');
|
|
41
|
-
const {
|
|
20
|
+
const { CustomHandler } = require('../custom/handler');
|
|
21
|
+
const { filterCustomizationData } = require('../../../lib/agent/compiler');
|
|
42
22
|
|
|
43
23
|
class Installer {
|
|
44
24
|
constructor() {
|
|
@@ -52,49 +32,66 @@ class Installer {
|
|
|
52
32
|
this.dependencyResolver = new DependencyResolver();
|
|
53
33
|
this.configCollector = new ConfigCollector();
|
|
54
34
|
this.ideConfigManager = new IdeConfigManager();
|
|
55
|
-
this.installedFiles =
|
|
35
|
+
this.installedFiles = new Set(); // Track all installed files
|
|
56
36
|
this.ttsInjectedFiles = []; // Track files with TTS injection applied
|
|
57
37
|
}
|
|
58
38
|
|
|
59
39
|
/**
|
|
60
40
|
* Find the bmad installation directory in a project
|
|
61
|
-
* V6+ installations can use ANY folder name but ALWAYS have
|
|
41
|
+
* V6+ installations can use ANY folder name but ALWAYS have _config/manifest.yaml
|
|
42
|
+
* Also checks for legacy _cfg folder for migration
|
|
62
43
|
* @param {string} projectDir - Project directory
|
|
63
|
-
* @returns {Promise<
|
|
44
|
+
* @returns {Promise<Object>} { bmadDir: string, hasLegacyCfg: boolean }
|
|
64
45
|
*/
|
|
65
46
|
async findBmadDir(projectDir) {
|
|
66
47
|
// Check if project directory exists
|
|
67
48
|
if (!(await fs.pathExists(projectDir))) {
|
|
68
49
|
// Project doesn't exist yet, return default
|
|
69
|
-
return path.join(projectDir, '
|
|
50
|
+
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
|
|
70
51
|
}
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
53
|
+
let bmadDir = null;
|
|
54
|
+
let hasLegacyCfg = false;
|
|
55
|
+
|
|
74
56
|
try {
|
|
75
57
|
const entries = await fs.readdir(projectDir, { withFileTypes: true });
|
|
76
58
|
for (const entry of entries) {
|
|
77
59
|
if (entry.isDirectory()) {
|
|
78
|
-
const
|
|
60
|
+
const bmadPath = path.join(projectDir, entry.name);
|
|
61
|
+
|
|
62
|
+
// Check for current _config folder
|
|
63
|
+
const manifestPath = path.join(bmadPath, '_config', 'manifest.yaml');
|
|
79
64
|
if (await fs.pathExists(manifestPath)) {
|
|
80
|
-
// Found a V6+ installation
|
|
81
|
-
return
|
|
65
|
+
// Found a V6+ installation with current _config folder
|
|
66
|
+
return { bmadDir: bmadPath, hasLegacyCfg: false };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check for legacy _cfg folder
|
|
70
|
+
const legacyManifestPath = path.join(bmadPath, '_cfg', 'manifest.yaml');
|
|
71
|
+
if (await fs.pathExists(legacyManifestPath)) {
|
|
72
|
+
bmadDir = bmadPath;
|
|
73
|
+
hasLegacyCfg = true;
|
|
82
74
|
}
|
|
83
75
|
}
|
|
84
76
|
}
|
|
85
77
|
} catch {
|
|
86
|
-
|
|
78
|
+
console.log(chalk.red('Error reading project directory for BMAD installation detection'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// If we found a bmad directory (with or without legacy _cfg)
|
|
82
|
+
if (bmadDir) {
|
|
83
|
+
return { bmadDir, hasLegacyCfg };
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
// No V6+ installation found, return default
|
|
90
87
|
// This will be used for new installations
|
|
91
|
-
return path.join(projectDir, '
|
|
88
|
+
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
/**
|
|
95
92
|
* @function copyFileWithPlaceholderReplacement
|
|
96
93
|
* @intent Copy files from BMAD source to installation directory with dynamic content transformation
|
|
97
|
-
* @why Enables installation-time customization:
|
|
94
|
+
* @why Enables installation-time customization: _bmad replacement + optional AgentVibes TTS injection
|
|
98
95
|
* @param {string} sourcePath - Absolute path to source file in BMAD repository
|
|
99
96
|
* @param {string} targetPath - Absolute path to destination file in user's project
|
|
100
97
|
* @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad')
|
|
@@ -104,11 +101,6 @@ class Installer {
|
|
|
104
101
|
* @calledby installCore(), installModule(), IDE installers during file vendoring
|
|
105
102
|
* @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy()
|
|
106
103
|
*
|
|
107
|
-
* AI NOTE: This is the core transformation pipeline for ALL BMAD installation file copies.
|
|
108
|
-
* It performs two transformations in sequence:
|
|
109
|
-
* 1. {bmad_folder} → user's custom folder name (e.g., ".bmad" or "bmad")
|
|
110
|
-
* 2. <!-- TTS_INJECTION:* --> → TTS bash calls (if enabled) OR stripped (if disabled)
|
|
111
|
-
*
|
|
112
104
|
* The injection point processing enables loose coupling between BMAD and TTS providers:
|
|
113
105
|
* - BMAD source contains injection markers (not actual TTS code)
|
|
114
106
|
* - At install-time, markers are replaced OR removed based on user preference
|
|
@@ -139,16 +131,6 @@ class Installer {
|
|
|
139
131
|
// Read the file content
|
|
140
132
|
let content = await fs.readFile(sourcePath, 'utf8');
|
|
141
133
|
|
|
142
|
-
// Replace {bmad_folder} placeholder with actual folder name
|
|
143
|
-
if (content.includes('{bmad_folder}')) {
|
|
144
|
-
content = content.replaceAll('{bmad_folder}', bmadFolderName);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
|
|
148
|
-
if (content.includes('{*bmad_folder*}')) {
|
|
149
|
-
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
134
|
// Process AgentVibes injection points (pass targetPath for tracking)
|
|
153
135
|
content = this.processTTSInjectionPoints(content, targetPath);
|
|
154
136
|
|
|
@@ -338,6 +320,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
338
320
|
|
|
339
321
|
for (const ide of newlySelectedIdes) {
|
|
340
322
|
// List of IDEs that have interactive prompts
|
|
323
|
+
//TODO: Why is this here, hardcoding this list here is bad, fix me!
|
|
341
324
|
const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes(
|
|
342
325
|
ide,
|
|
343
326
|
);
|
|
@@ -361,7 +344,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
361
344
|
} else if (ideModule.default) {
|
|
362
345
|
SetupClass = ideModule.default;
|
|
363
346
|
} else {
|
|
364
|
-
// Skip if no setup class found
|
|
365
347
|
continue;
|
|
366
348
|
}
|
|
367
349
|
|
|
@@ -407,12 +389,21 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
407
389
|
* @param {string[]} config.ides - IDEs to configure
|
|
408
390
|
* @param {boolean} config.skipIde - Skip IDE configuration
|
|
409
391
|
*/
|
|
410
|
-
async install(
|
|
411
|
-
//
|
|
412
|
-
|
|
392
|
+
async install(originalConfig) {
|
|
393
|
+
// Clone config to avoid mutating the caller's object
|
|
394
|
+
const config = { ...originalConfig };
|
|
395
|
+
|
|
396
|
+
// Check if core config was already collected in UI
|
|
397
|
+
const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
|
|
398
|
+
|
|
399
|
+
// Only display logo if core config wasn't already collected (meaning we're not continuing from UI)
|
|
400
|
+
if (!hasCoreConfig) {
|
|
401
|
+
// Display BMAD logo
|
|
402
|
+
CLIUtils.displayLogo();
|
|
413
403
|
|
|
414
|
-
|
|
415
|
-
|
|
404
|
+
// Display welcome message
|
|
405
|
+
CLIUtils.displaySection('BMad™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
|
|
406
|
+
}
|
|
416
407
|
|
|
417
408
|
// Note: Legacy V4 detection now happens earlier in UI.promptInstall()
|
|
418
409
|
// before any config collection, so we don't need to check again here
|
|
@@ -420,7 +411,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
420
411
|
const projectDir = path.resolve(config.directory);
|
|
421
412
|
|
|
422
413
|
// If core config was pre-collected (from interactive mode), use it
|
|
423
|
-
if (config.coreConfig) {
|
|
414
|
+
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
|
424
415
|
this.configCollector.collectedConfig.core = config.coreConfig;
|
|
425
416
|
// Also store in allAnswers for cross-referencing
|
|
426
417
|
this.configCollector.allAnswers = {};
|
|
@@ -431,16 +422,118 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
431
422
|
|
|
432
423
|
// Collect configurations for modules (skip if quick update already collected them)
|
|
433
424
|
let moduleConfigs;
|
|
425
|
+
let customModulePaths = new Map();
|
|
426
|
+
|
|
434
427
|
if (config._quickUpdate) {
|
|
435
428
|
// Quick update already collected all configs, use them directly
|
|
436
429
|
moduleConfigs = this.configCollector.collectedConfig;
|
|
430
|
+
|
|
431
|
+
// For quick update, populate customModulePaths from _customModuleSources
|
|
432
|
+
if (config._customModuleSources) {
|
|
433
|
+
for (const [moduleId, customInfo] of config._customModuleSources) {
|
|
434
|
+
customModulePaths.set(moduleId, customInfo.sourcePath);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
437
|
} else {
|
|
438
|
-
//
|
|
439
|
-
|
|
438
|
+
// For regular updates (modify flow), check manifest for custom module sources
|
|
439
|
+
if (config._isUpdate && config._existingInstall && config._existingInstall.customModules) {
|
|
440
|
+
for (const customModule of config._existingInstall.customModules) {
|
|
441
|
+
// Ensure we have an absolute sourcePath
|
|
442
|
+
let absoluteSourcePath = customModule.sourcePath;
|
|
443
|
+
|
|
444
|
+
// Check if sourcePath is a cache-relative path (starts with _config)
|
|
445
|
+
if (absoluteSourcePath && absoluteSourcePath.startsWith('_config')) {
|
|
446
|
+
// Convert cache-relative path to absolute path
|
|
447
|
+
absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
|
|
448
|
+
}
|
|
449
|
+
// If no sourcePath but we have relativePath, convert it
|
|
450
|
+
else if (!absoluteSourcePath && customModule.relativePath) {
|
|
451
|
+
// relativePath is relative to the project root (parent of bmad dir)
|
|
452
|
+
absoluteSourcePath = path.resolve(projectDir, customModule.relativePath);
|
|
453
|
+
}
|
|
454
|
+
// Ensure sourcePath is absolute for anything else
|
|
455
|
+
else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
|
|
456
|
+
absoluteSourcePath = path.resolve(absoluteSourcePath);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (absoluteSourcePath) {
|
|
460
|
+
customModulePaths.set(customModule.id, absoluteSourcePath);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Build custom module paths map from customContent
|
|
466
|
+
|
|
467
|
+
// Handle selectedFiles (from existing install path or manual directory input)
|
|
468
|
+
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
|
469
|
+
const customHandler = new CustomHandler();
|
|
470
|
+
for (const customFile of config.customContent.selectedFiles) {
|
|
471
|
+
const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory));
|
|
472
|
+
if (customInfo && customInfo.id) {
|
|
473
|
+
customModulePaths.set(customInfo.id, customInfo.path);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Handle new custom content sources from UI
|
|
479
|
+
if (config.customContent && config.customContent.sources) {
|
|
480
|
+
for (const source of config.customContent.sources) {
|
|
481
|
+
customModulePaths.set(source.id, source.path);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Handle cachedModules (from new install path where modules are cached)
|
|
486
|
+
// Only include modules that were actually selected for installation
|
|
487
|
+
if (config.customContent && config.customContent.cachedModules) {
|
|
488
|
+
// Get selected cached module IDs (if available)
|
|
489
|
+
const selectedCachedIds = config.customContent.selectedCachedModules || [];
|
|
490
|
+
// If no selection info, include all cached modules (for backward compatibility)
|
|
491
|
+
const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected;
|
|
492
|
+
|
|
493
|
+
for (const cachedModule of config.customContent.cachedModules) {
|
|
494
|
+
// For cached modules, the path is the cachePath which contains the module.yaml
|
|
495
|
+
if (
|
|
496
|
+
cachedModule.id &&
|
|
497
|
+
cachedModule.cachePath && // Include if selected or if we should include all
|
|
498
|
+
(shouldIncludeAll || selectedCachedIds.includes(cachedModule.id))
|
|
499
|
+
) {
|
|
500
|
+
customModulePaths.set(cachedModule.id, cachedModule.cachePath);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Get list of all modules including custom modules
|
|
506
|
+
// Order: core first, then official modules, then custom modules
|
|
507
|
+
const allModulesForConfig = ['core'];
|
|
508
|
+
|
|
509
|
+
// Add official modules (excluding core and any custom modules)
|
|
510
|
+
const officialModules = (config.modules || []).filter((m) => m !== 'core' && !customModulePaths.has(m));
|
|
511
|
+
allModulesForConfig.push(...officialModules);
|
|
512
|
+
|
|
513
|
+
// Add custom modules at the end
|
|
514
|
+
for (const [moduleId] of customModulePaths) {
|
|
515
|
+
if (!allModulesForConfig.includes(moduleId)) {
|
|
516
|
+
allModulesForConfig.push(moduleId);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Check if core was already collected in UI
|
|
521
|
+
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
|
522
|
+
// Core already collected, skip it in config collection
|
|
523
|
+
const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
|
|
524
|
+
moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, path.resolve(config.directory), {
|
|
525
|
+
customModulePaths,
|
|
526
|
+
});
|
|
527
|
+
} else {
|
|
528
|
+
// Core not collected yet, include it
|
|
529
|
+
moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
|
|
530
|
+
customModulePaths,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
440
533
|
}
|
|
441
534
|
|
|
442
|
-
//
|
|
443
|
-
const bmadFolderName =
|
|
535
|
+
// Always use _bmad as the folder name
|
|
536
|
+
const bmadFolderName = '_bmad';
|
|
444
537
|
this.bmadFolderName = bmadFolderName; // Store for use in other methods
|
|
445
538
|
|
|
446
539
|
// Store AgentVibes configuration for injection point processing
|
|
@@ -449,6 +542,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
449
542
|
// Set bmad folder name on module manager and IDE manager for placeholder replacement
|
|
450
543
|
this.moduleManager.setBmadFolderName(bmadFolderName);
|
|
451
544
|
this.moduleManager.setCoreConfig(moduleConfigs.core || {});
|
|
545
|
+
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
452
546
|
this.ideManager.setBmadFolderName(bmadFolderName);
|
|
453
547
|
|
|
454
548
|
// Tool selection will be collected after we determine if it's a reinstall/update/new install
|
|
@@ -459,63 +553,15 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
459
553
|
// Resolve target directory (path.resolve handles platform differences)
|
|
460
554
|
const projectDir = path.resolve(config.directory);
|
|
461
555
|
|
|
462
|
-
// Check if bmad_folder has changed from existing installation (only if project dir exists)
|
|
463
556
|
let existingBmadDir = null;
|
|
464
557
|
let existingBmadFolderName = null;
|
|
465
558
|
|
|
466
559
|
if (await fs.pathExists(projectDir)) {
|
|
467
|
-
|
|
560
|
+
const result = await this.findBmadDir(projectDir);
|
|
561
|
+
existingBmadDir = result.bmadDir;
|
|
468
562
|
existingBmadFolderName = path.basename(existingBmadDir);
|
|
469
563
|
}
|
|
470
564
|
|
|
471
|
-
const targetBmadDir = path.join(projectDir, bmadFolderName);
|
|
472
|
-
|
|
473
|
-
// If bmad_folder changed during update/upgrade, back up old folder and do fresh install
|
|
474
|
-
if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== bmadFolderName) {
|
|
475
|
-
spinner.stop();
|
|
476
|
-
console.log(chalk.yellow(`\n⚠️ bmad_folder has changed: ${existingBmadFolderName} → ${bmadFolderName}`));
|
|
477
|
-
console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
|
|
478
|
-
|
|
479
|
-
const inquirer = require('inquirer');
|
|
480
|
-
const { confirmFreshInstall } = await inquirer.prompt([
|
|
481
|
-
{
|
|
482
|
-
type: 'confirm',
|
|
483
|
-
name: 'confirmFreshInstall',
|
|
484
|
-
message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
|
|
485
|
-
default: true,
|
|
486
|
-
},
|
|
487
|
-
]);
|
|
488
|
-
|
|
489
|
-
if (!confirmFreshInstall) {
|
|
490
|
-
console.log(chalk.yellow('Installation cancelled.'));
|
|
491
|
-
return { success: false, cancelled: true };
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
spinner.start('Backing up existing installation...');
|
|
495
|
-
|
|
496
|
-
// Find a unique backup name
|
|
497
|
-
let backupDir = `${existingBmadDir}-bak`;
|
|
498
|
-
let counter = 1;
|
|
499
|
-
while (await fs.pathExists(backupDir)) {
|
|
500
|
-
backupDir = `${existingBmadDir}-bak-${counter}`;
|
|
501
|
-
counter++;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Rename the old folder to backup
|
|
505
|
-
await fs.move(existingBmadDir, backupDir);
|
|
506
|
-
|
|
507
|
-
spinner.succeed(`Backed up ${existingBmadFolderName} → ${path.basename(backupDir)}`);
|
|
508
|
-
console.log(chalk.cyan('\n📋 Important:'));
|
|
509
|
-
console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
|
|
510
|
-
console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
|
|
511
|
-
console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
|
|
512
|
-
console.log(chalk.dim(` - To the new location:`));
|
|
513
|
-
console.log(chalk.dim(` ${bmadFolderName}/_cfg/`));
|
|
514
|
-
console.log('');
|
|
515
|
-
|
|
516
|
-
spinner.start('Starting fresh installation...');
|
|
517
|
-
}
|
|
518
|
-
|
|
519
565
|
// Create a project directory if it doesn't exist (user already confirmed)
|
|
520
566
|
if (!(await fs.pathExists(projectDir))) {
|
|
521
567
|
spinner.text = 'Creating installation directory...';
|
|
@@ -547,9 +593,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
547
593
|
|
|
548
594
|
// Check if user already decided what to do (from early menu in ui.js)
|
|
549
595
|
let action = null;
|
|
550
|
-
if (config.
|
|
551
|
-
action = 'reinstall';
|
|
552
|
-
} else if (config.actionType === 'update') {
|
|
596
|
+
if (config.actionType === 'update') {
|
|
553
597
|
action = 'update';
|
|
554
598
|
} else {
|
|
555
599
|
// Fallback: Ask the user (backwards compatibility for other code paths)
|
|
@@ -561,64 +605,49 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
561
605
|
action = promptResult.action;
|
|
562
606
|
}
|
|
563
607
|
|
|
564
|
-
if (action === '
|
|
565
|
-
console.log('Installation cancelled.');
|
|
566
|
-
return { success: false, cancelled: true };
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (action === 'reinstall') {
|
|
570
|
-
// Warn about destructive operation
|
|
571
|
-
console.log(chalk.red.bold('\n⚠️ WARNING: This is a destructive operation!'));
|
|
572
|
-
console.log(chalk.red('All custom files and modifications in the bmad directory will be lost.'));
|
|
573
|
-
|
|
574
|
-
const inquirer = require('inquirer');
|
|
575
|
-
const { confirmReinstall } = await inquirer.prompt([
|
|
576
|
-
{
|
|
577
|
-
type: 'confirm',
|
|
578
|
-
name: 'confirmReinstall',
|
|
579
|
-
message: chalk.yellow('Are you sure you want to delete and reinstall?'),
|
|
580
|
-
default: false,
|
|
581
|
-
},
|
|
582
|
-
]);
|
|
583
|
-
|
|
584
|
-
if (!confirmReinstall) {
|
|
585
|
-
console.log('Installation cancelled.');
|
|
586
|
-
return { success: false, cancelled: true };
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// Remember previously configured IDEs before deleting
|
|
590
|
-
config._previouslyConfiguredIdes = existingInstall.ides || [];
|
|
591
|
-
|
|
592
|
-
// Remove existing installation
|
|
593
|
-
await fs.remove(bmadDir);
|
|
594
|
-
console.log(chalk.green('✓ Removed existing installation\n'));
|
|
595
|
-
|
|
596
|
-
// Mark this as a full reinstall so we re-collect IDE configurations
|
|
597
|
-
config._isFullReinstall = true;
|
|
598
|
-
} else if (action === 'update') {
|
|
608
|
+
if (action === 'update') {
|
|
599
609
|
// Store that we're updating for later processing
|
|
600
610
|
config._isUpdate = true;
|
|
601
611
|
config._existingInstall = existingInstall;
|
|
602
612
|
|
|
603
613
|
// Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
|
|
604
614
|
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
|
605
|
-
console.log(chalk.dim(`DEBUG: Read ${existingFilesManifest.length} files from manifest`));
|
|
606
|
-
console.log(chalk.dim(`DEBUG: Manifest has hashes: ${existingFilesManifest.some((f) => f.hash)}`));
|
|
607
|
-
|
|
608
615
|
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
|
609
616
|
|
|
610
|
-
console.log(chalk.dim(`DEBUG: Found ${customFiles.length} custom files, ${modifiedFiles.length} modified files`));
|
|
611
|
-
if (modifiedFiles.length > 0) {
|
|
612
|
-
console.log(chalk.yellow('DEBUG: Modified files:'));
|
|
613
|
-
for (const f of modifiedFiles) console.log(chalk.dim(` - ${f.path}`));
|
|
614
|
-
}
|
|
615
|
-
|
|
616
617
|
config._customFiles = customFiles;
|
|
617
618
|
config._modifiedFiles = modifiedFiles;
|
|
618
619
|
|
|
620
|
+
// Also check cache directory for custom modules (like quick update does)
|
|
621
|
+
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
|
622
|
+
if (await fs.pathExists(cacheDir)) {
|
|
623
|
+
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
|
624
|
+
|
|
625
|
+
for (const cachedModule of cachedModules) {
|
|
626
|
+
if (cachedModule.isDirectory()) {
|
|
627
|
+
const moduleId = cachedModule.name;
|
|
628
|
+
|
|
629
|
+
// Skip if we already have this module from manifest
|
|
630
|
+
if (customModulePaths.has(moduleId)) {
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const cachedPath = path.join(cacheDir, moduleId);
|
|
635
|
+
|
|
636
|
+
// Check if this is actually a custom module (has module.yaml)
|
|
637
|
+
const moduleYamlPath = path.join(cachedPath, 'module.yaml');
|
|
638
|
+
if (await fs.pathExists(moduleYamlPath)) {
|
|
639
|
+
customModulePaths.set(moduleId, cachedPath);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Update module manager with the new custom module paths from cache
|
|
645
|
+
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
646
|
+
}
|
|
647
|
+
|
|
619
648
|
// If there are custom files, back them up temporarily
|
|
620
649
|
if (customFiles.length > 0) {
|
|
621
|
-
const tempBackupDir = path.join(projectDir, '
|
|
650
|
+
const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
|
|
622
651
|
await fs.ensureDir(tempBackupDir);
|
|
623
652
|
|
|
624
653
|
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
|
@@ -635,23 +664,19 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
635
664
|
|
|
636
665
|
// For modified files, back them up to temp directory (will be restored as .bak files after install)
|
|
637
666
|
if (modifiedFiles.length > 0) {
|
|
638
|
-
const tempModifiedBackupDir = path.join(projectDir, '
|
|
667
|
+
const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
|
|
639
668
|
await fs.ensureDir(tempModifiedBackupDir);
|
|
640
669
|
|
|
641
|
-
console.log(chalk.yellow(`\nDEBUG: Backing up ${modifiedFiles.length} modified files to temp location`));
|
|
642
670
|
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
|
643
671
|
for (const modifiedFile of modifiedFiles) {
|
|
644
672
|
const relativePath = path.relative(bmadDir, modifiedFile.path);
|
|
645
673
|
const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
|
|
646
|
-
console.log(chalk.dim(`DEBUG: Backing up ${relativePath} to temp`));
|
|
647
674
|
await fs.ensureDir(path.dirname(tempBackupPath));
|
|
648
675
|
await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
|
|
649
676
|
}
|
|
650
677
|
spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
|
|
651
678
|
|
|
652
679
|
config._tempModifiedBackupDir = tempModifiedBackupDir;
|
|
653
|
-
} else {
|
|
654
|
-
console.log(chalk.dim('DEBUG: No modified files detected'));
|
|
655
680
|
}
|
|
656
681
|
}
|
|
657
682
|
} else if (existingInstall.installed && config._quickUpdate) {
|
|
@@ -667,9 +692,37 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
667
692
|
config._customFiles = customFiles;
|
|
668
693
|
config._modifiedFiles = modifiedFiles;
|
|
669
694
|
|
|
695
|
+
// Also check cache directory for custom modules (like quick update does)
|
|
696
|
+
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
|
697
|
+
if (await fs.pathExists(cacheDir)) {
|
|
698
|
+
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
|
699
|
+
|
|
700
|
+
for (const cachedModule of cachedModules) {
|
|
701
|
+
if (cachedModule.isDirectory()) {
|
|
702
|
+
const moduleId = cachedModule.name;
|
|
703
|
+
|
|
704
|
+
// Skip if we already have this module from manifest
|
|
705
|
+
if (customModulePaths.has(moduleId)) {
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const cachedPath = path.join(cacheDir, moduleId);
|
|
710
|
+
|
|
711
|
+
// Check if this is actually a custom module (has module.yaml)
|
|
712
|
+
const moduleYamlPath = path.join(cachedPath, 'module.yaml');
|
|
713
|
+
if (await fs.pathExists(moduleYamlPath)) {
|
|
714
|
+
customModulePaths.set(moduleId, cachedPath);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Update module manager with the new custom module paths from cache
|
|
720
|
+
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
721
|
+
}
|
|
722
|
+
|
|
670
723
|
// Back up custom files
|
|
671
724
|
if (customFiles.length > 0) {
|
|
672
|
-
const tempBackupDir = path.join(projectDir, '
|
|
725
|
+
const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
|
|
673
726
|
await fs.ensureDir(tempBackupDir);
|
|
674
727
|
|
|
675
728
|
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
|
@@ -685,7 +738,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
685
738
|
|
|
686
739
|
// Back up modified files
|
|
687
740
|
if (modifiedFiles.length > 0) {
|
|
688
|
-
const tempModifiedBackupDir = path.join(projectDir, '
|
|
741
|
+
const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
|
|
689
742
|
await fs.ensureDir(tempModifiedBackupDir);
|
|
690
743
|
|
|
691
744
|
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
|
@@ -740,7 +793,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
740
793
|
config.skipIde = toolSelection.skipIde;
|
|
741
794
|
const ideConfigurations = toolSelection.configurations;
|
|
742
795
|
|
|
743
|
-
// Check if spinner is already running (e.g., from folder name change scenario)
|
|
744
796
|
if (spinner.isSpinning) {
|
|
745
797
|
spinner.text = 'Continuing installation...';
|
|
746
798
|
} else {
|
|
@@ -751,7 +803,26 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
751
803
|
spinner.text = 'Creating directory structure...';
|
|
752
804
|
await this.createDirectoryStructure(bmadDir);
|
|
753
805
|
|
|
754
|
-
//
|
|
806
|
+
// Cache custom modules if any
|
|
807
|
+
if (customModulePaths && customModulePaths.size > 0) {
|
|
808
|
+
spinner.text = 'Caching custom modules...';
|
|
809
|
+
const { CustomModuleCache } = require('./custom-module-cache');
|
|
810
|
+
const customCache = new CustomModuleCache(bmadDir);
|
|
811
|
+
|
|
812
|
+
for (const [moduleId, sourcePath] of customModulePaths) {
|
|
813
|
+
const cachedInfo = await customCache.cacheModule(moduleId, sourcePath, {
|
|
814
|
+
sourcePath: sourcePath, // Store original path for updates
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// Update the customModulePaths to use the cached location
|
|
818
|
+
customModulePaths.set(moduleId, cachedInfo.cachePath);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Update module manager with the cached paths
|
|
822
|
+
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
823
|
+
spinner.succeed('Custom modules cached');
|
|
824
|
+
}
|
|
825
|
+
|
|
755
826
|
const projectRoot = getProjectRoot();
|
|
756
827
|
|
|
757
828
|
// Step 1: Install core module first (if requested)
|
|
@@ -792,9 +863,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
792
863
|
// Regular custom content from user input (non-cached)
|
|
793
864
|
if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
|
794
865
|
// Add custom modules to the installation list
|
|
866
|
+
const customHandler = new CustomHandler();
|
|
795
867
|
for (const customFile of finalCustomContent.selectedFiles) {
|
|
796
|
-
const { CustomHandler } = require('../custom/handler');
|
|
797
|
-
const customHandler = new CustomHandler();
|
|
798
868
|
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
799
869
|
if (customInfo && customInfo.id) {
|
|
800
870
|
allModules.push(customInfo.id);
|
|
@@ -809,31 +879,32 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
809
879
|
|
|
810
880
|
const modulesToInstall = allModules;
|
|
811
881
|
|
|
882
|
+
// For dependency resolution, we only need regular modules (not custom modules)
|
|
883
|
+
// Custom modules are already installed in _bmad and don't need dependency resolution from source
|
|
884
|
+
const regularModulesForResolution = allModules.filter((module) => {
|
|
885
|
+
// Check if this is a custom module
|
|
886
|
+
const isCustom =
|
|
887
|
+
customModulePaths.has(module) ||
|
|
888
|
+
(finalCustomContent && finalCustomContent.cachedModules && finalCustomContent.cachedModules.some((cm) => cm.id === module)) ||
|
|
889
|
+
(finalCustomContent &&
|
|
890
|
+
finalCustomContent.selected &&
|
|
891
|
+
finalCustomContent.selectedFiles &&
|
|
892
|
+
finalCustomContent.selectedFiles.some((f) => f.includes(module)));
|
|
893
|
+
return !isCustom;
|
|
894
|
+
});
|
|
895
|
+
|
|
812
896
|
// For dependency resolution, we need to pass the project root
|
|
813
897
|
// Create a temporary module manager that knows about custom content locations
|
|
814
898
|
const tempModuleManager = new ModuleManager({
|
|
815
|
-
scanProjectForModules: true,
|
|
816
899
|
bmadDir: bmadDir, // Pass bmadDir so we can check cache
|
|
817
900
|
});
|
|
818
901
|
|
|
819
|
-
|
|
820
|
-
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
|
821
|
-
// The dependency resolver needs to know about these modules
|
|
822
|
-
// We'll handle custom modules separately in the installation loop
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
|
|
902
|
+
const resolution = await this.dependencyResolver.resolve(projectRoot, regularModulesForResolution, {
|
|
826
903
|
verbose: config.verbose,
|
|
827
904
|
moduleManager: tempModuleManager,
|
|
828
905
|
});
|
|
829
906
|
|
|
830
|
-
|
|
831
|
-
spinner.succeed('Dependencies resolved');
|
|
832
|
-
} else {
|
|
833
|
-
spinner.succeed('Dependencies resolved');
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
// Core is already installed above, skip if included in resolution
|
|
907
|
+
spinner.succeed('Dependencies resolved');
|
|
837
908
|
|
|
838
909
|
// Install modules with their dependencies
|
|
839
910
|
if (allModules && allModules.length > 0) {
|
|
@@ -846,7 +917,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
846
917
|
}
|
|
847
918
|
installedModuleNames.add(moduleName);
|
|
848
919
|
|
|
849
|
-
|
|
920
|
+
// Show appropriate message based on whether this is a quick update
|
|
921
|
+
const isQuickUpdate = config._quickUpdate || false;
|
|
922
|
+
spinner.start(`${isQuickUpdate ? 'Updating' : 'Installing'} module: ${moduleName}...`);
|
|
850
923
|
|
|
851
924
|
// Check if this is a custom module
|
|
852
925
|
let isCustomModule = false;
|
|
@@ -872,8 +945,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
872
945
|
customInfo = config._customModuleSources.get(moduleName);
|
|
873
946
|
isCustomModule = true;
|
|
874
947
|
|
|
875
|
-
// Check if this is a cached module (source path starts with
|
|
876
|
-
if (
|
|
948
|
+
// Check if this is a cached module (source path starts with _config)
|
|
949
|
+
if (
|
|
950
|
+
customInfo.sourcePath &&
|
|
951
|
+
(customInfo.sourcePath.startsWith('_config') || customInfo.sourcePath.includes('_config/custom'))
|
|
952
|
+
) {
|
|
877
953
|
useCache = true;
|
|
878
954
|
// Make sure we have the right path structure
|
|
879
955
|
if (!customInfo.path) {
|
|
@@ -884,7 +960,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
884
960
|
|
|
885
961
|
// Finally check regular custom content
|
|
886
962
|
if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
|
887
|
-
const { CustomHandler } = require('../custom/handler');
|
|
888
963
|
const customHandler = new CustomHandler();
|
|
889
964
|
for (const customFile of finalCustomContent.selectedFiles) {
|
|
890
965
|
const info = await customHandler.getCustomInfo(customFile, projectDir);
|
|
@@ -897,78 +972,33 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
897
972
|
}
|
|
898
973
|
|
|
899
974
|
if (isCustomModule && customInfo) {
|
|
900
|
-
//
|
|
901
|
-
|
|
902
|
-
|
|
975
|
+
// Custom modules are now installed via ModuleManager just like standard modules
|
|
976
|
+
// The custom module path should already be in customModulePaths from earlier setup
|
|
977
|
+
if (!customModulePaths.has(moduleName) && customInfo.path) {
|
|
978
|
+
customModulePaths.set(moduleName, customInfo.path);
|
|
979
|
+
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
980
|
+
}
|
|
903
981
|
|
|
904
|
-
|
|
905
|
-
const moduleTargetPath = path.join(bmadDir, moduleName);
|
|
906
|
-
await fs.ensureDir(moduleTargetPath);
|
|
982
|
+
const collectedModuleConfig = moduleConfigs[moduleName] || {};
|
|
907
983
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
984
|
+
// Use ModuleManager to install the custom module
|
|
985
|
+
await this.moduleManager.install(
|
|
986
|
+
moduleName,
|
|
987
|
+
bmadDir,
|
|
912
988
|
(filePath) => {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
989
|
+
this.installedFiles.add(filePath);
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
isCustom: true,
|
|
993
|
+
moduleConfig: collectedModuleConfig,
|
|
994
|
+
isQuickUpdate: config._quickUpdate || false,
|
|
995
|
+
installer: this,
|
|
917
996
|
},
|
|
918
997
|
);
|
|
919
998
|
|
|
920
|
-
//
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const customDir = path.join(tempCustomPath, 'custom');
|
|
924
|
-
if (await fs.pathExists(customDir)) {
|
|
925
|
-
// Move contents to module directory
|
|
926
|
-
const items = await fs.readdir(customDir);
|
|
927
|
-
for (const item of items) {
|
|
928
|
-
const srcPath = path.join(customDir, item);
|
|
929
|
-
const destPath = path.join(moduleTargetPath, item);
|
|
930
|
-
|
|
931
|
-
// If destination exists, remove it first (or we could merge)
|
|
932
|
-
if (await fs.pathExists(destPath)) {
|
|
933
|
-
await fs.remove(destPath);
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
await fs.move(srcPath, destPath);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
await fs.remove(tempCustomPath);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// Create module config
|
|
943
|
-
await this.generateModuleConfigs(bmadDir, { [moduleName]: { ...config.coreConfig, ...customInfo.config } });
|
|
944
|
-
|
|
945
|
-
// Store custom module info for later manifest update
|
|
946
|
-
if (!config._customModulesToTrack) {
|
|
947
|
-
config._customModulesToTrack = [];
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
// For cached modules, use appropriate path handling
|
|
951
|
-
let sourcePath;
|
|
952
|
-
if (useCache) {
|
|
953
|
-
// Check if we have cached modules info (from initial install)
|
|
954
|
-
if (finalCustomContent && finalCustomContent.cachedModules) {
|
|
955
|
-
sourcePath = finalCustomContent.cachedModules.find((m) => m.id === moduleName)?.relativePath;
|
|
956
|
-
} else {
|
|
957
|
-
// During update, the sourcePath is already cache-relative if it starts with _cfg
|
|
958
|
-
sourcePath =
|
|
959
|
-
customInfo.sourcePath && customInfo.sourcePath.startsWith('_cfg')
|
|
960
|
-
? customInfo.sourcePath
|
|
961
|
-
: path.relative(bmadDir, customInfo.path || customInfo.sourcePath);
|
|
962
|
-
}
|
|
963
|
-
} else {
|
|
964
|
-
sourcePath = path.resolve(customInfo.path || customInfo.sourcePath);
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
config._customModulesToTrack.push({
|
|
968
|
-
id: customInfo.id,
|
|
969
|
-
name: customInfo.name,
|
|
970
|
-
sourcePath: sourcePath,
|
|
971
|
-
installDate: new Date().toISOString(),
|
|
999
|
+
// Create module config (include collected config from module.yaml prompts)
|
|
1000
|
+
await this.generateModuleConfigs(bmadDir, {
|
|
1001
|
+
[moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig },
|
|
972
1002
|
});
|
|
973
1003
|
} else {
|
|
974
1004
|
// Regular module installation
|
|
@@ -980,7 +1010,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
980
1010
|
}
|
|
981
1011
|
}
|
|
982
1012
|
|
|
983
|
-
spinner.succeed(`Module installed: ${moduleName}`);
|
|
1013
|
+
spinner.succeed(`Module ${isQuickUpdate ? 'updated' : 'installed'}: ${moduleName}`);
|
|
984
1014
|
}
|
|
985
1015
|
|
|
986
1016
|
// Install partial modules (only dependencies)
|
|
@@ -1002,71 +1032,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1002
1032
|
}
|
|
1003
1033
|
}
|
|
1004
1034
|
|
|
1005
|
-
//
|
|
1006
|
-
// Process custom content that wasn't installed as modules
|
|
1007
|
-
// This is now handled in the module installation loop above
|
|
1008
|
-
// This section is kept for backward compatibility with any custom content
|
|
1009
|
-
// that doesn't have a module structure
|
|
1010
|
-
const remainingCustomContent = [];
|
|
1011
|
-
if (
|
|
1012
|
-
config.customContent &&
|
|
1013
|
-
config.customContent.hasCustomContent &&
|
|
1014
|
-
config.customContent.customPath &&
|
|
1015
|
-
config.customContent.selected &&
|
|
1016
|
-
config.customContent.selectedFiles
|
|
1017
|
-
) {
|
|
1018
|
-
// Filter out custom modules that were already installed
|
|
1019
|
-
for (const customFile of config.customContent.selectedFiles) {
|
|
1020
|
-
const { CustomHandler } = require('../custom/handler');
|
|
1021
|
-
const customHandler = new CustomHandler();
|
|
1022
|
-
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
1023
|
-
|
|
1024
|
-
// Skip if this was installed as a module
|
|
1025
|
-
if (!customInfo || !customInfo.id || !allModules.includes(customInfo.id)) {
|
|
1026
|
-
remainingCustomContent.push(customFile);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
if (remainingCustomContent.length > 0) {
|
|
1032
|
-
spinner.start('Installing remaining custom content...');
|
|
1033
|
-
const { CustomHandler } = require('../custom/handler');
|
|
1034
|
-
const customHandler = new CustomHandler();
|
|
1035
|
-
|
|
1036
|
-
// Use the remaining files
|
|
1037
|
-
const customFiles = remainingCustomContent;
|
|
1038
|
-
|
|
1039
|
-
if (customFiles.length > 0) {
|
|
1040
|
-
console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
|
|
1041
|
-
for (const customFile of customFiles) {
|
|
1042
|
-
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
1043
|
-
if (customInfo) {
|
|
1044
|
-
console.log(chalk.dim(` • ${customInfo.name} (${customInfo.relativePath})`));
|
|
1045
|
-
|
|
1046
|
-
// Install the custom content
|
|
1047
|
-
const result = await customHandler.install(
|
|
1048
|
-
customInfo.path,
|
|
1049
|
-
bmadDir,
|
|
1050
|
-
{ ...config.coreConfig, ...customInfo.config },
|
|
1051
|
-
(filePath) => {
|
|
1052
|
-
// Track installed files
|
|
1053
|
-
this.installedFiles.push(filePath);
|
|
1054
|
-
},
|
|
1055
|
-
);
|
|
1056
|
-
|
|
1057
|
-
if (result.errors.length > 0) {
|
|
1058
|
-
console.log(chalk.yellow(` ⚠️ ${result.errors.length} error(s) occurred`));
|
|
1059
|
-
for (const error of result.errors) {
|
|
1060
|
-
console.log(chalk.dim(` - ${error}`));
|
|
1061
|
-
}
|
|
1062
|
-
} else {
|
|
1063
|
-
console.log(chalk.green(` ✓ Installed ${result.agentsInstalled} agents, ${result.workflowsInstalled} workflows`));
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
spinner.succeed('Custom content installed');
|
|
1069
|
-
}
|
|
1035
|
+
// All content is now installed as modules - no separate custom content handling needed
|
|
1070
1036
|
|
|
1071
1037
|
// Generate clean config.yaml files for each installed module
|
|
1072
1038
|
spinner.start('Generating module configurations...');
|
|
@@ -1078,13 +1044,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1078
1044
|
// Customize templates are now created in processAgentFiles when building YAML agents
|
|
1079
1045
|
|
|
1080
1046
|
// Pre-register manifest files that will be created (except files-manifest.csv to avoid recursion)
|
|
1081
|
-
const cfgDir = path.join(bmadDir, '
|
|
1082
|
-
this.installedFiles.
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
path.join(cfgDir, 'task-manifest.csv'),
|
|
1087
|
-
);
|
|
1047
|
+
const cfgDir = path.join(bmadDir, '_config');
|
|
1048
|
+
this.installedFiles.add(path.join(cfgDir, 'manifest.yaml'));
|
|
1049
|
+
this.installedFiles.add(path.join(cfgDir, 'workflow-manifest.csv'));
|
|
1050
|
+
this.installedFiles.add(path.join(cfgDir, 'agent-manifest.csv'));
|
|
1051
|
+
this.installedFiles.add(path.join(cfgDir, 'task-manifest.csv'));
|
|
1088
1052
|
|
|
1089
1053
|
// Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes BEFORE IDE setup
|
|
1090
1054
|
spinner.start('Generating workflow and agent manifests...');
|
|
@@ -1108,18 +1072,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1108
1072
|
modulesForCsvPreserve = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules;
|
|
1109
1073
|
}
|
|
1110
1074
|
|
|
1111
|
-
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, this.installedFiles, {
|
|
1075
|
+
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, [...this.installedFiles], {
|
|
1112
1076
|
ides: config.ides || [],
|
|
1113
1077
|
preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
|
|
1114
1078
|
});
|
|
1115
1079
|
|
|
1116
|
-
//
|
|
1117
|
-
if (config._customModulesToTrack && config._customModulesToTrack.length > 0) {
|
|
1118
|
-
spinner.text = 'Storing custom module sources...';
|
|
1119
|
-
for (const customModule of config._customModulesToTrack) {
|
|
1120
|
-
await this.manifest.addCustomModule(bmadDir, customModule);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1080
|
+
// Custom modules are now included in the main modules list - no separate tracking needed
|
|
1123
1081
|
|
|
1124
1082
|
spinner.succeed(
|
|
1125
1083
|
`Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
|
|
@@ -1160,7 +1118,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1160
1118
|
|
|
1161
1119
|
// Pass pre-collected configuration to avoid re-prompting
|
|
1162
1120
|
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
1163
|
-
selectedModules:
|
|
1121
|
+
selectedModules: allModules || [],
|
|
1164
1122
|
preCollectedConfig: ideConfigurations[ide] || null,
|
|
1165
1123
|
verbose: config.verbose,
|
|
1166
1124
|
});
|
|
@@ -1180,24 +1138,24 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1180
1138
|
console.log = originalLog;
|
|
1181
1139
|
|
|
1182
1140
|
if (spinner.isSpinning) {
|
|
1183
|
-
spinner.succeed(`Configured ${validIdes.
|
|
1141
|
+
spinner.succeed(`Configured: ${validIdes.join(', ')}`);
|
|
1184
1142
|
} else {
|
|
1185
|
-
console.log(chalk.green(`✓ Configured ${validIdes.
|
|
1143
|
+
console.log(chalk.green(`✓ Configured: ${validIdes.join(', ')}`));
|
|
1186
1144
|
}
|
|
1187
1145
|
}
|
|
1188
|
-
|
|
1189
|
-
// Copy IDE-specific documentation (only for valid IDEs)
|
|
1190
|
-
const validIdesForDocs = (config.ides || []).filter((ide) => ide && typeof ide === 'string');
|
|
1191
|
-
if (validIdesForDocs.length > 0) {
|
|
1192
|
-
spinner.start('Copying IDE documentation...');
|
|
1193
|
-
await this.copyIdeDocumentation(validIdesForDocs, bmadDir);
|
|
1194
|
-
spinner.succeed('IDE documentation copied');
|
|
1195
|
-
}
|
|
1196
1146
|
}
|
|
1197
1147
|
|
|
1198
1148
|
// Run module-specific installers after IDE setup
|
|
1199
1149
|
spinner.start('Running module-specific installers...');
|
|
1200
1150
|
|
|
1151
|
+
// Create a conditional logger based on verbose mode
|
|
1152
|
+
const verboseMode = process.env.BMAD_VERBOSE_INSTALL === 'true' || config.verbose;
|
|
1153
|
+
const moduleLogger = {
|
|
1154
|
+
log: (msg) => (verboseMode ? console.log(msg) : {}), // Only log in verbose mode
|
|
1155
|
+
error: (msg) => console.error(msg), // Always show errors
|
|
1156
|
+
warn: (msg) => console.warn(msg), // Always show warnings
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1201
1159
|
// Run core module installer if core was installed
|
|
1202
1160
|
if (config.installCore || resolution.byModule.core) {
|
|
1203
1161
|
spinner.text = 'Running core module installer...';
|
|
@@ -1206,11 +1164,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1206
1164
|
installedIDEs: config.ides || [],
|
|
1207
1165
|
moduleConfig: moduleConfigs.core || {},
|
|
1208
1166
|
coreConfig: moduleConfigs.core || {},
|
|
1209
|
-
logger:
|
|
1210
|
-
log: (msg) => console.log(msg),
|
|
1211
|
-
error: (msg) => console.error(msg),
|
|
1212
|
-
warn: (msg) => console.warn(msg),
|
|
1213
|
-
},
|
|
1167
|
+
logger: moduleLogger,
|
|
1214
1168
|
});
|
|
1215
1169
|
}
|
|
1216
1170
|
|
|
@@ -1224,11 +1178,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1224
1178
|
installedIDEs: config.ides || [],
|
|
1225
1179
|
moduleConfig: moduleConfigs[moduleName] || {},
|
|
1226
1180
|
coreConfig: moduleConfigs.core || {},
|
|
1227
|
-
logger:
|
|
1228
|
-
log: (msg) => console.log(msg),
|
|
1229
|
-
error: (msg) => console.error(msg),
|
|
1230
|
-
warn: (msg) => console.warn(msg),
|
|
1231
|
-
},
|
|
1181
|
+
logger: moduleLogger,
|
|
1232
1182
|
});
|
|
1233
1183
|
}
|
|
1234
1184
|
}
|
|
@@ -1295,44 +1245,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1295
1245
|
// Report custom and modified files if any were found
|
|
1296
1246
|
if (customFiles.length > 0) {
|
|
1297
1247
|
console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`));
|
|
1298
|
-
console.log(chalk.dim('The following custom files were found and restored:\n'));
|
|
1299
|
-
for (const file of customFiles) {
|
|
1300
|
-
console.log(chalk.dim(` - ${path.relative(bmadDir, file)}`));
|
|
1301
|
-
}
|
|
1302
|
-
console.log('');
|
|
1303
1248
|
}
|
|
1304
1249
|
|
|
1305
1250
|
if (modifiedFiles.length > 0) {
|
|
1306
|
-
console.log(chalk.yellow(`\n⚠️
|
|
1307
|
-
console.log(chalk.dim('The following files were modified and backed up with .bak extension:\n'));
|
|
1308
|
-
for (const file of modifiedFiles) {
|
|
1309
|
-
console.log(chalk.dim(` - ${file.relativePath} → ${file.relativePath}.bak`));
|
|
1310
|
-
}
|
|
1311
|
-
console.log(chalk.dim('\nThese files have been updated with the new version.'));
|
|
1312
|
-
console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n'));
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
// Reinstall custom agents from _cfg/custom/agents/ sources
|
|
1316
|
-
const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
|
|
1317
|
-
if (customAgentResults.count > 0) {
|
|
1318
|
-
console.log(chalk.green(`\n✓ Reinstalled ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`));
|
|
1319
|
-
for (const agent of customAgentResults.agents) {
|
|
1320
|
-
console.log(chalk.dim(` - ${agent}`));
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
// Replace {agent_sidecar_folder} placeholders in all agent files
|
|
1325
|
-
console.log(chalk.dim('\n Configuring agent sidecar folders...'));
|
|
1326
|
-
const sidecarResults = await replaceAgentSidecarFolders(bmadDir);
|
|
1327
|
-
|
|
1328
|
-
if (sidecarResults.filesReplaced > 0) {
|
|
1251
|
+
console.log(chalk.yellow(`\n⚠️ User modified files detected: ${modifiedFiles.length}`));
|
|
1329
1252
|
console.log(
|
|
1330
|
-
chalk.
|
|
1331
|
-
|
|
1253
|
+
chalk.dim(
|
|
1254
|
+
'\nThese user modified files have been updated with the new version, search the project for .bak files that had your customizations.',
|
|
1332
1255
|
),
|
|
1333
1256
|
);
|
|
1334
|
-
|
|
1335
|
-
console.log(chalk.dim(' No agent sidecar references found'));
|
|
1257
|
+
console.log(chalk.dim('Remove these .bak files it no longer needed\n'));
|
|
1336
1258
|
}
|
|
1337
1259
|
|
|
1338
1260
|
// Display completion message
|
|
@@ -1369,7 +1291,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1369
1291
|
|
|
1370
1292
|
try {
|
|
1371
1293
|
const projectDir = path.resolve(config.directory);
|
|
1372
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
1294
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
1373
1295
|
const existingInstall = await this.detector.detect(bmadDir);
|
|
1374
1296
|
|
|
1375
1297
|
if (!existingInstall.installed) {
|
|
@@ -1385,12 +1307,44 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1385
1307
|
|
|
1386
1308
|
// Check for custom modules with missing sources before update
|
|
1387
1309
|
const customModuleSources = new Map();
|
|
1310
|
+
|
|
1311
|
+
// Check manifest for backward compatibility
|
|
1388
1312
|
if (existingInstall.customModules) {
|
|
1389
1313
|
for (const customModule of existingInstall.customModules) {
|
|
1390
1314
|
customModuleSources.set(customModule.id, customModule);
|
|
1391
1315
|
}
|
|
1392
1316
|
}
|
|
1393
1317
|
|
|
1318
|
+
// Also check cache directory
|
|
1319
|
+
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
|
1320
|
+
if (await fs.pathExists(cacheDir)) {
|
|
1321
|
+
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
|
1322
|
+
|
|
1323
|
+
for (const cachedModule of cachedModules) {
|
|
1324
|
+
if (cachedModule.isDirectory()) {
|
|
1325
|
+
const moduleId = cachedModule.name;
|
|
1326
|
+
|
|
1327
|
+
// Skip if we already have this module
|
|
1328
|
+
if (customModuleSources.has(moduleId)) {
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
const cachedPath = path.join(cacheDir, moduleId);
|
|
1333
|
+
|
|
1334
|
+
// Check if this is actually a custom module (has module.yaml)
|
|
1335
|
+
const moduleYamlPath = path.join(cachedPath, 'module.yaml');
|
|
1336
|
+
if (await fs.pathExists(moduleYamlPath)) {
|
|
1337
|
+
customModuleSources.set(moduleId, {
|
|
1338
|
+
id: moduleId,
|
|
1339
|
+
name: moduleId,
|
|
1340
|
+
sourcePath: path.join('_config', 'custom', moduleId), // Relative path
|
|
1341
|
+
cached: true,
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1394
1348
|
if (customModuleSources.size > 0) {
|
|
1395
1349
|
spinner.stop();
|
|
1396
1350
|
console.log(chalk.yellow('\nChecking custom module sources before update...'));
|
|
@@ -1454,7 +1408,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1454
1408
|
*/
|
|
1455
1409
|
async getStatus(directory) {
|
|
1456
1410
|
const projectDir = path.resolve(directory);
|
|
1457
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
1411
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
1458
1412
|
return await this.detector.detect(bmadDir);
|
|
1459
1413
|
}
|
|
1460
1414
|
|
|
@@ -1470,7 +1424,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1470
1424
|
*/
|
|
1471
1425
|
async uninstall(directory) {
|
|
1472
1426
|
const projectDir = path.resolve(directory);
|
|
1473
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
1427
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
1474
1428
|
|
|
1475
1429
|
if (await fs.pathExists(bmadDir)) {
|
|
1476
1430
|
await fs.remove(bmadDir);
|
|
@@ -1487,8 +1441,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1487
1441
|
*/
|
|
1488
1442
|
async createDirectoryStructure(bmadDir) {
|
|
1489
1443
|
await fs.ensureDir(bmadDir);
|
|
1490
|
-
await fs.ensureDir(path.join(bmadDir, '
|
|
1491
|
-
await fs.ensureDir(path.join(bmadDir, '
|
|
1444
|
+
await fs.ensureDir(path.join(bmadDir, '_config'));
|
|
1445
|
+
await fs.ensureDir(path.join(bmadDir, '_config', 'agents'));
|
|
1446
|
+
await fs.ensureDir(path.join(bmadDir, '_config', 'custom'));
|
|
1492
1447
|
}
|
|
1493
1448
|
|
|
1494
1449
|
/**
|
|
@@ -1497,7 +1452,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1497
1452
|
* @param {Object} moduleConfigs - Collected configuration values
|
|
1498
1453
|
*/
|
|
1499
1454
|
async generateModuleConfigs(bmadDir, moduleConfigs) {
|
|
1500
|
-
const yaml = require('
|
|
1455
|
+
const yaml = require('yaml');
|
|
1501
1456
|
|
|
1502
1457
|
// Extract core config values to share with other modules
|
|
1503
1458
|
const coreConfig = moduleConfigs.core || {};
|
|
@@ -1505,7 +1460,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1505
1460
|
// Get all installed module directories
|
|
1506
1461
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
1507
1462
|
const installedModules = entries
|
|
1508
|
-
.filter((entry) => entry.isDirectory() && entry.name !== '
|
|
1463
|
+
.filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs')
|
|
1509
1464
|
.map((entry) => entry.name);
|
|
1510
1465
|
|
|
1511
1466
|
// Generate config.yaml for each installed module
|
|
@@ -1543,12 +1498,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1543
1498
|
coreSection = '\n# Core Configuration Values\n';
|
|
1544
1499
|
}
|
|
1545
1500
|
|
|
1501
|
+
// Clean the config to remove any non-serializable values (like functions)
|
|
1502
|
+
const cleanConfig = structuredClone(finalConfig);
|
|
1503
|
+
|
|
1546
1504
|
// Convert config to YAML
|
|
1547
|
-
let yamlContent = yaml.
|
|
1505
|
+
let yamlContent = yaml.stringify(cleanConfig, {
|
|
1548
1506
|
indent: 2,
|
|
1549
|
-
lineWidth:
|
|
1550
|
-
|
|
1551
|
-
sortKeys: false,
|
|
1507
|
+
lineWidth: 0,
|
|
1508
|
+
minContentWidth: 0,
|
|
1552
1509
|
});
|
|
1553
1510
|
|
|
1554
1511
|
// If we have core values, reorganize the YAML to group them with their comment
|
|
@@ -1580,7 +1537,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1580
1537
|
await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
|
|
1581
1538
|
|
|
1582
1539
|
// Track the config file in installedFiles
|
|
1583
|
-
this.installedFiles.
|
|
1540
|
+
this.installedFiles.add(configPath);
|
|
1584
1541
|
}
|
|
1585
1542
|
}
|
|
1586
1543
|
}
|
|
@@ -1593,14 +1550,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1593
1550
|
async installCoreWithDependencies(bmadDir, coreFiles) {
|
|
1594
1551
|
const sourcePath = getModulePath('core');
|
|
1595
1552
|
const targetPath = path.join(bmadDir, 'core');
|
|
1596
|
-
|
|
1597
|
-
// Install full core
|
|
1598
1553
|
await this.installCore(bmadDir);
|
|
1599
|
-
|
|
1600
|
-
// If there are specific dependency files, ensure they're included
|
|
1601
|
-
if (coreFiles) {
|
|
1602
|
-
// Already handled by installCore for core module
|
|
1603
|
-
}
|
|
1604
1554
|
}
|
|
1605
1555
|
|
|
1606
1556
|
/**
|
|
@@ -1619,11 +1569,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1619
1569
|
moduleName,
|
|
1620
1570
|
bmadDir,
|
|
1621
1571
|
(filePath) => {
|
|
1622
|
-
this.installedFiles.
|
|
1572
|
+
this.installedFiles.add(filePath);
|
|
1623
1573
|
},
|
|
1624
1574
|
{
|
|
1625
1575
|
skipModuleInstaller: true, // We'll run it later after IDE setup
|
|
1626
1576
|
moduleConfig: moduleConfig, // Pass module config for conditional filtering
|
|
1577
|
+
installer: this,
|
|
1627
1578
|
},
|
|
1628
1579
|
);
|
|
1629
1580
|
|
|
@@ -1656,7 +1607,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1656
1607
|
|
|
1657
1608
|
if (await fs.pathExists(sourcePath)) {
|
|
1658
1609
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1659
|
-
this.installedFiles.
|
|
1610
|
+
this.installedFiles.add(targetPath);
|
|
1660
1611
|
}
|
|
1661
1612
|
}
|
|
1662
1613
|
}
|
|
@@ -1672,7 +1623,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1672
1623
|
|
|
1673
1624
|
if (await fs.pathExists(sourcePath)) {
|
|
1674
1625
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1675
|
-
this.installedFiles.
|
|
1626
|
+
this.installedFiles.add(targetPath);
|
|
1676
1627
|
}
|
|
1677
1628
|
}
|
|
1678
1629
|
}
|
|
@@ -1688,7 +1639,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1688
1639
|
|
|
1689
1640
|
if (await fs.pathExists(sourcePath)) {
|
|
1690
1641
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1691
|
-
this.installedFiles.
|
|
1642
|
+
this.installedFiles.add(targetPath);
|
|
1692
1643
|
}
|
|
1693
1644
|
}
|
|
1694
1645
|
}
|
|
@@ -1704,7 +1655,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1704
1655
|
|
|
1705
1656
|
if (await fs.pathExists(sourcePath)) {
|
|
1706
1657
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1707
|
-
this.installedFiles.
|
|
1658
|
+
this.installedFiles.add(targetPath);
|
|
1708
1659
|
}
|
|
1709
1660
|
}
|
|
1710
1661
|
}
|
|
@@ -1719,7 +1670,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1719
1670
|
|
|
1720
1671
|
if (await fs.pathExists(dataPath)) {
|
|
1721
1672
|
await this.copyFileWithPlaceholderReplacement(dataPath, targetPath, this.bmadFolderName || 'bmad');
|
|
1722
|
-
this.installedFiles.
|
|
1673
|
+
this.installedFiles.add(targetPath);
|
|
1723
1674
|
}
|
|
1724
1675
|
}
|
|
1725
1676
|
}
|
|
@@ -1740,25 +1691,55 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1740
1691
|
const sourcePath = getModulePath('core');
|
|
1741
1692
|
const targetPath = path.join(bmadDir, 'core');
|
|
1742
1693
|
|
|
1743
|
-
// Copy core files
|
|
1744
|
-
await this.
|
|
1694
|
+
// Copy core files (skip .agent.yaml files like modules do)
|
|
1695
|
+
await this.copyCoreFiles(sourcePath, targetPath);
|
|
1696
|
+
|
|
1697
|
+
// Compile agents using the same compiler as modules
|
|
1698
|
+
const { ModuleManager } = require('../modules/manager');
|
|
1699
|
+
const moduleManager = new ModuleManager();
|
|
1700
|
+
await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
|
|
1745
1701
|
|
|
1746
1702
|
// Process agent files to inject activation block
|
|
1747
1703
|
await this.processAgentFiles(targetPath, 'core');
|
|
1748
1704
|
}
|
|
1749
1705
|
|
|
1750
1706
|
/**
|
|
1751
|
-
* Copy
|
|
1752
|
-
* @param {string} sourcePath - Source
|
|
1753
|
-
* @param {string} targetPath - Target
|
|
1707
|
+
* Copy core files (similar to copyModuleWithFiltering but for core)
|
|
1708
|
+
* @param {string} sourcePath - Source path
|
|
1709
|
+
* @param {string} targetPath - Target path
|
|
1754
1710
|
*/
|
|
1755
|
-
async
|
|
1756
|
-
// Get all files in source
|
|
1711
|
+
async copyCoreFiles(sourcePath, targetPath) {
|
|
1712
|
+
// Get all files in source
|
|
1757
1713
|
const files = await this.getFileList(sourcePath);
|
|
1758
1714
|
|
|
1759
1715
|
for (const file of files) {
|
|
1716
|
+
// Skip sub-modules directory - these are IDE-specific and handled separately
|
|
1717
|
+
if (file.startsWith('sub-modules/')) {
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// Skip sidecar directories - they are handled separately during agent compilation
|
|
1722
|
+
if (
|
|
1723
|
+
path
|
|
1724
|
+
.dirname(file)
|
|
1725
|
+
.split('/')
|
|
1726
|
+
.some((dir) => dir.toLowerCase().includes('sidecar'))
|
|
1727
|
+
) {
|
|
1728
|
+
continue;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// Skip _module-installer directory - it's only needed at install time
|
|
1732
|
+
if (file.startsWith('_module-installer/') || file === 'module.yaml') {
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1760
1736
|
// Skip config.yaml templates - we'll generate clean ones with actual values
|
|
1761
|
-
if (file === 'config.yaml' || file.endsWith('/config.yaml')) {
|
|
1737
|
+
if (file === 'config.yaml' || file.endsWith('/config.yaml') || file === 'custom.yaml' || file.endsWith('/custom.yaml')) {
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
// Skip .agent.yaml files - they will be compiled separately
|
|
1742
|
+
if (file.endsWith('.agent.yaml')) {
|
|
1762
1743
|
continue;
|
|
1763
1744
|
}
|
|
1764
1745
|
|
|
@@ -1766,7 +1747,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1766
1747
|
const targetFile = path.join(targetPath, file);
|
|
1767
1748
|
|
|
1768
1749
|
// Check if this is an agent file
|
|
1769
|
-
if (file.
|
|
1750
|
+
if (file.startsWith('agents/') && file.endsWith('.md')) {
|
|
1770
1751
|
// Read the file to check for localskip
|
|
1771
1752
|
const content = await fs.readFile(sourceFile, 'utf8');
|
|
1772
1753
|
|
|
@@ -1778,11 +1759,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1778
1759
|
}
|
|
1779
1760
|
}
|
|
1780
1761
|
|
|
1781
|
-
//
|
|
1782
|
-
|
|
1762
|
+
// Check if this is a workflow.yaml file
|
|
1763
|
+
if (file.endsWith('workflow.yaml')) {
|
|
1764
|
+
await fs.ensureDir(path.dirname(targetFile));
|
|
1765
|
+
await this.copyWorkflowYamlStripped(sourceFile, targetFile);
|
|
1766
|
+
} else {
|
|
1767
|
+
// Copy the file with placeholder replacement
|
|
1768
|
+
await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile, this.bmadFolderName || 'bmad');
|
|
1769
|
+
}
|
|
1783
1770
|
|
|
1784
1771
|
// Track the installed file
|
|
1785
|
-
this.installedFiles.
|
|
1772
|
+
this.installedFiles.add(targetFile);
|
|
1786
1773
|
}
|
|
1787
1774
|
}
|
|
1788
1775
|
|
|
@@ -1829,128 +1816,39 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1829
1816
|
|
|
1830
1817
|
// Determine project directory (parent of bmad/ directory)
|
|
1831
1818
|
const bmadDir = path.dirname(modulePath);
|
|
1832
|
-
const
|
|
1833
|
-
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
|
|
1819
|
+
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
1834
1820
|
|
|
1835
|
-
// Ensure
|
|
1821
|
+
// Ensure _config/agents directory exists
|
|
1836
1822
|
await fs.ensureDir(cfgAgentsDir);
|
|
1837
1823
|
|
|
1838
1824
|
// Get all agent files
|
|
1839
1825
|
const agentFiles = await fs.readdir(agentsPath);
|
|
1840
1826
|
|
|
1841
1827
|
for (const agentFile of agentFiles) {
|
|
1842
|
-
//
|
|
1828
|
+
// Skip .agent.yaml files - they should already be compiled by compileModuleAgents
|
|
1843
1829
|
if (agentFile.endsWith('.agent.yaml')) {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const mdPath = path.join(agentsPath, `${agentName}.md`);
|
|
1847
|
-
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1848
|
-
|
|
1849
|
-
// Create customize template if it doesn't exist
|
|
1850
|
-
if (!(await fs.pathExists(customizePath))) {
|
|
1851
|
-
const genericTemplatePath = getSourcePath('utility', 'templates', 'agent.customize.template.yaml');
|
|
1852
|
-
if (await fs.pathExists(genericTemplatePath)) {
|
|
1853
|
-
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
|
|
1854
|
-
console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
// Build YAML + customize to .md
|
|
1859
|
-
const customizeExists = await fs.pathExists(customizePath);
|
|
1860
|
-
let xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
|
|
1861
|
-
includeMetadata: true,
|
|
1862
|
-
});
|
|
1863
|
-
|
|
1864
|
-
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1865
|
-
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
1866
|
-
|
|
1867
|
-
// Replace {bmad_folder} with actual folder name
|
|
1868
|
-
xmlContent = xmlContent.replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
|
|
1869
|
-
|
|
1870
|
-
// Replace {agent_sidecar_folder} if configured
|
|
1871
|
-
const coreConfig = this.configCollector.collectedConfig.core || {};
|
|
1872
|
-
if (coreConfig.agent_sidecar_folder && xmlContent.includes('{agent_sidecar_folder}')) {
|
|
1873
|
-
xmlContent = xmlContent.replaceAll('{agent_sidecar_folder}', coreConfig.agent_sidecar_folder);
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
// Process TTS injection points (pass targetPath for tracking)
|
|
1877
|
-
xmlContent = this.processTTSInjectionPoints(xmlContent, mdPath);
|
|
1878
|
-
|
|
1879
|
-
// Check if agent has sidecar and copy it
|
|
1880
|
-
let agentYamlContent = null;
|
|
1881
|
-
let hasSidecar = false;
|
|
1882
|
-
|
|
1883
|
-
try {
|
|
1884
|
-
agentYamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
1885
|
-
const yamlLib = require('yaml');
|
|
1886
|
-
const agentYaml = yamlLib.parse(agentYamlContent);
|
|
1887
|
-
hasSidecar = agentYaml?.agent?.metadata?.hasSidecar === true;
|
|
1888
|
-
} catch {
|
|
1889
|
-
// Continue without sidecar processing
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
// Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
|
|
1893
|
-
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
|
1894
|
-
await fs.writeFile(mdPath, content, 'utf8');
|
|
1895
|
-
this.installedFiles.push(mdPath);
|
|
1896
|
-
|
|
1897
|
-
// Copy sidecar files if agent has hasSidecar flag
|
|
1898
|
-
if (hasSidecar) {
|
|
1899
|
-
const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
|
|
1900
|
-
|
|
1901
|
-
// Get agent sidecar folder from core config
|
|
1902
|
-
const coreConfigPath = path.join(bmadDir, 'bmb', 'config.yaml');
|
|
1903
|
-
let agentSidecarFolder;
|
|
1904
|
-
|
|
1905
|
-
if (await fs.pathExists(coreConfigPath)) {
|
|
1906
|
-
const yamlLib = require('yaml');
|
|
1907
|
-
const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
|
|
1908
|
-
const coreConfig = yamlLib.parse(coreConfigContent);
|
|
1909
|
-
agentSidecarFolder = coreConfig.agent_sidecar_folder || agentSidecarFolder;
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
// Resolve path variables
|
|
1913
|
-
const resolvedSidecarFolder = agentSidecarFolder
|
|
1914
|
-
.replaceAll('{project-root}', projectDir)
|
|
1915
|
-
.replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
|
|
1916
|
-
|
|
1917
|
-
// Create sidecar directory for this agent
|
|
1918
|
-
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
|
|
1919
|
-
await fs.ensureDir(agentSidecarDir);
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1920
1832
|
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1833
|
+
// Only process .md files (already compiled from YAML)
|
|
1834
|
+
if (!agentFile.endsWith('.md')) {
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1924
1837
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1838
|
+
const agentName = agentFile.replace('.md', '');
|
|
1839
|
+
const mdPath = path.join(agentsPath, agentFile);
|
|
1840
|
+
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1927
1841
|
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1842
|
+
// For .md files that are already compiled, we don't need to do much
|
|
1843
|
+
// Just ensure the customize template exists
|
|
1844
|
+
if (!(await fs.pathExists(customizePath))) {
|
|
1845
|
+
const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
|
|
1846
|
+
if (await fs.pathExists(genericTemplatePath)) {
|
|
1847
|
+
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
|
|
1848
|
+
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
|
|
1849
|
+
console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
|
|
1933
1850
|
}
|
|
1934
1851
|
}
|
|
1935
|
-
|
|
1936
|
-
// Remove the source YAML file - we can regenerate from installer source if needed
|
|
1937
|
-
await fs.remove(yamlPath);
|
|
1938
|
-
|
|
1939
|
-
console.log(chalk.dim(` Built agent: ${agentName}.md${hasSidecar ? ' (with sidecar)' : ''}`));
|
|
1940
|
-
}
|
|
1941
|
-
// Handle legacy .md agents - inject activation if needed
|
|
1942
|
-
else if (agentFile.endsWith('.md')) {
|
|
1943
|
-
const agentPath = path.join(agentsPath, agentFile);
|
|
1944
|
-
let content = await fs.readFile(agentPath, 'utf8');
|
|
1945
|
-
|
|
1946
|
-
// Check if content has agent XML and no activation block
|
|
1947
|
-
if (content.includes('<agent') && !content.includes('<activation')) {
|
|
1948
|
-
// Inject the activation block using XML handler
|
|
1949
|
-
content = this.xmlHandler.injectActivationSimple(content);
|
|
1950
|
-
// Ensure POSIX-compliant final newline
|
|
1951
|
-
const finalContent = content.endsWith('\n') ? content : content + '\n';
|
|
1952
|
-
await fs.writeFile(agentPath, finalContent, 'utf8');
|
|
1953
|
-
}
|
|
1954
1852
|
}
|
|
1955
1853
|
}
|
|
1956
1854
|
}
|
|
@@ -1962,7 +1860,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1962
1860
|
*/
|
|
1963
1861
|
async buildStandaloneAgents(bmadDir, projectDir) {
|
|
1964
1862
|
const standaloneAgentsPath = path.join(bmadDir, 'agents');
|
|
1965
|
-
const cfgAgentsDir = path.join(bmadDir, '
|
|
1863
|
+
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
1966
1864
|
|
|
1967
1865
|
// Check if standalone agents directory exists
|
|
1968
1866
|
if (!(await fs.pathExists(standaloneAgentsPath))) {
|
|
@@ -1994,8 +1892,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1994
1892
|
|
|
1995
1893
|
if (customizeExists) {
|
|
1996
1894
|
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
1997
|
-
const yaml = require('
|
|
1998
|
-
const customizeYaml = yaml.
|
|
1895
|
+
const yaml = require('yaml');
|
|
1896
|
+
const customizeYaml = yaml.parse(customizeContent);
|
|
1999
1897
|
|
|
2000
1898
|
// Detect what fields are customized (similar to rebuildAgentFiles)
|
|
2001
1899
|
if (customizeYaml) {
|
|
@@ -2063,7 +1961,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2063
1961
|
// Determine project directory (parent of bmad/ directory)
|
|
2064
1962
|
const bmadDir = path.dirname(modulePath);
|
|
2065
1963
|
const projectDir = path.dirname(bmadDir);
|
|
2066
|
-
const cfgAgentsDir = path.join(bmadDir, '
|
|
1964
|
+
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
2067
1965
|
const targetAgentsPath = path.join(modulePath, 'agents');
|
|
2068
1966
|
|
|
2069
1967
|
// Ensure target directory exists
|
|
@@ -2085,8 +1983,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2085
1983
|
|
|
2086
1984
|
if (customizeExists) {
|
|
2087
1985
|
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
2088
|
-
const yaml = require('
|
|
2089
|
-
const customizeYaml = yaml.
|
|
1986
|
+
const yaml = require('yaml');
|
|
1987
|
+
const customizeYaml = yaml.parse(customizeContent);
|
|
2090
1988
|
|
|
2091
1989
|
// Detect what fields are customized
|
|
2092
1990
|
if (customizeYaml) {
|
|
@@ -2119,34 +2017,59 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2119
2017
|
}
|
|
2120
2018
|
}
|
|
2121
2019
|
|
|
2122
|
-
//
|
|
2123
|
-
|
|
2124
|
-
includeMetadata: true,
|
|
2125
|
-
});
|
|
2020
|
+
// Read the YAML content
|
|
2021
|
+
const yamlContent = await fs.readFile(sourceYamlPath, 'utf8');
|
|
2126
2022
|
|
|
2127
|
-
//
|
|
2128
|
-
|
|
2023
|
+
// Read customize content if exists
|
|
2024
|
+
let customizeData = {};
|
|
2025
|
+
if (customizeExists) {
|
|
2026
|
+
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
2027
|
+
const yaml = require('yaml');
|
|
2028
|
+
customizeData = yaml.parse(customizeContent);
|
|
2029
|
+
}
|
|
2129
2030
|
|
|
2130
|
-
//
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2031
|
+
// Build agent answers from customize data (filter empty values)
|
|
2032
|
+
const answers = {};
|
|
2033
|
+
if (customizeData.persona) {
|
|
2034
|
+
Object.assign(answers, filterCustomizationData(customizeData.persona));
|
|
2035
|
+
}
|
|
2036
|
+
if (customizeData.agent?.metadata) {
|
|
2037
|
+
const filteredMetadata = filterCustomizationData(customizeData.agent.metadata);
|
|
2038
|
+
if (Object.keys(filteredMetadata).length > 0) {
|
|
2039
|
+
Object.assign(answers, { metadata: filteredMetadata });
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
if (customizeData.critical_actions && customizeData.critical_actions.length > 0) {
|
|
2043
|
+
answers.critical_actions = customizeData.critical_actions;
|
|
2044
|
+
}
|
|
2045
|
+
if (customizeData.memories && customizeData.memories.length > 0) {
|
|
2046
|
+
answers.memories = customizeData.memories;
|
|
2047
|
+
}
|
|
2133
2048
|
|
|
2049
|
+
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
|
|
2050
|
+
let coreConfig = {};
|
|
2134
2051
|
if (await fs.pathExists(coreConfigPath)) {
|
|
2135
|
-
const
|
|
2052
|
+
const yaml = require('yaml');
|
|
2136
2053
|
const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
|
|
2137
|
-
|
|
2138
|
-
agentSidecarFolder = coreConfig.agent_sidecar_folder;
|
|
2054
|
+
coreConfig = yaml.parse(coreConfigContent);
|
|
2139
2055
|
}
|
|
2140
2056
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2057
|
+
// Compile using the same compiler as initial installation
|
|
2058
|
+
const { compileAgent } = require('../../../lib/agent/compiler');
|
|
2059
|
+
const result = await compileAgent(yamlContent, answers, agentName, path.relative(bmadDir, targetMdPath), {
|
|
2060
|
+
config: coreConfig,
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
// Check if compilation succeeded
|
|
2064
|
+
if (!result || !result.xml) {
|
|
2065
|
+
throw new Error(`Failed to compile agent ${agentName}: No XML returned from compiler`);
|
|
2143
2066
|
}
|
|
2144
2067
|
|
|
2145
|
-
//
|
|
2146
|
-
|
|
2068
|
+
// Replace _bmad with actual folder name if needed
|
|
2069
|
+
const finalXml = result.xml.replaceAll('_bmad', path.basename(bmadDir));
|
|
2147
2070
|
|
|
2148
2071
|
// Write the rebuilt .md file with POSIX-compliant final newline
|
|
2149
|
-
const content =
|
|
2072
|
+
const content = finalXml.endsWith('\n') ? finalXml : finalXml + '\n';
|
|
2150
2073
|
await fs.writeFile(targetMdPath, content, 'utf8');
|
|
2151
2074
|
|
|
2152
2075
|
// Display result with customizations if any
|
|
@@ -2165,23 +2088,28 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2165
2088
|
* @returns {Object} Compilation results
|
|
2166
2089
|
*/
|
|
2167
2090
|
async compileAgents(config) {
|
|
2168
|
-
const ora = require('ora');
|
|
2169
|
-
const spinner = ora('Starting agent compilation...').start();
|
|
2170
|
-
|
|
2171
2091
|
try {
|
|
2172
2092
|
const projectDir = path.resolve(config.directory);
|
|
2173
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
2093
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
2174
2094
|
|
|
2175
2095
|
// Check if bmad directory exists
|
|
2176
2096
|
if (!(await fs.pathExists(bmadDir))) {
|
|
2177
|
-
spinner.fail('No BMAD installation found');
|
|
2178
2097
|
throw new Error(`BMAD not installed at ${bmadDir}`);
|
|
2179
2098
|
}
|
|
2180
2099
|
|
|
2100
|
+
// Get installed modules from manifest
|
|
2101
|
+
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
2102
|
+
let installedModules = [];
|
|
2103
|
+
let manifest = null;
|
|
2104
|
+
if (await fs.pathExists(manifestPath)) {
|
|
2105
|
+
const manifestContent = await fs.readFile(manifestPath, 'utf8');
|
|
2106
|
+
const yaml = require('yaml');
|
|
2107
|
+
manifest = yaml.parse(manifestContent);
|
|
2108
|
+
installedModules = manifest.modules || [];
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2181
2111
|
// Check for custom modules with missing sources
|
|
2182
|
-
const manifest = await this.manifest.read(bmadDir);
|
|
2183
2112
|
if (manifest && manifest.customModules && manifest.customModules.length > 0) {
|
|
2184
|
-
spinner.stop();
|
|
2185
2113
|
console.log(chalk.yellow('\nChecking custom module sources before compilation...'));
|
|
2186
2114
|
|
|
2187
2115
|
const customModuleSources = new Map();
|
|
@@ -2190,26 +2118,21 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2190
2118
|
}
|
|
2191
2119
|
|
|
2192
2120
|
const projectRoot = getProjectRoot();
|
|
2193
|
-
const installedModules = manifest.modules || [];
|
|
2194
2121
|
await this.handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, 'compile-agents', installedModules);
|
|
2195
|
-
|
|
2196
|
-
spinner.start('Rebuilding agent files...');
|
|
2197
2122
|
}
|
|
2198
2123
|
|
|
2199
2124
|
let agentCount = 0;
|
|
2200
2125
|
let taskCount = 0;
|
|
2201
2126
|
|
|
2202
2127
|
// Process all modules in bmad directory
|
|
2203
|
-
spinner.text = 'Rebuilding agent files...';
|
|
2204
2128
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
2205
2129
|
|
|
2206
2130
|
for (const entry of entries) {
|
|
2207
|
-
if (entry.isDirectory() && entry.name !== '
|
|
2131
|
+
if (entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs') {
|
|
2208
2132
|
const modulePath = path.join(bmadDir, entry.name);
|
|
2209
2133
|
|
|
2210
2134
|
// Special handling for standalone agents in bmad/agents/ directory
|
|
2211
2135
|
if (entry.name === 'agents') {
|
|
2212
|
-
spinner.text = 'Building standalone agents...';
|
|
2213
2136
|
await this.buildStandaloneAgents(bmadDir, projectDir);
|
|
2214
2137
|
|
|
2215
2138
|
// Count standalone agents
|
|
@@ -2241,60 +2164,22 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2241
2164
|
}
|
|
2242
2165
|
}
|
|
2243
2166
|
|
|
2244
|
-
// Reinstall custom agents from _cfg/custom/agents/ sources
|
|
2245
|
-
spinner.start('Rebuilding custom agents...');
|
|
2246
|
-
const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
|
|
2247
|
-
if (customAgentResults.count > 0) {
|
|
2248
|
-
spinner.succeed(`Rebuilt ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`);
|
|
2249
|
-
agentCount += customAgentResults.count;
|
|
2250
|
-
} else {
|
|
2251
|
-
spinner.succeed('No custom agents found to rebuild');
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
// Skip full manifest regeneration during compileAgents to preserve custom agents
|
|
2255
|
-
// Custom agents are already added to manifests during individual installation
|
|
2256
|
-
// Only regenerate YAML manifest for IDE updates if needed
|
|
2257
|
-
const existingManifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
|
2258
|
-
let existingIdes = [];
|
|
2259
|
-
if (await fs.pathExists(existingManifestPath)) {
|
|
2260
|
-
const manifestContent = await fs.readFile(existingManifestPath, 'utf8');
|
|
2261
|
-
const yaml = require('js-yaml');
|
|
2262
|
-
const manifest = yaml.load(manifestContent);
|
|
2263
|
-
existingIdes = manifest.ides || [];
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
2167
|
// Update IDE configurations using the existing IDE list from manifest
|
|
2267
|
-
if (
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
for (const ide of existingIdes) {
|
|
2271
|
-
spinner.text = `Updating ${ide}...`;
|
|
2272
|
-
|
|
2273
|
-
// Stop spinner before IDE setup to prevent blocking any potential prompts
|
|
2274
|
-
// However, we pass _alreadyConfigured to skip all prompts during compile
|
|
2275
|
-
spinner.stop();
|
|
2276
|
-
|
|
2168
|
+
if (manifest && manifest.ides && manifest.ides.length > 0) {
|
|
2169
|
+
for (const ide of manifest.ides) {
|
|
2277
2170
|
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
2278
2171
|
selectedModules: installedModules,
|
|
2279
2172
|
skipModuleInstall: true, // Skip module installation, just update IDE files
|
|
2280
2173
|
verbose: config.verbose,
|
|
2281
2174
|
preCollectedConfig: { _alreadyConfigured: true }, // Skip all interactive prompts during compile
|
|
2282
2175
|
});
|
|
2283
|
-
|
|
2284
|
-
// Restart spinner for next IDE
|
|
2285
|
-
if (existingIdes.indexOf(ide) < existingIdes.length - 1) {
|
|
2286
|
-
spinner.start('Updating IDE configurations...');
|
|
2287
|
-
}
|
|
2288
2176
|
}
|
|
2289
|
-
|
|
2290
2177
|
console.log(chalk.green('✓ IDE configurations updated'));
|
|
2291
2178
|
} else {
|
|
2292
2179
|
console.log(chalk.yellow('⚠️ No IDEs configured. Skipping IDE update.'));
|
|
2293
2180
|
}
|
|
2294
|
-
|
|
2295
2181
|
return { agentCount, taskCount };
|
|
2296
2182
|
} catch (error) {
|
|
2297
|
-
spinner.fail('Compilation failed');
|
|
2298
2183
|
throw error;
|
|
2299
2184
|
}
|
|
2300
2185
|
}
|
|
@@ -2312,6 +2197,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2312
2197
|
} else {
|
|
2313
2198
|
// Selective update - preserve user modifications
|
|
2314
2199
|
await this.fileOps.syncDirectory(sourcePath, targetPath);
|
|
2200
|
+
|
|
2201
|
+
// Recompile agents (#1133)
|
|
2202
|
+
const { ModuleManager } = require('../modules/manager');
|
|
2203
|
+
const moduleManager = new ModuleManager();
|
|
2204
|
+
await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
|
|
2205
|
+
await this.processAgentFiles(targetPath, 'core');
|
|
2315
2206
|
}
|
|
2316
2207
|
}
|
|
2317
2208
|
|
|
@@ -2326,7 +2217,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2326
2217
|
|
|
2327
2218
|
try {
|
|
2328
2219
|
const projectDir = path.resolve(config.directory);
|
|
2329
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
2220
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
2330
2221
|
|
|
2331
2222
|
// Check if bmad directory exists
|
|
2332
2223
|
if (!(await fs.pathExists(bmadDir))) {
|
|
@@ -2342,35 +2233,35 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2342
2233
|
const configuredIdes = existingInstall.ides || [];
|
|
2343
2234
|
const projectRoot = path.dirname(bmadDir);
|
|
2344
2235
|
|
|
2345
|
-
// Get custom module sources from
|
|
2236
|
+
// Get custom module sources from cache
|
|
2346
2237
|
const customModuleSources = new Map();
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
let absoluteSourcePath = customModule.sourcePath;
|
|
2238
|
+
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
|
2239
|
+
if (await fs.pathExists(cacheDir)) {
|
|
2240
|
+
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
|
2351
2241
|
|
|
2352
|
-
|
|
2353
|
-
if (
|
|
2354
|
-
|
|
2355
|
-
absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
|
|
2356
|
-
}
|
|
2357
|
-
// If no sourcePath but we have relativePath, convert it
|
|
2358
|
-
else if (!absoluteSourcePath && customModule.relativePath) {
|
|
2359
|
-
// relativePath is relative to the project root (parent of bmad dir)
|
|
2360
|
-
absoluteSourcePath = path.resolve(projectRoot, customModule.relativePath);
|
|
2361
|
-
}
|
|
2362
|
-
// Ensure sourcePath is absolute for anything else
|
|
2363
|
-
else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
|
|
2364
|
-
absoluteSourcePath = path.resolve(absoluteSourcePath);
|
|
2365
|
-
}
|
|
2242
|
+
for (const cachedModule of cachedModules) {
|
|
2243
|
+
if (cachedModule.isDirectory()) {
|
|
2244
|
+
const moduleId = cachedModule.name;
|
|
2366
2245
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
};
|
|
2246
|
+
// Skip if we already have this module from manifest
|
|
2247
|
+
if (customModuleSources.has(moduleId)) {
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2372
2250
|
|
|
2373
|
-
|
|
2251
|
+
const cachedPath = path.join(cacheDir, moduleId);
|
|
2252
|
+
|
|
2253
|
+
// Check if this is actually a custom module (has module.yaml)
|
|
2254
|
+
const moduleYamlPath = path.join(cachedPath, 'module.yaml');
|
|
2255
|
+
if (await fs.pathExists(moduleYamlPath)) {
|
|
2256
|
+
// For quick update, we always rebuild from cache
|
|
2257
|
+
customModuleSources.set(moduleId, {
|
|
2258
|
+
id: moduleId,
|
|
2259
|
+
name: moduleId, // We'll read the actual name if needed
|
|
2260
|
+
sourcePath: cachedPath,
|
|
2261
|
+
cached: true, // Flag to indicate this is from cache
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2374
2265
|
}
|
|
2375
2266
|
}
|
|
2376
2267
|
|
|
@@ -2402,126 +2293,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2402
2293
|
}
|
|
2403
2294
|
}
|
|
2404
2295
|
|
|
2405
|
-
// Check for untracked custom modules (installed but not in manifest)
|
|
2406
|
-
const untrackedCustomModules = [];
|
|
2407
|
-
for (const installedModule of installedModules) {
|
|
2408
|
-
// Skip standard modules and core
|
|
2409
|
-
const standardModuleIds = ['bmb', 'bmgd', 'bmm', 'cis', 'core'];
|
|
2410
|
-
if (standardModuleIds.includes(installedModule)) {
|
|
2411
|
-
continue;
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
// Check if this installed module is not tracked in customModules
|
|
2415
|
-
if (!customModuleSources.has(installedModule)) {
|
|
2416
|
-
const modulePath = path.join(bmadDir, installedModule);
|
|
2417
|
-
if (await fs.pathExists(modulePath)) {
|
|
2418
|
-
untrackedCustomModules.push({
|
|
2419
|
-
id: installedModule,
|
|
2420
|
-
name: installedModule, // We don't have the original name
|
|
2421
|
-
path: modulePath,
|
|
2422
|
-
untracked: true,
|
|
2423
|
-
});
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
// If we found untracked custom modules, offer to track them
|
|
2429
|
-
if (untrackedCustomModules.length > 0) {
|
|
2430
|
-
spinner.stop();
|
|
2431
|
-
console.log(chalk.yellow(`\n⚠️ Found ${untrackedCustomModules.length} custom module(s) not tracked in manifest:`));
|
|
2432
|
-
|
|
2433
|
-
for (const untracked of untrackedCustomModules) {
|
|
2434
|
-
console.log(chalk.dim(` • ${untracked.id} (installed at ${path.relative(projectRoot, untracked.path)})`));
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
|
-
const { trackModules } = await inquirer.prompt([
|
|
2438
|
-
{
|
|
2439
|
-
type: 'confirm',
|
|
2440
|
-
name: 'trackModules',
|
|
2441
|
-
message: chalk.cyan('Would you like to scan for their source locations?'),
|
|
2442
|
-
default: true,
|
|
2443
|
-
},
|
|
2444
|
-
]);
|
|
2445
|
-
|
|
2446
|
-
if (trackModules) {
|
|
2447
|
-
const { scanDirectory } = await inquirer.prompt([
|
|
2448
|
-
{
|
|
2449
|
-
type: 'input',
|
|
2450
|
-
name: 'scanDirectory',
|
|
2451
|
-
message: 'Enter directory to scan for custom module sources (or leave blank to skip):',
|
|
2452
|
-
default: projectRoot,
|
|
2453
|
-
validate: async (input) => {
|
|
2454
|
-
if (input && input.trim() !== '') {
|
|
2455
|
-
const expandedPath = path.resolve(input.trim());
|
|
2456
|
-
if (!(await fs.pathExists(expandedPath))) {
|
|
2457
|
-
return 'Directory does not exist';
|
|
2458
|
-
}
|
|
2459
|
-
const stats = await fs.stat(expandedPath);
|
|
2460
|
-
if (!stats.isDirectory()) {
|
|
2461
|
-
return 'Path must be a directory';
|
|
2462
|
-
}
|
|
2463
|
-
}
|
|
2464
|
-
return true;
|
|
2465
|
-
},
|
|
2466
|
-
},
|
|
2467
|
-
]);
|
|
2468
|
-
|
|
2469
|
-
if (scanDirectory && scanDirectory.trim() !== '') {
|
|
2470
|
-
console.log(chalk.dim('\nScanning for custom module sources...'));
|
|
2471
|
-
|
|
2472
|
-
// Scan for all module.yaml files
|
|
2473
|
-
const allModulePaths = await this.moduleManager.findModulesInProject(scanDirectory);
|
|
2474
|
-
const { ModuleManager } = require('../modules/manager');
|
|
2475
|
-
const mm = new ModuleManager({ scanProjectForModules: true });
|
|
2476
|
-
|
|
2477
|
-
for (const untracked of untrackedCustomModules) {
|
|
2478
|
-
let foundSource = null;
|
|
2479
|
-
|
|
2480
|
-
// Try to find by module ID
|
|
2481
|
-
for (const modulePath of allModulePaths) {
|
|
2482
|
-
try {
|
|
2483
|
-
const moduleInfo = await mm.getModuleInfo(modulePath);
|
|
2484
|
-
if (moduleInfo && moduleInfo.id === untracked.id) {
|
|
2485
|
-
foundSource = {
|
|
2486
|
-
path: modulePath,
|
|
2487
|
-
info: moduleInfo,
|
|
2488
|
-
};
|
|
2489
|
-
break;
|
|
2490
|
-
}
|
|
2491
|
-
} catch {
|
|
2492
|
-
// Continue searching
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
if (foundSource) {
|
|
2497
|
-
console.log(chalk.green(` ✓ Found source for ${untracked.id}: ${path.relative(projectRoot, foundSource.path)}`));
|
|
2498
|
-
|
|
2499
|
-
// Add to manifest
|
|
2500
|
-
await this.manifest.addCustomModule(bmadDir, {
|
|
2501
|
-
id: untracked.id,
|
|
2502
|
-
name: foundSource.info.name || untracked.name,
|
|
2503
|
-
sourcePath: path.resolve(foundSource.path),
|
|
2504
|
-
installDate: new Date().toISOString(),
|
|
2505
|
-
tracked: true,
|
|
2506
|
-
});
|
|
2507
|
-
|
|
2508
|
-
// Add to customModuleSources for processing
|
|
2509
|
-
customModuleSources.set(untracked.id, {
|
|
2510
|
-
id: untracked.id,
|
|
2511
|
-
name: foundSource.info.name || untracked.name,
|
|
2512
|
-
sourcePath: path.resolve(foundSource.path),
|
|
2513
|
-
});
|
|
2514
|
-
} else {
|
|
2515
|
-
console.log(chalk.yellow(` ⚠ Could not find source for ${untracked.id}`));
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
}
|
|
2520
|
-
|
|
2521
|
-
console.log(chalk.dim('\nUntracked custom modules will remain installed but cannot be updated without their source.'));
|
|
2522
|
-
spinner.start('Preparing update...');
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
2296
|
// Handle missing custom module sources using shared method
|
|
2526
2297
|
const customModuleResult = await this.handleMissingCustomSources(
|
|
2527
2298
|
customModuleSources,
|
|
@@ -2531,18 +2302,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2531
2302
|
installedModules,
|
|
2532
2303
|
);
|
|
2533
2304
|
|
|
2534
|
-
|
|
2535
|
-
let validCustomModules = [];
|
|
2536
|
-
let keptModulesWithoutSources = [];
|
|
2537
|
-
|
|
2538
|
-
if (Array.isArray(customModuleResult)) {
|
|
2539
|
-
// Old format - just an array
|
|
2540
|
-
validCustomModules = customModuleResult;
|
|
2541
|
-
} else if (customModuleResult && typeof customModuleResult === 'object') {
|
|
2542
|
-
// New format - object with two arrays
|
|
2543
|
-
validCustomModules = customModuleResult.validCustomModules || [];
|
|
2544
|
-
keptModulesWithoutSources = customModuleResult.keptModulesWithoutSources || [];
|
|
2545
|
-
}
|
|
2305
|
+
const { validCustomModules, keptModulesWithoutSources } = customModuleResult;
|
|
2546
2306
|
|
|
2547
2307
|
const customModulesFromManifest = validCustomModules.map((m) => ({
|
|
2548
2308
|
...m,
|
|
@@ -2550,18 +2310,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2550
2310
|
hasUpdate: true,
|
|
2551
2311
|
}));
|
|
2552
2312
|
|
|
2553
|
-
// Add untracked modules to the update list but mark them as untrackable
|
|
2554
|
-
for (const untracked of untrackedCustomModules) {
|
|
2555
|
-
if (!customModuleSources.has(untracked.id)) {
|
|
2556
|
-
customModulesFromManifest.push({
|
|
2557
|
-
...untracked,
|
|
2558
|
-
isCustom: true,
|
|
2559
|
-
hasUpdate: false, // Can't update without source
|
|
2560
|
-
untracked: true,
|
|
2561
|
-
});
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
|
|
2565
2313
|
const allAvailableModules = [...availableModules, ...customModulesFromManifest];
|
|
2566
2314
|
const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
|
|
2567
2315
|
|
|
@@ -2617,20 +2365,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2617
2365
|
lastModified: new Date().toISOString(),
|
|
2618
2366
|
};
|
|
2619
2367
|
|
|
2620
|
-
// Check if bmad_folder has changed
|
|
2621
|
-
const existingBmadFolderName = path.basename(bmadDir);
|
|
2622
|
-
const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
|
|
2623
|
-
|
|
2624
|
-
if (existingBmadFolderName === newBmadFolderName) {
|
|
2625
|
-
// Normal quick update - start the spinner
|
|
2626
|
-
console.log(chalk.cyan('Updating BMAD installation...'));
|
|
2627
|
-
} else {
|
|
2628
|
-
// Folder name has changed - stop spinner and let install() handle it
|
|
2629
|
-
spinner.stop();
|
|
2630
|
-
console.log(chalk.yellow(`\n⚠️ Folder name will change: ${existingBmadFolderName} → ${newBmadFolderName}`));
|
|
2631
|
-
console.log(chalk.yellow('The installer will handle the folder migration.\n'));
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
2368
|
// Build the config object for the installer
|
|
2635
2369
|
const installConfig = {
|
|
2636
2370
|
directory: projectDir,
|
|
@@ -2680,11 +2414,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2680
2414
|
type: 'list',
|
|
2681
2415
|
name: 'action',
|
|
2682
2416
|
message: 'What would you like to do?',
|
|
2683
|
-
choices: [
|
|
2684
|
-
{ name: 'Update existing installation', value: 'update' },
|
|
2685
|
-
{ name: 'Remove and reinstall', value: 'reinstall' },
|
|
2686
|
-
{ name: 'Cancel', value: 'cancel' },
|
|
2687
|
-
],
|
|
2417
|
+
choices: [{ name: 'Update existing installation', value: 'update' }],
|
|
2688
2418
|
},
|
|
2689
2419
|
]);
|
|
2690
2420
|
}
|
|
@@ -2698,14 +2428,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2698
2428
|
console.log(chalk.yellow.bold('\n⚠️ Legacy BMAD v4 detected'));
|
|
2699
2429
|
console.log(chalk.dim('The installer found legacy artefacts in your project.\n'));
|
|
2700
2430
|
|
|
2701
|
-
// Separate
|
|
2431
|
+
// Separate _bmad* folders (auto-backup) from other offending paths (manual cleanup)
|
|
2702
2432
|
const bmadFolders = legacyV4.offenders.filter((p) => {
|
|
2703
2433
|
const name = path.basename(p);
|
|
2704
|
-
return name.startsWith('
|
|
2434
|
+
return name.startsWith('_bmad'); // Only dot-prefixed folders get auto-backed up
|
|
2705
2435
|
});
|
|
2706
2436
|
const otherOffenders = legacyV4.offenders.filter((p) => {
|
|
2707
2437
|
const name = path.basename(p);
|
|
2708
|
-
return !name.startsWith('
|
|
2438
|
+
return !name.startsWith('_bmad'); // Everything else is manual cleanup
|
|
2709
2439
|
});
|
|
2710
2440
|
|
|
2711
2441
|
const inquirer = require('inquirer');
|
|
@@ -2738,7 +2468,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2738
2468
|
}
|
|
2739
2469
|
}
|
|
2740
2470
|
|
|
2741
|
-
// Handle
|
|
2471
|
+
// Handle _bmad* folders with automatic backup
|
|
2742
2472
|
if (bmadFolders.length > 0) {
|
|
2743
2473
|
console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:'));
|
|
2744
2474
|
for (const p of bmadFolders) console.log(chalk.dim(` - ${p}`));
|
|
@@ -2782,7 +2512,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2782
2512
|
* @returns {Array} Array of file entries from files-manifest.csv
|
|
2783
2513
|
*/
|
|
2784
2514
|
async readFilesManifest(bmadDir) {
|
|
2785
|
-
const filesManifestPath = path.join(bmadDir, '
|
|
2515
|
+
const filesManifestPath = path.join(bmadDir, '_config', 'files-manifest.csv');
|
|
2786
2516
|
if (!(await fs.pathExists(filesManifestPath))) {
|
|
2787
2517
|
return [];
|
|
2788
2518
|
}
|
|
@@ -2842,6 +2572,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2842
2572
|
const customFiles = [];
|
|
2843
2573
|
const modifiedFiles = [];
|
|
2844
2574
|
|
|
2575
|
+
// Memory is always in _bmad/_memory
|
|
2576
|
+
const bmadMemoryPath = '_memory';
|
|
2577
|
+
|
|
2845
2578
|
// Check if the manifest has hashes - if not, we can't detect modifications
|
|
2846
2579
|
let manifestHasHashes = false;
|
|
2847
2580
|
if (existingFilesManifest && existingFilesManifest.length > 0) {
|
|
@@ -2852,14 +2585,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2852
2585
|
const installedFilesMap = new Map();
|
|
2853
2586
|
for (const fileEntry of existingFilesManifest) {
|
|
2854
2587
|
if (fileEntry.path) {
|
|
2855
|
-
|
|
2856
|
-
// strip it if present. This is safe because no real path inside bmadDir would
|
|
2857
|
-
// start with 'bmad/' (you'd never have .bmad/bmad/... as an actual structure).
|
|
2858
|
-
const relativePath = fileEntry.path.startsWith('bmad/') ? fileEntry.path.slice(5) : fileEntry.path;
|
|
2859
|
-
const absolutePath = path.join(bmadDir, relativePath);
|
|
2588
|
+
const absolutePath = path.join(bmadDir, fileEntry.path);
|
|
2860
2589
|
installedFilesMap.set(path.normalize(absolutePath), {
|
|
2861
2590
|
hash: fileEntry.hash,
|
|
2862
|
-
relativePath:
|
|
2591
|
+
relativePath: fileEntry.path,
|
|
2863
2592
|
});
|
|
2864
2593
|
}
|
|
2865
2594
|
}
|
|
@@ -2885,20 +2614,47 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2885
2614
|
const relativePath = path.relative(bmadDir, fullPath);
|
|
2886
2615
|
const fileName = path.basename(fullPath);
|
|
2887
2616
|
|
|
2888
|
-
// Skip
|
|
2889
|
-
if (relativePath.startsWith('
|
|
2617
|
+
// Skip _config directory EXCEPT for modified agent customizations
|
|
2618
|
+
if (relativePath.startsWith('_config/') || relativePath.startsWith('_config\\')) {
|
|
2619
|
+
// Special handling for .customize.yaml files - only preserve if modified
|
|
2620
|
+
if (relativePath.includes('/agents/') && fileName.endsWith('.customize.yaml')) {
|
|
2621
|
+
// Check if the customization file has been modified from manifest
|
|
2622
|
+
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
2623
|
+
if (await fs.pathExists(manifestPath)) {
|
|
2624
|
+
const crypto = require('node:crypto');
|
|
2625
|
+
const currentContent = await fs.readFile(fullPath, 'utf8');
|
|
2626
|
+
const currentHash = crypto.createHash('sha256').update(currentContent).digest('hex');
|
|
2627
|
+
|
|
2628
|
+
const yaml = require('yaml');
|
|
2629
|
+
const manifestContent = await fs.readFile(manifestPath, 'utf8');
|
|
2630
|
+
const manifestData = yaml.parse(manifestContent);
|
|
2631
|
+
const originalHash = manifestData.agentCustomizations?.[relativePath];
|
|
2632
|
+
|
|
2633
|
+
// Only add to customFiles if hash differs (user modified)
|
|
2634
|
+
if (originalHash && currentHash !== originalHash) {
|
|
2635
|
+
customFiles.push(fullPath);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
continue;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
if (relativePath.startsWith(bmadMemoryPath + '/') && path.dirname(relativePath).includes('-sidecar')) {
|
|
2890
2643
|
continue;
|
|
2891
2644
|
}
|
|
2892
2645
|
|
|
2893
2646
|
// Skip config.yaml files - these are regenerated on each install/update
|
|
2894
|
-
// Users should use _cfg/agents/ override files instead
|
|
2895
2647
|
if (fileName === 'config.yaml') {
|
|
2896
2648
|
continue;
|
|
2897
2649
|
}
|
|
2898
2650
|
|
|
2899
2651
|
if (!fileInfo) {
|
|
2900
2652
|
// File not in manifest = custom file
|
|
2901
|
-
|
|
2653
|
+
// EXCEPT: Agent .md files in module folders are generated files, not custom
|
|
2654
|
+
// Only treat .md files under _config/agents/ as custom
|
|
2655
|
+
if (!(fileName.endsWith('.md') && relativePath.includes('/agents/') && !relativePath.startsWith('_config/'))) {
|
|
2656
|
+
customFiles.push(fullPath);
|
|
2657
|
+
}
|
|
2902
2658
|
} else if (manifestHasHashes && fileInfo.hash) {
|
|
2903
2659
|
// File in manifest with hash - check if it was modified
|
|
2904
2660
|
const currentHash = await this.manifest.calculateFileHash(fullPath);
|
|
@@ -2910,8 +2666,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2910
2666
|
});
|
|
2911
2667
|
}
|
|
2912
2668
|
}
|
|
2913
|
-
// If manifest doesn't have hashes, we can't detect modifications
|
|
2914
|
-
// so we just skip files that are in the manifest
|
|
2915
2669
|
}
|
|
2916
2670
|
}
|
|
2917
2671
|
} catch {
|
|
@@ -2929,7 +2683,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2929
2683
|
* @param {Object} userInfo - User information including name and language
|
|
2930
2684
|
*/
|
|
2931
2685
|
async createAgentConfigs(bmadDir, userInfo = null) {
|
|
2932
|
-
const agentConfigDir = path.join(bmadDir, '
|
|
2686
|
+
const agentConfigDir = path.join(bmadDir, '_config', 'agents');
|
|
2933
2687
|
await fs.ensureDir(agentConfigDir);
|
|
2934
2688
|
|
|
2935
2689
|
// Get all agents from all modules
|
|
@@ -2939,7 +2693,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2939
2693
|
// Check modules for agents (including core)
|
|
2940
2694
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
2941
2695
|
for (const entry of entries) {
|
|
2942
|
-
if (entry.isDirectory() && entry.name !== '
|
|
2696
|
+
if (entry.isDirectory() && entry.name !== '_config') {
|
|
2943
2697
|
const moduleAgentsPath = path.join(bmadDir, entry.name, 'agents');
|
|
2944
2698
|
if (await fs.pathExists(moduleAgentsPath)) {
|
|
2945
2699
|
const agentFiles = await fs.readdir(moduleAgentsPath);
|
|
@@ -3041,7 +2795,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3041
2795
|
}
|
|
3042
2796
|
|
|
3043
2797
|
await fs.writeFile(configPath, configContent, 'utf8');
|
|
3044
|
-
this.installedFiles.
|
|
2798
|
+
this.installedFiles.add(configPath); // Track agent config files
|
|
3045
2799
|
createdCount++;
|
|
3046
2800
|
}
|
|
3047
2801
|
|
|
@@ -3057,7 +2811,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3057
2811
|
* @param {Array} agentDetails - Array of agent details
|
|
3058
2812
|
*/
|
|
3059
2813
|
async generateAgentManifest(bmadDir, agentDetails) {
|
|
3060
|
-
const manifestPath = path.join(bmadDir, '
|
|
2814
|
+
const manifestPath = path.join(bmadDir, '_config', 'agent-manifest.csv');
|
|
3061
2815
|
await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
|
|
3062
2816
|
}
|
|
3063
2817
|
|
|
@@ -3107,184 +2861,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3107
2861
|
return nodes;
|
|
3108
2862
|
}
|
|
3109
2863
|
|
|
3110
|
-
/**
|
|
3111
|
-
* Reinstall custom agents from backup and source locations
|
|
3112
|
-
* This preserves custom agents across quick updates/reinstalls
|
|
3113
|
-
* @param {string} projectDir - Project directory
|
|
3114
|
-
* @param {string} bmadDir - BMAD installation directory
|
|
3115
|
-
* @returns {Object} Result with count and agent names
|
|
3116
|
-
*/
|
|
3117
|
-
async reinstallCustomAgents(projectDir, bmadDir) {
|
|
3118
|
-
const {
|
|
3119
|
-
discoverAgents,
|
|
3120
|
-
loadAgentConfig,
|
|
3121
|
-
extractManifestData,
|
|
3122
|
-
addToManifest,
|
|
3123
|
-
createIdeSlashCommands,
|
|
3124
|
-
updateManifestYaml,
|
|
3125
|
-
} = require('../../../lib/agent/installer');
|
|
3126
|
-
const { compileAgent } = require('../../../lib/agent/compiler');
|
|
3127
|
-
|
|
3128
|
-
const results = { count: 0, agents: [] };
|
|
3129
|
-
|
|
3130
|
-
// Check multiple locations for custom agents
|
|
3131
|
-
const sourceLocations = [
|
|
3132
|
-
path.join(bmadDir, '_cfg', 'custom', 'agents'), // Backup location
|
|
3133
|
-
path.join(bmadDir, 'custom', 'src', 'agents'), // BMAD folder source location
|
|
3134
|
-
path.join(projectDir, 'custom', 'src', 'agents'), // Project root source location
|
|
3135
|
-
];
|
|
3136
|
-
|
|
3137
|
-
let foundAgents = [];
|
|
3138
|
-
let processedAgents = new Set(); // Track to avoid duplicates
|
|
3139
|
-
|
|
3140
|
-
// Discover agents from all locations
|
|
3141
|
-
for (const location of sourceLocations) {
|
|
3142
|
-
if (await fs.pathExists(location)) {
|
|
3143
|
-
const agents = discoverAgents(location);
|
|
3144
|
-
// Only add agents we haven't processed yet
|
|
3145
|
-
const newAgents = agents.filter((agent) => !processedAgents.has(agent.name));
|
|
3146
|
-
foundAgents.push(...newAgents);
|
|
3147
|
-
for (const agent of newAgents) processedAgents.add(agent.name);
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
if (foundAgents.length === 0) {
|
|
3152
|
-
return results;
|
|
3153
|
-
}
|
|
3154
|
-
|
|
3155
|
-
try {
|
|
3156
|
-
const customAgentsDir = path.join(bmadDir, 'custom', 'agents');
|
|
3157
|
-
await fs.ensureDir(customAgentsDir);
|
|
3158
|
-
|
|
3159
|
-
const manifestFile = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
|
|
3160
|
-
const manifestYamlFile = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
|
3161
|
-
|
|
3162
|
-
for (const agent of foundAgents) {
|
|
3163
|
-
try {
|
|
3164
|
-
const agentConfig = loadAgentConfig(agent.yamlFile);
|
|
3165
|
-
const finalAgentName = agent.name; // Already named correctly from save
|
|
3166
|
-
|
|
3167
|
-
// Determine agent type from the name (e.g., "fred-commit-poet" → "commit-poet")
|
|
3168
|
-
let agentType = finalAgentName;
|
|
3169
|
-
const parts = finalAgentName.split('-');
|
|
3170
|
-
if (parts.length >= 2) {
|
|
3171
|
-
// Try to extract type (last part or last two parts)
|
|
3172
|
-
// For "fred-commit-poet", we want "commit-poet"
|
|
3173
|
-
// This is heuristic - could be improved with metadata storage
|
|
3174
|
-
agentType = parts.slice(-2).join('-'); // Take last 2 parts as type
|
|
3175
|
-
}
|
|
3176
|
-
|
|
3177
|
-
// Create target directory - use relative path if agent is in a subdirectory
|
|
3178
|
-
const agentTargetDir = agent.relativePath
|
|
3179
|
-
? path.join(customAgentsDir, agent.relativePath)
|
|
3180
|
-
: path.join(customAgentsDir, finalAgentName);
|
|
3181
|
-
await fs.ensureDir(agentTargetDir);
|
|
3182
|
-
|
|
3183
|
-
// Calculate paths
|
|
3184
|
-
const compiledFileName = `${finalAgentName}.md`;
|
|
3185
|
-
const compiledPath = path.join(agentTargetDir, compiledFileName);
|
|
3186
|
-
const relativePath = path.relative(projectDir, compiledPath);
|
|
3187
|
-
|
|
3188
|
-
// Compile with embedded defaults (answers are already in defaults section)
|
|
3189
|
-
const { xml, metadata } = compileAgent(
|
|
3190
|
-
await fs.readFile(agent.yamlFile, 'utf8'),
|
|
3191
|
-
agentConfig.defaults || {},
|
|
3192
|
-
finalAgentName,
|
|
3193
|
-
relativePath,
|
|
3194
|
-
{ config: config.coreConfig },
|
|
3195
|
-
);
|
|
3196
|
-
|
|
3197
|
-
// Write compiled agent
|
|
3198
|
-
await fs.writeFile(compiledPath, xml, 'utf8');
|
|
3199
|
-
|
|
3200
|
-
// Backup source YAML to _cfg/custom/agents if not already there
|
|
3201
|
-
const cfgAgentsBackupDir = path.join(bmadDir, '_cfg', 'custom', 'agents');
|
|
3202
|
-
await fs.ensureDir(cfgAgentsBackupDir);
|
|
3203
|
-
const backupYamlPath = path.join(cfgAgentsBackupDir, `${finalAgentName}.agent.yaml`);
|
|
3204
|
-
|
|
3205
|
-
// Only backup if source is not already in backup location
|
|
3206
|
-
if (agent.yamlFile !== backupYamlPath) {
|
|
3207
|
-
await fs.copy(agent.yamlFile, backupYamlPath);
|
|
3208
|
-
}
|
|
3209
|
-
|
|
3210
|
-
// Copy sidecar files for agents with hasSidecar flag
|
|
3211
|
-
if (agentConfig.hasSidecar === true && agent.type === 'expert') {
|
|
3212
|
-
const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
|
|
3213
|
-
|
|
3214
|
-
// Get agent sidecar folder from config or use default
|
|
3215
|
-
const agentSidecarFolder = config.coreConfig?.agent_sidecar_folder;
|
|
3216
|
-
|
|
3217
|
-
// Resolve path variables
|
|
3218
|
-
const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('{bmad_folder}', bmadDir);
|
|
3219
|
-
|
|
3220
|
-
// Create sidecar directory for this agent
|
|
3221
|
-
const agentSidecarDir = path.join(resolvedSidecarFolder, finalAgentName);
|
|
3222
|
-
await fs.ensureDir(agentSidecarDir);
|
|
3223
|
-
|
|
3224
|
-
// Copy sidecar files (preserve existing, add new)
|
|
3225
|
-
const sidecarResult = copyAgentSidecarFiles(agent.path, agentSidecarDir, agent.yamlFile);
|
|
3226
|
-
|
|
3227
|
-
if (sidecarResult.copied.length > 0 || sidecarResult.preserved.length > 0) {
|
|
3228
|
-
console.log(chalk.dim(` Sidecar: ${sidecarResult.copied.length} new, ${sidecarResult.preserved.length} preserved`));
|
|
3229
|
-
}
|
|
3230
|
-
}
|
|
3231
|
-
|
|
3232
|
-
// Update manifest CSV
|
|
3233
|
-
if (await fs.pathExists(manifestFile)) {
|
|
3234
|
-
// Preserve YAML metadata for persona name, but override id for filename
|
|
3235
|
-
const manifestMetadata = {
|
|
3236
|
-
...metadata,
|
|
3237
|
-
id: relativePath, // Use the compiled agent path for id
|
|
3238
|
-
name: metadata.name || finalAgentName, // Use YAML metadata.name (persona name) or fallback
|
|
3239
|
-
title: metadata.title, // Use YAML title
|
|
3240
|
-
icon: metadata.icon, // Use YAML icon
|
|
3241
|
-
};
|
|
3242
|
-
const manifestData = extractManifestData(xml, manifestMetadata, relativePath, 'custom');
|
|
3243
|
-
manifestData.name = finalAgentName; // Use filename for the name field
|
|
3244
|
-
manifestData.path = relativePath;
|
|
3245
|
-
addToManifest(manifestFile, manifestData);
|
|
3246
|
-
}
|
|
3247
|
-
|
|
3248
|
-
// Create IDE slash commands (async function)
|
|
3249
|
-
await createIdeSlashCommands(projectDir, finalAgentName, relativePath, metadata);
|
|
3250
|
-
|
|
3251
|
-
// Update manifest.yaml
|
|
3252
|
-
if (await fs.pathExists(manifestYamlFile)) {
|
|
3253
|
-
updateManifestYaml(manifestYamlFile, finalAgentName, agentType);
|
|
3254
|
-
}
|
|
3255
|
-
|
|
3256
|
-
results.count++;
|
|
3257
|
-
results.agents.push(finalAgentName);
|
|
3258
|
-
} catch (agentError) {
|
|
3259
|
-
console.log(chalk.yellow(` ⚠️ Failed to reinstall ${agent.name}: ${agentError.message}`));
|
|
3260
|
-
}
|
|
3261
|
-
}
|
|
3262
|
-
} catch (error) {
|
|
3263
|
-
console.log(chalk.yellow(` ⚠️ Error reinstalling custom agents: ${error.message}`));
|
|
3264
|
-
}
|
|
3265
|
-
|
|
3266
|
-
return results;
|
|
3267
|
-
}
|
|
3268
|
-
|
|
3269
|
-
/**
|
|
3270
|
-
* Copy IDE-specific documentation to BMAD docs
|
|
3271
|
-
* @param {Array} ides - List of selected IDEs
|
|
3272
|
-
* @param {string} bmadDir - BMAD installation directory
|
|
3273
|
-
*/
|
|
3274
|
-
async copyIdeDocumentation(ides, bmadDir) {
|
|
3275
|
-
const docsDir = path.join(bmadDir, 'docs');
|
|
3276
|
-
await fs.ensureDir(docsDir);
|
|
3277
|
-
|
|
3278
|
-
for (const ide of ides) {
|
|
3279
|
-
const sourceDocPath = path.join(getProjectRoot(), 'docs', 'ide-info', `${ide}.md`);
|
|
3280
|
-
const targetDocPath = path.join(docsDir, `${ide}-instructions.md`);
|
|
3281
|
-
|
|
3282
|
-
if (await fs.pathExists(sourceDocPath)) {
|
|
3283
|
-
await this.copyFileWithPlaceholderReplacement(sourceDocPath, targetDocPath, this.bmadFolderName || 'bmad');
|
|
3284
|
-
}
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
2864
|
/**
|
|
3289
2865
|
* Handle missing custom module sources interactively
|
|
3290
2866
|
* @param {Map} customModuleSources - Map of custom module ID to info
|
|
@@ -3309,19 +2885,32 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3309
2885
|
info: customInfo,
|
|
3310
2886
|
});
|
|
3311
2887
|
} else {
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
2888
|
+
// For cached modules that are missing, we just skip them without prompting
|
|
2889
|
+
if (customInfo.cached) {
|
|
2890
|
+
// Skip cached modules without prompting
|
|
2891
|
+
keptModulesWithoutSources.push({
|
|
2892
|
+
id: moduleId,
|
|
2893
|
+
name: customInfo.name,
|
|
2894
|
+
cached: true,
|
|
2895
|
+
});
|
|
2896
|
+
} else {
|
|
2897
|
+
customModulesWithMissingSources.push({
|
|
2898
|
+
id: moduleId,
|
|
2899
|
+
name: customInfo.name,
|
|
2900
|
+
sourcePath: customInfo.sourcePath,
|
|
2901
|
+
relativePath: customInfo.relativePath,
|
|
2902
|
+
info: customInfo,
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
3319
2905
|
}
|
|
3320
2906
|
}
|
|
3321
2907
|
|
|
3322
2908
|
// If no missing sources, return immediately
|
|
3323
2909
|
if (customModulesWithMissingSources.length === 0) {
|
|
3324
|
-
return
|
|
2910
|
+
return {
|
|
2911
|
+
validCustomModules,
|
|
2912
|
+
keptModulesWithoutSources: [],
|
|
2913
|
+
};
|
|
3325
2914
|
}
|
|
3326
2915
|
|
|
3327
2916
|
// Stop any spinner for interactive prompts
|