bmad-method 4.37.0 → 4.39.1
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/ISSUE_TEMPLATE/bug_report.md +3 -3
- package/.github/ISSUE_TEMPLATE/feature_request.md +3 -3
- package/.github/workflows/discord.yaml +11 -2
- package/.github/workflows/format-check.yaml +42 -0
- package/.github/workflows/manual-release.yaml +173 -0
- package/.husky/pre-commit +3 -0
- package/.vscode/settings.json +26 -1
- package/CHANGELOG.md +2 -23
- package/README.md +2 -0
- package/bmad-core/agent-teams/team-all.yaml +1 -1
- package/bmad-core/agents/analyst.md +16 -15
- package/bmad-core/agents/architect.md +11 -11
- package/bmad-core/agents/bmad-master.md +23 -22
- package/bmad-core/agents/bmad-orchestrator.md +13 -17
- package/bmad-core/agents/dev.md +14 -11
- package/bmad-core/agents/pm.md +15 -14
- package/bmad-core/agents/po.md +9 -8
- package/bmad-core/agents/qa.md +42 -22
- package/bmad-core/agents/sm.md +7 -6
- package/bmad-core/agents/ux-expert.md +6 -5
- package/bmad-core/core-config.yaml +2 -0
- package/bmad-core/data/bmad-kb.md +1 -1
- package/bmad-core/data/test-levels-framework.md +146 -0
- package/bmad-core/data/test-priorities-matrix.md +172 -0
- package/bmad-core/tasks/apply-qa-fixes.md +148 -0
- package/bmad-core/tasks/facilitate-brainstorming-session.md +1 -1
- package/bmad-core/tasks/nfr-assess.md +343 -0
- package/bmad-core/tasks/qa-gate.md +161 -0
- package/bmad-core/tasks/review-story.md +234 -74
- package/bmad-core/tasks/risk-profile.md +353 -0
- package/bmad-core/tasks/test-design.md +174 -0
- package/bmad-core/tasks/trace-requirements.md +264 -0
- package/bmad-core/templates/architecture-tmpl.yaml +49 -49
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +5 -5
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +31 -31
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +13 -13
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +19 -6
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +21 -9
- package/bmad-core/templates/front-end-spec-tmpl.yaml +24 -24
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +122 -104
- package/bmad-core/templates/market-research-tmpl.yaml +2 -2
- package/bmad-core/templates/prd-tmpl.yaml +9 -9
- package/bmad-core/templates/project-brief-tmpl.yaml +4 -4
- package/bmad-core/templates/qa-gate-tmpl.yaml +102 -0
- package/bmad-core/templates/story-tmpl.yaml +12 -12
- package/bmad-core/workflows/brownfield-fullstack.yaml +9 -9
- package/bmad-core/workflows/brownfield-service.yaml +1 -1
- package/bmad-core/workflows/brownfield-ui.yaml +1 -1
- package/bmad-core/workflows/greenfield-fullstack.yaml +1 -1
- package/bmad-core/workflows/greenfield-service.yaml +1 -1
- package/bmad-core/workflows/greenfield-ui.yaml +1 -1
- package/common/utils/bmad-doc-template.md +5 -5
- package/dist/agents/analyst.txt +1086 -1079
- package/dist/agents/architect.txt +1534 -1526
- package/dist/agents/bmad-master.txt +646 -632
- package/dist/agents/bmad-orchestrator.txt +40 -18
- package/dist/agents/dev.txt +158 -19
- package/dist/agents/pm.txt +1082 -1107
- package/dist/agents/po.txt +314 -332
- package/dist/agents/qa.txt +1754 -151
- package/dist/agents/sm.txt +88 -98
- package/dist/agents/ux-expert.txt +80 -87
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +109 -146
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +75 -86
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +41 -48
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +1903 -1941
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +15 -50
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +149 -195
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +0 -15
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +20 -37
- package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +2660 -2752
- package/dist/expansion-packs/bmad-creative-writing/agents/beta-reader.txt +871 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/book-critic.txt +78 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/character-psychologist.txt +839 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/cover-designer.txt +85 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/dialog-specialist.txt +861 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/editor.txt +796 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/genre-specialist.txt +927 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/narrative-designer.txt +842 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/plot-architect.txt +1126 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/world-builder.txt +864 -0
- package/dist/expansion-packs/bmad-creative-writing/teams/agent-team.txt +5917 -0
- package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +25 -27
- package/dist/teams/team-all.txt +5541 -3768
- package/dist/teams/team-fullstack.txt +3014 -2987
- package/dist/teams/team-ide-minimal.txt +2219 -469
- package/dist/teams/team-no-ui.txt +2993 -2966
- package/docs/enhanced-ide-development-workflow.md +220 -15
- package/docs/user-guide.md +271 -18
- package/docs/versioning-and-releases.md +122 -44
- package/docs/working-in-the-brownfield.md +264 -31
- package/eslint.config.mjs +119 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +4 -4
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +1 -1
- package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +1 -1
- package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +26 -28
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +50 -50
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +23 -23
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +24 -24
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +42 -42
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +5 -5
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +1 -1
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +3 -3
- package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +1 -1
- package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +1 -1
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +23 -23
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +63 -63
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +20 -20
- package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +5 -5
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +1 -1
- package/expansion-packs/bmad-creative-writing/README.md +132 -0
- package/expansion-packs/bmad-creative-writing/agent-teams/agent-team.yaml +19 -0
- package/expansion-packs/bmad-creative-writing/agents/beta-reader.md +91 -0
- package/expansion-packs/bmad-creative-writing/agents/book-critic.md +35 -0
- package/expansion-packs/bmad-creative-writing/agents/character-psychologist.md +90 -0
- package/expansion-packs/bmad-creative-writing/agents/cover-designer.md +41 -0
- package/expansion-packs/bmad-creative-writing/agents/dialog-specialist.md +89 -0
- package/expansion-packs/bmad-creative-writing/agents/editor.md +90 -0
- package/expansion-packs/bmad-creative-writing/agents/genre-specialist.md +92 -0
- package/expansion-packs/bmad-creative-writing/agents/narrative-designer.md +90 -0
- package/expansion-packs/bmad-creative-writing/agents/plot-architect.md +92 -0
- package/expansion-packs/bmad-creative-writing/agents/world-builder.md +91 -0
- package/expansion-packs/bmad-creative-writing/checklists/beta-feedback-closure-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/character-consistency-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/comedic-timing-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/cyberpunk-aesthetic-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/ebook-formatting-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/epic-poetry-meter-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/fantasy-magic-system-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/foreshadowing-payoff-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/genre-tropes-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/historical-accuracy-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/horror-suspense-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/kdp-cover-ready-checklist.md +18 -0
- package/expansion-packs/bmad-creative-writing/checklists/line-edit-quality-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/marketing-copy-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/mystery-clue-trail-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/orbital-mechanics-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/plot-structure-checklist.md +49 -0
- package/expansion-packs/bmad-creative-writing/checklists/publication-readiness-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/romance-emotional-beats-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/scene-quality-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/scifi-technology-plausibility-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/sensitivity-representation-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/steampunk-gadget-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/thriller-pacing-stakes-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/timeline-continuity-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/world-building-continuity-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/ya-appropriateness-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/config.yaml +11 -0
- package/expansion-packs/bmad-creative-writing/data/bmad-kb.md +197 -0
- package/expansion-packs/bmad-creative-writing/data/story-structures.md +58 -0
- package/expansion-packs/bmad-creative-writing/docs/brief.md +183 -0
- package/expansion-packs/bmad-creative-writing/tasks/advanced-elicitation.md +117 -0
- package/expansion-packs/bmad-creative-writing/tasks/analyze-reader-feedback.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/analyze-story-structure.md +55 -0
- package/expansion-packs/bmad-creative-writing/tasks/assemble-kdp-package.md +22 -0
- package/expansion-packs/bmad-creative-writing/tasks/brainstorm-premise.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/build-world.md +17 -0
- package/expansion-packs/bmad-creative-writing/tasks/character-depth-pass.md +15 -0
- package/expansion-packs/bmad-creative-writing/tasks/create-doc.md +101 -0
- package/expansion-packs/bmad-creative-writing/tasks/create-draft-section.md +19 -0
- package/expansion-packs/bmad-creative-writing/tasks/critical-review.md +19 -0
- package/expansion-packs/bmad-creative-writing/tasks/develop-character.md +17 -0
- package/expansion-packs/bmad-creative-writing/tasks/execute-checklist.md +93 -0
- package/expansion-packs/bmad-creative-writing/tasks/expand-premise.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/expand-synopsis.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/final-polish.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/generate-cover-brief.md +18 -0
- package/expansion-packs/bmad-creative-writing/tasks/generate-cover-prompts.md +19 -0
- package/expansion-packs/bmad-creative-writing/tasks/generate-scene-list.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/incorporate-feedback.md +18 -0
- package/expansion-packs/bmad-creative-writing/tasks/outline-scenes.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/provide-feedback.md +17 -0
- package/expansion-packs/bmad-creative-writing/tasks/publish-chapter.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/quick-feedback.md +15 -0
- package/expansion-packs/bmad-creative-writing/tasks/select-next-arc.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/workshop-dialog.md +51 -0
- package/expansion-packs/bmad-creative-writing/templates/beta-feedback-form.yaml +96 -0
- package/expansion-packs/bmad-creative-writing/templates/chapter-draft-tmpl.yaml +81 -0
- package/expansion-packs/bmad-creative-writing/templates/character-profile-tmpl.yaml +92 -0
- package/expansion-packs/bmad-creative-writing/templates/cover-design-brief-tmpl.yaml +97 -0
- package/expansion-packs/bmad-creative-writing/templates/premise-brief-tmpl.yaml +77 -0
- package/expansion-packs/bmad-creative-writing/templates/scene-list-tmpl.yaml +54 -0
- package/expansion-packs/bmad-creative-writing/templates/story-outline-tmpl.yaml +96 -0
- package/expansion-packs/bmad-creative-writing/templates/world-guide-tmpl.yaml +88 -0
- package/expansion-packs/bmad-creative-writing/workflows/book-cover-design-workflow.md +176 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-greenfield-workflow.yaml +58 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-serial-workflow.yaml +51 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-snowflake-workflow.yaml +69 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-writing.yaml +92 -0
- package/expansion-packs/bmad-creative-writing/workflows/screenplay-development.yaml +86 -0
- package/expansion-packs/bmad-creative-writing/workflows/series-planning.yaml +79 -0
- package/expansion-packs/bmad-creative-writing/workflows/short-story-creation.yaml +65 -0
- package/expansion-packs/bmad-infrastructure-devops/config.yaml +1 -1
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +20 -20
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +7 -7
- package/package.json +62 -39
- package/prettier.config.mjs +32 -0
- package/sync-version.sh +23 -0
- package/tools/bmad-npx-wrapper.js +10 -10
- package/tools/builders/web-builder.js +124 -130
- package/tools/bump-all-versions.js +42 -33
- package/tools/bump-expansion-version.js +23 -16
- package/tools/cli.js +10 -12
- package/tools/flattener/aggregate.js +10 -10
- package/tools/flattener/binary.js +44 -17
- package/tools/flattener/discovery.js +19 -18
- package/tools/flattener/files.js +6 -6
- package/tools/flattener/ignoreRules.js +125 -125
- package/tools/flattener/main.js +426 -70
- package/tools/flattener/projectRoot.js +186 -25
- package/tools/flattener/prompts.js +9 -9
- package/tools/flattener/stats.helpers.js +395 -0
- package/tools/flattener/stats.js +64 -14
- package/tools/flattener/test-matrix.js +413 -0
- package/tools/flattener/xml.js +33 -31
- package/tools/installer/bin/bmad.js +156 -113
- package/tools/installer/config/ide-agent-config.yaml +1 -1
- package/tools/installer/config/install.config.yaml +13 -3
- package/tools/installer/lib/config-loader.js +46 -42
- package/tools/installer/lib/file-manager.js +91 -113
- package/tools/installer/lib/ide-base-setup.js +57 -56
- package/tools/installer/lib/ide-setup.js +545 -399
- package/tools/installer/lib/installer.js +875 -714
- package/tools/installer/lib/memory-profiler.js +54 -53
- package/tools/installer/lib/module-manager.js +19 -15
- package/tools/installer/lib/resource-locator.js +26 -28
- package/tools/installer/package.json +19 -19
- package/tools/lib/dependency-resolver.js +26 -30
- package/tools/lib/yaml-utils.js +7 -7
- package/tools/preview-release-notes.js +66 -0
- package/tools/shared/bannerArt.js +3 -3
- package/tools/sync-installer-version.js +7 -9
- package/tools/update-expansion-version.js +14 -15
- package/tools/upgraders/v3-to-v4-upgrader.js +203 -294
- package/tools/version-bump.js +41 -26
- package/tools/yaml-format.js +56 -43
- package/.github/workflows/release.yaml +0 -60
- package/.releaserc.json +0 -21
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +0 -102
- 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 +0 -13
- 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 +0 -13
- 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 +0 -25
- 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 +0 -34
- 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 +0 -70
- 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 +0 -26
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +0 -109
- package/tools/semantic-release-sync-installer.js +0 -30
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const fs = require(
|
|
3
|
-
const chalk = require(
|
|
4
|
-
const ora = require(
|
|
5
|
-
const inquirer = require(
|
|
6
|
-
const fileManager = require(
|
|
7
|
-
const configLoader = require(
|
|
8
|
-
const ideSetup = require(
|
|
9
|
-
const { extractYamlFromAgent } = require(
|
|
10
|
-
const resourceLocator = require(
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const ora = require('ora');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
6
|
+
const fileManager = require('./file-manager');
|
|
7
|
+
const configLoader = require('./config-loader');
|
|
8
|
+
const ideSetup = require('./ide-setup');
|
|
9
|
+
const { extractYamlFromAgent } = require('../../lib/yaml-utils');
|
|
10
|
+
const resourceLocator = require('./resource-locator');
|
|
11
11
|
|
|
12
12
|
class Installer {
|
|
13
13
|
async getCoreVersion() {
|
|
@@ -16,29 +16,29 @@ class Installer {
|
|
|
16
16
|
const packagePath = path.join(__dirname, '..', '..', '..', 'package.json');
|
|
17
17
|
const packageJson = require(packagePath);
|
|
18
18
|
return packageJson.version;
|
|
19
|
-
} catch
|
|
19
|
+
} catch {
|
|
20
20
|
console.warn("Could not read version from package.json, using 'unknown'");
|
|
21
|
-
return
|
|
21
|
+
return 'unknown';
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async install(config) {
|
|
26
|
-
const spinner = ora(
|
|
27
|
-
|
|
26
|
+
const spinner = ora('Analyzing installation directory...').start();
|
|
27
|
+
|
|
28
28
|
try {
|
|
29
29
|
// Store the original CWD where npx was executed
|
|
30
30
|
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
// Resolve installation directory relative to where the user ran the command
|
|
33
|
-
let installDir = path.isAbsolute(config.directory)
|
|
34
|
-
? config.directory
|
|
33
|
+
let installDir = path.isAbsolute(config.directory)
|
|
34
|
+
? config.directory
|
|
35
35
|
: path.resolve(originalCwd, config.directory);
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
if (path.basename(installDir) === '.bmad-core') {
|
|
38
38
|
// If user points directly to .bmad-core, treat its parent as the project root
|
|
39
39
|
installDir = path.dirname(installDir);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
// Log resolved path for clarity
|
|
43
43
|
if (!path.isAbsolute(config.directory)) {
|
|
44
44
|
spinner.text = `Resolving "${config.directory}" to: ${installDir}`;
|
|
@@ -48,7 +48,7 @@ class Installer {
|
|
|
48
48
|
if (!(await fileManager.pathExists(installDir))) {
|
|
49
49
|
spinner.stop();
|
|
50
50
|
console.log(`\nThe directory ${installDir} does not exist.`);
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
const { action } = await inquirer.prompt([
|
|
53
53
|
{
|
|
54
54
|
type: 'list',
|
|
@@ -57,52 +57,61 @@ class Installer {
|
|
|
57
57
|
choices: [
|
|
58
58
|
{
|
|
59
59
|
name: 'Create the directory and continue',
|
|
60
|
-
value: 'create'
|
|
60
|
+
value: 'create',
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
name: 'Choose a different directory',
|
|
64
|
-
value: 'change'
|
|
64
|
+
value: 'change',
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
name: 'Cancel installation',
|
|
68
|
-
value: 'cancel'
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
}
|
|
68
|
+
value: 'cancel',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
72
|
]);
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
switch (action) {
|
|
75
|
+
case 'cancel': {
|
|
75
76
|
console.log('Installation cancelled.');
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
process.exit(0);
|
|
78
|
+
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'change': {
|
|
82
|
+
const { newDirectory } = await inquirer.prompt([
|
|
83
|
+
{
|
|
84
|
+
type: 'input',
|
|
85
|
+
name: 'newDirectory',
|
|
86
|
+
message: 'Enter the new directory path:',
|
|
87
|
+
validate: (input) => {
|
|
88
|
+
if (!input.trim()) {
|
|
89
|
+
return 'Please enter a valid directory path';
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
// Preserve the original CWD for the recursive call
|
|
96
|
+
config.directory = newDirectory;
|
|
97
|
+
return await this.install(config); // Recursive call with new directory
|
|
98
|
+
}
|
|
99
|
+
case 'create': {
|
|
100
|
+
try {
|
|
101
|
+
await fileManager.ensureDirectory(installDir);
|
|
102
|
+
console.log(`✓ Created directory: ${installDir}`);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(`Failed to create directory: ${error.message}`);
|
|
105
|
+
console.error('You may need to check permissions or use a different path.');
|
|
106
|
+
process.exit(1);
|
|
89
107
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
config.directory = newDirectory;
|
|
93
|
-
return await this.install(config); // Recursive call with new directory
|
|
94
|
-
} else if (action === 'create') {
|
|
95
|
-
try {
|
|
96
|
-
await fileManager.ensureDirectory(installDir);
|
|
97
|
-
console.log(`✓ Created directory: ${installDir}`);
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error(`Failed to create directory: ${error.message}`);
|
|
100
|
-
console.error('You may need to check permissions or use a different path.');
|
|
101
|
-
process.exit(1);
|
|
108
|
+
|
|
109
|
+
break;
|
|
102
110
|
}
|
|
111
|
+
// No default
|
|
103
112
|
}
|
|
104
|
-
|
|
105
|
-
spinner.start(
|
|
113
|
+
|
|
114
|
+
spinner.start('Analyzing installation directory...');
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
// If this is an update request from early detection, handle it directly
|
|
@@ -121,39 +130,28 @@ class Installer {
|
|
|
121
130
|
|
|
122
131
|
// Handle different states
|
|
123
132
|
switch (state.type) {
|
|
124
|
-
case
|
|
133
|
+
case 'clean': {
|
|
125
134
|
return await this.performFreshInstall(config, installDir, spinner);
|
|
135
|
+
}
|
|
126
136
|
|
|
127
|
-
case
|
|
128
|
-
return await this.handleExistingV4Installation(
|
|
129
|
-
|
|
130
|
-
installDir,
|
|
131
|
-
state,
|
|
132
|
-
spinner
|
|
133
|
-
);
|
|
137
|
+
case 'v4_existing': {
|
|
138
|
+
return await this.handleExistingV4Installation(config, installDir, state, spinner);
|
|
139
|
+
}
|
|
134
140
|
|
|
135
|
-
case
|
|
136
|
-
return await this.handleV3Installation(
|
|
137
|
-
|
|
138
|
-
installDir,
|
|
139
|
-
state,
|
|
140
|
-
spinner
|
|
141
|
-
);
|
|
141
|
+
case 'v3_existing': {
|
|
142
|
+
return await this.handleV3Installation(config, installDir, state, spinner);
|
|
143
|
+
}
|
|
142
144
|
|
|
143
|
-
case
|
|
144
|
-
return await this.handleUnknownInstallation(
|
|
145
|
-
|
|
146
|
-
installDir,
|
|
147
|
-
state,
|
|
148
|
-
spinner
|
|
149
|
-
);
|
|
145
|
+
case 'unknown_existing': {
|
|
146
|
+
return await this.handleUnknownInstallation(config, installDir, state, spinner);
|
|
147
|
+
}
|
|
150
148
|
}
|
|
151
149
|
} catch (error) {
|
|
152
150
|
// Check if modules were initialized
|
|
153
151
|
if (spinner) {
|
|
154
|
-
spinner.fail(
|
|
152
|
+
spinner.fail('Installation failed');
|
|
155
153
|
} else {
|
|
156
|
-
console.error(
|
|
154
|
+
console.error('Installation failed:', error.message);
|
|
157
155
|
}
|
|
158
156
|
throw error;
|
|
159
157
|
}
|
|
@@ -161,7 +159,7 @@ class Installer {
|
|
|
161
159
|
|
|
162
160
|
async detectInstallationState(installDir) {
|
|
163
161
|
const state = {
|
|
164
|
-
type:
|
|
162
|
+
type: 'clean',
|
|
165
163
|
hasV4Manifest: false,
|
|
166
164
|
hasV3Structure: false,
|
|
167
165
|
hasBmadCore: false,
|
|
@@ -176,11 +174,11 @@ class Installer {
|
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
// Check for V4 installation (has .bmad-core with manifest)
|
|
179
|
-
const bmadCorePath = path.join(installDir,
|
|
180
|
-
const manifestPath = path.join(bmadCorePath,
|
|
177
|
+
const bmadCorePath = path.join(installDir, '.bmad-core');
|
|
178
|
+
const manifestPath = path.join(bmadCorePath, 'install-manifest.yaml');
|
|
181
179
|
|
|
182
180
|
if (await fileManager.pathExists(manifestPath)) {
|
|
183
|
-
state.type =
|
|
181
|
+
state.type = 'v4_existing';
|
|
184
182
|
state.hasV4Manifest = true;
|
|
185
183
|
state.hasBmadCore = true;
|
|
186
184
|
state.manifest = await fileManager.readManifest(installDir);
|
|
@@ -188,25 +186,25 @@ class Installer {
|
|
|
188
186
|
}
|
|
189
187
|
|
|
190
188
|
// Check for V3 installation (has bmad-agent directory)
|
|
191
|
-
const bmadAgentPath = path.join(installDir,
|
|
189
|
+
const bmadAgentPath = path.join(installDir, 'bmad-agent');
|
|
192
190
|
if (await fileManager.pathExists(bmadAgentPath)) {
|
|
193
|
-
state.type =
|
|
191
|
+
state.type = 'v3_existing';
|
|
194
192
|
state.hasV3Structure = true;
|
|
195
193
|
return state;
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
// Check for .bmad-core without manifest (broken V4 or manual copy)
|
|
199
197
|
if (await fileManager.pathExists(bmadCorePath)) {
|
|
200
|
-
state.type =
|
|
198
|
+
state.type = 'unknown_existing';
|
|
201
199
|
state.hasBmadCore = true;
|
|
202
200
|
return state;
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
// Check if directory has other files
|
|
206
|
-
const files = await resourceLocator.findFiles(
|
|
204
|
+
const files = await resourceLocator.findFiles('**/*', {
|
|
207
205
|
cwd: installDir,
|
|
208
206
|
nodir: true,
|
|
209
|
-
ignore: [
|
|
207
|
+
ignore: ['**/.git/**', '**/node_modules/**'],
|
|
210
208
|
});
|
|
211
209
|
|
|
212
210
|
if (files.length > 0) {
|
|
@@ -223,167 +221,184 @@ class Installer {
|
|
|
223
221
|
}
|
|
224
222
|
|
|
225
223
|
async performFreshInstall(config, installDir, spinner, options = {}) {
|
|
226
|
-
spinner.text =
|
|
224
|
+
spinner.text = 'Installing BMad Method...';
|
|
227
225
|
|
|
228
226
|
let files = [];
|
|
229
227
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// Copy documentation files from docs/ to .bmad-core
|
|
242
|
-
spinner.text = "Copying documentation files...";
|
|
243
|
-
await this.copyDocsItems(installDir, ".bmad-core", spinner);
|
|
244
|
-
|
|
245
|
-
// Get list of all files for manifest
|
|
246
|
-
const foundFiles = await resourceLocator.findFiles("**/*", {
|
|
247
|
-
cwd: bmadCoreDestDir,
|
|
248
|
-
nodir: true,
|
|
249
|
-
ignore: ["**/.git/**", "**/node_modules/**"],
|
|
250
|
-
});
|
|
251
|
-
files = foundFiles.map((file) => path.join(".bmad-core", file));
|
|
252
|
-
} else if (config.installType === "single-agent") {
|
|
253
|
-
// Single agent installation
|
|
254
|
-
spinner.text = `Installing ${config.agent} agent...`;
|
|
255
|
-
|
|
256
|
-
// Copy agent file with {root} replacement
|
|
257
|
-
const agentPath = configLoader.getAgentPath(config.agent);
|
|
258
|
-
const destAgentPath = path.join(
|
|
259
|
-
installDir,
|
|
260
|
-
".bmad-core",
|
|
261
|
-
"agents",
|
|
262
|
-
`${config.agent}.md`
|
|
263
|
-
);
|
|
264
|
-
await fileManager.copyFileWithRootReplacement(agentPath, destAgentPath, ".bmad-core");
|
|
265
|
-
files.push(`.bmad-core/agents/${config.agent}.md`);
|
|
228
|
+
switch (config.installType) {
|
|
229
|
+
case 'full': {
|
|
230
|
+
// Full installation - copy entire .bmad-core folder as a subdirectory
|
|
231
|
+
spinner.text = 'Copying complete .bmad-core folder...';
|
|
232
|
+
const sourceDir = resourceLocator.getBmadCorePath();
|
|
233
|
+
const bmadCoreDestDir = path.join(installDir, '.bmad-core');
|
|
234
|
+
await fileManager.copyDirectoryWithRootReplacement(
|
|
235
|
+
sourceDir,
|
|
236
|
+
bmadCoreDestDir,
|
|
237
|
+
'.bmad-core',
|
|
238
|
+
);
|
|
266
239
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
);
|
|
271
|
-
const sourceBase = resourceLocator.getBmadCorePath();
|
|
240
|
+
// Copy common/ items to .bmad-core
|
|
241
|
+
spinner.text = 'Copying common utilities...';
|
|
242
|
+
await this.copyCommonItems(installDir, '.bmad-core', spinner);
|
|
272
243
|
|
|
273
|
-
|
|
274
|
-
spinner.text =
|
|
244
|
+
// Copy documentation files from docs/ to .bmad-core
|
|
245
|
+
spinner.text = 'Copying documentation files...';
|
|
246
|
+
await this.copyDocsItems(installDir, '.bmad-core', spinner);
|
|
275
247
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
);
|
|
284
|
-
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
|
285
|
-
} else {
|
|
286
|
-
// Handle single files with {root} replacement if needed
|
|
287
|
-
const sourcePath = path.join(
|
|
288
|
-
sourceBase,
|
|
289
|
-
dep.replace(".bmad-core/", "")
|
|
290
|
-
);
|
|
291
|
-
const destPath = path.join(
|
|
292
|
-
installDir,
|
|
293
|
-
dep
|
|
294
|
-
);
|
|
248
|
+
// Get list of all files for manifest
|
|
249
|
+
const foundFiles = await resourceLocator.findFiles('**/*', {
|
|
250
|
+
cwd: bmadCoreDestDir,
|
|
251
|
+
nodir: true,
|
|
252
|
+
ignore: ['**/.git/**', '**/node_modules/**'],
|
|
253
|
+
});
|
|
254
|
+
files = foundFiles.map((file) => path.join('.bmad-core', file));
|
|
295
255
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
case 'single-agent': {
|
|
259
|
+
// Single agent installation
|
|
260
|
+
spinner.text = `Installing ${config.agent} agent...`;
|
|
261
|
+
|
|
262
|
+
// Copy agent file with {root} replacement
|
|
263
|
+
const agentPath = configLoader.getAgentPath(config.agent);
|
|
264
|
+
const destinationAgentPath = path.join(
|
|
265
|
+
installDir,
|
|
266
|
+
'.bmad-core',
|
|
267
|
+
'agents',
|
|
268
|
+
`${config.agent}.md`,
|
|
269
|
+
);
|
|
270
|
+
await fileManager.copyFileWithRootReplacement(
|
|
271
|
+
agentPath,
|
|
272
|
+
destinationAgentPath,
|
|
273
|
+
'.bmad-core',
|
|
274
|
+
);
|
|
275
|
+
files.push(`.bmad-core/agents/${config.agent}.md`);
|
|
276
|
+
|
|
277
|
+
// Copy dependencies
|
|
278
|
+
const { all: dependencies } = await resourceLocator.getAgentDependencies(config.agent);
|
|
279
|
+
const sourceBase = resourceLocator.getBmadCorePath();
|
|
280
|
+
|
|
281
|
+
for (const dep of dependencies) {
|
|
282
|
+
spinner.text = `Copying dependency: ${dep}`;
|
|
283
|
+
|
|
284
|
+
if (dep.includes('*')) {
|
|
285
|
+
// Handle glob patterns with {root} replacement
|
|
286
|
+
const copiedFiles = await fileManager.copyGlobPattern(
|
|
287
|
+
dep.replace('.bmad-core/', ''),
|
|
288
|
+
sourceBase,
|
|
289
|
+
path.join(installDir, '.bmad-core'),
|
|
290
|
+
'.bmad-core',
|
|
291
|
+
);
|
|
292
|
+
files.push(...copiedFiles.map((f) => `.bmad-core/${f}`));
|
|
301
293
|
} else {
|
|
302
|
-
|
|
303
|
-
|
|
294
|
+
// Handle single files with {root} replacement if needed
|
|
295
|
+
const sourcePath = path.join(sourceBase, dep.replace('.bmad-core/', ''));
|
|
296
|
+
const destinationPath = path.join(installDir, dep);
|
|
297
|
+
|
|
298
|
+
const needsRootReplacement =
|
|
299
|
+
dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
|
|
300
|
+
let success = false;
|
|
301
|
+
|
|
302
|
+
success = await (needsRootReplacement
|
|
303
|
+
? fileManager.copyFileWithRootReplacement(sourcePath, destinationPath, '.bmad-core')
|
|
304
|
+
: fileManager.copyFile(sourcePath, destinationPath));
|
|
304
305
|
|
|
305
|
-
|
|
306
|
-
|
|
306
|
+
if (success) {
|
|
307
|
+
files.push(dep);
|
|
308
|
+
}
|
|
307
309
|
}
|
|
308
310
|
}
|
|
311
|
+
|
|
312
|
+
// Copy common/ items to .bmad-core
|
|
313
|
+
spinner.text = 'Copying common utilities...';
|
|
314
|
+
const commonFiles = await this.copyCommonItems(installDir, '.bmad-core', spinner);
|
|
315
|
+
files.push(...commonFiles);
|
|
316
|
+
|
|
317
|
+
// Copy documentation files from docs/ to .bmad-core
|
|
318
|
+
spinner.text = 'Copying documentation files...';
|
|
319
|
+
const documentFiles = await this.copyDocsItems(installDir, '.bmad-core', spinner);
|
|
320
|
+
files.push(...documentFiles);
|
|
321
|
+
|
|
322
|
+
break;
|
|
309
323
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (dep.includes("*")) {
|
|
333
|
-
// Handle glob patterns with {root} replacement
|
|
334
|
-
const copiedFiles = await fileManager.copyGlobPattern(
|
|
335
|
-
dep.replace(".bmad-core/", ""),
|
|
336
|
-
sourceBase,
|
|
337
|
-
path.join(installDir, ".bmad-core"),
|
|
338
|
-
".bmad-core"
|
|
339
|
-
);
|
|
340
|
-
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
|
341
|
-
} else {
|
|
342
|
-
// Handle single files with {root} replacement if needed
|
|
343
|
-
const sourcePath = path.join(sourceBase, dep.replace(".bmad-core/", ""));
|
|
344
|
-
const destPath = path.join(installDir, dep);
|
|
345
|
-
|
|
346
|
-
const needsRootReplacement = dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
|
|
347
|
-
let success = false;
|
|
348
|
-
|
|
349
|
-
if (needsRootReplacement) {
|
|
350
|
-
success = await fileManager.copyFileWithRootReplacement(sourcePath, destPath, ".bmad-core");
|
|
324
|
+
case 'team': {
|
|
325
|
+
// Team installation
|
|
326
|
+
spinner.text = `Installing ${config.team} team...`;
|
|
327
|
+
|
|
328
|
+
// Get team dependencies
|
|
329
|
+
const teamDependencies = await configLoader.getTeamDependencies(config.team);
|
|
330
|
+
const sourceBase = resourceLocator.getBmadCorePath();
|
|
331
|
+
|
|
332
|
+
// Install all team dependencies
|
|
333
|
+
for (const dep of teamDependencies) {
|
|
334
|
+
spinner.text = `Copying team dependency: ${dep}`;
|
|
335
|
+
|
|
336
|
+
if (dep.includes('*')) {
|
|
337
|
+
// Handle glob patterns with {root} replacement
|
|
338
|
+
const copiedFiles = await fileManager.copyGlobPattern(
|
|
339
|
+
dep.replace('.bmad-core/', ''),
|
|
340
|
+
sourceBase,
|
|
341
|
+
path.join(installDir, '.bmad-core'),
|
|
342
|
+
'.bmad-core',
|
|
343
|
+
);
|
|
344
|
+
files.push(...copiedFiles.map((f) => `.bmad-core/${f}`));
|
|
351
345
|
} else {
|
|
352
|
-
|
|
353
|
-
|
|
346
|
+
// Handle single files with {root} replacement if needed
|
|
347
|
+
const sourcePath = path.join(sourceBase, dep.replace('.bmad-core/', ''));
|
|
348
|
+
const destinationPath = path.join(installDir, dep);
|
|
354
349
|
|
|
355
|
-
|
|
356
|
-
|
|
350
|
+
const needsRootReplacement =
|
|
351
|
+
dep.endsWith('.md') || dep.endsWith('.yaml') || dep.endsWith('.yml');
|
|
352
|
+
let success = false;
|
|
353
|
+
|
|
354
|
+
success = await (needsRootReplacement
|
|
355
|
+
? fileManager.copyFileWithRootReplacement(sourcePath, destinationPath, '.bmad-core')
|
|
356
|
+
: fileManager.copyFile(sourcePath, destinationPath));
|
|
357
|
+
|
|
358
|
+
if (success) {
|
|
359
|
+
files.push(dep);
|
|
360
|
+
}
|
|
357
361
|
}
|
|
358
362
|
}
|
|
363
|
+
|
|
364
|
+
// Copy common/ items to .bmad-core
|
|
365
|
+
spinner.text = 'Copying common utilities...';
|
|
366
|
+
const commonFiles = await this.copyCommonItems(installDir, '.bmad-core', spinner);
|
|
367
|
+
files.push(...commonFiles);
|
|
368
|
+
|
|
369
|
+
// Copy documentation files from docs/ to .bmad-core
|
|
370
|
+
spinner.text = 'Copying documentation files...';
|
|
371
|
+
const documentFiles = await this.copyDocsItems(installDir, '.bmad-core', spinner);
|
|
372
|
+
files.push(...documentFiles);
|
|
373
|
+
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
case 'expansion-only': {
|
|
377
|
+
// Expansion-only installation - DO NOT create .bmad-core
|
|
378
|
+
// Only install expansion packs
|
|
379
|
+
spinner.text = 'Installing expansion packs only...';
|
|
380
|
+
|
|
381
|
+
break;
|
|
359
382
|
}
|
|
360
|
-
|
|
361
|
-
// Copy common/ items to .bmad-core
|
|
362
|
-
spinner.text = "Copying common utilities...";
|
|
363
|
-
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
364
|
-
files.push(...commonFiles);
|
|
365
|
-
|
|
366
|
-
// Copy documentation files from docs/ to .bmad-core
|
|
367
|
-
spinner.text = "Copying documentation files...";
|
|
368
|
-
const docFiles = await this.copyDocsItems(installDir, ".bmad-core", spinner);
|
|
369
|
-
files.push(...docFiles);
|
|
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...";
|
|
383
|
+
// No default
|
|
374
384
|
}
|
|
375
385
|
|
|
376
386
|
// Install expansion packs if requested
|
|
377
|
-
const expansionFiles = await this.installExpansionPacks(
|
|
387
|
+
const expansionFiles = await this.installExpansionPacks(
|
|
388
|
+
installDir,
|
|
389
|
+
config.expansionPacks,
|
|
390
|
+
spinner,
|
|
391
|
+
config,
|
|
392
|
+
);
|
|
378
393
|
files.push(...expansionFiles);
|
|
379
394
|
|
|
380
395
|
// Install web bundles if requested
|
|
381
396
|
if (config.includeWebBundles && config.webBundlesDirectory) {
|
|
382
|
-
spinner.text =
|
|
397
|
+
spinner.text = 'Installing web bundles...';
|
|
383
398
|
// Resolve web bundles directory using the same logic as the main installation directory
|
|
384
399
|
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
|
385
|
-
let resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
|
|
386
|
-
? config.webBundlesDirectory
|
|
400
|
+
let resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
|
|
401
|
+
? config.webBundlesDirectory
|
|
387
402
|
: path.resolve(originalCwd, config.webBundlesDirectory);
|
|
388
403
|
await this.installWebBundles(resolvedWebBundlesDir, config, spinner);
|
|
389
404
|
}
|
|
@@ -399,18 +414,21 @@ class Installer {
|
|
|
399
414
|
}
|
|
400
415
|
|
|
401
416
|
// Modify core-config.yaml if sharding preferences were provided
|
|
402
|
-
if (
|
|
403
|
-
|
|
417
|
+
if (
|
|
418
|
+
config.installType !== 'expansion-only' &&
|
|
419
|
+
(config.prdSharded !== undefined || config.architectureSharded !== undefined)
|
|
420
|
+
) {
|
|
421
|
+
spinner.text = 'Configuring document sharding settings...';
|
|
404
422
|
await fileManager.modifyCoreConfig(installDir, config);
|
|
405
423
|
}
|
|
406
424
|
|
|
407
425
|
// Create manifest (skip for expansion-only installations)
|
|
408
|
-
if (config.installType !==
|
|
409
|
-
spinner.text =
|
|
426
|
+
if (config.installType !== 'expansion-only') {
|
|
427
|
+
spinner.text = 'Creating installation manifest...';
|
|
410
428
|
await fileManager.createManifest(installDir, config, files);
|
|
411
429
|
}
|
|
412
430
|
|
|
413
|
-
spinner.succeed(
|
|
431
|
+
spinner.succeed('Installation complete!');
|
|
414
432
|
this.showSuccessMessage(config, installDir, options);
|
|
415
433
|
}
|
|
416
434
|
|
|
@@ -421,44 +439,40 @@ class Installer {
|
|
|
421
439
|
const newVersion = await this.getCoreVersion();
|
|
422
440
|
const versionCompare = this.compareVersions(currentVersion, newVersion);
|
|
423
441
|
|
|
424
|
-
console.log(chalk.yellow(
|
|
442
|
+
console.log(chalk.yellow('\n🔍 Found existing BMad v4 installation'));
|
|
425
443
|
console.log(` Directory: ${installDir}`);
|
|
426
444
|
console.log(` Current version: ${currentVersion}`);
|
|
427
445
|
console.log(` Available version: ${newVersion}`);
|
|
428
|
-
console.log(
|
|
429
|
-
` Installed: ${new Date(
|
|
430
|
-
state.manifest.installed_at
|
|
431
|
-
).toLocaleDateString()}`
|
|
432
|
-
);
|
|
446
|
+
console.log(` Installed: ${new Date(state.manifest.installed_at).toLocaleDateString()}`);
|
|
433
447
|
|
|
434
448
|
// Check file integrity
|
|
435
|
-
spinner.start(
|
|
449
|
+
spinner.start('Checking installation integrity...');
|
|
436
450
|
const integrity = await fileManager.checkFileIntegrity(installDir, state.manifest);
|
|
437
451
|
spinner.stop();
|
|
438
|
-
|
|
452
|
+
|
|
439
453
|
const hasMissingFiles = integrity.missing.length > 0;
|
|
440
454
|
const hasModifiedFiles = integrity.modified.length > 0;
|
|
441
455
|
const hasIntegrityIssues = hasMissingFiles || hasModifiedFiles;
|
|
442
|
-
|
|
456
|
+
|
|
443
457
|
if (hasIntegrityIssues) {
|
|
444
|
-
|
|
458
|
+
console.log(chalk.red('\n⚠️ Installation issues detected:'));
|
|
445
459
|
if (hasMissingFiles) {
|
|
446
460
|
console.log(chalk.red(` Missing files: ${integrity.missing.length}`));
|
|
447
461
|
if (integrity.missing.length <= 5) {
|
|
448
|
-
integrity.missing
|
|
462
|
+
for (const file of integrity.missing) console.log(chalk.dim(` - ${file}`));
|
|
449
463
|
}
|
|
450
464
|
}
|
|
451
465
|
if (hasModifiedFiles) {
|
|
452
466
|
console.log(chalk.yellow(` Modified files: ${integrity.modified.length}`));
|
|
453
467
|
if (integrity.modified.length <= 5) {
|
|
454
|
-
integrity.modified
|
|
468
|
+
for (const file of integrity.modified) console.log(chalk.dim(` - ${file}`));
|
|
455
469
|
}
|
|
456
470
|
}
|
|
457
471
|
}
|
|
458
472
|
|
|
459
473
|
// Show existing expansion packs
|
|
460
474
|
if (Object.keys(state.expansionPacks).length > 0) {
|
|
461
|
-
console.log(chalk.cyan(
|
|
475
|
+
console.log(chalk.cyan('\n📦 Installed expansion packs:'));
|
|
462
476
|
for (const [packId, packInfo] of Object.entries(state.expansionPacks)) {
|
|
463
477
|
if (packInfo.hasManifest && packInfo.manifest) {
|
|
464
478
|
console.log(` - ${packId} (v${packInfo.manifest.version || 'unknown'})`);
|
|
@@ -469,236 +483,251 @@ class Installer {
|
|
|
469
483
|
}
|
|
470
484
|
|
|
471
485
|
let choices = [];
|
|
472
|
-
|
|
486
|
+
|
|
473
487
|
if (versionCompare < 0) {
|
|
474
|
-
|
|
475
|
-
choices.push({
|
|
488
|
+
console.log(chalk.cyan('\n⬆️ Upgrade available for BMad core'));
|
|
489
|
+
choices.push({
|
|
490
|
+
name: `Upgrade BMad core (v${currentVersion} → v${newVersion})`,
|
|
491
|
+
value: 'upgrade',
|
|
492
|
+
});
|
|
476
493
|
} else if (versionCompare === 0) {
|
|
477
494
|
if (hasIntegrityIssues) {
|
|
478
495
|
// Offer repair option when files are missing or modified
|
|
479
|
-
choices.push({
|
|
480
|
-
name:
|
|
481
|
-
value:
|
|
496
|
+
choices.push({
|
|
497
|
+
name: 'Repair installation (restore missing/modified files)',
|
|
498
|
+
value: 'repair',
|
|
482
499
|
});
|
|
483
500
|
}
|
|
484
|
-
|
|
485
|
-
choices.push({
|
|
501
|
+
console.log(chalk.yellow('\n⚠️ Same version already installed'));
|
|
502
|
+
choices.push({
|
|
503
|
+
name: `Force reinstall BMad core (v${currentVersion} - reinstall)`,
|
|
504
|
+
value: 'reinstall',
|
|
505
|
+
});
|
|
486
506
|
} else {
|
|
487
|
-
|
|
488
|
-
choices.push({
|
|
507
|
+
console.log(chalk.yellow('\n⬇️ Installed version is newer than available'));
|
|
508
|
+
choices.push({
|
|
509
|
+
name: `Downgrade BMad core (v${currentVersion} → v${newVersion})`,
|
|
510
|
+
value: 'reinstall',
|
|
511
|
+
});
|
|
489
512
|
}
|
|
490
|
-
|
|
513
|
+
|
|
491
514
|
choices.push(
|
|
492
|
-
{ name:
|
|
493
|
-
{ name:
|
|
515
|
+
{ name: 'Add/update expansion packs only', value: 'expansions' },
|
|
516
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
494
517
|
);
|
|
495
518
|
|
|
496
519
|
const { action } = await inquirer.prompt([
|
|
497
520
|
{
|
|
498
|
-
type:
|
|
499
|
-
name:
|
|
500
|
-
message:
|
|
521
|
+
type: 'list',
|
|
522
|
+
name: 'action',
|
|
523
|
+
message: 'What would you like to do?',
|
|
501
524
|
choices: choices,
|
|
502
525
|
},
|
|
503
526
|
]);
|
|
504
527
|
|
|
505
528
|
switch (action) {
|
|
506
|
-
case
|
|
529
|
+
case 'upgrade': {
|
|
507
530
|
return await this.performUpdate(config, installDir, state.manifest, spinner);
|
|
508
|
-
|
|
531
|
+
}
|
|
532
|
+
case 'repair': {
|
|
509
533
|
// For repair, restore missing/modified files while backing up modified ones
|
|
510
534
|
return await this.performRepair(config, installDir, state.manifest, integrity, spinner);
|
|
511
|
-
|
|
535
|
+
}
|
|
536
|
+
case 'reinstall': {
|
|
512
537
|
// For reinstall, don't check for modifications - just overwrite
|
|
513
538
|
return await this.performReinstall(config, installDir, spinner);
|
|
514
|
-
|
|
539
|
+
}
|
|
540
|
+
case 'expansions': {
|
|
515
541
|
// Ask which expansion packs to install
|
|
516
542
|
const availableExpansionPacks = await resourceLocator.getExpansionPacks();
|
|
517
|
-
|
|
543
|
+
|
|
518
544
|
if (availableExpansionPacks.length === 0) {
|
|
519
|
-
console.log(chalk.yellow(
|
|
545
|
+
console.log(chalk.yellow('No expansion packs available.'));
|
|
520
546
|
return;
|
|
521
547
|
}
|
|
522
|
-
|
|
548
|
+
|
|
523
549
|
const { selectedPacks } = await inquirer.prompt([
|
|
524
550
|
{
|
|
525
551
|
type: 'checkbox',
|
|
526
552
|
name: 'selectedPacks',
|
|
527
553
|
message: 'Select expansion packs to install/update:',
|
|
528
|
-
choices: availableExpansionPacks.map(pack => ({
|
|
554
|
+
choices: availableExpansionPacks.map((pack) => ({
|
|
529
555
|
name: `${pack.name} (v${pack.version}) .${pack.id}`,
|
|
530
556
|
value: pack.id,
|
|
531
|
-
checked: state.expansionPacks[pack.id] !== undefined
|
|
532
|
-
}))
|
|
533
|
-
}
|
|
557
|
+
checked: state.expansionPacks[pack.id] !== undefined,
|
|
558
|
+
})),
|
|
559
|
+
},
|
|
534
560
|
]);
|
|
535
|
-
|
|
561
|
+
|
|
536
562
|
if (selectedPacks.length === 0) {
|
|
537
|
-
console.log(chalk.yellow(
|
|
563
|
+
console.log(chalk.yellow('No expansion packs selected.'));
|
|
538
564
|
return;
|
|
539
565
|
}
|
|
540
|
-
|
|
541
|
-
spinner.start(
|
|
542
|
-
const expansionFiles = await this.installExpansionPacks(
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
566
|
+
|
|
567
|
+
spinner.start('Installing expansion packs...');
|
|
568
|
+
const expansionFiles = await this.installExpansionPacks(
|
|
569
|
+
installDir,
|
|
570
|
+
selectedPacks,
|
|
571
|
+
spinner,
|
|
572
|
+
{ ides: config.ides || [] },
|
|
573
|
+
);
|
|
574
|
+
spinner.succeed('Expansion packs installed successfully!');
|
|
575
|
+
|
|
576
|
+
console.log(chalk.green('\n✓ Installation complete!'));
|
|
546
577
|
console.log(chalk.green(`✓ Expansion packs installed/updated:`));
|
|
547
578
|
for (const packId of selectedPacks) {
|
|
548
579
|
console.log(chalk.green(` - ${packId} → .${packId}/`));
|
|
549
580
|
}
|
|
550
581
|
return;
|
|
551
582
|
}
|
|
552
|
-
case
|
|
553
|
-
console.log(
|
|
583
|
+
case 'cancel': {
|
|
584
|
+
console.log('Installation cancelled.');
|
|
554
585
|
return;
|
|
586
|
+
}
|
|
555
587
|
}
|
|
556
588
|
}
|
|
557
589
|
|
|
558
590
|
async handleV3Installation(config, installDir, state, spinner) {
|
|
559
591
|
spinner.stop();
|
|
560
592
|
|
|
561
|
-
console.log(
|
|
562
|
-
chalk.yellow("\n🔍 Found BMad v3 installation (bmad-agent/ directory)")
|
|
563
|
-
);
|
|
593
|
+
console.log(chalk.yellow('\n🔍 Found BMad v3 installation (bmad-agent/ directory)'));
|
|
564
594
|
console.log(` Directory: ${installDir}`);
|
|
565
595
|
|
|
566
596
|
const { action } = await inquirer.prompt([
|
|
567
597
|
{
|
|
568
|
-
type:
|
|
569
|
-
name:
|
|
570
|
-
message:
|
|
598
|
+
type: 'list',
|
|
599
|
+
name: 'action',
|
|
600
|
+
message: 'What would you like to do?',
|
|
571
601
|
choices: [
|
|
572
|
-
{ name:
|
|
573
|
-
{ name:
|
|
574
|
-
{ name:
|
|
602
|
+
{ name: 'Upgrade from v3 to v4 (recommended)', value: 'upgrade' },
|
|
603
|
+
{ name: 'Install v4 alongside v3', value: 'alongside' },
|
|
604
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
575
605
|
],
|
|
576
606
|
},
|
|
577
607
|
]);
|
|
578
608
|
|
|
579
609
|
switch (action) {
|
|
580
|
-
case
|
|
581
|
-
console.log(chalk.cyan(
|
|
582
|
-
const V3ToV4Upgrader = require(
|
|
610
|
+
case 'upgrade': {
|
|
611
|
+
console.log(chalk.cyan('\n📦 Starting v3 to v4 upgrade process...'));
|
|
612
|
+
const V3ToV4Upgrader = require('../../upgraders/v3-to-v4-upgrader');
|
|
583
613
|
const upgrader = new V3ToV4Upgrader();
|
|
584
|
-
return await upgrader.upgrade({
|
|
614
|
+
return await upgrader.upgrade({
|
|
585
615
|
projectPath: installDir,
|
|
586
|
-
ides: config.ides || [] // Pass IDE selections from initial config
|
|
616
|
+
ides: config.ides || [], // Pass IDE selections from initial config
|
|
587
617
|
});
|
|
588
618
|
}
|
|
589
|
-
case
|
|
619
|
+
case 'alongside': {
|
|
590
620
|
return await this.performFreshInstall(config, installDir, spinner);
|
|
591
|
-
|
|
592
|
-
|
|
621
|
+
}
|
|
622
|
+
case 'cancel': {
|
|
623
|
+
console.log('Installation cancelled.');
|
|
593
624
|
return;
|
|
625
|
+
}
|
|
594
626
|
}
|
|
595
627
|
}
|
|
596
628
|
|
|
597
629
|
async handleUnknownInstallation(config, installDir, state, spinner) {
|
|
598
630
|
spinner.stop();
|
|
599
631
|
|
|
600
|
-
console.log(chalk.yellow(
|
|
632
|
+
console.log(chalk.yellow('\n⚠️ Directory contains existing files'));
|
|
601
633
|
console.log(` Directory: ${installDir}`);
|
|
602
634
|
|
|
603
635
|
if (state.hasBmadCore) {
|
|
604
|
-
console.log(
|
|
636
|
+
console.log(' Found: .bmad-core directory (but no manifest)');
|
|
605
637
|
}
|
|
606
638
|
if (state.hasOtherFiles) {
|
|
607
|
-
console.log(
|
|
639
|
+
console.log(' Found: Other files in directory');
|
|
608
640
|
}
|
|
609
641
|
|
|
610
642
|
const { action } = await inquirer.prompt([
|
|
611
643
|
{
|
|
612
|
-
type:
|
|
613
|
-
name:
|
|
614
|
-
message:
|
|
644
|
+
type: 'list',
|
|
645
|
+
name: 'action',
|
|
646
|
+
message: 'What would you like to do?',
|
|
615
647
|
choices: [
|
|
616
|
-
{ name:
|
|
617
|
-
{ name:
|
|
618
|
-
{ name:
|
|
648
|
+
{ name: 'Install anyway (may overwrite files)', value: 'force' },
|
|
649
|
+
{ name: 'Choose different directory', value: 'different' },
|
|
650
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
619
651
|
],
|
|
620
652
|
},
|
|
621
653
|
]);
|
|
622
654
|
|
|
623
655
|
switch (action) {
|
|
624
|
-
case
|
|
656
|
+
case 'force': {
|
|
625
657
|
return await this.performFreshInstall(config, installDir, spinner);
|
|
626
|
-
|
|
658
|
+
}
|
|
659
|
+
case 'different': {
|
|
627
660
|
const { newDir } = await inquirer.prompt([
|
|
628
661
|
{
|
|
629
|
-
type:
|
|
630
|
-
name:
|
|
631
|
-
message:
|
|
632
|
-
default: path.join(path.dirname(installDir),
|
|
662
|
+
type: 'input',
|
|
663
|
+
name: 'newDir',
|
|
664
|
+
message: 'Enter new installation directory:',
|
|
665
|
+
default: path.join(path.dirname(installDir), 'bmad-project'),
|
|
633
666
|
},
|
|
634
667
|
]);
|
|
635
668
|
config.directory = newDir;
|
|
636
669
|
return await this.install(config);
|
|
637
670
|
}
|
|
638
|
-
case
|
|
639
|
-
console.log(
|
|
671
|
+
case 'cancel': {
|
|
672
|
+
console.log('Installation cancelled.');
|
|
640
673
|
return;
|
|
674
|
+
}
|
|
641
675
|
}
|
|
642
676
|
}
|
|
643
677
|
|
|
644
678
|
async performUpdate(newConfig, installDir, manifest, spinner) {
|
|
645
|
-
spinner.start(
|
|
679
|
+
spinner.start('Checking for updates...');
|
|
646
680
|
|
|
647
681
|
try {
|
|
648
682
|
// Get current and new versions
|
|
649
683
|
const currentVersion = manifest.version;
|
|
650
684
|
const newVersion = await this.getCoreVersion();
|
|
651
685
|
const versionCompare = this.compareVersions(currentVersion, newVersion);
|
|
652
|
-
|
|
686
|
+
|
|
653
687
|
// Only check for modified files if it's an actual version upgrade
|
|
654
688
|
let modifiedFiles = [];
|
|
655
689
|
if (versionCompare !== 0) {
|
|
656
|
-
spinner.text =
|
|
657
|
-
modifiedFiles = await fileManager.checkModifiedFiles(
|
|
658
|
-
installDir,
|
|
659
|
-
manifest
|
|
660
|
-
);
|
|
690
|
+
spinner.text = 'Checking for modified files...';
|
|
691
|
+
modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest);
|
|
661
692
|
}
|
|
662
693
|
|
|
663
694
|
if (modifiedFiles.length > 0) {
|
|
664
|
-
spinner.warn(
|
|
665
|
-
console.log(chalk.yellow(
|
|
695
|
+
spinner.warn('Found modified files');
|
|
696
|
+
console.log(chalk.yellow('\nThe following files have been modified:'));
|
|
666
697
|
for (const file of modifiedFiles) {
|
|
667
698
|
console.log(` - ${file}`);
|
|
668
699
|
}
|
|
669
700
|
|
|
670
701
|
const { action } = await inquirer.prompt([
|
|
671
702
|
{
|
|
672
|
-
type:
|
|
673
|
-
name:
|
|
674
|
-
message:
|
|
703
|
+
type: 'list',
|
|
704
|
+
name: 'action',
|
|
705
|
+
message: 'How would you like to proceed?',
|
|
675
706
|
choices: [
|
|
676
|
-
{ name:
|
|
677
|
-
{ name:
|
|
678
|
-
{ name:
|
|
707
|
+
{ name: 'Backup and overwrite modified files', value: 'backup' },
|
|
708
|
+
{ name: 'Skip modified files', value: 'skip' },
|
|
709
|
+
{ name: 'Cancel update', value: 'cancel' },
|
|
679
710
|
],
|
|
680
711
|
},
|
|
681
712
|
]);
|
|
682
713
|
|
|
683
|
-
if (action ===
|
|
684
|
-
console.log(
|
|
714
|
+
if (action === 'cancel') {
|
|
715
|
+
console.log('Update cancelled.');
|
|
685
716
|
return;
|
|
686
717
|
}
|
|
687
718
|
|
|
688
|
-
if (action ===
|
|
689
|
-
spinner.start(
|
|
719
|
+
if (action === 'backup') {
|
|
720
|
+
spinner.start('Backing up modified files...');
|
|
690
721
|
for (const file of modifiedFiles) {
|
|
691
722
|
const filePath = path.join(installDir, file);
|
|
692
723
|
const backupPath = await fileManager.backupFile(filePath);
|
|
693
|
-
console.log(
|
|
694
|
-
chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`)
|
|
695
|
-
);
|
|
724
|
+
console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`));
|
|
696
725
|
}
|
|
697
726
|
}
|
|
698
727
|
}
|
|
699
728
|
|
|
700
729
|
// Perform update by re-running installation
|
|
701
|
-
spinner.text = versionCompare === 0 ?
|
|
730
|
+
spinner.text = versionCompare === 0 ? 'Reinstalling files...' : 'Updating files...';
|
|
702
731
|
const config = {
|
|
703
732
|
installType: manifest.install_type,
|
|
704
733
|
agent: manifest.agent,
|
|
@@ -707,23 +736,23 @@ class Installer {
|
|
|
707
736
|
};
|
|
708
737
|
|
|
709
738
|
await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
|
|
710
|
-
|
|
739
|
+
|
|
711
740
|
// Clean up .yml files that now have .yaml counterparts
|
|
712
|
-
spinner.text =
|
|
741
|
+
spinner.text = 'Cleaning up legacy .yml files...';
|
|
713
742
|
await this.cleanupLegacyYmlFiles(installDir, spinner);
|
|
714
743
|
} catch (error) {
|
|
715
|
-
spinner.fail(
|
|
744
|
+
spinner.fail('Update failed');
|
|
716
745
|
throw error;
|
|
717
746
|
}
|
|
718
747
|
}
|
|
719
748
|
|
|
720
749
|
async performRepair(config, installDir, manifest, integrity, spinner) {
|
|
721
|
-
spinner.start(
|
|
750
|
+
spinner.start('Preparing to repair installation...');
|
|
722
751
|
|
|
723
752
|
try {
|
|
724
753
|
// Back up modified files
|
|
725
754
|
if (integrity.modified.length > 0) {
|
|
726
|
-
spinner.text =
|
|
755
|
+
spinner.text = 'Backing up modified files...';
|
|
727
756
|
for (const file of integrity.modified) {
|
|
728
757
|
const filePath = path.join(installDir, file);
|
|
729
758
|
if (await fileManager.pathExists(filePath)) {
|
|
@@ -734,42 +763,42 @@ class Installer {
|
|
|
734
763
|
}
|
|
735
764
|
|
|
736
765
|
// Restore missing and modified files
|
|
737
|
-
spinner.text =
|
|
766
|
+
spinner.text = 'Restoring files...';
|
|
738
767
|
const sourceBase = resourceLocator.getBmadCorePath();
|
|
739
768
|
const filesToRestore = [...integrity.missing, ...integrity.modified];
|
|
740
|
-
|
|
769
|
+
|
|
741
770
|
for (const file of filesToRestore) {
|
|
742
771
|
// Skip the manifest file itself
|
|
743
772
|
if (file.endsWith('install-manifest.yaml')) continue;
|
|
744
|
-
|
|
773
|
+
|
|
745
774
|
const relativePath = file.replace('.bmad-core/', '');
|
|
746
|
-
const
|
|
747
|
-
|
|
775
|
+
const destinationPath = path.join(installDir, file);
|
|
776
|
+
|
|
748
777
|
// Check if this is a common/ file that needs special processing
|
|
749
778
|
const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
|
|
750
779
|
const commonSourcePath = path.join(commonBase, 'common', relativePath);
|
|
751
|
-
|
|
780
|
+
|
|
752
781
|
if (await fileManager.pathExists(commonSourcePath)) {
|
|
753
782
|
// This is a common/ file - needs template processing
|
|
754
|
-
const fs = require('fs').promises;
|
|
783
|
+
const fs = require('node:fs').promises;
|
|
755
784
|
const content = await fs.readFile(commonSourcePath, 'utf8');
|
|
756
|
-
const updatedContent = content.
|
|
757
|
-
await fileManager.ensureDirectory(path.dirname(
|
|
758
|
-
await fs.writeFile(
|
|
785
|
+
const updatedContent = content.replaceAll('{root}', '.bmad-core');
|
|
786
|
+
await fileManager.ensureDirectory(path.dirname(destinationPath));
|
|
787
|
+
await fs.writeFile(destinationPath, updatedContent, 'utf8');
|
|
759
788
|
spinner.text = `Restored: ${file}`;
|
|
760
789
|
} else {
|
|
761
790
|
// Regular file from bmad-core
|
|
762
791
|
const sourcePath = path.join(sourceBase, relativePath);
|
|
763
792
|
if (await fileManager.pathExists(sourcePath)) {
|
|
764
|
-
await fileManager.copyFile(sourcePath,
|
|
793
|
+
await fileManager.copyFile(sourcePath, destinationPath);
|
|
765
794
|
spinner.text = `Restored: ${file}`;
|
|
766
|
-
|
|
795
|
+
|
|
767
796
|
// If this is a .yaml file, check for and remove corresponding .yml file
|
|
768
797
|
if (file.endsWith('.yaml')) {
|
|
769
798
|
const ymlFile = file.replace(/\.yaml$/, '.yml');
|
|
770
799
|
const ymlPath = path.join(installDir, ymlFile);
|
|
771
800
|
if (await fileManager.pathExists(ymlPath)) {
|
|
772
|
-
const fs = require('fs').promises;
|
|
801
|
+
const fs = require('node:fs').promises;
|
|
773
802
|
await fs.unlink(ymlPath);
|
|
774
803
|
console.log(chalk.dim(` Removed legacy: ${ymlFile} (replaced by ${file})`));
|
|
775
804
|
}
|
|
@@ -779,187 +808,192 @@ class Installer {
|
|
|
779
808
|
}
|
|
780
809
|
}
|
|
781
810
|
}
|
|
782
|
-
|
|
811
|
+
|
|
783
812
|
// Clean up .yml files that now have .yaml counterparts
|
|
784
|
-
spinner.text =
|
|
813
|
+
spinner.text = 'Cleaning up legacy .yml files...';
|
|
785
814
|
await this.cleanupLegacyYmlFiles(installDir, spinner);
|
|
786
|
-
|
|
787
|
-
spinner.succeed(
|
|
788
|
-
|
|
815
|
+
|
|
816
|
+
spinner.succeed('Repair completed successfully!');
|
|
817
|
+
|
|
789
818
|
// Show summary
|
|
790
|
-
console.log(chalk.green(
|
|
819
|
+
console.log(chalk.green('\n✓ Installation repaired!'));
|
|
791
820
|
if (integrity.missing.length > 0) {
|
|
792
821
|
console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
|
|
793
822
|
}
|
|
794
823
|
if (integrity.modified.length > 0) {
|
|
795
|
-
console.log(
|
|
824
|
+
console.log(
|
|
825
|
+
chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`),
|
|
826
|
+
);
|
|
796
827
|
}
|
|
797
|
-
|
|
828
|
+
|
|
798
829
|
// Warning for Cursor custom modes if agents were repaired
|
|
799
830
|
const ides = manifest.ides_setup || [];
|
|
800
831
|
if (ides.includes('cursor')) {
|
|
801
|
-
console.log(chalk.yellow.bold(
|
|
802
|
-
console.log(
|
|
832
|
+
console.log(chalk.yellow.bold('\n⚠️ IMPORTANT: Cursor Custom Modes Update Required'));
|
|
833
|
+
console.log(
|
|
834
|
+
chalk.yellow(
|
|
835
|
+
'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.',
|
|
836
|
+
),
|
|
837
|
+
);
|
|
803
838
|
}
|
|
804
|
-
|
|
805
839
|
} catch (error) {
|
|
806
|
-
spinner.fail(
|
|
840
|
+
spinner.fail('Repair failed');
|
|
807
841
|
throw error;
|
|
808
842
|
}
|
|
809
843
|
}
|
|
810
844
|
|
|
811
845
|
async performReinstall(config, installDir, spinner) {
|
|
812
|
-
spinner.start(
|
|
846
|
+
spinner.start('Preparing to reinstall BMad Method...');
|
|
813
847
|
|
|
814
848
|
// Remove existing .bmad-core
|
|
815
|
-
const bmadCorePath = path.join(installDir,
|
|
849
|
+
const bmadCorePath = path.join(installDir, '.bmad-core');
|
|
816
850
|
if (await fileManager.pathExists(bmadCorePath)) {
|
|
817
|
-
spinner.text =
|
|
851
|
+
spinner.text = 'Removing existing installation...';
|
|
818
852
|
await fileManager.removeDirectory(bmadCorePath);
|
|
819
853
|
}
|
|
820
|
-
|
|
821
|
-
spinner.text =
|
|
854
|
+
|
|
855
|
+
spinner.text = 'Installing fresh copy...';
|
|
822
856
|
const result = await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
|
|
823
|
-
|
|
857
|
+
|
|
824
858
|
// Clean up .yml files that now have .yaml counterparts
|
|
825
|
-
spinner.text =
|
|
859
|
+
spinner.text = 'Cleaning up legacy .yml files...';
|
|
826
860
|
await this.cleanupLegacyYmlFiles(installDir, spinner);
|
|
827
|
-
|
|
861
|
+
|
|
828
862
|
return result;
|
|
829
863
|
}
|
|
830
864
|
|
|
831
865
|
showSuccessMessage(config, installDir, options = {}) {
|
|
832
|
-
console.log(chalk.green(
|
|
866
|
+
console.log(chalk.green('\n✓ BMad Method installed successfully!\n'));
|
|
833
867
|
|
|
834
868
|
const ides = config.ides || (config.ide ? [config.ide] : []);
|
|
835
869
|
if (ides.length > 0) {
|
|
836
870
|
for (const ide of ides) {
|
|
837
871
|
const ideConfig = configLoader.getIdeConfiguration(ide);
|
|
838
872
|
if (ideConfig?.instructions) {
|
|
839
|
-
console.log(
|
|
840
|
-
chalk.bold(`To use BMad agents in ${ideConfig.name}:`)
|
|
841
|
-
);
|
|
873
|
+
console.log(chalk.bold(`To use BMad agents in ${ideConfig.name}:`));
|
|
842
874
|
console.log(ideConfig.instructions);
|
|
843
875
|
}
|
|
844
876
|
}
|
|
845
877
|
} else {
|
|
846
|
-
console.log(chalk.yellow(
|
|
847
|
-
console.log(
|
|
848
|
-
"You can manually configure your IDE using the agent files in:",
|
|
849
|
-
installDir
|
|
850
|
-
);
|
|
878
|
+
console.log(chalk.yellow('No IDE configuration was set up.'));
|
|
879
|
+
console.log('You can manually configure your IDE using the agent files in:', installDir);
|
|
851
880
|
}
|
|
852
881
|
|
|
853
882
|
// Information about installation components
|
|
854
|
-
console.log(chalk.bold(
|
|
855
|
-
if (config.installType !==
|
|
856
|
-
console.log(chalk.green(
|
|
883
|
+
console.log(chalk.bold('\n🎯 Installation Summary:'));
|
|
884
|
+
if (config.installType !== 'expansion-only') {
|
|
885
|
+
console.log(chalk.green('✓ .bmad-core framework installed with all agents and workflows'));
|
|
857
886
|
}
|
|
858
|
-
|
|
887
|
+
|
|
859
888
|
if (config.expansionPacks && config.expansionPacks.length > 0) {
|
|
860
889
|
console.log(chalk.green(`✓ Expansion packs installed:`));
|
|
861
890
|
for (const packId of config.expansionPacks) {
|
|
862
891
|
console.log(chalk.green(` - ${packId} → .${packId}/`));
|
|
863
892
|
}
|
|
864
893
|
}
|
|
865
|
-
|
|
894
|
+
|
|
866
895
|
if (config.includeWebBundles && config.webBundlesDirectory) {
|
|
867
896
|
const bundleInfo = this.getWebBundleInfo(config);
|
|
868
897
|
// Resolve the web bundles directory for display
|
|
869
898
|
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
|
870
|
-
const resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
|
|
871
|
-
? config.webBundlesDirectory
|
|
899
|
+
const resolvedWebBundlesDir = path.isAbsolute(config.webBundlesDirectory)
|
|
900
|
+
? config.webBundlesDirectory
|
|
872
901
|
: path.resolve(originalCwd, config.webBundlesDirectory);
|
|
873
|
-
console.log(
|
|
902
|
+
console.log(
|
|
903
|
+
chalk.green(`✓ Web bundles (${bundleInfo}) installed to: ${resolvedWebBundlesDir}`),
|
|
904
|
+
);
|
|
874
905
|
}
|
|
875
|
-
|
|
906
|
+
|
|
876
907
|
if (ides.length > 0) {
|
|
877
|
-
const ideNames = ides
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
908
|
+
const ideNames = ides
|
|
909
|
+
.map((ide) => {
|
|
910
|
+
const ideConfig = configLoader.getIdeConfiguration(ide);
|
|
911
|
+
return ideConfig?.name || ide;
|
|
912
|
+
})
|
|
913
|
+
.join(', ');
|
|
881
914
|
console.log(chalk.green(`✓ IDE rules and configurations set up for: ${ideNames}`));
|
|
882
915
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
916
|
|
|
886
917
|
// Information about web bundles
|
|
887
918
|
if (!config.includeWebBundles) {
|
|
888
|
-
console.log(chalk.bold(
|
|
889
|
-
console.log(
|
|
890
|
-
console.log(chalk.cyan(
|
|
891
|
-
console.log(
|
|
892
|
-
console.log(
|
|
919
|
+
console.log(chalk.bold('\n📦 Web Bundles Available:'));
|
|
920
|
+
console.log('Pre-built web bundles are available and can be added later:');
|
|
921
|
+
console.log(chalk.cyan(' Run the installer again to add them to your project'));
|
|
922
|
+
console.log('These bundles work independently and can be shared, moved, or used');
|
|
923
|
+
console.log('in other projects as standalone files.');
|
|
893
924
|
}
|
|
894
925
|
|
|
895
|
-
if (config.installType ===
|
|
896
|
-
console.log(
|
|
897
|
-
|
|
898
|
-
"\nNeed other agents? Run: npx bmad-method install --agent=<name>"
|
|
899
|
-
)
|
|
900
|
-
);
|
|
901
|
-
console.log(
|
|
902
|
-
chalk.dim("Need everything? Run: npx bmad-method install --full")
|
|
903
|
-
);
|
|
926
|
+
if (config.installType === 'single-agent') {
|
|
927
|
+
console.log(chalk.dim('\nNeed other agents? Run: npx bmad-method install --agent=<name>'));
|
|
928
|
+
console.log(chalk.dim('Need everything? Run: npx bmad-method install --full'));
|
|
904
929
|
}
|
|
905
930
|
|
|
906
931
|
// Warning for Cursor custom modes if agents were updated
|
|
907
932
|
if (options.isUpdate && ides.includes('cursor')) {
|
|
908
|
-
console.log(chalk.yellow.bold(
|
|
909
|
-
console.log(
|
|
933
|
+
console.log(chalk.yellow.bold('\n⚠️ IMPORTANT: Cursor Custom Modes Update Required'));
|
|
934
|
+
console.log(
|
|
935
|
+
chalk.yellow(
|
|
936
|
+
'Since agents have been updated, you need to update any custom agent modes configured in the Cursor custom agent GUI per the Cursor docs.',
|
|
937
|
+
),
|
|
938
|
+
);
|
|
910
939
|
}
|
|
911
940
|
|
|
912
941
|
// Important notice to read the user guide
|
|
913
|
-
console.log(
|
|
914
|
-
|
|
942
|
+
console.log(
|
|
943
|
+
chalk.red.bold(
|
|
944
|
+
'\n📖 IMPORTANT: Please read the user guide at docs/user-guide.md (also installed at .bmad-core/user-guide.md)',
|
|
945
|
+
),
|
|
946
|
+
);
|
|
947
|
+
console.log(
|
|
948
|
+
chalk.red(
|
|
949
|
+
'This guide contains essential information about the BMad workflow and how to use the agents effectively.',
|
|
950
|
+
),
|
|
951
|
+
);
|
|
915
952
|
}
|
|
916
953
|
|
|
917
954
|
// Legacy method for backward compatibility
|
|
918
955
|
async update() {
|
|
919
956
|
console.log(chalk.yellow('The "update" command is deprecated.'));
|
|
920
957
|
console.log(
|
|
921
|
-
'Please use "install" instead - it will detect and offer to update existing installations.'
|
|
958
|
+
'Please use "install" instead - it will detect and offer to update existing installations.',
|
|
922
959
|
);
|
|
923
960
|
|
|
924
961
|
const installDir = await this.findInstallation();
|
|
925
962
|
if (installDir) {
|
|
926
963
|
const config = {
|
|
927
|
-
installType:
|
|
964
|
+
installType: 'full',
|
|
928
965
|
directory: path.dirname(installDir),
|
|
929
966
|
ide: null,
|
|
930
967
|
};
|
|
931
968
|
return await this.install(config);
|
|
932
969
|
}
|
|
933
|
-
console.log(chalk.red(
|
|
970
|
+
console.log(chalk.red('No BMad installation found.'));
|
|
934
971
|
}
|
|
935
972
|
|
|
936
973
|
async listAgents() {
|
|
937
974
|
const agents = await resourceLocator.getAvailableAgents();
|
|
938
975
|
|
|
939
|
-
console.log(chalk.bold(
|
|
976
|
+
console.log(chalk.bold('\nAvailable BMad Agents:\n'));
|
|
940
977
|
|
|
941
978
|
for (const agent of agents) {
|
|
942
979
|
console.log(chalk.cyan(` ${agent.id.padEnd(20)}`), agent.description);
|
|
943
980
|
}
|
|
944
981
|
|
|
945
|
-
console.log(
|
|
946
|
-
chalk.dim("\nInstall with: npx bmad-method install --agent=<id>\n")
|
|
947
|
-
);
|
|
982
|
+
console.log(chalk.dim('\nInstall with: npx bmad-method install --agent=<id>\n'));
|
|
948
983
|
}
|
|
949
984
|
|
|
950
985
|
async listExpansionPacks() {
|
|
951
986
|
const expansionPacks = await resourceLocator.getExpansionPacks();
|
|
952
987
|
|
|
953
|
-
console.log(chalk.bold(
|
|
988
|
+
console.log(chalk.bold('\nAvailable BMad Expansion Packs:\n'));
|
|
954
989
|
|
|
955
990
|
if (expansionPacks.length === 0) {
|
|
956
|
-
console.log(chalk.yellow(
|
|
991
|
+
console.log(chalk.yellow('No expansion packs found.'));
|
|
957
992
|
return;
|
|
958
993
|
}
|
|
959
994
|
|
|
960
995
|
for (const pack of expansionPacks) {
|
|
961
|
-
console.log(chalk.cyan(` ${pack.id.padEnd(20)}`),
|
|
962
|
-
`${pack.name} v${pack.version}`);
|
|
996
|
+
console.log(chalk.cyan(` ${pack.id.padEnd(20)}`), `${pack.name} v${pack.version}`);
|
|
963
997
|
console.log(chalk.dim(` ${' '.repeat(22)}${pack.description}`));
|
|
964
998
|
if (pack.author && pack.author !== 'Unknown') {
|
|
965
999
|
console.log(chalk.dim(` ${' '.repeat(22)}by ${pack.author}`));
|
|
@@ -967,36 +1001,28 @@ class Installer {
|
|
|
967
1001
|
console.log();
|
|
968
1002
|
}
|
|
969
1003
|
|
|
970
|
-
console.log(
|
|
971
|
-
chalk.dim("Install with: npx bmad-method install --full --expansion-packs <id>\n")
|
|
972
|
-
);
|
|
1004
|
+
console.log(chalk.dim('Install with: npx bmad-method install --full --expansion-packs <id>\n'));
|
|
973
1005
|
}
|
|
974
1006
|
|
|
975
1007
|
async showStatus() {
|
|
976
1008
|
const installDir = await this.findInstallation();
|
|
977
1009
|
|
|
978
1010
|
if (!installDir) {
|
|
979
|
-
console.log(
|
|
980
|
-
chalk.yellow("No BMad installation found in current directory tree")
|
|
981
|
-
);
|
|
1011
|
+
console.log(chalk.yellow('No BMad installation found in current directory tree'));
|
|
982
1012
|
return;
|
|
983
1013
|
}
|
|
984
1014
|
|
|
985
1015
|
const manifest = await fileManager.readManifest(installDir);
|
|
986
1016
|
|
|
987
1017
|
if (!manifest) {
|
|
988
|
-
console.log(chalk.red(
|
|
1018
|
+
console.log(chalk.red('Invalid installation - manifest not found'));
|
|
989
1019
|
return;
|
|
990
1020
|
}
|
|
991
1021
|
|
|
992
|
-
console.log(chalk.bold(
|
|
1022
|
+
console.log(chalk.bold('\nBMad Installation Status:\n'));
|
|
993
1023
|
console.log(` Directory: ${installDir}`);
|
|
994
1024
|
console.log(` Version: ${manifest.version}`);
|
|
995
|
-
console.log(
|
|
996
|
-
` Installed: ${new Date(
|
|
997
|
-
manifest.installed_at
|
|
998
|
-
).toLocaleDateString()}`
|
|
999
|
-
);
|
|
1025
|
+
console.log(` Installed: ${new Date(manifest.installed_at).toLocaleDateString()}`);
|
|
1000
1026
|
console.log(` Type: ${manifest.install_type}`);
|
|
1001
1027
|
|
|
1002
1028
|
if (manifest.agent) {
|
|
@@ -1010,15 +1036,12 @@ class Installer {
|
|
|
1010
1036
|
console.log(` Total Files: ${manifest.files.length}`);
|
|
1011
1037
|
|
|
1012
1038
|
// Check for modifications
|
|
1013
|
-
const modifiedFiles = await fileManager.checkModifiedFiles(
|
|
1014
|
-
installDir,
|
|
1015
|
-
manifest
|
|
1016
|
-
);
|
|
1039
|
+
const modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest);
|
|
1017
1040
|
if (modifiedFiles.length > 0) {
|
|
1018
1041
|
console.log(chalk.yellow(` Modified Files: ${modifiedFiles.length}`));
|
|
1019
1042
|
}
|
|
1020
1043
|
|
|
1021
|
-
console.log(
|
|
1044
|
+
console.log('');
|
|
1022
1045
|
}
|
|
1023
1046
|
|
|
1024
1047
|
async getAvailableAgents() {
|
|
@@ -1042,34 +1065,35 @@ class Installer {
|
|
|
1042
1065
|
|
|
1043
1066
|
for (const packId of selectedPacks) {
|
|
1044
1067
|
spinner.text = `Installing expansion pack: ${packId}...`;
|
|
1045
|
-
|
|
1068
|
+
|
|
1046
1069
|
try {
|
|
1047
1070
|
const expansionPacks = await resourceLocator.getExpansionPacks();
|
|
1048
|
-
const pack = expansionPacks.find(p => p.id === packId);
|
|
1049
|
-
|
|
1071
|
+
const pack = expansionPacks.find((p) => p.id === packId);
|
|
1072
|
+
|
|
1050
1073
|
if (!pack) {
|
|
1051
1074
|
console.warn(`Expansion pack ${packId} not found, skipping...`);
|
|
1052
1075
|
continue;
|
|
1053
1076
|
}
|
|
1054
|
-
|
|
1077
|
+
|
|
1055
1078
|
// Check if expansion pack already exists
|
|
1056
1079
|
let expansionDotFolder = path.join(installDir, `.${packId}`);
|
|
1057
1080
|
const existingManifestPath = path.join(expansionDotFolder, 'install-manifest.yaml');
|
|
1058
|
-
|
|
1081
|
+
|
|
1059
1082
|
if (await fileManager.pathExists(existingManifestPath)) {
|
|
1060
1083
|
spinner.stop();
|
|
1061
1084
|
const existingManifest = await fileManager.readExpansionPackManifest(installDir, packId);
|
|
1062
|
-
|
|
1085
|
+
|
|
1063
1086
|
console.log(chalk.yellow(`\n🔍 Found existing ${pack.name} installation`));
|
|
1064
1087
|
console.log(` Current version: ${existingManifest.version || 'unknown'}`);
|
|
1065
1088
|
console.log(` New version: ${pack.version}`);
|
|
1066
|
-
|
|
1089
|
+
|
|
1067
1090
|
// Check integrity of existing expansion pack
|
|
1068
1091
|
const packIntegrity = await fileManager.checkFileIntegrity(installDir, existingManifest);
|
|
1069
|
-
const hasPackIntegrityIssues =
|
|
1070
|
-
|
|
1092
|
+
const hasPackIntegrityIssues =
|
|
1093
|
+
packIntegrity.missing.length > 0 || packIntegrity.modified.length > 0;
|
|
1094
|
+
|
|
1071
1095
|
if (hasPackIntegrityIssues) {
|
|
1072
|
-
console.log(chalk.red(
|
|
1096
|
+
console.log(chalk.red(' ⚠️ Installation issues detected:'));
|
|
1073
1097
|
if (packIntegrity.missing.length > 0) {
|
|
1074
1098
|
console.log(chalk.red(` Missing files: ${packIntegrity.missing.length}`));
|
|
1075
1099
|
}
|
|
@@ -1077,12 +1101,15 @@ class Installer {
|
|
|
1077
1101
|
console.log(chalk.yellow(` Modified files: ${packIntegrity.modified.length}`));
|
|
1078
1102
|
}
|
|
1079
1103
|
}
|
|
1080
|
-
|
|
1081
|
-
const versionCompare = this.compareVersions(
|
|
1082
|
-
|
|
1104
|
+
|
|
1105
|
+
const versionCompare = this.compareVersions(
|
|
1106
|
+
existingManifest.version || '0.0.0',
|
|
1107
|
+
pack.version,
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1083
1110
|
if (versionCompare === 0) {
|
|
1084
1111
|
console.log(chalk.yellow(' ⚠️ Same version already installed'));
|
|
1085
|
-
|
|
1112
|
+
|
|
1086
1113
|
const choices = [];
|
|
1087
1114
|
if (hasPackIntegrityIssues) {
|
|
1088
1115
|
choices.push({ name: 'Repair (restore missing/modified files)', value: 'repair' });
|
|
@@ -1090,75 +1117,92 @@ class Installer {
|
|
|
1090
1117
|
choices.push(
|
|
1091
1118
|
{ name: 'Force reinstall (overwrite)', value: 'overwrite' },
|
|
1092
1119
|
{ name: 'Skip this expansion pack', value: 'skip' },
|
|
1093
|
-
{ name: 'Cancel installation', value: 'cancel' }
|
|
1120
|
+
{ name: 'Cancel installation', value: 'cancel' },
|
|
1094
1121
|
);
|
|
1095
|
-
|
|
1096
|
-
const { action } = await inquirer.prompt([
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1122
|
+
|
|
1123
|
+
const { action } = await inquirer.prompt([
|
|
1124
|
+
{
|
|
1125
|
+
type: 'list',
|
|
1126
|
+
name: 'action',
|
|
1127
|
+
message: `${pack.name} v${pack.version} is already installed. What would you like to do?`,
|
|
1128
|
+
choices: choices,
|
|
1129
|
+
},
|
|
1130
|
+
]);
|
|
1131
|
+
|
|
1132
|
+
switch (action) {
|
|
1133
|
+
case 'skip': {
|
|
1134
|
+
spinner.start();
|
|
1135
|
+
continue;
|
|
1136
|
+
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
case 'cancel': {
|
|
1107
1140
|
console.log('Installation cancelled.');
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1141
|
+
process.exit(0);
|
|
1142
|
+
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
case 'repair': {
|
|
1146
|
+
// Repair the expansion pack
|
|
1147
|
+
await this.repairExpansionPack(installDir, packId, pack, packIntegrity, spinner);
|
|
1148
|
+
continue;
|
|
1149
|
+
|
|
1150
|
+
break;
|
|
1151
|
+
}
|
|
1152
|
+
// No default
|
|
1113
1153
|
}
|
|
1114
1154
|
} else if (versionCompare < 0) {
|
|
1115
1155
|
console.log(chalk.cyan(' ⬆️ Upgrade available'));
|
|
1116
|
-
|
|
1117
|
-
const { proceed } = await inquirer.prompt([
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1156
|
+
|
|
1157
|
+
const { proceed } = await inquirer.prompt([
|
|
1158
|
+
{
|
|
1159
|
+
type: 'confirm',
|
|
1160
|
+
name: 'proceed',
|
|
1161
|
+
message: `Upgrade ${pack.name} from v${existingManifest.version} to v${pack.version}?`,
|
|
1162
|
+
default: true,
|
|
1163
|
+
},
|
|
1164
|
+
]);
|
|
1165
|
+
|
|
1124
1166
|
if (!proceed) {
|
|
1125
1167
|
spinner.start();
|
|
1126
1168
|
continue;
|
|
1127
1169
|
}
|
|
1128
1170
|
} else {
|
|
1129
1171
|
console.log(chalk.yellow(' ⬇️ Installed version is newer than available version'));
|
|
1130
|
-
|
|
1131
|
-
const { action } = await inquirer.prompt([
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1172
|
+
|
|
1173
|
+
const { action } = await inquirer.prompt([
|
|
1174
|
+
{
|
|
1175
|
+
type: 'list',
|
|
1176
|
+
name: 'action',
|
|
1177
|
+
message: 'What would you like to do?',
|
|
1178
|
+
choices: [
|
|
1179
|
+
{ name: 'Keep current version', value: 'skip' },
|
|
1180
|
+
{ name: 'Downgrade to available version', value: 'downgrade' },
|
|
1181
|
+
{ name: 'Cancel installation', value: 'cancel' },
|
|
1182
|
+
],
|
|
1183
|
+
},
|
|
1184
|
+
]);
|
|
1185
|
+
|
|
1142
1186
|
if (action === 'skip') {
|
|
1143
1187
|
spinner.start();
|
|
1144
1188
|
continue;
|
|
1145
1189
|
} else if (action === 'cancel') {
|
|
1146
|
-
|
|
1190
|
+
console.log('Installation cancelled.');
|
|
1147
1191
|
process.exit(0);
|
|
1148
1192
|
}
|
|
1149
1193
|
}
|
|
1150
|
-
|
|
1194
|
+
|
|
1151
1195
|
// If we get here, we're proceeding with installation
|
|
1152
1196
|
spinner.start(`Removing old ${pack.name} installation...`);
|
|
1153
1197
|
await fileManager.removeDirectory(expansionDotFolder);
|
|
1154
1198
|
}
|
|
1155
1199
|
|
|
1156
1200
|
const expansionPackDir = pack.path;
|
|
1157
|
-
|
|
1201
|
+
|
|
1158
1202
|
// Ensure dedicated dot folder exists for this expansion pack
|
|
1159
1203
|
expansionDotFolder = path.join(installDir, `.${packId}`);
|
|
1160
1204
|
await fileManager.ensureDirectory(expansionDotFolder);
|
|
1161
|
-
|
|
1205
|
+
|
|
1162
1206
|
// Define the folders to copy from expansion packs
|
|
1163
1207
|
const foldersToSync = [
|
|
1164
1208
|
'agents',
|
|
@@ -1169,35 +1213,34 @@ class Installer {
|
|
|
1169
1213
|
'workflows',
|
|
1170
1214
|
'data',
|
|
1171
1215
|
'utils',
|
|
1172
|
-
'schemas'
|
|
1216
|
+
'schemas',
|
|
1173
1217
|
];
|
|
1174
1218
|
|
|
1175
1219
|
// Copy each folder if it exists
|
|
1176
1220
|
for (const folder of foldersToSync) {
|
|
1177
1221
|
const sourceFolder = path.join(expansionPackDir, folder);
|
|
1178
|
-
|
|
1222
|
+
|
|
1179
1223
|
// Check if folder exists in expansion pack
|
|
1180
1224
|
if (await fileManager.pathExists(sourceFolder)) {
|
|
1181
1225
|
// Get all files in this folder
|
|
1182
1226
|
const files = await resourceLocator.findFiles('**/*', {
|
|
1183
1227
|
cwd: sourceFolder,
|
|
1184
|
-
nodir: true
|
|
1228
|
+
nodir: true,
|
|
1185
1229
|
});
|
|
1186
1230
|
|
|
1187
1231
|
// Copy each file to the expansion pack's dot folder with {root} replacement
|
|
1188
1232
|
for (const file of files) {
|
|
1189
1233
|
const sourcePath = path.join(sourceFolder, file);
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
const needsRootReplacement =
|
|
1234
|
+
const destinationPath = path.join(expansionDotFolder, folder, file);
|
|
1235
|
+
|
|
1236
|
+
const needsRootReplacement =
|
|
1237
|
+
file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml');
|
|
1193
1238
|
let success = false;
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1239
|
+
|
|
1240
|
+
success = await (needsRootReplacement
|
|
1241
|
+
? fileManager.copyFileWithRootReplacement(sourcePath, destinationPath, `.${packId}`)
|
|
1242
|
+
: fileManager.copyFile(sourcePath, destinationPath));
|
|
1243
|
+
|
|
1201
1244
|
if (success) {
|
|
1202
1245
|
installedFiles.push(path.join(`.${packId}`, folder, file));
|
|
1203
1246
|
}
|
|
@@ -1208,17 +1251,29 @@ class Installer {
|
|
|
1208
1251
|
// Copy config.yaml with {root} replacement
|
|
1209
1252
|
const configPath = path.join(expansionPackDir, 'config.yaml');
|
|
1210
1253
|
if (await fileManager.pathExists(configPath)) {
|
|
1211
|
-
const
|
|
1212
|
-
if (
|
|
1254
|
+
const configDestinationPath = path.join(expansionDotFolder, 'config.yaml');
|
|
1255
|
+
if (
|
|
1256
|
+
await fileManager.copyFileWithRootReplacement(
|
|
1257
|
+
configPath,
|
|
1258
|
+
configDestinationPath,
|
|
1259
|
+
`.${packId}`,
|
|
1260
|
+
)
|
|
1261
|
+
) {
|
|
1213
1262
|
installedFiles.push(path.join(`.${packId}`, 'config.yaml'));
|
|
1214
1263
|
}
|
|
1215
1264
|
}
|
|
1216
|
-
|
|
1265
|
+
|
|
1217
1266
|
// Copy README if it exists with {root} replacement
|
|
1218
1267
|
const readmePath = path.join(expansionPackDir, 'README.md');
|
|
1219
1268
|
if (await fileManager.pathExists(readmePath)) {
|
|
1220
|
-
const
|
|
1221
|
-
if (
|
|
1269
|
+
const readmeDestinationPath = path.join(expansionDotFolder, 'README.md');
|
|
1270
|
+
if (
|
|
1271
|
+
await fileManager.copyFileWithRootReplacement(
|
|
1272
|
+
readmePath,
|
|
1273
|
+
readmeDestinationPath,
|
|
1274
|
+
`.${packId}`,
|
|
1275
|
+
)
|
|
1276
|
+
) {
|
|
1222
1277
|
installedFiles.push(path.join(`.${packId}`, 'README.md'));
|
|
1223
1278
|
}
|
|
1224
1279
|
}
|
|
@@ -1226,10 +1281,16 @@ class Installer {
|
|
|
1226
1281
|
// Copy common/ items to expansion pack folder
|
|
1227
1282
|
spinner.text = `Copying common utilities to ${packId}...`;
|
|
1228
1283
|
await this.copyCommonItems(installDir, `.${packId}`, spinner);
|
|
1229
|
-
|
|
1284
|
+
|
|
1230
1285
|
// Check and resolve core dependencies
|
|
1231
|
-
await this.resolveExpansionPackCoreDependencies(
|
|
1232
|
-
|
|
1286
|
+
await this.resolveExpansionPackCoreDependencies(
|
|
1287
|
+
installDir,
|
|
1288
|
+
expansionDotFolder,
|
|
1289
|
+
packId,
|
|
1290
|
+
pack,
|
|
1291
|
+
spinner,
|
|
1292
|
+
);
|
|
1293
|
+
|
|
1233
1294
|
// Check and resolve core agents referenced by teams
|
|
1234
1295
|
await this.resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner);
|
|
1235
1296
|
|
|
@@ -1240,17 +1301,22 @@ class Installer {
|
|
|
1240
1301
|
expansionPackId: packId,
|
|
1241
1302
|
expansionPackName: pack.name,
|
|
1242
1303
|
expansionPackVersion: pack.version,
|
|
1243
|
-
ides: config.ides || []
|
|
1304
|
+
ides: config.ides || [], // Use ides_setup instead of ide_setup
|
|
1244
1305
|
};
|
|
1245
|
-
|
|
1306
|
+
|
|
1246
1307
|
// Get all files installed in this expansion pack
|
|
1247
1308
|
const foundFiles = await resourceLocator.findFiles('**/*', {
|
|
1248
1309
|
cwd: expansionDotFolder,
|
|
1249
|
-
nodir: true
|
|
1310
|
+
nodir: true,
|
|
1250
1311
|
});
|
|
1251
|
-
const expansionPackFiles = foundFiles.map(f => path.join(`.${packId}`, f));
|
|
1252
|
-
|
|
1253
|
-
await fileManager.createExpansionPackManifest(
|
|
1312
|
+
const expansionPackFiles = foundFiles.map((f) => path.join(`.${packId}`, f));
|
|
1313
|
+
|
|
1314
|
+
await fileManager.createExpansionPackManifest(
|
|
1315
|
+
installDir,
|
|
1316
|
+
packId,
|
|
1317
|
+
expansionConfig,
|
|
1318
|
+
expansionPackFiles,
|
|
1319
|
+
);
|
|
1254
1320
|
|
|
1255
1321
|
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name} to ${`.${packId}`}`));
|
|
1256
1322
|
} catch (error) {
|
|
@@ -1262,63 +1328,96 @@ class Installer {
|
|
|
1262
1328
|
return installedFiles;
|
|
1263
1329
|
}
|
|
1264
1330
|
|
|
1265
|
-
async resolveExpansionPackCoreDependencies(
|
|
1331
|
+
async resolveExpansionPackCoreDependencies(
|
|
1332
|
+
installDir,
|
|
1333
|
+
expansionDotFolder,
|
|
1334
|
+
packId,
|
|
1335
|
+
pack,
|
|
1336
|
+
spinner,
|
|
1337
|
+
) {
|
|
1266
1338
|
const yaml = require('js-yaml');
|
|
1267
|
-
const fs = require('fs').promises;
|
|
1268
|
-
|
|
1339
|
+
const fs = require('node:fs').promises;
|
|
1340
|
+
|
|
1269
1341
|
// Find all agent files in the expansion pack
|
|
1270
1342
|
const agentFiles = await resourceLocator.findFiles('agents/*.md', {
|
|
1271
|
-
cwd: expansionDotFolder
|
|
1343
|
+
cwd: expansionDotFolder,
|
|
1272
1344
|
});
|
|
1273
1345
|
|
|
1274
1346
|
for (const agentFile of agentFiles) {
|
|
1275
1347
|
const agentPath = path.join(expansionDotFolder, agentFile);
|
|
1276
1348
|
const agentContent = await fs.readFile(agentPath, 'utf8');
|
|
1277
|
-
|
|
1349
|
+
|
|
1278
1350
|
// Extract YAML frontmatter to check dependencies
|
|
1279
1351
|
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1280
1352
|
if (yamlContent) {
|
|
1281
1353
|
try {
|
|
1282
1354
|
const agentConfig = yaml.load(yamlContent);
|
|
1283
1355
|
const dependencies = agentConfig.dependencies || {};
|
|
1284
|
-
|
|
1356
|
+
|
|
1285
1357
|
// Check for core dependencies (those that don't exist in the expansion pack)
|
|
1286
|
-
for (const depType of [
|
|
1358
|
+
for (const depType of [
|
|
1359
|
+
'tasks',
|
|
1360
|
+
'templates',
|
|
1361
|
+
'checklists',
|
|
1362
|
+
'workflows',
|
|
1363
|
+
'utils',
|
|
1364
|
+
'data',
|
|
1365
|
+
]) {
|
|
1287
1366
|
const deps = dependencies[depType] || [];
|
|
1288
|
-
|
|
1367
|
+
|
|
1289
1368
|
for (const dep of deps) {
|
|
1290
|
-
const depFileName =
|
|
1291
|
-
|
|
1369
|
+
const depFileName =
|
|
1370
|
+
dep.endsWith('.md') || dep.endsWith('.yaml')
|
|
1371
|
+
? dep
|
|
1372
|
+
: depType === 'templates'
|
|
1373
|
+
? `${dep}.yaml`
|
|
1374
|
+
: `${dep}.md`;
|
|
1292
1375
|
const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1293
|
-
|
|
1376
|
+
|
|
1294
1377
|
// Check if dependency exists in expansion pack dot folder
|
|
1295
1378
|
if (!(await fileManager.pathExists(expansionDepPath))) {
|
|
1296
1379
|
// Try to find it in expansion pack source
|
|
1297
1380
|
const sourceDepPath = path.join(pack.path, depType, depFileName);
|
|
1298
|
-
|
|
1381
|
+
|
|
1299
1382
|
if (await fileManager.pathExists(sourceDepPath)) {
|
|
1300
1383
|
// Copy from expansion pack source
|
|
1301
1384
|
spinner.text = `Copying ${packId} dependency ${dep}...`;
|
|
1302
|
-
const
|
|
1303
|
-
await fileManager.copyFileWithRootReplacement(
|
|
1385
|
+
const destinationPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1386
|
+
await fileManager.copyFileWithRootReplacement(
|
|
1387
|
+
sourceDepPath,
|
|
1388
|
+
destinationPath,
|
|
1389
|
+
`.${packId}`,
|
|
1390
|
+
);
|
|
1304
1391
|
console.log(chalk.dim(` Added ${packId} dependency: ${depType}/${depFileName}`));
|
|
1305
1392
|
} else {
|
|
1306
1393
|
// Try to find it in core
|
|
1307
|
-
const coreDepPath = path.join(
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1394
|
+
const coreDepPath = path.join(
|
|
1395
|
+
resourceLocator.getBmadCorePath(),
|
|
1396
|
+
depType,
|
|
1397
|
+
depFileName,
|
|
1398
|
+
);
|
|
1399
|
+
|
|
1400
|
+
if (await fileManager.pathExists(coreDepPath)) {
|
|
1401
|
+
spinner.text = `Copying core dependency ${dep} for ${packId}...`;
|
|
1402
|
+
|
|
1403
|
+
// Copy from core to expansion pack dot folder with {root} replacement
|
|
1404
|
+
const destinationPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1405
|
+
await fileManager.copyFileWithRootReplacement(
|
|
1406
|
+
coreDepPath,
|
|
1407
|
+
destinationPath,
|
|
1408
|
+
`.${packId}`,
|
|
1409
|
+
);
|
|
1410
|
+
|
|
1411
|
+
console.log(chalk.dim(` Added core dependency: ${depType}/${depFileName}`));
|
|
1412
|
+
} else {
|
|
1413
|
+
console.warn(
|
|
1414
|
+
chalk.yellow(
|
|
1415
|
+
` Warning: Dependency ${depType}/${dep} not found in core or expansion pack`,
|
|
1416
|
+
),
|
|
1417
|
+
);
|
|
1320
1418
|
}
|
|
1321
1419
|
}
|
|
1420
|
+
}
|
|
1322
1421
|
}
|
|
1323
1422
|
}
|
|
1324
1423
|
} catch (error) {
|
|
@@ -1330,17 +1429,17 @@ class Installer {
|
|
|
1330
1429
|
|
|
1331
1430
|
async resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner) {
|
|
1332
1431
|
const yaml = require('js-yaml');
|
|
1333
|
-
const fs = require('fs').promises;
|
|
1334
|
-
|
|
1432
|
+
const fs = require('node:fs').promises;
|
|
1433
|
+
|
|
1335
1434
|
// Find all team files in the expansion pack
|
|
1336
1435
|
const teamFiles = await resourceLocator.findFiles('agent-teams/*.yaml', {
|
|
1337
|
-
cwd: expansionDotFolder
|
|
1436
|
+
cwd: expansionDotFolder,
|
|
1338
1437
|
});
|
|
1339
1438
|
|
|
1340
1439
|
// Also get existing agents in the expansion pack
|
|
1341
1440
|
const existingAgents = new Set();
|
|
1342
1441
|
const agentFiles = await resourceLocator.findFiles('agents/*.md', {
|
|
1343
|
-
cwd: expansionDotFolder
|
|
1442
|
+
cwd: expansionDotFolder,
|
|
1344
1443
|
});
|
|
1345
1444
|
for (const agentFile of agentFiles) {
|
|
1346
1445
|
const agentName = path.basename(agentFile, '.md');
|
|
@@ -1351,79 +1450,132 @@ class Installer {
|
|
|
1351
1450
|
for (const teamFile of teamFiles) {
|
|
1352
1451
|
const teamPath = path.join(expansionDotFolder, teamFile);
|
|
1353
1452
|
const teamContent = await fs.readFile(teamPath, 'utf8');
|
|
1354
|
-
|
|
1453
|
+
|
|
1355
1454
|
try {
|
|
1356
1455
|
const teamConfig = yaml.load(teamContent);
|
|
1357
1456
|
const agents = teamConfig.agents || [];
|
|
1358
|
-
|
|
1457
|
+
|
|
1359
1458
|
// Add bmad-orchestrator if not present (required for all teams)
|
|
1360
1459
|
if (!agents.includes('bmad-orchestrator')) {
|
|
1361
1460
|
agents.unshift('bmad-orchestrator');
|
|
1362
1461
|
}
|
|
1363
|
-
|
|
1462
|
+
|
|
1364
1463
|
// Check each agent in the team
|
|
1365
1464
|
for (const agentId of agents) {
|
|
1366
1465
|
if (!existingAgents.has(agentId)) {
|
|
1367
1466
|
// Agent not in expansion pack, try to get from core
|
|
1368
|
-
const coreAgentPath = path.join(
|
|
1369
|
-
|
|
1467
|
+
const coreAgentPath = path.join(
|
|
1468
|
+
resourceLocator.getBmadCorePath(),
|
|
1469
|
+
'agents',
|
|
1470
|
+
`${agentId}.md`,
|
|
1471
|
+
);
|
|
1472
|
+
|
|
1370
1473
|
if (await fileManager.pathExists(coreAgentPath)) {
|
|
1371
1474
|
spinner.text = `Copying core agent ${agentId} for ${packId}...`;
|
|
1372
|
-
|
|
1475
|
+
|
|
1373
1476
|
// Copy agent file with {root} replacement
|
|
1374
|
-
const
|
|
1375
|
-
await fileManager.copyFileWithRootReplacement(
|
|
1477
|
+
const destinationPath = path.join(expansionDotFolder, 'agents', `${agentId}.md`);
|
|
1478
|
+
await fileManager.copyFileWithRootReplacement(
|
|
1479
|
+
coreAgentPath,
|
|
1480
|
+
destinationPath,
|
|
1481
|
+
`.${packId}`,
|
|
1482
|
+
);
|
|
1376
1483
|
existingAgents.add(agentId);
|
|
1377
|
-
|
|
1484
|
+
|
|
1378
1485
|
console.log(chalk.dim(` Added core agent: ${agentId}`));
|
|
1379
|
-
|
|
1486
|
+
|
|
1380
1487
|
// Now resolve this agent's dependencies too
|
|
1381
1488
|
const agentContent = await fs.readFile(coreAgentPath, 'utf8');
|
|
1382
1489
|
const yamlContent = extractYamlFromAgent(agentContent, true);
|
|
1383
|
-
|
|
1490
|
+
|
|
1384
1491
|
if (yamlContent) {
|
|
1385
1492
|
try {
|
|
1386
|
-
|
|
1387
1493
|
const agentConfig = yaml.load(yamlContent);
|
|
1388
1494
|
const dependencies = agentConfig.dependencies || {};
|
|
1389
|
-
|
|
1495
|
+
|
|
1390
1496
|
// Copy all dependencies for this agent
|
|
1391
|
-
for (const depType of [
|
|
1497
|
+
for (const depType of [
|
|
1498
|
+
'tasks',
|
|
1499
|
+
'templates',
|
|
1500
|
+
'checklists',
|
|
1501
|
+
'workflows',
|
|
1502
|
+
'utils',
|
|
1503
|
+
'data',
|
|
1504
|
+
]) {
|
|
1392
1505
|
const deps = dependencies[depType] || [];
|
|
1393
|
-
|
|
1506
|
+
|
|
1394
1507
|
for (const dep of deps) {
|
|
1395
|
-
const depFileName =
|
|
1396
|
-
|
|
1508
|
+
const depFileName =
|
|
1509
|
+
dep.endsWith('.md') || dep.endsWith('.yaml')
|
|
1510
|
+
? dep
|
|
1511
|
+
: depType === 'templates'
|
|
1512
|
+
? `${dep}.yaml`
|
|
1513
|
+
: `${dep}.md`;
|
|
1397
1514
|
const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
|
|
1398
|
-
|
|
1515
|
+
|
|
1399
1516
|
// Check if dependency exists in expansion pack
|
|
1400
1517
|
if (!(await fileManager.pathExists(expansionDepPath))) {
|
|
1401
1518
|
// Try to find it in core
|
|
1402
|
-
const coreDepPath = path.join(
|
|
1403
|
-
|
|
1519
|
+
const coreDepPath = path.join(
|
|
1520
|
+
resourceLocator.getBmadCorePath(),
|
|
1521
|
+
depType,
|
|
1522
|
+
depFileName,
|
|
1523
|
+
);
|
|
1524
|
+
|
|
1404
1525
|
if (await fileManager.pathExists(coreDepPath)) {
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1407
|
-
|
|
1526
|
+
const destinationDepPath = path.join(
|
|
1527
|
+
expansionDotFolder,
|
|
1528
|
+
depType,
|
|
1529
|
+
depFileName,
|
|
1530
|
+
);
|
|
1531
|
+
await fileManager.copyFileWithRootReplacement(
|
|
1532
|
+
coreDepPath,
|
|
1533
|
+
destinationDepPath,
|
|
1534
|
+
`.${packId}`,
|
|
1535
|
+
);
|
|
1536
|
+
console.log(
|
|
1537
|
+
chalk.dim(` Added agent dependency: ${depType}/${depFileName}`),
|
|
1538
|
+
);
|
|
1408
1539
|
} else {
|
|
1409
1540
|
// Try common folder
|
|
1410
|
-
const sourceBase = path.dirname(
|
|
1411
|
-
|
|
1541
|
+
const sourceBase = path.dirname(
|
|
1542
|
+
path.dirname(path.dirname(path.dirname(__filename))),
|
|
1543
|
+
); // Go up to project root
|
|
1544
|
+
const commonDepPath = path.join(
|
|
1545
|
+
sourceBase,
|
|
1546
|
+
'common',
|
|
1547
|
+
depType,
|
|
1548
|
+
depFileName,
|
|
1549
|
+
);
|
|
1412
1550
|
if (await fileManager.pathExists(commonDepPath)) {
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1551
|
+
const destinationDepPath = path.join(
|
|
1552
|
+
expansionDotFolder,
|
|
1553
|
+
depType,
|
|
1554
|
+
depFileName,
|
|
1555
|
+
);
|
|
1556
|
+
await fileManager.copyFile(commonDepPath, destinationDepPath);
|
|
1557
|
+
console.log(
|
|
1558
|
+
chalk.dim(
|
|
1559
|
+
` Added agent dependency from common: ${depType}/${depFileName}`,
|
|
1560
|
+
),
|
|
1561
|
+
);
|
|
1416
1562
|
}
|
|
1417
1563
|
}
|
|
1418
1564
|
}
|
|
1419
1565
|
}
|
|
1420
1566
|
}
|
|
1421
1567
|
} catch (error) {
|
|
1422
|
-
console.warn(
|
|
1568
|
+
console.warn(
|
|
1569
|
+
` Warning: Could not parse agent ${agentId} dependencies: ${error.message}`,
|
|
1570
|
+
);
|
|
1423
1571
|
}
|
|
1424
1572
|
}
|
|
1425
1573
|
} else {
|
|
1426
|
-
console.warn(
|
|
1574
|
+
console.warn(
|
|
1575
|
+
chalk.yellow(
|
|
1576
|
+
` Warning: Core agent ${agentId} not found for team ${path.basename(teamFile, '.yaml')}`,
|
|
1577
|
+
),
|
|
1578
|
+
);
|
|
1427
1579
|
}
|
|
1428
1580
|
}
|
|
1429
1581
|
}
|
|
@@ -1435,16 +1587,19 @@ class Installer {
|
|
|
1435
1587
|
|
|
1436
1588
|
getWebBundleInfo(config) {
|
|
1437
1589
|
const webBundleType = config.webBundleType || 'all';
|
|
1438
|
-
|
|
1590
|
+
|
|
1439
1591
|
switch (webBundleType) {
|
|
1440
|
-
case 'all':
|
|
1592
|
+
case 'all': {
|
|
1441
1593
|
return 'all bundles';
|
|
1442
|
-
|
|
1594
|
+
}
|
|
1595
|
+
case 'agents': {
|
|
1443
1596
|
return 'individual agents only';
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1597
|
+
}
|
|
1598
|
+
case 'teams': {
|
|
1599
|
+
return config.selectedWebBundleTeams
|
|
1600
|
+
? `teams: ${config.selectedWebBundleTeams.join(', ')}`
|
|
1601
|
+
: 'selected teams';
|
|
1602
|
+
}
|
|
1448
1603
|
case 'custom': {
|
|
1449
1604
|
const parts = [];
|
|
1450
1605
|
if (config.selectedWebBundleTeams && config.selectedWebBundleTeams.length > 0) {
|
|
@@ -1455,17 +1610,17 @@ class Installer {
|
|
|
1455
1610
|
}
|
|
1456
1611
|
return parts.length > 0 ? parts.join(' + ') : 'custom selection';
|
|
1457
1612
|
}
|
|
1458
|
-
default:
|
|
1613
|
+
default: {
|
|
1459
1614
|
return 'selected bundles';
|
|
1615
|
+
}
|
|
1460
1616
|
}
|
|
1461
1617
|
}
|
|
1462
1618
|
|
|
1463
1619
|
async installWebBundles(webBundlesDirectory, config, spinner) {
|
|
1464
|
-
|
|
1465
1620
|
try {
|
|
1466
1621
|
// Find the dist directory in the BMad installation
|
|
1467
1622
|
const distDir = configLoader.getDistPath();
|
|
1468
|
-
|
|
1623
|
+
|
|
1469
1624
|
if (!(await fileManager.pathExists(distDir))) {
|
|
1470
1625
|
console.warn('Web bundles not found. Run "npm run build" to generate them.');
|
|
1471
1626
|
return;
|
|
@@ -1473,18 +1628,21 @@ class Installer {
|
|
|
1473
1628
|
|
|
1474
1629
|
// Ensure web bundles directory exists
|
|
1475
1630
|
await fileManager.ensureDirectory(webBundlesDirectory);
|
|
1476
|
-
|
|
1631
|
+
|
|
1477
1632
|
const webBundleType = config.webBundleType || 'all';
|
|
1478
|
-
|
|
1633
|
+
|
|
1479
1634
|
if (webBundleType === 'all') {
|
|
1480
1635
|
// Copy the entire dist directory structure
|
|
1481
1636
|
await fileManager.copyDirectory(distDir, webBundlesDirectory);
|
|
1482
1637
|
console.log(chalk.green(`✓ Installed all web bundles to: ${webBundlesDirectory}`));
|
|
1483
1638
|
} else {
|
|
1484
1639
|
let copiedCount = 0;
|
|
1485
|
-
|
|
1640
|
+
|
|
1486
1641
|
// Copy specific selections based on type
|
|
1487
|
-
if (
|
|
1642
|
+
if (
|
|
1643
|
+
webBundleType === 'agents' ||
|
|
1644
|
+
(webBundleType === 'custom' && config.includeIndividualAgents)
|
|
1645
|
+
) {
|
|
1488
1646
|
const agentsSource = path.join(distDir, 'agents');
|
|
1489
1647
|
const agentsTarget = path.join(webBundlesDirectory, 'agents');
|
|
1490
1648
|
if (await fileManager.pathExists(agentsSource)) {
|
|
@@ -1493,27 +1651,29 @@ class Installer {
|
|
|
1493
1651
|
copiedCount += 10; // Approximate count for agents
|
|
1494
1652
|
}
|
|
1495
1653
|
}
|
|
1496
|
-
|
|
1497
|
-
if (
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1654
|
+
|
|
1655
|
+
if (
|
|
1656
|
+
(webBundleType === 'teams' || webBundleType === 'custom') &&
|
|
1657
|
+
config.selectedWebBundleTeams &&
|
|
1658
|
+
config.selectedWebBundleTeams.length > 0
|
|
1659
|
+
) {
|
|
1660
|
+
const teamsSource = path.join(distDir, 'teams');
|
|
1661
|
+
const teamsTarget = path.join(webBundlesDirectory, 'teams');
|
|
1662
|
+
await fileManager.ensureDirectory(teamsTarget);
|
|
1663
|
+
|
|
1664
|
+
for (const teamId of config.selectedWebBundleTeams) {
|
|
1665
|
+
const teamFile = `${teamId}.txt`;
|
|
1666
|
+
const sourcePath = path.join(teamsSource, teamFile);
|
|
1667
|
+
const targetPath = path.join(teamsTarget, teamFile);
|
|
1668
|
+
|
|
1669
|
+
if (await fileManager.pathExists(sourcePath)) {
|
|
1670
|
+
await fileManager.copyFile(sourcePath, targetPath);
|
|
1671
|
+
copiedCount++;
|
|
1672
|
+
console.log(chalk.green(`✓ Copied team bundle: ${teamId}`));
|
|
1513
1673
|
}
|
|
1514
1674
|
}
|
|
1515
1675
|
}
|
|
1516
|
-
|
|
1676
|
+
|
|
1517
1677
|
// Always copy expansion packs if they exist
|
|
1518
1678
|
const expansionSource = path.join(distDir, 'expansion-packs');
|
|
1519
1679
|
const expansionTarget = path.join(webBundlesDirectory, 'expansion-packs');
|
|
@@ -1521,8 +1681,10 @@ class Installer {
|
|
|
1521
1681
|
await fileManager.copyDirectory(expansionSource, expansionTarget);
|
|
1522
1682
|
console.log(chalk.green(`✓ Copied expansion pack bundles`));
|
|
1523
1683
|
}
|
|
1524
|
-
|
|
1525
|
-
console.log(
|
|
1684
|
+
|
|
1685
|
+
console.log(
|
|
1686
|
+
chalk.green(`✓ Installed ${copiedCount} selected web bundles to: ${webBundlesDirectory}`),
|
|
1687
|
+
);
|
|
1526
1688
|
}
|
|
1527
1689
|
} catch (error) {
|
|
1528
1690
|
console.error(`Failed to install web bundles: ${error.message}`);
|
|
@@ -1530,89 +1692,88 @@ class Installer {
|
|
|
1530
1692
|
}
|
|
1531
1693
|
|
|
1532
1694
|
async copyCommonItems(installDir, targetSubdir, spinner) {
|
|
1533
|
-
|
|
1534
|
-
const fs = require('fs').promises;
|
|
1695
|
+
const fs = require('node:fs').promises;
|
|
1535
1696
|
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
|
1536
1697
|
const commonPath = path.join(sourceBase, 'common');
|
|
1537
1698
|
const targetPath = path.join(installDir, targetSubdir);
|
|
1538
1699
|
const copiedFiles = [];
|
|
1539
|
-
|
|
1700
|
+
|
|
1540
1701
|
// Check if common/ exists
|
|
1541
1702
|
if (!(await fileManager.pathExists(commonPath))) {
|
|
1542
1703
|
console.warn('Warning: common/ folder not found');
|
|
1543
1704
|
return copiedFiles;
|
|
1544
1705
|
}
|
|
1545
|
-
|
|
1706
|
+
|
|
1546
1707
|
// Copy all items from common/ to target
|
|
1547
1708
|
const commonItems = await resourceLocator.findFiles('**/*', {
|
|
1548
1709
|
cwd: commonPath,
|
|
1549
|
-
nodir: true
|
|
1710
|
+
nodir: true,
|
|
1550
1711
|
});
|
|
1551
|
-
|
|
1712
|
+
|
|
1552
1713
|
for (const item of commonItems) {
|
|
1553
1714
|
const sourcePath = path.join(commonPath, item);
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1715
|
+
const destinationPath = path.join(targetPath, item);
|
|
1716
|
+
|
|
1556
1717
|
// Read the file content
|
|
1557
1718
|
const content = await fs.readFile(sourcePath, 'utf8');
|
|
1558
|
-
|
|
1719
|
+
|
|
1559
1720
|
// Replace {root} with the target subdirectory
|
|
1560
|
-
const updatedContent = content.
|
|
1561
|
-
|
|
1721
|
+
const updatedContent = content.replaceAll('{root}', targetSubdir);
|
|
1722
|
+
|
|
1562
1723
|
// Ensure directory exists
|
|
1563
|
-
await fileManager.ensureDirectory(path.dirname(
|
|
1564
|
-
|
|
1724
|
+
await fileManager.ensureDirectory(path.dirname(destinationPath));
|
|
1725
|
+
|
|
1565
1726
|
// Write the updated content
|
|
1566
|
-
await fs.writeFile(
|
|
1727
|
+
await fs.writeFile(destinationPath, updatedContent, 'utf8');
|
|
1567
1728
|
copiedFiles.push(path.join(targetSubdir, item));
|
|
1568
1729
|
}
|
|
1569
|
-
|
|
1730
|
+
|
|
1570
1731
|
console.log(chalk.dim(` Added ${commonItems.length} common utilities`));
|
|
1571
1732
|
return copiedFiles;
|
|
1572
1733
|
}
|
|
1573
1734
|
|
|
1574
1735
|
async copyDocsItems(installDir, targetSubdir, spinner) {
|
|
1575
|
-
const fs = require('fs').promises;
|
|
1736
|
+
const fs = require('node:fs').promises;
|
|
1576
1737
|
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
|
1577
1738
|
const docsPath = path.join(sourceBase, 'docs');
|
|
1578
1739
|
const targetPath = path.join(installDir, targetSubdir);
|
|
1579
1740
|
const copiedFiles = [];
|
|
1580
|
-
|
|
1741
|
+
|
|
1581
1742
|
// Specific documentation files to copy
|
|
1582
|
-
const
|
|
1743
|
+
const documentFiles = [
|
|
1583
1744
|
'enhanced-ide-development-workflow.md',
|
|
1584
1745
|
'user-guide.md',
|
|
1585
|
-
'working-in-the-brownfield.md'
|
|
1746
|
+
'working-in-the-brownfield.md',
|
|
1586
1747
|
];
|
|
1587
|
-
|
|
1748
|
+
|
|
1588
1749
|
// Check if docs/ exists
|
|
1589
1750
|
if (!(await fileManager.pathExists(docsPath))) {
|
|
1590
1751
|
console.warn('Warning: docs/ folder not found');
|
|
1591
1752
|
return copiedFiles;
|
|
1592
1753
|
}
|
|
1593
|
-
|
|
1754
|
+
|
|
1594
1755
|
// Copy specific documentation files from docs/ to target
|
|
1595
|
-
for (const
|
|
1596
|
-
const sourcePath = path.join(docsPath,
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1756
|
+
for (const documentFile of documentFiles) {
|
|
1757
|
+
const sourcePath = path.join(docsPath, documentFile);
|
|
1758
|
+
const destinationPath = path.join(targetPath, documentFile);
|
|
1759
|
+
|
|
1599
1760
|
// Check if the source file exists
|
|
1600
1761
|
if (await fileManager.pathExists(sourcePath)) {
|
|
1601
1762
|
// Read the file content
|
|
1602
1763
|
const content = await fs.readFile(sourcePath, 'utf8');
|
|
1603
|
-
|
|
1764
|
+
|
|
1604
1765
|
// Replace {root} with the target subdirectory
|
|
1605
|
-
const updatedContent = content.
|
|
1606
|
-
|
|
1766
|
+
const updatedContent = content.replaceAll('{root}', targetSubdir);
|
|
1767
|
+
|
|
1607
1768
|
// Ensure directory exists
|
|
1608
|
-
await fileManager.ensureDirectory(path.dirname(
|
|
1609
|
-
|
|
1769
|
+
await fileManager.ensureDirectory(path.dirname(destinationPath));
|
|
1770
|
+
|
|
1610
1771
|
// Write the updated content
|
|
1611
|
-
await fs.writeFile(
|
|
1612
|
-
copiedFiles.push(path.join(targetSubdir,
|
|
1772
|
+
await fs.writeFile(destinationPath, updatedContent, 'utf8');
|
|
1773
|
+
copiedFiles.push(path.join(targetSubdir, documentFile));
|
|
1613
1774
|
}
|
|
1614
1775
|
}
|
|
1615
|
-
|
|
1776
|
+
|
|
1616
1777
|
if (copiedFiles.length > 0) {
|
|
1617
1778
|
console.log(chalk.dim(` Added ${copiedFiles.length} documentation files`));
|
|
1618
1779
|
}
|
|
@@ -1621,56 +1782,56 @@ class Installer {
|
|
|
1621
1782
|
|
|
1622
1783
|
async detectExpansionPacks(installDir) {
|
|
1623
1784
|
const expansionPacks = {};
|
|
1624
|
-
const glob = require(
|
|
1625
|
-
|
|
1785
|
+
const glob = require('glob');
|
|
1786
|
+
|
|
1626
1787
|
// Find all dot folders that might be expansion packs
|
|
1627
|
-
const dotFolders = glob.sync(
|
|
1788
|
+
const dotFolders = glob.sync('.*', {
|
|
1628
1789
|
cwd: installDir,
|
|
1629
|
-
ignore: [
|
|
1790
|
+
ignore: ['.git', '.git/**', '.bmad-core', '.bmad-core/**'],
|
|
1630
1791
|
});
|
|
1631
|
-
|
|
1792
|
+
|
|
1632
1793
|
for (const folder of dotFolders) {
|
|
1633
1794
|
const folderPath = path.join(installDir, folder);
|
|
1634
1795
|
const stats = await fileManager.pathExists(folderPath);
|
|
1635
|
-
|
|
1796
|
+
|
|
1636
1797
|
if (stats) {
|
|
1637
1798
|
// Check if it has a manifest
|
|
1638
|
-
const manifestPath = path.join(folderPath,
|
|
1799
|
+
const manifestPath = path.join(folderPath, 'install-manifest.yaml');
|
|
1639
1800
|
if (await fileManager.pathExists(manifestPath)) {
|
|
1640
|
-
const manifest = await fileManager.readExpansionPackManifest(installDir, folder.
|
|
1801
|
+
const manifest = await fileManager.readExpansionPackManifest(installDir, folder.slice(1));
|
|
1641
1802
|
if (manifest) {
|
|
1642
|
-
expansionPacks[folder.
|
|
1803
|
+
expansionPacks[folder.slice(1)] = {
|
|
1643
1804
|
path: folderPath,
|
|
1644
1805
|
manifest: manifest,
|
|
1645
|
-
hasManifest: true
|
|
1806
|
+
hasManifest: true,
|
|
1646
1807
|
};
|
|
1647
1808
|
}
|
|
1648
1809
|
} else {
|
|
1649
1810
|
// Check if it has a config.yaml (expansion pack without manifest)
|
|
1650
|
-
const configPath = path.join(folderPath,
|
|
1811
|
+
const configPath = path.join(folderPath, 'config.yaml');
|
|
1651
1812
|
if (await fileManager.pathExists(configPath)) {
|
|
1652
|
-
expansionPacks[folder.
|
|
1813
|
+
expansionPacks[folder.slice(1)] = {
|
|
1653
1814
|
path: folderPath,
|
|
1654
1815
|
manifest: null,
|
|
1655
|
-
hasManifest: false
|
|
1816
|
+
hasManifest: false,
|
|
1656
1817
|
};
|
|
1657
1818
|
}
|
|
1658
1819
|
}
|
|
1659
1820
|
}
|
|
1660
1821
|
}
|
|
1661
|
-
|
|
1822
|
+
|
|
1662
1823
|
return expansionPacks;
|
|
1663
1824
|
}
|
|
1664
1825
|
|
|
1665
1826
|
async repairExpansionPack(installDir, packId, pack, integrity, spinner) {
|
|
1666
1827
|
spinner.start(`Repairing ${pack.name}...`);
|
|
1667
|
-
|
|
1828
|
+
|
|
1668
1829
|
try {
|
|
1669
1830
|
const expansionDotFolder = path.join(installDir, `.${packId}`);
|
|
1670
|
-
|
|
1831
|
+
|
|
1671
1832
|
// Back up modified files
|
|
1672
1833
|
if (integrity.modified.length > 0) {
|
|
1673
|
-
spinner.text =
|
|
1834
|
+
spinner.text = 'Backing up modified files...';
|
|
1674
1835
|
for (const file of integrity.modified) {
|
|
1675
1836
|
const filePath = path.join(installDir, file);
|
|
1676
1837
|
if (await fileManager.pathExists(filePath)) {
|
|
@@ -1679,51 +1840,52 @@ class Installer {
|
|
|
1679
1840
|
}
|
|
1680
1841
|
}
|
|
1681
1842
|
}
|
|
1682
|
-
|
|
1843
|
+
|
|
1683
1844
|
// Restore missing and modified files
|
|
1684
|
-
spinner.text =
|
|
1845
|
+
spinner.text = 'Restoring files...';
|
|
1685
1846
|
const filesToRestore = [...integrity.missing, ...integrity.modified];
|
|
1686
|
-
|
|
1847
|
+
|
|
1687
1848
|
for (const file of filesToRestore) {
|
|
1688
1849
|
// Skip the manifest file itself
|
|
1689
1850
|
if (file.endsWith('install-manifest.yaml')) continue;
|
|
1690
|
-
|
|
1851
|
+
|
|
1691
1852
|
const relativePath = file.replace(`.${packId}/`, '');
|
|
1692
1853
|
const sourcePath = path.join(pack.path, relativePath);
|
|
1693
|
-
const
|
|
1694
|
-
|
|
1854
|
+
const destinationPath = path.join(installDir, file);
|
|
1855
|
+
|
|
1695
1856
|
// Check if this is a common/ file that needs special processing
|
|
1696
1857
|
const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
|
|
1697
1858
|
const commonSourcePath = path.join(commonBase, 'common', relativePath);
|
|
1698
|
-
|
|
1859
|
+
|
|
1699
1860
|
if (await fileManager.pathExists(commonSourcePath)) {
|
|
1700
1861
|
// This is a common/ file - needs template processing
|
|
1701
|
-
const fs = require('fs').promises;
|
|
1862
|
+
const fs = require('node:fs').promises;
|
|
1702
1863
|
const content = await fs.readFile(commonSourcePath, 'utf8');
|
|
1703
|
-
const updatedContent = content.
|
|
1704
|
-
await fileManager.ensureDirectory(path.dirname(
|
|
1705
|
-
await fs.writeFile(
|
|
1864
|
+
const updatedContent = content.replaceAll('{root}', `.${packId}`);
|
|
1865
|
+
await fileManager.ensureDirectory(path.dirname(destinationPath));
|
|
1866
|
+
await fs.writeFile(destinationPath, updatedContent, 'utf8');
|
|
1706
1867
|
spinner.text = `Restored: ${file}`;
|
|
1707
1868
|
} else if (await fileManager.pathExists(sourcePath)) {
|
|
1708
1869
|
// Regular file from expansion pack
|
|
1709
|
-
await fileManager.copyFile(sourcePath,
|
|
1870
|
+
await fileManager.copyFile(sourcePath, destinationPath);
|
|
1710
1871
|
spinner.text = `Restored: ${file}`;
|
|
1711
1872
|
} else {
|
|
1712
1873
|
console.warn(chalk.yellow(` Warning: Source file not found: ${file}`));
|
|
1713
1874
|
}
|
|
1714
1875
|
}
|
|
1715
|
-
|
|
1876
|
+
|
|
1716
1877
|
spinner.succeed(`${pack.name} repaired successfully!`);
|
|
1717
|
-
|
|
1878
|
+
|
|
1718
1879
|
// Show summary
|
|
1719
1880
|
console.log(chalk.green(`\n✓ ${pack.name} repaired!`));
|
|
1720
1881
|
if (integrity.missing.length > 0) {
|
|
1721
1882
|
console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
|
|
1722
1883
|
}
|
|
1723
1884
|
if (integrity.modified.length > 0) {
|
|
1724
|
-
console.log(
|
|
1885
|
+
console.log(
|
|
1886
|
+
chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`),
|
|
1887
|
+
);
|
|
1725
1888
|
}
|
|
1726
|
-
|
|
1727
1889
|
} catch (error) {
|
|
1728
1890
|
if (spinner) spinner.fail(`Failed to repair ${pack.name}`);
|
|
1729
1891
|
console.error(`Error: ${error.message}`);
|
|
@@ -1734,37 +1896,37 @@ class Installer {
|
|
|
1734
1896
|
// Simple semver comparison
|
|
1735
1897
|
const parts1 = v1.split('.').map(Number);
|
|
1736
1898
|
const parts2 = v2.split('.').map(Number);
|
|
1737
|
-
|
|
1738
|
-
for (let
|
|
1739
|
-
const part1 = parts1[
|
|
1740
|
-
const part2 = parts2[
|
|
1741
|
-
|
|
1899
|
+
|
|
1900
|
+
for (let index = 0; index < 3; index++) {
|
|
1901
|
+
const part1 = parts1[index] || 0;
|
|
1902
|
+
const part2 = parts2[index] || 0;
|
|
1903
|
+
|
|
1742
1904
|
if (part1 > part2) return 1;
|
|
1743
1905
|
if (part1 < part2) return -1;
|
|
1744
1906
|
}
|
|
1745
|
-
|
|
1907
|
+
|
|
1746
1908
|
return 0;
|
|
1747
1909
|
}
|
|
1748
1910
|
|
|
1749
1911
|
async cleanupLegacyYmlFiles(installDir, spinner) {
|
|
1750
1912
|
const glob = require('glob');
|
|
1751
|
-
const fs = require('fs').promises;
|
|
1752
|
-
|
|
1913
|
+
const fs = require('node:fs').promises;
|
|
1914
|
+
|
|
1753
1915
|
try {
|
|
1754
1916
|
// Find all .yml files in the installation directory
|
|
1755
1917
|
const ymlFiles = glob.sync('**/*.yml', {
|
|
1756
1918
|
cwd: installDir,
|
|
1757
|
-
ignore: ['**/node_modules/**', '**/.git/**']
|
|
1919
|
+
ignore: ['**/node_modules/**', '**/.git/**'],
|
|
1758
1920
|
});
|
|
1759
|
-
|
|
1921
|
+
|
|
1760
1922
|
let deletedCount = 0;
|
|
1761
|
-
|
|
1923
|
+
|
|
1762
1924
|
for (const ymlFile of ymlFiles) {
|
|
1763
1925
|
// Check if corresponding .yaml file exists
|
|
1764
1926
|
const yamlFile = ymlFile.replace(/\.yml$/, '.yaml');
|
|
1765
1927
|
const ymlPath = path.join(installDir, ymlFile);
|
|
1766
1928
|
const yamlPath = path.join(installDir, yamlFile);
|
|
1767
|
-
|
|
1929
|
+
|
|
1768
1930
|
if (await fileManager.pathExists(yamlPath)) {
|
|
1769
1931
|
// .yaml counterpart exists, delete the .yml file
|
|
1770
1932
|
await fs.unlink(ymlPath);
|
|
@@ -1772,11 +1934,10 @@ class Installer {
|
|
|
1772
1934
|
console.log(chalk.dim(` Removed legacy: ${ymlFile} (replaced by ${yamlFile})`));
|
|
1773
1935
|
}
|
|
1774
1936
|
}
|
|
1775
|
-
|
|
1937
|
+
|
|
1776
1938
|
if (deletedCount > 0) {
|
|
1777
1939
|
console.log(chalk.green(`✓ Cleaned up ${deletedCount} legacy .yml files`));
|
|
1778
1940
|
}
|
|
1779
|
-
|
|
1780
1941
|
} catch (error) {
|
|
1781
1942
|
console.warn(`Warning: Could not cleanup legacy .yml files: ${error.message}`);
|
|
1782
1943
|
}
|
|
@@ -1787,8 +1948,8 @@ class Installer {
|
|
|
1787
1948
|
let currentDir = process.cwd();
|
|
1788
1949
|
|
|
1789
1950
|
while (currentDir !== path.dirname(currentDir)) {
|
|
1790
|
-
const bmadDir = path.join(currentDir,
|
|
1791
|
-
const manifestPath = path.join(bmadDir,
|
|
1951
|
+
const bmadDir = path.join(currentDir, '.bmad-core');
|
|
1952
|
+
const manifestPath = path.join(bmadDir, 'install-manifest.yaml');
|
|
1792
1953
|
|
|
1793
1954
|
if (await fileManager.pathExists(manifestPath)) {
|
|
1794
1955
|
return currentDir; // Return parent directory, not .bmad-core itself
|
|
@@ -1798,8 +1959,8 @@ class Installer {
|
|
|
1798
1959
|
}
|
|
1799
1960
|
|
|
1800
1961
|
// Also check if we're inside a .bmad-core directory
|
|
1801
|
-
if (path.basename(process.cwd()) ===
|
|
1802
|
-
const manifestPath = path.join(process.cwd(),
|
|
1962
|
+
if (path.basename(process.cwd()) === '.bmad-core') {
|
|
1963
|
+
const manifestPath = path.join(process.cwd(), 'install-manifest.yaml');
|
|
1803
1964
|
if (await fileManager.pathExists(manifestPath)) {
|
|
1804
1965
|
return path.dirname(process.cwd()); // Return parent directory
|
|
1805
1966
|
}
|
|
@@ -1809,22 +1970,22 @@ class Installer {
|
|
|
1809
1970
|
}
|
|
1810
1971
|
|
|
1811
1972
|
async flatten(options) {
|
|
1812
|
-
const { spawn } = require('child_process');
|
|
1973
|
+
const { spawn } = require('node:child_process');
|
|
1813
1974
|
const flattenerPath = path.join(__dirname, '..', '..', 'flattener', 'main.js');
|
|
1814
|
-
|
|
1815
|
-
const
|
|
1975
|
+
|
|
1976
|
+
const arguments_ = [];
|
|
1816
1977
|
if (options.input) {
|
|
1817
|
-
|
|
1978
|
+
arguments_.push('--input', options.input);
|
|
1818
1979
|
}
|
|
1819
1980
|
if (options.output) {
|
|
1820
|
-
|
|
1981
|
+
arguments_.push('--output', options.output);
|
|
1821
1982
|
}
|
|
1822
|
-
|
|
1823
|
-
const child = spawn('node', [flattenerPath, ...
|
|
1983
|
+
|
|
1984
|
+
const child = spawn('node', [flattenerPath, ...arguments_], {
|
|
1824
1985
|
stdio: 'inherit',
|
|
1825
|
-
cwd: process.cwd()
|
|
1986
|
+
cwd: process.cwd(),
|
|
1826
1987
|
});
|
|
1827
|
-
|
|
1988
|
+
|
|
1828
1989
|
child.on('exit', (code) => {
|
|
1829
1990
|
process.exit(code);
|
|
1830
1991
|
});
|