bmad-method 4.24.0 → 4.24.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 (81) hide show
  1. package/.vscode/settings.json +11 -5
  2. package/CHANGELOG.md +10 -1
  3. package/README.md +1 -1
  4. package/bmad-core/agents/bmad-master.md +1 -1
  5. package/bmad-core/agents/dev.md +2 -2
  6. package/bmad-core/agents/pm.md +1 -1
  7. package/bmad-core/agents/po.md +1 -1
  8. package/bmad-core/data/bmad-kb.md +4 -4
  9. package/bmad-core/tasks/create-brownfield-story.md +1 -1
  10. package/bmad-core/tasks/create-next-story.md +4 -4
  11. package/bmad-core/tasks/shard-doc.md +3 -3
  12. package/bmad-core/tasks/update-workflow-plan.md +1 -1
  13. package/bmad-core/templates/architecture-tmpl.md +1 -1
  14. package/bmad-core/templates/fullstack-architecture-tmpl.md +3 -3
  15. package/bmad-core/utils/plan-management.md +1 -1
  16. package/common/tasks/create-doc.md +1 -1
  17. package/docs/agentic-tools/roo-code-guide.md +1 -1
  18. package/docs/core-architecture.md +12 -12
  19. package/docs/user-guide.md +6 -6
  20. package/expansion-packs/bmad-creator-tools/tasks/generate-expansion-pack.md +9 -9
  21. package/expansion-packs/bmad-creator-tools/templates/agent-teams-tmpl.md +1 -1
  22. package/expansion-packs/bmad-creator-tools/templates/agent-tmpl.md +1 -1
  23. package/expansion-packs/bmad-infrastructure-devops/README.md +3 -3
  24. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
  25. package/package.json +1 -1
  26. package/tools/builders/web-builder.js +19 -20
  27. package/tools/bump-all-versions.js +2 -2
  28. package/tools/bump-core-version.js +1 -1
  29. package/tools/bump-expansion-version.js +1 -1
  30. package/tools/installer/README.md +1 -1
  31. package/tools/installer/bin/bmad.js +2 -2
  32. package/tools/installer/lib/config-loader.js +13 -12
  33. package/tools/installer/lib/file-manager.js +5 -5
  34. package/tools/installer/lib/ide-setup.js +14 -13
  35. package/tools/installer/lib/installer.js +24 -26
  36. package/tools/installer/package.json +1 -1
  37. package/tools/lib/dependency-resolver.js +8 -12
  38. package/tools/lib/yaml-utils.js +29 -0
  39. package/tools/update-expansion-version.js +3 -3
  40. package/tools/yaml-format.js +1 -1
  41. package/dist/agents/analyst.txt +0 -2731
  42. package/dist/agents/architect.txt +0 -3923
  43. package/dist/agents/bmad-master.txt +0 -10026
  44. package/dist/agents/bmad-orchestrator.txt +0 -2062
  45. package/dist/agents/dev.txt +0 -298
  46. package/dist/agents/pm.txt +0 -2249
  47. package/dist/agents/po.txt +0 -1511
  48. package/dist/agents/qa.txt +0 -262
  49. package/dist/agents/sm.txt +0 -726
  50. package/dist/agents/ux-expert.txt +0 -1101
  51. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +0 -2378
  52. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +0 -1584
  53. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +0 -809
  54. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +0 -7475
  55. package/dist/expansion-packs/bmad-creator-tools/agents/bmad-the-creator.txt +0 -1960
  56. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +0 -2073
  57. package/dist/teams/team-all.txt +0 -11952
  58. package/dist/teams/team-fullstack.txt +0 -11115
  59. package/dist/teams/team-ide-minimal.txt +0 -4365
  60. package/dist/teams/team-no-ui.txt +0 -9596
  61. /package/.github/{FUNDING.yml → FUNDING.yaml} +0 -0
  62. /package/.github/workflows/{release.yml → release.yaml} +0 -0
  63. /package/bmad-core/agent-teams/{team-all.yml → team-all.yaml} +0 -0
  64. /package/bmad-core/agent-teams/{team-fullstack.yml → team-fullstack.yaml} +0 -0
  65. /package/bmad-core/agent-teams/{team-ide-minimal.yml → team-ide-minimal.yaml} +0 -0
  66. /package/bmad-core/agent-teams/{team-no-ui.yml → team-no-ui.yaml} +0 -0
  67. /package/bmad-core/{core-config.yml → core-config.yaml} +0 -0
  68. /package/bmad-core/workflows/{brownfield-fullstack.yml → brownfield-fullstack.yaml} +0 -0
  69. /package/bmad-core/workflows/{brownfield-service.yml → brownfield-service.yaml} +0 -0
  70. /package/bmad-core/workflows/{brownfield-ui.yml → brownfield-ui.yaml} +0 -0
  71. /package/bmad-core/workflows/{greenfield-fullstack.yml → greenfield-fullstack.yaml} +0 -0
  72. /package/bmad-core/workflows/{greenfield-service.yml → greenfield-service.yaml} +0 -0
  73. /package/bmad-core/workflows/{greenfield-ui.yml → greenfield-ui.yaml} +0 -0
  74. /package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/{phaser-2d-nodejs-game-team.yml → phaser-2d-nodejs-game-team.yaml} +0 -0
  75. /package/expansion-packs/bmad-2d-phaser-game-dev/{config.yml → config.yaml} +0 -0
  76. /package/expansion-packs/bmad-2d-phaser-game-dev/workflows/{game-dev-greenfield.yml → game-dev-greenfield.yaml} +0 -0
  77. /package/expansion-packs/bmad-2d-phaser-game-dev/workflows/{game-prototype.yml → game-prototype.yaml} +0 -0
  78. /package/expansion-packs/bmad-creator-tools/{config.yml → config.yaml} +0 -0
  79. /package/expansion-packs/bmad-infrastructure-devops/{config.yml → config.yaml} +0 -0
  80. /package/tools/installer/config/{ide-agent-config.yml → ide-agent-config.yaml} +0 -0
  81. /package/tools/installer/config/{install.config.yml → install.config.yaml} +0 -0
@@ -38,7 +38,7 @@ function bumpVersion(currentVersion, type) {
38
38
 
39
39
  async function updateVersion() {
40
40
  try {
41
- const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yml');
41
+ const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yaml');
42
42
 
43
43
  if (!fs.existsSync(configPath)) {
44
44
  console.error(`Error: Expansion pack '${packId}' not found`);
@@ -16,7 +16,7 @@ installer/
16
16
  │ ├── ide-setup.js # IDE-specific setup
17
17
  │ └── prompts.js # Interactive CLI prompts
18
18
  ├── config/ # Configuration files
19
- │ └── install.config.yml # Installation profiles
19
+ │ └── install.config.yaml # Installation profiles
20
20
  ├── templates/ # IDE template files
21
21
  │ ├── cursor-rules.md # Cursor template
22
22
  │ ├── claude-commands.md # Claude Code template
@@ -158,7 +158,7 @@ async function promptInstallation() {
158
158
  const choices = [];
159
159
 
160
160
  // Load core config to get short-title
161
- const coreConfigPath = path.join(__dirname, '..', '..', '..', 'bmad-core', 'core-config.yml');
161
+ const coreConfigPath = path.join(__dirname, '..', '..', '..', 'bmad-core', 'core-config.yaml');
162
162
  const coreConfig = yaml.load(await fs.readFile(coreConfigPath, 'utf8'));
163
163
  const coreShortTitle = coreConfig['short-title'] || 'BMad Agile Core System';
164
164
 
@@ -166,7 +166,7 @@ async function promptInstallation() {
166
166
  let bmadOptionText;
167
167
  if (state.type === 'v4_existing') {
168
168
  const currentVersion = state.manifest?.version || 'unknown';
169
- const newVersion = coreConfig.version || 'unknown'; // Use version from core-config.yml
169
+ const newVersion = coreConfig.version || 'unknown'; // Use version from core-config.yaml
170
170
  const versionInfo = currentVersion === newVersion
171
171
  ? `(v${currentVersion} - reinstall)`
172
172
  : `(v${currentVersion} → v${newVersion})`;
@@ -1,10 +1,11 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const yaml = require('js-yaml');
4
+ const { extractYamlFromAgent } = require('../../lib/yaml-utils');
4
5
 
5
6
  class ConfigLoader {
6
7
  constructor() {
7
- this.configPath = path.join(__dirname, '..', 'config', 'install.config.yml');
8
+ this.configPath = path.join(__dirname, '..', 'config', 'install.config.yaml');
8
9
  this.config = null;
9
10
  }
10
11
 
@@ -41,9 +42,9 @@ class ConfigLoader {
41
42
  const agentContent = await fs.readFile(agentPath, 'utf8');
42
43
 
43
44
  // Extract YAML block from agent file
44
- const yamlMatch = agentContent.match(/```yml\n([\s\S]*?)\n```/);
45
- if (yamlMatch) {
46
- const yamlContent = yaml.load(yamlMatch[1]);
45
+ const yamlContentText = extractYamlFromAgent(agentContent);
46
+ if (yamlContentText) {
47
+ const yamlContent = yaml.load(yamlContentText);
47
48
  const agentConfig = yamlContent.agent || {};
48
49
 
49
50
  agents.push({
@@ -79,10 +80,10 @@ class ConfigLoader {
79
80
  for (const entry of entries) {
80
81
  if (entry.isDirectory() && !entry.name.startsWith('.')) {
81
82
  const packPath = path.join(expansionPacksDir, entry.name);
82
- const configPath = path.join(packPath, 'config.yml');
83
+ const configPath = path.join(packPath, 'config.yaml');
83
84
 
84
85
  try {
85
- // Read config.yml
86
+ // Read config.yaml
86
87
  const configContent = await fs.readFile(configPath, 'utf8');
87
88
  const config = yaml.load(configContent);
88
89
 
@@ -97,7 +98,7 @@ class ConfigLoader {
97
98
  dependencies: config.dependencies?.agents || []
98
99
  });
99
100
  } catch (error) {
100
- // Fallback if config.yml doesn't exist or can't be read
101
+ // Fallback if config.yaml doesn't exist or can't be read
101
102
  console.warn(`Failed to read config for expansion pack ${entry.name}: ${error.message}`);
102
103
 
103
104
  // Try to derive info from directory name as fallback
@@ -180,7 +181,7 @@ class ConfigLoader {
180
181
  const teams = [];
181
182
 
182
183
  for (const entry of entries) {
183
- if (entry.isFile() && entry.name.endsWith('.yml')) {
184
+ if (entry.isFile() && entry.name.endsWith('.yaml')) {
184
185
  const teamPath = path.join(teamsDir, entry.name);
185
186
 
186
187
  try {
@@ -189,7 +190,7 @@ class ConfigLoader {
189
190
 
190
191
  if (teamConfig.bundle) {
191
192
  teams.push({
192
- id: path.basename(entry.name, '.yml'),
193
+ id: path.basename(entry.name, '.yaml'),
193
194
  name: teamConfig.bundle.name || entry.name,
194
195
  description: teamConfig.bundle.description || 'Team configuration',
195
196
  icon: teamConfig.bundle.icon || '📋'
@@ -209,7 +210,7 @@ class ConfigLoader {
209
210
  }
210
211
 
211
212
  getTeamPath(teamId) {
212
- return path.join(this.getBmadCorePath(), 'agent-teams', `${teamId}.yml`);
213
+ return path.join(this.getBmadCorePath(), 'agent-teams', `${teamId}.yaml`);
213
214
  }
214
215
 
215
216
  async getTeamDependencies(teamId) {
@@ -224,7 +225,7 @@ class ConfigLoader {
224
225
  const depPaths = [];
225
226
 
226
227
  // Add team config file
227
- depPaths.push(`.bmad-core/agent-teams/${teamId}.yml`);
228
+ depPaths.push(`.bmad-core/agent-teams/${teamId}.yaml`);
228
229
 
229
230
  // Add all agents
230
231
  for (const agent of teamDeps.agents) {
@@ -236,7 +237,7 @@ class ConfigLoader {
236
237
 
237
238
  // Add all resolved resources
238
239
  for (const resource of teamDeps.resources) {
239
- const filePath = `.bmad-core/${resource.type}/${resource.id}.${resource.type === 'workflows' ? 'yml' : 'md'}`;
240
+ const filePath = `.bmad-core/${resource.type}/${resource.id}.${resource.type === 'workflows' ? 'yaml' : 'md'}`;
240
241
  if (!depPaths.includes(filePath)) {
241
242
  depPaths.push(filePath);
242
243
  }
@@ -17,7 +17,7 @@ async function initializeModules() {
17
17
  class FileManager {
18
18
  constructor() {
19
19
  this.manifestDir = ".bmad-core";
20
- this.manifestFile = "install-manifest.yml";
20
+ this.manifestFile = "install-manifest.yaml";
21
21
  }
22
22
 
23
23
  async copyFile(source, destination) {
@@ -83,15 +83,15 @@ class FileManager {
83
83
  this.manifestFile
84
84
  );
85
85
 
86
- // Read version from core-config.yml
87
- const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yml");
86
+ // Read version from core-config.yaml
87
+ const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yaml");
88
88
  let coreVersion = "unknown";
89
89
  try {
90
90
  const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
91
91
  const coreConfig = yaml.load(coreConfigContent);
92
92
  coreVersion = coreConfig.version || "unknown";
93
93
  } catch (error) {
94
- console.warn("Could not read version from core-config.yml, using 'unknown'");
94
+ console.warn("Could not read version from core-config.yaml, using 'unknown'");
95
95
  }
96
96
 
97
97
  const manifest = {
@@ -178,7 +178,7 @@ class FileManager {
178
178
  const filePath = path.join(installDir, file.path);
179
179
 
180
180
  // Skip checking the manifest file itself - it will always be different due to timestamps
181
- if (file.path.endsWith('install-manifest.yml')) {
181
+ if (file.path.endsWith('install-manifest.yaml')) {
182
182
  continue;
183
183
  }
184
184
 
@@ -3,6 +3,7 @@ const fs = require("fs-extra");
3
3
  const yaml = require("js-yaml");
4
4
  const fileManager = require("./file-manager");
5
5
  const configLoader = require("./config-loader");
6
+ const { extractYamlFromAgent } = require("../../lib/yaml-utils");
6
7
 
7
8
  // Dynamic import for ES module
8
9
  let chalk;
@@ -27,7 +28,7 @@ class IdeSetup {
27
28
  if (this.ideAgentConfig) return this.ideAgentConfig;
28
29
 
29
30
  try {
30
- const configPath = path.join(__dirname, '..', 'config', 'ide-agent-config.yml');
31
+ const configPath = path.join(__dirname, '..', 'config', 'ide-agent-config.yaml');
31
32
  const configContent = await fs.readFile(configPath, 'utf8');
32
33
  this.ideAgentConfig = yaml.load(configContent);
33
34
  return this.ideAgentConfig;
@@ -98,11 +99,11 @@ class IdeSetup {
98
99
  mdcContent += "## Agent Activation\n\n";
99
100
  mdcContent +=
100
101
  "CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
101
- mdcContent += "```yml\n";
102
+ mdcContent += "```yaml\n";
102
103
  // Extract just the YAML content from the agent file
103
- const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
104
- if (yamlMatch) {
105
- mdcContent += yamlMatch[1].trim();
104
+ const yamlContent = extractYamlFromAgent(agentContent);
105
+ if (yamlContent) {
106
+ mdcContent += yamlContent;
106
107
  } else {
107
108
  // If no YAML found, include the whole content minus the header
108
109
  mdcContent += agentContent.replace(/^#.*$/m, "").trim();
@@ -180,11 +181,11 @@ class IdeSetup {
180
181
  mdContent += "## Agent Activation\n\n";
181
182
  mdContent +=
182
183
  "CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
183
- mdContent += "```yml\n";
184
+ mdContent += "```yaml\n";
184
185
  // Extract just the YAML content from the agent file
185
- const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
186
- if (yamlMatch) {
187
- mdContent += yamlMatch[1].trim();
186
+ const yamlContent = extractYamlFromAgent(agentContent);
187
+ if (yamlContent) {
188
+ mdContent += yamlContent;
188
189
  } else {
189
190
  // If no YAML found, include the whole content minus the header
190
191
  mdContent += agentContent.replace(/^#.*$/m, "").trim();
@@ -428,11 +429,11 @@ class IdeSetup {
428
429
  mdContent += "## Role Definition\n\n";
429
430
  mdContent +=
430
431
  "When the user types `@" + agentId + "`, adopt this persona and follow these guidelines:\n\n";
431
- mdContent += "```yml\n";
432
+ mdContent += "```yaml\n";
432
433
  // Extract just the YAML content from the agent file
433
- const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
434
- if (yamlMatch) {
435
- mdContent += yamlMatch[1].trim();
434
+ const yamlContent = extractYamlFromAgent(agentContent);
435
+ if (yamlContent) {
436
+ mdContent += yamlContent;
436
437
  } else {
437
438
  // If no YAML found, include the whole content minus the header
438
439
  mdContent += agentContent.replace(/^#.*$/m, "").trim();
@@ -2,6 +2,7 @@ const path = require("node:path");
2
2
  const fileManager = require("./file-manager");
3
3
  const configLoader = require("./config-loader");
4
4
  const ideSetup = require("./ide-setup");
5
+ const { extractYamlFromAgent } = require("../../lib/yaml-utils");
5
6
 
6
7
  // Dynamic imports for ES modules
7
8
  let chalk, ora, inquirer;
@@ -19,13 +20,13 @@ class Installer {
19
20
  async getCoreVersion() {
20
21
  const yaml = require("js-yaml");
21
22
  const fs = require("fs-extra");
22
- const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yml");
23
+ const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yaml");
23
24
  try {
24
25
  const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
25
26
  const coreConfig = yaml.load(coreConfigContent);
26
27
  return coreConfig.version || "unknown";
27
28
  } catch (error) {
28
- console.warn("Could not read version from core-config.yml, using 'unknown'");
29
+ console.warn("Could not read version from core-config.yaml, using 'unknown'");
29
30
  return "unknown";
30
31
  }
31
32
  }
@@ -185,7 +186,7 @@ class Installer {
185
186
 
186
187
  // Check for V4 installation (has .bmad-core with manifest)
187
188
  const bmadCorePath = path.join(installDir, ".bmad-core");
188
- const manifestPath = path.join(bmadCorePath, "install-manifest.yml");
189
+ const manifestPath = path.join(bmadCorePath, "install-manifest.yaml");
189
190
 
190
191
  if (await fileManager.pathExists(manifestPath)) {
191
192
  state.type = "v4_existing";
@@ -713,7 +714,7 @@ class Installer {
713
714
 
714
715
  for (const file of filesToRestore) {
715
716
  // Skip the manifest file itself
716
- if (file.endsWith('install-manifest.yml')) continue;
717
+ if (file.endsWith('install-manifest.yaml')) continue;
717
718
 
718
719
  const relativePath = file.replace('.bmad-core/', '');
719
720
  const destPath = path.join(installDir, file);
@@ -1009,7 +1010,7 @@ class Installer {
1009
1010
 
1010
1011
  // Check if expansion pack already exists
1011
1012
  let expansionDotFolder = path.join(installDir, `.${packId}`);
1012
- const existingManifestPath = path.join(expansionDotFolder, 'install-manifest.yml');
1013
+ const existingManifestPath = path.join(expansionDotFolder, 'install-manifest.yaml');
1013
1014
 
1014
1015
  if (await fileManager.pathExists(existingManifestPath)) {
1015
1016
  spinner.stop();
@@ -1151,12 +1152,12 @@ class Installer {
1151
1152
  }
1152
1153
  }
1153
1154
 
1154
- // Copy config.yml
1155
- const configPath = path.join(expansionPackDir, 'config.yml');
1155
+ // Copy config.yaml
1156
+ const configPath = path.join(expansionPackDir, 'config.yaml');
1156
1157
  if (await fileManager.pathExists(configPath)) {
1157
- const configDestPath = path.join(expansionDotFolder, 'config.yml');
1158
+ const configDestPath = path.join(expansionDotFolder, 'config.yaml');
1158
1159
  if (await fileManager.copyFile(configPath, configDestPath)) {
1159
- installedFiles.push(path.join(`.${packId}`, 'config.yml'));
1160
+ installedFiles.push(path.join(`.${packId}`, 'config.yaml'));
1160
1161
  }
1161
1162
  }
1162
1163
 
@@ -1222,10 +1223,10 @@ class Installer {
1222
1223
  const agentContent = await fs.readFile(agentPath, 'utf8');
1223
1224
 
1224
1225
  // Extract YAML frontmatter to check dependencies
1225
- const yamlMatch = agentContent.match(/```yaml\n([\s\S]*?)```/);
1226
- if (yamlMatch) {
1226
+ const yamlContent = extractYamlFromAgent(agentContent);
1227
+ if (yamlContent) {
1227
1228
  try {
1228
- const agentConfig = yaml.parse(yamlMatch[1]);
1229
+ const agentConfig = yaml.parse(yamlContent);
1229
1230
  const dependencies = agentConfig.dependencies || {};
1230
1231
 
1231
1232
  // Check for core dependencies (those that don't exist in the expansion pack)
@@ -1268,7 +1269,7 @@ class Installer {
1268
1269
  const fs = require('fs').promises;
1269
1270
 
1270
1271
  // Find all team files in the expansion pack
1271
- const teamFiles = glob.sync('agent-teams/*.yml', {
1272
+ const teamFiles = glob.sync('agent-teams/*.yaml', {
1272
1273
  cwd: expansionDotFolder
1273
1274
  });
1274
1275
 
@@ -1314,13 +1315,10 @@ class Installer {
1314
1315
 
1315
1316
  // Now resolve this agent's dependencies too
1316
1317
  const agentContent = await fs.readFile(coreAgentPath, 'utf8');
1317
- const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
1318
+ const yamlContent = extractYamlFromAgent(agentContent, true);
1318
1319
 
1319
- if (yamlMatch) {
1320
+ if (yamlContent) {
1320
1321
  try {
1321
- // Clean up the YAML to handle command descriptions
1322
- let yamlContent = yamlMatch[1];
1323
- yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
1324
1322
 
1325
1323
  const agentConfig = yaml.parse(yamlContent);
1326
1324
  const dependencies = agentConfig.dependencies || {};
@@ -1330,7 +1328,7 @@ class Installer {
1330
1328
  const deps = dependencies[depType] || [];
1331
1329
 
1332
1330
  for (const dep of deps) {
1333
- const depFileName = dep.endsWith('.md') || dep.endsWith('.yml') ? dep : `${dep}.md`;
1331
+ const depFileName = dep.endsWith('.md') || dep.endsWith('.yaml') ? dep : `${dep}.md`;
1334
1332
  const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
1335
1333
 
1336
1334
  // Check if dependency exists in expansion pack
@@ -1360,7 +1358,7 @@ class Installer {
1360
1358
  }
1361
1359
  }
1362
1360
  } else {
1363
- console.warn(chalk.yellow(` Warning: Core agent ${agentId} not found for team ${path.basename(teamFile, '.yml')}`));
1361
+ console.warn(chalk.yellow(` Warning: Core agent ${agentId} not found for team ${path.basename(teamFile, '.yaml')}`));
1364
1362
  }
1365
1363
  }
1366
1364
  }
@@ -1528,7 +1526,7 @@ class Installer {
1528
1526
 
1529
1527
  if (stats) {
1530
1528
  // Check if it has a manifest
1531
- const manifestPath = path.join(folderPath, "install-manifest.yml");
1529
+ const manifestPath = path.join(folderPath, "install-manifest.yaml");
1532
1530
  if (await fileManager.pathExists(manifestPath)) {
1533
1531
  const manifest = await fileManager.readExpansionPackManifest(installDir, folder.substring(1));
1534
1532
  if (manifest) {
@@ -1539,8 +1537,8 @@ class Installer {
1539
1537
  };
1540
1538
  }
1541
1539
  } else {
1542
- // Check if it has a config.yml (expansion pack without manifest)
1543
- const configPath = path.join(folderPath, "config.yml");
1540
+ // Check if it has a config.yaml (expansion pack without manifest)
1541
+ const configPath = path.join(folderPath, "config.yaml");
1544
1542
  if (await fileManager.pathExists(configPath)) {
1545
1543
  expansionPacks[folder.substring(1)] = {
1546
1544
  path: folderPath,
@@ -1579,7 +1577,7 @@ class Installer {
1579
1577
 
1580
1578
  for (const file of filesToRestore) {
1581
1579
  // Skip the manifest file itself
1582
- if (file.endsWith('install-manifest.yml')) continue;
1580
+ if (file.endsWith('install-manifest.yaml')) continue;
1583
1581
 
1584
1582
  const relativePath = file.replace(`.${packId}/`, '');
1585
1583
  const sourcePath = path.join(pack.packPath, relativePath);
@@ -1645,7 +1643,7 @@ class Installer {
1645
1643
 
1646
1644
  while (currentDir !== path.dirname(currentDir)) {
1647
1645
  const bmadDir = path.join(currentDir, ".bmad-core");
1648
- const manifestPath = path.join(bmadDir, "install-manifest.yml");
1646
+ const manifestPath = path.join(bmadDir, "install-manifest.yaml");
1649
1647
 
1650
1648
  if (await fileManager.pathExists(manifestPath)) {
1651
1649
  return bmadDir;
@@ -1656,7 +1654,7 @@ class Installer {
1656
1654
 
1657
1655
  // Also check if we're inside a .bmad-core directory
1658
1656
  if (path.basename(process.cwd()) === ".bmad-core") {
1659
- const manifestPath = path.join(process.cwd(), "install-manifest.yml");
1657
+ const manifestPath = path.join(process.cwd(), "install-manifest.yaml");
1660
1658
  if (await fileManager.pathExists(manifestPath)) {
1661
1659
  return process.cwd();
1662
1660
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmad-method",
3
- "version": "4.24.0",
3
+ "version": "4.24.1",
4
4
  "description": "BMAD Method installer - AI-powered Agile development framework",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
  const yaml = require('js-yaml');
4
+ const { extractYamlFromAgent } = require('./yaml-utils');
4
5
 
5
6
  class DependencyResolver {
6
7
  constructor(rootDir) {
@@ -14,17 +15,12 @@ class DependencyResolver {
14
15
  const agentPath = path.join(this.bmadCore, 'agents', `${agentId}.md`);
15
16
  const agentContent = await fs.readFile(agentPath, 'utf8');
16
17
 
17
- // Extract YAML from markdown content
18
- const yamlMatch = agentContent.replace(/\r/g, "").match(/```ya?ml\n([\s\S]*?)\n```/);
19
- if (!yamlMatch) {
18
+ // Extract YAML from markdown content with command cleaning
19
+ const yamlContent = extractYamlFromAgent(agentContent, true);
20
+ if (!yamlContent) {
20
21
  throw new Error(`No YAML configuration found in agent ${agentId}`);
21
22
  }
22
23
 
23
- // Clean up the YAML to handle command descriptions after dashes
24
- let yamlContent = yamlMatch[1];
25
- // Fix commands section: convert "- command - description" to just "- command"
26
- yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
27
-
28
24
  const agentConfig = yaml.load(yamlContent);
29
25
 
30
26
  const dependencies = {
@@ -53,7 +49,7 @@ class DependencyResolver {
53
49
  }
54
50
 
55
51
  async resolveTeamDependencies(teamId) {
56
- const teamPath = path.join(this.bmadCore, 'agent-teams', `${teamId}.yml`);
52
+ const teamPath = path.join(this.bmadCore, 'agent-teams', `${teamId}.yaml`);
57
53
  const teamContent = await fs.readFile(teamPath, 'utf8');
58
54
  const teamConfig = yaml.load(teamContent);
59
55
 
@@ -120,7 +116,7 @@ class DependencyResolver {
120
116
  }
121
117
 
122
118
  try {
123
- const extensions = ['.md', '.yml', '.yaml'];
119
+ const extensions = ['.md', '.yaml'];
124
120
  let content = null;
125
121
  let filePath = null;
126
122
 
@@ -183,8 +179,8 @@ class DependencyResolver {
183
179
  try {
184
180
  const files = await fs.readdir(path.join(this.bmadCore, 'agent-teams'));
185
181
  return files
186
- .filter(f => f.endsWith('.yml'))
187
- .map(f => f.replace('.yml', ''));
182
+ .filter(f => f.endsWith('.yaml'))
183
+ .map(f => f.replace('.yaml', ''));
188
184
  } catch (error) {
189
185
  return [];
190
186
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Utility functions for YAML extraction from agent files
3
+ */
4
+
5
+ /**
6
+ * Extract YAML content from agent markdown files
7
+ * @param {string} agentContent - The full content of the agent file
8
+ * @param {boolean} cleanCommands - Whether to clean command descriptions (default: false)
9
+ * @returns {string|null} - The extracted YAML content or null if not found
10
+ */
11
+ function extractYamlFromAgent(agentContent, cleanCommands = false) {
12
+ // Remove carriage returns and match YAML block
13
+ const yamlMatch = agentContent.replace(/\r/g, "").match(/```ya?ml\n([\s\S]*?)\n```/);
14
+ if (!yamlMatch) return null;
15
+
16
+ let yamlContent = yamlMatch[1].trim();
17
+
18
+ // Clean up command descriptions if requested
19
+ // Converts "- command - description" to just "- command"
20
+ if (cleanCommands) {
21
+ yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
22
+ }
23
+
24
+ return yamlContent;
25
+ }
26
+
27
+ module.exports = {
28
+ extractYamlFromAgent
29
+ };
@@ -22,8 +22,8 @@ if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
22
22
 
23
23
  async function updateVersion() {
24
24
  try {
25
- // Update in config.yml
26
- const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yml');
25
+ // Update in config.yaml
26
+ const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yaml');
27
27
 
28
28
  if (!fs.existsSync(configPath)) {
29
29
  console.error(`Error: Expansion pack '${packId}' not found`);
@@ -39,7 +39,7 @@ async function updateVersion() {
39
39
  const updatedYaml = yaml.dump(config, { indent: 2 });
40
40
  fs.writeFileSync(configPath, updatedYaml);
41
41
 
42
- console.log(`✓ Updated ${packId}/config.yml: ${oldVersion} → ${newVersion}`);
42
+ console.log(`✓ Updated ${packId}/config.yaml: ${oldVersion} → ${newVersion}`);
43
43
  console.log(`\n✓ Successfully updated ${packId} to version ${newVersion}`);
44
44
  console.log('\nNext steps:');
45
45
  console.log('1. Test the changes');
@@ -197,7 +197,7 @@ async function main() {
197
197
  let changed = false;
198
198
  if (ext === '.md') {
199
199
  changed = await processMarkdownFile(filePath);
200
- } else if (ext === '.yml' || ext === '.yaml' || basename.includes('roomodes') || basename.includes('.yml') || basename.includes('.yaml')) {
200
+ } else if (ext === '.yaml' || ext === '.yml' || basename.includes('roomodes') || basename.includes('.yaml') || basename.includes('.yml')) {
201
201
  // Handle YAML files and special cases like .roomodes
202
202
  changed = await processYamlFile(filePath);
203
203