bmad-method 6.0.0-alpha.16 → 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 +7 -3
- 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 +96 -2
- 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 +13 -13
- package/docs/sample-custom-modules/README.md +11 -0
- package/docs/sample-custom-modules/sample-unitary-module/README.md +8 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/commit-poet/commit-poet.agent.yaml +129 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/instructions.md +70 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/bundlers.md +111 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/deploy.md +70 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/docs.md +114 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +134 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +161 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/tests.md +103 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/memories.md +17 -0
- package/docs/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith.agent.yaml +109 -0
- package/docs/sample-custom-modules/sample-unitary-module/module.yaml +8 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-01-init.md +168 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-02-q1.md +155 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-03-q2.md +89 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-04-q3.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-05-q4.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-06-q5.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-07-q6.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-08-q7.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-09-q8.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-10-q9.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-11-q10.md +36 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/steps/step-12-results.md +150 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/templates/csv-headers.template +1 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/quiz-master/workflow.md +54 -0
- package/docs/sample-custom-modules/sample-unitary-module/workflows/wassup/workflow.md +26 -0
- package/docs/sample-custom-modules/sample-wellness-module/README.md +6 -0
- package/docs/sample-custom-modules/sample-wellness-module/agents/meditation-guide.agent.yaml +136 -0
- 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/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion-sidecar/insights.md +13 -0
- package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion-sidecar/instructions.md +30 -0
- package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion-sidecar/memories.md +13 -0
- package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion-sidecar/patterns.md +17 -0
- package/docs/sample-custom-modules/sample-wellness-module/agents/wellness-companion/wellness-companion.agent.yaml +120 -0
- package/docs/sample-custom-modules/sample-wellness-module/module.yaml +17 -0
- package/docs/sample-custom-modules/sample-wellness-module/workflows/daily-checkin/README.md +32 -0
- package/docs/sample-custom-modules/sample-wellness-module/workflows/daily-checkin/workflow.md +45 -0
- package/docs/sample-custom-modules/sample-wellness-module/workflows/guided-meditation/README.md +31 -0
- package/docs/sample-custom-modules/sample-wellness-module/workflows/guided-meditation/workflow.md +45 -0
- package/docs/sample-custom-modules/sample-wellness-module/workflows/wellness-journal/README.md +31 -0
- package/docs/sample-custom-modules/sample-wellness-module/workflows/wellness-journal/workflow.md +45 -0
- 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 +11 -18
- 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 +20 -21
- 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 -15
- 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 +3 -3
- package/src/modules/bmb/workflows/create-agent/steps/step-01-brainstorm.md +4 -4
- package/src/modules/bmb/workflows/create-agent/steps/step-02-discover.md +6 -10
- package/src/modules/bmb/workflows/create-agent/steps/step-03-persona.md +6 -6
- package/src/modules/bmb/workflows/create-agent/steps/step-04-commands.md +9 -9
- package/src/modules/bmb/workflows/create-agent/steps/step-05-name.md +6 -5
- package/src/modules/bmb/workflows/create-agent/steps/step-06-build.md +19 -57
- package/src/modules/bmb/workflows/create-agent/steps/step-07-validate.md +5 -5
- package/src/modules/bmb/workflows/create-agent/steps/{step-11-celebrate.md → step-08-celebrate.md} +2 -6
- 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 +4 -4
- package/src/modules/bmb/workflows/create-module/steps/step-03-components.md +4 -4
- package/src/modules/bmb/workflows/create-module/steps/step-04-structure.md +6 -6
- package/src/modules/bmb/workflows/create-module/steps/step-05-config.md +5 -5
- package/src/modules/bmb/workflows/create-module/steps/step-06-agents.md +8 -8
- 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 +2 -2
- package/src/modules/bmb/workflows/edit-agent/steps/step-02-analyze-agent.md +12 -12
- package/src/modules/bmb/workflows/edit-agent/steps/step-03-propose-changes.md +4 -4
- package/src/modules/bmb/workflows/edit-agent/steps/step-04-apply-changes.md +2 -2
- package/src/modules/bmb/workflows/edit-agent/steps/step-05-validate.md +4 -4
- 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 -7
- 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/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 -4
- package/src/modules/bmm/module.yaml +25 -23
- 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 +1 -1
- 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 +1 -1
- 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 -7
- 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 +224 -59
- package/tools/cli/installers/lib/core/custom-module-cache.js +18 -10
- 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 +540 -946
- 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 -46
- 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 +631 -568
- 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/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 -366
- 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 -1754
- 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 -27
- package/tools/cli/test-yaml-builder.js +0 -43
- /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), 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,14 +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 { replaceAgentSidecarFolders } = require('./post-install-sidecar-replacement');
|
|
42
20
|
const { CustomHandler } = require('../custom/handler');
|
|
21
|
+
const { filterCustomizationData } = require('../../../lib/agent/compiler');
|
|
43
22
|
|
|
44
23
|
class Installer {
|
|
45
24
|
constructor() {
|
|
@@ -53,49 +32,66 @@ class Installer {
|
|
|
53
32
|
this.dependencyResolver = new DependencyResolver();
|
|
54
33
|
this.configCollector = new ConfigCollector();
|
|
55
34
|
this.ideConfigManager = new IdeConfigManager();
|
|
56
|
-
this.installedFiles =
|
|
35
|
+
this.installedFiles = new Set(); // Track all installed files
|
|
57
36
|
this.ttsInjectedFiles = []; // Track files with TTS injection applied
|
|
58
37
|
}
|
|
59
38
|
|
|
60
39
|
/**
|
|
61
40
|
* Find the bmad installation directory in a project
|
|
62
|
-
* 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
|
|
63
43
|
* @param {string} projectDir - Project directory
|
|
64
|
-
* @returns {Promise<
|
|
44
|
+
* @returns {Promise<Object>} { bmadDir: string, hasLegacyCfg: boolean }
|
|
65
45
|
*/
|
|
66
46
|
async findBmadDir(projectDir) {
|
|
67
47
|
// Check if project directory exists
|
|
68
48
|
if (!(await fs.pathExists(projectDir))) {
|
|
69
49
|
// Project doesn't exist yet, return default
|
|
70
|
-
return path.join(projectDir, '
|
|
50
|
+
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
|
|
71
51
|
}
|
|
72
52
|
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
let bmadDir = null;
|
|
54
|
+
let hasLegacyCfg = false;
|
|
55
|
+
|
|
75
56
|
try {
|
|
76
57
|
const entries = await fs.readdir(projectDir, { withFileTypes: true });
|
|
77
58
|
for (const entry of entries) {
|
|
78
59
|
if (entry.isDirectory()) {
|
|
79
|
-
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');
|
|
80
64
|
if (await fs.pathExists(manifestPath)) {
|
|
81
|
-
// Found a V6+ installation
|
|
82
|
-
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;
|
|
83
74
|
}
|
|
84
75
|
}
|
|
85
76
|
}
|
|
86
77
|
} catch {
|
|
87
|
-
|
|
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 };
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
// No V6+ installation found, return default
|
|
91
87
|
// This will be used for new installations
|
|
92
|
-
return path.join(projectDir, '
|
|
88
|
+
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
|
|
93
89
|
}
|
|
94
90
|
|
|
95
91
|
/**
|
|
96
92
|
* @function copyFileWithPlaceholderReplacement
|
|
97
93
|
* @intent Copy files from BMAD source to installation directory with dynamic content transformation
|
|
98
|
-
* @why Enables installation-time customization:
|
|
94
|
+
* @why Enables installation-time customization: _bmad replacement + optional AgentVibes TTS injection
|
|
99
95
|
* @param {string} sourcePath - Absolute path to source file in BMAD repository
|
|
100
96
|
* @param {string} targetPath - Absolute path to destination file in user's project
|
|
101
97
|
* @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad')
|
|
@@ -324,6 +320,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
324
320
|
|
|
325
321
|
for (const ide of newlySelectedIdes) {
|
|
326
322
|
// List of IDEs that have interactive prompts
|
|
323
|
+
//TODO: Why is this here, hardcoding this list here is bad, fix me!
|
|
327
324
|
const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes(
|
|
328
325
|
ide,
|
|
329
326
|
);
|
|
@@ -347,7 +344,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
347
344
|
} else if (ideModule.default) {
|
|
348
345
|
SetupClass = ideModule.default;
|
|
349
346
|
} else {
|
|
350
|
-
// Skip if no setup class found
|
|
351
347
|
continue;
|
|
352
348
|
}
|
|
353
349
|
|
|
@@ -397,11 +393,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
397
393
|
// Clone config to avoid mutating the caller's object
|
|
398
394
|
const config = { ...originalConfig };
|
|
399
395
|
|
|
400
|
-
//
|
|
401
|
-
|
|
396
|
+
// Check if core config was already collected in UI
|
|
397
|
+
const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
|
|
402
398
|
|
|
403
|
-
//
|
|
404
|
-
|
|
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();
|
|
403
|
+
|
|
404
|
+
// Display welcome message
|
|
405
|
+
CLIUtils.displaySection('BMad™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
|
|
406
|
+
}
|
|
405
407
|
|
|
406
408
|
// Note: Legacy V4 detection now happens earlier in UI.promptInstall()
|
|
407
409
|
// before any config collection, so we don't need to check again here
|
|
@@ -409,7 +411,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
409
411
|
const projectDir = path.resolve(config.directory);
|
|
410
412
|
|
|
411
413
|
// If core config was pre-collected (from interactive mode), use it
|
|
412
|
-
if (config.coreConfig) {
|
|
414
|
+
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
|
413
415
|
this.configCollector.collectedConfig.core = config.coreConfig;
|
|
414
416
|
// Also store in allAnswers for cross-referencing
|
|
415
417
|
this.configCollector.allAnswers = {};
|
|
@@ -420,12 +422,47 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
420
422
|
|
|
421
423
|
// Collect configurations for modules (skip if quick update already collected them)
|
|
422
424
|
let moduleConfigs;
|
|
425
|
+
let customModulePaths = new Map();
|
|
426
|
+
|
|
423
427
|
if (config._quickUpdate) {
|
|
424
428
|
// Quick update already collected all configs, use them directly
|
|
425
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
|
+
}
|
|
426
437
|
} else {
|
|
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
|
+
|
|
427
465
|
// Build custom module paths map from customContent
|
|
428
|
-
const customModulePaths = new Map();
|
|
429
466
|
|
|
430
467
|
// Handle selectedFiles (from existing install path or manual directory input)
|
|
431
468
|
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
|
@@ -438,6 +475,13 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
438
475
|
}
|
|
439
476
|
}
|
|
440
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
|
+
|
|
441
485
|
// Handle cachedModules (from new install path where modules are cached)
|
|
442
486
|
// Only include modules that were actually selected for installation
|
|
443
487
|
if (config.customContent && config.customContent.cachedModules) {
|
|
@@ -459,21 +503,37 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
459
503
|
}
|
|
460
504
|
|
|
461
505
|
// Get list of all modules including custom modules
|
|
462
|
-
|
|
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
|
|
463
514
|
for (const [moduleId] of customModulePaths) {
|
|
464
515
|
if (!allModulesForConfig.includes(moduleId)) {
|
|
465
516
|
allModulesForConfig.push(moduleId);
|
|
466
517
|
}
|
|
467
518
|
}
|
|
468
519
|
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
+
}
|
|
473
533
|
}
|
|
474
534
|
|
|
475
|
-
// Always use
|
|
476
|
-
const bmadFolderName = '
|
|
535
|
+
// Always use _bmad as the folder name
|
|
536
|
+
const bmadFolderName = '_bmad';
|
|
477
537
|
this.bmadFolderName = bmadFolderName; // Store for use in other methods
|
|
478
538
|
|
|
479
539
|
// Store AgentVibes configuration for injection point processing
|
|
@@ -482,6 +542,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
482
542
|
// Set bmad folder name on module manager and IDE manager for placeholder replacement
|
|
483
543
|
this.moduleManager.setBmadFolderName(bmadFolderName);
|
|
484
544
|
this.moduleManager.setCoreConfig(moduleConfigs.core || {});
|
|
545
|
+
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
485
546
|
this.ideManager.setBmadFolderName(bmadFolderName);
|
|
486
547
|
|
|
487
548
|
// Tool selection will be collected after we determine if it's a reinstall/update/new install
|
|
@@ -496,7 +557,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
496
557
|
let existingBmadFolderName = null;
|
|
497
558
|
|
|
498
559
|
if (await fs.pathExists(projectDir)) {
|
|
499
|
-
|
|
560
|
+
const result = await this.findBmadDir(projectDir);
|
|
561
|
+
existingBmadDir = result.bmadDir;
|
|
500
562
|
existingBmadFolderName = path.basename(existingBmadDir);
|
|
501
563
|
}
|
|
502
564
|
|
|
@@ -531,9 +593,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
531
593
|
|
|
532
594
|
// Check if user already decided what to do (from early menu in ui.js)
|
|
533
595
|
let action = null;
|
|
534
|
-
if (config.
|
|
535
|
-
action = 'reinstall';
|
|
536
|
-
} else if (config.actionType === 'update') {
|
|
596
|
+
if (config.actionType === 'update') {
|
|
537
597
|
action = 'update';
|
|
538
598
|
} else {
|
|
539
599
|
// Fallback: Ask the user (backwards compatibility for other code paths)
|
|
@@ -545,64 +605,49 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
545
605
|
action = promptResult.action;
|
|
546
606
|
}
|
|
547
607
|
|
|
548
|
-
if (action === '
|
|
549
|
-
console.log('Installation cancelled.');
|
|
550
|
-
return { success: false, cancelled: true };
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
if (action === 'reinstall') {
|
|
554
|
-
// Warn about destructive operation
|
|
555
|
-
console.log(chalk.red.bold('\n⚠️ WARNING: This is a destructive operation!'));
|
|
556
|
-
console.log(chalk.red('All custom files and modifications in the bmad directory will be lost.'));
|
|
557
|
-
|
|
558
|
-
const inquirer = require('inquirer');
|
|
559
|
-
const { confirmReinstall } = await inquirer.prompt([
|
|
560
|
-
{
|
|
561
|
-
type: 'confirm',
|
|
562
|
-
name: 'confirmReinstall',
|
|
563
|
-
message: chalk.yellow('Are you sure you want to delete and reinstall?'),
|
|
564
|
-
default: false,
|
|
565
|
-
},
|
|
566
|
-
]);
|
|
567
|
-
|
|
568
|
-
if (!confirmReinstall) {
|
|
569
|
-
console.log('Installation cancelled.');
|
|
570
|
-
return { success: false, cancelled: true };
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Remember previously configured IDEs before deleting
|
|
574
|
-
config._previouslyConfiguredIdes = existingInstall.ides || [];
|
|
575
|
-
|
|
576
|
-
// Remove existing installation
|
|
577
|
-
await fs.remove(bmadDir);
|
|
578
|
-
console.log(chalk.green('✓ Removed existing installation\n'));
|
|
579
|
-
|
|
580
|
-
// Mark this as a full reinstall so we re-collect IDE configurations
|
|
581
|
-
config._isFullReinstall = true;
|
|
582
|
-
} else if (action === 'update') {
|
|
608
|
+
if (action === 'update') {
|
|
583
609
|
// Store that we're updating for later processing
|
|
584
610
|
config._isUpdate = true;
|
|
585
611
|
config._existingInstall = existingInstall;
|
|
586
612
|
|
|
587
613
|
// Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
|
|
588
614
|
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
|
589
|
-
console.log(chalk.dim(`DEBUG: Read ${existingFilesManifest.length} files from manifest`));
|
|
590
|
-
console.log(chalk.dim(`DEBUG: Manifest has hashes: ${existingFilesManifest.some((f) => f.hash)}`));
|
|
591
|
-
|
|
592
615
|
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
|
593
616
|
|
|
594
|
-
console.log(chalk.dim(`DEBUG: Found ${customFiles.length} custom files, ${modifiedFiles.length} modified files`));
|
|
595
|
-
if (modifiedFiles.length > 0) {
|
|
596
|
-
console.log(chalk.yellow('DEBUG: Modified files:'));
|
|
597
|
-
for (const f of modifiedFiles) console.log(chalk.dim(` - ${f.path}`));
|
|
598
|
-
}
|
|
599
|
-
|
|
600
617
|
config._customFiles = customFiles;
|
|
601
618
|
config._modifiedFiles = modifiedFiles;
|
|
602
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
|
+
|
|
603
648
|
// If there are custom files, back them up temporarily
|
|
604
649
|
if (customFiles.length > 0) {
|
|
605
|
-
const tempBackupDir = path.join(projectDir, '
|
|
650
|
+
const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
|
|
606
651
|
await fs.ensureDir(tempBackupDir);
|
|
607
652
|
|
|
608
653
|
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
|
@@ -619,23 +664,19 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
619
664
|
|
|
620
665
|
// For modified files, back them up to temp directory (will be restored as .bak files after install)
|
|
621
666
|
if (modifiedFiles.length > 0) {
|
|
622
|
-
const tempModifiedBackupDir = path.join(projectDir, '
|
|
667
|
+
const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
|
|
623
668
|
await fs.ensureDir(tempModifiedBackupDir);
|
|
624
669
|
|
|
625
|
-
console.log(chalk.yellow(`\nDEBUG: Backing up ${modifiedFiles.length} modified files to temp location`));
|
|
626
670
|
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
|
627
671
|
for (const modifiedFile of modifiedFiles) {
|
|
628
672
|
const relativePath = path.relative(bmadDir, modifiedFile.path);
|
|
629
673
|
const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
|
|
630
|
-
console.log(chalk.dim(`DEBUG: Backing up ${relativePath} to temp`));
|
|
631
674
|
await fs.ensureDir(path.dirname(tempBackupPath));
|
|
632
675
|
await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
|
|
633
676
|
}
|
|
634
677
|
spinner.succeed(`Backed up ${modifiedFiles.length} modified files`);
|
|
635
678
|
|
|
636
679
|
config._tempModifiedBackupDir = tempModifiedBackupDir;
|
|
637
|
-
} else {
|
|
638
|
-
console.log(chalk.dim('DEBUG: No modified files detected'));
|
|
639
680
|
}
|
|
640
681
|
}
|
|
641
682
|
} else if (existingInstall.installed && config._quickUpdate) {
|
|
@@ -651,9 +692,37 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
651
692
|
config._customFiles = customFiles;
|
|
652
693
|
config._modifiedFiles = modifiedFiles;
|
|
653
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
|
+
|
|
654
723
|
// Back up custom files
|
|
655
724
|
if (customFiles.length > 0) {
|
|
656
|
-
const tempBackupDir = path.join(projectDir, '
|
|
725
|
+
const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
|
|
657
726
|
await fs.ensureDir(tempBackupDir);
|
|
658
727
|
|
|
659
728
|
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
|
@@ -669,7 +738,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
669
738
|
|
|
670
739
|
// Back up modified files
|
|
671
740
|
if (modifiedFiles.length > 0) {
|
|
672
|
-
const tempModifiedBackupDir = path.join(projectDir, '
|
|
741
|
+
const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
|
|
673
742
|
await fs.ensureDir(tempModifiedBackupDir);
|
|
674
743
|
|
|
675
744
|
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
|
@@ -724,7 +793,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
724
793
|
config.skipIde = toolSelection.skipIde;
|
|
725
794
|
const ideConfigurations = toolSelection.configurations;
|
|
726
795
|
|
|
727
|
-
// Check if spinner is already running (e.g., from folder name change scenario)
|
|
728
796
|
if (spinner.isSpinning) {
|
|
729
797
|
spinner.text = 'Continuing installation...';
|
|
730
798
|
} else {
|
|
@@ -735,7 +803,26 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
735
803
|
spinner.text = 'Creating directory structure...';
|
|
736
804
|
await this.createDirectoryStructure(bmadDir);
|
|
737
805
|
|
|
738
|
-
//
|
|
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
|
+
|
|
739
826
|
const projectRoot = getProjectRoot();
|
|
740
827
|
|
|
741
828
|
// Step 1: Install core module first (if requested)
|
|
@@ -792,31 +879,32 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
792
879
|
|
|
793
880
|
const modulesToInstall = allModules;
|
|
794
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
|
+
|
|
795
896
|
// For dependency resolution, we need to pass the project root
|
|
796
897
|
// Create a temporary module manager that knows about custom content locations
|
|
797
898
|
const tempModuleManager = new ModuleManager({
|
|
798
|
-
scanProjectForModules: true,
|
|
799
899
|
bmadDir: bmadDir, // Pass bmadDir so we can check cache
|
|
800
900
|
});
|
|
801
901
|
|
|
802
|
-
|
|
803
|
-
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
|
804
|
-
// The dependency resolver needs to know about these modules
|
|
805
|
-
// We'll handle custom modules separately in the installation loop
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
|
|
902
|
+
const resolution = await this.dependencyResolver.resolve(projectRoot, regularModulesForResolution, {
|
|
809
903
|
verbose: config.verbose,
|
|
810
904
|
moduleManager: tempModuleManager,
|
|
811
905
|
});
|
|
812
906
|
|
|
813
|
-
|
|
814
|
-
spinner.succeed('Dependencies resolved');
|
|
815
|
-
} else {
|
|
816
|
-
spinner.succeed('Dependencies resolved');
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Core is already installed above, skip if included in resolution
|
|
907
|
+
spinner.succeed('Dependencies resolved');
|
|
820
908
|
|
|
821
909
|
// Install modules with their dependencies
|
|
822
910
|
if (allModules && allModules.length > 0) {
|
|
@@ -829,7 +917,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
829
917
|
}
|
|
830
918
|
installedModuleNames.add(moduleName);
|
|
831
919
|
|
|
832
|
-
|
|
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}...`);
|
|
833
923
|
|
|
834
924
|
// Check if this is a custom module
|
|
835
925
|
let isCustomModule = false;
|
|
@@ -855,8 +945,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
855
945
|
customInfo = config._customModuleSources.get(moduleName);
|
|
856
946
|
isCustomModule = true;
|
|
857
947
|
|
|
858
|
-
// Check if this is a cached module (source path starts with
|
|
859
|
-
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
|
+
) {
|
|
860
953
|
useCache = true;
|
|
861
954
|
// Make sure we have the right path structure
|
|
862
955
|
if (!customInfo.path) {
|
|
@@ -879,103 +972,34 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
879
972
|
}
|
|
880
973
|
|
|
881
974
|
if (isCustomModule && customInfo) {
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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
|
+
}
|
|
888
981
|
|
|
889
|
-
// Get collected config for this custom module (from module.yaml prompts)
|
|
890
982
|
const collectedModuleConfig = moduleConfigs[moduleName] || {};
|
|
891
983
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
984
|
+
// Use ModuleManager to install the custom module
|
|
985
|
+
await this.moduleManager.install(
|
|
986
|
+
moduleName,
|
|
987
|
+
bmadDir,
|
|
896
988
|
(filePath) => {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
989
|
+
this.installedFiles.add(filePath);
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
isCustom: true,
|
|
993
|
+
moduleConfig: collectedModuleConfig,
|
|
994
|
+
isQuickUpdate: config._quickUpdate || false,
|
|
995
|
+
installer: this,
|
|
901
996
|
},
|
|
902
997
|
);
|
|
903
998
|
|
|
904
|
-
// Move from temp-custom to actual module directory
|
|
905
|
-
const tempCustomPath = path.join(bmadDir, 'temp-custom');
|
|
906
|
-
if (await fs.pathExists(tempCustomPath)) {
|
|
907
|
-
const customDir = path.join(tempCustomPath, 'custom');
|
|
908
|
-
if (await fs.pathExists(customDir)) {
|
|
909
|
-
// Move contents to module directory
|
|
910
|
-
const items = await fs.readdir(customDir);
|
|
911
|
-
const movedItems = [];
|
|
912
|
-
try {
|
|
913
|
-
for (const item of items) {
|
|
914
|
-
const srcPath = path.join(customDir, item);
|
|
915
|
-
const destPath = path.join(moduleTargetPath, item);
|
|
916
|
-
|
|
917
|
-
// If destination exists, remove it first (or we could merge)
|
|
918
|
-
if (await fs.pathExists(destPath)) {
|
|
919
|
-
await fs.remove(destPath);
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
await fs.move(srcPath, destPath);
|
|
923
|
-
movedItems.push({ src: srcPath, dest: destPath });
|
|
924
|
-
}
|
|
925
|
-
} catch (moveError) {
|
|
926
|
-
// Rollback: restore any successfully moved items
|
|
927
|
-
for (const moved of movedItems) {
|
|
928
|
-
try {
|
|
929
|
-
await fs.move(moved.dest, moved.src);
|
|
930
|
-
} catch {
|
|
931
|
-
// Best-effort rollback - log if it fails
|
|
932
|
-
console.error(`Failed to rollback ${moved.dest} during cleanup`);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
throw new Error(`Failed to move custom module files: ${moveError.message}`);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
try {
|
|
939
|
-
await fs.remove(tempCustomPath);
|
|
940
|
-
} catch (cleanupError) {
|
|
941
|
-
// Non-fatal: temp directory cleanup failed but files were moved successfully
|
|
942
|
-
console.warn(`Warning: Could not clean up temp directory: ${cleanupError.message}`);
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
|
|
946
999
|
// Create module config (include collected config from module.yaml prompts)
|
|
947
1000
|
await this.generateModuleConfigs(bmadDir, {
|
|
948
1001
|
[moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig },
|
|
949
1002
|
});
|
|
950
|
-
|
|
951
|
-
// Store custom module info for later manifest update
|
|
952
|
-
if (!config._customModulesToTrack) {
|
|
953
|
-
config._customModulesToTrack = [];
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// For cached modules, use appropriate path handling
|
|
957
|
-
let sourcePath;
|
|
958
|
-
if (useCache) {
|
|
959
|
-
// Check if we have cached modules info (from initial install)
|
|
960
|
-
if (finalCustomContent && finalCustomContent.cachedModules) {
|
|
961
|
-
sourcePath = finalCustomContent.cachedModules.find((m) => m.id === moduleName)?.relativePath;
|
|
962
|
-
} else {
|
|
963
|
-
// During update, the sourcePath is already cache-relative if it starts with _cfg
|
|
964
|
-
sourcePath =
|
|
965
|
-
customInfo.sourcePath && customInfo.sourcePath.startsWith('_cfg')
|
|
966
|
-
? customInfo.sourcePath
|
|
967
|
-
: path.relative(bmadDir, customInfo.path || customInfo.sourcePath);
|
|
968
|
-
}
|
|
969
|
-
} else {
|
|
970
|
-
sourcePath = path.resolve(customInfo.path || customInfo.sourcePath);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
config._customModulesToTrack.push({
|
|
974
|
-
id: customInfo.id,
|
|
975
|
-
name: customInfo.name,
|
|
976
|
-
sourcePath: sourcePath,
|
|
977
|
-
installDate: new Date().toISOString(),
|
|
978
|
-
});
|
|
979
1003
|
} else {
|
|
980
1004
|
// Regular module installation
|
|
981
1005
|
// Special case for core module
|
|
@@ -986,7 +1010,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
986
1010
|
}
|
|
987
1011
|
}
|
|
988
1012
|
|
|
989
|
-
spinner.succeed(`Module installed: ${moduleName}`);
|
|
1013
|
+
spinner.succeed(`Module ${isQuickUpdate ? 'updated' : 'installed'}: ${moduleName}`);
|
|
990
1014
|
}
|
|
991
1015
|
|
|
992
1016
|
// Install partial modules (only dependencies)
|
|
@@ -1008,69 +1032,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1008
1032
|
}
|
|
1009
1033
|
}
|
|
1010
1034
|
|
|
1011
|
-
//
|
|
1012
|
-
// Process custom content that wasn't installed as modules
|
|
1013
|
-
// This is now handled in the module installation loop above
|
|
1014
|
-
// This section is kept for backward compatibility with any custom content
|
|
1015
|
-
// that doesn't have a module structure
|
|
1016
|
-
const remainingCustomContent = [];
|
|
1017
|
-
if (
|
|
1018
|
-
config.customContent &&
|
|
1019
|
-
config.customContent.hasCustomContent &&
|
|
1020
|
-
config.customContent.customPath &&
|
|
1021
|
-
config.customContent.selected &&
|
|
1022
|
-
config.customContent.selectedFiles
|
|
1023
|
-
) {
|
|
1024
|
-
// Filter out custom modules that were already installed
|
|
1025
|
-
const customHandler = new CustomHandler();
|
|
1026
|
-
for (const customFile of config.customContent.selectedFiles) {
|
|
1027
|
-
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
1028
|
-
|
|
1029
|
-
// Skip if this was installed as a module
|
|
1030
|
-
if (!customInfo || !customInfo.id || !allModules.includes(customInfo.id)) {
|
|
1031
|
-
remainingCustomContent.push(customFile);
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
if (remainingCustomContent.length > 0) {
|
|
1037
|
-
spinner.start('Installing remaining custom content...');
|
|
1038
|
-
const customHandler = new CustomHandler();
|
|
1039
|
-
|
|
1040
|
-
// Use the remaining files
|
|
1041
|
-
const customFiles = remainingCustomContent;
|
|
1042
|
-
|
|
1043
|
-
if (customFiles.length > 0) {
|
|
1044
|
-
console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
|
|
1045
|
-
for (const customFile of customFiles) {
|
|
1046
|
-
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
|
1047
|
-
if (customInfo) {
|
|
1048
|
-
console.log(chalk.dim(` • ${customInfo.name} (${customInfo.relativePath})`));
|
|
1049
|
-
|
|
1050
|
-
// Install the custom content
|
|
1051
|
-
const result = await customHandler.install(
|
|
1052
|
-
customInfo.path,
|
|
1053
|
-
bmadDir,
|
|
1054
|
-
{ ...config.coreConfig, ...customInfo.config },
|
|
1055
|
-
(filePath) => {
|
|
1056
|
-
// Track installed files
|
|
1057
|
-
this.installedFiles.push(filePath);
|
|
1058
|
-
},
|
|
1059
|
-
);
|
|
1060
|
-
|
|
1061
|
-
if (result.errors.length > 0) {
|
|
1062
|
-
console.log(chalk.yellow(` ⚠️ ${result.errors.length} error(s) occurred`));
|
|
1063
|
-
for (const error of result.errors) {
|
|
1064
|
-
console.log(chalk.dim(` - ${error}`));
|
|
1065
|
-
}
|
|
1066
|
-
} else {
|
|
1067
|
-
console.log(chalk.green(` ✓ Installed ${result.agentsInstalled} agents, ${result.workflowsInstalled} workflows`));
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
spinner.succeed('Custom content installed');
|
|
1073
|
-
}
|
|
1035
|
+
// All content is now installed as modules - no separate custom content handling needed
|
|
1074
1036
|
|
|
1075
1037
|
// Generate clean config.yaml files for each installed module
|
|
1076
1038
|
spinner.start('Generating module configurations...');
|
|
@@ -1082,13 +1044,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1082
1044
|
// Customize templates are now created in processAgentFiles when building YAML agents
|
|
1083
1045
|
|
|
1084
1046
|
// Pre-register manifest files that will be created (except files-manifest.csv to avoid recursion)
|
|
1085
|
-
const cfgDir = path.join(bmadDir, '
|
|
1086
|
-
this.installedFiles.
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
path.join(cfgDir, 'task-manifest.csv'),
|
|
1091
|
-
);
|
|
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'));
|
|
1092
1052
|
|
|
1093
1053
|
// Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes BEFORE IDE setup
|
|
1094
1054
|
spinner.start('Generating workflow and agent manifests...');
|
|
@@ -1112,18 +1072,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1112
1072
|
modulesForCsvPreserve = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules;
|
|
1113
1073
|
}
|
|
1114
1074
|
|
|
1115
|
-
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, this.installedFiles, {
|
|
1075
|
+
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, [...this.installedFiles], {
|
|
1116
1076
|
ides: config.ides || [],
|
|
1117
1077
|
preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
|
|
1118
1078
|
});
|
|
1119
1079
|
|
|
1120
|
-
//
|
|
1121
|
-
if (config._customModulesToTrack && config._customModulesToTrack.length > 0) {
|
|
1122
|
-
spinner.text = 'Storing custom module sources...';
|
|
1123
|
-
for (const customModule of config._customModulesToTrack) {
|
|
1124
|
-
await this.manifest.addCustomModule(bmadDir, customModule);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1080
|
+
// Custom modules are now included in the main modules list - no separate tracking needed
|
|
1127
1081
|
|
|
1128
1082
|
spinner.succeed(
|
|
1129
1083
|
`Manifests generated: ${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools, ${manifestStats.files} files`,
|
|
@@ -1164,7 +1118,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1164
1118
|
|
|
1165
1119
|
// Pass pre-collected configuration to avoid re-prompting
|
|
1166
1120
|
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
1167
|
-
selectedModules:
|
|
1121
|
+
selectedModules: allModules || [],
|
|
1168
1122
|
preCollectedConfig: ideConfigurations[ide] || null,
|
|
1169
1123
|
verbose: config.verbose,
|
|
1170
1124
|
});
|
|
@@ -1184,24 +1138,24 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1184
1138
|
console.log = originalLog;
|
|
1185
1139
|
|
|
1186
1140
|
if (spinner.isSpinning) {
|
|
1187
|
-
spinner.succeed(`Configured ${validIdes.
|
|
1141
|
+
spinner.succeed(`Configured: ${validIdes.join(', ')}`);
|
|
1188
1142
|
} else {
|
|
1189
|
-
console.log(chalk.green(`✓ Configured ${validIdes.
|
|
1143
|
+
console.log(chalk.green(`✓ Configured: ${validIdes.join(', ')}`));
|
|
1190
1144
|
}
|
|
1191
1145
|
}
|
|
1192
|
-
|
|
1193
|
-
// Copy IDE-specific documentation (only for valid IDEs)
|
|
1194
|
-
const validIdesForDocs = (config.ides || []).filter((ide) => ide && typeof ide === 'string');
|
|
1195
|
-
if (validIdesForDocs.length > 0) {
|
|
1196
|
-
spinner.start('Copying IDE documentation...');
|
|
1197
|
-
await this.copyIdeDocumentation(validIdesForDocs, bmadDir);
|
|
1198
|
-
spinner.succeed('IDE documentation copied');
|
|
1199
|
-
}
|
|
1200
1146
|
}
|
|
1201
1147
|
|
|
1202
1148
|
// Run module-specific installers after IDE setup
|
|
1203
1149
|
spinner.start('Running module-specific installers...');
|
|
1204
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
|
+
|
|
1205
1159
|
// Run core module installer if core was installed
|
|
1206
1160
|
if (config.installCore || resolution.byModule.core) {
|
|
1207
1161
|
spinner.text = 'Running core module installer...';
|
|
@@ -1210,11 +1164,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1210
1164
|
installedIDEs: config.ides || [],
|
|
1211
1165
|
moduleConfig: moduleConfigs.core || {},
|
|
1212
1166
|
coreConfig: moduleConfigs.core || {},
|
|
1213
|
-
logger:
|
|
1214
|
-
log: (msg) => console.log(msg),
|
|
1215
|
-
error: (msg) => console.error(msg),
|
|
1216
|
-
warn: (msg) => console.warn(msg),
|
|
1217
|
-
},
|
|
1167
|
+
logger: moduleLogger,
|
|
1218
1168
|
});
|
|
1219
1169
|
}
|
|
1220
1170
|
|
|
@@ -1228,11 +1178,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1228
1178
|
installedIDEs: config.ides || [],
|
|
1229
1179
|
moduleConfig: moduleConfigs[moduleName] || {},
|
|
1230
1180
|
coreConfig: moduleConfigs.core || {},
|
|
1231
|
-
logger:
|
|
1232
|
-
log: (msg) => console.log(msg),
|
|
1233
|
-
error: (msg) => console.error(msg),
|
|
1234
|
-
warn: (msg) => console.warn(msg),
|
|
1235
|
-
},
|
|
1181
|
+
logger: moduleLogger,
|
|
1236
1182
|
});
|
|
1237
1183
|
}
|
|
1238
1184
|
}
|
|
@@ -1299,44 +1245,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1299
1245
|
// Report custom and modified files if any were found
|
|
1300
1246
|
if (customFiles.length > 0) {
|
|
1301
1247
|
console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`));
|
|
1302
|
-
console.log(chalk.dim('The following custom files were found and restored:\n'));
|
|
1303
|
-
for (const file of customFiles) {
|
|
1304
|
-
console.log(chalk.dim(` - ${path.relative(bmadDir, file)}`));
|
|
1305
|
-
}
|
|
1306
|
-
console.log('');
|
|
1307
1248
|
}
|
|
1308
1249
|
|
|
1309
1250
|
if (modifiedFiles.length > 0) {
|
|
1310
|
-
console.log(chalk.yellow(`\n⚠️
|
|
1311
|
-
console.log(chalk.dim('The following files were modified and backed up with .bak extension:\n'));
|
|
1312
|
-
for (const file of modifiedFiles) {
|
|
1313
|
-
console.log(chalk.dim(` - ${file.relativePath} → ${file.relativePath}.bak`));
|
|
1314
|
-
}
|
|
1315
|
-
console.log(chalk.dim('\nThese files have been updated with the new version.'));
|
|
1316
|
-
console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n'));
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
// Reinstall custom agents from _cfg/custom/agents/ sources
|
|
1320
|
-
const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
|
|
1321
|
-
if (customAgentResults.count > 0) {
|
|
1322
|
-
console.log(chalk.green(`\n✓ Reinstalled ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`));
|
|
1323
|
-
for (const agent of customAgentResults.agents) {
|
|
1324
|
-
console.log(chalk.dim(` - ${agent}`));
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
// Replace {agent_sidecar_folder} placeholders in all agent files
|
|
1329
|
-
console.log(chalk.dim('\n Configuring agent sidecar folders...'));
|
|
1330
|
-
const sidecarResults = await replaceAgentSidecarFolders(bmadDir);
|
|
1331
|
-
|
|
1332
|
-
if (sidecarResults.filesReplaced > 0) {
|
|
1251
|
+
console.log(chalk.yellow(`\n⚠️ User modified files detected: ${modifiedFiles.length}`));
|
|
1333
1252
|
console.log(
|
|
1334
|
-
chalk.
|
|
1335
|
-
|
|
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.',
|
|
1336
1255
|
),
|
|
1337
1256
|
);
|
|
1338
|
-
|
|
1339
|
-
console.log(chalk.dim(' No agent sidecar references found'));
|
|
1257
|
+
console.log(chalk.dim('Remove these .bak files it no longer needed\n'));
|
|
1340
1258
|
}
|
|
1341
1259
|
|
|
1342
1260
|
// Display completion message
|
|
@@ -1373,7 +1291,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1373
1291
|
|
|
1374
1292
|
try {
|
|
1375
1293
|
const projectDir = path.resolve(config.directory);
|
|
1376
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
1294
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
1377
1295
|
const existingInstall = await this.detector.detect(bmadDir);
|
|
1378
1296
|
|
|
1379
1297
|
if (!existingInstall.installed) {
|
|
@@ -1389,12 +1307,44 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1389
1307
|
|
|
1390
1308
|
// Check for custom modules with missing sources before update
|
|
1391
1309
|
const customModuleSources = new Map();
|
|
1310
|
+
|
|
1311
|
+
// Check manifest for backward compatibility
|
|
1392
1312
|
if (existingInstall.customModules) {
|
|
1393
1313
|
for (const customModule of existingInstall.customModules) {
|
|
1394
1314
|
customModuleSources.set(customModule.id, customModule);
|
|
1395
1315
|
}
|
|
1396
1316
|
}
|
|
1397
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
|
+
|
|
1398
1348
|
if (customModuleSources.size > 0) {
|
|
1399
1349
|
spinner.stop();
|
|
1400
1350
|
console.log(chalk.yellow('\nChecking custom module sources before update...'));
|
|
@@ -1458,7 +1408,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1458
1408
|
*/
|
|
1459
1409
|
async getStatus(directory) {
|
|
1460
1410
|
const projectDir = path.resolve(directory);
|
|
1461
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
1411
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
1462
1412
|
return await this.detector.detect(bmadDir);
|
|
1463
1413
|
}
|
|
1464
1414
|
|
|
@@ -1474,7 +1424,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1474
1424
|
*/
|
|
1475
1425
|
async uninstall(directory) {
|
|
1476
1426
|
const projectDir = path.resolve(directory);
|
|
1477
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
1427
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
1478
1428
|
|
|
1479
1429
|
if (await fs.pathExists(bmadDir)) {
|
|
1480
1430
|
await fs.remove(bmadDir);
|
|
@@ -1491,8 +1441,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1491
1441
|
*/
|
|
1492
1442
|
async createDirectoryStructure(bmadDir) {
|
|
1493
1443
|
await fs.ensureDir(bmadDir);
|
|
1494
|
-
await fs.ensureDir(path.join(bmadDir, '
|
|
1495
|
-
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'));
|
|
1496
1447
|
}
|
|
1497
1448
|
|
|
1498
1449
|
/**
|
|
@@ -1501,7 +1452,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1501
1452
|
* @param {Object} moduleConfigs - Collected configuration values
|
|
1502
1453
|
*/
|
|
1503
1454
|
async generateModuleConfigs(bmadDir, moduleConfigs) {
|
|
1504
|
-
const yaml = require('
|
|
1455
|
+
const yaml = require('yaml');
|
|
1505
1456
|
|
|
1506
1457
|
// Extract core config values to share with other modules
|
|
1507
1458
|
const coreConfig = moduleConfigs.core || {};
|
|
@@ -1509,7 +1460,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1509
1460
|
// Get all installed module directories
|
|
1510
1461
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
1511
1462
|
const installedModules = entries
|
|
1512
|
-
.filter((entry) => entry.isDirectory() && entry.name !== '
|
|
1463
|
+
.filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs')
|
|
1513
1464
|
.map((entry) => entry.name);
|
|
1514
1465
|
|
|
1515
1466
|
// Generate config.yaml for each installed module
|
|
@@ -1547,12 +1498,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1547
1498
|
coreSection = '\n# Core Configuration Values\n';
|
|
1548
1499
|
}
|
|
1549
1500
|
|
|
1501
|
+
// Clean the config to remove any non-serializable values (like functions)
|
|
1502
|
+
const cleanConfig = structuredClone(finalConfig);
|
|
1503
|
+
|
|
1550
1504
|
// Convert config to YAML
|
|
1551
|
-
let yamlContent = yaml.
|
|
1505
|
+
let yamlContent = yaml.stringify(cleanConfig, {
|
|
1552
1506
|
indent: 2,
|
|
1553
|
-
lineWidth:
|
|
1554
|
-
|
|
1555
|
-
sortKeys: false,
|
|
1507
|
+
lineWidth: 0,
|
|
1508
|
+
minContentWidth: 0,
|
|
1556
1509
|
});
|
|
1557
1510
|
|
|
1558
1511
|
// If we have core values, reorganize the YAML to group them with their comment
|
|
@@ -1584,7 +1537,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1584
1537
|
await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
|
|
1585
1538
|
|
|
1586
1539
|
// Track the config file in installedFiles
|
|
1587
|
-
this.installedFiles.
|
|
1540
|
+
this.installedFiles.add(configPath);
|
|
1588
1541
|
}
|
|
1589
1542
|
}
|
|
1590
1543
|
}
|
|
@@ -1597,14 +1550,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1597
1550
|
async installCoreWithDependencies(bmadDir, coreFiles) {
|
|
1598
1551
|
const sourcePath = getModulePath('core');
|
|
1599
1552
|
const targetPath = path.join(bmadDir, 'core');
|
|
1600
|
-
|
|
1601
|
-
// Install full core
|
|
1602
1553
|
await this.installCore(bmadDir);
|
|
1603
|
-
|
|
1604
|
-
// If there are specific dependency files, ensure they're included
|
|
1605
|
-
if (coreFiles) {
|
|
1606
|
-
// Already handled by installCore for core module
|
|
1607
|
-
}
|
|
1608
1554
|
}
|
|
1609
1555
|
|
|
1610
1556
|
/**
|
|
@@ -1623,11 +1569,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1623
1569
|
moduleName,
|
|
1624
1570
|
bmadDir,
|
|
1625
1571
|
(filePath) => {
|
|
1626
|
-
this.installedFiles.
|
|
1572
|
+
this.installedFiles.add(filePath);
|
|
1627
1573
|
},
|
|
1628
1574
|
{
|
|
1629
1575
|
skipModuleInstaller: true, // We'll run it later after IDE setup
|
|
1630
1576
|
moduleConfig: moduleConfig, // Pass module config for conditional filtering
|
|
1577
|
+
installer: this,
|
|
1631
1578
|
},
|
|
1632
1579
|
);
|
|
1633
1580
|
|
|
@@ -1660,7 +1607,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1660
1607
|
|
|
1661
1608
|
if (await fs.pathExists(sourcePath)) {
|
|
1662
1609
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1663
|
-
this.installedFiles.
|
|
1610
|
+
this.installedFiles.add(targetPath);
|
|
1664
1611
|
}
|
|
1665
1612
|
}
|
|
1666
1613
|
}
|
|
@@ -1676,7 +1623,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1676
1623
|
|
|
1677
1624
|
if (await fs.pathExists(sourcePath)) {
|
|
1678
1625
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1679
|
-
this.installedFiles.
|
|
1626
|
+
this.installedFiles.add(targetPath);
|
|
1680
1627
|
}
|
|
1681
1628
|
}
|
|
1682
1629
|
}
|
|
@@ -1692,7 +1639,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1692
1639
|
|
|
1693
1640
|
if (await fs.pathExists(sourcePath)) {
|
|
1694
1641
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1695
|
-
this.installedFiles.
|
|
1642
|
+
this.installedFiles.add(targetPath);
|
|
1696
1643
|
}
|
|
1697
1644
|
}
|
|
1698
1645
|
}
|
|
@@ -1708,7 +1655,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1708
1655
|
|
|
1709
1656
|
if (await fs.pathExists(sourcePath)) {
|
|
1710
1657
|
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath, this.bmadFolderName || 'bmad');
|
|
1711
|
-
this.installedFiles.
|
|
1658
|
+
this.installedFiles.add(targetPath);
|
|
1712
1659
|
}
|
|
1713
1660
|
}
|
|
1714
1661
|
}
|
|
@@ -1723,7 +1670,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1723
1670
|
|
|
1724
1671
|
if (await fs.pathExists(dataPath)) {
|
|
1725
1672
|
await this.copyFileWithPlaceholderReplacement(dataPath, targetPath, this.bmadFolderName || 'bmad');
|
|
1726
|
-
this.installedFiles.
|
|
1673
|
+
this.installedFiles.add(targetPath);
|
|
1727
1674
|
}
|
|
1728
1675
|
}
|
|
1729
1676
|
}
|
|
@@ -1744,25 +1691,55 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1744
1691
|
const sourcePath = getModulePath('core');
|
|
1745
1692
|
const targetPath = path.join(bmadDir, 'core');
|
|
1746
1693
|
|
|
1747
|
-
// Copy core files
|
|
1748
|
-
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);
|
|
1749
1701
|
|
|
1750
1702
|
// Process agent files to inject activation block
|
|
1751
1703
|
await this.processAgentFiles(targetPath, 'core');
|
|
1752
1704
|
}
|
|
1753
1705
|
|
|
1754
1706
|
/**
|
|
1755
|
-
* Copy
|
|
1756
|
-
* @param {string} sourcePath - Source
|
|
1757
|
-
* @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
|
|
1758
1710
|
*/
|
|
1759
|
-
async
|
|
1760
|
-
// Get all files in source
|
|
1711
|
+
async copyCoreFiles(sourcePath, targetPath) {
|
|
1712
|
+
// Get all files in source
|
|
1761
1713
|
const files = await this.getFileList(sourcePath);
|
|
1762
1714
|
|
|
1763
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
|
+
|
|
1764
1736
|
// Skip config.yaml templates - we'll generate clean ones with actual values
|
|
1765
|
-
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')) {
|
|
1766
1743
|
continue;
|
|
1767
1744
|
}
|
|
1768
1745
|
|
|
@@ -1770,7 +1747,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1770
1747
|
const targetFile = path.join(targetPath, file);
|
|
1771
1748
|
|
|
1772
1749
|
// Check if this is an agent file
|
|
1773
|
-
if (file.
|
|
1750
|
+
if (file.startsWith('agents/') && file.endsWith('.md')) {
|
|
1774
1751
|
// Read the file to check for localskip
|
|
1775
1752
|
const content = await fs.readFile(sourceFile, 'utf8');
|
|
1776
1753
|
|
|
@@ -1782,11 +1759,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1782
1759
|
}
|
|
1783
1760
|
}
|
|
1784
1761
|
|
|
1785
|
-
//
|
|
1786
|
-
|
|
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
|
+
}
|
|
1787
1770
|
|
|
1788
1771
|
// Track the installed file
|
|
1789
|
-
this.installedFiles.
|
|
1772
|
+
this.installedFiles.add(targetFile);
|
|
1790
1773
|
}
|
|
1791
1774
|
}
|
|
1792
1775
|
|
|
@@ -1833,128 +1816,39 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1833
1816
|
|
|
1834
1817
|
// Determine project directory (parent of bmad/ directory)
|
|
1835
1818
|
const bmadDir = path.dirname(modulePath);
|
|
1836
|
-
const
|
|
1837
|
-
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
|
|
1819
|
+
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
1838
1820
|
|
|
1839
|
-
// Ensure
|
|
1821
|
+
// Ensure _config/agents directory exists
|
|
1840
1822
|
await fs.ensureDir(cfgAgentsDir);
|
|
1841
1823
|
|
|
1842
1824
|
// Get all agent files
|
|
1843
1825
|
const agentFiles = await fs.readdir(agentsPath);
|
|
1844
1826
|
|
|
1845
1827
|
for (const agentFile of agentFiles) {
|
|
1846
|
-
//
|
|
1828
|
+
// Skip .agent.yaml files - they should already be compiled by compileModuleAgents
|
|
1847
1829
|
if (agentFile.endsWith('.agent.yaml')) {
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
const mdPath = path.join(agentsPath, `${agentName}.md`);
|
|
1851
|
-
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1852
|
-
|
|
1853
|
-
// Create customize template if it doesn't exist
|
|
1854
|
-
if (!(await fs.pathExists(customizePath))) {
|
|
1855
|
-
const genericTemplatePath = getSourcePath('utility', 'templates', 'agent.customize.template.yaml');
|
|
1856
|
-
if (await fs.pathExists(genericTemplatePath)) {
|
|
1857
|
-
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
|
|
1858
|
-
console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
// Build YAML + customize to .md
|
|
1863
|
-
const customizeExists = await fs.pathExists(customizePath);
|
|
1864
|
-
let xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
|
|
1865
|
-
includeMetadata: true,
|
|
1866
|
-
});
|
|
1867
|
-
|
|
1868
|
-
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1869
|
-
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
1870
|
-
|
|
1871
|
-
// Replace .bmad with actual folder name
|
|
1872
|
-
xmlContent = xmlContent.replaceAll('.bmad', this.bmadFolderName || 'bmad');
|
|
1873
|
-
|
|
1874
|
-
// Replace {agent_sidecar_folder} if configured
|
|
1875
|
-
const coreConfig = this.configCollector.collectedConfig.core || {};
|
|
1876
|
-
if (coreConfig.agent_sidecar_folder && xmlContent.includes('{agent_sidecar_folder}')) {
|
|
1877
|
-
xmlContent = xmlContent.replaceAll('{agent_sidecar_folder}', coreConfig.agent_sidecar_folder);
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
// Process TTS injection points (pass targetPath for tracking)
|
|
1881
|
-
xmlContent = this.processTTSInjectionPoints(xmlContent, mdPath);
|
|
1882
|
-
|
|
1883
|
-
// Check if agent has sidecar and copy it
|
|
1884
|
-
let agentYamlContent = null;
|
|
1885
|
-
let hasSidecar = false;
|
|
1886
|
-
|
|
1887
|
-
try {
|
|
1888
|
-
agentYamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
1889
|
-
const yamlLib = require('yaml');
|
|
1890
|
-
const agentYaml = yamlLib.parse(agentYamlContent);
|
|
1891
|
-
hasSidecar = agentYaml?.agent?.metadata?.hasSidecar === true;
|
|
1892
|
-
} catch {
|
|
1893
|
-
// Continue without sidecar processing
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
|
-
// Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
|
|
1897
|
-
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
|
1898
|
-
await fs.writeFile(mdPath, content, 'utf8');
|
|
1899
|
-
this.installedFiles.push(mdPath);
|
|
1900
|
-
|
|
1901
|
-
// Copy sidecar files if agent has hasSidecar flag
|
|
1902
|
-
if (hasSidecar) {
|
|
1903
|
-
const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
|
|
1904
|
-
|
|
1905
|
-
// Get agent sidecar folder from core config
|
|
1906
|
-
const coreConfigPath = path.join(bmadDir, 'bmb', 'config.yaml');
|
|
1907
|
-
let agentSidecarFolder;
|
|
1908
|
-
|
|
1909
|
-
if (await fs.pathExists(coreConfigPath)) {
|
|
1910
|
-
const yamlLib = require('yaml');
|
|
1911
|
-
const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
|
|
1912
|
-
const coreConfig = yamlLib.parse(coreConfigContent);
|
|
1913
|
-
agentSidecarFolder = coreConfig.agent_sidecar_folder || agentSidecarFolder;
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
// Resolve path variables
|
|
1917
|
-
const resolvedSidecarFolder = agentSidecarFolder
|
|
1918
|
-
.replaceAll('{project-root}', projectDir)
|
|
1919
|
-
.replaceAll('.bmad', this.bmadFolderName || 'bmad');
|
|
1920
|
-
|
|
1921
|
-
// Create sidecar directory for this agent
|
|
1922
|
-
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
|
|
1923
|
-
await fs.ensureDir(agentSidecarDir);
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1924
1832
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1833
|
+
// Only process .md files (already compiled from YAML)
|
|
1834
|
+
if (!agentFile.endsWith('.md')) {
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1928
1837
|
|
|
1929
|
-
|
|
1930
|
-
|
|
1838
|
+
const agentName = agentFile.replace('.md', '');
|
|
1839
|
+
const mdPath = path.join(agentsPath, agentFile);
|
|
1840
|
+
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1931
1841
|
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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`));
|
|
1937
1850
|
}
|
|
1938
1851
|
}
|
|
1939
|
-
|
|
1940
|
-
// Remove the source YAML file - we can regenerate from installer source if needed
|
|
1941
|
-
await fs.remove(yamlPath);
|
|
1942
|
-
|
|
1943
|
-
console.log(chalk.dim(` Built agent: ${agentName}.md${hasSidecar ? ' (with sidecar)' : ''}`));
|
|
1944
|
-
}
|
|
1945
|
-
// Handle legacy .md agents - inject activation if needed
|
|
1946
|
-
else if (agentFile.endsWith('.md')) {
|
|
1947
|
-
const agentPath = path.join(agentsPath, agentFile);
|
|
1948
|
-
let content = await fs.readFile(agentPath, 'utf8');
|
|
1949
|
-
|
|
1950
|
-
// Check if content has agent XML and no activation block
|
|
1951
|
-
if (content.includes('<agent') && !content.includes('<activation')) {
|
|
1952
|
-
// Inject the activation block using XML handler
|
|
1953
|
-
content = this.xmlHandler.injectActivationSimple(content);
|
|
1954
|
-
// Ensure POSIX-compliant final newline
|
|
1955
|
-
const finalContent = content.endsWith('\n') ? content : content + '\n';
|
|
1956
|
-
await fs.writeFile(agentPath, finalContent, 'utf8');
|
|
1957
|
-
}
|
|
1958
1852
|
}
|
|
1959
1853
|
}
|
|
1960
1854
|
}
|
|
@@ -1966,7 +1860,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1966
1860
|
*/
|
|
1967
1861
|
async buildStandaloneAgents(bmadDir, projectDir) {
|
|
1968
1862
|
const standaloneAgentsPath = path.join(bmadDir, 'agents');
|
|
1969
|
-
const cfgAgentsDir = path.join(bmadDir, '
|
|
1863
|
+
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
1970
1864
|
|
|
1971
1865
|
// Check if standalone agents directory exists
|
|
1972
1866
|
if (!(await fs.pathExists(standaloneAgentsPath))) {
|
|
@@ -1998,8 +1892,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1998
1892
|
|
|
1999
1893
|
if (customizeExists) {
|
|
2000
1894
|
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
2001
|
-
const yaml = require('
|
|
2002
|
-
const customizeYaml = yaml.
|
|
1895
|
+
const yaml = require('yaml');
|
|
1896
|
+
const customizeYaml = yaml.parse(customizeContent);
|
|
2003
1897
|
|
|
2004
1898
|
// Detect what fields are customized (similar to rebuildAgentFiles)
|
|
2005
1899
|
if (customizeYaml) {
|
|
@@ -2067,7 +1961,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2067
1961
|
// Determine project directory (parent of bmad/ directory)
|
|
2068
1962
|
const bmadDir = path.dirname(modulePath);
|
|
2069
1963
|
const projectDir = path.dirname(bmadDir);
|
|
2070
|
-
const cfgAgentsDir = path.join(bmadDir, '
|
|
1964
|
+
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
2071
1965
|
const targetAgentsPath = path.join(modulePath, 'agents');
|
|
2072
1966
|
|
|
2073
1967
|
// Ensure target directory exists
|
|
@@ -2089,8 +1983,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2089
1983
|
|
|
2090
1984
|
if (customizeExists) {
|
|
2091
1985
|
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
2092
|
-
const yaml = require('
|
|
2093
|
-
const customizeYaml = yaml.
|
|
1986
|
+
const yaml = require('yaml');
|
|
1987
|
+
const customizeYaml = yaml.parse(customizeContent);
|
|
2094
1988
|
|
|
2095
1989
|
// Detect what fields are customized
|
|
2096
1990
|
if (customizeYaml) {
|
|
@@ -2123,34 +2017,59 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2123
2017
|
}
|
|
2124
2018
|
}
|
|
2125
2019
|
|
|
2126
|
-
//
|
|
2127
|
-
|
|
2128
|
-
includeMetadata: true,
|
|
2129
|
-
});
|
|
2020
|
+
// Read the YAML content
|
|
2021
|
+
const yamlContent = await fs.readFile(sourceYamlPath, 'utf8');
|
|
2130
2022
|
|
|
2131
|
-
//
|
|
2132
|
-
|
|
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
|
+
}
|
|
2133
2030
|
|
|
2134
|
-
//
|
|
2135
|
-
const
|
|
2136
|
-
|
|
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
|
+
}
|
|
2137
2048
|
|
|
2049
|
+
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
|
|
2050
|
+
let coreConfig = {};
|
|
2138
2051
|
if (await fs.pathExists(coreConfigPath)) {
|
|
2139
|
-
const
|
|
2052
|
+
const yaml = require('yaml');
|
|
2140
2053
|
const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
|
|
2141
|
-
|
|
2142
|
-
agentSidecarFolder = coreConfig.agent_sidecar_folder;
|
|
2054
|
+
coreConfig = yaml.parse(coreConfigContent);
|
|
2143
2055
|
}
|
|
2144
2056
|
|
|
2145
|
-
|
|
2146
|
-
|
|
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`);
|
|
2147
2066
|
}
|
|
2148
2067
|
|
|
2149
|
-
//
|
|
2150
|
-
|
|
2068
|
+
// Replace _bmad with actual folder name if needed
|
|
2069
|
+
const finalXml = result.xml.replaceAll('_bmad', path.basename(bmadDir));
|
|
2151
2070
|
|
|
2152
2071
|
// Write the rebuilt .md file with POSIX-compliant final newline
|
|
2153
|
-
const content =
|
|
2072
|
+
const content = finalXml.endsWith('\n') ? finalXml : finalXml + '\n';
|
|
2154
2073
|
await fs.writeFile(targetMdPath, content, 'utf8');
|
|
2155
2074
|
|
|
2156
2075
|
// Display result with customizations if any
|
|
@@ -2169,23 +2088,28 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2169
2088
|
* @returns {Object} Compilation results
|
|
2170
2089
|
*/
|
|
2171
2090
|
async compileAgents(config) {
|
|
2172
|
-
const ora = require('ora');
|
|
2173
|
-
const spinner = ora('Starting agent compilation...').start();
|
|
2174
|
-
|
|
2175
2091
|
try {
|
|
2176
2092
|
const projectDir = path.resolve(config.directory);
|
|
2177
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
2093
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
2178
2094
|
|
|
2179
2095
|
// Check if bmad directory exists
|
|
2180
2096
|
if (!(await fs.pathExists(bmadDir))) {
|
|
2181
|
-
spinner.fail('No BMAD installation found');
|
|
2182
2097
|
throw new Error(`BMAD not installed at ${bmadDir}`);
|
|
2183
2098
|
}
|
|
2184
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
|
+
|
|
2185
2111
|
// Check for custom modules with missing sources
|
|
2186
|
-
const manifest = await this.manifest.read(bmadDir);
|
|
2187
2112
|
if (manifest && manifest.customModules && manifest.customModules.length > 0) {
|
|
2188
|
-
spinner.stop();
|
|
2189
2113
|
console.log(chalk.yellow('\nChecking custom module sources before compilation...'));
|
|
2190
2114
|
|
|
2191
2115
|
const customModuleSources = new Map();
|
|
@@ -2194,26 +2118,21 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2194
2118
|
}
|
|
2195
2119
|
|
|
2196
2120
|
const projectRoot = getProjectRoot();
|
|
2197
|
-
const installedModules = manifest.modules || [];
|
|
2198
2121
|
await this.handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, 'compile-agents', installedModules);
|
|
2199
|
-
|
|
2200
|
-
spinner.start('Rebuilding agent files...');
|
|
2201
2122
|
}
|
|
2202
2123
|
|
|
2203
2124
|
let agentCount = 0;
|
|
2204
2125
|
let taskCount = 0;
|
|
2205
2126
|
|
|
2206
2127
|
// Process all modules in bmad directory
|
|
2207
|
-
spinner.text = 'Rebuilding agent files...';
|
|
2208
2128
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
2209
2129
|
|
|
2210
2130
|
for (const entry of entries) {
|
|
2211
|
-
if (entry.isDirectory() && entry.name !== '
|
|
2131
|
+
if (entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs') {
|
|
2212
2132
|
const modulePath = path.join(bmadDir, entry.name);
|
|
2213
2133
|
|
|
2214
2134
|
// Special handling for standalone agents in bmad/agents/ directory
|
|
2215
2135
|
if (entry.name === 'agents') {
|
|
2216
|
-
spinner.text = 'Building standalone agents...';
|
|
2217
2136
|
await this.buildStandaloneAgents(bmadDir, projectDir);
|
|
2218
2137
|
|
|
2219
2138
|
// Count standalone agents
|
|
@@ -2245,60 +2164,22 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2245
2164
|
}
|
|
2246
2165
|
}
|
|
2247
2166
|
|
|
2248
|
-
// Reinstall custom agents from _cfg/custom/agents/ sources
|
|
2249
|
-
spinner.start('Rebuilding custom agents...');
|
|
2250
|
-
const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
|
|
2251
|
-
if (customAgentResults.count > 0) {
|
|
2252
|
-
spinner.succeed(`Rebuilt ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`);
|
|
2253
|
-
agentCount += customAgentResults.count;
|
|
2254
|
-
} else {
|
|
2255
|
-
spinner.succeed('No custom agents found to rebuild');
|
|
2256
|
-
}
|
|
2257
|
-
|
|
2258
|
-
// Skip full manifest regeneration during compileAgents to preserve custom agents
|
|
2259
|
-
// Custom agents are already added to manifests during individual installation
|
|
2260
|
-
// Only regenerate YAML manifest for IDE updates if needed
|
|
2261
|
-
const existingManifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
|
2262
|
-
let existingIdes = [];
|
|
2263
|
-
if (await fs.pathExists(existingManifestPath)) {
|
|
2264
|
-
const manifestContent = await fs.readFile(existingManifestPath, 'utf8');
|
|
2265
|
-
const yaml = require('js-yaml');
|
|
2266
|
-
const manifest = yaml.load(manifestContent);
|
|
2267
|
-
existingIdes = manifest.ides || [];
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
2167
|
// Update IDE configurations using the existing IDE list from manifest
|
|
2271
|
-
if (
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
for (const ide of existingIdes) {
|
|
2275
|
-
spinner.text = `Updating ${ide}...`;
|
|
2276
|
-
|
|
2277
|
-
// Stop spinner before IDE setup to prevent blocking any potential prompts
|
|
2278
|
-
// However, we pass _alreadyConfigured to skip all prompts during compile
|
|
2279
|
-
spinner.stop();
|
|
2280
|
-
|
|
2168
|
+
if (manifest && manifest.ides && manifest.ides.length > 0) {
|
|
2169
|
+
for (const ide of manifest.ides) {
|
|
2281
2170
|
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
2282
2171
|
selectedModules: installedModules,
|
|
2283
2172
|
skipModuleInstall: true, // Skip module installation, just update IDE files
|
|
2284
2173
|
verbose: config.verbose,
|
|
2285
2174
|
preCollectedConfig: { _alreadyConfigured: true }, // Skip all interactive prompts during compile
|
|
2286
2175
|
});
|
|
2287
|
-
|
|
2288
|
-
// Restart spinner for next IDE
|
|
2289
|
-
if (existingIdes.indexOf(ide) < existingIdes.length - 1) {
|
|
2290
|
-
spinner.start('Updating IDE configurations...');
|
|
2291
|
-
}
|
|
2292
2176
|
}
|
|
2293
|
-
|
|
2294
2177
|
console.log(chalk.green('✓ IDE configurations updated'));
|
|
2295
2178
|
} else {
|
|
2296
2179
|
console.log(chalk.yellow('⚠️ No IDEs configured. Skipping IDE update.'));
|
|
2297
2180
|
}
|
|
2298
|
-
|
|
2299
2181
|
return { agentCount, taskCount };
|
|
2300
2182
|
} catch (error) {
|
|
2301
|
-
spinner.fail('Compilation failed');
|
|
2302
2183
|
throw error;
|
|
2303
2184
|
}
|
|
2304
2185
|
}
|
|
@@ -2316,6 +2197,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2316
2197
|
} else {
|
|
2317
2198
|
// Selective update - preserve user modifications
|
|
2318
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');
|
|
2319
2206
|
}
|
|
2320
2207
|
}
|
|
2321
2208
|
|
|
@@ -2330,7 +2217,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2330
2217
|
|
|
2331
2218
|
try {
|
|
2332
2219
|
const projectDir = path.resolve(config.directory);
|
|
2333
|
-
const bmadDir = await this.findBmadDir(projectDir);
|
|
2220
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
2334
2221
|
|
|
2335
2222
|
// Check if bmad directory exists
|
|
2336
2223
|
if (!(await fs.pathExists(bmadDir))) {
|
|
@@ -2346,35 +2233,35 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2346
2233
|
const configuredIdes = existingInstall.ides || [];
|
|
2347
2234
|
const projectRoot = path.dirname(bmadDir);
|
|
2348
2235
|
|
|
2349
|
-
// Get custom module sources from
|
|
2236
|
+
// Get custom module sources from cache
|
|
2350
2237
|
const customModuleSources = new Map();
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
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 });
|
|
2355
2241
|
|
|
2356
|
-
|
|
2357
|
-
if (
|
|
2358
|
-
|
|
2359
|
-
absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
|
|
2360
|
-
}
|
|
2361
|
-
// If no sourcePath but we have relativePath, convert it
|
|
2362
|
-
else if (!absoluteSourcePath && customModule.relativePath) {
|
|
2363
|
-
// relativePath is relative to the project root (parent of bmad dir)
|
|
2364
|
-
absoluteSourcePath = path.resolve(projectRoot, customModule.relativePath);
|
|
2365
|
-
}
|
|
2366
|
-
// Ensure sourcePath is absolute for anything else
|
|
2367
|
-
else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
|
|
2368
|
-
absoluteSourcePath = path.resolve(absoluteSourcePath);
|
|
2369
|
-
}
|
|
2242
|
+
for (const cachedModule of cachedModules) {
|
|
2243
|
+
if (cachedModule.isDirectory()) {
|
|
2244
|
+
const moduleId = cachedModule.name;
|
|
2370
2245
|
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
};
|
|
2246
|
+
// Skip if we already have this module from manifest
|
|
2247
|
+
if (customModuleSources.has(moduleId)) {
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2376
2250
|
|
|
2377
|
-
|
|
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
|
+
}
|
|
2378
2265
|
}
|
|
2379
2266
|
}
|
|
2380
2267
|
|
|
@@ -2406,126 +2293,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2406
2293
|
}
|
|
2407
2294
|
}
|
|
2408
2295
|
|
|
2409
|
-
// Check for untracked custom modules (installed but not in manifest)
|
|
2410
|
-
const untrackedCustomModules = [];
|
|
2411
|
-
for (const installedModule of installedModules) {
|
|
2412
|
-
// Skip standard modules and core
|
|
2413
|
-
const standardModuleIds = ['bmb', 'bmgd', 'bmm', 'cis', 'core'];
|
|
2414
|
-
if (standardModuleIds.includes(installedModule)) {
|
|
2415
|
-
continue;
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
// Check if this installed module is not tracked in customModules
|
|
2419
|
-
if (!customModuleSources.has(installedModule)) {
|
|
2420
|
-
const modulePath = path.join(bmadDir, installedModule);
|
|
2421
|
-
if (await fs.pathExists(modulePath)) {
|
|
2422
|
-
untrackedCustomModules.push({
|
|
2423
|
-
id: installedModule,
|
|
2424
|
-
name: installedModule, // We don't have the original name
|
|
2425
|
-
path: modulePath,
|
|
2426
|
-
untracked: true,
|
|
2427
|
-
});
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
// If we found untracked custom modules, offer to track them
|
|
2433
|
-
if (untrackedCustomModules.length > 0) {
|
|
2434
|
-
spinner.stop();
|
|
2435
|
-
console.log(chalk.yellow(`\n⚠️ Found ${untrackedCustomModules.length} custom module(s) not tracked in manifest:`));
|
|
2436
|
-
|
|
2437
|
-
for (const untracked of untrackedCustomModules) {
|
|
2438
|
-
console.log(chalk.dim(` • ${untracked.id} (installed at ${path.relative(projectRoot, untracked.path)})`));
|
|
2439
|
-
}
|
|
2440
|
-
|
|
2441
|
-
const { trackModules } = await inquirer.prompt([
|
|
2442
|
-
{
|
|
2443
|
-
type: 'confirm',
|
|
2444
|
-
name: 'trackModules',
|
|
2445
|
-
message: chalk.cyan('Would you like to scan for their source locations?'),
|
|
2446
|
-
default: true,
|
|
2447
|
-
},
|
|
2448
|
-
]);
|
|
2449
|
-
|
|
2450
|
-
if (trackModules) {
|
|
2451
|
-
const { scanDirectory } = await inquirer.prompt([
|
|
2452
|
-
{
|
|
2453
|
-
type: 'input',
|
|
2454
|
-
name: 'scanDirectory',
|
|
2455
|
-
message: 'Enter directory to scan for custom module sources (or leave blank to skip):',
|
|
2456
|
-
default: projectRoot,
|
|
2457
|
-
validate: async (input) => {
|
|
2458
|
-
if (input && input.trim() !== '') {
|
|
2459
|
-
const expandedPath = path.resolve(input.trim());
|
|
2460
|
-
if (!(await fs.pathExists(expandedPath))) {
|
|
2461
|
-
return 'Directory does not exist';
|
|
2462
|
-
}
|
|
2463
|
-
const stats = await fs.stat(expandedPath);
|
|
2464
|
-
if (!stats.isDirectory()) {
|
|
2465
|
-
return 'Path must be a directory';
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
return true;
|
|
2469
|
-
},
|
|
2470
|
-
},
|
|
2471
|
-
]);
|
|
2472
|
-
|
|
2473
|
-
if (scanDirectory && scanDirectory.trim() !== '') {
|
|
2474
|
-
console.log(chalk.dim('\nScanning for custom module sources...'));
|
|
2475
|
-
|
|
2476
|
-
// Scan for all module.yaml files
|
|
2477
|
-
const allModulePaths = await this.moduleManager.findModulesInProject(scanDirectory);
|
|
2478
|
-
const { ModuleManager } = require('../modules/manager');
|
|
2479
|
-
const mm = new ModuleManager({ scanProjectForModules: true });
|
|
2480
|
-
|
|
2481
|
-
for (const untracked of untrackedCustomModules) {
|
|
2482
|
-
let foundSource = null;
|
|
2483
|
-
|
|
2484
|
-
// Try to find by module ID
|
|
2485
|
-
for (const modulePath of allModulePaths) {
|
|
2486
|
-
try {
|
|
2487
|
-
const moduleInfo = await mm.getModuleInfo(modulePath);
|
|
2488
|
-
if (moduleInfo && moduleInfo.id === untracked.id) {
|
|
2489
|
-
foundSource = {
|
|
2490
|
-
path: modulePath,
|
|
2491
|
-
info: moduleInfo,
|
|
2492
|
-
};
|
|
2493
|
-
break;
|
|
2494
|
-
}
|
|
2495
|
-
} catch {
|
|
2496
|
-
// Continue searching
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
|
|
2500
|
-
if (foundSource) {
|
|
2501
|
-
console.log(chalk.green(` ✓ Found source for ${untracked.id}: ${path.relative(projectRoot, foundSource.path)}`));
|
|
2502
|
-
|
|
2503
|
-
// Add to manifest
|
|
2504
|
-
await this.manifest.addCustomModule(bmadDir, {
|
|
2505
|
-
id: untracked.id,
|
|
2506
|
-
name: foundSource.info.name || untracked.name,
|
|
2507
|
-
sourcePath: path.resolve(foundSource.path),
|
|
2508
|
-
installDate: new Date().toISOString(),
|
|
2509
|
-
tracked: true,
|
|
2510
|
-
});
|
|
2511
|
-
|
|
2512
|
-
// Add to customModuleSources for processing
|
|
2513
|
-
customModuleSources.set(untracked.id, {
|
|
2514
|
-
id: untracked.id,
|
|
2515
|
-
name: foundSource.info.name || untracked.name,
|
|
2516
|
-
sourcePath: path.resolve(foundSource.path),
|
|
2517
|
-
});
|
|
2518
|
-
} else {
|
|
2519
|
-
console.log(chalk.yellow(` ⚠ Could not find source for ${untracked.id}`));
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
console.log(chalk.dim('\nUntracked custom modules will remain installed but cannot be updated without their source.'));
|
|
2526
|
-
spinner.start('Preparing update...');
|
|
2527
|
-
}
|
|
2528
|
-
|
|
2529
2296
|
// Handle missing custom module sources using shared method
|
|
2530
2297
|
const customModuleResult = await this.handleMissingCustomSources(
|
|
2531
2298
|
customModuleSources,
|
|
@@ -2543,18 +2310,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2543
2310
|
hasUpdate: true,
|
|
2544
2311
|
}));
|
|
2545
2312
|
|
|
2546
|
-
// Add untracked modules to the update list but mark them as untrackable
|
|
2547
|
-
for (const untracked of untrackedCustomModules) {
|
|
2548
|
-
if (!customModuleSources.has(untracked.id)) {
|
|
2549
|
-
customModulesFromManifest.push({
|
|
2550
|
-
...untracked,
|
|
2551
|
-
isCustom: true,
|
|
2552
|
-
hasUpdate: false, // Can't update without source
|
|
2553
|
-
untracked: true,
|
|
2554
|
-
});
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
|
|
2558
2313
|
const allAvailableModules = [...availableModules, ...customModulesFromManifest];
|
|
2559
2314
|
const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
|
|
2560
2315
|
|
|
@@ -2610,19 +2365,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2610
2365
|
lastModified: new Date().toISOString(),
|
|
2611
2366
|
};
|
|
2612
2367
|
|
|
2613
|
-
const existingBmadFolderName = path.basename(bmadDir);
|
|
2614
|
-
const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
|
|
2615
|
-
|
|
2616
|
-
if (existingBmadFolderName === newBmadFolderName) {
|
|
2617
|
-
// Normal quick update - start the spinner
|
|
2618
|
-
console.log(chalk.cyan('Updating BMAD installation...'));
|
|
2619
|
-
} else {
|
|
2620
|
-
// Folder name has changed - stop spinner and let install() handle it
|
|
2621
|
-
spinner.stop();
|
|
2622
|
-
console.log(chalk.yellow(`\n⚠️ Folder name will change: ${existingBmadFolderName} → ${newBmadFolderName}`));
|
|
2623
|
-
console.log(chalk.yellow('The installer will handle the folder migration.\n'));
|
|
2624
|
-
}
|
|
2625
|
-
|
|
2626
2368
|
// Build the config object for the installer
|
|
2627
2369
|
const installConfig = {
|
|
2628
2370
|
directory: projectDir,
|
|
@@ -2672,11 +2414,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2672
2414
|
type: 'list',
|
|
2673
2415
|
name: 'action',
|
|
2674
2416
|
message: 'What would you like to do?',
|
|
2675
|
-
choices: [
|
|
2676
|
-
{ name: 'Update existing installation', value: 'update' },
|
|
2677
|
-
{ name: 'Remove and reinstall', value: 'reinstall' },
|
|
2678
|
-
{ name: 'Cancel', value: 'cancel' },
|
|
2679
|
-
],
|
|
2417
|
+
choices: [{ name: 'Update existing installation', value: 'update' }],
|
|
2680
2418
|
},
|
|
2681
2419
|
]);
|
|
2682
2420
|
}
|
|
@@ -2690,14 +2428,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2690
2428
|
console.log(chalk.yellow.bold('\n⚠️ Legacy BMAD v4 detected'));
|
|
2691
2429
|
console.log(chalk.dim('The installer found legacy artefacts in your project.\n'));
|
|
2692
2430
|
|
|
2693
|
-
// Separate
|
|
2431
|
+
// Separate _bmad* folders (auto-backup) from other offending paths (manual cleanup)
|
|
2694
2432
|
const bmadFolders = legacyV4.offenders.filter((p) => {
|
|
2695
2433
|
const name = path.basename(p);
|
|
2696
|
-
return name.startsWith('
|
|
2434
|
+
return name.startsWith('_bmad'); // Only dot-prefixed folders get auto-backed up
|
|
2697
2435
|
});
|
|
2698
2436
|
const otherOffenders = legacyV4.offenders.filter((p) => {
|
|
2699
2437
|
const name = path.basename(p);
|
|
2700
|
-
return !name.startsWith('
|
|
2438
|
+
return !name.startsWith('_bmad'); // Everything else is manual cleanup
|
|
2701
2439
|
});
|
|
2702
2440
|
|
|
2703
2441
|
const inquirer = require('inquirer');
|
|
@@ -2730,7 +2468,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2730
2468
|
}
|
|
2731
2469
|
}
|
|
2732
2470
|
|
|
2733
|
-
// Handle
|
|
2471
|
+
// Handle _bmad* folders with automatic backup
|
|
2734
2472
|
if (bmadFolders.length > 0) {
|
|
2735
2473
|
console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:'));
|
|
2736
2474
|
for (const p of bmadFolders) console.log(chalk.dim(` - ${p}`));
|
|
@@ -2774,7 +2512,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2774
2512
|
* @returns {Array} Array of file entries from files-manifest.csv
|
|
2775
2513
|
*/
|
|
2776
2514
|
async readFilesManifest(bmadDir) {
|
|
2777
|
-
const filesManifestPath = path.join(bmadDir, '
|
|
2515
|
+
const filesManifestPath = path.join(bmadDir, '_config', 'files-manifest.csv');
|
|
2778
2516
|
if (!(await fs.pathExists(filesManifestPath))) {
|
|
2779
2517
|
return [];
|
|
2780
2518
|
}
|
|
@@ -2834,6 +2572,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2834
2572
|
const customFiles = [];
|
|
2835
2573
|
const modifiedFiles = [];
|
|
2836
2574
|
|
|
2575
|
+
// Memory is always in _bmad/_memory
|
|
2576
|
+
const bmadMemoryPath = '_memory';
|
|
2577
|
+
|
|
2837
2578
|
// Check if the manifest has hashes - if not, we can't detect modifications
|
|
2838
2579
|
let manifestHasHashes = false;
|
|
2839
2580
|
if (existingFilesManifest && existingFilesManifest.length > 0) {
|
|
@@ -2844,14 +2585,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2844
2585
|
const installedFilesMap = new Map();
|
|
2845
2586
|
for (const fileEntry of existingFilesManifest) {
|
|
2846
2587
|
if (fileEntry.path) {
|
|
2847
|
-
|
|
2848
|
-
// strip it if present. This is safe because no real path inside bmadDir would
|
|
2849
|
-
// start with 'bmad/' (you'd never have .bmad/bmad/... as an actual structure).
|
|
2850
|
-
const relativePath = fileEntry.path.startsWith('bmad/') ? fileEntry.path.slice(5) : fileEntry.path;
|
|
2851
|
-
const absolutePath = path.join(bmadDir, relativePath);
|
|
2588
|
+
const absolutePath = path.join(bmadDir, fileEntry.path);
|
|
2852
2589
|
installedFilesMap.set(path.normalize(absolutePath), {
|
|
2853
2590
|
hash: fileEntry.hash,
|
|
2854
|
-
relativePath:
|
|
2591
|
+
relativePath: fileEntry.path,
|
|
2855
2592
|
});
|
|
2856
2593
|
}
|
|
2857
2594
|
}
|
|
@@ -2877,20 +2614,47 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2877
2614
|
const relativePath = path.relative(bmadDir, fullPath);
|
|
2878
2615
|
const fileName = path.basename(fullPath);
|
|
2879
2616
|
|
|
2880
|
-
// Skip
|
|
2881
|
-
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')) {
|
|
2882
2643
|
continue;
|
|
2883
2644
|
}
|
|
2884
2645
|
|
|
2885
2646
|
// Skip config.yaml files - these are regenerated on each install/update
|
|
2886
|
-
// Users should use _cfg/agents/ override files instead
|
|
2887
2647
|
if (fileName === 'config.yaml') {
|
|
2888
2648
|
continue;
|
|
2889
2649
|
}
|
|
2890
2650
|
|
|
2891
2651
|
if (!fileInfo) {
|
|
2892
2652
|
// File not in manifest = custom file
|
|
2893
|
-
|
|
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
|
+
}
|
|
2894
2658
|
} else if (manifestHasHashes && fileInfo.hash) {
|
|
2895
2659
|
// File in manifest with hash - check if it was modified
|
|
2896
2660
|
const currentHash = await this.manifest.calculateFileHash(fullPath);
|
|
@@ -2902,8 +2666,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2902
2666
|
});
|
|
2903
2667
|
}
|
|
2904
2668
|
}
|
|
2905
|
-
// If manifest doesn't have hashes, we can't detect modifications
|
|
2906
|
-
// so we just skip files that are in the manifest
|
|
2907
2669
|
}
|
|
2908
2670
|
}
|
|
2909
2671
|
} catch {
|
|
@@ -2921,7 +2683,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2921
2683
|
* @param {Object} userInfo - User information including name and language
|
|
2922
2684
|
*/
|
|
2923
2685
|
async createAgentConfigs(bmadDir, userInfo = null) {
|
|
2924
|
-
const agentConfigDir = path.join(bmadDir, '
|
|
2686
|
+
const agentConfigDir = path.join(bmadDir, '_config', 'agents');
|
|
2925
2687
|
await fs.ensureDir(agentConfigDir);
|
|
2926
2688
|
|
|
2927
2689
|
// Get all agents from all modules
|
|
@@ -2931,7 +2693,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2931
2693
|
// Check modules for agents (including core)
|
|
2932
2694
|
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
2933
2695
|
for (const entry of entries) {
|
|
2934
|
-
if (entry.isDirectory() && entry.name !== '
|
|
2696
|
+
if (entry.isDirectory() && entry.name !== '_config') {
|
|
2935
2697
|
const moduleAgentsPath = path.join(bmadDir, entry.name, 'agents');
|
|
2936
2698
|
if (await fs.pathExists(moduleAgentsPath)) {
|
|
2937
2699
|
const agentFiles = await fs.readdir(moduleAgentsPath);
|
|
@@ -3033,7 +2795,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3033
2795
|
}
|
|
3034
2796
|
|
|
3035
2797
|
await fs.writeFile(configPath, configContent, 'utf8');
|
|
3036
|
-
this.installedFiles.
|
|
2798
|
+
this.installedFiles.add(configPath); // Track agent config files
|
|
3037
2799
|
createdCount++;
|
|
3038
2800
|
}
|
|
3039
2801
|
|
|
@@ -3049,7 +2811,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3049
2811
|
* @param {Array} agentDetails - Array of agent details
|
|
3050
2812
|
*/
|
|
3051
2813
|
async generateAgentManifest(bmadDir, agentDetails) {
|
|
3052
|
-
const manifestPath = path.join(bmadDir, '
|
|
2814
|
+
const manifestPath = path.join(bmadDir, '_config', 'agent-manifest.csv');
|
|
3053
2815
|
await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
|
|
3054
2816
|
}
|
|
3055
2817
|
|
|
@@ -3099,184 +2861,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3099
2861
|
return nodes;
|
|
3100
2862
|
}
|
|
3101
2863
|
|
|
3102
|
-
/**
|
|
3103
|
-
* Reinstall custom agents from backup and source locations
|
|
3104
|
-
* This preserves custom agents across quick updates/reinstalls
|
|
3105
|
-
* @param {string} projectDir - Project directory
|
|
3106
|
-
* @param {string} bmadDir - BMAD installation directory
|
|
3107
|
-
* @returns {Object} Result with count and agent names
|
|
3108
|
-
*/
|
|
3109
|
-
async reinstallCustomAgents(projectDir, bmadDir) {
|
|
3110
|
-
const {
|
|
3111
|
-
discoverAgents,
|
|
3112
|
-
loadAgentConfig,
|
|
3113
|
-
extractManifestData,
|
|
3114
|
-
addToManifest,
|
|
3115
|
-
createIdeSlashCommands,
|
|
3116
|
-
updateManifestYaml,
|
|
3117
|
-
} = require('../../../lib/agent/installer');
|
|
3118
|
-
const { compileAgent } = require('../../../lib/agent/compiler');
|
|
3119
|
-
|
|
3120
|
-
const results = { count: 0, agents: [] };
|
|
3121
|
-
|
|
3122
|
-
// Check multiple locations for custom agents
|
|
3123
|
-
const sourceLocations = [
|
|
3124
|
-
path.join(bmadDir, '_cfg', 'custom', 'agents'), // Backup location
|
|
3125
|
-
path.join(bmadDir, 'custom', 'src', 'agents'), // BMAD folder source location
|
|
3126
|
-
path.join(projectDir, 'custom', 'src', 'agents'), // Project root source location
|
|
3127
|
-
];
|
|
3128
|
-
|
|
3129
|
-
let foundAgents = [];
|
|
3130
|
-
let processedAgents = new Set(); // Track to avoid duplicates
|
|
3131
|
-
|
|
3132
|
-
// Discover agents from all locations
|
|
3133
|
-
for (const location of sourceLocations) {
|
|
3134
|
-
if (await fs.pathExists(location)) {
|
|
3135
|
-
const agents = discoverAgents(location);
|
|
3136
|
-
// Only add agents we haven't processed yet
|
|
3137
|
-
const newAgents = agents.filter((agent) => !processedAgents.has(agent.name));
|
|
3138
|
-
foundAgents.push(...newAgents);
|
|
3139
|
-
for (const agent of newAgents) processedAgents.add(agent.name);
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
|
-
if (foundAgents.length === 0) {
|
|
3144
|
-
return results;
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
try {
|
|
3148
|
-
const customAgentsDir = path.join(bmadDir, 'custom', 'agents');
|
|
3149
|
-
await fs.ensureDir(customAgentsDir);
|
|
3150
|
-
|
|
3151
|
-
const manifestFile = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
|
|
3152
|
-
const manifestYamlFile = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
|
3153
|
-
|
|
3154
|
-
for (const agent of foundAgents) {
|
|
3155
|
-
try {
|
|
3156
|
-
const agentConfig = loadAgentConfig(agent.yamlFile);
|
|
3157
|
-
const finalAgentName = agent.name; // Already named correctly from save
|
|
3158
|
-
|
|
3159
|
-
// Determine agent type from the name (e.g., "fred-commit-poet" → "commit-poet")
|
|
3160
|
-
let agentType = finalAgentName;
|
|
3161
|
-
const parts = finalAgentName.split('-');
|
|
3162
|
-
if (parts.length >= 2) {
|
|
3163
|
-
// Try to extract type (last part or last two parts)
|
|
3164
|
-
// For "fred-commit-poet", we want "commit-poet"
|
|
3165
|
-
// This is heuristic - could be improved with metadata storage
|
|
3166
|
-
agentType = parts.slice(-2).join('-'); // Take last 2 parts as type
|
|
3167
|
-
}
|
|
3168
|
-
|
|
3169
|
-
// Create target directory - use relative path if agent is in a subdirectory
|
|
3170
|
-
const agentTargetDir = agent.relativePath
|
|
3171
|
-
? path.join(customAgentsDir, agent.relativePath)
|
|
3172
|
-
: path.join(customAgentsDir, finalAgentName);
|
|
3173
|
-
await fs.ensureDir(agentTargetDir);
|
|
3174
|
-
|
|
3175
|
-
// Calculate paths
|
|
3176
|
-
const compiledFileName = `${finalAgentName}.md`;
|
|
3177
|
-
const compiledPath = path.join(agentTargetDir, compiledFileName);
|
|
3178
|
-
const relativePath = path.relative(projectDir, compiledPath);
|
|
3179
|
-
|
|
3180
|
-
// Compile with embedded defaults (answers are already in defaults section)
|
|
3181
|
-
const { xml, metadata } = compileAgent(
|
|
3182
|
-
await fs.readFile(agent.yamlFile, 'utf8'),
|
|
3183
|
-
agentConfig.defaults || {},
|
|
3184
|
-
finalAgentName,
|
|
3185
|
-
relativePath,
|
|
3186
|
-
{ config: config.coreConfig },
|
|
3187
|
-
);
|
|
3188
|
-
|
|
3189
|
-
// Write compiled agent
|
|
3190
|
-
await fs.writeFile(compiledPath, xml, 'utf8');
|
|
3191
|
-
|
|
3192
|
-
// Backup source YAML to _cfg/custom/agents if not already there
|
|
3193
|
-
const cfgAgentsBackupDir = path.join(bmadDir, '_cfg', 'custom', 'agents');
|
|
3194
|
-
await fs.ensureDir(cfgAgentsBackupDir);
|
|
3195
|
-
const backupYamlPath = path.join(cfgAgentsBackupDir, `${finalAgentName}.agent.yaml`);
|
|
3196
|
-
|
|
3197
|
-
// Only backup if source is not already in backup location
|
|
3198
|
-
if (agent.yamlFile !== backupYamlPath) {
|
|
3199
|
-
await fs.copy(agent.yamlFile, backupYamlPath);
|
|
3200
|
-
}
|
|
3201
|
-
|
|
3202
|
-
// Copy sidecar files for agents with hasSidecar flag
|
|
3203
|
-
if (agentConfig.hasSidecar === true && agent.type === 'expert') {
|
|
3204
|
-
const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
|
|
3205
|
-
|
|
3206
|
-
// Get agent sidecar folder from config or use default
|
|
3207
|
-
const agentSidecarFolder = config.coreConfig?.agent_sidecar_folder;
|
|
3208
|
-
|
|
3209
|
-
// Resolve path variables
|
|
3210
|
-
const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('.bmad', bmadDir);
|
|
3211
|
-
|
|
3212
|
-
// Create sidecar directory for this agent
|
|
3213
|
-
const agentSidecarDir = path.join(resolvedSidecarFolder, finalAgentName);
|
|
3214
|
-
await fs.ensureDir(agentSidecarDir);
|
|
3215
|
-
|
|
3216
|
-
// Copy sidecar files (preserve existing, add new)
|
|
3217
|
-
const sidecarResult = copyAgentSidecarFiles(agent.path, agentSidecarDir, agent.yamlFile);
|
|
3218
|
-
|
|
3219
|
-
if (sidecarResult.copied.length > 0 || sidecarResult.preserved.length > 0) {
|
|
3220
|
-
console.log(chalk.dim(` Sidecar: ${sidecarResult.copied.length} new, ${sidecarResult.preserved.length} preserved`));
|
|
3221
|
-
}
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
// Update manifest CSV
|
|
3225
|
-
if (await fs.pathExists(manifestFile)) {
|
|
3226
|
-
// Preserve YAML metadata for persona name, but override id for filename
|
|
3227
|
-
const manifestMetadata = {
|
|
3228
|
-
...metadata,
|
|
3229
|
-
id: relativePath, // Use the compiled agent path for id
|
|
3230
|
-
name: metadata.name || finalAgentName, // Use YAML metadata.name (persona name) or fallback
|
|
3231
|
-
title: metadata.title, // Use YAML title
|
|
3232
|
-
icon: metadata.icon, // Use YAML icon
|
|
3233
|
-
};
|
|
3234
|
-
const manifestData = extractManifestData(xml, manifestMetadata, relativePath, 'custom');
|
|
3235
|
-
manifestData.name = finalAgentName; // Use filename for the name field
|
|
3236
|
-
manifestData.path = relativePath;
|
|
3237
|
-
addToManifest(manifestFile, manifestData);
|
|
3238
|
-
}
|
|
3239
|
-
|
|
3240
|
-
// Create IDE slash commands (async function)
|
|
3241
|
-
await createIdeSlashCommands(projectDir, finalAgentName, relativePath, metadata);
|
|
3242
|
-
|
|
3243
|
-
// Update manifest.yaml
|
|
3244
|
-
if (await fs.pathExists(manifestYamlFile)) {
|
|
3245
|
-
updateManifestYaml(manifestYamlFile, finalAgentName, agentType);
|
|
3246
|
-
}
|
|
3247
|
-
|
|
3248
|
-
results.count++;
|
|
3249
|
-
results.agents.push(finalAgentName);
|
|
3250
|
-
} catch (agentError) {
|
|
3251
|
-
console.log(chalk.yellow(` ⚠️ Failed to reinstall ${agent.name}: ${agentError.message}`));
|
|
3252
|
-
}
|
|
3253
|
-
}
|
|
3254
|
-
} catch (error) {
|
|
3255
|
-
console.log(chalk.yellow(` ⚠️ Error reinstalling custom agents: ${error.message}`));
|
|
3256
|
-
}
|
|
3257
|
-
|
|
3258
|
-
return results;
|
|
3259
|
-
}
|
|
3260
|
-
|
|
3261
|
-
/**
|
|
3262
|
-
* Copy IDE-specific documentation to BMAD docs
|
|
3263
|
-
* @param {Array} ides - List of selected IDEs
|
|
3264
|
-
* @param {string} bmadDir - BMAD installation directory
|
|
3265
|
-
*/
|
|
3266
|
-
async copyIdeDocumentation(ides, bmadDir) {
|
|
3267
|
-
const docsDir = path.join(bmadDir, 'docs');
|
|
3268
|
-
await fs.ensureDir(docsDir);
|
|
3269
|
-
|
|
3270
|
-
for (const ide of ides) {
|
|
3271
|
-
const sourceDocPath = path.join(getProjectRoot(), 'docs', 'ide-info', `${ide}.md`);
|
|
3272
|
-
const targetDocPath = path.join(docsDir, `${ide}-instructions.md`);
|
|
3273
|
-
|
|
3274
|
-
if (await fs.pathExists(sourceDocPath)) {
|
|
3275
|
-
await this.copyFileWithPlaceholderReplacement(sourceDocPath, targetDocPath, this.bmadFolderName || 'bmad');
|
|
3276
|
-
}
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
|
|
3280
2864
|
/**
|
|
3281
2865
|
* Handle missing custom module sources interactively
|
|
3282
2866
|
* @param {Map} customModuleSources - Map of custom module ID to info
|
|
@@ -3301,13 +2885,23 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3301
2885
|
info: customInfo,
|
|
3302
2886
|
});
|
|
3303
2887
|
} else {
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
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
|
+
}
|
|
3311
2905
|
}
|
|
3312
2906
|
}
|
|
3313
2907
|
|