bmad-method 4.30.2 → 4.30.3

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +1 -1
  3. package/bmad-core/core-config.yaml +0 -1
  4. package/dist/agents/analyst.txt +1 -1
  5. package/dist/agents/bmad-master.txt +1 -1
  6. package/dist/agents/bmad-orchestrator.txt +1 -1
  7. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +2409 -0
  8. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +1480 -0
  9. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +826 -0
  10. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +10690 -0
  11. package/dist/teams/team-all.txt +1 -1
  12. package/dist/teams/team-fullstack.txt +1 -1
  13. package/dist/teams/team-ide-minimal.txt +1 -1
  14. package/dist/teams/team-no-ui.txt +1 -1
  15. package/docs/bmad-workflow-guide.md +1 -1
  16. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +2 -2
  17. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +13 -0
  18. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +72 -0
  19. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
  20. package/expansion-packs/{bmad-creator-tools/agents/bmad-the-creator.md → bmad-2d-unity-game-dev/agents/game-sm.md} +26 -28
  21. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
  22. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +160 -0
  23. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
  24. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +251 -0
  25. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +590 -0
  26. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +111 -0
  27. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +217 -0
  28. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +308 -0
  29. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +545 -0
  30. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
  31. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
  32. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
  33. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
  34. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
  35. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
  36. package/expansion-packs/bmad-infrastructure-devops/config.yaml +2 -2
  37. package/package.json +4 -8
  38. package/tools/bump-all-versions.js +8 -9
  39. package/tools/bump-expansion-version.js +40 -35
  40. package/tools/installer/bin/bmad.js +8 -21
  41. package/tools/installer/lib/file-manager.js +76 -44
  42. package/tools/installer/lib/ide-base-setup.js +227 -0
  43. package/tools/installer/lib/ide-setup.js +8 -58
  44. package/tools/installer/lib/installer.js +99 -121
  45. package/tools/installer/lib/memory-profiler.js +224 -0
  46. package/tools/installer/lib/module-manager.js +110 -0
  47. package/tools/installer/lib/resource-locator.js +310 -0
  48. package/tools/installer/package.json +1 -1
  49. package/tools/semantic-release-sync-installer.js +20 -21
  50. package/dist/expansion-packs/bmad-creator-tools/agents/bmad-the-creator.txt +0 -2008
  51. package/expansion-packs/bmad-creator-tools/README.md +0 -8
  52. package/expansion-packs/bmad-creator-tools/config.yaml +0 -6
  53. package/expansion-packs/bmad-creator-tools/tasks/create-agent.md +0 -200
  54. package/expansion-packs/bmad-creator-tools/tasks/generate-expansion-pack.md +0 -1020
  55. package/expansion-packs/bmad-creator-tools/templates/agent-teams-tmpl.yaml +0 -178
  56. package/expansion-packs/bmad-creator-tools/templates/agent-tmpl.yaml +0 -154
  57. package/expansion-packs/bmad-creator-tools/templates/expansion-pack-plan-tmpl.yaml +0 -120
  58. package/tools/bump-core-version.js +0 -57
@@ -0,0 +1,175 @@
1
+ workflow:
2
+ id: unity-game-prototype
3
+ name: Game Prototype Development (Unity)
4
+ description: Fast-track workflow for rapid game prototyping and concept validation. Optimized for game jams, proof-of-concept development, and quick iteration on game mechanics using Unity and C#.
5
+ type: prototype
6
+ project_types:
7
+ - game-jam
8
+ - proof-of-concept
9
+ - mechanic-test
10
+ - technical-demo
11
+ - learning-project
12
+ - rapid-iteration
13
+ prototype_sequence:
14
+ - step: concept_definition
15
+ agent: game-designer
16
+ duration: 15-30 minutes
17
+ creates: concept-summary.md
18
+ notes: Quickly define core game concept, primary mechanic, and target experience. Focus on what makes this game unique and fun.
19
+ - step: rapid_design
20
+ agent: game-designer
21
+ duration: 30-60 minutes
22
+ creates: prototype-spec.md
23
+ requires: concept-summary.md
24
+ optional_steps:
25
+ - quick_brainstorming
26
+ - reference_research
27
+ notes: Create minimal but complete design specification. Focus on core mechanics, basic controls, and success/failure conditions.
28
+ - step: technical_planning
29
+ agent: game-developer
30
+ duration: 15-30 minutes
31
+ creates: prototype-architecture.md
32
+ requires: prototype-spec.md
33
+ notes: Define minimal technical implementation plan. Identify core Unity systems needed and performance constraints.
34
+ - step: implementation_stories
35
+ agent: game-sm
36
+ duration: 30-45 minutes
37
+ creates: prototype-stories/
38
+ requires: prototype-spec.md, prototype-architecture.md
39
+ notes: Create 3-5 focused implementation stories for core prototype features. Each story should be completable in 2-4 hours.
40
+ - step: iterative_development
41
+ agent: game-developer
42
+ duration: varies
43
+ implements: prototype-stories/
44
+ notes: Implement stories in priority order. Test frequently in the Unity Editor and adjust design based on what feels fun. Document discoveries.
45
+ workflow_end:
46
+ action: prototype_evaluation
47
+ notes: 'Prototype complete. Evaluate core mechanics, gather feedback, and decide next steps: iterate, expand, or archive.'
48
+ game_jam_sequence:
49
+ - step: jam_concept
50
+ agent: game-designer
51
+ duration: 10-15 minutes
52
+ creates: jam-concept.md
53
+ notes: Define game concept based on jam theme. One sentence core mechanic, basic controls, win condition.
54
+ - step: jam_implementation
55
+ agent: game-developer
56
+ duration: varies (jam timeline)
57
+ creates: working-prototype
58
+ requires: jam-concept.md
59
+ notes: Directly implement core mechanic in Unity. No formal stories - iterate rapidly on what's fun. Document major decisions.
60
+ jam_workflow_end:
61
+ action: jam_submission
62
+ notes: Submit to game jam. Capture lessons learned and consider post-jam development if concept shows promise.
63
+ flow_diagram: |
64
+ ```mermaid
65
+ graph TD
66
+ A[Start: Prototype Project] --> B{Development Context?}
67
+ B -->|Standard Prototype| C[game-designer: concept-summary.md]
68
+ B -->|Game Jam| D[game-designer: jam-concept.md]
69
+
70
+ C --> E[game-designer: prototype-spec.md]
71
+ E --> F[game-developer: prototype-architecture.md]
72
+ F --> G[game-sm: create prototype stories]
73
+ G --> H[game-developer: iterative implementation]
74
+ H --> I[Prototype Evaluation]
75
+
76
+ D --> J[game-developer: direct implementation]
77
+ J --> K[Game Jam Submission]
78
+
79
+ E -.-> E1[Optional: quick brainstorming]
80
+ E -.-> E2[Optional: reference research]
81
+
82
+ style I fill:#90EE90
83
+ style K fill:#90EE90
84
+ style C fill:#FFE4B5
85
+ style E fill:#FFE4B5
86
+ style F fill:#FFE4B5
87
+ style G fill:#FFE4B5
88
+ style H fill:#FFE4B5
89
+ style D fill:#FFB6C1
90
+ style J fill:#FFB6C1
91
+ ```
92
+ decision_guidance:
93
+ use_prototype_sequence_when:
94
+ - Learning new game development concepts
95
+ - Testing specific game mechanics
96
+ - Building portfolio pieces
97
+ - Have 1-7 days for development
98
+ - Need structured but fast development
99
+ - Want to validate game concepts before full development
100
+ use_game_jam_sequence_when:
101
+ - Participating in time-constrained game jams
102
+ - Have 24-72 hours total development time
103
+ - Want to experiment with wild or unusual concepts
104
+ - Learning through rapid iteration
105
+ - Building networking/portfolio presence
106
+ prototype_best_practices:
107
+ scope_management:
108
+ - Start with absolute minimum viable gameplay
109
+ - One core mechanic implemented well beats many mechanics poorly
110
+ - Focus on "game feel" over features
111
+ - Cut features ruthlessly to meet timeline
112
+ rapid_iteration:
113
+ - Test the game every 1-2 hours of development in the Unity Editor
114
+ - Ask "Is this fun?" frequently during development
115
+ - Be willing to pivot mechanics if they don't feel good
116
+ - Document what works and what doesn't
117
+ technical_efficiency:
118
+ - Use simple graphics (geometric shapes, basic sprites)
119
+ - Leverage Unity's built-in components heavily
120
+ - Avoid complex custom systems in prototypes
121
+ - Prioritize functional over polished
122
+ prototype_evaluation_criteria:
123
+ core_mechanic_validation:
124
+ - Is the primary mechanic engaging for 30+ seconds?
125
+ - Do players understand the mechanic without explanation?
126
+ - Does the mechanic have depth for extended play?
127
+ - Are there natural difficulty progression opportunities?
128
+ technical_feasibility:
129
+ - Does the prototype run at acceptable frame rates?
130
+ - Are there obvious technical blockers for expansion?
131
+ - Is the codebase clean enough for further development?
132
+ - Are performance targets realistic for full game?
133
+ player_experience:
134
+ - Do testers engage with the game voluntarily?
135
+ - What emotions does the game create in players?
136
+ - Are players asking for "just one more try"?
137
+ - What do players want to see added or changed?
138
+ post_prototype_options:
139
+ iterate_and_improve:
140
+ action: continue_prototyping
141
+ when: Core mechanic shows promise but needs refinement
142
+ next_steps: Create new prototype iteration focusing on identified improvements
143
+ expand_to_full_game:
144
+ action: transition_to_full_development
145
+ when: Prototype validates strong game concept
146
+ next_steps: Use game-dev-greenfield workflow to create full game design and architecture
147
+ pivot_concept:
148
+ action: new_prototype_direction
149
+ when: Current mechanic doesn't work but insights suggest new direction
150
+ next_steps: Apply learnings to new prototype concept
151
+ archive_and_learn:
152
+ action: document_learnings
153
+ when: Prototype doesn't work but provides valuable insights
154
+ next_steps: Document lessons learned and move to next prototype concept
155
+ time_boxing_guidance:
156
+ concept_phase: Maximum 30 minutes - if you can't explain the game simply, simplify it
157
+ design_phase: Maximum 1 hour - focus on core mechanics only
158
+ planning_phase: Maximum 30 minutes - identify critical path to playable prototype
159
+ implementation_phase: Time-boxed iterations - test every 2-4 hours of work
160
+ success_metrics:
161
+ development_velocity:
162
+ - Playable prototype in first day of development
163
+ - Core mechanic demonstrable within 4-6 hours of coding
164
+ - Major iteration cycles completed in 2-4 hour blocks
165
+ learning_objectives:
166
+ - Clear understanding of what makes the mechanic fun (or not)
167
+ - Technical feasibility assessment for full development
168
+ - Player reaction and engagement validation
169
+ - Design insights for future development
170
+ handoff_prompts:
171
+ concept_to_design: Game concept defined. Create minimal design specification focusing on core mechanics and player experience.
172
+ design_to_technical: Design specification ready. Create technical implementation plan for rapid prototyping.
173
+ technical_to_stories: Technical plan complete. Create focused implementation stories for prototype development.
174
+ stories_to_implementation: Stories ready. Begin iterative implementation with frequent playtesting and design validation.
175
+ prototype_to_evaluation: Prototype playable. Evaluate core mechanics, gather feedback, and determine next development steps.
@@ -1,6 +1,6 @@
1
1
  name: bmad-infrastructure-devops
2
- version: 1.9.0
3
- short-title: Infrastructure and DevOps capabilities
2
+ version: 1.10.0
3
+ short-title: Infrastructure DevOps Pack
4
4
  description: >-
5
5
  This expansion pack extends BMad Method with comprehensive infrastructure and
6
6
  DevOps capabilities. It's designed for teams that need to define, implement,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmad-method",
3
- "version": "4.30.2",
3
+ "version": "4.30.3",
4
4
  "description": "Breakthrough Method of Agile AI-driven Development",
5
5
  "main": "tools/cli.js",
6
6
  "bin": {
@@ -18,10 +18,6 @@
18
18
  "version:patch": "node tools/version-bump.js patch",
19
19
  "version:minor": "node tools/version-bump.js minor",
20
20
  "version:major": "node tools/version-bump.js major",
21
- "version:core": "node tools/bump-core-version.js",
22
- "version:core:major": "node tools/bump-core-version.js major",
23
- "version:core:minor": "node tools/bump-core-version.js minor",
24
- "version:core:patch": "node tools/bump-core-version.js patch",
25
21
  "version:expansion": "node tools/bump-expansion-version.js",
26
22
  "version:expansion:set": "node tools/update-expansion-version.js",
27
23
  "version:all": "node tools/bump-all-versions.js",
@@ -38,13 +34,13 @@
38
34
  },
39
35
  "dependencies": {
40
36
  "@kayvan/markdown-tree-parser": "^1.5.0",
41
- "chalk": "^5.4.1",
37
+ "chalk": "^4.1.2",
42
38
  "commander": "^14.0.0",
43
39
  "fs-extra": "^11.3.0",
44
40
  "glob": "^11.0.3",
45
- "inquirer": "^12.6.3",
41
+ "inquirer": "^8.2.6",
46
42
  "js-yaml": "^4.1.0",
47
- "ora": "^8.2.0"
43
+ "ora": "^5.4.1"
48
44
  },
49
45
  "keywords": [
50
46
  "agile",
@@ -31,21 +31,20 @@ function bumpVersion(currentVersion, type) {
31
31
  async function bumpAllVersions() {
32
32
  const updatedItems = [];
33
33
 
34
- // First, bump the core version
35
- const coreConfigPath = path.join(__dirname, '..', 'bmad-core', 'core-config.yaml');
34
+ // First, bump the core version (package.json)
35
+ const packagePath = path.join(__dirname, '..', 'package.json');
36
36
  try {
37
- const coreConfigContent = fs.readFileSync(coreConfigPath, 'utf8');
38
- const coreConfig = yaml.load(coreConfigContent);
39
- const oldCoreVersion = coreConfig.version || '1.0.0';
37
+ const packageContent = fs.readFileSync(packagePath, 'utf8');
38
+ const packageJson = JSON.parse(packageContent);
39
+ const oldCoreVersion = packageJson.version || '1.0.0';
40
40
  const newCoreVersion = bumpVersion(oldCoreVersion, bumpType);
41
41
 
42
- coreConfig.version = newCoreVersion;
42
+ packageJson.version = newCoreVersion;
43
43
 
44
- const updatedCoreYaml = yaml.dump(coreConfig, { indent: 2 });
45
- fs.writeFileSync(coreConfigPath, updatedCoreYaml);
44
+ fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
46
45
 
47
46
  updatedItems.push({ type: 'core', name: 'BMad Core', oldVersion: oldCoreVersion, newVersion: newCoreVersion });
48
- console.log(`✓ BMad Core: ${oldCoreVersion} → ${newCoreVersion}`);
47
+ console.log(`✓ BMad Core (package.json): ${oldCoreVersion} → ${newCoreVersion}`);
49
48
  } catch (error) {
50
49
  console.error(`✗ Failed to update BMad Core: ${error.message}`);
51
50
  }
@@ -1,78 +1,83 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // Load required modules
3
4
  const fs = require('fs');
4
5
  const path = require('path');
5
6
  const yaml = require('js-yaml');
6
7
 
8
+ // Parse CLI arguments
7
9
  const args = process.argv.slice(2);
10
+ const packId = args[0];
11
+ const bumpType = args[1] || 'minor';
8
12
 
9
- if (args.length < 1 || args.length > 2) {
13
+ // Validate arguments
14
+ if (!packId || args.length > 2) {
10
15
  console.log('Usage: node bump-expansion-version.js <expansion-pack-id> [major|minor|patch]');
11
16
  console.log('Default: minor');
12
17
  console.log('Example: node bump-expansion-version.js bmad-creator-tools patch');
13
18
  process.exit(1);
14
19
  }
15
20
 
16
- const packId = args[0];
17
- const bumpType = args[1] || 'minor'; // default to minor
18
-
19
21
  if (!['major', 'minor', 'patch'].includes(bumpType)) {
20
22
  console.error('Error: Bump type must be major, minor, or patch');
21
23
  process.exit(1);
22
24
  }
23
25
 
26
+ // Version bump logic
24
27
  function bumpVersion(currentVersion, type) {
25
28
  const [major, minor, patch] = currentVersion.split('.').map(Number);
26
-
29
+
27
30
  switch (type) {
28
- case 'major':
29
- return `${major + 1}.0.0`;
30
- case 'minor':
31
- return `${major}.${minor + 1}.0`;
32
- case 'patch':
33
- return `${major}.${minor}.${patch + 1}`;
34
- default:
35
- return currentVersion;
31
+ case 'major': return `${major + 1}.0.0`;
32
+ case 'minor': return `${major}.${minor + 1}.0`;
33
+ case 'patch': return `${major}.${minor}.${patch + 1}`;
34
+ default: return currentVersion;
36
35
  }
37
36
  }
38
37
 
38
+ // Main function to bump version
39
39
  async function updateVersion() {
40
+ const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yaml');
41
+
42
+ // Check if config exists
43
+ if (!fs.existsSync(configPath)) {
44
+ console.error(`Error: Expansion pack '${packId}' not found`);
45
+ console.log('\nAvailable expansion packs:');
46
+
47
+ const packsDir = path.join(__dirname, '..', 'expansion-packs');
48
+ const entries = fs.readdirSync(packsDir, { withFileTypes: true });
49
+
50
+ entries.forEach(entry => {
51
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
52
+ console.log(` - ${entry.name}`);
53
+ }
54
+ });
55
+
56
+ process.exit(1);
57
+ }
58
+
40
59
  try {
41
- const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yaml');
42
-
43
- if (!fs.existsSync(configPath)) {
44
- console.error(`Error: Expansion pack '${packId}' not found`);
45
- console.log('\nAvailable expansion packs:');
46
- const expansionPacksDir = path.join(__dirname, '..', 'expansion-packs');
47
- const entries = fs.readdirSync(expansionPacksDir, { withFileTypes: true });
48
- entries.forEach(entry => {
49
- if (entry.isDirectory() && !entry.name.startsWith('.')) {
50
- console.log(` - ${entry.name}`);
51
- }
52
- });
53
- process.exit(1);
54
- }
55
-
56
60
  const configContent = fs.readFileSync(configPath, 'utf8');
57
61
  const config = yaml.load(configContent);
62
+
58
63
  const oldVersion = config.version || '1.0.0';
59
64
  const newVersion = bumpVersion(oldVersion, bumpType);
60
-
65
+
61
66
  config.version = newVersion;
62
-
67
+
63
68
  const updatedYaml = yaml.dump(config, { indent: 2 });
64
69
  fs.writeFileSync(configPath, updatedYaml);
65
-
70
+
66
71
  console.log(`✓ ${packId}: ${oldVersion} → ${newVersion}`);
67
72
  console.log(`\n✓ Successfully bumped ${packId} with ${bumpType} version bump`);
68
73
  console.log('\nNext steps:');
69
- console.log('1. Test the changes');
70
- console.log('2. Commit: git add -A && git commit -m "chore: bump ' + packId + ' version (' + bumpType + ')"');
71
-
74
+ console.log(`1. Test the changes`);
75
+ console.log(`2. Commit: git add -A && git commit -m "chore: bump ${packId} version (${bumpType})"`);
76
+
72
77
  } catch (error) {
73
78
  console.error('Error updating version:', error.message);
74
79
  process.exit(1);
75
80
  }
76
81
  }
77
82
 
78
- updateVersion();
83
+ updateVersion();
@@ -4,17 +4,8 @@ const { program } = require('commander');
4
4
  const path = require('path');
5
5
  const fs = require('fs').promises;
6
6
  const yaml = require('js-yaml');
7
-
8
- // Dynamic imports for ES modules
9
- let chalk, inquirer;
10
-
11
- // Initialize ES modules
12
- async function initializeModules() {
13
- if (!chalk) {
14
- chalk = (await import('chalk')).default;
15
- inquirer = (await import('inquirer')).default;
16
- }
17
- }
7
+ const chalk = require('chalk');
8
+ const inquirer = require('inquirer');
18
9
 
19
10
  // Handle both execution contexts (from root via npx or from installer directory)
20
11
  let version;
@@ -54,12 +45,12 @@ program
54
45
  .option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
55
46
  .action(async (options) => {
56
47
  try {
57
- await initializeModules();
58
48
  if (!options.full && !options.expansionOnly) {
59
49
  // Interactive mode
60
50
  const answers = await promptInstallation();
61
51
  if (!answers._alreadyInstalled) {
62
52
  await installer.install(answers);
53
+ process.exit(0);
63
54
  }
64
55
  } else {
65
56
  // Direct mode
@@ -73,9 +64,9 @@ program
73
64
  expansionPacks: options.expansionPacks || []
74
65
  };
75
66
  await installer.install(config);
67
+ process.exit(0);
76
68
  }
77
69
  } catch (error) {
78
- if (!chalk) await initializeModules();
79
70
  console.error(chalk.red('Installation failed:'), error.message);
80
71
  process.exit(1);
81
72
  }
@@ -90,7 +81,6 @@ program
90
81
  try {
91
82
  await installer.update();
92
83
  } catch (error) {
93
- if (!chalk) await initializeModules();
94
84
  console.error(chalk.red('Update failed:'), error.message);
95
85
  process.exit(1);
96
86
  }
@@ -103,7 +93,6 @@ program
103
93
  try {
104
94
  await installer.listExpansionPacks();
105
95
  } catch (error) {
106
- if (!chalk) await initializeModules();
107
96
  console.error(chalk.red('Error:'), error.message);
108
97
  process.exit(1);
109
98
  }
@@ -116,14 +105,12 @@ program
116
105
  try {
117
106
  await installer.showStatus();
118
107
  } catch (error) {
119
- if (!chalk) await initializeModules();
120
108
  console.error(chalk.red('Error:'), error.message);
121
109
  process.exit(1);
122
110
  }
123
111
  });
124
112
 
125
113
  async function promptInstallation() {
126
- await initializeModules();
127
114
 
128
115
  // Display ASCII logo
129
116
  console.log(chalk.bold.cyan(`
@@ -178,13 +165,13 @@ async function promptInstallation() {
178
165
  let bmadOptionText;
179
166
  if (state.type === 'v4_existing') {
180
167
  const currentVersion = state.manifest?.version || 'unknown';
181
- const newVersion = coreConfig.version || 'unknown'; // Use version from core-config.yaml
168
+ const newVersion = version; // Always use package.json version
182
169
  const versionInfo = currentVersion === newVersion
183
170
  ? `(v${currentVersion} - reinstall)`
184
171
  : `(v${currentVersion} → v${newVersion})`;
185
172
  bmadOptionText = `Update ${coreShortTitle} ${versionInfo} .bmad-core`;
186
173
  } else {
187
- bmadOptionText = `Install ${coreShortTitle} (v${coreConfig.version || version}) .bmad-core`;
174
+ bmadOptionText = `${coreShortTitle} (v${version}) .bmad-core`;
188
175
  }
189
176
 
190
177
  choices.push({
@@ -204,9 +191,9 @@ async function promptInstallation() {
204
191
  const versionInfo = currentVersion === newVersion
205
192
  ? `(v${currentVersion} - reinstall)`
206
193
  : `(v${currentVersion} → v${newVersion})`;
207
- packOptionText = `Update ${pack.description} ${versionInfo} .${pack.id}`;
194
+ packOptionText = `Update ${pack.shortTitle} ${versionInfo} .${pack.id}`;
208
195
  } else {
209
- packOptionText = `Install ${pack.description} (v${pack.version}) .${pack.id}`;
196
+ packOptionText = `${pack.shortTitle} (v${pack.version}) .${pack.id}`;
210
197
  }
211
198
 
212
199
  choices.push({
@@ -1,18 +1,11 @@
1
1
  const fs = require("fs-extra");
2
2
  const path = require("path");
3
3
  const crypto = require("crypto");
4
- const glob = require("glob");
5
4
  const yaml = require("js-yaml");
6
-
7
- // Dynamic import for ES module
8
- let chalk;
9
-
10
- // Initialize ES modules
11
- async function initializeModules() {
12
- if (!chalk) {
13
- chalk = (await import("chalk")).default;
14
- }
15
- }
5
+ const chalk = require("chalk");
6
+ const { createReadStream, createWriteStream, promises: fsPromises } = require('fs');
7
+ const { pipeline } = require('stream/promises');
8
+ const resourceLocator = require('./resource-locator');
16
9
 
17
10
  class FileManager {
18
11
  constructor() {
@@ -23,10 +16,19 @@ class FileManager {
23
16
  async copyFile(source, destination) {
24
17
  try {
25
18
  await fs.ensureDir(path.dirname(destination));
26
- await fs.copy(source, destination);
19
+
20
+ // Use streaming for large files (> 10MB)
21
+ const stats = await fs.stat(source);
22
+ if (stats.size > 10 * 1024 * 1024) {
23
+ await pipeline(
24
+ createReadStream(source),
25
+ createWriteStream(destination)
26
+ );
27
+ } else {
28
+ await fs.copy(source, destination);
29
+ }
27
30
  return true;
28
31
  } catch (error) {
29
- await initializeModules();
30
32
  console.error(chalk.red(`Failed to copy ${source}:`), error.message);
31
33
  return false;
32
34
  }
@@ -35,10 +37,28 @@ class FileManager {
35
37
  async copyDirectory(source, destination) {
36
38
  try {
37
39
  await fs.ensureDir(destination);
38
- await fs.copy(source, destination);
40
+
41
+ // Use streaming copy for large directories
42
+ const files = await resourceLocator.findFiles('**/*', {
43
+ cwd: source,
44
+ nodir: true
45
+ });
46
+
47
+ // Process files in batches to avoid memory issues
48
+ const batchSize = 50;
49
+ for (let i = 0; i < files.length; i += batchSize) {
50
+ const batch = files.slice(i, i + batchSize);
51
+ await Promise.all(
52
+ batch.map(file =>
53
+ this.copyFile(
54
+ path.join(source, file),
55
+ path.join(destination, file)
56
+ )
57
+ )
58
+ );
59
+ }
39
60
  return true;
40
61
  } catch (error) {
41
- await initializeModules();
42
62
  console.error(
43
63
  chalk.red(`Failed to copy directory ${source}:`),
44
64
  error.message
@@ -48,7 +68,7 @@ class FileManager {
48
68
  }
49
69
 
50
70
  async copyGlobPattern(pattern, sourceDir, destDir, rootValue = null) {
51
- const files = glob.sync(pattern, { cwd: sourceDir });
71
+ const files = await resourceLocator.findFiles(pattern, { cwd: sourceDir });
52
72
  const copied = [];
53
73
 
54
74
  for (const file of files) {
@@ -75,12 +95,15 @@ class FileManager {
75
95
 
76
96
  async calculateFileHash(filePath) {
77
97
  try {
78
- const content = await fs.readFile(filePath);
79
- return crypto
80
- .createHash("sha256")
81
- .update(content)
82
- .digest("hex")
83
- .slice(0, 16);
98
+ // Use streaming for hash calculation to reduce memory usage
99
+ const stream = createReadStream(filePath);
100
+ const hash = crypto.createHash("sha256");
101
+
102
+ for await (const chunk of stream) {
103
+ hash.update(chunk);
104
+ }
105
+
106
+ return hash.digest("hex").slice(0, 16);
84
107
  } catch (error) {
85
108
  return null;
86
109
  }
@@ -93,15 +116,14 @@ class FileManager {
93
116
  this.manifestFile
94
117
  );
95
118
 
96
- // Read version from core-config.yaml
97
- const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yaml");
119
+ // Read version from package.json
98
120
  let coreVersion = "unknown";
99
121
  try {
100
- const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
101
- const coreConfig = yaml.load(coreConfigContent);
102
- coreVersion = coreConfig.version || "unknown";
122
+ const packagePath = path.join(__dirname, '..', '..', '..', 'package.json');
123
+ const packageJson = require(packagePath);
124
+ coreVersion = packageJson.version;
103
125
  } catch (error) {
104
- console.warn("Could not read version from core-config.yaml, using 'unknown'");
126
+ console.warn("Could not read version from package.json, using 'unknown'");
105
127
  }
106
128
 
107
129
  const manifest = {
@@ -304,7 +326,6 @@ class FileManager {
304
326
 
305
327
  return true;
306
328
  } catch (error) {
307
- await initializeModules();
308
329
  console.error(chalk.red(`Failed to modify core-config.yaml:`), error.message);
309
330
  return false;
310
331
  }
@@ -312,22 +333,35 @@ class FileManager {
312
333
 
313
334
  async copyFileWithRootReplacement(source, destination, rootValue) {
314
335
  try {
315
- // Read the source file content
316
- const fs = require('fs').promises;
317
- const content = await fs.readFile(source, 'utf8');
318
-
319
- // Replace {root} with the specified root value
320
- const updatedContent = content.replace(/\{root\}/g, rootValue);
336
+ // Check file size to determine if we should stream
337
+ const stats = await fs.stat(source);
321
338
 
322
- // Ensure directory exists
323
- await this.ensureDirectory(path.dirname(destination));
324
-
325
- // Write the updated content
326
- await fs.writeFile(destination, updatedContent, 'utf8');
339
+ if (stats.size > 5 * 1024 * 1024) { // 5MB threshold
340
+ // Use streaming for large files
341
+ const { Transform } = require('stream');
342
+ const replaceStream = new Transform({
343
+ transform(chunk, encoding, callback) {
344
+ const modified = chunk.toString().replace(/\{root\}/g, rootValue);
345
+ callback(null, modified);
346
+ }
347
+ });
348
+
349
+ await this.ensureDirectory(path.dirname(destination));
350
+ await pipeline(
351
+ createReadStream(source, { encoding: 'utf8' }),
352
+ replaceStream,
353
+ createWriteStream(destination, { encoding: 'utf8' })
354
+ );
355
+ } else {
356
+ // Regular approach for smaller files
357
+ const content = await fsPromises.readFile(source, 'utf8');
358
+ const updatedContent = content.replace(/\{root\}/g, rootValue);
359
+ await this.ensureDirectory(path.dirname(destination));
360
+ await fsPromises.writeFile(destination, updatedContent, 'utf8');
361
+ }
327
362
 
328
363
  return true;
329
364
  } catch (error) {
330
- await initializeModules();
331
365
  console.error(chalk.red(`Failed to copy ${source} with root replacement:`), error.message);
332
366
  return false;
333
367
  }
@@ -335,11 +369,10 @@ class FileManager {
335
369
 
336
370
  async copyDirectoryWithRootReplacement(source, destination, rootValue, fileExtensions = ['.md', '.yaml', '.yml']) {
337
371
  try {
338
- await initializeModules(); // Ensure chalk is initialized
339
372
  await this.ensureDirectory(destination);
340
373
 
341
374
  // Get all files in source directory
342
- const files = glob.sync('**/*', {
375
+ const files = await resourceLocator.findFiles('**/*', {
343
376
  cwd: source,
344
377
  nodir: true
345
378
  });
@@ -369,7 +402,6 @@ class FileManager {
369
402
 
370
403
  return true;
371
404
  } catch (error) {
372
- await initializeModules();
373
405
  console.error(chalk.red(`Failed to copy directory ${source} with root replacement:`), error.message);
374
406
  return false;
375
407
  }