bmad-method 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yaml +15 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- package/.github/workflows/discord.yaml +25 -0
- package/.github/workflows/format-check.yaml +42 -0
- package/.github/workflows/manual-release.yaml +173 -0
- package/.husky/pre-commit +3 -2
- package/.vscode/settings.json +67 -74
- package/CHANGELOG.md +564 -19
- package/CONTRIBUTING.md +168 -5
- package/LICENSE +1 -1
- package/README.md +146 -218
- package/bmad-core/agent-teams/team-all.yaml +14 -0
- package/bmad-core/agent-teams/team-fullstack.yaml +18 -0
- package/bmad-core/agent-teams/team-ide-minimal.yaml +10 -0
- package/bmad-core/agent-teams/team-no-ui.yaml +13 -0
- package/bmad-core/agents/analyst.md +81 -0
- package/bmad-core/agents/architect.md +83 -0
- package/bmad-core/agents/bmad-master.md +107 -0
- package/bmad-core/agents/bmad-orchestrator.md +149 -0
- package/bmad-core/agents/dev.md +75 -0
- package/bmad-core/agents/pm.md +81 -0
- package/bmad-core/agents/po.md +76 -0
- package/bmad-core/agents/qa.md +88 -0
- package/bmad-core/agents/sm.md +62 -0
- package/bmad-core/agents/ux-expert.md +66 -0
- package/{.bmad-core → bmad-core}/checklists/architect-checklist.md +0 -5
- package/{.bmad-core → bmad-core}/checklists/change-checklist.md +2 -2
- package/{.bmad-core → bmad-core}/checklists/pm-checklist.md +0 -5
- package/{.bmad-core → bmad-core}/checklists/po-master-checklist.md +0 -9
- package/{.bmad-core → bmad-core}/checklists/story-dod-checklist.md +0 -7
- package/{.bmad-core → bmad-core}/checklists/story-draft-checklist.md +1 -4
- package/bmad-core/core-config.yaml +20 -0
- package/bmad-core/data/bmad-kb.md +806 -0
- package/bmad-core/data/brainstorming-techniques.md +36 -0
- package/bmad-core/data/elicitation-methods.md +154 -0
- package/bmad-core/data/test-levels-framework.md +146 -0
- package/bmad-core/data/test-priorities-matrix.md +172 -0
- package/bmad-core/tasks/advanced-elicitation.md +117 -0
- package/{.bmad-core → bmad-core}/tasks/correct-course.md +9 -12
- package/bmad-core/tasks/create-brownfield-story.md +312 -0
- package/{.bmad-core → bmad-core}/tasks/create-deep-research-prompt.md +4 -27
- package/bmad-core/tasks/create-next-story.md +112 -0
- package/bmad-core/tasks/document-project.md +343 -0
- package/bmad-core/tasks/facilitate-brainstorming-session.md +136 -0
- package/bmad-core/tasks/generate-ai-frontend-prompt.md +51 -0
- package/{.bmad-core → bmad-core}/tasks/index-docs.md +3 -13
- package/bmad-core/tasks/kb-mode-interaction.md +75 -0
- package/bmad-core/tasks/nfr-assess.md +343 -0
- package/bmad-core/tasks/qa-gate.md +159 -0
- package/bmad-core/tasks/review-story.md +314 -0
- package/bmad-core/tasks/risk-profile.md +353 -0
- package/{.bmad-core → bmad-core}/tasks/shard-doc.md +27 -15
- package/bmad-core/tasks/test-design.md +174 -0
- package/bmad-core/tasks/trace-requirements.md +264 -0
- package/bmad-core/tasks/validate-next-story.md +134 -0
- package/bmad-core/templates/architecture-tmpl.yaml +650 -0
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -0
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +476 -0
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +280 -0
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +306 -0
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +218 -0
- package/bmad-core/templates/front-end-spec-tmpl.yaml +349 -0
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +823 -0
- package/bmad-core/templates/market-research-tmpl.yaml +252 -0
- package/bmad-core/templates/prd-tmpl.yaml +202 -0
- package/bmad-core/templates/project-brief-tmpl.yaml +221 -0
- package/bmad-core/templates/qa-gate-tmpl.yaml +102 -0
- package/bmad-core/templates/story-tmpl.yaml +137 -0
- package/bmad-core/workflows/brownfield-fullstack.yaml +297 -0
- package/bmad-core/workflows/brownfield-service.yaml +187 -0
- package/bmad-core/workflows/brownfield-ui.yaml +197 -0
- package/{.bmad-core/workflows/greenfield-fullstack.yml → bmad-core/workflows/greenfield-fullstack.yaml} +140 -77
- package/bmad-core/workflows/greenfield-service.yaml +206 -0
- package/bmad-core/workflows/greenfield-ui.yaml +235 -0
- package/common/tasks/create-doc.md +101 -0
- package/{.bmad-core → common}/tasks/execute-checklist.md +2 -13
- package/common/utils/bmad-doc-template.md +325 -0
- package/common/utils/workflow-management.md +69 -0
- package/dist/agents/analyst.txt +2889 -0
- package/dist/agents/architect.txt +3552 -0
- package/dist/agents/bmad-master.txt +8769 -0
- package/dist/agents/bmad-orchestrator.txt +1513 -0
- package/dist/agents/dev.txt +414 -0
- package/{.bmad-core/web-bundles → dist}/agents/pm.txt +668 -1119
- package/{.bmad-core/web-bundles → dist}/agents/po.txt +341 -484
- package/dist/agents/qa.txt +1987 -0
- package/dist/agents/sm.txt +658 -0
- package/dist/agents/ux-expert.txt +694 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +2371 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +1620 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +815 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +10952 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +4012 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +3698 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +450 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +973 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +15376 -0
- package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2075 -0
- package/dist/teams/team-all.txt +12682 -0
- package/dist/teams/team-fullstack.txt +10421 -0
- package/dist/teams/team-ide-minimal.txt +5103 -0
- package/dist/teams/team-no-ui.txt +8980 -0
- package/docs/GUIDING-PRINCIPLES.md +91 -0
- package/docs/core-architecture.md +219 -0
- package/docs/enhanced-ide-development-workflow.md +248 -0
- package/docs/expansion-packs.md +280 -0
- package/docs/how-to-contribute-with-pull-requests.md +158 -0
- package/docs/user-guide.md +504 -0
- package/docs/versioning-and-releases.md +8 -16
- package/docs/versions.md +4 -5
- package/docs/working-in-the-brownfield.md +597 -0
- package/eslint.config.mjs +119 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +102 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash copy.txt +13 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash.txt +13 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.2 Agent Development Kit Installation/1.2.2 - Basic Project Structure - txt.txt +25 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.1 - settings.py +34 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.2 - main.py - Base Application.py +70 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.4 Deployment Configuration/1.4.2 - cloudbuild.yaml +26 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +109 -0
- package/expansion-packs/README.md +2 -112
- package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +13 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +71 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +78 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +64 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +201 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +160 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +8 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +250 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +647 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +110 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +216 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +290 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +613 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +356 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +253 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +183 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +175 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +14 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +80 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +77 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +65 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +391 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +203 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +124 -0
- package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
- package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +769 -0
- package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +586 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +110 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +141 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +184 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +290 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +200 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +1030 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +705 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/README.md +9 -9
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/agents/infra-devops-platform.md +30 -18
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/checklists/infrastructure-checklist.md +1 -1
- package/expansion-packs/bmad-infrastructure-devops/config.yaml +9 -0
- package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +305 -0
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/tasks/review-infrastructure.md +4 -5
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/tasks/validate-infrastructure.md +4 -5
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +424 -0
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +629 -0
- package/package.json +74 -42
- package/prettier.config.mjs +32 -0
- package/release_notes.md +25 -0
- package/tools/bmad-npx-wrapper.js +13 -15
- package/tools/builders/web-builder.js +544 -15
- package/tools/bump-all-versions.js +115 -0
- package/tools/bump-expansion-version.js +90 -0
- package/tools/cli.js +65 -32
- package/tools/flattener/aggregate.js +76 -0
- package/tools/flattener/binary.js +80 -0
- package/tools/flattener/discovery.js +71 -0
- package/tools/flattener/files.js +35 -0
- package/tools/flattener/ignoreRules.js +176 -0
- package/tools/flattener/main.js +573 -0
- package/tools/flattener/projectRoot.js +206 -0
- package/tools/flattener/prompts.js +44 -0
- package/tools/flattener/stats.helpers.js +395 -0
- package/tools/flattener/stats.js +80 -0
- package/tools/flattener/test-matrix.js +413 -0
- package/tools/flattener/xml.js +88 -0
- package/tools/installer/README.md +3 -53
- package/tools/installer/bin/bmad.js +475 -90
- package/tools/installer/config/ide-agent-config.yaml +58 -0
- package/tools/installer/config/install.config.yaml +123 -0
- package/tools/installer/lib/config-loader.js +208 -40
- package/tools/installer/lib/file-manager.js +258 -55
- package/tools/installer/lib/ide-base-setup.js +228 -0
- package/tools/installer/lib/ide-setup.js +1265 -253
- package/tools/installer/lib/installer.js +1651 -310
- package/tools/installer/lib/memory-profiler.js +225 -0
- package/tools/installer/lib/module-manager.js +114 -0
- package/tools/installer/lib/resource-locator.js +308 -0
- package/tools/installer/package.json +25 -24
- package/tools/lib/dependency-resolver.js +44 -48
- package/tools/lib/yaml-utils.js +29 -0
- package/tools/md-assets/web-agent-startup-instructions.md +39 -0
- package/tools/preview-release-notes.js +66 -0
- package/tools/shared/bannerArt.js +105 -0
- package/tools/sync-installer-version.js +7 -9
- package/tools/update-expansion-version.js +53 -0
- package/tools/upgraders/v3-to-v4-upgrader.js +221 -320
- package/tools/version-bump.js +42 -27
- package/tools/yaml-format.js +57 -44
- package/.bmad-core/agent-teams/team-all.yml +0 -16
- package/.bmad-core/agent-teams/team-fullstack.yml +0 -26
- package/.bmad-core/agent-teams/team-no-ui.yml +0 -15
- package/.bmad-core/agents/analyst.md +0 -59
- package/.bmad-core/agents/architect.md +0 -66
- package/.bmad-core/agents/bmad-master.md +0 -104
- package/.bmad-core/agents/bmad-orchestrator.md +0 -81
- package/.bmad-core/agents/dev.md +0 -70
- package/.bmad-core/agents/pm.md +0 -59
- package/.bmad-core/agents/po.md +0 -60
- package/.bmad-core/agents/qa.md +0 -52
- package/.bmad-core/agents/sm.md +0 -55
- package/.bmad-core/agents/ux-expert.md +0 -66
- package/.bmad-core/data/bmad-kb.md +0 -47
- package/.bmad-core/schemas/agent-team-schema.yml +0 -153
- package/.bmad-core/tasks/advanced-elicitation.md +0 -92
- package/.bmad-core/tasks/brainstorming-techniques.md +0 -238
- package/.bmad-core/tasks/core-dump.md +0 -74
- package/.bmad-core/tasks/create-agent.md +0 -202
- package/.bmad-core/tasks/create-doc.md +0 -74
- package/.bmad-core/tasks/create-expansion-pack.md +0 -425
- package/.bmad-core/tasks/create-next-story.md +0 -206
- package/.bmad-core/tasks/create-team.md +0 -229
- package/.bmad-core/tasks/doc-migration-task.md +0 -143
- package/.bmad-core/tasks/generate-ai-frontend-prompt.md +0 -58
- package/.bmad-core/templates/agent-tmpl.md +0 -58
- package/.bmad-core/templates/architecture-tmpl.md +0 -771
- package/.bmad-core/templates/brownfield-architecture-tmpl.md +0 -542
- package/.bmad-core/templates/brownfield-prd-tmpl.md +0 -240
- package/.bmad-core/templates/competitor-analysis-tmpl.md +0 -289
- package/.bmad-core/templates/expansion-pack-plan-tmpl.md +0 -91
- package/.bmad-core/templates/front-end-architecture-tmpl.md +0 -173
- package/.bmad-core/templates/front-end-spec-tmpl.md +0 -411
- package/.bmad-core/templates/fullstack-architecture-tmpl.md +0 -1016
- package/.bmad-core/templates/market-research-tmpl.md +0 -261
- package/.bmad-core/templates/prd-tmpl.md +0 -200
- package/.bmad-core/templates/project-brief-tmpl.md +0 -228
- package/.bmad-core/templates/simple-project-prd-tmpl.md +0 -461
- package/.bmad-core/templates/story-tmpl.md +0 -61
- package/.bmad-core/templates/web-agent-startup-instructions-template.md +0 -39
- package/.bmad-core/utils/agent-switcher.ide.md +0 -112
- package/.bmad-core/utils/template-format.md +0 -26
- package/.bmad-core/utils/workflow-management.md +0 -224
- package/.bmad-core/web-bundles/agents/analyst.txt +0 -1684
- package/.bmad-core/web-bundles/agents/architect.txt +0 -3584
- package/.bmad-core/web-bundles/agents/bmad-master.txt +0 -9491
- package/.bmad-core/web-bundles/agents/bmad-orchestrator.txt +0 -1466
- package/.bmad-core/web-bundles/agents/dev.txt +0 -316
- package/.bmad-core/web-bundles/agents/qa.txt +0 -129
- package/.bmad-core/web-bundles/agents/sm.txt +0 -658
- package/.bmad-core/web-bundles/agents/ux-expert.txt +0 -1099
- package/.bmad-core/web-bundles/teams/team-all.txt +0 -10757
- package/.bmad-core/web-bundles/teams/team-fullstack.txt +0 -10109
- package/.bmad-core/web-bundles/teams/team-no-ui.txt +0 -8950
- package/.bmad-core/workflows/brownfield-fullstack.yml +0 -116
- package/.bmad-core/workflows/brownfield-service.yml +0 -117
- package/.bmad-core/workflows/brownfield-ui.yml +0 -127
- package/.bmad-core/workflows/greenfield-service.yml +0 -143
- package/.bmad-core/workflows/greenfield-ui.yml +0 -172
- package/.claude/commands/analyst.md +0 -63
- package/.claude/commands/architect.md +0 -70
- package/.claude/commands/bmad-master.md +0 -108
- package/.claude/commands/bmad-orchestrator.md +0 -85
- package/.claude/commands/dev.md +0 -74
- package/.claude/commands/pm.md +0 -63
- package/.claude/commands/po.md +0 -64
- package/.claude/commands/qa.md +0 -56
- package/.claude/commands/sm.md +0 -59
- package/.claude/commands/ux-expert.md +0 -70
- package/.cursor/rules/analyst.mdc +0 -77
- package/.cursor/rules/architect.mdc +0 -84
- package/.cursor/rules/bmad-master.mdc +0 -122
- package/.cursor/rules/bmad-orchestrator.mdc +0 -99
- package/.cursor/rules/dev.mdc +0 -88
- package/.cursor/rules/pm.mdc +0 -77
- package/.cursor/rules/po.mdc +0 -78
- package/.cursor/rules/qa.mdc +0 -70
- package/.cursor/rules/sm.mdc +0 -73
- package/.cursor/rules/ux-expert.mdc +0 -84
- package/.github/workflows/release.yml +0 -59
- package/.releaserc.json +0 -18
- package/.roo/.roomodes +0 -95
- package/.roo/README.md +0 -38
- package/.vscode/extensions.json +0 -6
- package/.windsurf/rules/analyst.md +0 -71
- package/.windsurf/rules/architect.md +0 -78
- package/.windsurf/rules/bmad-master.md +0 -116
- package/.windsurf/rules/bmad-orchestrator.md +0 -93
- package/.windsurf/rules/dev.md +0 -82
- package/.windsurf/rules/pm.md +0 -71
- package/.windsurf/rules/po.md +0 -72
- package/.windsurf/rules/qa.md +0 -64
- package/.windsurf/rules/sm.md +0 -67
- package/.windsurf/rules/ux-expert.md +0 -78
- package/docs/bmad-workflow-guide.md +0 -161
- package/docs/claude-code-guide.md +0 -119
- package/docs/cursor-guide.md +0 -127
- package/docs/roo-code-guide.md +0 -140
- package/docs/sample-output/simple-fullstack-greenfield/prd.md +0 -42
- package/docs/windsurf-guide.md +0 -127
- package/expansion-packs/infrastructure-devops/manifest.yml +0 -38
- package/expansion-packs/infrastructure-devops/templates/infrastructure-architecture-tmpl.md +0 -415
- package/expansion-packs/infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
- package/tools/installer/config/install.config.yml +0 -139
- package/tools/installer/package-lock.json +0 -906
- package/tools/installer/templates/claude-commands.md +0 -7
- package/tools/installer/templates/cursor-rules.md +0 -22
- package/tools/installer/templates/windsurf-rules.md +0 -22
- package/tools/semantic-release-sync-installer.js +0 -31
- /package/{.bmad-core → bmad-core}/data/technical-preferences.md +0 -0
- /package/{.bmad-core → bmad-core}/tasks/brownfield-create-epic.md +0 -0
- /package/{.bmad-core → bmad-core}/tasks/brownfield-create-story.md +0 -0
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const
|
|
3
|
-
const
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
6
|
+
const fileManager = require('./file-manager');
|
|
7
|
+
const configLoader = require('./config-loader');
|
|
8
|
+
const { extractYamlFromAgent } = require('../../lib/yaml-utils');
|
|
9
|
+
const BaseIdeSetup = require('./ide-base-setup');
|
|
10
|
+
const resourceLocator = require('./resource-locator');
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
12
|
+
class IdeSetup extends BaseIdeSetup {
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
this.ideAgentConfig = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async loadIdeAgentConfig() {
|
|
19
|
+
if (this.ideAgentConfig) return this.ideAgentConfig;
|
|
7
20
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
try {
|
|
22
|
+
const configPath = path.join(__dirname, '..', 'config', 'ide-agent-config.yaml');
|
|
23
|
+
const configContent = await fs.readFile(configPath, 'utf8');
|
|
24
|
+
this.ideAgentConfig = yaml.load(configContent);
|
|
25
|
+
return this.ideAgentConfig;
|
|
26
|
+
} catch {
|
|
27
|
+
console.warn('Failed to load IDE agent configuration, using defaults');
|
|
28
|
+
return {
|
|
29
|
+
'roo-permissions': {},
|
|
30
|
+
'cline-order': {},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
12
33
|
}
|
|
13
|
-
}
|
|
14
34
|
|
|
15
|
-
|
|
16
|
-
async setup(ide, installDir, selectedAgent = null) {
|
|
17
|
-
await initializeModules();
|
|
35
|
+
async setup(ide, installDir, selectedAgent = null, spinner = null, preConfiguredSettings = null) {
|
|
18
36
|
const ideConfig = await configLoader.getIdeConfiguration(ide);
|
|
19
37
|
|
|
20
38
|
if (!ideConfig) {
|
|
@@ -23,108 +41,185 @@ class IdeSetup {
|
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
switch (ide) {
|
|
26
|
-
case
|
|
44
|
+
case 'cursor': {
|
|
27
45
|
return this.setupCursor(installDir, selectedAgent);
|
|
28
|
-
|
|
46
|
+
}
|
|
47
|
+
case 'claude-code': {
|
|
29
48
|
return this.setupClaudeCode(installDir, selectedAgent);
|
|
30
|
-
|
|
49
|
+
}
|
|
50
|
+
case 'crush': {
|
|
51
|
+
return this.setupCrush(installDir, selectedAgent);
|
|
52
|
+
}
|
|
53
|
+
case 'windsurf': {
|
|
31
54
|
return this.setupWindsurf(installDir, selectedAgent);
|
|
32
|
-
|
|
55
|
+
}
|
|
56
|
+
case 'trae': {
|
|
57
|
+
return this.setupTrae(installDir, selectedAgent);
|
|
58
|
+
}
|
|
59
|
+
case 'roo': {
|
|
33
60
|
return this.setupRoo(installDir, selectedAgent);
|
|
34
|
-
|
|
61
|
+
}
|
|
62
|
+
case 'cline': {
|
|
63
|
+
return this.setupCline(installDir, selectedAgent);
|
|
64
|
+
}
|
|
65
|
+
case 'kilo': {
|
|
66
|
+
return this.setupKilocode(installDir, selectedAgent);
|
|
67
|
+
}
|
|
68
|
+
case 'gemini': {
|
|
69
|
+
return this.setupGeminiCli(installDir, selectedAgent);
|
|
70
|
+
}
|
|
71
|
+
case 'github-copilot': {
|
|
72
|
+
return this.setupGitHubCopilot(installDir, selectedAgent, spinner, preConfiguredSettings);
|
|
73
|
+
}
|
|
74
|
+
case 'qwen-code': {
|
|
75
|
+
return this.setupQwenCode(installDir, selectedAgent);
|
|
76
|
+
}
|
|
77
|
+
default: {
|
|
35
78
|
console.log(chalk.yellow(`\nIDE ${ide} not yet supported`));
|
|
36
79
|
return false;
|
|
80
|
+
}
|
|
37
81
|
}
|
|
38
82
|
}
|
|
39
83
|
|
|
40
84
|
async setupCursor(installDir, selectedAgent) {
|
|
41
|
-
const cursorRulesDir = path.join(installDir,
|
|
42
|
-
const agents = selectedAgent
|
|
43
|
-
? [selectedAgent]
|
|
44
|
-
: await this.getAllAgentIds(installDir);
|
|
85
|
+
const cursorRulesDir = path.join(installDir, '.cursor', 'rules', 'bmad');
|
|
86
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
45
87
|
|
|
46
88
|
await fileManager.ensureDirectory(cursorRulesDir);
|
|
47
89
|
|
|
48
90
|
for (const agentId of agents) {
|
|
49
|
-
|
|
50
|
-
let agentPath = path.join(
|
|
51
|
-
installDir,
|
|
52
|
-
".bmad-core",
|
|
53
|
-
"agents",
|
|
54
|
-
`${agentId}.md`
|
|
55
|
-
);
|
|
56
|
-
if (!(await fileManager.pathExists(agentPath))) {
|
|
57
|
-
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
|
58
|
-
}
|
|
91
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
59
92
|
|
|
60
|
-
if (
|
|
61
|
-
const
|
|
93
|
+
if (agentPath) {
|
|
94
|
+
const mdcContent = await this.createAgentRuleContent(agentId, agentPath, installDir, 'mdc');
|
|
62
95
|
const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
|
|
63
|
-
|
|
64
|
-
// Create MDC content with proper format
|
|
65
|
-
let mdcContent = "---\n";
|
|
66
|
-
mdcContent += "description: \n";
|
|
67
|
-
mdcContent += "globs: []\n";
|
|
68
|
-
mdcContent += "alwaysApply: false\n";
|
|
69
|
-
mdcContent += "---\n\n";
|
|
70
|
-
mdcContent += `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
|
71
|
-
mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
|
|
72
|
-
agentId
|
|
73
|
-
)} agent persona.\n\n`;
|
|
74
|
-
mdcContent += "## Agent Activation\n\n";
|
|
75
|
-
mdcContent +=
|
|
76
|
-
"CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
|
|
77
|
-
mdcContent += "```yml\n";
|
|
78
|
-
// Extract just the YAML content from the agent file
|
|
79
|
-
const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
|
|
80
|
-
if (yamlMatch) {
|
|
81
|
-
mdcContent += yamlMatch[1].trim();
|
|
82
|
-
} else {
|
|
83
|
-
// If no YAML found, include the whole content minus the header
|
|
84
|
-
mdcContent += agentContent.replace(/^#.*$/m, "").trim();
|
|
85
|
-
}
|
|
86
|
-
mdcContent += "\n```\n\n";
|
|
87
|
-
mdcContent += "## File Reference\n\n";
|
|
88
|
-
mdcContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](mdc:.bmad-core/agents/${agentId}.md).\n\n`;
|
|
89
|
-
mdcContent += "## Usage\n\n";
|
|
90
|
-
mdcContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
|
|
91
|
-
agentId
|
|
92
|
-
)} persona and follow all instructions defined in the YML configuration above.\n`;
|
|
93
|
-
|
|
94
96
|
await fileManager.writeFile(mdcPath, mdcContent);
|
|
95
97
|
console.log(chalk.green(`✓ Created rule: ${agentId}.mdc`));
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
console.log(chalk.green(`\n✓ Created Cursor rules in ${cursorRulesDir}`));
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async setupCrush(installDir, selectedAgent) {
|
|
106
|
+
// Setup bmad-core commands
|
|
107
|
+
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
|
108
|
+
const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
|
|
109
|
+
const coreTasks = await this.getCoreTaskIds(installDir);
|
|
110
|
+
await this.setupCrushForPackage(
|
|
111
|
+
installDir,
|
|
112
|
+
'core',
|
|
113
|
+
coreSlashPrefix,
|
|
114
|
+
coreAgents,
|
|
115
|
+
coreTasks,
|
|
116
|
+
'.bmad-core',
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Setup expansion pack commands
|
|
120
|
+
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
|
121
|
+
for (const packInfo of expansionPacks) {
|
|
122
|
+
const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
|
|
123
|
+
const packAgents = await this.getExpansionPackAgents(packInfo.path);
|
|
124
|
+
const packTasks = await this.getExpansionPackTasks(packInfo.path);
|
|
125
|
+
|
|
126
|
+
if (packAgents.length > 0 || packTasks.length > 0) {
|
|
127
|
+
// Use the actual directory name where the expansion pack is installed
|
|
128
|
+
const rootPath = path.relative(installDir, packInfo.path);
|
|
129
|
+
await this.setupCrushForPackage(
|
|
130
|
+
installDir,
|
|
131
|
+
packInfo.name,
|
|
132
|
+
packSlashPrefix,
|
|
133
|
+
packAgents,
|
|
134
|
+
packTasks,
|
|
135
|
+
rootPath,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
100
139
|
|
|
101
140
|
return true;
|
|
102
141
|
}
|
|
103
142
|
|
|
104
143
|
async setupClaudeCode(installDir, selectedAgent) {
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
144
|
+
// Setup bmad-core commands
|
|
145
|
+
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
|
146
|
+
const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
|
|
147
|
+
const coreTasks = await this.getCoreTaskIds(installDir);
|
|
148
|
+
await this.setupClaudeCodeForPackage(
|
|
149
|
+
installDir,
|
|
150
|
+
'core',
|
|
151
|
+
coreSlashPrefix,
|
|
152
|
+
coreAgents,
|
|
153
|
+
coreTasks,
|
|
154
|
+
'.bmad-core',
|
|
155
|
+
);
|
|
109
156
|
|
|
110
|
-
|
|
157
|
+
// Setup expansion pack commands
|
|
158
|
+
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
|
159
|
+
for (const packInfo of expansionPacks) {
|
|
160
|
+
const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
|
|
161
|
+
const packAgents = await this.getExpansionPackAgents(packInfo.path);
|
|
162
|
+
const packTasks = await this.getExpansionPackTasks(packInfo.path);
|
|
111
163
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
164
|
+
if (packAgents.length > 0 || packTasks.length > 0) {
|
|
165
|
+
// Use the actual directory name where the expansion pack is installed
|
|
166
|
+
const rootPath = path.relative(installDir, packInfo.path);
|
|
167
|
+
await this.setupClaudeCodeForPackage(
|
|
168
|
+
installDir,
|
|
169
|
+
packInfo.name,
|
|
170
|
+
packSlashPrefix,
|
|
171
|
+
packAgents,
|
|
172
|
+
packTasks,
|
|
173
|
+
rootPath,
|
|
174
|
+
);
|
|
122
175
|
}
|
|
123
|
-
|
|
176
|
+
}
|
|
124
177
|
|
|
125
|
-
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async setupClaudeCodeForPackage(
|
|
182
|
+
installDir,
|
|
183
|
+
packageName,
|
|
184
|
+
slashPrefix,
|
|
185
|
+
agentIds,
|
|
186
|
+
taskIds,
|
|
187
|
+
rootPath,
|
|
188
|
+
) {
|
|
189
|
+
const commandsBaseDir = path.join(installDir, '.claude', 'commands', slashPrefix);
|
|
190
|
+
const agentsDir = path.join(commandsBaseDir, 'agents');
|
|
191
|
+
const tasksDir = path.join(commandsBaseDir, 'tasks');
|
|
192
|
+
|
|
193
|
+
// Ensure directories exist
|
|
194
|
+
await fileManager.ensureDirectory(agentsDir);
|
|
195
|
+
await fileManager.ensureDirectory(tasksDir);
|
|
196
|
+
|
|
197
|
+
// Setup agents
|
|
198
|
+
for (const agentId of agentIds) {
|
|
199
|
+
// Find the agent file - for expansion packs, prefer the expansion pack version
|
|
200
|
+
let agentPath;
|
|
201
|
+
if (packageName === 'core') {
|
|
202
|
+
// For core, use the normal search
|
|
203
|
+
agentPath = await this.findAgentPath(agentId, installDir);
|
|
204
|
+
} else {
|
|
205
|
+
// For expansion packs, first try to find the agent in the expansion pack directory
|
|
206
|
+
const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
|
|
207
|
+
if (await fileManager.pathExists(expansionPackPath)) {
|
|
208
|
+
agentPath = expansionPackPath;
|
|
209
|
+
} else {
|
|
210
|
+
// Fall back to core if not found in expansion pack
|
|
211
|
+
agentPath = await this.findAgentPath(agentId, installDir);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const commandPath = path.join(agentsDir, `${agentId}.md`);
|
|
216
|
+
|
|
217
|
+
if (agentPath) {
|
|
126
218
|
// Create command file with agent content
|
|
127
|
-
|
|
219
|
+
let agentContent = await fileManager.readFile(agentPath);
|
|
220
|
+
|
|
221
|
+
// Replace {root} placeholder with the appropriate root path for this context
|
|
222
|
+
agentContent = agentContent.replaceAll('{root}', rootPath);
|
|
128
223
|
|
|
129
224
|
// Add command header
|
|
130
225
|
let commandContent = `# /${agentId} Command\n\n`;
|
|
@@ -132,119 +227,551 @@ class IdeSetup {
|
|
|
132
227
|
commandContent += agentContent;
|
|
133
228
|
|
|
134
229
|
await fileManager.writeFile(commandPath, commandContent);
|
|
135
|
-
console.log(chalk.green(`✓ Created command: /${agentId}`));
|
|
230
|
+
console.log(chalk.green(`✓ Created agent command: /${agentId}`));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Setup tasks
|
|
235
|
+
for (const taskId of taskIds) {
|
|
236
|
+
// Find the task file - for expansion packs, prefer the expansion pack version
|
|
237
|
+
let taskPath;
|
|
238
|
+
if (packageName === 'core') {
|
|
239
|
+
// For core, use the normal search
|
|
240
|
+
taskPath = await this.findTaskPath(taskId, installDir);
|
|
241
|
+
} else {
|
|
242
|
+
// For expansion packs, first try to find the task in the expansion pack directory
|
|
243
|
+
const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
|
|
244
|
+
if (await fileManager.pathExists(expansionPackPath)) {
|
|
245
|
+
taskPath = expansionPackPath;
|
|
246
|
+
} else {
|
|
247
|
+
// Fall back to core if not found in expansion pack
|
|
248
|
+
taskPath = await this.findTaskPath(taskId, installDir);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const commandPath = path.join(tasksDir, `${taskId}.md`);
|
|
253
|
+
|
|
254
|
+
if (taskPath) {
|
|
255
|
+
// Create command file with task content
|
|
256
|
+
let taskContent = await fileManager.readFile(taskPath);
|
|
257
|
+
|
|
258
|
+
// Replace {root} placeholder with the appropriate root path for this context
|
|
259
|
+
taskContent = taskContent.replaceAll('{root}', rootPath);
|
|
260
|
+
|
|
261
|
+
// Add command header
|
|
262
|
+
let commandContent = `# /${taskId} Task\n\n`;
|
|
263
|
+
commandContent += `When this command is used, execute the following task:\n\n`;
|
|
264
|
+
commandContent += taskContent;
|
|
265
|
+
|
|
266
|
+
await fileManager.writeFile(commandPath, commandContent);
|
|
267
|
+
console.log(chalk.green(`✓ Created task command: /${taskId}`));
|
|
136
268
|
}
|
|
137
269
|
}
|
|
138
270
|
|
|
139
271
|
console.log(
|
|
140
|
-
chalk.green(`\n✓ Created Claude Code commands in ${
|
|
272
|
+
chalk.green(`\n✓ Created Claude Code commands for ${packageName} in ${commandsBaseDir}`),
|
|
141
273
|
);
|
|
274
|
+
console.log(chalk.dim(` - Agents in: ${agentsDir}`));
|
|
275
|
+
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
|
276
|
+
}
|
|
142
277
|
|
|
143
|
-
|
|
278
|
+
async setupCrushForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
|
|
279
|
+
const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix);
|
|
280
|
+
const agentsDir = path.join(commandsBaseDir, 'agents');
|
|
281
|
+
const tasksDir = path.join(commandsBaseDir, 'tasks');
|
|
282
|
+
|
|
283
|
+
// Ensure directories exist
|
|
284
|
+
await fileManager.ensureDirectory(agentsDir);
|
|
285
|
+
await fileManager.ensureDirectory(tasksDir);
|
|
286
|
+
|
|
287
|
+
// Setup agents
|
|
288
|
+
for (const agentId of agentIds) {
|
|
289
|
+
// Find the agent file - for expansion packs, prefer the expansion pack version
|
|
290
|
+
let agentPath;
|
|
291
|
+
if (packageName === 'core') {
|
|
292
|
+
// For core, use the normal search
|
|
293
|
+
agentPath = await this.findAgentPath(agentId, installDir);
|
|
294
|
+
} else {
|
|
295
|
+
// For expansion packs, first try to find the agent in the expansion pack directory
|
|
296
|
+
const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
|
|
297
|
+
if (await fileManager.pathExists(expansionPackPath)) {
|
|
298
|
+
agentPath = expansionPackPath;
|
|
299
|
+
} else {
|
|
300
|
+
// Fall back to core if not found in expansion pack
|
|
301
|
+
agentPath = await this.findAgentPath(agentId, installDir);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const commandPath = path.join(agentsDir, `${agentId}.md`);
|
|
306
|
+
|
|
307
|
+
if (agentPath) {
|
|
308
|
+
// Create command file with agent content
|
|
309
|
+
let agentContent = await fileManager.readFile(agentPath);
|
|
310
|
+
|
|
311
|
+
// Replace {root} placeholder with the appropriate root path for this context
|
|
312
|
+
agentContent = agentContent.replaceAll('{root}', rootPath);
|
|
313
|
+
|
|
314
|
+
// Add command header
|
|
315
|
+
let commandContent = `# /${agentId} Command\n\n`;
|
|
316
|
+
commandContent += `When this command is used, adopt the following agent persona:\n\n`;
|
|
317
|
+
commandContent += agentContent;
|
|
318
|
+
|
|
319
|
+
await fileManager.writeFile(commandPath, commandContent);
|
|
320
|
+
console.log(chalk.green(`✓ Created agent command: /${agentId}`));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Setup tasks
|
|
325
|
+
for (const taskId of taskIds) {
|
|
326
|
+
// Find the task file - for expansion packs, prefer the expansion pack version
|
|
327
|
+
let taskPath;
|
|
328
|
+
if (packageName === 'core') {
|
|
329
|
+
// For core, use the normal search
|
|
330
|
+
taskPath = await this.findTaskPath(taskId, installDir);
|
|
331
|
+
} else {
|
|
332
|
+
// For expansion packs, first try to find the task in the expansion pack directory
|
|
333
|
+
const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
|
|
334
|
+
if (await fileManager.pathExists(expansionPackPath)) {
|
|
335
|
+
taskPath = expansionPackPath;
|
|
336
|
+
} else {
|
|
337
|
+
// Fall back to core if not found in expansion pack
|
|
338
|
+
taskPath = await this.findTaskPath(taskId, installDir);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const commandPath = path.join(tasksDir, `${taskId}.md`);
|
|
343
|
+
|
|
344
|
+
if (taskPath) {
|
|
345
|
+
// Create command file with task content
|
|
346
|
+
let taskContent = await fileManager.readFile(taskPath);
|
|
347
|
+
|
|
348
|
+
// Replace {root} placeholder with the appropriate root path for this context
|
|
349
|
+
taskContent = taskContent.replaceAll('{root}', rootPath);
|
|
350
|
+
|
|
351
|
+
// Add command header
|
|
352
|
+
let commandContent = `# /${taskId} Task\n\n`;
|
|
353
|
+
commandContent += `When this command is used, execute the following task:\n\n`;
|
|
354
|
+
commandContent += taskContent;
|
|
355
|
+
|
|
356
|
+
await fileManager.writeFile(commandPath, commandContent);
|
|
357
|
+
console.log(chalk.green(`✓ Created task command: /${taskId}`));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
console.log(chalk.green(`\n✓ Created Crush commands for ${packageName} in ${commandsBaseDir}`));
|
|
362
|
+
console.log(chalk.dim(` - Agents in: ${agentsDir}`));
|
|
363
|
+
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
|
144
364
|
}
|
|
145
365
|
|
|
146
366
|
async setupWindsurf(installDir, selectedAgent) {
|
|
147
|
-
const
|
|
148
|
-
const agents = selectedAgent
|
|
149
|
-
? [selectedAgent]
|
|
150
|
-
: await this.getAllAgentIds(installDir);
|
|
367
|
+
const windsurfWorkflowDir = path.join(installDir, '.windsurf', 'workflows');
|
|
368
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
151
369
|
|
|
152
|
-
await fileManager.ensureDirectory(
|
|
370
|
+
await fileManager.ensureDirectory(windsurfWorkflowDir);
|
|
153
371
|
|
|
154
372
|
for (const agentId of agents) {
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
`${agentId}.md`
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
373
|
+
// Find the agent file
|
|
374
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
375
|
+
|
|
376
|
+
if (agentPath) {
|
|
377
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
378
|
+
const mdPath = path.join(windsurfWorkflowDir, `${agentId}.md`);
|
|
379
|
+
|
|
380
|
+
// Write the agent file contents prefixed with Windsurf frontmatter
|
|
381
|
+
let mdContent = `---\n`;
|
|
382
|
+
mdContent += `description: ${agentId}\n`;
|
|
383
|
+
mdContent += `auto_execution_mode: 3\n`;
|
|
384
|
+
mdContent += `---\n\n`;
|
|
385
|
+
mdContent += agentContent;
|
|
386
|
+
|
|
387
|
+
await fileManager.writeFile(mdPath, mdContent);
|
|
388
|
+
console.log(chalk.green(`✓ Created workflow: ${agentId}.md`));
|
|
164
389
|
}
|
|
390
|
+
}
|
|
165
391
|
|
|
166
|
-
|
|
392
|
+
console.log(chalk.green(`\n✓ Created Windsurf workflows in ${windsurfWorkflowDir}`));
|
|
393
|
+
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async setupTrae(installDir, selectedAgent) {
|
|
398
|
+
const traeRulesDir = path.join(installDir, '.trae', 'rules');
|
|
399
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
400
|
+
|
|
401
|
+
await fileManager.ensureDirectory(traeRulesDir);
|
|
402
|
+
|
|
403
|
+
for (const agentId of agents) {
|
|
404
|
+
// Find the agent file
|
|
405
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
406
|
+
|
|
407
|
+
if (agentPath) {
|
|
167
408
|
const agentContent = await fileManager.readFile(agentPath);
|
|
168
|
-
const mdPath = path.join(
|
|
409
|
+
const mdPath = path.join(traeRulesDir, `${agentId}.md`);
|
|
169
410
|
|
|
170
411
|
// Create MD content (similar to Cursor but without frontmatter)
|
|
171
412
|
let mdContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
|
172
|
-
mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
|
|
173
|
-
agentId
|
|
413
|
+
mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${await this.getAgentTitle(
|
|
414
|
+
agentId,
|
|
415
|
+
installDir,
|
|
174
416
|
)} agent persona.\n\n`;
|
|
175
|
-
mdContent +=
|
|
417
|
+
mdContent += '## Agent Activation\n\n';
|
|
176
418
|
mdContent +=
|
|
177
|
-
|
|
178
|
-
mdContent +=
|
|
419
|
+
'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n';
|
|
420
|
+
mdContent += '```yaml\n';
|
|
179
421
|
// Extract just the YAML content from the agent file
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
mdContent +=
|
|
422
|
+
const yamlContent = extractYamlFromAgent(agentContent);
|
|
423
|
+
if (yamlContent) {
|
|
424
|
+
mdContent += yamlContent;
|
|
183
425
|
} else {
|
|
184
426
|
// If no YAML found, include the whole content minus the header
|
|
185
|
-
mdContent += agentContent.replace(/^#.*$/m,
|
|
427
|
+
mdContent += agentContent.replace(/^#.*$/m, '').trim();
|
|
186
428
|
}
|
|
187
|
-
mdContent +=
|
|
188
|
-
mdContent +=
|
|
189
|
-
|
|
190
|
-
mdContent +=
|
|
191
|
-
mdContent +=
|
|
192
|
-
|
|
193
|
-
|
|
429
|
+
mdContent += '\n```\n\n';
|
|
430
|
+
mdContent += '## File Reference\n\n';
|
|
431
|
+
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
|
432
|
+
mdContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
|
433
|
+
mdContent += '## Usage\n\n';
|
|
434
|
+
mdContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
|
|
435
|
+
agentId,
|
|
436
|
+
installDir,
|
|
437
|
+
)} persona and follow all instructions defined in the YAML configuration above.\n`;
|
|
194
438
|
|
|
195
439
|
await fileManager.writeFile(mdPath, mdContent);
|
|
196
440
|
console.log(chalk.green(`✓ Created rule: ${agentId}.md`));
|
|
197
441
|
}
|
|
198
442
|
}
|
|
443
|
+
}
|
|
199
444
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
445
|
+
async findAgentPath(agentId, installDir) {
|
|
446
|
+
// Try to find the agent file in various locations
|
|
447
|
+
const possiblePaths = [
|
|
448
|
+
path.join(installDir, '.bmad-core', 'agents', `${agentId}.md`),
|
|
449
|
+
path.join(installDir, 'agents', `${agentId}.md`),
|
|
450
|
+
];
|
|
203
451
|
|
|
204
|
-
|
|
452
|
+
// Also check expansion pack directories
|
|
453
|
+
const glob = require('glob');
|
|
454
|
+
const expansionDirectories = glob.sync('.*/agents', { cwd: installDir });
|
|
455
|
+
for (const expDir of expansionDirectories) {
|
|
456
|
+
possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
for (const agentPath of possiblePaths) {
|
|
460
|
+
if (await fileManager.pathExists(agentPath)) {
|
|
461
|
+
return agentPath;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return null;
|
|
205
466
|
}
|
|
206
467
|
|
|
207
468
|
async getAllAgentIds(installDir) {
|
|
208
|
-
|
|
209
|
-
|
|
469
|
+
const glob = require('glob');
|
|
470
|
+
const allAgentIds = [];
|
|
471
|
+
|
|
472
|
+
// Check core agents in .bmad-core or root
|
|
473
|
+
let agentsDir = path.join(installDir, '.bmad-core', 'agents');
|
|
210
474
|
if (!(await fileManager.pathExists(agentsDir))) {
|
|
211
|
-
agentsDir = path.join(installDir,
|
|
475
|
+
agentsDir = path.join(installDir, 'agents');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (await fileManager.pathExists(agentsDir)) {
|
|
479
|
+
const agentFiles = glob.sync('*.md', { cwd: agentsDir });
|
|
480
|
+
allAgentIds.push(...agentFiles.map((file) => path.basename(file, '.md')));
|
|
212
481
|
}
|
|
213
482
|
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
483
|
+
// Also check for expansion pack agents in dot folders
|
|
484
|
+
const expansionDirectories = glob.sync('.*/agents', { cwd: installDir });
|
|
485
|
+
for (const expDir of expansionDirectories) {
|
|
486
|
+
const fullExpDir = path.join(installDir, expDir);
|
|
487
|
+
const expAgentFiles = glob.sync('*.md', { cwd: fullExpDir });
|
|
488
|
+
allAgentIds.push(...expAgentFiles.map((file) => path.basename(file, '.md')));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Remove duplicates
|
|
492
|
+
return [...new Set(allAgentIds)];
|
|
217
493
|
}
|
|
218
494
|
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
495
|
+
async getCoreAgentIds(installDir) {
|
|
496
|
+
const allAgentIds = [];
|
|
497
|
+
|
|
498
|
+
// Check core agents in .bmad-core or root only
|
|
499
|
+
let agentsDir = path.join(installDir, '.bmad-core', 'agents');
|
|
500
|
+
if (!(await fileManager.pathExists(agentsDir))) {
|
|
501
|
+
agentsDir = path.join(installDir, 'bmad-core', 'agents');
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (await fileManager.pathExists(agentsDir)) {
|
|
505
|
+
const glob = require('glob');
|
|
506
|
+
const agentFiles = glob.sync('*.md', { cwd: agentsDir });
|
|
507
|
+
allAgentIds.push(...agentFiles.map((file) => path.basename(file, '.md')));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return [...new Set(allAgentIds)];
|
|
233
511
|
}
|
|
234
512
|
|
|
235
|
-
async
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
513
|
+
async getCoreTaskIds(installDir) {
|
|
514
|
+
const allTaskIds = [];
|
|
515
|
+
|
|
516
|
+
// Check core tasks in .bmad-core or root only
|
|
517
|
+
let tasksDir = path.join(installDir, '.bmad-core', 'tasks');
|
|
518
|
+
if (!(await fileManager.pathExists(tasksDir))) {
|
|
519
|
+
tasksDir = path.join(installDir, 'bmad-core', 'tasks');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (await fileManager.pathExists(tasksDir)) {
|
|
523
|
+
const glob = require('glob');
|
|
524
|
+
const taskFiles = glob.sync('*.md', { cwd: tasksDir });
|
|
525
|
+
allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Check common tasks
|
|
529
|
+
const commonTasksDir = path.join(installDir, 'common', 'tasks');
|
|
530
|
+
if (await fileManager.pathExists(commonTasksDir)) {
|
|
531
|
+
const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir });
|
|
532
|
+
allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return [...new Set(allTaskIds)];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async getAgentTitle(agentId, installDir) {
|
|
539
|
+
// Try to find the agent file in various locations
|
|
540
|
+
const possiblePaths = [
|
|
541
|
+
path.join(installDir, '.bmad-core', 'agents', `${agentId}.md`),
|
|
542
|
+
path.join(installDir, 'agents', `${agentId}.md`),
|
|
543
|
+
];
|
|
239
544
|
|
|
240
|
-
//
|
|
241
|
-
const
|
|
242
|
-
|
|
545
|
+
// Also check expansion pack directories
|
|
546
|
+
const glob = require('glob');
|
|
547
|
+
const expansionDirectories = glob.sync('.*/agents', { cwd: installDir });
|
|
548
|
+
for (const expDir of expansionDirectories) {
|
|
549
|
+
possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
for (const agentPath of possiblePaths) {
|
|
553
|
+
if (await fileManager.pathExists(agentPath)) {
|
|
554
|
+
try {
|
|
555
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
556
|
+
const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
|
|
557
|
+
|
|
558
|
+
if (yamlMatch) {
|
|
559
|
+
const yaml = yamlMatch[1];
|
|
560
|
+
const titleMatch = yaml.match(/title:\s*(.+)/);
|
|
561
|
+
if (titleMatch) {
|
|
562
|
+
return titleMatch[1].trim();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
} catch (error) {
|
|
566
|
+
console.warn(`Failed to read agent title for ${agentId}: ${error.message}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Fallback to formatted agent ID
|
|
572
|
+
return agentId
|
|
573
|
+
.split('-')
|
|
574
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
575
|
+
.join(' ');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async getAllTaskIds(installDir) {
|
|
579
|
+
const glob = require('glob');
|
|
580
|
+
const allTaskIds = [];
|
|
581
|
+
|
|
582
|
+
// Check core tasks in .bmad-core or root
|
|
583
|
+
let tasksDir = path.join(installDir, '.bmad-core', 'tasks');
|
|
584
|
+
if (!(await fileManager.pathExists(tasksDir))) {
|
|
585
|
+
tasksDir = path.join(installDir, 'bmad-core', 'tasks');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (await fileManager.pathExists(tasksDir)) {
|
|
589
|
+
const taskFiles = glob.sync('*.md', { cwd: tasksDir });
|
|
590
|
+
allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Check common tasks
|
|
594
|
+
const commonTasksDir = path.join(installDir, 'common', 'tasks');
|
|
595
|
+
if (await fileManager.pathExists(commonTasksDir)) {
|
|
596
|
+
const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir });
|
|
597
|
+
allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Also check for expansion pack tasks in dot folders
|
|
601
|
+
const expansionDirectories = glob.sync('.*/tasks', { cwd: installDir });
|
|
602
|
+
for (const expDir of expansionDirectories) {
|
|
603
|
+
const fullExpDir = path.join(installDir, expDir);
|
|
604
|
+
const expTaskFiles = glob.sync('*.md', { cwd: fullExpDir });
|
|
605
|
+
allTaskIds.push(...expTaskFiles.map((file) => path.basename(file, '.md')));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Check expansion-packs folder tasks
|
|
609
|
+
const expansionPacksDir = path.join(installDir, 'expansion-packs');
|
|
610
|
+
if (await fileManager.pathExists(expansionPacksDir)) {
|
|
611
|
+
const expPackDirectories = glob.sync('*/tasks', { cwd: expansionPacksDir });
|
|
612
|
+
for (const expDir of expPackDirectories) {
|
|
613
|
+
const fullExpDir = path.join(expansionPacksDir, expDir);
|
|
614
|
+
const expTaskFiles = glob.sync('*.md', { cwd: fullExpDir });
|
|
615
|
+
allTaskIds.push(...expTaskFiles.map((file) => path.basename(file, '.md')));
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Remove duplicates
|
|
620
|
+
return [...new Set(allTaskIds)];
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async findTaskPath(taskId, installDir) {
|
|
624
|
+
// Try to find the task file in various locations
|
|
625
|
+
const possiblePaths = [
|
|
626
|
+
path.join(installDir, '.bmad-core', 'tasks', `${taskId}.md`),
|
|
627
|
+
path.join(installDir, 'bmad-core', 'tasks', `${taskId}.md`),
|
|
628
|
+
path.join(installDir, 'common', 'tasks', `${taskId}.md`),
|
|
629
|
+
];
|
|
243
630
|
|
|
244
|
-
//
|
|
245
|
-
const
|
|
631
|
+
// Also check expansion pack directories
|
|
632
|
+
const glob = require('glob');
|
|
633
|
+
|
|
634
|
+
// Check dot folder expansion packs
|
|
635
|
+
const expansionDirectories = glob.sync('.*/tasks', { cwd: installDir });
|
|
636
|
+
for (const expDir of expansionDirectories) {
|
|
637
|
+
possiblePaths.push(path.join(installDir, expDir, `${taskId}.md`));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Check expansion-packs folder
|
|
641
|
+
const expansionPacksDir = path.join(installDir, 'expansion-packs');
|
|
642
|
+
if (await fileManager.pathExists(expansionPacksDir)) {
|
|
643
|
+
const expPackDirectories = glob.sync('*/tasks', { cwd: expansionPacksDir });
|
|
644
|
+
for (const expDir of expPackDirectories) {
|
|
645
|
+
possiblePaths.push(path.join(expansionPacksDir, expDir, `${taskId}.md`));
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
for (const taskPath of possiblePaths) {
|
|
650
|
+
if (await fileManager.pathExists(taskPath)) {
|
|
651
|
+
return taskPath;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async getCoreSlashPrefix(installDir) {
|
|
659
|
+
try {
|
|
660
|
+
const coreConfigPath = path.join(installDir, '.bmad-core', 'core-config.yaml');
|
|
661
|
+
if (!(await fileManager.pathExists(coreConfigPath))) {
|
|
662
|
+
// Try bmad-core directory
|
|
663
|
+
const altConfigPath = path.join(installDir, 'bmad-core', 'core-config.yaml');
|
|
664
|
+
if (await fileManager.pathExists(altConfigPath)) {
|
|
665
|
+
const configContent = await fileManager.readFile(altConfigPath);
|
|
666
|
+
const config = yaml.load(configContent);
|
|
667
|
+
return config.slashPrefix || 'BMad';
|
|
668
|
+
}
|
|
669
|
+
return 'BMad'; // fallback
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const configContent = await fileManager.readFile(coreConfigPath);
|
|
673
|
+
const config = yaml.load(configContent);
|
|
674
|
+
return config.slashPrefix || 'BMad';
|
|
675
|
+
} catch (error) {
|
|
676
|
+
console.warn(`Failed to read core slashPrefix, using default 'BMad': ${error.message}`);
|
|
677
|
+
return 'BMad';
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async getInstalledExpansionPacks(installDir) {
|
|
682
|
+
const expansionPacks = [];
|
|
683
|
+
|
|
684
|
+
// Check for dot-prefixed expansion packs in install directory
|
|
685
|
+
const glob = require('glob');
|
|
686
|
+
const dotExpansions = glob.sync('.bmad-*', { cwd: installDir });
|
|
687
|
+
|
|
688
|
+
for (const dotExpansion of dotExpansions) {
|
|
689
|
+
if (dotExpansion !== '.bmad-core') {
|
|
690
|
+
const packPath = path.join(installDir, dotExpansion);
|
|
691
|
+
const packName = dotExpansion.slice(1); // remove the dot
|
|
692
|
+
expansionPacks.push({
|
|
693
|
+
name: packName,
|
|
694
|
+
path: packPath,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Check for expansion-packs directory style
|
|
700
|
+
const expansionPacksDir = path.join(installDir, 'expansion-packs');
|
|
701
|
+
if (await fileManager.pathExists(expansionPacksDir)) {
|
|
702
|
+
const packDirectories = glob.sync('*', { cwd: expansionPacksDir });
|
|
703
|
+
|
|
704
|
+
for (const packDir of packDirectories) {
|
|
705
|
+
const packPath = path.join(expansionPacksDir, packDir);
|
|
706
|
+
if (
|
|
707
|
+
(await fileManager.pathExists(packPath)) &&
|
|
708
|
+
(await fileManager.pathExists(path.join(packPath, 'config.yaml')))
|
|
709
|
+
) {
|
|
710
|
+
expansionPacks.push({
|
|
711
|
+
name: packDir,
|
|
712
|
+
path: packPath,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return expansionPacks;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
async getExpansionPackSlashPrefix(packPath) {
|
|
722
|
+
try {
|
|
723
|
+
const configPath = path.join(packPath, 'config.yaml');
|
|
724
|
+
if (await fileManager.pathExists(configPath)) {
|
|
725
|
+
const configContent = await fileManager.readFile(configPath);
|
|
726
|
+
const config = yaml.load(configContent);
|
|
727
|
+
return config.slashPrefix || path.basename(packPath);
|
|
728
|
+
}
|
|
729
|
+
} catch (error) {
|
|
730
|
+
console.warn(`Failed to read expansion pack slashPrefix from ${packPath}: ${error.message}`);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return path.basename(packPath); // fallback to directory name
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
async getExpansionPackAgents(packPath) {
|
|
737
|
+
const agentsDir = path.join(packPath, 'agents');
|
|
738
|
+
if (!(await fileManager.pathExists(agentsDir))) {
|
|
739
|
+
return [];
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
try {
|
|
743
|
+
const glob = require('glob');
|
|
744
|
+
const agentFiles = glob.sync('*.md', { cwd: agentsDir });
|
|
745
|
+
return agentFiles.map((file) => path.basename(file, '.md'));
|
|
746
|
+
} catch (error) {
|
|
747
|
+
console.warn(`Failed to read expansion pack agents from ${packPath}: ${error.message}`);
|
|
748
|
+
return [];
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
async getExpansionPackTasks(packPath) {
|
|
753
|
+
const tasksDir = path.join(packPath, 'tasks');
|
|
754
|
+
if (!(await fileManager.pathExists(tasksDir))) {
|
|
755
|
+
return [];
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
try {
|
|
759
|
+
const glob = require('glob');
|
|
760
|
+
const taskFiles = glob.sync('*.md', { cwd: tasksDir });
|
|
761
|
+
return taskFiles.map((file) => path.basename(file, '.md'));
|
|
762
|
+
} catch (error) {
|
|
763
|
+
console.warn(`Failed to read expansion pack tasks from ${packPath}: ${error.message}`);
|
|
764
|
+
return [];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async setupRoo(installDir, selectedAgent) {
|
|
769
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
770
|
+
|
|
771
|
+
// Check for existing .roomodes file in project root
|
|
772
|
+
const roomodesPath = path.join(installDir, '.roomodes');
|
|
246
773
|
let existingModes = [];
|
|
247
|
-
let existingContent =
|
|
774
|
+
let existingContent = '';
|
|
248
775
|
|
|
249
776
|
if (await fileManager.pathExists(roomodesPath)) {
|
|
250
777
|
existingContent = await fileManager.readFile(roomodesPath);
|
|
@@ -253,76 +780,33 @@ class IdeSetup {
|
|
|
253
780
|
for (const match of modeMatches) {
|
|
254
781
|
existingModes.push(match[1]);
|
|
255
782
|
}
|
|
256
|
-
console.log(
|
|
257
|
-
chalk.yellow(
|
|
258
|
-
`Found existing .roomodes file with ${existingModes.length} modes`
|
|
259
|
-
)
|
|
260
|
-
);
|
|
783
|
+
console.log(chalk.yellow(`Found existing .roomodes file with ${existingModes.length} modes`));
|
|
261
784
|
}
|
|
262
785
|
|
|
263
786
|
// Create new modes content
|
|
264
|
-
let newModesContent =
|
|
265
|
-
|
|
266
|
-
//
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
fileRegex: '\\.(md|txt)$',
|
|
270
|
-
description: 'Documentation and text files'
|
|
271
|
-
},
|
|
272
|
-
'pm': {
|
|
273
|
-
fileRegex: '\\.(md|txt)$',
|
|
274
|
-
description: 'Product documentation'
|
|
275
|
-
},
|
|
276
|
-
'architect': {
|
|
277
|
-
fileRegex: '\\.(md|txt|yml|yaml|json)$',
|
|
278
|
-
description: 'Architecture docs and configs'
|
|
279
|
-
},
|
|
280
|
-
'dev': null, // Full edit access
|
|
281
|
-
'qa': {
|
|
282
|
-
fileRegex: '\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$',
|
|
283
|
-
description: 'Test files and documentation'
|
|
284
|
-
},
|
|
285
|
-
'ux-expert': {
|
|
286
|
-
fileRegex: '\\.(md|css|scss|html|jsx|tsx)$',
|
|
287
|
-
description: 'Design-related files'
|
|
288
|
-
},
|
|
289
|
-
'po': {
|
|
290
|
-
fileRegex: '\\.(md|txt)$',
|
|
291
|
-
description: 'Story and requirement docs'
|
|
292
|
-
},
|
|
293
|
-
'sm': {
|
|
294
|
-
fileRegex: '\\.(md|txt)$',
|
|
295
|
-
description: 'Process and planning docs'
|
|
296
|
-
},
|
|
297
|
-
'bmad-orchestrator': null, // Full edit access
|
|
298
|
-
'bmad-master': null // Full edit access
|
|
299
|
-
};
|
|
787
|
+
let newModesContent = '';
|
|
788
|
+
|
|
789
|
+
// Load dynamic agent permissions from configuration
|
|
790
|
+
const config = await this.loadIdeAgentConfig();
|
|
791
|
+
const agentPermissions = config['roo-permissions'] || {};
|
|
300
792
|
|
|
301
793
|
for (const agentId of agents) {
|
|
302
794
|
// Skip if already exists
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
);
|
|
795
|
+
// Check both with and without bmad- prefix to handle both cases
|
|
796
|
+
const checkSlug = agentId.startsWith('bmad-') ? agentId : `bmad-${agentId}`;
|
|
797
|
+
if (existingModes.includes(checkSlug)) {
|
|
798
|
+
console.log(chalk.dim(`Skipping ${agentId} - already exists in .roomodes`));
|
|
307
799
|
continue;
|
|
308
800
|
}
|
|
309
801
|
|
|
310
802
|
// Read agent file to extract all information
|
|
311
|
-
|
|
312
|
-
installDir,
|
|
313
|
-
".bmad-core",
|
|
314
|
-
"agents",
|
|
315
|
-
`${agentId}.md`
|
|
316
|
-
);
|
|
317
|
-
if (!(await fileManager.pathExists(agentPath))) {
|
|
318
|
-
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
|
319
|
-
}
|
|
803
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
320
804
|
|
|
321
|
-
if (
|
|
805
|
+
if (agentPath) {
|
|
322
806
|
const agentContent = await fileManager.readFile(agentPath);
|
|
323
807
|
|
|
324
808
|
// Extract YAML content
|
|
325
|
-
const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
|
|
809
|
+
const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
|
|
326
810
|
if (yamlMatch) {
|
|
327
811
|
const yaml = yamlMatch[1];
|
|
328
812
|
|
|
@@ -332,26 +816,33 @@ class IdeSetup {
|
|
|
332
816
|
const whenToUseMatch = yaml.match(/whenToUse:\s*"(.+)"/);
|
|
333
817
|
const roleDefinitionMatch = yaml.match(/roleDefinition:\s*"(.+)"/);
|
|
334
818
|
|
|
335
|
-
const title = titleMatch
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
819
|
+
const title = titleMatch
|
|
820
|
+
? titleMatch[1].trim()
|
|
821
|
+
: await this.getAgentTitle(agentId, installDir);
|
|
822
|
+
const icon = iconMatch ? iconMatch[1].trim() : '🤖';
|
|
823
|
+
const whenToUse = whenToUseMatch ? whenToUseMatch[1].trim() : `Use for ${title} tasks`;
|
|
340
824
|
const roleDefinition = roleDefinitionMatch
|
|
341
825
|
? roleDefinitionMatch[1].trim()
|
|
342
826
|
: `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
|
|
343
827
|
|
|
828
|
+
// Add permissions based on agent type
|
|
829
|
+
const permissions = agentPermissions[agentId];
|
|
344
830
|
// Build mode entry with proper formatting (matching exact indentation)
|
|
345
|
-
|
|
831
|
+
// Avoid double "bmad-" prefix for agents that already have it
|
|
832
|
+
const slug = agentId.startsWith('bmad-') ? agentId : `bmad-${agentId}`;
|
|
833
|
+
newModesContent += ` - slug: ${slug}\n`;
|
|
346
834
|
newModesContent += ` name: '${icon} ${title}'\n`;
|
|
835
|
+
if (permissions) {
|
|
836
|
+
newModesContent += ` description: '${permissions.description}'\n`;
|
|
837
|
+
}
|
|
347
838
|
newModesContent += ` roleDefinition: ${roleDefinition}\n`;
|
|
348
839
|
newModesContent += ` whenToUse: ${whenToUse}\n`;
|
|
349
|
-
|
|
840
|
+
// Get relative path from installDir to agent file
|
|
841
|
+
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
|
842
|
+
newModesContent += ` customInstructions: CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
|
|
350
843
|
newModesContent += ` groups:\n`;
|
|
351
844
|
newModesContent += ` - read\n`;
|
|
352
|
-
|
|
353
|
-
// Add permissions based on agent type
|
|
354
|
-
const permissions = agentPermissions[agentId];
|
|
845
|
+
|
|
355
846
|
if (permissions) {
|
|
356
847
|
newModesContent += ` - - edit\n`;
|
|
357
848
|
newModesContent += ` - fileRegex: ${permissions.fileRegex}\n`;
|
|
@@ -360,70 +851,591 @@ class IdeSetup {
|
|
|
360
851
|
newModesContent += ` - edit\n`;
|
|
361
852
|
}
|
|
362
853
|
|
|
363
|
-
console.log(
|
|
364
|
-
chalk.green(`✓ Added mode: bmad-${agentId} (${icon} ${title})`)
|
|
365
|
-
);
|
|
854
|
+
console.log(chalk.green(`✓ Added mode: bmad-${agentId} (${icon} ${title})`));
|
|
366
855
|
}
|
|
367
856
|
}
|
|
368
857
|
}
|
|
369
858
|
|
|
370
859
|
// Build final roomodes content
|
|
371
|
-
let roomodesContent =
|
|
860
|
+
let roomodesContent = '';
|
|
372
861
|
if (existingContent) {
|
|
373
862
|
// If there's existing content, append new modes to it
|
|
374
|
-
roomodesContent = existingContent.trim() +
|
|
863
|
+
roomodesContent = existingContent.trim() + '\n' + newModesContent;
|
|
375
864
|
} else {
|
|
376
865
|
// Create new .roomodes file with proper YAML structure
|
|
377
|
-
roomodesContent =
|
|
866
|
+
roomodesContent = 'customModes:\n' + newModesContent;
|
|
378
867
|
}
|
|
379
868
|
|
|
380
869
|
// Write .roomodes file
|
|
381
870
|
await fileManager.writeFile(roomodesPath, roomodesContent);
|
|
382
|
-
console.log(chalk.green(
|
|
871
|
+
console.log(chalk.green('✓ Created .roomodes file in project root'));
|
|
872
|
+
|
|
873
|
+
console.log(chalk.green(`\n✓ Roo Code setup complete!`));
|
|
874
|
+
console.log(chalk.dim('Custom modes will be available when you open this project in Roo Code'));
|
|
875
|
+
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
async setupKilocode(installDir, selectedAgent) {
|
|
880
|
+
const filePath = path.join(installDir, '.kilocodemodes');
|
|
881
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
882
|
+
|
|
883
|
+
let existingModes = [],
|
|
884
|
+
existingContent = '';
|
|
885
|
+
if (await fileManager.pathExists(filePath)) {
|
|
886
|
+
existingContent = await fileManager.readFile(filePath);
|
|
887
|
+
for (const match of existingContent.matchAll(/- slug: ([\w-]+)/g)) {
|
|
888
|
+
existingModes.push(match[1]);
|
|
889
|
+
}
|
|
890
|
+
console.log(
|
|
891
|
+
chalk.yellow(`Found existing .kilocodemodes file with ${existingModes.length} modes`),
|
|
892
|
+
);
|
|
893
|
+
}
|
|
383
894
|
|
|
384
|
-
|
|
385
|
-
const
|
|
895
|
+
const config = await this.loadIdeAgentConfig();
|
|
896
|
+
const permissions = config['roo-permissions'] || {}; // reuse same roo permissions block (Kilo Code understands same mode schema)
|
|
386
897
|
|
|
387
|
-
|
|
898
|
+
let newContent = '';
|
|
388
899
|
|
|
389
|
-
|
|
900
|
+
for (const agentId of agents) {
|
|
901
|
+
const slug = agentId.startsWith('bmad-') ? agentId : `bmad-${agentId}`;
|
|
902
|
+
if (existingModes.includes(slug)) {
|
|
903
|
+
console.log(chalk.dim(`Skipping ${agentId} - already exists in .kilocodemodes`));
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
908
|
+
if (!agentPath) {
|
|
909
|
+
console.log(chalk.red(`✗ Could not find agent file for ${agentId}`));
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
914
|
+
const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
|
|
915
|
+
if (!yamlMatch) {
|
|
916
|
+
console.log(chalk.red(`✗ Could not extract YAML block for ${agentId}`));
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const yaml = yamlMatch[1];
|
|
921
|
+
|
|
922
|
+
// Robust fallback for title and icon
|
|
923
|
+
const title =
|
|
924
|
+
yaml.match(/title:\s*(.+)/)?.[1]?.trim() || (await this.getAgentTitle(agentId, installDir));
|
|
925
|
+
const icon = yaml.match(/icon:\s*(.+)/)?.[1]?.trim() || '🤖';
|
|
926
|
+
const whenToUse = yaml.match(/whenToUse:\s*"(.+)"/)?.[1]?.trim() || `Use for ${title} tasks`;
|
|
927
|
+
const roleDefinition =
|
|
928
|
+
yaml.match(/roleDefinition:\s*"(.+)"/)?.[1]?.trim() ||
|
|
929
|
+
`You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
|
|
930
|
+
|
|
931
|
+
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
|
932
|
+
const customInstructions = `CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode`;
|
|
933
|
+
|
|
934
|
+
// Add permissions from config if they exist
|
|
935
|
+
const agentPermission = permissions[agentId];
|
|
936
|
+
|
|
937
|
+
// Begin .kilocodemodes block
|
|
938
|
+
newContent += ` - slug: ${slug}\n`;
|
|
939
|
+
newContent += ` name: '${icon} ${title}'\n`;
|
|
940
|
+
if (agentPermission) {
|
|
941
|
+
newContent += ` description: '${agentPermission.description}'\n`;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
newContent += ` roleDefinition: ${roleDefinition}\n`;
|
|
945
|
+
newContent += ` whenToUse: ${whenToUse}\n`;
|
|
946
|
+
newContent += ` customInstructions: ${customInstructions}\n`;
|
|
947
|
+
newContent += ` groups:\n`;
|
|
948
|
+
newContent += ` - read\n`;
|
|
949
|
+
|
|
950
|
+
if (agentPermission) {
|
|
951
|
+
newContent += ` - - edit\n`;
|
|
952
|
+
newContent += ` - fileRegex: ${agentPermission.fileRegex}\n`;
|
|
953
|
+
newContent += ` description: ${agentPermission.description}\n`;
|
|
954
|
+
} else {
|
|
955
|
+
// Fallback to generic edit
|
|
956
|
+
newContent += ` - edit\n`;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
console.log(chalk.green(`✓ Added Kilo mode: ${slug} (${icon} ${title})`));
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
const finalContent = existingContent
|
|
963
|
+
? existingContent.trim() + '\n' + newContent
|
|
964
|
+
: 'customModes:\n' + newContent;
|
|
965
|
+
|
|
966
|
+
await fileManager.writeFile(filePath, finalContent);
|
|
967
|
+
console.log(chalk.green('✓ Created .kilocodemodes file in project root'));
|
|
968
|
+
console.log(chalk.green(`✓ KiloCode setup complete!`));
|
|
969
|
+
console.log(chalk.dim('Custom modes will be available when you open this project in KiloCode'));
|
|
970
|
+
|
|
971
|
+
return true;
|
|
972
|
+
}
|
|
390
973
|
|
|
391
|
-
|
|
974
|
+
async setupCline(installDir, selectedAgent) {
|
|
975
|
+
const clineRulesDir = path.join(installDir, '.clinerules');
|
|
976
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
392
977
|
|
|
393
|
-
|
|
978
|
+
await fileManager.ensureDirectory(clineRulesDir);
|
|
394
979
|
|
|
395
|
-
|
|
980
|
+
// Load dynamic agent ordering from configuration
|
|
981
|
+
const config = await this.loadIdeAgentConfig();
|
|
982
|
+
const agentOrder = config['cline-order'] || {};
|
|
396
983
|
|
|
397
|
-
|
|
984
|
+
for (const agentId of agents) {
|
|
985
|
+
// Find the agent file
|
|
986
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
987
|
+
|
|
988
|
+
if (agentPath) {
|
|
989
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
990
|
+
|
|
991
|
+
// Get numeric prefix for ordering
|
|
992
|
+
const order = agentOrder[agentId] || 99;
|
|
993
|
+
const prefix = order.toString().padStart(2, '0');
|
|
994
|
+
const mdPath = path.join(clineRulesDir, `${prefix}-${agentId}.md`);
|
|
398
995
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
996
|
+
// Create MD content for Cline (focused on project standards and role)
|
|
997
|
+
let mdContent = `# ${await this.getAgentTitle(agentId, installDir)} Agent\n\n`;
|
|
998
|
+
mdContent += `This rule defines the ${await this.getAgentTitle(agentId, installDir)} persona and project standards.\n\n`;
|
|
999
|
+
mdContent += '## Role Definition\n\n';
|
|
1000
|
+
mdContent +=
|
|
1001
|
+
'When the user types `@' +
|
|
1002
|
+
agentId +
|
|
1003
|
+
'`, adopt this persona and follow these guidelines:\n\n';
|
|
1004
|
+
mdContent += '```yaml\n';
|
|
1005
|
+
// Extract just the YAML content from the agent file
|
|
1006
|
+
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1007
|
+
if (yamlContent) {
|
|
1008
|
+
mdContent += yamlContent;
|
|
1009
|
+
} else {
|
|
1010
|
+
// If no YAML found, include the whole content minus the header
|
|
1011
|
+
mdContent += agentContent.replace(/^#.*$/m, '').trim();
|
|
1012
|
+
}
|
|
1013
|
+
mdContent += '\n```\n\n';
|
|
1014
|
+
mdContent += '## Project Standards\n\n';
|
|
1015
|
+
mdContent += `- Always maintain consistency with project documentation in .bmad-core/\n`;
|
|
1016
|
+
mdContent += `- Follow the agent's specific guidelines and constraints\n`;
|
|
1017
|
+
mdContent += `- Update relevant project files when making changes\n`;
|
|
1018
|
+
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
|
1019
|
+
mdContent += `- Reference the complete agent definition in [${relativePath}](${relativePath})\n\n`;
|
|
1020
|
+
mdContent += '## Usage\n\n';
|
|
1021
|
+
mdContent += `Type \`@${agentId}\` to activate this ${await this.getAgentTitle(agentId, installDir)} persona.\n`;
|
|
1022
|
+
|
|
1023
|
+
await fileManager.writeFile(mdPath, mdContent);
|
|
1024
|
+
console.log(chalk.green(`✓ Created rule: ${prefix}-${agentId}.md`));
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
403
1027
|
|
|
404
|
-
|
|
1028
|
+
console.log(chalk.green(`\n✓ Created Cline rules in ${clineRulesDir}`));
|
|
1029
|
+
|
|
1030
|
+
return true;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
async setupGeminiCli(installDir) {
|
|
1034
|
+
const geminiDir = path.join(installDir, '.gemini');
|
|
1035
|
+
const bmadMethodDir = path.join(geminiDir, 'bmad-method');
|
|
1036
|
+
await fileManager.ensureDirectory(bmadMethodDir);
|
|
1037
|
+
|
|
1038
|
+
// Update logic for existing settings.json
|
|
1039
|
+
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
1040
|
+
if (await fileManager.pathExists(settingsPath)) {
|
|
1041
|
+
try {
|
|
1042
|
+
const settingsContent = await fileManager.readFile(settingsPath);
|
|
1043
|
+
const settings = JSON.parse(settingsContent);
|
|
1044
|
+
let updated = false;
|
|
1045
|
+
|
|
1046
|
+
// Handle contextFileName property
|
|
1047
|
+
if (settings.contextFileName && Array.isArray(settings.contextFileName)) {
|
|
1048
|
+
const originalLength = settings.contextFileName.length;
|
|
1049
|
+
settings.contextFileName = settings.contextFileName.filter(
|
|
1050
|
+
(fileName) => !fileName.startsWith('agents/'),
|
|
1051
|
+
);
|
|
1052
|
+
if (settings.contextFileName.length !== originalLength) {
|
|
1053
|
+
updated = true;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
if (updated) {
|
|
1058
|
+
await fileManager.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
1059
|
+
console.log(
|
|
1060
|
+
chalk.green('✓ Updated .gemini/settings.json - removed agent file references'),
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
console.warn(chalk.yellow('Could not update .gemini/settings.json'), error);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Remove old agents directory
|
|
1069
|
+
const agentsDir = path.join(geminiDir, 'agents');
|
|
1070
|
+
if (await fileManager.pathExists(agentsDir)) {
|
|
1071
|
+
await fileManager.removeDirectory(agentsDir);
|
|
1072
|
+
console.log(chalk.green('✓ Removed old .gemini/agents directory'));
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// Get all available agents
|
|
1076
|
+
const agents = await this.getAllAgentIds(installDir);
|
|
1077
|
+
let concatenatedContent = '';
|
|
1078
|
+
|
|
1079
|
+
for (const agentId of agents) {
|
|
1080
|
+
// Find the source agent file
|
|
1081
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
1082
|
+
|
|
1083
|
+
if (agentPath) {
|
|
1084
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
1085
|
+
|
|
1086
|
+
// Create properly formatted agent rule content (similar to trae)
|
|
1087
|
+
let agentRuleContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
|
1088
|
+
agentRuleContent += `This rule is triggered when the user types \`*${agentId}\` and activates the ${await this.getAgentTitle(
|
|
1089
|
+
agentId,
|
|
1090
|
+
installDir,
|
|
1091
|
+
)} agent persona.\n\n`;
|
|
1092
|
+
agentRuleContent += '## Agent Activation\n\n';
|
|
1093
|
+
agentRuleContent +=
|
|
1094
|
+
'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n';
|
|
1095
|
+
agentRuleContent += '```yaml\n';
|
|
1096
|
+
// Extract just the YAML content from the agent file
|
|
1097
|
+
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1098
|
+
if (yamlContent) {
|
|
1099
|
+
agentRuleContent += yamlContent;
|
|
1100
|
+
} else {
|
|
1101
|
+
// If no YAML found, include the whole content minus the header
|
|
1102
|
+
agentRuleContent += agentContent.replace(/^#.*$/m, '').trim();
|
|
1103
|
+
}
|
|
1104
|
+
agentRuleContent += '\n```\n\n';
|
|
1105
|
+
agentRuleContent += '## File Reference\n\n';
|
|
1106
|
+
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
|
1107
|
+
agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
|
1108
|
+
agentRuleContent += '## Usage\n\n';
|
|
1109
|
+
agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
|
|
1110
|
+
agentId,
|
|
1111
|
+
installDir,
|
|
1112
|
+
)} persona and follow all instructions defined in the YAML configuration above.\n`;
|
|
1113
|
+
|
|
1114
|
+
// Add to concatenated content with separator
|
|
1115
|
+
concatenatedContent += agentRuleContent + '\n\n---\n\n';
|
|
1116
|
+
console.log(chalk.green(`✓ Added context for @${agentId}`));
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Write the concatenated content to GEMINI.md
|
|
1121
|
+
const geminiMdPath = path.join(bmadMethodDir, 'GEMINI.md');
|
|
1122
|
+
await fileManager.writeFile(geminiMdPath, concatenatedContent);
|
|
1123
|
+
console.log(chalk.green(`\n✓ Created GEMINI.md in ${bmadMethodDir}`));
|
|
1124
|
+
|
|
1125
|
+
return true;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
async setupQwenCode(installDir, selectedAgent) {
|
|
1129
|
+
const qwenDir = path.join(installDir, '.qwen');
|
|
1130
|
+
const bmadMethodDir = path.join(qwenDir, 'bmad-method');
|
|
1131
|
+
await fileManager.ensureDirectory(bmadMethodDir);
|
|
1132
|
+
|
|
1133
|
+
// Update logic for existing settings.json
|
|
1134
|
+
const settingsPath = path.join(qwenDir, 'settings.json');
|
|
1135
|
+
if (await fileManager.pathExists(settingsPath)) {
|
|
1136
|
+
try {
|
|
1137
|
+
const settingsContent = await fileManager.readFile(settingsPath);
|
|
1138
|
+
const settings = JSON.parse(settingsContent);
|
|
1139
|
+
let updated = false;
|
|
1140
|
+
|
|
1141
|
+
// Handle contextFileName property
|
|
1142
|
+
if (settings.contextFileName && Array.isArray(settings.contextFileName)) {
|
|
1143
|
+
const originalLength = settings.contextFileName.length;
|
|
1144
|
+
settings.contextFileName = settings.contextFileName.filter(
|
|
1145
|
+
(fileName) => !fileName.startsWith('agents/'),
|
|
1146
|
+
);
|
|
1147
|
+
if (settings.contextFileName.length !== originalLength) {
|
|
1148
|
+
updated = true;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (updated) {
|
|
1153
|
+
await fileManager.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
1154
|
+
console.log(chalk.green('✓ Updated .qwen/settings.json - removed agent file references'));
|
|
1155
|
+
}
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
console.warn(chalk.yellow('Could not update .qwen/settings.json'), error);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// Remove old agents directory
|
|
1162
|
+
const agentsDir = path.join(qwenDir, 'agents');
|
|
1163
|
+
if (await fileManager.pathExists(agentsDir)) {
|
|
1164
|
+
await fileManager.removeDirectory(agentsDir);
|
|
1165
|
+
console.log(chalk.green('✓ Removed old .qwen/agents directory'));
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Get all available agents
|
|
1169
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
1170
|
+
let concatenatedContent = '';
|
|
1171
|
+
|
|
1172
|
+
for (const agentId of agents) {
|
|
1173
|
+
// Find the source agent file
|
|
1174
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
1175
|
+
|
|
1176
|
+
if (agentPath) {
|
|
1177
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
1178
|
+
|
|
1179
|
+
// Create properly formatted agent rule content (similar to gemini)
|
|
1180
|
+
let agentRuleContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
|
1181
|
+
agentRuleContent += `This rule is triggered when the user types \`*${agentId}\` and activates the ${await this.getAgentTitle(
|
|
1182
|
+
agentId,
|
|
1183
|
+
installDir,
|
|
1184
|
+
)} agent persona.\n\n`;
|
|
1185
|
+
agentRuleContent += '## Agent Activation\n\n';
|
|
1186
|
+
agentRuleContent +=
|
|
1187
|
+
'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n';
|
|
1188
|
+
agentRuleContent += '```yaml\n';
|
|
1189
|
+
// Extract just the YAML content from the agent file
|
|
1190
|
+
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1191
|
+
if (yamlContent) {
|
|
1192
|
+
agentRuleContent += yamlContent;
|
|
1193
|
+
} else {
|
|
1194
|
+
// If no YAML found, include the whole content minus the header
|
|
1195
|
+
agentRuleContent += agentContent.replace(/^#.*$/m, '').trim();
|
|
1196
|
+
}
|
|
1197
|
+
agentRuleContent += '\n```\n\n';
|
|
1198
|
+
agentRuleContent += '## File Reference\n\n';
|
|
1199
|
+
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
|
1200
|
+
agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
|
1201
|
+
agentRuleContent += '## Usage\n\n';
|
|
1202
|
+
agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
|
|
1203
|
+
agentId,
|
|
1204
|
+
installDir,
|
|
1205
|
+
)} persona and follow all instructions defined in the YAML configuration above.\n`;
|
|
1206
|
+
|
|
1207
|
+
// Add to concatenated content with separator
|
|
1208
|
+
concatenatedContent += agentRuleContent + '\n\n---\n\n';
|
|
1209
|
+
console.log(chalk.green(`✓ Added context for *${agentId}`));
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// Write the concatenated content to QWEN.md
|
|
1214
|
+
const qwenMdPath = path.join(bmadMethodDir, 'QWEN.md');
|
|
1215
|
+
await fileManager.writeFile(qwenMdPath, concatenatedContent);
|
|
1216
|
+
console.log(chalk.green(`\n✓ Created QWEN.md in ${bmadMethodDir}`));
|
|
1217
|
+
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
async setupGitHubCopilot(
|
|
1222
|
+
installDir,
|
|
1223
|
+
selectedAgent,
|
|
1224
|
+
spinner = null,
|
|
1225
|
+
preConfiguredSettings = null,
|
|
1226
|
+
) {
|
|
1227
|
+
// Configure VS Code workspace settings first to avoid UI conflicts with loading spinners
|
|
1228
|
+
await this.configureVsCodeSettings(installDir, spinner, preConfiguredSettings);
|
|
1229
|
+
|
|
1230
|
+
const chatmodesDir = path.join(installDir, '.github', 'chatmodes');
|
|
1231
|
+
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
|
1232
|
+
|
|
1233
|
+
await fileManager.ensureDirectory(chatmodesDir);
|
|
1234
|
+
|
|
1235
|
+
for (const agentId of agents) {
|
|
1236
|
+
// Find the agent file
|
|
1237
|
+
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
1238
|
+
const chatmodePath = path.join(chatmodesDir, `${agentId}.chatmode.md`);
|
|
1239
|
+
|
|
1240
|
+
if (agentPath) {
|
|
1241
|
+
// Create chat mode file with agent content
|
|
1242
|
+
const agentContent = await fileManager.readFile(agentPath);
|
|
1243
|
+
const agentTitle = await this.getAgentTitle(agentId, installDir);
|
|
1244
|
+
|
|
1245
|
+
// Extract whenToUse for the description
|
|
1246
|
+
const yamlMatch = agentContent.match(/```ya?ml\r?\n([\s\S]*?)```/);
|
|
1247
|
+
let description = `Activates the ${agentTitle} agent persona.`;
|
|
1248
|
+
if (yamlMatch) {
|
|
1249
|
+
const whenToUseMatch = yamlMatch[1].match(/whenToUse:\s*"(.*?)"/);
|
|
1250
|
+
if (whenToUseMatch && whenToUseMatch[1]) {
|
|
1251
|
+
description = whenToUseMatch[1];
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
let chatmodeContent = `---
|
|
1256
|
+
description: "${description.replaceAll('"', String.raw`\"`)}"
|
|
1257
|
+
tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems', 'usages', 'editFiles', 'runCommands', 'runTasks', 'runTests', 'search', 'searchResults', 'terminalLastCommand', 'terminalSelection', 'testFailure']
|
|
1258
|
+
---
|
|
405
1259
|
|
|
406
|
-
Each agent has specific file access permissions:
|
|
407
|
-
- **Analysts, PM, PO, SM**: Limited to documentation files (.md, .txt)
|
|
408
|
-
- **Architect**: Architecture docs and configs (.md, .txt, .yml, .yaml, .json)
|
|
409
|
-
- **QA**: Test files and documentation
|
|
410
|
-
- **UX Expert**: Design-related files (.md, .css, .scss, .html, .jsx, .tsx)
|
|
411
|
-
- **Developer, Orchestrator, Master**: Full edit access to all files
|
|
412
1260
|
`;
|
|
1261
|
+
chatmodeContent += agentContent;
|
|
413
1262
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
1263
|
+
await fileManager.writeFile(chatmodePath, chatmodeContent);
|
|
1264
|
+
console.log(chalk.green(`✓ Created chat mode: ${agentId}.chatmode.md`));
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
417
1267
|
|
|
418
|
-
console.log(chalk.green(`\n✓
|
|
419
|
-
console.log(
|
|
420
|
-
chalk.dim(
|
|
421
|
-
"Custom modes will be available when you open this project in Roo Code"
|
|
422
|
-
)
|
|
423
|
-
);
|
|
1268
|
+
console.log(chalk.green(`\n✓ Github Copilot setup complete!`));
|
|
1269
|
+
console.log(chalk.dim(`You can now find the BMad agents in the Chat view's mode selector.`));
|
|
424
1270
|
|
|
425
1271
|
return true;
|
|
426
1272
|
}
|
|
1273
|
+
|
|
1274
|
+
async configureVsCodeSettings(installDir, spinner, preConfiguredSettings = null) {
|
|
1275
|
+
const vscodeDir = path.join(installDir, '.vscode');
|
|
1276
|
+
const settingsPath = path.join(vscodeDir, 'settings.json');
|
|
1277
|
+
|
|
1278
|
+
await fileManager.ensureDirectory(vscodeDir);
|
|
1279
|
+
|
|
1280
|
+
// Read existing settings if they exist
|
|
1281
|
+
let existingSettings = {};
|
|
1282
|
+
if (await fileManager.pathExists(settingsPath)) {
|
|
1283
|
+
try {
|
|
1284
|
+
const existingContent = await fileManager.readFile(settingsPath);
|
|
1285
|
+
existingSettings = JSON.parse(existingContent);
|
|
1286
|
+
console.log(chalk.yellow('Found existing .vscode/settings.json. Merging BMad settings...'));
|
|
1287
|
+
} catch {
|
|
1288
|
+
console.warn(chalk.yellow('Could not parse existing settings.json. Creating new one.'));
|
|
1289
|
+
existingSettings = {};
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// Use pre-configured settings if provided, otherwise prompt
|
|
1294
|
+
let configChoice;
|
|
1295
|
+
if (preConfiguredSettings && preConfiguredSettings.configChoice) {
|
|
1296
|
+
configChoice = preConfiguredSettings.configChoice;
|
|
1297
|
+
console.log(chalk.dim(`Using pre-configured GitHub Copilot settings: ${configChoice}`));
|
|
1298
|
+
} else {
|
|
1299
|
+
// Clear any previous output and add spacing to avoid conflicts with loaders
|
|
1300
|
+
console.log('\n'.repeat(2));
|
|
1301
|
+
console.log(chalk.blue('🔧 Github Copilot Agent Settings Configuration'));
|
|
1302
|
+
console.log(
|
|
1303
|
+
chalk.dim('BMad works best with specific VS Code settings for optimal agent experience.'),
|
|
1304
|
+
);
|
|
1305
|
+
console.log(''); // Add extra spacing
|
|
1306
|
+
|
|
1307
|
+
const response = await inquirer.prompt([
|
|
1308
|
+
{
|
|
1309
|
+
type: 'list',
|
|
1310
|
+
name: 'configChoice',
|
|
1311
|
+
message: chalk.yellow('How would you like to configure GitHub Copilot settings?'),
|
|
1312
|
+
choices: [
|
|
1313
|
+
{
|
|
1314
|
+
name: 'Use recommended defaults (fastest setup)',
|
|
1315
|
+
value: 'defaults',
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
name: 'Configure each setting manually (customize to your preferences)',
|
|
1319
|
+
value: 'manual',
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
name: "Skip settings configuration (I'll configure manually later)",
|
|
1323
|
+
value: 'skip',
|
|
1324
|
+
},
|
|
1325
|
+
],
|
|
1326
|
+
default: 'defaults',
|
|
1327
|
+
},
|
|
1328
|
+
]);
|
|
1329
|
+
configChoice = response.configChoice;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
let bmadSettings = {};
|
|
1333
|
+
|
|
1334
|
+
if (configChoice === 'skip') {
|
|
1335
|
+
console.log(chalk.yellow('⚠️ Skipping VS Code settings configuration.'));
|
|
1336
|
+
console.log(chalk.dim('You can manually configure these settings in .vscode/settings.json:'));
|
|
1337
|
+
console.log(chalk.dim(' • chat.agent.enabled: true'));
|
|
1338
|
+
console.log(chalk.dim(' • chat.agent.maxRequests: 15'));
|
|
1339
|
+
console.log(chalk.dim(' • github.copilot.chat.agent.runTasks: true'));
|
|
1340
|
+
console.log(chalk.dim(' • chat.mcp.discovery.enabled: true'));
|
|
1341
|
+
console.log(chalk.dim(' • github.copilot.chat.agent.autoFix: true'));
|
|
1342
|
+
console.log(chalk.dim(' • chat.tools.autoApprove: false'));
|
|
1343
|
+
return true;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
if (configChoice === 'defaults') {
|
|
1347
|
+
// Use recommended defaults
|
|
1348
|
+
bmadSettings = {
|
|
1349
|
+
'chat.agent.enabled': true,
|
|
1350
|
+
'chat.agent.maxRequests': 15,
|
|
1351
|
+
'github.copilot.chat.agent.runTasks': true,
|
|
1352
|
+
'chat.mcp.discovery.enabled': true,
|
|
1353
|
+
'github.copilot.chat.agent.autoFix': true,
|
|
1354
|
+
'chat.tools.autoApprove': false,
|
|
1355
|
+
};
|
|
1356
|
+
console.log(chalk.green('✓ Using recommended BMad defaults for Github Copilot settings'));
|
|
1357
|
+
} else {
|
|
1358
|
+
// Manual configuration
|
|
1359
|
+
console.log(chalk.blue("\n📋 Let's configure each setting for your preferences:"));
|
|
1360
|
+
|
|
1361
|
+
// Pause spinner during manual configuration prompts
|
|
1362
|
+
let spinnerWasActive = false;
|
|
1363
|
+
if (spinner && spinner.isSpinning) {
|
|
1364
|
+
spinner.stop();
|
|
1365
|
+
spinnerWasActive = true;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const manualSettings = await inquirer.prompt([
|
|
1369
|
+
{
|
|
1370
|
+
type: 'input',
|
|
1371
|
+
name: 'maxRequests',
|
|
1372
|
+
message: 'Maximum requests per agent session (recommended: 15)?',
|
|
1373
|
+
default: '15',
|
|
1374
|
+
validate: (input) => {
|
|
1375
|
+
const number_ = Number.parseInt(input);
|
|
1376
|
+
if (isNaN(number_) || number_ < 1 || number_ > 50) {
|
|
1377
|
+
return 'Please enter a number between 1 and 50';
|
|
1378
|
+
}
|
|
1379
|
+
return true;
|
|
1380
|
+
},
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
type: 'confirm',
|
|
1384
|
+
name: 'runTasks',
|
|
1385
|
+
message: 'Allow agents to run workspace tasks (package.json scripts, etc.)?',
|
|
1386
|
+
default: true,
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
type: 'confirm',
|
|
1390
|
+
name: 'mcpDiscovery',
|
|
1391
|
+
message: 'Enable MCP (Model Context Protocol) server discovery?',
|
|
1392
|
+
default: true,
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
type: 'confirm',
|
|
1396
|
+
name: 'autoFix',
|
|
1397
|
+
message: 'Enable automatic error detection and fixing in generated code?',
|
|
1398
|
+
default: true,
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
type: 'confirm',
|
|
1402
|
+
name: 'autoApprove',
|
|
1403
|
+
message: 'Auto-approve ALL tools without confirmation? (⚠️ EXPERIMENTAL - less secure)',
|
|
1404
|
+
default: false,
|
|
1405
|
+
},
|
|
1406
|
+
]);
|
|
1407
|
+
|
|
1408
|
+
// Restart spinner if it was active before prompts
|
|
1409
|
+
if (spinner && spinnerWasActive) {
|
|
1410
|
+
spinner.start();
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
bmadSettings = {
|
|
1414
|
+
'chat.agent.enabled': true, // Always enabled - required for BMad agents
|
|
1415
|
+
'chat.agent.maxRequests': Number.parseInt(manualSettings.maxRequests),
|
|
1416
|
+
'github.copilot.chat.agent.runTasks': manualSettings.runTasks,
|
|
1417
|
+
'chat.mcp.discovery.enabled': manualSettings.mcpDiscovery,
|
|
1418
|
+
'github.copilot.chat.agent.autoFix': manualSettings.autoFix,
|
|
1419
|
+
'chat.tools.autoApprove': manualSettings.autoApprove,
|
|
1420
|
+
};
|
|
1421
|
+
|
|
1422
|
+
console.log(chalk.green('✓ Custom settings configured'));
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// Merge settings (existing settings take precedence to avoid overriding user preferences)
|
|
1426
|
+
const mergedSettings = { ...bmadSettings, ...existingSettings };
|
|
1427
|
+
|
|
1428
|
+
// Write the updated settings
|
|
1429
|
+
await fileManager.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2));
|
|
1430
|
+
|
|
1431
|
+
console.log(chalk.green('✓ VS Code workspace settings configured successfully'));
|
|
1432
|
+
console.log(chalk.dim(' Settings written to .vscode/settings.json:'));
|
|
1433
|
+
for (const [key, value] of Object.entries(bmadSettings)) {
|
|
1434
|
+
console.log(chalk.dim(` • ${key}: ${value}`));
|
|
1435
|
+
}
|
|
1436
|
+
console.log(chalk.dim(''));
|
|
1437
|
+
console.log(chalk.dim('You can modify these settings anytime in .vscode/settings.json'));
|
|
1438
|
+
}
|
|
427
1439
|
}
|
|
428
1440
|
|
|
429
1441
|
module.exports = new IdeSetup();
|