bmad-method 4.17.0 → 4.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/.claude/commands/bmad-master.md +0 -1
  2. package/CHANGELOG.md +20 -0
  3. package/bmad-core/core-config.yml +0 -1
  4. package/{bmad-core → common}/tasks/create-doc.md +2 -2
  5. package/{expansion-packs/expansion-creator/common-tasks → common/tasks}/execute-checklist.md +2 -6
  6. package/common/utils/workflow-management.md +69 -0
  7. package/dist/agents/analyst.txt +2 -2
  8. package/dist/agents/architect.txt +4 -8
  9. package/dist/agents/bmad-master.txt +35 -270
  10. package/dist/agents/bmad-orchestrator.txt +33 -187
  11. package/dist/agents/dev.txt +2 -6
  12. package/dist/agents/pm.txt +4 -8
  13. package/dist/agents/po.txt +2 -6
  14. package/dist/agents/sm.txt +2 -6
  15. package/dist/agents/ux-expert.txt +4 -8
  16. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +4 -8
  17. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +2 -6
  18. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +2 -6
  19. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +35 -193
  20. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2 -2
  21. package/dist/expansion-packs/expansion-creator/agents/bmad-the-creator.txt +5 -5
  22. package/dist/teams/team-all.txt +35 -193
  23. package/dist/teams/team-fullstack.txt +35 -193
  24. package/dist/teams/team-ide-minimal.txt +35 -193
  25. package/dist/teams/team-no-ui.txt +35 -193
  26. package/docs/working-in-the-brownfield.md +2 -2
  27. package/expansion-packs/bmad-2d-phaser-game-dev/config.yml +5 -0
  28. package/expansion-packs/bmad-creator-tools/config.yml +5 -0
  29. package/expansion-packs/{expansion-creator → bmad-creator-tools}/tasks/generate-expansion-pack.md +5 -5
  30. package/expansion-packs/bmad-infrastructure-devops/config.yml +5 -0
  31. package/package.json +1 -1
  32. package/test-ide-paths.js +41 -0
  33. package/tools/builders/web-builder.js +60 -19
  34. package/tools/installer/config/ide-agent-config.yml +58 -0
  35. package/tools/installer/config/install.config.yml +0 -85
  36. package/tools/installer/lib/config-loader.js +89 -41
  37. package/tools/installer/lib/file-manager.js +1 -0
  38. package/tools/installer/lib/ide-setup.js +150 -116
  39. package/tools/installer/lib/installer.js +263 -9
  40. package/tools/installer/package.json +1 -1
  41. package/tools/lib/dependency-resolver.js +15 -0
  42. package/bmad-core/tasks/core-dump.md +0 -74
  43. package/bmad-core/tasks/execute-checklist.md +0 -97
  44. package/bmad-core/utils/file-resolution-context.md +0 -10
  45. package/bmad-core/utils/workflow-management.md +0 -223
  46. package/expansion-packs/bmad-2d-phaser-game-dev/manifest.yml +0 -45
  47. package/expansion-packs/bmad-infrastructure-devops/manifest.yml +0 -23
  48. package/expansion-packs/bmad-infrastructure-devops/tasks/create-doc.md +0 -74
  49. package/expansion-packs/expansion-creator/common-tasks/create-doc.md +0 -74
  50. package/expansion-packs/expansion-creator/manifest.yml +0 -12
  51. package/expansion-packs/expansion-creator/utils/template-format.md +0 -26
  52. package/expansion-packs/expansion-creator/utils/workflow-management.md +0 -223
  53. /package/{bmad-core → common}/utils/template-format.md +0 -0
  54. /package/expansion-packs/{expansion-creator → bmad-creator-tools}/README.md +0 -0
  55. /package/expansion-packs/{expansion-creator → bmad-creator-tools}/agents/bmad-the-creator.md +0 -0
  56. /package/expansion-packs/{expansion-creator → bmad-creator-tools}/tasks/create-agent.md +0 -0
  57. /package/expansion-packs/{expansion-creator → bmad-creator-tools}/templates/agent-teams-tmpl.md +0 -0
  58. /package/expansion-packs/{expansion-creator → bmad-creator-tools}/templates/agent-tmpl.md +0 -0
  59. /package/expansion-packs/{expansion-creator → bmad-creator-tools}/templates/expansion-pack-plan-tmpl.md +0 -0
  60. /package/{bmad-core/utils → tools/md-assets}/web-agent-startup-instructions.md +0 -0
@@ -8,50 +8,6 @@ installation-options:
8
8
  name: Single Agent
9
9
  description: Select and install a single agent with its dependencies
10
10
  action: copy-agent
11
- agent-dependencies:
12
- core-files:
13
- - bmad-core/utils/template-format.md
14
- dev:
15
- - bmad-core/templates/story-tmpl.md
16
- - bmad-core/checklists/story-dod-checklist.md
17
- pm:
18
- - bmad-core/templates/prd-tmpl.md
19
- - bmad-core/templates/brownfield-prd-tmpl.md
20
- - bmad-core/checklists/pm-checklist.md
21
- - bmad-core/checklists/change-checklist.md
22
- - bmad-core/tasks/advanced-elicitation.md
23
- - bmad-core/tasks/create-doc.md
24
- - bmad-core/tasks/correct-course.md
25
- - bmad-core/tasks/create-deep-research-prompt.md
26
- - bmad-core/tasks/brownfield-create-epic.md
27
- - bmad-core/tasks/brownfield-create-story.md
28
- - bmad-core/tasks/execute-checklist.md
29
- - bmad-core/tasks/shard-doc.md
30
- architect:
31
- - bmad-core/templates/architecture-tmpl.md
32
- - bmad-core/checklists/architect-checklist.md
33
- sm:
34
- - bmad-core/templates/story-tmpl.md
35
- - bmad-core/checklists/story-draft-checklist.md
36
- - bmad-core/workflows/*.yml
37
- po:
38
- - bmad-core/checklists/po-master-checklist.md
39
- - bmad-core/templates/acceptance-criteria-tmpl.md
40
- analyst:
41
- - bmad-core/templates/prd-tmpl.md
42
- - bmad-core/tasks/advanced-elicitation.md
43
- qa:
44
- - bmad-core/checklists/story-dod-checklist.md
45
- - bmad-core/templates/test-plan-tmpl.md
46
- ux-expert:
47
- - bmad-core/templates/ux-tmpl.md
48
- bmad-master:
49
- - bmad-core/templates/*.md
50
- - bmad-core/tasks/*.md
51
- - bmad-core/schemas/*.yml
52
- bmad-orchestrator:
53
- - bmad-core/agent-teams/*.yml
54
- - bmad-core/workflows/*.yml
55
11
  ide-configurations:
56
12
  cursor:
57
13
  name: Cursor
@@ -111,44 +67,3 @@ ide-configurations:
111
67
  # 2. It also configures .gemini/settings.json to load all agent files.
112
68
  # 3. Simply mention the agent in your prompt (e.g., "As @dev, ...").
113
69
  # 4. The Gemini CLI will automatically have the context for that agent.
114
- available-agents:
115
- - id: analyst
116
- name: Business Analyst
117
- file: bmad-core/agents/analyst.md
118
- description: Requirements gathering and analysis
119
- - id: pm
120
- name: Product Manager
121
- file: bmad-core/agents/pm.md
122
- description: Product strategy and roadmap planning
123
- - id: architect
124
- name: Solution Architect
125
- file: bmad-core/agents/architect.md
126
- description: Technical design and architecture
127
- - id: po
128
- name: Product Owner
129
- file: bmad-core/agents/po.md
130
- description: Backlog management and prioritization
131
- - id: sm
132
- name: Scrum Master
133
- file: bmad-core/agents/sm.md
134
- description: Agile process and story creation
135
- - id: dev
136
- name: Developer
137
- file: bmad-core/agents/dev.md
138
- description: Code implementation and testing
139
- - id: qa
140
- name: QA Engineer
141
- file: bmad-core/agents/qa.md
142
- description: Quality assurance and testing
143
- - id: ux-expert
144
- name: UX Expert
145
- file: bmad-core/agents/ux-expert.md
146
- description: User experience design
147
- - id: bmad-master
148
- name: BMAD Master
149
- file: bmad-core/agents/bmad-master.md
150
- description: BMAD framework expert and guide
151
- - id: bmad-orchestrator
152
- name: BMAD Orchestrator
153
- file: bmad-core/agents/bmad-orchestrator.md
154
- description: Multi-agent workflow coordinator
@@ -26,8 +26,47 @@ class ConfigLoader {
26
26
  }
27
27
 
28
28
  async getAvailableAgents() {
29
- const config = await this.load();
30
- return config['available-agents'] || [];
29
+ const agentsDir = path.join(this.getBmadCorePath(), 'agents');
30
+
31
+ try {
32
+ const entries = await fs.readdir(agentsDir, { withFileTypes: true });
33
+ const agents = [];
34
+
35
+ for (const entry of entries) {
36
+ if (entry.isFile() && entry.name.endsWith('.md')) {
37
+ const agentPath = path.join(agentsDir, entry.name);
38
+ const agentId = path.basename(entry.name, '.md');
39
+
40
+ try {
41
+ const agentContent = await fs.readFile(agentPath, 'utf8');
42
+
43
+ // 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]);
47
+ const agentConfig = yamlContent.agent || {};
48
+
49
+ agents.push({
50
+ id: agentId,
51
+ name: agentConfig.title || agentConfig.name || agentId,
52
+ file: `bmad-core/agents/${entry.name}`,
53
+ description: agentConfig.whenToUse || 'No description available'
54
+ });
55
+ }
56
+ } catch (error) {
57
+ console.warn(`Failed to read agent ${entry.name}: ${error.message}`);
58
+ }
59
+ }
60
+ }
61
+
62
+ // Sort agents by name for consistent display
63
+ agents.sort((a, b) => a.name.localeCompare(b.name));
64
+
65
+ return agents;
66
+ } catch (error) {
67
+ console.warn(`Failed to read agents directory: ${error.message}`);
68
+ return [];
69
+ }
31
70
  }
32
71
 
33
72
  async getAvailableExpansionPacks() {
@@ -38,24 +77,45 @@ class ConfigLoader {
38
77
  const expansionPacks = [];
39
78
 
40
79
  for (const entry of entries) {
41
- if (entry.isDirectory()) {
42
- const manifestPath = path.join(expansionPacksDir, entry.name, 'manifest.yml');
80
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
81
+ const packPath = path.join(expansionPacksDir, entry.name);
82
+ const configPath = path.join(packPath, 'config.yml');
43
83
 
44
84
  try {
45
- const manifestContent = await fs.readFile(manifestPath, 'utf8');
46
- const manifest = yaml.load(manifestContent);
85
+ // Read config.yml
86
+ const configContent = await fs.readFile(configPath, 'utf8');
87
+ const config = yaml.load(configContent);
47
88
 
48
89
  expansionPacks.push({
49
90
  id: entry.name,
50
- name: manifest.name || entry.name,
51
- description: manifest.description || 'No description available',
52
- version: manifest.version || '1.0.0',
53
- author: manifest.author || 'Unknown',
54
- manifestPath: manifestPath,
55
- dependencies: manifest.dependencies || []
91
+ name: config.name || entry.name,
92
+ description: config['short-title'] || config.description || 'No description available',
93
+ fullDescription: config.description || config['short-title'] || 'No description available',
94
+ version: config.version || '1.0.0',
95
+ author: config.author || 'BMAD Team',
96
+ packPath: packPath,
97
+ dependencies: config.dependencies?.agents || []
56
98
  });
57
99
  } catch (error) {
58
- console.warn(`Failed to read manifest for expansion pack ${entry.name}: ${error.message}`);
100
+ // Fallback if config.yml doesn't exist or can't be read
101
+ console.warn(`Failed to read config for expansion pack ${entry.name}: ${error.message}`);
102
+
103
+ // Try to derive info from directory name as fallback
104
+ const name = entry.name
105
+ .split('-')
106
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
107
+ .join(' ');
108
+
109
+ expansionPacks.push({
110
+ id: entry.name,
111
+ name: name,
112
+ description: 'No description available',
113
+ fullDescription: 'No description available',
114
+ version: '1.0.0',
115
+ author: 'BMAD Team',
116
+ packPath: packPath,
117
+ dependencies: []
118
+ });
59
119
  }
60
120
  }
61
121
  }
@@ -72,36 +132,24 @@ class ConfigLoader {
72
132
  const DependencyResolver = require('../../lib/dependency-resolver');
73
133
  const resolver = new DependencyResolver(path.join(__dirname, '..', '..', '..'));
74
134
 
75
- try {
76
- const agentDeps = await resolver.resolveAgentDependencies(agentId);
77
-
78
- // Convert to flat list of file paths
79
- const depPaths = [];
80
-
81
- // Core files and utilities are included automatically by DependencyResolver
82
-
83
- // Add agent file itself is already handled by installer
84
-
85
- // Add all resolved resources
86
- for (const resource of agentDeps.resources) {
87
- const filePath = `.bmad-core/${resource.type}/${resource.id}.md`;
88
- if (!depPaths.includes(filePath)) {
89
- depPaths.push(filePath);
90
- }
135
+ const agentDeps = await resolver.resolveAgentDependencies(agentId);
136
+
137
+ // Convert to flat list of file paths
138
+ const depPaths = [];
139
+
140
+ // Core files and utilities are included automatically by DependencyResolver
141
+
142
+ // Add agent file itself is already handled by installer
143
+
144
+ // Add all resolved resources
145
+ for (const resource of agentDeps.resources) {
146
+ const filePath = `.bmad-core/${resource.type}/${resource.id}.md`;
147
+ if (!depPaths.includes(filePath)) {
148
+ depPaths.push(filePath);
91
149
  }
92
-
93
- return depPaths;
94
- } catch (error) {
95
- console.warn(`Failed to dynamically resolve dependencies for ${agentId}: ${error.message}`);
96
-
97
- // Fall back to static config
98
- const config = await this.load();
99
- const dependencies = config['agent-dependencies'] || {};
100
- const coreFiles = dependencies['core-files'] || [];
101
- const agentDeps = dependencies[agentId] || [];
102
-
103
- return [...coreFiles, ...agentDeps];
104
150
  }
151
+
152
+ return depPaths;
105
153
  }
106
154
 
107
155
  async getIdeConfiguration(ide) {
@@ -90,6 +90,7 @@ class FileManager {
90
90
  agent: config.agent || null,
91
91
  ide_setup: config.ide || null,
92
92
  ides_setup: config.ides || [],
93
+ expansion_packs: config.expansionPacks || [],
93
94
  files: [],
94
95
  };
95
96
 
@@ -1,4 +1,6 @@
1
1
  const path = require("path");
2
+ const fs = require("fs-extra");
3
+ const yaml = require("js-yaml");
2
4
  const fileManager = require("./file-manager");
3
5
  const configLoader = require("./config-loader");
4
6
 
@@ -13,6 +15,27 @@ async function initializeModules() {
13
15
  }
14
16
 
15
17
  class IdeSetup {
18
+ constructor() {
19
+ this.ideAgentConfig = null;
20
+ }
21
+
22
+ async loadIdeAgentConfig() {
23
+ if (this.ideAgentConfig) return this.ideAgentConfig;
24
+
25
+ try {
26
+ const configPath = path.join(__dirname, '..', 'config', 'ide-agent-config.yml');
27
+ const configContent = await fs.readFile(configPath, 'utf8');
28
+ this.ideAgentConfig = yaml.load(configContent);
29
+ return this.ideAgentConfig;
30
+ } catch (error) {
31
+ console.warn('Failed to load IDE agent configuration, using defaults');
32
+ return {
33
+ 'roo-permissions': {},
34
+ 'cline-order': {}
35
+ };
36
+ }
37
+ }
38
+
16
39
  async setup(ide, installDir, selectedAgent = null) {
17
40
  await initializeModules();
18
41
  const ideConfig = await configLoader.getIdeConfiguration(ide);
@@ -48,13 +71,10 @@ class IdeSetup {
48
71
  await fileManager.ensureDirectory(cursorRulesDir);
49
72
 
50
73
  for (const agentId of agents) {
51
- // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
52
- let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
53
- if (!(await fileManager.pathExists(agentPath))) {
54
- agentPath = path.join(installDir, "agents", `${agentId}.md`);
55
- }
74
+ // Find the agent file
75
+ const agentPath = await this.findAgentPath(agentId, installDir);
56
76
 
57
- if (await fileManager.pathExists(agentPath)) {
77
+ if (agentPath) {
58
78
  const agentContent = await fileManager.readFile(agentPath);
59
79
  const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
60
80
 
@@ -65,8 +85,9 @@ class IdeSetup {
65
85
  mdcContent += "alwaysApply: false\n";
66
86
  mdcContent += "---\n\n";
67
87
  mdcContent += `# ${agentId.toUpperCase()} Agent Rule\n\n`;
68
- mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
69
- agentId
88
+ mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${await this.getAgentTitle(
89
+ agentId,
90
+ installDir
70
91
  )} agent persona.\n\n`;
71
92
  mdcContent += "## Agent Activation\n\n";
72
93
  mdcContent +=
@@ -82,10 +103,12 @@ class IdeSetup {
82
103
  }
83
104
  mdcContent += "\n```\n\n";
84
105
  mdcContent += "## File Reference\n\n";
85
- mdcContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](mdc:.bmad-core/agents/${agentId}.md).\n\n`;
106
+ const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
107
+ mdcContent += `The complete agent definition is available in [${relativePath}](mdc:${relativePath}).\n\n`;
86
108
  mdcContent += "## Usage\n\n";
87
- mdcContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
88
- agentId
109
+ mdcContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
110
+ agentId,
111
+ installDir
89
112
  )} persona and follow all instructions defined in the YML configuration above.\n`;
90
113
 
91
114
  await fileManager.writeFile(mdcPath, mdcContent);
@@ -105,14 +128,11 @@ class IdeSetup {
105
128
  await fileManager.ensureDirectory(commandsDir);
106
129
 
107
130
  for (const agentId of agents) {
108
- // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
109
- let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
110
- if (!(await fileManager.pathExists(agentPath))) {
111
- agentPath = path.join(installDir, "agents", `${agentId}.md`);
112
- }
131
+ // Find the agent file
132
+ const agentPath = await this.findAgentPath(agentId, installDir);
113
133
  const commandPath = path.join(commandsDir, `${agentId}.md`);
114
134
 
115
- if (await fileManager.pathExists(agentPath)) {
135
+ if (agentPath) {
116
136
  // Create command file with agent content
117
137
  const agentContent = await fileManager.readFile(agentPath);
118
138
 
@@ -138,20 +158,18 @@ class IdeSetup {
138
158
  await fileManager.ensureDirectory(windsurfRulesDir);
139
159
 
140
160
  for (const agentId of agents) {
141
- // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
142
- let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
143
- if (!(await fileManager.pathExists(agentPath))) {
144
- agentPath = path.join(installDir, "agents", `${agentId}.md`);
145
- }
161
+ // Find the agent file
162
+ const agentPath = await this.findAgentPath(agentId, installDir);
146
163
 
147
- if (await fileManager.pathExists(agentPath)) {
164
+ if (agentPath) {
148
165
  const agentContent = await fileManager.readFile(agentPath);
149
166
  const mdPath = path.join(windsurfRulesDir, `${agentId}.md`);
150
167
 
151
168
  // Create MD content (similar to Cursor but without frontmatter)
152
169
  let mdContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
153
- mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
154
- agentId
170
+ mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${await this.getAgentTitle(
171
+ agentId,
172
+ installDir
155
173
  )} agent persona.\n\n`;
156
174
  mdContent += "## Agent Activation\n\n";
157
175
  mdContent +=
@@ -167,10 +185,12 @@ class IdeSetup {
167
185
  }
168
186
  mdContent += "\n```\n\n";
169
187
  mdContent += "## File Reference\n\n";
170
- mdContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md).\n\n`;
188
+ const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
189
+ mdContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
171
190
  mdContent += "## Usage\n\n";
172
- mdContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
173
- agentId
191
+ mdContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
192
+ agentId,
193
+ installDir
174
194
  )} persona and follow all instructions defined in the YML configuration above.\n`;
175
195
 
176
196
  await fileManager.writeFile(mdPath, mdContent);
@@ -183,32 +203,93 @@ class IdeSetup {
183
203
  return true;
184
204
  }
185
205
 
206
+ async findAgentPath(agentId, installDir) {
207
+ // Try to find the agent file in various locations
208
+ const possiblePaths = [
209
+ path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
210
+ path.join(installDir, "agents", `${agentId}.md`)
211
+ ];
212
+
213
+ // Also check expansion pack directories
214
+ const glob = require("glob");
215
+ const expansionDirs = glob.sync(".*/agents", { cwd: installDir });
216
+ for (const expDir of expansionDirs) {
217
+ possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
218
+ }
219
+
220
+ for (const agentPath of possiblePaths) {
221
+ if (await fileManager.pathExists(agentPath)) {
222
+ return agentPath;
223
+ }
224
+ }
225
+
226
+ return null;
227
+ }
228
+
186
229
  async getAllAgentIds(installDir) {
187
- // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
230
+ const glob = require("glob");
231
+ const allAgentIds = [];
232
+
233
+ // Check core agents in .bmad-core or root
188
234
  let agentsDir = path.join(installDir, ".bmad-core", "agents");
189
235
  if (!(await fileManager.pathExists(agentsDir))) {
190
236
  agentsDir = path.join(installDir, "agents");
191
237
  }
192
-
193
- const glob = require("glob");
194
- const agentFiles = glob.sync("*.md", { cwd: agentsDir });
195
- return agentFiles.map((file) => path.basename(file, ".md"));
238
+
239
+ if (await fileManager.pathExists(agentsDir)) {
240
+ const agentFiles = glob.sync("*.md", { cwd: agentsDir });
241
+ allAgentIds.push(...agentFiles.map((file) => path.basename(file, ".md")));
242
+ }
243
+
244
+ // Also check for expansion pack agents in dot folders
245
+ const expansionDirs = glob.sync(".*/agents", { cwd: installDir });
246
+ for (const expDir of expansionDirs) {
247
+ const fullExpDir = path.join(installDir, expDir);
248
+ const expAgentFiles = glob.sync("*.md", { cwd: fullExpDir });
249
+ allAgentIds.push(...expAgentFiles.map((file) => path.basename(file, ".md")));
250
+ }
251
+
252
+ // Remove duplicates
253
+ return [...new Set(allAgentIds)];
196
254
  }
197
255
 
198
- getAgentTitle(agentId) {
199
- const agentTitles = {
200
- analyst: "Business Analyst",
201
- architect: "Solution Architect",
202
- "bmad-master": "BMAD Master",
203
- "bmad-orchestrator": "BMAD Orchestrator",
204
- dev: "Developer",
205
- pm: "Product Manager",
206
- po: "Product Owner",
207
- qa: "QA Specialist",
208
- sm: "Scrum Master",
209
- "ux-expert": "UX Expert",
210
- };
211
- return agentTitles[agentId] || agentId;
256
+ async getAgentTitle(agentId, installDir) {
257
+ // Try to find the agent file in various locations
258
+ const possiblePaths = [
259
+ path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
260
+ path.join(installDir, "agents", `${agentId}.md`)
261
+ ];
262
+
263
+ // Also check expansion pack directories
264
+ const glob = require("glob");
265
+ const expansionDirs = glob.sync(".*/agents", { cwd: installDir });
266
+ for (const expDir of expansionDirs) {
267
+ possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
268
+ }
269
+
270
+ for (const agentPath of possiblePaths) {
271
+ if (await fileManager.pathExists(agentPath)) {
272
+ try {
273
+ const agentContent = await fileManager.readFile(agentPath);
274
+ const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
275
+
276
+ if (yamlMatch) {
277
+ const yaml = yamlMatch[1];
278
+ const titleMatch = yaml.match(/title:\s*(.+)/);
279
+ if (titleMatch) {
280
+ return titleMatch[1].trim();
281
+ }
282
+ }
283
+ } catch (error) {
284
+ console.warn(`Failed to read agent title for ${agentId}: ${error.message}`);
285
+ }
286
+ }
287
+ }
288
+
289
+ // Fallback to formatted agent ID
290
+ return agentId.split('-').map(word =>
291
+ word.charAt(0).toUpperCase() + word.slice(1)
292
+ ).join(' ');
212
293
  }
213
294
 
214
295
  async setupRoo(installDir, selectedAgent) {
@@ -232,40 +313,9 @@ class IdeSetup {
232
313
  // Create new modes content
233
314
  let newModesContent = "";
234
315
 
235
- // Define file permissions for each agent type
236
- const agentPermissions = {
237
- analyst: {
238
- fileRegex: "\\.(md|txt)$",
239
- description: "Documentation and text files",
240
- },
241
- pm: {
242
- fileRegex: "\\.(md|txt)$",
243
- description: "Product documentation",
244
- },
245
- architect: {
246
- fileRegex: "\\.(md|txt|yml|yaml|json)$",
247
- description: "Architecture docs and configs",
248
- },
249
- dev: null, // Full edit access
250
- qa: {
251
- fileRegex: "\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$",
252
- description: "Test files and documentation",
253
- },
254
- "ux-expert": {
255
- fileRegex: "\\.(md|css|scss|html|jsx|tsx)$",
256
- description: "Design-related files",
257
- },
258
- po: {
259
- fileRegex: "\\.(md|txt)$",
260
- description: "Story and requirement docs",
261
- },
262
- sm: {
263
- fileRegex: "\\.(md|txt)$",
264
- description: "Process and planning docs",
265
- },
266
- "bmad-orchestrator": null, // Full edit access
267
- "bmad-master": null, // Full edit access
268
- };
316
+ // Load dynamic agent permissions from configuration
317
+ const config = await this.loadIdeAgentConfig();
318
+ const agentPermissions = config['roo-permissions'] || {};
269
319
 
270
320
  for (const agentId of agents) {
271
321
  // Skip if already exists
@@ -275,12 +325,9 @@ class IdeSetup {
275
325
  }
276
326
 
277
327
  // Read agent file to extract all information
278
- let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
279
- if (!(await fileManager.pathExists(agentPath))) {
280
- agentPath = path.join(installDir, "agents", `${agentId}.md`);
281
- }
328
+ const agentPath = await this.findAgentPath(agentId, installDir);
282
329
 
283
- if (await fileManager.pathExists(agentPath)) {
330
+ if (agentPath) {
284
331
  const agentContent = await fileManager.readFile(agentPath);
285
332
 
286
333
  // Extract YAML content
@@ -294,7 +341,7 @@ class IdeSetup {
294
341
  const whenToUseMatch = yaml.match(/whenToUse:\s*"(.+)"/);
295
342
  const roleDefinitionMatch = yaml.match(/roleDefinition:\s*"(.+)"/);
296
343
 
297
- const title = titleMatch ? titleMatch[1].trim() : this.getAgentTitle(agentId);
344
+ const title = titleMatch ? titleMatch[1].trim() : await this.getAgentTitle(agentId, installDir);
298
345
  const icon = iconMatch ? iconMatch[1].trim() : "🤖";
299
346
  const whenToUse = whenToUseMatch ? whenToUseMatch[1].trim() : `Use for ${title} tasks`;
300
347
  const roleDefinition = roleDefinitionMatch
@@ -306,7 +353,9 @@ class IdeSetup {
306
353
  newModesContent += ` name: '${icon} ${title}'\n`;
307
354
  newModesContent += ` roleDefinition: ${roleDefinition}\n`;
308
355
  newModesContent += ` whenToUse: ${whenToUse}\n`;
309
- newModesContent += ` customInstructions: CRITICAL Read the full YML from .bmad-core/agents/${agentId}.md start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
356
+ // Get relative path from installDir to agent file
357
+ const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
358
+ newModesContent += ` customInstructions: CRITICAL Read the full YML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
310
359
  newModesContent += ` groups:\n`;
311
360
  newModesContent += ` - read\n`;
312
361
 
@@ -351,28 +400,15 @@ class IdeSetup {
351
400
 
352
401
  await fileManager.ensureDirectory(clineRulesDir);
353
402
 
354
- // Define agent order for numeric prefixes
355
- const agentOrder = {
356
- 'bmad-master': 1,
357
- 'bmad-orchestrator': 2,
358
- 'pm': 3,
359
- 'analyst': 4,
360
- 'architect': 5,
361
- 'po': 6,
362
- 'sm': 7,
363
- 'dev': 8,
364
- 'qa': 9,
365
- 'ux-expert': 10
366
- };
403
+ // Load dynamic agent ordering from configuration
404
+ const config = await this.loadIdeAgentConfig();
405
+ const agentOrder = config['cline-order'] || {};
367
406
 
368
407
  for (const agentId of agents) {
369
- // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
370
- let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
371
- if (!(await fileManager.pathExists(agentPath))) {
372
- agentPath = path.join(installDir, "agents", `${agentId}.md`);
373
- }
408
+ // Find the agent file
409
+ const agentPath = await this.findAgentPath(agentId, installDir);
374
410
 
375
- if (await fileManager.pathExists(agentPath)) {
411
+ if (agentPath) {
376
412
  const agentContent = await fileManager.readFile(agentPath);
377
413
 
378
414
  // Get numeric prefix for ordering
@@ -381,8 +417,8 @@ class IdeSetup {
381
417
  const mdPath = path.join(clineRulesDir, `${prefix}-${agentId}.md`);
382
418
 
383
419
  // Create MD content for Cline (focused on project standards and role)
384
- let mdContent = `# ${this.getAgentTitle(agentId)} Agent\n\n`;
385
- mdContent += `This rule defines the ${this.getAgentTitle(agentId)} persona and project standards.\n\n`;
420
+ let mdContent = `# ${await this.getAgentTitle(agentId, installDir)} Agent\n\n`;
421
+ mdContent += `This rule defines the ${await this.getAgentTitle(agentId, installDir)} persona and project standards.\n\n`;
386
422
  mdContent += "## Role Definition\n\n";
387
423
  mdContent +=
388
424
  "When the user types `@" + agentId + "`, adopt this persona and follow these guidelines:\n\n";
@@ -400,9 +436,10 @@ class IdeSetup {
400
436
  mdContent += `- Always maintain consistency with project documentation in .bmad-core/\n`;
401
437
  mdContent += `- Follow the agent's specific guidelines and constraints\n`;
402
438
  mdContent += `- Update relevant project files when making changes\n`;
403
- mdContent += `- Reference the complete agent definition in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md)\n\n`;
439
+ const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
440
+ mdContent += `- Reference the complete agent definition in [${relativePath}](${relativePath})\n\n`;
404
441
  mdContent += "## Usage\n\n";
405
- mdContent += `Type \`@${agentId}\` to activate this ${this.getAgentTitle(agentId)} persona.\n`;
442
+ mdContent += `Type \`@${agentId}\` to activate this ${await this.getAgentTitle(agentId, installDir)} persona.\n`;
406
443
 
407
444
  await fileManager.writeFile(mdPath, mdContent);
408
445
  console.log(chalk.green(`✓ Created rule: ${prefix}-${agentId}.md`));
@@ -426,12 +463,9 @@ class IdeSetup {
426
463
 
427
464
  for (const agentId of agents) {
428
465
  // Find the source agent file
429
- let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
430
- if (!(await fileManager.pathExists(agentPath))) {
431
- agentPath = path.join(installDir, "agents", `${agentId}.md`);
432
- }
466
+ const agentPath = await this.findAgentPath(agentId, installDir);
433
467
 
434
- if (await fileManager.pathExists(agentPath)) {
468
+ if (agentPath) {
435
469
  const agentContent = await fileManager.readFile(agentPath);
436
470
  const contextFilePath = path.join(agentsContextDir, `${agentId}.md`);
437
471