bmad-method 5.0.0-beta.2 → 5.0.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.
Files changed (131) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +3 -3
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +3 -3
  3. package/.github/workflows/discord.yaml +11 -2
  4. package/.github/workflows/format-check.yaml +42 -0
  5. package/.github/workflows/manual-release.yaml +173 -0
  6. package/.husky/pre-commit +3 -0
  7. package/.vscode/settings.json +26 -1
  8. package/CHANGELOG.md +0 -11
  9. package/README.md +2 -0
  10. package/bmad-core/agent-teams/team-all.yaml +1 -1
  11. package/bmad-core/agents/bmad-orchestrator.md +1 -1
  12. package/bmad-core/agents/dev.md +4 -4
  13. package/bmad-core/data/bmad-kb.md +1 -1
  14. package/bmad-core/data/test-levels-framework.md +12 -12
  15. package/bmad-core/tasks/facilitate-brainstorming-session.md +1 -1
  16. package/bmad-core/tasks/nfr-assess.md +10 -10
  17. package/bmad-core/tasks/qa-gate.md +23 -23
  18. package/bmad-core/tasks/review-story.md +18 -18
  19. package/bmad-core/tasks/risk-profile.md +25 -25
  20. package/bmad-core/tasks/test-design.md +9 -9
  21. package/bmad-core/tasks/trace-requirements.md +21 -21
  22. package/bmad-core/templates/architecture-tmpl.yaml +49 -49
  23. package/bmad-core/templates/brainstorming-output-tmpl.yaml +5 -5
  24. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +31 -31
  25. package/bmad-core/templates/brownfield-prd-tmpl.yaml +13 -13
  26. package/bmad-core/templates/competitor-analysis-tmpl.yaml +19 -6
  27. package/bmad-core/templates/front-end-architecture-tmpl.yaml +21 -9
  28. package/bmad-core/templates/front-end-spec-tmpl.yaml +24 -24
  29. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +122 -104
  30. package/bmad-core/templates/market-research-tmpl.yaml +2 -2
  31. package/bmad-core/templates/prd-tmpl.yaml +9 -9
  32. package/bmad-core/templates/project-brief-tmpl.yaml +4 -4
  33. package/bmad-core/templates/qa-gate-tmpl.yaml +9 -9
  34. package/bmad-core/templates/story-tmpl.yaml +12 -12
  35. package/bmad-core/workflows/brownfield-fullstack.yaml +9 -9
  36. package/bmad-core/workflows/brownfield-service.yaml +1 -1
  37. package/bmad-core/workflows/brownfield-ui.yaml +1 -1
  38. package/bmad-core/workflows/greenfield-fullstack.yaml +1 -1
  39. package/bmad-core/workflows/greenfield-service.yaml +1 -1
  40. package/bmad-core/workflows/greenfield-ui.yaml +1 -1
  41. package/common/utils/bmad-doc-template.md +5 -5
  42. package/dist/agents/analyst.txt +28 -15
  43. package/dist/agents/architect.txt +220 -190
  44. package/dist/agents/bmad-master.txt +298 -255
  45. package/dist/agents/bmad-orchestrator.txt +1 -1
  46. package/dist/agents/pm.txt +20 -20
  47. package/dist/agents/po.txt +11 -11
  48. package/dist/agents/qa.txt +275 -618
  49. package/dist/agents/sm.txt +11 -11
  50. package/dist/agents/ux-expert.txt +23 -23
  51. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +109 -109
  52. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +75 -77
  53. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +41 -41
  54. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +483 -474
  55. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +1 -1
  56. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +149 -149
  57. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +20 -20
  58. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +371 -358
  59. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +25 -25
  60. package/dist/teams/team-all.txt +581 -881
  61. package/dist/teams/team-fullstack.txt +316 -273
  62. package/dist/teams/team-ide-minimal.txt +276 -619
  63. package/dist/teams/team-no-ui.txt +281 -238
  64. package/docs/versioning-and-releases.md +114 -44
  65. package/eslint.config.mjs +119 -0
  66. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.4 Deployment Configuration/1.4.2 - cloudbuild.yaml +26 -26
  67. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +4 -4
  68. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +1 -1
  69. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +26 -28
  70. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +50 -50
  71. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +23 -23
  72. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +24 -24
  73. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +42 -42
  74. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
  75. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +5 -5
  76. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +1 -1
  77. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +3 -3
  78. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +1 -1
  79. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +23 -23
  80. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +63 -63
  81. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +20 -20
  82. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
  83. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +5 -5
  84. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +1 -1
  85. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +20 -20
  86. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +7 -7
  87. package/package.json +62 -39
  88. package/prettier.config.mjs +32 -0
  89. package/release_notes.md +30 -0
  90. package/tools/bmad-npx-wrapper.js +10 -10
  91. package/tools/builders/web-builder.js +124 -130
  92. package/tools/bump-all-versions.js +42 -33
  93. package/tools/bump-expansion-version.js +23 -16
  94. package/tools/cli.js +10 -12
  95. package/tools/flattener/aggregate.js +10 -10
  96. package/tools/flattener/binary.js +44 -17
  97. package/tools/flattener/discovery.js +19 -18
  98. package/tools/flattener/files.js +6 -6
  99. package/tools/flattener/ignoreRules.js +125 -125
  100. package/tools/flattener/main.js +201 -304
  101. package/tools/flattener/projectRoot.js +75 -73
  102. package/tools/flattener/prompts.js +9 -9
  103. package/tools/flattener/stats.helpers.js +131 -67
  104. package/tools/flattener/stats.js +3 -3
  105. package/tools/flattener/test-matrix.js +201 -193
  106. package/tools/flattener/xml.js +33 -31
  107. package/tools/installer/bin/bmad.js +130 -89
  108. package/tools/installer/config/ide-agent-config.yaml +1 -1
  109. package/tools/installer/config/install.config.yaml +2 -2
  110. package/tools/installer/lib/config-loader.js +46 -42
  111. package/tools/installer/lib/file-manager.js +91 -113
  112. package/tools/installer/lib/ide-base-setup.js +57 -56
  113. package/tools/installer/lib/ide-setup.js +375 -343
  114. package/tools/installer/lib/installer.js +875 -714
  115. package/tools/installer/lib/memory-profiler.js +54 -53
  116. package/tools/installer/lib/module-manager.js +19 -15
  117. package/tools/installer/lib/resource-locator.js +26 -28
  118. package/tools/installer/package.json +19 -19
  119. package/tools/lib/dependency-resolver.js +26 -30
  120. package/tools/lib/yaml-utils.js +7 -7
  121. package/tools/preview-release-notes.js +66 -0
  122. package/tools/shared/bannerArt.js +3 -3
  123. package/tools/sync-installer-version.js +7 -9
  124. package/tools/update-expansion-version.js +14 -15
  125. package/tools/upgraders/v3-to-v4-upgrader.js +203 -294
  126. package/tools/version-bump.js +41 -26
  127. package/tools/yaml-format.js +56 -43
  128. package/.github/workflows/promote-to-stable.yml +0 -144
  129. package/.github/workflows/release.yaml +0 -60
  130. package/.releaserc.json +0 -21
  131. package/tools/semantic-release-sync-installer.js +0 -30
@@ -1,5 +1,5 @@
1
1
  const fs = require('fs-extra');
2
- const path = require('path');
2
+ const path = require('node:path');
3
3
  const yaml = require('js-yaml');
4
4
  const { extractYamlFromAgent } = require('../../lib/yaml-utils');
5
5
 
@@ -11,7 +11,7 @@ class ConfigLoader {
11
11
 
12
12
  async load() {
13
13
  if (this.config) return this.config;
14
-
14
+
15
15
  try {
16
16
  const configContent = await fs.readFile(this.configPath, 'utf8');
17
17
  this.config = yaml.load(configContent);
@@ -28,30 +28,30 @@ class ConfigLoader {
28
28
 
29
29
  async getAvailableAgents() {
30
30
  const agentsDir = path.join(this.getBmadCorePath(), 'agents');
31
-
31
+
32
32
  try {
33
33
  const entries = await fs.readdir(agentsDir, { withFileTypes: true });
34
34
  const agents = [];
35
-
35
+
36
36
  for (const entry of entries) {
37
37
  if (entry.isFile() && entry.name.endsWith('.md')) {
38
38
  const agentPath = path.join(agentsDir, entry.name);
39
39
  const agentId = path.basename(entry.name, '.md');
40
-
40
+
41
41
  try {
42
42
  const agentContent = await fs.readFile(agentPath, 'utf8');
43
-
43
+
44
44
  // Extract YAML block from agent file
45
45
  const yamlContentText = extractYamlFromAgent(agentContent);
46
46
  if (yamlContentText) {
47
47
  const yamlContent = yaml.load(yamlContentText);
48
48
  const agentConfig = yamlContent.agent || {};
49
-
49
+
50
50
  agents.push({
51
51
  id: agentId,
52
52
  name: agentConfig.title || agentConfig.name || agentId,
53
53
  file: `bmad-core/agents/${entry.name}`,
54
- description: agentConfig.whenToUse || 'No description available'
54
+ description: agentConfig.whenToUse || 'No description available',
55
55
  });
56
56
  }
57
57
  } catch (error) {
@@ -59,10 +59,10 @@ class ConfigLoader {
59
59
  }
60
60
  }
61
61
  }
62
-
62
+
63
63
  // Sort agents by name for consistent display
64
64
  agents.sort((a, b) => a.name.localeCompare(b.name));
65
-
65
+
66
66
  return agents;
67
67
  } catch (error) {
68
68
  console.warn(`Failed to read agents directory: ${error.message}`);
@@ -72,41 +72,45 @@ class ConfigLoader {
72
72
 
73
73
  async getAvailableExpansionPacks() {
74
74
  const expansionPacksDir = path.join(this.getBmadCorePath(), '..', 'expansion-packs');
75
-
75
+
76
76
  try {
77
77
  const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true });
78
78
  const expansionPacks = [];
79
-
79
+
80
80
  for (const entry of entries) {
81
81
  if (entry.isDirectory() && !entry.name.startsWith('.')) {
82
82
  const packPath = path.join(expansionPacksDir, entry.name);
83
83
  const configPath = path.join(packPath, 'config.yaml');
84
-
84
+
85
85
  try {
86
86
  // Read config.yaml
87
87
  const configContent = await fs.readFile(configPath, 'utf8');
88
88
  const config = yaml.load(configContent);
89
-
89
+
90
90
  expansionPacks.push({
91
91
  id: entry.name,
92
92
  name: config.name || entry.name,
93
- description: config['short-title'] || config.description || 'No description available',
94
- fullDescription: config.description || config['short-title'] || 'No description available',
93
+ description:
94
+ config['short-title'] || config.description || 'No description available',
95
+ fullDescription:
96
+ config.description || config['short-title'] || 'No description available',
95
97
  version: config.version || '1.0.0',
96
98
  author: config.author || 'BMad Team',
97
99
  packPath: packPath,
98
- dependencies: config.dependencies?.agents || []
100
+ dependencies: config.dependencies?.agents || [],
99
101
  });
100
102
  } catch (error) {
101
103
  // Fallback if config.yaml doesn't exist or can't be read
102
- console.warn(`Failed to read config for expansion pack ${entry.name}: ${error.message}`);
103
-
104
+ console.warn(
105
+ `Failed to read config for expansion pack ${entry.name}: ${error.message}`,
106
+ );
107
+
104
108
  // Try to derive info from directory name as fallback
105
109
  const name = entry.name
106
110
  .split('-')
107
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
111
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
108
112
  .join(' ');
109
-
113
+
110
114
  expansionPacks.push({
111
115
  id: entry.name,
112
116
  name: name,
@@ -115,12 +119,12 @@ class ConfigLoader {
115
119
  version: '1.0.0',
116
120
  author: 'BMad Team',
117
121
  packPath: packPath,
118
- dependencies: []
122
+ dependencies: [],
119
123
  });
120
124
  }
121
125
  }
122
126
  }
123
-
127
+
124
128
  return expansionPacks;
125
129
  } catch (error) {
126
130
  console.warn(`Failed to read expansion packs directory: ${error.message}`);
@@ -132,16 +136,16 @@ class ConfigLoader {
132
136
  // Use DependencyResolver to dynamically parse agent dependencies
133
137
  const DependencyResolver = require('../../lib/dependency-resolver');
134
138
  const resolver = new DependencyResolver(path.join(__dirname, '..', '..', '..'));
135
-
139
+
136
140
  const agentDeps = await resolver.resolveAgentDependencies(agentId);
137
-
141
+
138
142
  // Convert to flat list of file paths
139
143
  const depPaths = [];
140
-
144
+
141
145
  // Core files and utilities are included automatically by DependencyResolver
142
-
146
+
143
147
  // Add agent file itself is already handled by installer
144
-
148
+
145
149
  // Add all resolved resources
146
150
  for (const resource of agentDeps.resources) {
147
151
  const filePath = `.bmad-core/${resource.type}/${resource.id}.md`;
@@ -149,7 +153,7 @@ class ConfigLoader {
149
153
  depPaths.push(filePath);
150
154
  }
151
155
  }
152
-
156
+
153
157
  return depPaths;
154
158
  }
155
159
 
@@ -175,25 +179,25 @@ class ConfigLoader {
175
179
 
176
180
  async getAvailableTeams() {
177
181
  const teamsDir = path.join(this.getBmadCorePath(), 'agent-teams');
178
-
182
+
179
183
  try {
180
184
  const entries = await fs.readdir(teamsDir, { withFileTypes: true });
181
185
  const teams = [];
182
-
186
+
183
187
  for (const entry of entries) {
184
188
  if (entry.isFile() && entry.name.endsWith('.yaml')) {
185
189
  const teamPath = path.join(teamsDir, entry.name);
186
-
190
+
187
191
  try {
188
192
  const teamContent = await fs.readFile(teamPath, 'utf8');
189
193
  const teamConfig = yaml.load(teamContent);
190
-
194
+
191
195
  if (teamConfig.bundle) {
192
196
  teams.push({
193
197
  id: path.basename(entry.name, '.yaml'),
194
198
  name: teamConfig.bundle.name || entry.name,
195
199
  description: teamConfig.bundle.description || 'Team configuration',
196
- icon: teamConfig.bundle.icon || '📋'
200
+ icon: teamConfig.bundle.icon || '📋',
197
201
  });
198
202
  }
199
203
  } catch (error) {
@@ -201,7 +205,7 @@ class ConfigLoader {
201
205
  }
202
206
  }
203
207
  }
204
-
208
+
205
209
  return teams;
206
210
  } catch (error) {
207
211
  console.warn(`Warning: Could not scan teams directory: ${error.message}`);
@@ -217,16 +221,16 @@ class ConfigLoader {
217
221
  // Use DependencyResolver to dynamically parse team dependencies
218
222
  const DependencyResolver = require('../../lib/dependency-resolver');
219
223
  const resolver = new DependencyResolver(path.join(__dirname, '..', '..', '..'));
220
-
224
+
221
225
  try {
222
226
  const teamDeps = await resolver.resolveTeamDependencies(teamId);
223
-
227
+
224
228
  // Convert to flat list of file paths
225
229
  const depPaths = [];
226
-
230
+
227
231
  // Add team config file
228
232
  depPaths.push(`.bmad-core/agent-teams/${teamId}.yaml`);
229
-
233
+
230
234
  // Add all agents
231
235
  for (const agent of teamDeps.agents) {
232
236
  const filePath = `.bmad-core/agents/${agent.id}.md`;
@@ -234,7 +238,7 @@ class ConfigLoader {
234
238
  depPaths.push(filePath);
235
239
  }
236
240
  }
237
-
241
+
238
242
  // Add all resolved resources
239
243
  for (const resource of teamDeps.resources) {
240
244
  const filePath = `.bmad-core/${resource.type}/${resource.id}.${resource.type === 'workflows' ? 'yaml' : 'md'}`;
@@ -242,7 +246,7 @@ class ConfigLoader {
242
246
  depPaths.push(filePath);
243
247
  }
244
248
  }
245
-
249
+
246
250
  return depPaths;
247
251
  } catch (error) {
248
252
  throw new Error(`Failed to resolve team dependencies for ${teamId}: ${error.message}`);
@@ -250,4 +254,4 @@ class ConfigLoader {
250
254
  }
251
255
  }
252
256
 
253
- module.exports = new ConfigLoader();
257
+ module.exports = new ConfigLoader();
@@ -1,32 +1,24 @@
1
- const fs = require("fs-extra");
2
- const path = require("path");
3
- const crypto = require("crypto");
4
- const yaml = require("js-yaml");
5
- const chalk = require("chalk").default || require("chalk");
6
- const { createReadStream, createWriteStream, promises: fsPromises } = require('fs');
7
- const { pipeline } = require('stream/promises');
1
+ const fs = require('fs-extra');
2
+ const path = require('node:path');
3
+ const crypto = require('node:crypto');
4
+ const yaml = require('js-yaml');
5
+ const chalk = require('chalk');
6
+ const { createReadStream, createWriteStream, promises: fsPromises } = require('node:fs');
7
+ const { pipeline } = require('node:stream/promises');
8
8
  const resourceLocator = require('./resource-locator');
9
9
 
10
10
  class FileManager {
11
- constructor() {
12
- this.manifestDir = ".bmad-core";
13
- this.manifestFile = "install-manifest.yaml";
14
- }
11
+ constructor() {}
15
12
 
16
13
  async copyFile(source, destination) {
17
14
  try {
18
15
  await fs.ensureDir(path.dirname(destination));
19
-
16
+
20
17
  // Use streaming for large files (> 10MB)
21
18
  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
- }
19
+ await (stats.size > 10 * 1024 * 1024
20
+ ? pipeline(createReadStream(source), createWriteStream(destination))
21
+ : fs.copy(source, destination));
30
22
  return true;
31
23
  } catch (error) {
32
24
  console.error(chalk.red(`Failed to copy ${source}:`), error.message);
@@ -37,32 +29,24 @@ class FileManager {
37
29
  async copyDirectory(source, destination) {
38
30
  try {
39
31
  await fs.ensureDir(destination);
40
-
32
+
41
33
  // Use streaming copy for large directories
42
34
  const files = await resourceLocator.findFiles('**/*', {
43
35
  cwd: source,
44
- nodir: true
36
+ nodir: true,
45
37
  });
46
-
38
+
47
39
  // Process files in batches to avoid memory issues
48
40
  const batchSize = 50;
49
- for (let i = 0; i < files.length; i += batchSize) {
50
- const batch = files.slice(i, i + batchSize);
41
+ for (let index = 0; index < files.length; index += batchSize) {
42
+ const batch = files.slice(index, index + batchSize);
51
43
  await Promise.all(
52
- batch.map(file =>
53
- this.copyFile(
54
- path.join(source, file),
55
- path.join(destination, file)
56
- )
57
- )
44
+ batch.map((file) => this.copyFile(path.join(source, file), path.join(destination, file))),
58
45
  );
59
46
  }
60
47
  return true;
61
48
  } catch (error) {
62
- console.error(
63
- chalk.red(`Failed to copy directory ${source}:`),
64
- error.message
65
- );
49
+ console.error(chalk.red(`Failed to copy directory ${source}:`), error.message);
66
50
  return false;
67
51
  }
68
52
  }
@@ -73,17 +57,16 @@ class FileManager {
73
57
 
74
58
  for (const file of files) {
75
59
  const sourcePath = path.join(sourceDir, file);
76
- const destPath = path.join(destDir, file);
60
+ const destinationPath = path.join(destDir, file);
77
61
 
78
62
  // Use root replacement if rootValue is provided and file needs it
79
- const needsRootReplacement = rootValue && (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'));
80
-
63
+ const needsRootReplacement =
64
+ rootValue && (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'));
65
+
81
66
  let success = false;
82
- if (needsRootReplacement) {
83
- success = await this.copyFileWithRootReplacement(sourcePath, destPath, rootValue);
84
- } else {
85
- success = await this.copyFile(sourcePath, destPath);
86
- }
67
+ success = await (needsRootReplacement
68
+ ? this.copyFileWithRootReplacement(sourcePath, destinationPath, rootValue)
69
+ : this.copyFile(sourcePath, destinationPath));
87
70
 
88
71
  if (success) {
89
72
  copied.push(file);
@@ -97,32 +80,28 @@ class FileManager {
97
80
  try {
98
81
  // Use streaming for hash calculation to reduce memory usage
99
82
  const stream = createReadStream(filePath);
100
- const hash = crypto.createHash("sha256");
101
-
83
+ const hash = crypto.createHash('sha256');
84
+
102
85
  for await (const chunk of stream) {
103
86
  hash.update(chunk);
104
87
  }
105
-
106
- return hash.digest("hex").slice(0, 16);
107
- } catch (error) {
88
+
89
+ return hash.digest('hex').slice(0, 16);
90
+ } catch {
108
91
  return null;
109
92
  }
110
93
  }
111
94
 
112
95
  async createManifest(installDir, config, files) {
113
- const manifestPath = path.join(
114
- installDir,
115
- this.manifestDir,
116
- this.manifestFile
117
- );
96
+ const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile);
118
97
 
119
98
  // Read version from package.json
120
- let coreVersion = "unknown";
99
+ let coreVersion = 'unknown';
121
100
  try {
122
101
  const packagePath = path.join(__dirname, '..', '..', '..', 'package.json');
123
102
  const packageJson = require(packagePath);
124
103
  coreVersion = packageJson.version;
125
- } catch (error) {
104
+ } catch {
126
105
  console.warn("Could not read version from package.json, using 'unknown'");
127
106
  }
128
107
 
@@ -156,31 +135,23 @@ class FileManager {
156
135
  }
157
136
 
158
137
  async readManifest(installDir) {
159
- const manifestPath = path.join(
160
- installDir,
161
- this.manifestDir,
162
- this.manifestFile
163
- );
138
+ const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile);
164
139
 
165
140
  try {
166
- const content = await fs.readFile(manifestPath, "utf8");
141
+ const content = await fs.readFile(manifestPath, 'utf8');
167
142
  return yaml.load(content);
168
- } catch (error) {
143
+ } catch {
169
144
  return null;
170
145
  }
171
146
  }
172
147
 
173
148
  async readExpansionPackManifest(installDir, packId) {
174
- const manifestPath = path.join(
175
- installDir,
176
- `.${packId}`,
177
- this.manifestFile
178
- );
149
+ const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
179
150
 
180
151
  try {
181
- const content = await fs.readFile(manifestPath, "utf8");
152
+ const content = await fs.readFile(manifestPath, 'utf8');
182
153
  return yaml.load(content);
183
- } catch (error) {
154
+ } catch {
184
155
  return null;
185
156
  }
186
157
  }
@@ -203,24 +174,24 @@ class FileManager {
203
174
  async checkFileIntegrity(installDir, manifest) {
204
175
  const result = {
205
176
  missing: [],
206
- modified: []
177
+ modified: [],
207
178
  };
208
179
 
209
180
  for (const file of manifest.files) {
210
181
  const filePath = path.join(installDir, file.path);
211
-
182
+
212
183
  // Skip checking the manifest file itself - it will always be different due to timestamps
213
184
  if (file.path.endsWith('install-manifest.yaml')) {
214
185
  continue;
215
186
  }
216
-
217
- if (!(await this.pathExists(filePath))) {
218
- result.missing.push(file.path);
219
- } else {
187
+
188
+ if (await this.pathExists(filePath)) {
220
189
  const currentHash = await this.calculateFileHash(filePath);
221
190
  if (currentHash && currentHash !== file.hash) {
222
191
  result.modified.push(file.path);
223
192
  }
193
+ } else {
194
+ result.missing.push(file.path);
224
195
  }
225
196
  }
226
197
 
@@ -228,7 +199,7 @@ class FileManager {
228
199
  }
229
200
 
230
201
  async backupFile(filePath) {
231
- const backupPath = filePath + ".bak";
202
+ const backupPath = filePath + '.bak';
232
203
  let counter = 1;
233
204
  let finalBackupPath = backupPath;
234
205
 
@@ -256,7 +227,7 @@ class FileManager {
256
227
  }
257
228
 
258
229
  async readFile(filePath) {
259
- return fs.readFile(filePath, "utf8");
230
+ return fs.readFile(filePath, 'utf8');
260
231
  }
261
232
 
262
233
  async writeFile(filePath, content) {
@@ -269,14 +240,10 @@ class FileManager {
269
240
  }
270
241
 
271
242
  async createExpansionPackManifest(installDir, packId, config, files) {
272
- const manifestPath = path.join(
273
- installDir,
274
- `.${packId}`,
275
- this.manifestFile
276
- );
243
+ const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
277
244
 
278
245
  const manifest = {
279
- version: config.expansionPackVersion || require("../../../package.json").version,
246
+ version: config.expansionPackVersion || require('../../../package.json').version,
280
247
  installed_at: new Date().toISOString(),
281
248
  install_type: config.installType,
282
249
  expansion_pack_id: config.expansionPackId,
@@ -306,24 +273,24 @@ class FileManager {
306
273
 
307
274
  async modifyCoreConfig(installDir, config) {
308
275
  const coreConfigPath = path.join(installDir, '.bmad-core', 'core-config.yaml');
309
-
276
+
310
277
  try {
311
278
  // Read the existing core-config.yaml
312
279
  const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
313
280
  const coreConfig = yaml.load(coreConfigContent);
314
-
281
+
315
282
  // Modify sharding settings if provided
316
283
  if (config.prdSharded !== undefined) {
317
284
  coreConfig.prd.prdSharded = config.prdSharded;
318
285
  }
319
-
286
+
320
287
  if (config.architectureSharded !== undefined) {
321
288
  coreConfig.architecture.architectureSharded = config.architectureSharded;
322
289
  }
323
-
290
+
324
291
  // Write back the modified config
325
292
  await fs.writeFile(coreConfigPath, yaml.dump(coreConfig, { indent: 2 }));
326
-
293
+
327
294
  return true;
328
295
  } catch (error) {
329
296
  console.error(chalk.red(`Failed to modify core-config.yaml:`), error.message);
@@ -335,31 +302,32 @@ class FileManager {
335
302
  try {
336
303
  // Check file size to determine if we should stream
337
304
  const stats = await fs.stat(source);
338
-
339
- if (stats.size > 5 * 1024 * 1024) { // 5MB threshold
305
+
306
+ if (stats.size > 5 * 1024 * 1024) {
307
+ // 5MB threshold
340
308
  // Use streaming for large files
341
- const { Transform } = require('stream');
309
+ const { Transform } = require('node:stream');
342
310
  const replaceStream = new Transform({
343
311
  transform(chunk, encoding, callback) {
344
- const modified = chunk.toString().replace(/\{root\}/g, rootValue);
312
+ const modified = chunk.toString().replaceAll('{root}', rootValue);
345
313
  callback(null, modified);
346
- }
314
+ },
347
315
  });
348
-
316
+
349
317
  await this.ensureDirectory(path.dirname(destination));
350
318
  await pipeline(
351
319
  createReadStream(source, { encoding: 'utf8' }),
352
320
  replaceStream,
353
- createWriteStream(destination, { encoding: 'utf8' })
321
+ createWriteStream(destination, { encoding: 'utf8' }),
354
322
  );
355
323
  } else {
356
324
  // Regular approach for smaller files
357
325
  const content = await fsPromises.readFile(source, 'utf8');
358
- const updatedContent = content.replace(/\{root\}/g, rootValue);
326
+ const updatedContent = content.replaceAll('{root}', rootValue);
359
327
  await this.ensureDirectory(path.dirname(destination));
360
328
  await fsPromises.writeFile(destination, updatedContent, 'utf8');
361
329
  }
362
-
330
+
363
331
  return true;
364
332
  } catch (error) {
365
333
  console.error(chalk.red(`Failed to copy ${source} with root replacement:`), error.message);
@@ -367,45 +335,55 @@ class FileManager {
367
335
  }
368
336
  }
369
337
 
370
- async copyDirectoryWithRootReplacement(source, destination, rootValue, fileExtensions = ['.md', '.yaml', '.yml']) {
338
+ async copyDirectoryWithRootReplacement(
339
+ source,
340
+ destination,
341
+ rootValue,
342
+ fileExtensions = ['.md', '.yaml', '.yml'],
343
+ ) {
371
344
  try {
372
345
  await this.ensureDirectory(destination);
373
-
346
+
374
347
  // Get all files in source directory
375
- const files = await resourceLocator.findFiles('**/*', {
376
- cwd: source,
377
- nodir: true
348
+ const files = await resourceLocator.findFiles('**/*', {
349
+ cwd: source,
350
+ nodir: true,
378
351
  });
379
-
352
+
380
353
  let replacedCount = 0;
381
-
354
+
382
355
  for (const file of files) {
383
356
  const sourcePath = path.join(source, file);
384
- const destPath = path.join(destination, file);
385
-
357
+ const destinationPath = path.join(destination, file);
358
+
386
359
  // Check if this file type should have {root} replacement
387
- const shouldReplace = fileExtensions.some(ext => file.endsWith(ext));
388
-
360
+ const shouldReplace = fileExtensions.some((extension) => file.endsWith(extension));
361
+
389
362
  if (shouldReplace) {
390
- if (await this.copyFileWithRootReplacement(sourcePath, destPath, rootValue)) {
363
+ if (await this.copyFileWithRootReplacement(sourcePath, destinationPath, rootValue)) {
391
364
  replacedCount++;
392
365
  }
393
366
  } else {
394
367
  // Regular copy for files that don't need replacement
395
- await this.copyFile(sourcePath, destPath);
368
+ await this.copyFile(sourcePath, destinationPath);
396
369
  }
397
370
  }
398
-
371
+
399
372
  if (replacedCount > 0) {
400
373
  console.log(chalk.dim(` Processed ${replacedCount} files with {root} replacement`));
401
374
  }
402
-
375
+
403
376
  return true;
404
377
  } catch (error) {
405
- console.error(chalk.red(`Failed to copy directory ${source} with root replacement:`), error.message);
378
+ console.error(
379
+ chalk.red(`Failed to copy directory ${source} with root replacement:`),
380
+ error.message,
381
+ );
406
382
  return false;
407
383
  }
408
384
  }
385
+ manifestDir = '.bmad-core';
386
+ manifestFile = 'install-manifest.yaml';
409
387
  }
410
388
 
411
389
  module.exports = new FileManager();