bmad-method 6.2.1-next.36 → 6.2.1-next.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.2.1-next.36",
4
+ "version": "6.2.1-next.37",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-analyst
3
3
  displayName: Mary
4
4
  title: Business Analyst
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-tech-writer
3
3
  displayName: Paige
4
4
  title: Technical Writer
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-pm
3
3
  displayName: John
4
4
  title: Product Manager
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-ux-designer
3
3
  displayName: Sally
4
4
  title: UX Designer
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-architect
3
3
  displayName: Winston
4
4
  title: Architect
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-dev
3
3
  displayName: Amelia
4
4
  title: Developer Agent
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-qa
3
3
  displayName: Quinn
4
4
  title: QA Engineer
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-quick-flow-solo-dev
3
3
  displayName: Barry
4
4
  title: Quick Flow Solo Dev
@@ -1,4 +1,4 @@
1
- type: skill
1
+ type: agent
2
2
  name: bmad-agent-sm
3
3
  displayName: Bob
4
4
  title: Scrum Master
@@ -5,7 +5,7 @@
5
5
  * This file ensures proper execution when run via npx from GitHub or npm registry
6
6
  */
7
7
 
8
- const { execSync } = require('node:child_process');
8
+ const { execFileSync } = require('node:child_process');
9
9
  const path = require('node:path');
10
10
  const fs = require('node:fs');
11
11
 
@@ -25,7 +25,7 @@ if (isNpxExecution) {
25
25
 
26
26
  try {
27
27
  // Execute CLI from user's working directory (process.cwd()), not npm cache
28
- execSync(`node "${bmadCliPath}" ${args.join(' ')}`, {
28
+ execFileSync('node', [bmadCliPath, ...args], {
29
29
  stdio: 'inherit',
30
30
  cwd: process.cwd(), // This preserves the user's working directory
31
31
  });
@@ -268,153 +268,103 @@ class ManifestGenerator {
268
268
  }
269
269
 
270
270
  /**
271
- * Collect all agents from core and selected modules
272
- * Scans the INSTALLED bmad directory, not the source
271
+ * Collect all agents from selected modules by walking their directory trees.
273
272
  */
274
273
  async collectAgents(selectedModules) {
275
274
  this.agents = [];
275
+ const debug = process.env.BMAD_DEBUG_MANIFEST === 'true';
276
276
 
277
- // Use updatedModules which already includes deduplicated 'core' + selectedModules
277
+ // Walk each module's full directory tree looking for type:agent manifests
278
278
  for (const moduleName of this.updatedModules) {
279
- const agentsPath = path.join(this.bmadDir, moduleName, 'agents');
279
+ const modulePath = path.join(this.bmadDir, moduleName);
280
+ if (!(await fs.pathExists(modulePath))) continue;
280
281
 
281
- if (await fs.pathExists(agentsPath)) {
282
- const moduleAgents = await this.getAgentsFromDir(agentsPath, moduleName);
283
- this.agents.push(...moduleAgents);
284
- }
282
+ const moduleAgents = await this.getAgentsFromDirRecursive(modulePath, moduleName, '', debug);
283
+ this.agents.push(...moduleAgents);
285
284
  }
286
285
 
287
286
  // Get standalone agents from bmad/agents/ directory
288
287
  const standaloneAgentsDir = path.join(this.bmadDir, 'agents');
289
288
  if (await fs.pathExists(standaloneAgentsDir)) {
290
- const agentDirs = await fs.readdir(standaloneAgentsDir, { withFileTypes: true });
291
-
292
- for (const agentDir of agentDirs) {
293
- if (!agentDir.isDirectory()) continue;
289
+ const standaloneAgents = await this.getAgentsFromDirRecursive(standaloneAgentsDir, 'standalone', '', debug);
290
+ this.agents.push(...standaloneAgents);
291
+ }
294
292
 
295
- const agentDirPath = path.join(standaloneAgentsDir, agentDir.name);
296
- const standaloneAgents = await this.getAgentsFromDir(agentDirPath, 'standalone');
297
- this.agents.push(...standaloneAgents);
298
- }
293
+ if (debug) {
294
+ console.log(`[DEBUG] collectAgents: total agents found: ${this.agents.length}`);
299
295
  }
300
296
  }
301
297
 
302
298
  /**
303
- * Get agents from a directory recursively
304
- * Only includes .md files with agent content
299
+ * Recursively walk a directory tree collecting agents.
300
+ * Discovers agents via directory with bmad-skill-manifest.yaml containing type: agent
301
+ *
302
+ * @param {string} dirPath - Current directory being scanned
303
+ * @param {string} moduleName - Module this directory belongs to
304
+ * @param {string} relativePath - Path relative to the module root (for install path construction)
305
+ * @param {boolean} debug - Emit debug messages
305
306
  */
306
- async getAgentsFromDir(dirPath, moduleName, relativePath = '') {
307
- // Skip directories claimed by collectSkills
308
- if (this.skillClaimedDirs && this.skillClaimedDirs.has(dirPath)) return [];
307
+ async getAgentsFromDirRecursive(dirPath, moduleName, relativePath = '', debug = false) {
309
308
  const agents = [];
310
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
311
- // Load skill manifest for this directory (if present)
312
- const skillManifest = await this.loadSkillManifest(dirPath);
309
+ let entries;
310
+ try {
311
+ entries = await fs.readdir(dirPath, { withFileTypes: true });
312
+ } catch {
313
+ return agents;
314
+ }
313
315
 
314
316
  for (const entry of entries) {
315
- const fullPath = path.join(dirPath, entry.name);
316
-
317
- if (entry.isDirectory()) {
318
- // Check for new-format agent: bmad-skill-manifest.yaml with type: agent
319
- // Note: type:agent dirs may also be claimed by collectSkills for IDE installation,
320
- // but we still need to process them here for agent-manifest.csv
321
- const dirManifest = await this.loadSkillManifest(fullPath);
322
- if (dirManifest && dirManifest.__single && dirManifest.__single.type === 'agent') {
323
- const m = dirManifest.__single;
324
- const dirRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
325
- const installPath =
326
- moduleName === 'core'
327
- ? `${this.bmadFolderName}/core/agents/${dirRelativePath}`
328
- : `${this.bmadFolderName}/${moduleName}/agents/${dirRelativePath}`;
329
-
330
- agents.push({
331
- name: m.name || entry.name,
332
- displayName: m.displayName || m.name || entry.name,
333
- title: m.title || '',
334
- icon: m.icon || '',
335
- capabilities: m.capabilities ? this.cleanForCSV(m.capabilities) : '',
336
- role: m.role ? this.cleanForCSV(m.role) : '',
337
- identity: m.identity ? this.cleanForCSV(m.identity) : '',
338
- communicationStyle: m.communicationStyle ? this.cleanForCSV(m.communicationStyle) : '',
339
- principles: m.principles ? this.cleanForCSV(m.principles) : '',
340
- module: m.module || moduleName,
341
- path: installPath,
342
- canonicalId: m.canonicalId || '',
343
- });
344
-
345
- this.files.push({
346
- type: 'agent',
347
- name: m.name || entry.name,
348
- module: moduleName,
349
- path: installPath,
350
- });
351
- continue;
352
- }
353
-
354
- // Skip directories claimed by collectSkills (non-agent type skills)
355
- if (this.skillClaimedDirs && this.skillClaimedDirs.has(fullPath)) continue;
356
-
357
- // Recurse into subdirectories
358
- const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
359
- const subDirAgents = await this.getAgentsFromDir(fullPath, moduleName, newRelativePath);
360
- agents.push(...subDirAgents);
361
- } else if (entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
362
- const content = await fs.readFile(fullPath, 'utf8');
363
-
364
- // Skip files that don't contain <agent> tag (e.g., README files)
365
- if (!content.includes('<agent')) {
366
- continue;
367
- }
368
-
369
- // Skip web-only agents
370
- if (content.includes('localskip="true"')) {
371
- continue;
372
- }
373
-
374
- // Extract agent metadata from the XML structure
375
- const nameMatch = content.match(/name="([^"]+)"/);
376
- const titleMatch = content.match(/title="([^"]+)"/);
377
- const iconMatch = content.match(/icon="([^"]+)"/);
378
- const capabilitiesMatch = content.match(/capabilities="([^"]+)"/);
379
-
380
- // Extract persona fields
381
- const roleMatch = content.match(/<role>([^<]+)<\/role>/);
382
- const identityMatch = content.match(/<identity>([\s\S]*?)<\/identity>/);
383
- const styleMatch = content.match(/<communication_style>([\s\S]*?)<\/communication_style>/);
384
- const principlesMatch = content.match(/<principles>([\s\S]*?)<\/principles>/);
317
+ if (!entry.isDirectory()) continue;
318
+ if (entry.name.startsWith('.') || entry.name.startsWith('_')) continue;
385
319
 
386
- // Build relative path for installation
387
- const fileRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
388
- const installPath =
389
- moduleName === 'core'
390
- ? `${this.bmadFolderName}/core/agents/${fileRelativePath}`
391
- : `${this.bmadFolderName}/${moduleName}/agents/${fileRelativePath}`;
320
+ const fullPath = path.join(dirPath, entry.name);
392
321
 
393
- const agentName = entry.name.replace('.md', '');
322
+ // Check for type:agent manifest BEFORE checking skillClaimedDirs —
323
+ // agent dirs may be claimed by collectSkills for IDE installation,
324
+ // but we still need them in agent-manifest.csv.
325
+ const dirManifest = await this.loadSkillManifest(fullPath);
326
+ if (dirManifest && dirManifest.__single && dirManifest.__single.type === 'agent') {
327
+ const m = dirManifest.__single;
328
+ const dirRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
329
+ const agentModule = m.module || moduleName;
330
+ const installPath = `${this.bmadFolderName}/${agentModule}/${dirRelativePath}`;
394
331
 
395
332
  agents.push({
396
- name: agentName,
397
- displayName: nameMatch ? nameMatch[1] : agentName,
398
- title: titleMatch ? titleMatch[1] : '',
399
- icon: iconMatch ? iconMatch[1] : '',
400
- capabilities: capabilitiesMatch ? this.cleanForCSV(capabilitiesMatch[1]) : '',
401
- role: roleMatch ? this.cleanForCSV(roleMatch[1]) : '',
402
- identity: identityMatch ? this.cleanForCSV(identityMatch[1]) : '',
403
- communicationStyle: styleMatch ? this.cleanForCSV(styleMatch[1]) : '',
404
- principles: principlesMatch ? this.cleanForCSV(principlesMatch[1]) : '',
405
- module: moduleName,
333
+ name: m.name || entry.name,
334
+ displayName: m.displayName || m.name || entry.name,
335
+ title: m.title || '',
336
+ icon: m.icon || '',
337
+ capabilities: m.capabilities ? this.cleanForCSV(m.capabilities) : '',
338
+ role: m.role ? this.cleanForCSV(m.role) : '',
339
+ identity: m.identity ? this.cleanForCSV(m.identity) : '',
340
+ communicationStyle: m.communicationStyle ? this.cleanForCSV(m.communicationStyle) : '',
341
+ principles: m.principles ? this.cleanForCSV(m.principles) : '',
342
+ module: agentModule,
406
343
  path: installPath,
407
- canonicalId: this.getCanonicalId(skillManifest, entry.name),
344
+ canonicalId: m.canonicalId || '',
408
345
  });
409
346
 
410
- // Add to files list
411
347
  this.files.push({
412
348
  type: 'agent',
413
- name: agentName,
414
- module: moduleName,
349
+ name: m.name || entry.name,
350
+ module: agentModule,
415
351
  path: installPath,
416
352
  });
353
+
354
+ if (debug) {
355
+ console.log(`[DEBUG] collectAgents: found type:agent "${m.name || entry.name}" at ${fullPath}`);
356
+ }
357
+ continue;
417
358
  }
359
+
360
+ // Skip directories claimed by collectSkills (non-agent type skills) —
361
+ // avoids recursing into skill trees that can't contain agents.
362
+ if (this.skillClaimedDirs && this.skillClaimedDirs.has(fullPath)) continue;
363
+
364
+ // Recurse into subdirectories
365
+ const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
366
+ const subDirAgents = await this.getAgentsFromDirRecursive(fullPath, moduleName, newRelativePath, debug);
367
+ agents.push(...subDirAgents);
418
368
  }
419
369
 
420
370
  return agents;
@@ -5,6 +5,33 @@ const { loadSkillManifest, getCanonicalId } = require('./skill-manifest');
5
5
  /**
6
6
  * Helpers for gathering BMAD agents/tasks from the installed tree.
7
7
  * Shared by installers that need Claude-style exports.
8
+ *
9
+ * TODO: Dead code cleanup — compiled XML agents are retired.
10
+ *
11
+ * All agents now use the SKILL.md directory format with bmad-skill-manifest.yaml
12
+ * (type: agent). The legacy pipeline below only discovers compiled .md files
13
+ * containing <agent> XML tags, which no longer exist. The following are dead:
14
+ *
15
+ * - getAgentsFromBmad() — scans {module}/agents/ for .md files with <agent> tags
16
+ * - getAgentsFromDir() — recursive helper for the above
17
+ * - AgentCommandGenerator — (agent-command-generator.js) generates launcher .md files
18
+ * that tell the LLM to load a compiled agent .md file
19
+ * - agent-command-template.md — (templates/) the launcher template with hardcoded
20
+ * {module}/agents/{{path}} reference
21
+ *
22
+ * Agent metadata for agent-manifest.csv is now handled entirely by
23
+ * ManifestGenerator.getAgentsFromDirRecursive() in manifest-generator.js,
24
+ * which walks the full module tree and finds type:agent directories.
25
+ *
26
+ * IDE installation of agents is handled by the native skill pipeline —
27
+ * each agent's SKILL.md directory is installed directly to the IDE's
28
+ * skills path, so no launcher intermediary is needed.
29
+ *
30
+ * Cleanup: remove getAgentsFromBmad, getAgentsFromDir, their exports,
31
+ * AgentCommandGenerator, agent-command-template.md, and all call sites
32
+ * in IDE installers that invoke collectAgentArtifacts / writeAgentLaunchers /
33
+ * writeColonArtifacts / writeDashArtifacts.
34
+ * getTasksFromBmad and getTasksFromDir may still be live — verify before removing.
8
35
  */
9
36
  async function getAgentsFromBmad(bmadDir, selectedModules = []) {
10
37
  const agents = [];