bmad-method 4.27.6 → 5.0.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/.bmad-core/agent-teams/team-all.yml +16 -0
- package/.bmad-core/agent-teams/team-fullstack.yml +26 -0
- package/.bmad-core/agent-teams/team-no-ui.yml +15 -0
- package/{bmad-core → .bmad-core}/agents/analyst.md +23 -29
- package/.bmad-core/agents/architect.md +66 -0
- package/.bmad-core/agents/bmad-master.md +104 -0
- package/.bmad-core/agents/bmad-orchestrator.md +81 -0
- package/.bmad-core/agents/dev.md +70 -0
- package/{bmad-core → .bmad-core}/agents/pm.md +24 -24
- package/{bmad-core → .bmad-core}/agents/po.md +24 -27
- package/.bmad-core/agents/qa.md +52 -0
- package/.bmad-core/agents/sm.md +55 -0
- package/.bmad-core/agents/ux-expert.md +66 -0
- package/{bmad-core → .bmad-core}/checklists/change-checklist.md +2 -2
- package/{bmad-core → .bmad-core}/checklists/story-draft-checklist.md +1 -1
- package/.bmad-core/data/bmad-kb.md +47 -0
- package/.bmad-core/schemas/agent-team-schema.yml +153 -0
- package/.bmad-core/tasks/advanced-elicitation.md +92 -0
- package/.bmad-core/tasks/brainstorming-techniques.md +238 -0
- package/.bmad-core/tasks/core-dump.md +74 -0
- package/{expansion-packs/bmad-creator-tools → .bmad-core}/tasks/create-agent.md +11 -9
- package/.bmad-core/tasks/create-doc.md +74 -0
- package/.bmad-core/tasks/create-expansion-pack.md +425 -0
- package/.bmad-core/tasks/create-next-story.md +206 -0
- package/.bmad-core/tasks/create-team.md +229 -0
- package/{bmad-core → .bmad-core}/tasks/doc-migration-task.md +9 -9
- package/{common → .bmad-core}/tasks/execute-checklist.md +6 -2
- package/.bmad-core/tasks/generate-ai-frontend-prompt.md +58 -0
- package/{bmad-core → .bmad-core}/tasks/index-docs.md +7 -3
- package/{bmad-core → .bmad-core}/tasks/shard-doc.md +7 -25
- package/.bmad-core/templates/agent-tmpl.md +58 -0
- package/.bmad-core/templates/architecture-tmpl.md +771 -0
- package/.bmad-core/templates/brownfield-architecture-tmpl.md +542 -0
- package/.bmad-core/templates/brownfield-prd-tmpl.md +240 -0
- package/.bmad-core/templates/competitor-analysis-tmpl.md +289 -0
- package/.bmad-core/templates/expansion-pack-plan-tmpl.md +91 -0
- package/.bmad-core/templates/front-end-architecture-tmpl.md +173 -0
- package/.bmad-core/templates/front-end-spec-tmpl.md +411 -0
- package/.bmad-core/templates/fullstack-architecture-tmpl.md +1016 -0
- package/.bmad-core/templates/market-research-tmpl.md +261 -0
- package/.bmad-core/templates/prd-tmpl.md +200 -0
- package/.bmad-core/templates/project-brief-tmpl.md +228 -0
- package/.bmad-core/templates/simple-project-prd-tmpl.md +461 -0
- package/.bmad-core/templates/story-tmpl.md +61 -0
- package/.bmad-core/templates/web-agent-startup-instructions-template.md +39 -0
- package/.bmad-core/utils/agent-switcher.ide.md +112 -0
- package/.bmad-core/utils/template-format.md +26 -0
- package/.bmad-core/utils/workflow-management.md +224 -0
- package/.bmad-core/web-bundles/agents/analyst.txt +1684 -0
- package/.bmad-core/web-bundles/agents/architect.txt +3584 -0
- package/.bmad-core/web-bundles/agents/bmad-master.txt +9491 -0
- package/.bmad-core/web-bundles/agents/bmad-orchestrator.txt +1466 -0
- package/{dist → .bmad-core/web-bundles}/agents/dev.txt +71 -179
- package/{dist → .bmad-core/web-bundles}/agents/pm.txt +1058 -624
- package/{dist → .bmad-core/web-bundles}/agents/po.txt +138 -337
- package/.bmad-core/web-bundles/agents/qa.txt +129 -0
- package/.bmad-core/web-bundles/agents/sm.txt +658 -0
- package/.bmad-core/web-bundles/agents/ux-expert.txt +1099 -0
- package/.bmad-core/web-bundles/teams/team-all.txt +10757 -0
- package/.bmad-core/web-bundles/teams/team-fullstack.txt +10109 -0
- package/.bmad-core/web-bundles/teams/team-no-ui.txt +8950 -0
- package/.bmad-core/workflows/brownfield-fullstack.yml +116 -0
- package/.bmad-core/workflows/brownfield-service.yml +117 -0
- package/.bmad-core/workflows/brownfield-ui.yml +127 -0
- package/.bmad-core/workflows/greenfield-fullstack.yml +177 -0
- package/.bmad-core/workflows/greenfield-service.yml +143 -0
- package/.bmad-core/workflows/greenfield-ui.yml +172 -0
- package/.claude/commands/analyst.md +63 -0
- package/.claude/commands/architect.md +70 -0
- package/.claude/commands/bmad-master.md +108 -0
- package/.claude/commands/bmad-orchestrator.md +85 -0
- package/.claude/commands/dev.md +74 -0
- package/.claude/commands/pm.md +63 -0
- package/.claude/commands/po.md +64 -0
- package/.claude/commands/qa.md +56 -0
- package/.claude/commands/sm.md +59 -0
- package/.claude/commands/ux-expert.md +70 -0
- package/.cursor/rules/analyst.mdc +77 -0
- package/.cursor/rules/architect.mdc +84 -0
- package/.cursor/rules/bmad-master.mdc +122 -0
- package/.cursor/rules/bmad-orchestrator.mdc +99 -0
- package/.cursor/rules/dev.mdc +88 -0
- package/.cursor/rules/pm.mdc +77 -0
- package/.cursor/rules/po.mdc +78 -0
- package/.cursor/rules/qa.mdc +70 -0
- package/.cursor/rules/sm.mdc +73 -0
- package/.cursor/rules/ux-expert.mdc +84 -0
- package/.roo/.roomodes +95 -0
- package/.roo/README.md +38 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +75 -49
- package/.windsurf/rules/analyst.md +71 -0
- package/.windsurf/rules/architect.md +78 -0
- package/.windsurf/rules/bmad-master.md +116 -0
- package/.windsurf/rules/bmad-orchestrator.md +93 -0
- package/.windsurf/rules/dev.md +82 -0
- package/.windsurf/rules/pm.md +71 -0
- package/.windsurf/rules/po.md +72 -0
- package/.windsurf/rules/qa.md +64 -0
- package/.windsurf/rules/sm.md +67 -0
- package/.windsurf/rules/ux-expert.md +78 -0
- package/CHANGELOG.md +16 -459
- package/CONTRIBUTING.md +5 -168
- package/LICENSE +1 -1
- package/README.md +230 -77
- package/docs/bmad-workflow-guide.md +15 -19
- package/docs/claude-code-guide.md +119 -0
- package/docs/cursor-guide.md +127 -0
- package/docs/roo-code-guide.md +140 -0
- package/docs/sample-output/simple-fullstack-greenfield/prd.md +42 -0
- package/docs/versioning-and-releases.md +16 -8
- package/docs/versions.md +5 -4
- package/docs/windsurf-guide.md +127 -0
- package/expansion-packs/README.md +112 -2
- package/expansion-packs/{bmad-infrastructure-devops → infrastructure-devops}/README.md +9 -9
- package/expansion-packs/{bmad-infrastructure-devops → infrastructure-devops}/agents/infra-devops-platform.md +15 -14
- package/expansion-packs/{bmad-infrastructure-devops → infrastructure-devops}/checklists/infrastructure-checklist.md +1 -1
- package/expansion-packs/infrastructure-devops/manifest.yml +38 -0
- package/expansion-packs/{bmad-infrastructure-devops → infrastructure-devops}/tasks/review-infrastructure.md +4 -4
- package/expansion-packs/{bmad-infrastructure-devops → infrastructure-devops}/tasks/validate-infrastructure.md +4 -4
- package/expansion-packs/infrastructure-devops/templates/infrastructure-architecture-tmpl.md +415 -0
- package/expansion-packs/infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
- package/package.json +11 -19
- package/tools/bmad-npx-wrapper.js +1 -1
- package/tools/builders/web-builder.js +28 -563
- package/tools/cli.js +22 -55
- package/tools/installer/README.md +53 -3
- package/tools/installer/bin/bmad.js +56 -294
- package/tools/installer/config/install.config.yml +139 -0
- package/tools/installer/lib/config-loader.js +34 -198
- package/tools/installer/lib/file-manager.js +7 -200
- package/tools/installer/lib/ide-setup.js +189 -545
- package/tools/installer/lib/installer.js +61 -1171
- package/tools/installer/package-lock.json +3 -3
- package/tools/installer/package.json +4 -4
- package/tools/installer/templates/claude-commands.md +7 -0
- package/tools/installer/templates/cursor-rules.md +22 -0
- package/tools/installer/templates/windsurf-rules.md +22 -0
- package/tools/lib/dependency-resolver.js +22 -22
- package/tools/upgraders/v3-to-v4-upgrader.js +43 -35
- package/tools/version-bump.js +1 -1
- package/tools/yaml-format.js +2 -2
- package/.github/FUNDING.yaml +0 -15
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -22
- package/.prettierignore +0 -21
- package/.prettierrc +0 -23
- package/bmad-core/agent-teams/team-all.yaml +0 -14
- package/bmad-core/agent-teams/team-fullstack.yaml +0 -18
- package/bmad-core/agent-teams/team-ide-minimal.yaml +0 -10
- package/bmad-core/agent-teams/team-no-ui.yaml +0 -13
- package/bmad-core/agents/architect.md +0 -62
- package/bmad-core/agents/bmad-master.md +0 -88
- package/bmad-core/agents/bmad-orchestrator.md +0 -135
- package/bmad-core/agents/dev.md +0 -56
- package/bmad-core/agents/qa.md +0 -54
- package/bmad-core/agents/sm.md +0 -45
- package/bmad-core/agents/ux-expert.md +0 -53
- package/bmad-core/core-config.yaml +0 -25
- package/bmad-core/data/bmad-kb.md +0 -803
- package/bmad-core/data/brainstorming-techniques.md +0 -36
- package/bmad-core/data/elicitation-methods.md +0 -134
- package/bmad-core/tasks/advanced-elicitation.md +0 -117
- package/bmad-core/tasks/create-brownfield-story.md +0 -355
- package/bmad-core/tasks/create-next-story.md +0 -113
- package/bmad-core/tasks/create-workflow-plan.md +0 -289
- package/bmad-core/tasks/document-project.md +0 -317
- package/bmad-core/tasks/facilitate-brainstorming-session.md +0 -136
- package/bmad-core/tasks/generate-ai-frontend-prompt.md +0 -51
- package/bmad-core/tasks/kb-mode-interaction.md +0 -70
- package/bmad-core/tasks/review-story.md +0 -145
- package/bmad-core/tasks/update-workflow-plan.md +0 -248
- package/bmad-core/tasks/validate-next-story.md +0 -134
- package/bmad-core/templates/architecture-tmpl.yaml +0 -650
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +0 -156
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +0 -476
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +0 -280
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +0 -293
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +0 -206
- package/bmad-core/templates/front-end-spec-tmpl.yaml +0 -349
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +0 -805
- package/bmad-core/templates/market-research-tmpl.yaml +0 -252
- package/bmad-core/templates/prd-tmpl.yaml +0 -202
- package/bmad-core/templates/project-brief-tmpl.yaml +0 -221
- package/bmad-core/templates/story-tmpl.yaml +0 -137
- package/bmad-core/workflows/brownfield-fullstack.yaml +0 -297
- package/bmad-core/workflows/brownfield-service.yaml +0 -187
- package/bmad-core/workflows/brownfield-ui.yaml +0 -197
- package/bmad-core/workflows/greenfield-fullstack.yaml +0 -240
- package/bmad-core/workflows/greenfield-service.yaml +0 -206
- package/bmad-core/workflows/greenfield-ui.yaml +0 -235
- package/common/tasks/create-doc.md +0 -79
- package/common/utils/bmad-doc-template.md +0 -325
- package/common/utils/workflow-management.md +0 -69
- package/dist/agents/analyst.txt +0 -2849
- package/dist/agents/architect.txt +0 -3505
- package/dist/agents/bmad-master.txt +0 -9271
- package/dist/agents/bmad-orchestrator.txt +0 -2006
- package/dist/agents/qa.txt +0 -388
- package/dist/agents/sm.txt +0 -672
- package/dist/agents/ux-expert.txt +0 -987
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +0 -2401
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +0 -1635
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +0 -825
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +0 -11504
- package/dist/expansion-packs/bmad-creator-tools/agents/bmad-the-creator.txt +0 -2023
- package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +0 -2052
- package/dist/teams/team-all.txt +0 -11572
- package/dist/teams/team-fullstack.txt +0 -10903
- package/dist/teams/team-ide-minimal.txt +0 -4346
- package/dist/teams/team-no-ui.txt +0 -9458
- package/docs/GUIDING-PRINCIPLES.md +0 -91
- package/docs/agentic-tools/claude-code-guide.md +0 -19
- package/docs/agentic-tools/cline-guide.md +0 -16
- package/docs/agentic-tools/cursor-guide.md +0 -14
- package/docs/agentic-tools/gemini-cli-guide.md +0 -32
- package/docs/agentic-tools/github-copilot-guide.md +0 -42
- package/docs/agentic-tools/roo-code-guide.md +0 -15
- package/docs/agentic-tools/trae-guide.md +0 -14
- package/docs/agentic-tools/windsurf-guide.md +0 -14
- package/docs/core-architecture.md +0 -219
- package/docs/expansion-packs.md +0 -280
- package/docs/how-to-contribute-with-pull-requests.md +0 -158
- package/docs/template-markup-references.md +0 -86
- package/docs/user-guide.md +0 -1142
- package/docs/working-in-the-brownfield.md +0 -361
- package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +0 -13
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +0 -59
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +0 -67
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +0 -52
- package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +0 -201
- package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +0 -160
- package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +0 -7
- package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +0 -254
- package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +0 -651
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +0 -111
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +0 -216
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +0 -308
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +0 -613
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +0 -356
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +0 -343
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +0 -253
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +0 -484
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +0 -183
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +0 -175
- package/expansion-packs/bmad-creator-tools/README.md +0 -8
- package/expansion-packs/bmad-creator-tools/agents/bmad-the-creator.md +0 -54
- package/expansion-packs/bmad-creator-tools/config.yaml +0 -5
- package/expansion-packs/bmad-creator-tools/tasks/generate-expansion-pack.md +0 -1020
- package/expansion-packs/bmad-creator-tools/templates/agent-teams-tmpl.yaml +0 -178
- package/expansion-packs/bmad-creator-tools/templates/agent-tmpl.yaml +0 -154
- package/expansion-packs/bmad-creator-tools/templates/expansion-pack-plan-tmpl.yaml +0 -120
- package/expansion-packs/bmad-infrastructure-devops/config.yaml +0 -8
- package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +0 -308
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +0 -424
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +0 -629
- package/tools/bump-all-versions.js +0 -107
- package/tools/bump-core-version.js +0 -57
- package/tools/bump-expansion-version.js +0 -78
- package/tools/installer/config/ide-agent-config.yaml +0 -58
- package/tools/installer/config/install.config.yaml +0 -91
- package/tools/lib/yaml-utils.js +0 -29
- package/tools/md-assets/web-agent-startup-instructions.md +0 -39
- package/tools/update-expansion-version.js +0 -54
- /package/{bmad-core → .bmad-core}/checklists/architect-checklist.md +0 -0
- /package/{bmad-core → .bmad-core}/checklists/pm-checklist.md +0 -0
- /package/{bmad-core → .bmad-core}/checklists/po-master-checklist.md +0 -0
- /package/{bmad-core → .bmad-core}/checklists/story-dod-checklist.md +0 -0
- /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
- /package/{bmad-core → .bmad-core}/tasks/correct-course.md +0 -0
- /package/{bmad-core → .bmad-core}/tasks/create-deep-research-prompt.md +0 -0
- /package/.github/workflows/{release.yaml → release.yml} +0 -0
|
@@ -2,7 +2,6 @@ const path = require("node:path");
|
|
|
2
2
|
const fileManager = require("./file-manager");
|
|
3
3
|
const configLoader = require("./config-loader");
|
|
4
4
|
const ideSetup = require("./ide-setup");
|
|
5
|
-
const { extractYamlFromAgent } = require("../../lib/yaml-utils");
|
|
6
5
|
|
|
7
6
|
// Dynamic imports for ES modules
|
|
8
7
|
let chalk, ora, inquirer;
|
|
@@ -17,20 +16,6 @@ async function initializeModules() {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
class Installer {
|
|
20
|
-
async getCoreVersion() {
|
|
21
|
-
const yaml = require("js-yaml");
|
|
22
|
-
const fs = require("fs-extra");
|
|
23
|
-
const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yaml");
|
|
24
|
-
try {
|
|
25
|
-
const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
|
|
26
|
-
const coreConfig = yaml.load(coreConfigContent);
|
|
27
|
-
return coreConfig.version || "unknown";
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.warn("Could not read version from core-config.yaml, using 'unknown'");
|
|
30
|
-
return "unknown";
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
19
|
async install(config) {
|
|
35
20
|
// Initialize ES modules
|
|
36
21
|
await initializeModules();
|
|
@@ -38,23 +23,12 @@ class Installer {
|
|
|
38
23
|
const spinner = ora("Analyzing installation directory...").start();
|
|
39
24
|
|
|
40
25
|
try {
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Resolve installation directory relative to where the user ran the command
|
|
45
|
-
let installDir = path.isAbsolute(config.directory)
|
|
46
|
-
? config.directory
|
|
47
|
-
: path.resolve(originalCwd, config.directory);
|
|
48
|
-
|
|
26
|
+
// Resolve installation directory
|
|
27
|
+
let installDir = path.resolve(config.directory);
|
|
49
28
|
if (path.basename(installDir) === '.bmad-core') {
|
|
50
29
|
// If user points directly to .bmad-core, treat its parent as the project root
|
|
51
30
|
installDir = path.dirname(installDir);
|
|
52
31
|
}
|
|
53
|
-
|
|
54
|
-
// Log resolved path for clarity
|
|
55
|
-
if (!path.isAbsolute(config.directory)) {
|
|
56
|
-
spinner.text = `Resolving "${config.directory}" to: ${installDir}`;
|
|
57
|
-
}
|
|
58
32
|
|
|
59
33
|
// Check if directory exists and handle non-existent directories
|
|
60
34
|
if (!(await fileManager.pathExists(installDir))) {
|
|
@@ -100,7 +74,6 @@ class Installer {
|
|
|
100
74
|
}
|
|
101
75
|
}
|
|
102
76
|
]);
|
|
103
|
-
// Preserve the original CWD for the recursive call
|
|
104
77
|
config.directory = newDirectory;
|
|
105
78
|
return await this.install(config); // Recursive call with new directory
|
|
106
79
|
} else if (action === 'create') {
|
|
@@ -117,17 +90,6 @@ class Installer {
|
|
|
117
90
|
spinner.start("Analyzing installation directory...");
|
|
118
91
|
}
|
|
119
92
|
|
|
120
|
-
// If this is an update request from early detection, handle it directly
|
|
121
|
-
if (config.installType === 'update') {
|
|
122
|
-
const state = await this.detectInstallationState(installDir);
|
|
123
|
-
if (state.type === 'v4_existing') {
|
|
124
|
-
return await this.performUpdate(config, installDir, state.manifest, spinner);
|
|
125
|
-
} else {
|
|
126
|
-
spinner.fail('No existing v4 installation found to update');
|
|
127
|
-
throw new Error('No existing v4 installation found');
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
93
|
// Detect current state
|
|
132
94
|
const state = await this.detectInstallationState(installDir);
|
|
133
95
|
|
|
@@ -176,7 +138,6 @@ class Installer {
|
|
|
176
138
|
hasBmadCore: false,
|
|
177
139
|
hasOtherFiles: false,
|
|
178
140
|
manifest: null,
|
|
179
|
-
expansionPacks: {},
|
|
180
141
|
};
|
|
181
142
|
|
|
182
143
|
// Check if directory exists
|
|
@@ -186,7 +147,7 @@ class Installer {
|
|
|
186
147
|
|
|
187
148
|
// Check for V4 installation (has .bmad-core with manifest)
|
|
188
149
|
const bmadCorePath = path.join(installDir, ".bmad-core");
|
|
189
|
-
const manifestPath = path.join(bmadCorePath, "install-manifest.
|
|
150
|
+
const manifestPath = path.join(bmadCorePath, "install-manifest.yml");
|
|
190
151
|
|
|
191
152
|
if (await fileManager.pathExists(manifestPath)) {
|
|
192
153
|
state.type = "v4_existing";
|
|
@@ -220,22 +181,18 @@ class Installer {
|
|
|
220
181
|
});
|
|
221
182
|
|
|
222
183
|
if (files.length > 0) {
|
|
223
|
-
// Directory has other files, but no
|
|
184
|
+
// Directory has other files, but no BMAD installation.
|
|
224
185
|
// Treat as clean install but record that it isn't empty.
|
|
225
186
|
state.hasOtherFiles = true;
|
|
226
187
|
}
|
|
227
188
|
|
|
228
|
-
// Check for expansion packs (folders starting with .)
|
|
229
|
-
const expansionPacks = await this.detectExpansionPacks(installDir);
|
|
230
|
-
state.expansionPacks = expansionPacks;
|
|
231
|
-
|
|
232
189
|
return state; // clean install
|
|
233
190
|
}
|
|
234
191
|
|
|
235
|
-
async performFreshInstall(config, installDir, spinner
|
|
192
|
+
async performFreshInstall(config, installDir, spinner) {
|
|
236
193
|
// Ensure modules are initialized
|
|
237
194
|
await initializeModules();
|
|
238
|
-
spinner.text = "Installing
|
|
195
|
+
spinner.text = "Installing BMAD Method...";
|
|
239
196
|
|
|
240
197
|
let files = [];
|
|
241
198
|
|
|
@@ -244,11 +201,7 @@ class Installer {
|
|
|
244
201
|
spinner.text = "Copying complete .bmad-core folder...";
|
|
245
202
|
const sourceDir = configLoader.getBmadCorePath();
|
|
246
203
|
const bmadCoreDestDir = path.join(installDir, ".bmad-core");
|
|
247
|
-
await fileManager.
|
|
248
|
-
|
|
249
|
-
// Copy common/ items to .bmad-core
|
|
250
|
-
spinner.text = "Copying common utilities...";
|
|
251
|
-
await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
204
|
+
await fileManager.copyDirectory(sourceDir, bmadCoreDestDir);
|
|
252
205
|
|
|
253
206
|
// Get list of all files for manifest
|
|
254
207
|
const glob = require("glob");
|
|
@@ -263,16 +216,15 @@ class Installer {
|
|
|
263
216
|
// Single agent installation
|
|
264
217
|
spinner.text = `Installing ${config.agent} agent...`;
|
|
265
218
|
|
|
266
|
-
// Copy agent file
|
|
219
|
+
// Copy agent file
|
|
267
220
|
const agentPath = configLoader.getAgentPath(config.agent);
|
|
268
221
|
const destAgentPath = path.join(
|
|
269
222
|
installDir,
|
|
270
|
-
".bmad-core",
|
|
271
223
|
"agents",
|
|
272
224
|
`${config.agent}.md`
|
|
273
225
|
);
|
|
274
|
-
await fileManager.
|
|
275
|
-
files.push(
|
|
226
|
+
await fileManager.copyFile(agentPath, destAgentPath);
|
|
227
|
+
files.push(`agents/${config.agent}.md`);
|
|
276
228
|
|
|
277
229
|
// Copy dependencies
|
|
278
230
|
const dependencies = await configLoader.getAgentDependencies(
|
|
@@ -284,108 +236,29 @@ class Installer {
|
|
|
284
236
|
spinner.text = `Copying dependency: ${dep}`;
|
|
285
237
|
|
|
286
238
|
if (dep.includes("*")) {
|
|
287
|
-
// Handle glob patterns
|
|
239
|
+
// Handle glob patterns
|
|
288
240
|
const copiedFiles = await fileManager.copyGlobPattern(
|
|
289
241
|
dep.replace(".bmad-core/", ""),
|
|
290
242
|
sourceBase,
|
|
291
|
-
|
|
292
|
-
".bmad-core"
|
|
243
|
+
installDir
|
|
293
244
|
);
|
|
294
|
-
files.push(...copiedFiles
|
|
245
|
+
files.push(...copiedFiles);
|
|
295
246
|
} else {
|
|
296
|
-
// Handle single files
|
|
247
|
+
// Handle single files
|
|
297
248
|
const sourcePath = path.join(
|
|
298
249
|
sourceBase,
|
|
299
250
|
dep.replace(".bmad-core/", "")
|
|
300
251
|
);
|
|
301
252
|
const destPath = path.join(
|
|
302
253
|
installDir,
|
|
303
|
-
dep
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
const needsRootReplacement = dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
|
|
307
|
-
let success = false;
|
|
308
|
-
|
|
309
|
-
if (needsRootReplacement) {
|
|
310
|
-
success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, ".bmad-core");
|
|
311
|
-
} else {
|
|
312
|
-
success = await fileManager.copyFile(sourcePath, destPath);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (success) {
|
|
316
|
-
files.push(dep);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Copy common/ items to .bmad-core
|
|
322
|
-
spinner.text = "Copying common utilities...";
|
|
323
|
-
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
324
|
-
files.push(...commonFiles);
|
|
325
|
-
} else if (config.installType === "team") {
|
|
326
|
-
// Team installation
|
|
327
|
-
spinner.text = `Installing ${config.team} team...`;
|
|
328
|
-
|
|
329
|
-
// Get team dependencies
|
|
330
|
-
const teamDependencies = await configLoader.getTeamDependencies(config.team);
|
|
331
|
-
const sourceBase = configLoader.getBmadCorePath();
|
|
332
|
-
|
|
333
|
-
// Install all team dependencies
|
|
334
|
-
for (const dep of teamDependencies) {
|
|
335
|
-
spinner.text = `Copying team dependency: ${dep}`;
|
|
336
|
-
|
|
337
|
-
if (dep.includes("*")) {
|
|
338
|
-
// Handle glob patterns with {root} replacement
|
|
339
|
-
const copiedFiles = await fileManager.copyGlobPattern(
|
|
340
|
-
dep.replace(".bmad-core/", ""),
|
|
341
|
-
sourceBase,
|
|
342
|
-
path.join(installDir, ".bmad-core"),
|
|
343
|
-
".bmad-core"
|
|
254
|
+
dep.replace(".bmad-core/", "")
|
|
344
255
|
);
|
|
345
|
-
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
|
346
|
-
} else {
|
|
347
|
-
// Handle single files with {root} replacement if needed
|
|
348
|
-
const sourcePath = path.join(sourceBase, dep.replace(".bmad-core/", ""));
|
|
349
|
-
const destPath = path.join(installDir, dep);
|
|
350
|
-
|
|
351
|
-
const needsRootReplacement = dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
|
|
352
|
-
let success = false;
|
|
353
|
-
|
|
354
|
-
if (needsRootReplacement) {
|
|
355
|
-
success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, ".bmad-core");
|
|
356
|
-
} else {
|
|
357
|
-
success = await fileManager.copyFile(sourcePath, destPath);
|
|
358
|
-
}
|
|
359
256
|
|
|
360
|
-
if (
|
|
361
|
-
files.push(dep);
|
|
257
|
+
if (await fileManager.copyFile(sourcePath, destPath)) {
|
|
258
|
+
files.push(dep.replace(".bmad-core/", ""));
|
|
362
259
|
}
|
|
363
260
|
}
|
|
364
261
|
}
|
|
365
|
-
|
|
366
|
-
// Copy common/ items to .bmad-core
|
|
367
|
-
spinner.text = "Copying common utilities...";
|
|
368
|
-
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
369
|
-
files.push(...commonFiles);
|
|
370
|
-
} else if (config.installType === "expansion-only") {
|
|
371
|
-
// Expansion-only installation - DO NOT create .bmad-core
|
|
372
|
-
// Only install expansion packs
|
|
373
|
-
spinner.text = "Installing expansion packs only...";
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Install expansion packs if requested
|
|
377
|
-
const expansionFiles = await this.installExpansionPacks(installDir, config.expansionPacks, spinner, config);
|
|
378
|
-
files.push(...expansionFiles);
|
|
379
|
-
|
|
380
|
-
// Install web bundles if requested
|
|
381
|
-
if (config.includeWebBundles && config.webBundlesDirectory) {
|
|
382
|
-
spinner.text = "Installing web bundles...";
|
|
383
|
-
// Resolve web bundles directory using the same logic as the main installation directory
|
|
384
|
-
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
|
385
|
-
let resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
|
|
386
|
-
? config.webBundlesDirectory
|
|
387
|
-
: path.resolve(originalCwd, config.webBundlesDirectory);
|
|
388
|
-
await this.installWebBundles(resolvedWebBundlesDir, config, spinner);
|
|
389
262
|
}
|
|
390
263
|
|
|
391
264
|
// Set up IDE integration if requested
|
|
@@ -393,25 +266,16 @@ class Installer {
|
|
|
393
266
|
if (ides.length > 0) {
|
|
394
267
|
for (const ide of ides) {
|
|
395
268
|
spinner.text = `Setting up ${ide} integration...`;
|
|
396
|
-
|
|
397
|
-
await ideSetup.setup(ide, installDir, config.agent, spinner, preConfiguredSettings);
|
|
269
|
+
await ideSetup.setup(ide, installDir, config.agent);
|
|
398
270
|
}
|
|
399
271
|
}
|
|
400
272
|
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
await fileManager.modifyCoreConfig(installDir, config);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Create manifest (skip for expansion-only installations)
|
|
408
|
-
if (config.installType !== "expansion-only") {
|
|
409
|
-
spinner.text = "Creating installation manifest...";
|
|
410
|
-
await fileManager.createManifest(installDir, config, files);
|
|
411
|
-
}
|
|
273
|
+
// Create manifest
|
|
274
|
+
spinner.text = "Creating installation manifest...";
|
|
275
|
+
await fileManager.createManifest(installDir, config, files);
|
|
412
276
|
|
|
413
277
|
spinner.succeed("Installation complete!");
|
|
414
|
-
this.showSuccessMessage(config, installDir
|
|
278
|
+
this.showSuccessMessage(config, installDir);
|
|
415
279
|
}
|
|
416
280
|
|
|
417
281
|
async handleExistingV4Installation(config, installDir, state, spinner) {
|
|
@@ -419,137 +283,33 @@ class Installer {
|
|
|
419
283
|
await initializeModules();
|
|
420
284
|
spinner.stop();
|
|
421
285
|
|
|
422
|
-
|
|
423
|
-
const newVersion = await this.getCoreVersion();
|
|
424
|
-
const versionCompare = this.compareVersions(currentVersion, newVersion);
|
|
425
|
-
|
|
426
|
-
console.log(chalk.yellow("\n🔍 Found existing BMad v4 installation"));
|
|
286
|
+
console.log(chalk.yellow("\n🔍 Found existing BMAD v4 installation"));
|
|
427
287
|
console.log(` Directory: ${installDir}`);
|
|
428
|
-
console.log(`
|
|
429
|
-
console.log(` Available version: ${newVersion}`);
|
|
288
|
+
console.log(` Version: ${state.manifest.version}`);
|
|
430
289
|
console.log(
|
|
431
290
|
` Installed: ${new Date(
|
|
432
291
|
state.manifest.installed_at
|
|
433
292
|
).toLocaleDateString()}`
|
|
434
293
|
);
|
|
435
294
|
|
|
436
|
-
// Check file integrity
|
|
437
|
-
spinner.start("Checking installation integrity...");
|
|
438
|
-
const integrity = await fileManager.checkFileIntegrity(installDir, state.manifest);
|
|
439
|
-
spinner.stop();
|
|
440
|
-
|
|
441
|
-
const hasMissingFiles = integrity.missing.length > 0;
|
|
442
|
-
const hasModifiedFiles = integrity.modified.length > 0;
|
|
443
|
-
const hasIntegrityIssues = hasMissingFiles || hasModifiedFiles;
|
|
444
|
-
|
|
445
|
-
if (hasIntegrityIssues) {
|
|
446
|
-
console.log(chalk.red("\n⚠️ Installation issues detected:"));
|
|
447
|
-
if (hasMissingFiles) {
|
|
448
|
-
console.log(chalk.red(` Missing files: ${integrity.missing.length}`));
|
|
449
|
-
if (integrity.missing.length <= 5) {
|
|
450
|
-
integrity.missing.forEach(file => console.log(chalk.dim(` - ${file}`)));
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
if (hasModifiedFiles) {
|
|
454
|
-
console.log(chalk.yellow(` Modified files: ${integrity.modified.length}`));
|
|
455
|
-
if (integrity.modified.length <= 5) {
|
|
456
|
-
integrity.modified.forEach(file => console.log(chalk.dim(` - ${file}`)));
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// Show existing expansion packs
|
|
462
|
-
if (Object.keys(state.expansionPacks).length > 0) {
|
|
463
|
-
console.log(chalk.cyan("\n📦 Installed expansion packs:"));
|
|
464
|
-
for (const [packId, packInfo] of Object.entries(state.expansionPacks)) {
|
|
465
|
-
if (packInfo.hasManifest && packInfo.manifest) {
|
|
466
|
-
console.log(` - ${packId} (v${packInfo.manifest.version || 'unknown'})`);
|
|
467
|
-
} else {
|
|
468
|
-
console.log(` - ${packId} (no manifest)`);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
let choices = [];
|
|
474
|
-
|
|
475
|
-
if (versionCompare < 0) {
|
|
476
|
-
console.log(chalk.cyan("\n⬆️ Upgrade available for BMad core"));
|
|
477
|
-
choices.push({ name: `Upgrade BMad core (v${currentVersion} → v${newVersion})`, value: "upgrade" });
|
|
478
|
-
} else if (versionCompare === 0) {
|
|
479
|
-
if (hasIntegrityIssues) {
|
|
480
|
-
// Offer repair option when files are missing or modified
|
|
481
|
-
choices.push({
|
|
482
|
-
name: "Repair installation (restore missing/modified files)",
|
|
483
|
-
value: "repair"
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
console.log(chalk.yellow("\n⚠️ Same version already installed"));
|
|
487
|
-
choices.push({ name: `Force reinstall BMad core (v${currentVersion} - reinstall)`, value: "reinstall" });
|
|
488
|
-
} else {
|
|
489
|
-
console.log(chalk.yellow("\n⬇️ Installed version is newer than available"));
|
|
490
|
-
choices.push({ name: `Downgrade BMad core (v${currentVersion} → v${newVersion})`, value: "reinstall" });
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
choices.push(
|
|
494
|
-
{ name: "Add/update expansion packs only", value: "expansions" },
|
|
495
|
-
{ name: "Cancel", value: "cancel" }
|
|
496
|
-
);
|
|
497
|
-
|
|
498
295
|
const { action } = await inquirer.prompt([
|
|
499
296
|
{
|
|
500
297
|
type: "list",
|
|
501
298
|
name: "action",
|
|
502
299
|
message: "What would you like to do?",
|
|
503
|
-
choices:
|
|
300
|
+
choices: [
|
|
301
|
+
{ name: "Update existing installation", value: "update" },
|
|
302
|
+
{ name: "Reinstall (overwrite)", value: "reinstall" },
|
|
303
|
+
{ name: "Cancel", value: "cancel" },
|
|
304
|
+
],
|
|
504
305
|
},
|
|
505
306
|
]);
|
|
506
307
|
|
|
507
308
|
switch (action) {
|
|
508
|
-
case "
|
|
309
|
+
case "update":
|
|
509
310
|
return await this.performUpdate(config, installDir, state.manifest, spinner);
|
|
510
|
-
case "repair":
|
|
511
|
-
// For repair, restore missing/modified files while backing up modified ones
|
|
512
|
-
return await this.performRepair(config, installDir, state.manifest, integrity, spinner);
|
|
513
311
|
case "reinstall":
|
|
514
|
-
// For reinstall, don't check for modifications - just overwrite
|
|
515
312
|
return await this.performReinstall(config, installDir, spinner);
|
|
516
|
-
case "expansions":
|
|
517
|
-
// Ask which expansion packs to install
|
|
518
|
-
const availableExpansionPacks = await this.getAvailableExpansionPacks();
|
|
519
|
-
|
|
520
|
-
if (availableExpansionPacks.length === 0) {
|
|
521
|
-
console.log(chalk.yellow("No expansion packs available."));
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
const { selectedPacks } = await inquirer.prompt([
|
|
526
|
-
{
|
|
527
|
-
type: 'checkbox',
|
|
528
|
-
name: 'selectedPacks',
|
|
529
|
-
message: 'Select expansion packs to install/update:',
|
|
530
|
-
choices: availableExpansionPacks.map(pack => ({
|
|
531
|
-
name: `${pack.name} v${pack.version} - ${pack.description}`,
|
|
532
|
-
value: pack.id,
|
|
533
|
-
checked: state.expansionPacks[pack.id] !== undefined
|
|
534
|
-
}))
|
|
535
|
-
}
|
|
536
|
-
]);
|
|
537
|
-
|
|
538
|
-
if (selectedPacks.length === 0) {
|
|
539
|
-
console.log(chalk.yellow("No expansion packs selected."));
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
spinner.start("Installing expansion packs...");
|
|
544
|
-
const expansionFiles = await this.installExpansionPacks(installDir, selectedPacks, spinner, { ides: config.ides || [] });
|
|
545
|
-
spinner.succeed("Expansion packs installed successfully!");
|
|
546
|
-
|
|
547
|
-
console.log(chalk.green("\n✓ Installation complete!"));
|
|
548
|
-
console.log(chalk.green(`✓ Expansion packs installed/updated:`));
|
|
549
|
-
for (const packId of selectedPacks) {
|
|
550
|
-
console.log(chalk.green(` - ${packId} → .${packId}/`));
|
|
551
|
-
}
|
|
552
|
-
return;
|
|
553
313
|
case "cancel":
|
|
554
314
|
console.log("Installation cancelled.");
|
|
555
315
|
return;
|
|
@@ -562,7 +322,7 @@ class Installer {
|
|
|
562
322
|
spinner.stop();
|
|
563
323
|
|
|
564
324
|
console.log(
|
|
565
|
-
chalk.yellow("\n🔍 Found
|
|
325
|
+
chalk.yellow("\n🔍 Found BMAD v3 installation (bmad-agent/ directory)")
|
|
566
326
|
);
|
|
567
327
|
console.log(` Directory: ${installDir}`);
|
|
568
328
|
|
|
@@ -584,10 +344,7 @@ class Installer {
|
|
|
584
344
|
console.log(chalk.cyan("\n📦 Starting v3 to v4 upgrade process..."));
|
|
585
345
|
const V3ToV4Upgrader = require("../../upgraders/v3-to-v4-upgrader");
|
|
586
346
|
const upgrader = new V3ToV4Upgrader();
|
|
587
|
-
return await upgrader.upgrade({
|
|
588
|
-
projectPath: installDir,
|
|
589
|
-
ides: config.ides || [] // Pass IDE selections from initial config
|
|
590
|
-
});
|
|
347
|
+
return await upgrader.upgrade({ projectPath: installDir });
|
|
591
348
|
}
|
|
592
349
|
case "alongside":
|
|
593
350
|
return await this.performFreshInstall(config, installDir, spinner);
|
|
@@ -650,20 +407,12 @@ class Installer {
|
|
|
650
407
|
spinner.start("Checking for updates...");
|
|
651
408
|
|
|
652
409
|
try {
|
|
653
|
-
//
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
let modifiedFiles = [];
|
|
660
|
-
if (versionCompare !== 0) {
|
|
661
|
-
spinner.text = "Checking for modified files...";
|
|
662
|
-
modifiedFiles = await fileManager.checkModifiedFiles(
|
|
663
|
-
installDir,
|
|
664
|
-
manifest
|
|
665
|
-
);
|
|
666
|
-
}
|
|
410
|
+
// Check for modified files
|
|
411
|
+
spinner.text = "Checking for modified files...";
|
|
412
|
+
const modifiedFiles = await fileManager.checkModifiedFiles(
|
|
413
|
+
installDir,
|
|
414
|
+
manifest
|
|
415
|
+
);
|
|
667
416
|
|
|
668
417
|
if (modifiedFiles.length > 0) {
|
|
669
418
|
spinner.warn("Found modified files");
|
|
@@ -703,138 +452,35 @@ class Installer {
|
|
|
703
452
|
}
|
|
704
453
|
|
|
705
454
|
// Perform update by re-running installation
|
|
706
|
-
spinner.text =
|
|
455
|
+
spinner.text = "Updating files...";
|
|
707
456
|
const config = {
|
|
708
457
|
installType: manifest.install_type,
|
|
709
458
|
agent: manifest.agent,
|
|
710
459
|
directory: installDir,
|
|
711
|
-
|
|
460
|
+
ide: newConfig.ide || manifest.ide_setup, // Use new IDE choice if provided
|
|
712
461
|
};
|
|
713
462
|
|
|
714
|
-
await this.performFreshInstall(config, installDir, spinner
|
|
715
|
-
|
|
716
|
-
// Clean up .yml files that now have .yaml counterparts
|
|
717
|
-
spinner.text = "Cleaning up legacy .yml files...";
|
|
718
|
-
await this.cleanupLegacyYmlFiles(installDir, spinner);
|
|
463
|
+
await this.performFreshInstall(config, installDir, spinner);
|
|
719
464
|
} catch (error) {
|
|
720
465
|
spinner.fail("Update failed");
|
|
721
466
|
throw error;
|
|
722
467
|
}
|
|
723
468
|
}
|
|
724
469
|
|
|
725
|
-
async performRepair(config, installDir, manifest, integrity, spinner) {
|
|
726
|
-
spinner.start("Preparing to repair installation...");
|
|
727
|
-
|
|
728
|
-
try {
|
|
729
|
-
// Back up modified files
|
|
730
|
-
if (integrity.modified.length > 0) {
|
|
731
|
-
spinner.text = "Backing up modified files...";
|
|
732
|
-
for (const file of integrity.modified) {
|
|
733
|
-
const filePath = path.join(installDir, file);
|
|
734
|
-
if (await fileManager.pathExists(filePath)) {
|
|
735
|
-
const backupPath = await fileManager.backupFile(filePath);
|
|
736
|
-
console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`));
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
// Restore missing and modified files
|
|
742
|
-
spinner.text = "Restoring files...";
|
|
743
|
-
const sourceBase = configLoader.getBmadCorePath();
|
|
744
|
-
const filesToRestore = [...integrity.missing, ...integrity.modified];
|
|
745
|
-
|
|
746
|
-
for (const file of filesToRestore) {
|
|
747
|
-
// Skip the manifest file itself
|
|
748
|
-
if (file.endsWith('install-manifest.yaml')) continue;
|
|
749
|
-
|
|
750
|
-
const relativePath = file.replace('.bmad-core/', '');
|
|
751
|
-
const destPath = path.join(installDir, file);
|
|
752
|
-
|
|
753
|
-
// Check if this is a common/ file that needs special processing
|
|
754
|
-
const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
|
|
755
|
-
const commonSourcePath = path.join(commonBase, 'common', relativePath);
|
|
756
|
-
|
|
757
|
-
if (await fileManager.pathExists(commonSourcePath)) {
|
|
758
|
-
// This is a common/ file - needs template processing
|
|
759
|
-
const fs = require('fs').promises;
|
|
760
|
-
const content = await fs.readFile(commonSourcePath, 'utf8');
|
|
761
|
-
const updatedContent = content.replace(/\{root\}/g, '.bmad-core');
|
|
762
|
-
await fileManager.ensureDirectory(path.dirname(destPath));
|
|
763
|
-
await fs.writeFile(destPath, updatedContent, 'utf8');
|
|
764
|
-
spinner.text = `Restored: ${file}`;
|
|
765
|
-
} else {
|
|
766
|
-
// Regular file from bmad-core
|
|
767
|
-
const sourcePath = path.join(sourceBase, relativePath);
|
|
768
|
-
if (await fileManager.pathExists(sourcePath)) {
|
|
769
|
-
await fileManager.copyFile(sourcePath, destPath);
|
|
770
|
-
spinner.text = `Restored: ${file}`;
|
|
771
|
-
|
|
772
|
-
// If this is a .yaml file, check for and remove corresponding .yml file
|
|
773
|
-
if (file.endsWith('.yaml')) {
|
|
774
|
-
const ymlFile = file.replace(/\.yaml$/, '.yml');
|
|
775
|
-
const ymlPath = path.join(installDir, ymlFile);
|
|
776
|
-
if (await fileManager.pathExists(ymlPath)) {
|
|
777
|
-
const fs = require('fs').promises;
|
|
778
|
-
await fs.unlink(ymlPath);
|
|
779
|
-
console.log(chalk.dim(` Removed legacy: ${ymlFile} (replaced by ${file})`));
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
} else {
|
|
783
|
-
console.warn(chalk.yellow(` Warning: Source file not found: ${file}`));
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Clean up .yml files that now have .yaml counterparts
|
|
789
|
-
spinner.text = "Cleaning up legacy .yml files...";
|
|
790
|
-
await this.cleanupLegacyYmlFiles(installDir, spinner);
|
|
791
|
-
|
|
792
|
-
spinner.succeed("Repair completed successfully!");
|
|
793
|
-
|
|
794
|
-
// Show summary
|
|
795
|
-
console.log(chalk.green("\n✓ Installation repaired!"));
|
|
796
|
-
if (integrity.missing.length > 0) {
|
|
797
|
-
console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
|
|
798
|
-
}
|
|
799
|
-
if (integrity.modified.length > 0) {
|
|
800
|
-
console.log(chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`));
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Warning for Cursor custom modes if agents were repaired
|
|
804
|
-
const ides = manifest.ides_setup || [];
|
|
805
|
-
if (ides.includes('cursor')) {
|
|
806
|
-
console.log(chalk.yellow.bold("\n⚠️ IMPORTANT: Cursor Custom Modes Update Required"));
|
|
807
|
-
console.log(chalk.yellow("Since agent files have been repaired, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs."));
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
} catch (error) {
|
|
811
|
-
spinner.fail("Repair failed");
|
|
812
|
-
throw error;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
|
|
816
470
|
async performReinstall(config, installDir, spinner) {
|
|
817
|
-
spinner.start("
|
|
471
|
+
spinner.start("Reinstalling BMAD Method...");
|
|
818
472
|
|
|
819
473
|
// Remove existing .bmad-core
|
|
820
474
|
const bmadCorePath = path.join(installDir, ".bmad-core");
|
|
821
475
|
if (await fileManager.pathExists(bmadCorePath)) {
|
|
822
|
-
spinner.text = "Removing existing installation...";
|
|
823
476
|
await fileManager.removeDirectory(bmadCorePath);
|
|
824
477
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
const result = await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
|
|
828
|
-
|
|
829
|
-
// Clean up .yml files that now have .yaml counterparts
|
|
830
|
-
spinner.text = "Cleaning up legacy .yml files...";
|
|
831
|
-
await this.cleanupLegacyYmlFiles(installDir, spinner);
|
|
832
|
-
|
|
833
|
-
return result;
|
|
478
|
+
|
|
479
|
+
return await this.performFreshInstall(config, installDir, spinner);
|
|
834
480
|
}
|
|
835
481
|
|
|
836
|
-
showSuccessMessage(config, installDir
|
|
837
|
-
console.log(chalk.green("\n✓
|
|
482
|
+
showSuccessMessage(config, installDir) {
|
|
483
|
+
console.log(chalk.green("\n✓ BMAD Method installed successfully!\n"));
|
|
838
484
|
|
|
839
485
|
const ides = config.ides || (config.ide ? [config.ide] : []);
|
|
840
486
|
if (ides.length > 0) {
|
|
@@ -842,7 +488,7 @@ class Installer {
|
|
|
842
488
|
const ideConfig = configLoader.getIdeConfiguration(ide);
|
|
843
489
|
if (ideConfig?.instructions) {
|
|
844
490
|
console.log(
|
|
845
|
-
chalk.bold(`To use
|
|
491
|
+
chalk.bold(`To use BMAD agents in ${ideConfig.name}:`)
|
|
846
492
|
);
|
|
847
493
|
console.log(ideConfig.instructions);
|
|
848
494
|
}
|
|
@@ -857,26 +503,7 @@ class Installer {
|
|
|
857
503
|
|
|
858
504
|
// Information about installation components
|
|
859
505
|
console.log(chalk.bold("\n🎯 Installation Summary:"));
|
|
860
|
-
|
|
861
|
-
console.log(chalk.green("✓ .bmad-core framework installed with all agents and workflows"));
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
if (config.expansionPacks && config.expansionPacks.length > 0) {
|
|
865
|
-
console.log(chalk.green(`✓ Expansion packs installed:`));
|
|
866
|
-
for (const packId of config.expansionPacks) {
|
|
867
|
-
console.log(chalk.green(` - ${packId} → .${packId}/`));
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
if (config.includeWebBundles && config.webBundlesDirectory) {
|
|
872
|
-
const bundleInfo = this.getWebBundleInfo(config);
|
|
873
|
-
// Resolve the web bundles directory for display
|
|
874
|
-
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
|
875
|
-
const resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
|
|
876
|
-
? config.webBundlesDirectory
|
|
877
|
-
: path.resolve(originalCwd, config.webBundlesDirectory);
|
|
878
|
-
console.log(chalk.green(`✓ Web bundles (${bundleInfo}) installed to: ${resolvedWebBundlesDir}`));
|
|
879
|
-
}
|
|
506
|
+
console.log(chalk.green("✓ .bmad-core framework installed with all agents and workflows"));
|
|
880
507
|
|
|
881
508
|
if (ides.length > 0) {
|
|
882
509
|
const ideNames = ides.map(ide => {
|
|
@@ -887,13 +514,11 @@ class Installer {
|
|
|
887
514
|
}
|
|
888
515
|
|
|
889
516
|
// Information about web bundles
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
console.log("in other projects as standalone files.");
|
|
896
|
-
}
|
|
517
|
+
console.log(chalk.bold("\n📦 Web Bundles Available:"));
|
|
518
|
+
console.log("Self-contained web bundles have been included in your installation:");
|
|
519
|
+
console.log(chalk.cyan(` ${installDir}/.bmad-core/web-bundles/`));
|
|
520
|
+
console.log("These bundles work independently without this installation and can be");
|
|
521
|
+
console.log("shared, moved, or used in other projects as standalone files.");
|
|
897
522
|
|
|
898
523
|
if (config.installType === "single-agent") {
|
|
899
524
|
console.log(
|
|
@@ -905,12 +530,6 @@ class Installer {
|
|
|
905
530
|
chalk.dim("Need everything? Run: npx bmad-method install --full")
|
|
906
531
|
);
|
|
907
532
|
}
|
|
908
|
-
|
|
909
|
-
// Warning for Cursor custom modes if agents were updated
|
|
910
|
-
if (options.isUpdate && ides.includes('cursor')) {
|
|
911
|
-
console.log(chalk.yellow.bold("\n⚠️ IMPORTANT: Cursor Custom Modes Update Required"));
|
|
912
|
-
console.log(chalk.yellow("Since agents have been updated, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs."));
|
|
913
|
-
}
|
|
914
533
|
}
|
|
915
534
|
|
|
916
535
|
// Legacy method for backward compatibility
|
|
@@ -931,7 +550,7 @@ class Installer {
|
|
|
931
550
|
};
|
|
932
551
|
return await this.install(config);
|
|
933
552
|
}
|
|
934
|
-
console.log(chalk.red("No
|
|
553
|
+
console.log(chalk.red("No BMAD installation found."));
|
|
935
554
|
}
|
|
936
555
|
|
|
937
556
|
async listAgents() {
|
|
@@ -939,7 +558,7 @@ class Installer {
|
|
|
939
558
|
await initializeModules();
|
|
940
559
|
const agents = await configLoader.getAvailableAgents();
|
|
941
560
|
|
|
942
|
-
console.log(chalk.bold("\nAvailable
|
|
561
|
+
console.log(chalk.bold("\nAvailable BMAD Agents:\n"));
|
|
943
562
|
|
|
944
563
|
for (const agent of agents) {
|
|
945
564
|
console.log(chalk.cyan(` ${agent.id.padEnd(20)}`), agent.description);
|
|
@@ -950,33 +569,6 @@ class Installer {
|
|
|
950
569
|
);
|
|
951
570
|
}
|
|
952
571
|
|
|
953
|
-
async listExpansionPacks() {
|
|
954
|
-
// Initialize ES modules
|
|
955
|
-
await initializeModules();
|
|
956
|
-
const expansionPacks = await this.getAvailableExpansionPacks();
|
|
957
|
-
|
|
958
|
-
console.log(chalk.bold("\nAvailable BMad Expansion Packs:\n"));
|
|
959
|
-
|
|
960
|
-
if (expansionPacks.length === 0) {
|
|
961
|
-
console.log(chalk.yellow("No expansion packs found."));
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
for (const pack of expansionPacks) {
|
|
966
|
-
console.log(chalk.cyan(` ${pack.id.padEnd(20)}`),
|
|
967
|
-
`${pack.name} v${pack.version}`);
|
|
968
|
-
console.log(chalk.dim(` ${' '.repeat(22)}${pack.description}`));
|
|
969
|
-
if (pack.author && pack.author !== 'Unknown') {
|
|
970
|
-
console.log(chalk.dim(` ${' '.repeat(22)}by ${pack.author}`));
|
|
971
|
-
}
|
|
972
|
-
console.log();
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
console.log(
|
|
976
|
-
chalk.dim("Install with: npx bmad-method install --full --expansion-packs <id>\n")
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
|
|
980
572
|
async showStatus() {
|
|
981
573
|
// Initialize ES modules
|
|
982
574
|
await initializeModules();
|
|
@@ -984,7 +576,7 @@ class Installer {
|
|
|
984
576
|
|
|
985
577
|
if (!installDir) {
|
|
986
578
|
console.log(
|
|
987
|
-
chalk.yellow("No
|
|
579
|
+
chalk.yellow("No BMAD installation found in current directory tree")
|
|
988
580
|
);
|
|
989
581
|
return;
|
|
990
582
|
}
|
|
@@ -996,7 +588,7 @@ class Installer {
|
|
|
996
588
|
return;
|
|
997
589
|
}
|
|
998
590
|
|
|
999
|
-
console.log(chalk.bold("\
|
|
591
|
+
console.log(chalk.bold("\nBMAD Installation Status:\n"));
|
|
1000
592
|
console.log(` Directory: ${installDir}`);
|
|
1001
593
|
console.log(` Version: ${manifest.version}`);
|
|
1002
594
|
console.log(
|
|
@@ -1010,8 +602,8 @@ class Installer {
|
|
|
1010
602
|
console.log(` Agent: ${manifest.agent}`);
|
|
1011
603
|
}
|
|
1012
604
|
|
|
1013
|
-
if (manifest.
|
|
1014
|
-
console.log(` IDE Setup: ${manifest.
|
|
605
|
+
if (manifest.ide_setup) {
|
|
606
|
+
console.log(` IDE Setup: ${manifest.ide_setup}`);
|
|
1015
607
|
}
|
|
1016
608
|
|
|
1017
609
|
console.log(` Total Files: ${manifest.files.length}`);
|
|
@@ -1032,715 +624,13 @@ class Installer {
|
|
|
1032
624
|
return configLoader.getAvailableAgents();
|
|
1033
625
|
}
|
|
1034
626
|
|
|
1035
|
-
async getAvailableExpansionPacks() {
|
|
1036
|
-
return configLoader.getAvailableExpansionPacks();
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
async getAvailableTeams() {
|
|
1040
|
-
return configLoader.getAvailableTeams();
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
async installExpansionPacks(installDir, selectedPacks, spinner, config = {}) {
|
|
1044
|
-
if (!selectedPacks || selectedPacks.length === 0) {
|
|
1045
|
-
return [];
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
const installedFiles = [];
|
|
1049
|
-
const glob = require('glob');
|
|
1050
|
-
|
|
1051
|
-
for (const packId of selectedPacks) {
|
|
1052
|
-
spinner.text = `Installing expansion pack: ${packId}...`;
|
|
1053
|
-
|
|
1054
|
-
try {
|
|
1055
|
-
const expansionPacks = await this.getAvailableExpansionPacks();
|
|
1056
|
-
const pack = expansionPacks.find(p => p.id === packId);
|
|
1057
|
-
|
|
1058
|
-
if (!pack) {
|
|
1059
|
-
console.warn(`Expansion pack ${packId} not found, skipping...`);
|
|
1060
|
-
continue;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
// Check if expansion pack already exists
|
|
1064
|
-
let expansionDotFolder = path.join(installDir, `.${packId}`);
|
|
1065
|
-
const existingManifestPath = path.join(expansionDotFolder, 'install-manifest.yaml');
|
|
1066
|
-
|
|
1067
|
-
if (await fileManager.pathExists(existingManifestPath)) {
|
|
1068
|
-
spinner.stop();
|
|
1069
|
-
const existingManifest = await fileManager.readExpansionPackManifest(installDir, packId);
|
|
1070
|
-
|
|
1071
|
-
console.log(chalk.yellow(`\n🔍 Found existing ${pack.name} installation`));
|
|
1072
|
-
console.log(` Current version: ${existingManifest.version || 'unknown'}`);
|
|
1073
|
-
console.log(` New version: ${pack.version}`);
|
|
1074
|
-
|
|
1075
|
-
// Check integrity of existing expansion pack
|
|
1076
|
-
const packIntegrity = await fileManager.checkFileIntegrity(installDir, existingManifest);
|
|
1077
|
-
const hasPackIntegrityIssues = packIntegrity.missing.length > 0 || packIntegrity.modified.length > 0;
|
|
1078
|
-
|
|
1079
|
-
if (hasPackIntegrityIssues) {
|
|
1080
|
-
console.log(chalk.red(" ⚠️ Installation issues detected:"));
|
|
1081
|
-
if (packIntegrity.missing.length > 0) {
|
|
1082
|
-
console.log(chalk.red(` Missing files: ${packIntegrity.missing.length}`));
|
|
1083
|
-
}
|
|
1084
|
-
if (packIntegrity.modified.length > 0) {
|
|
1085
|
-
console.log(chalk.yellow(` Modified files: ${packIntegrity.modified.length}`));
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
const versionCompare = this.compareVersions(existingManifest.version || '0.0.0', pack.version);
|
|
1090
|
-
|
|
1091
|
-
if (versionCompare === 0) {
|
|
1092
|
-
console.log(chalk.yellow(' ⚠️ Same version already installed'));
|
|
1093
|
-
|
|
1094
|
-
const choices = [];
|
|
1095
|
-
if (hasPackIntegrityIssues) {
|
|
1096
|
-
choices.push({ name: 'Repair (restore missing/modified files)', value: 'repair' });
|
|
1097
|
-
}
|
|
1098
|
-
choices.push(
|
|
1099
|
-
{ name: 'Force reinstall (overwrite)', value: 'overwrite' },
|
|
1100
|
-
{ name: 'Skip this expansion pack', value: 'skip' },
|
|
1101
|
-
{ name: 'Cancel installation', value: 'cancel' }
|
|
1102
|
-
);
|
|
1103
|
-
|
|
1104
|
-
const { action } = await inquirer.prompt([{
|
|
1105
|
-
type: 'list',
|
|
1106
|
-
name: 'action',
|
|
1107
|
-
message: `${pack.name} v${pack.version} is already installed. What would you like to do?`,
|
|
1108
|
-
choices: choices
|
|
1109
|
-
}]);
|
|
1110
|
-
|
|
1111
|
-
if (action === 'skip') {
|
|
1112
|
-
spinner.start();
|
|
1113
|
-
continue;
|
|
1114
|
-
} else if (action === 'cancel') {
|
|
1115
|
-
console.log(chalk.red('Installation cancelled.'));
|
|
1116
|
-
process.exit(0);
|
|
1117
|
-
} else if (action === 'repair') {
|
|
1118
|
-
// Repair the expansion pack
|
|
1119
|
-
await this.repairExpansionPack(installDir, packId, pack, packIntegrity, spinner);
|
|
1120
|
-
continue;
|
|
1121
|
-
}
|
|
1122
|
-
} else if (versionCompare < 0) {
|
|
1123
|
-
console.log(chalk.cyan(' ⬆️ Upgrade available'));
|
|
1124
|
-
|
|
1125
|
-
const { proceed } = await inquirer.prompt([{
|
|
1126
|
-
type: 'confirm',
|
|
1127
|
-
name: 'proceed',
|
|
1128
|
-
message: `Upgrade ${pack.name} from v${existingManifest.version} to v${pack.version}?`,
|
|
1129
|
-
default: true
|
|
1130
|
-
}]);
|
|
1131
|
-
|
|
1132
|
-
if (!proceed) {
|
|
1133
|
-
spinner.start();
|
|
1134
|
-
continue;
|
|
1135
|
-
}
|
|
1136
|
-
} else {
|
|
1137
|
-
console.log(chalk.yellow(' ⬇️ Installed version is newer than available version'));
|
|
1138
|
-
|
|
1139
|
-
const { action } = await inquirer.prompt([{
|
|
1140
|
-
type: 'list',
|
|
1141
|
-
name: 'action',
|
|
1142
|
-
message: 'What would you like to do?',
|
|
1143
|
-
choices: [
|
|
1144
|
-
{ name: 'Keep current version', value: 'skip' },
|
|
1145
|
-
{ name: 'Downgrade to available version', value: 'downgrade' },
|
|
1146
|
-
{ name: 'Cancel installation', value: 'cancel' }
|
|
1147
|
-
]
|
|
1148
|
-
}]);
|
|
1149
|
-
|
|
1150
|
-
if (action === 'skip') {
|
|
1151
|
-
spinner.start();
|
|
1152
|
-
continue;
|
|
1153
|
-
} else if (action === 'cancel') {
|
|
1154
|
-
console.log(chalk.red('Installation cancelled.'));
|
|
1155
|
-
process.exit(0);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
// If we get here, we're proceeding with installation
|
|
1160
|
-
spinner.start(`Removing old ${pack.name} installation...`);
|
|
1161
|
-
await fileManager.removeDirectory(expansionDotFolder);
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
const expansionPackDir = pack.packPath;
|
|
1165
|
-
|
|
1166
|
-
// Ensure dedicated dot folder exists for this expansion pack
|
|
1167
|
-
expansionDotFolder = path.join(installDir, `.${packId}`);
|
|
1168
|
-
await fileManager.ensureDirectory(expansionDotFolder);
|
|
1169
|
-
|
|
1170
|
-
// Define the folders to copy from expansion packs
|
|
1171
|
-
const foldersToSync = [
|
|
1172
|
-
'agents',
|
|
1173
|
-
'agent-teams',
|
|
1174
|
-
'templates',
|
|
1175
|
-
'tasks',
|
|
1176
|
-
'checklists',
|
|
1177
|
-
'workflows',
|
|
1178
|
-
'data',
|
|
1179
|
-
'utils',
|
|
1180
|
-
'schemas'
|
|
1181
|
-
];
|
|
1182
|
-
|
|
1183
|
-
// Copy each folder if it exists
|
|
1184
|
-
for (const folder of foldersToSync) {
|
|
1185
|
-
const sourceFolder = path.join(expansionPackDir, folder);
|
|
1186
|
-
|
|
1187
|
-
// Check if folder exists in expansion pack
|
|
1188
|
-
if (await fileManager.pathExists(sourceFolder)) {
|
|
1189
|
-
// Get all files in this folder
|
|
1190
|
-
const files = glob.sync('**/*', {
|
|
1191
|
-
cwd: sourceFolder,
|
|
1192
|
-
nodir: true
|
|
1193
|
-
});
|
|
1194
|
-
|
|
1195
|
-
// Copy each file to the expansion pack's dot folder with {root} replacement
|
|
1196
|
-
for (const file of files) {
|
|
1197
|
-
const sourcePath = path.join(sourceFolder, file);
|
|
1198
|
-
const destPath = path.join(expansionDotFolder, folder, file);
|
|
1199
|
-
|
|
1200
|
-
const needsRootReplacement = file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml');
|
|
1201
|
-
let success = false;
|
|
1202
|
-
|
|
1203
|
-
if (needsRootReplacement) {
|
|
1204
|
-
success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, `.${packId}`);
|
|
1205
|
-
} else {
|
|
1206
|
-
success = await fileManager.copyFile(sourcePath, destPath);
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
if (success) {
|
|
1210
|
-
installedFiles.push(path.join(`.${packId}`, folder, file));
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
// Copy config.yaml with {root} replacement
|
|
1217
|
-
const configPath = path.join(expansionPackDir, 'config.yaml');
|
|
1218
|
-
if (await fileManager.pathExists(configPath)) {
|
|
1219
|
-
const configDestPath = path.join(expansionDotFolder, 'config.yaml');
|
|
1220
|
-
if (await fileManager.copyFileWithRootReplacement(configPath, configDestPath, `.${packId}`)) {
|
|
1221
|
-
installedFiles.push(path.join(`.${packId}`, 'config.yaml'));
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
// Copy README if it exists with {root} replacement
|
|
1226
|
-
const readmePath = path.join(expansionPackDir, 'README.md');
|
|
1227
|
-
if (await fileManager.pathExists(readmePath)) {
|
|
1228
|
-
const readmeDestPath = path.join(expansionDotFolder, 'README.md');
|
|
1229
|
-
if (await fileManager.copyFileWithRootReplacement(readmePath, readmeDestPath, `.${packId}`)) {
|
|
1230
|
-
installedFiles.push(path.join(`.${packId}`, 'README.md'));
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// Copy common/ items to expansion pack folder
|
|
1235
|
-
spinner.text = `Copying common utilities to ${packId}...`;
|
|
1236
|
-
await this.copyCommonItems(installDir, `.${packId}`, spinner);
|
|
1237
|
-
|
|
1238
|
-
// Check and resolve core dependencies
|
|
1239
|
-
await this.resolveExpansionPackCoreDependencies(installDir, expansionDotFolder, packId, spinner);
|
|
1240
|
-
|
|
1241
|
-
// Check and resolve core agents referenced by teams
|
|
1242
|
-
await this.resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner);
|
|
1243
|
-
|
|
1244
|
-
// Create manifest for this expansion pack
|
|
1245
|
-
spinner.text = `Creating manifest for ${packId}...`;
|
|
1246
|
-
const expansionConfig = {
|
|
1247
|
-
installType: 'expansion-pack',
|
|
1248
|
-
expansionPackId: packId,
|
|
1249
|
-
expansionPackName: pack.name,
|
|
1250
|
-
expansionPackVersion: pack.version,
|
|
1251
|
-
ides: config.ides || [] // Use ides_setup instead of ide_setup
|
|
1252
|
-
};
|
|
1253
|
-
|
|
1254
|
-
// Get all files installed in this expansion pack
|
|
1255
|
-
const expansionPackFiles = glob.sync('**/*', {
|
|
1256
|
-
cwd: expansionDotFolder,
|
|
1257
|
-
nodir: true
|
|
1258
|
-
}).map(f => path.join(`.${packId}`, f));
|
|
1259
|
-
|
|
1260
|
-
await fileManager.createExpansionPackManifest(installDir, packId, expansionConfig, expansionPackFiles);
|
|
1261
|
-
|
|
1262
|
-
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name} to ${`.${packId}`}`));
|
|
1263
|
-
} catch (error) {
|
|
1264
|
-
console.error(chalk.red(`Failed to install expansion pack ${packId}: ${error.message}`));
|
|
1265
|
-
console.error(chalk.red(`Stack trace: ${error.stack}`));
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
return installedFiles;
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
async resolveExpansionPackCoreDependencies(installDir, expansionDotFolder, packId, spinner) {
|
|
1273
|
-
const glob = require('glob');
|
|
1274
|
-
const yaml = require('js-yaml');
|
|
1275
|
-
const fs = require('fs').promises;
|
|
1276
|
-
|
|
1277
|
-
// Find all agent files in the expansion pack
|
|
1278
|
-
const agentFiles = glob.sync('agents/*.md', {
|
|
1279
|
-
cwd: expansionDotFolder
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
for (const agentFile of agentFiles) {
|
|
1283
|
-
const agentPath = path.join(expansionDotFolder, agentFile);
|
|
1284
|
-
const agentContent = await fs.readFile(agentPath, 'utf8');
|
|
1285
|
-
|
|
1286
|
-
// Extract YAML frontmatter to check dependencies
|
|
1287
|
-
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1288
|
-
if (yamlContent) {
|
|
1289
|
-
try {
|
|
1290
|
-
const agentConfig = yaml.load(yamlContent);
|
|
1291
|
-
const dependencies = agentConfig.dependencies || {};
|
|
1292
|
-
|
|
1293
|
-
// Check for core dependencies (those that don't exist in the expansion pack)
|
|
1294
|
-
for (const depType of ['tasks', 'templates', 'checklists', 'workflows', 'utils', 'data']) {
|
|
1295
|
-
const deps = dependencies[depType] || [];
|
|
1296
|
-
|
|
1297
|
-
for (const dep of deps) {
|
|
1298
|
-
const depFileName = dep.endsWith('.md') ? dep : `${dep}.md`;
|
|
1299
|
-
const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1300
|
-
|
|
1301
|
-
// Check if dependency exists in expansion pack
|
|
1302
|
-
if (!(await fileManager.pathExists(expansionDepPath))) {
|
|
1303
|
-
// Try to find it in core
|
|
1304
|
-
const coreDepPath = path.join(configLoader.getBmadCorePath(), depType, depFileName);
|
|
1305
|
-
|
|
1306
|
-
if (await fileManager.pathExists(coreDepPath)) {
|
|
1307
|
-
spinner.text = `Copying core dependency ${dep} for ${packId}...`;
|
|
1308
|
-
|
|
1309
|
-
// Copy from core to expansion pack dot folder with {root} replacement
|
|
1310
|
-
const destPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1311
|
-
await fileManager.copyFileWithRootReplacement(coreDepPath, destPath, `.${packId}`);
|
|
1312
|
-
|
|
1313
|
-
console.log(chalk.dim(` Added core dependency: ${depType}/${depFileName}`));
|
|
1314
|
-
} else {
|
|
1315
|
-
console.warn(chalk.yellow(` Warning: Dependency ${depType}/${dep} not found in core or expansion pack`));
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
} catch (error) {
|
|
1321
|
-
console.warn(chalk.yellow(` Warning: Could not parse agent dependencies: ${error.message}`));
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
async resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner) {
|
|
1328
|
-
const glob = require('glob');
|
|
1329
|
-
const yaml = require('js-yaml');
|
|
1330
|
-
const fs = require('fs').promises;
|
|
1331
|
-
|
|
1332
|
-
// Find all team files in the expansion pack
|
|
1333
|
-
const teamFiles = glob.sync('agent-teams/*.yaml', {
|
|
1334
|
-
cwd: expansionDotFolder
|
|
1335
|
-
});
|
|
1336
|
-
|
|
1337
|
-
// Also get existing agents in the expansion pack
|
|
1338
|
-
const existingAgents = new Set();
|
|
1339
|
-
const agentFiles = glob.sync('agents/*.md', {
|
|
1340
|
-
cwd: expansionDotFolder
|
|
1341
|
-
});
|
|
1342
|
-
for (const agentFile of agentFiles) {
|
|
1343
|
-
const agentName = path.basename(agentFile, '.md');
|
|
1344
|
-
existingAgents.add(agentName);
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
// Process each team file
|
|
1348
|
-
for (const teamFile of teamFiles) {
|
|
1349
|
-
const teamPath = path.join(expansionDotFolder, teamFile);
|
|
1350
|
-
const teamContent = await fs.readFile(teamPath, 'utf8');
|
|
1351
|
-
|
|
1352
|
-
try {
|
|
1353
|
-
const teamConfig = yaml.load(teamContent);
|
|
1354
|
-
const agents = teamConfig.agents || [];
|
|
1355
|
-
|
|
1356
|
-
// Add bmad-orchestrator if not present (required for all teams)
|
|
1357
|
-
if (!agents.includes('bmad-orchestrator')) {
|
|
1358
|
-
agents.unshift('bmad-orchestrator');
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
// Check each agent in the team
|
|
1362
|
-
for (const agentId of agents) {
|
|
1363
|
-
if (!existingAgents.has(agentId)) {
|
|
1364
|
-
// Agent not in expansion pack, try to get from core
|
|
1365
|
-
const coreAgentPath = path.join(configLoader.getBmadCorePath(), 'agents', `${agentId}.md`);
|
|
1366
|
-
|
|
1367
|
-
if (await fileManager.pathExists(coreAgentPath)) {
|
|
1368
|
-
spinner.text = `Copying core agent ${agentId} for ${packId}...`;
|
|
1369
|
-
|
|
1370
|
-
// Copy agent file with {root} replacement
|
|
1371
|
-
const destPath = path.join(expansionDotFolder, 'agents', `${agentId}.md`);
|
|
1372
|
-
await fileManager.copyFileWithRootReplacement(coreAgentPath, destPath, `.${packId}`);
|
|
1373
|
-
existingAgents.add(agentId);
|
|
1374
|
-
|
|
1375
|
-
console.log(chalk.dim(` Added core agent: ${agentId}`));
|
|
1376
|
-
|
|
1377
|
-
// Now resolve this agent's dependencies too
|
|
1378
|
-
const agentContent = await fs.readFile(coreAgentPath, 'utf8');
|
|
1379
|
-
const yamlContent = extractYamlFromAgent(agentContent, true);
|
|
1380
|
-
|
|
1381
|
-
if (yamlContent) {
|
|
1382
|
-
try {
|
|
1383
|
-
|
|
1384
|
-
const agentConfig = yaml.load(yamlContent);
|
|
1385
|
-
const dependencies = agentConfig.dependencies || {};
|
|
1386
|
-
|
|
1387
|
-
// Copy all dependencies for this agent
|
|
1388
|
-
for (const depType of ['tasks', 'templates', 'checklists', 'workflows', 'utils', 'data']) {
|
|
1389
|
-
const deps = dependencies[depType] || [];
|
|
1390
|
-
|
|
1391
|
-
for (const dep of deps) {
|
|
1392
|
-
const depFileName = dep.endsWith('.md') || dep.endsWith('.yaml') ? dep : `${dep}.md`;
|
|
1393
|
-
const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1394
|
-
|
|
1395
|
-
// Check if dependency exists in expansion pack
|
|
1396
|
-
if (!(await fileManager.pathExists(expansionDepPath))) {
|
|
1397
|
-
// Try to find it in core
|
|
1398
|
-
const coreDepPath = path.join(configLoader.getBmadCorePath(), depType, depFileName);
|
|
1399
|
-
|
|
1400
|
-
if (await fileManager.pathExists(coreDepPath)) {
|
|
1401
|
-
const destDepPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1402
|
-
await fileManager.copyFileWithRootReplacement(coreDepPath, destDepPath, `.${packId}`);
|
|
1403
|
-
console.log(chalk.dim(` Added agent dependency: ${depType}/${depFileName}`));
|
|
1404
|
-
} else {
|
|
1405
|
-
// Try common folder
|
|
1406
|
-
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
|
1407
|
-
const commonDepPath = path.join(sourceBase, 'common', depType, depFileName);
|
|
1408
|
-
if (await fileManager.pathExists(commonDepPath)) {
|
|
1409
|
-
const destDepPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1410
|
-
await fileManager.copyFile(commonDepPath, destDepPath);
|
|
1411
|
-
console.log(chalk.dim(` Added agent dependency from common: ${depType}/${depFileName}`));
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
} catch (error) {
|
|
1418
|
-
console.warn(chalk.yellow(` Warning: Could not parse agent ${agentId} dependencies: ${error.message}`));
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
} else {
|
|
1422
|
-
console.warn(chalk.yellow(` Warning: Core agent ${agentId} not found for team ${path.basename(teamFile, '.yaml')}`));
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
} catch (error) {
|
|
1427
|
-
console.warn(chalk.yellow(` Warning: Could not parse team file ${teamFile}: ${error.message}`));
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
getWebBundleInfo(config) {
|
|
1433
|
-
const webBundleType = config.webBundleType || 'all';
|
|
1434
|
-
|
|
1435
|
-
switch (webBundleType) {
|
|
1436
|
-
case 'all':
|
|
1437
|
-
return 'all bundles';
|
|
1438
|
-
case 'agents':
|
|
1439
|
-
return 'individual agents only';
|
|
1440
|
-
case 'teams':
|
|
1441
|
-
return config.selectedWebBundleTeams ?
|
|
1442
|
-
`teams: ${config.selectedWebBundleTeams.join(', ')}` :
|
|
1443
|
-
'selected teams';
|
|
1444
|
-
case 'custom':
|
|
1445
|
-
const parts = [];
|
|
1446
|
-
if (config.selectedWebBundleTeams && config.selectedWebBundleTeams.length > 0) {
|
|
1447
|
-
parts.push(`teams: ${config.selectedWebBundleTeams.join(', ')}`);
|
|
1448
|
-
}
|
|
1449
|
-
if (config.includeIndividualAgents) {
|
|
1450
|
-
parts.push('individual agents');
|
|
1451
|
-
}
|
|
1452
|
-
return parts.length > 0 ? parts.join(' + ') : 'custom selection';
|
|
1453
|
-
default:
|
|
1454
|
-
return 'selected bundles';
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
async installWebBundles(webBundlesDirectory, config, spinner) {
|
|
1459
|
-
// Ensure modules are initialized
|
|
1460
|
-
await initializeModules();
|
|
1461
|
-
|
|
1462
|
-
try {
|
|
1463
|
-
// Find the dist directory in the BMad installation
|
|
1464
|
-
const distDir = configLoader.getDistPath();
|
|
1465
|
-
|
|
1466
|
-
if (!(await fileManager.pathExists(distDir))) {
|
|
1467
|
-
console.warn(chalk.yellow('Web bundles not found. Run "npm run build" to generate them.'));
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
// Ensure web bundles directory exists
|
|
1472
|
-
await fileManager.ensureDirectory(webBundlesDirectory);
|
|
1473
|
-
|
|
1474
|
-
const webBundleType = config.webBundleType || 'all';
|
|
1475
|
-
|
|
1476
|
-
if (webBundleType === 'all') {
|
|
1477
|
-
// Copy the entire dist directory structure
|
|
1478
|
-
await fileManager.copyDirectory(distDir, webBundlesDirectory);
|
|
1479
|
-
console.log(chalk.green(`✓ Installed all web bundles to: ${webBundlesDirectory}`));
|
|
1480
|
-
} else {
|
|
1481
|
-
let copiedCount = 0;
|
|
1482
|
-
|
|
1483
|
-
// Copy specific selections based on type
|
|
1484
|
-
if (webBundleType === 'agents' || (webBundleType === 'custom' && config.includeIndividualAgents)) {
|
|
1485
|
-
const agentsSource = path.join(distDir, 'agents');
|
|
1486
|
-
const agentsTarget = path.join(webBundlesDirectory, 'agents');
|
|
1487
|
-
if (await fileManager.pathExists(agentsSource)) {
|
|
1488
|
-
await fileManager.copyDirectory(agentsSource, agentsTarget);
|
|
1489
|
-
console.log(chalk.green(`✓ Copied individual agent bundles`));
|
|
1490
|
-
copiedCount += 10; // Approximate count for agents
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
if (webBundleType === 'teams' || webBundleType === 'custom') {
|
|
1495
|
-
if (config.selectedWebBundleTeams && config.selectedWebBundleTeams.length > 0) {
|
|
1496
|
-
const teamsSource = path.join(distDir, 'teams');
|
|
1497
|
-
const teamsTarget = path.join(webBundlesDirectory, 'teams');
|
|
1498
|
-
await fileManager.ensureDirectory(teamsTarget);
|
|
1499
|
-
|
|
1500
|
-
for (const teamId of config.selectedWebBundleTeams) {
|
|
1501
|
-
const teamFile = `${teamId}.txt`;
|
|
1502
|
-
const sourcePath = path.join(teamsSource, teamFile);
|
|
1503
|
-
const targetPath = path.join(teamsTarget, teamFile);
|
|
1504
|
-
|
|
1505
|
-
if (await fileManager.pathExists(sourcePath)) {
|
|
1506
|
-
await fileManager.copyFile(sourcePath, targetPath);
|
|
1507
|
-
copiedCount++;
|
|
1508
|
-
console.log(chalk.green(`✓ Copied team bundle: ${teamId}`));
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
// Always copy expansion packs if they exist
|
|
1515
|
-
const expansionSource = path.join(distDir, 'expansion-packs');
|
|
1516
|
-
const expansionTarget = path.join(webBundlesDirectory, 'expansion-packs');
|
|
1517
|
-
if (await fileManager.pathExists(expansionSource)) {
|
|
1518
|
-
await fileManager.copyDirectory(expansionSource, expansionTarget);
|
|
1519
|
-
console.log(chalk.green(`✓ Copied expansion pack bundles`));
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
console.log(chalk.green(`✓ Installed ${copiedCount} selected web bundles to: ${webBundlesDirectory}`));
|
|
1523
|
-
}
|
|
1524
|
-
} catch (error) {
|
|
1525
|
-
console.error(chalk.red(`Failed to install web bundles: ${error.message}`));
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
async copyCommonItems(installDir, targetSubdir, spinner) {
|
|
1530
|
-
// Ensure modules are initialized
|
|
1531
|
-
await initializeModules();
|
|
1532
|
-
|
|
1533
|
-
const glob = require('glob');
|
|
1534
|
-
const fs = require('fs').promises;
|
|
1535
|
-
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
|
1536
|
-
const commonPath = path.join(sourceBase, 'common');
|
|
1537
|
-
const targetPath = path.join(installDir, targetSubdir);
|
|
1538
|
-
const copiedFiles = [];
|
|
1539
|
-
|
|
1540
|
-
// Check if common/ exists
|
|
1541
|
-
if (!(await fileManager.pathExists(commonPath))) {
|
|
1542
|
-
console.warn(chalk.yellow('Warning: common/ folder not found'));
|
|
1543
|
-
return copiedFiles;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
// Copy all items from common/ to target
|
|
1547
|
-
const commonItems = glob.sync('**/*', {
|
|
1548
|
-
cwd: commonPath,
|
|
1549
|
-
nodir: true
|
|
1550
|
-
});
|
|
1551
|
-
|
|
1552
|
-
for (const item of commonItems) {
|
|
1553
|
-
const sourcePath = path.join(commonPath, item);
|
|
1554
|
-
const destPath = path.join(targetPath, item);
|
|
1555
|
-
|
|
1556
|
-
// Read the file content
|
|
1557
|
-
const content = await fs.readFile(sourcePath, 'utf8');
|
|
1558
|
-
|
|
1559
|
-
// Replace {root} with the target subdirectory
|
|
1560
|
-
const updatedContent = content.replace(/\{root\}/g, targetSubdir);
|
|
1561
|
-
|
|
1562
|
-
// Ensure directory exists
|
|
1563
|
-
await fileManager.ensureDirectory(path.dirname(destPath));
|
|
1564
|
-
|
|
1565
|
-
// Write the updated content
|
|
1566
|
-
await fs.writeFile(destPath, updatedContent, 'utf8');
|
|
1567
|
-
copiedFiles.push(path.join(targetSubdir, item));
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
console.log(chalk.dim(` Added ${commonItems.length} common utilities`));
|
|
1571
|
-
return copiedFiles;
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
async detectExpansionPacks(installDir) {
|
|
1575
|
-
const expansionPacks = {};
|
|
1576
|
-
const glob = require("glob");
|
|
1577
|
-
|
|
1578
|
-
// Find all dot folders that might be expansion packs
|
|
1579
|
-
const dotFolders = glob.sync(".*", {
|
|
1580
|
-
cwd: installDir,
|
|
1581
|
-
ignore: [".git", ".git/**", ".bmad-core", ".bmad-core/**"],
|
|
1582
|
-
});
|
|
1583
|
-
|
|
1584
|
-
for (const folder of dotFolders) {
|
|
1585
|
-
const folderPath = path.join(installDir, folder);
|
|
1586
|
-
const stats = await fileManager.pathExists(folderPath);
|
|
1587
|
-
|
|
1588
|
-
if (stats) {
|
|
1589
|
-
// Check if it has a manifest
|
|
1590
|
-
const manifestPath = path.join(folderPath, "install-manifest.yaml");
|
|
1591
|
-
if (await fileManager.pathExists(manifestPath)) {
|
|
1592
|
-
const manifest = await fileManager.readExpansionPackManifest(installDir, folder.substring(1));
|
|
1593
|
-
if (manifest) {
|
|
1594
|
-
expansionPacks[folder.substring(1)] = {
|
|
1595
|
-
path: folderPath,
|
|
1596
|
-
manifest: manifest,
|
|
1597
|
-
hasManifest: true
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
} else {
|
|
1601
|
-
// Check if it has a config.yaml (expansion pack without manifest)
|
|
1602
|
-
const configPath = path.join(folderPath, "config.yaml");
|
|
1603
|
-
if (await fileManager.pathExists(configPath)) {
|
|
1604
|
-
expansionPacks[folder.substring(1)] = {
|
|
1605
|
-
path: folderPath,
|
|
1606
|
-
manifest: null,
|
|
1607
|
-
hasManifest: false
|
|
1608
|
-
};
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
return expansionPacks;
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
async repairExpansionPack(installDir, packId, pack, integrity, spinner) {
|
|
1618
|
-
spinner.start(`Repairing ${pack.name}...`);
|
|
1619
|
-
|
|
1620
|
-
try {
|
|
1621
|
-
const expansionDotFolder = path.join(installDir, `.${packId}`);
|
|
1622
|
-
|
|
1623
|
-
// Back up modified files
|
|
1624
|
-
if (integrity.modified.length > 0) {
|
|
1625
|
-
spinner.text = "Backing up modified files...";
|
|
1626
|
-
for (const file of integrity.modified) {
|
|
1627
|
-
const filePath = path.join(installDir, file);
|
|
1628
|
-
if (await fileManager.pathExists(filePath)) {
|
|
1629
|
-
const backupPath = await fileManager.backupFile(filePath);
|
|
1630
|
-
console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`));
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
// Restore missing and modified files
|
|
1636
|
-
spinner.text = "Restoring files...";
|
|
1637
|
-
const filesToRestore = [...integrity.missing, ...integrity.modified];
|
|
1638
|
-
|
|
1639
|
-
for (const file of filesToRestore) {
|
|
1640
|
-
// Skip the manifest file itself
|
|
1641
|
-
if (file.endsWith('install-manifest.yaml')) continue;
|
|
1642
|
-
|
|
1643
|
-
const relativePath = file.replace(`.${packId}/`, '');
|
|
1644
|
-
const sourcePath = path.join(pack.packPath, relativePath);
|
|
1645
|
-
const destPath = path.join(installDir, file);
|
|
1646
|
-
|
|
1647
|
-
// Check if this is a common/ file that needs special processing
|
|
1648
|
-
const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
|
|
1649
|
-
const commonSourcePath = path.join(commonBase, 'common', relativePath);
|
|
1650
|
-
|
|
1651
|
-
if (await fileManager.pathExists(commonSourcePath)) {
|
|
1652
|
-
// This is a common/ file - needs template processing
|
|
1653
|
-
const fs = require('fs').promises;
|
|
1654
|
-
const content = await fs.readFile(commonSourcePath, 'utf8');
|
|
1655
|
-
const updatedContent = content.replace(/\{root\}/g, `.${packId}`);
|
|
1656
|
-
await fileManager.ensureDirectory(path.dirname(destPath));
|
|
1657
|
-
await fs.writeFile(destPath, updatedContent, 'utf8');
|
|
1658
|
-
spinner.text = `Restored: ${file}`;
|
|
1659
|
-
} else if (await fileManager.pathExists(sourcePath)) {
|
|
1660
|
-
// Regular file from expansion pack
|
|
1661
|
-
await fileManager.copyFile(sourcePath, destPath);
|
|
1662
|
-
spinner.text = `Restored: ${file}`;
|
|
1663
|
-
} else {
|
|
1664
|
-
console.warn(chalk.yellow(` Warning: Source file not found: ${file}`));
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
spinner.succeed(`${pack.name} repaired successfully!`);
|
|
1669
|
-
|
|
1670
|
-
// Show summary
|
|
1671
|
-
console.log(chalk.green(`\n✓ ${pack.name} repaired!`));
|
|
1672
|
-
if (integrity.missing.length > 0) {
|
|
1673
|
-
console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
|
|
1674
|
-
}
|
|
1675
|
-
if (integrity.modified.length > 0) {
|
|
1676
|
-
console.log(chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`));
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
} catch (error) {
|
|
1680
|
-
spinner.fail(`Failed to repair ${pack.name}`);
|
|
1681
|
-
console.error(chalk.red(`Error: ${error.message}`));
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
compareVersions(v1, v2) {
|
|
1686
|
-
// Simple semver comparison
|
|
1687
|
-
const parts1 = v1.split('.').map(Number);
|
|
1688
|
-
const parts2 = v2.split('.').map(Number);
|
|
1689
|
-
|
|
1690
|
-
for (let i = 0; i < 3; i++) {
|
|
1691
|
-
const part1 = parts1[i] || 0;
|
|
1692
|
-
const part2 = parts2[i] || 0;
|
|
1693
|
-
|
|
1694
|
-
if (part1 > part2) return 1;
|
|
1695
|
-
if (part1 < part2) return -1;
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
return 0;
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
async cleanupLegacyYmlFiles(installDir, spinner) {
|
|
1702
|
-
const glob = require('glob');
|
|
1703
|
-
const fs = require('fs').promises;
|
|
1704
|
-
|
|
1705
|
-
try {
|
|
1706
|
-
// Find all .yml files in the installation directory
|
|
1707
|
-
const ymlFiles = glob.sync('**/*.yml', {
|
|
1708
|
-
cwd: installDir,
|
|
1709
|
-
ignore: ['**/node_modules/**', '**/.git/**']
|
|
1710
|
-
});
|
|
1711
|
-
|
|
1712
|
-
let deletedCount = 0;
|
|
1713
|
-
|
|
1714
|
-
for (const ymlFile of ymlFiles) {
|
|
1715
|
-
// Check if corresponding .yaml file exists
|
|
1716
|
-
const yamlFile = ymlFile.replace(/\.yml$/, '.yaml');
|
|
1717
|
-
const ymlPath = path.join(installDir, ymlFile);
|
|
1718
|
-
const yamlPath = path.join(installDir, yamlFile);
|
|
1719
|
-
|
|
1720
|
-
if (await fileManager.pathExists(yamlPath)) {
|
|
1721
|
-
// .yaml counterpart exists, delete the .yml file
|
|
1722
|
-
await fs.unlink(ymlPath);
|
|
1723
|
-
deletedCount++;
|
|
1724
|
-
console.log(chalk.dim(` Removed legacy: ${ymlFile} (replaced by ${yamlFile})`));
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
if (deletedCount > 0) {
|
|
1729
|
-
console.log(chalk.green(`✓ Cleaned up ${deletedCount} legacy .yml files`));
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
} catch (error) {
|
|
1733
|
-
console.warn(chalk.yellow(`Warning: Could not cleanup legacy .yml files: ${error.message}`));
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
627
|
async findInstallation() {
|
|
1738
628
|
// Look for .bmad-core in current directory or parent directories
|
|
1739
629
|
let currentDir = process.cwd();
|
|
1740
630
|
|
|
1741
631
|
while (currentDir !== path.dirname(currentDir)) {
|
|
1742
632
|
const bmadDir = path.join(currentDir, ".bmad-core");
|
|
1743
|
-
const manifestPath = path.join(bmadDir, "install-manifest.
|
|
633
|
+
const manifestPath = path.join(bmadDir, "install-manifest.yml");
|
|
1744
634
|
|
|
1745
635
|
if (await fileManager.pathExists(manifestPath)) {
|
|
1746
636
|
return bmadDir;
|
|
@@ -1751,7 +641,7 @@ class Installer {
|
|
|
1751
641
|
|
|
1752
642
|
// Also check if we're inside a .bmad-core directory
|
|
1753
643
|
if (path.basename(process.cwd()) === ".bmad-core") {
|
|
1754
|
-
const manifestPath = path.join(process.cwd(), "install-manifest.
|
|
644
|
+
const manifestPath = path.join(process.cwd(), "install-manifest.yml");
|
|
1755
645
|
if (await fileManager.pathExists(manifestPath)) {
|
|
1756
646
|
return process.cwd();
|
|
1757
647
|
}
|