bmad-method 6.2.1-next.24 → 6.2.1-next.26

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 (51) hide show
  1. package/package.json +1 -1
  2. package/tools/cli/commands/install.js +1 -8
  3. package/tools/cli/installers/lib/core/installer.js +1 -209
  4. package/tools/cli/installers/lib/core/manifest-generator.js +59 -107
  5. package/tools/cli/installers/lib/custom/handler.js +1 -247
  6. package/tools/cli/installers/lib/ide/_base-ide.js +0 -16
  7. package/tools/cli/installers/lib/modules/manager.js +2 -450
  8. package/tools/cli/lib/ui.js +0 -19
  9. package/src/bmm-skills/1-analysis/bmad-document-project/bmad-skill-manifest.yaml +0 -1
  10. package/src/bmm-skills/1-analysis/bmad-product-brief/bmad-skill-manifest.yaml +0 -1
  11. package/src/bmm-skills/1-analysis/research/bmad-domain-research/bmad-skill-manifest.yaml +0 -1
  12. package/src/bmm-skills/1-analysis/research/bmad-market-research/bmad-skill-manifest.yaml +0 -1
  13. package/src/bmm-skills/1-analysis/research/bmad-technical-research/bmad-skill-manifest.yaml +0 -1
  14. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/bmad-skill-manifest.yaml +0 -1
  15. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/bmad-skill-manifest.yaml +0 -1
  16. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/bmad-skill-manifest.yaml +0 -1
  17. package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/bmad-skill-manifest.yaml +0 -1
  18. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/bmad-skill-manifest.yaml +0 -1
  19. package/src/bmm-skills/3-solutioning/bmad-create-architecture/bmad-skill-manifest.yaml +0 -1
  20. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/bmad-skill-manifest.yaml +0 -1
  21. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/bmad-skill-manifest.yaml +0 -1
  22. package/src/bmm-skills/4-implementation/bmad-code-review/bmad-skill-manifest.yaml +0 -1
  23. package/src/bmm-skills/4-implementation/bmad-correct-course/bmad-skill-manifest.yaml +0 -1
  24. package/src/bmm-skills/4-implementation/bmad-create-story/bmad-skill-manifest.yaml +0 -1
  25. package/src/bmm-skills/4-implementation/bmad-dev-story/bmad-skill-manifest.yaml +0 -1
  26. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/bmad-skill-manifest.yaml +0 -1
  27. package/src/bmm-skills/4-implementation/bmad-quick-dev/bmad-skill-manifest.yaml +0 -1
  28. package/src/bmm-skills/4-implementation/bmad-retrospective/bmad-skill-manifest.yaml +0 -1
  29. package/src/bmm-skills/4-implementation/bmad-sprint-planning/bmad-skill-manifest.yaml +0 -1
  30. package/src/bmm-skills/4-implementation/bmad-sprint-status/bmad-skill-manifest.yaml +0 -1
  31. package/src/core-skills/bmad-advanced-elicitation/bmad-skill-manifest.yaml +0 -1
  32. package/src/core-skills/bmad-brainstorming/bmad-skill-manifest.yaml +0 -1
  33. package/src/core-skills/bmad-distillator/bmad-skill-manifest.yaml +0 -15
  34. package/src/core-skills/bmad-editorial-review-prose/bmad-skill-manifest.yaml +0 -1
  35. package/src/core-skills/bmad-editorial-review-structure/bmad-skill-manifest.yaml +0 -1
  36. package/src/core-skills/bmad-help/bmad-skill-manifest.yaml +0 -1
  37. package/src/core-skills/bmad-index-docs/bmad-skill-manifest.yaml +0 -1
  38. package/src/core-skills/bmad-init/bmad-skill-manifest.yaml +0 -1
  39. package/src/core-skills/bmad-party-mode/bmad-skill-manifest.yaml +0 -1
  40. package/src/core-skills/bmad-review-adversarial-general/bmad-skill-manifest.yaml +0 -1
  41. package/src/core-skills/bmad-review-edge-case-hunter/bmad-skill-manifest.yaml +0 -1
  42. package/src/core-skills/bmad-shard-doc/bmad-skill-manifest.yaml +0 -1
  43. package/tools/cli/lib/activation-builder.js +0 -165
  44. package/tools/cli/lib/agent/compiler.js +0 -516
  45. package/tools/cli/lib/agent/installer.js +0 -680
  46. package/tools/cli/lib/agent/template-engine.js +0 -152
  47. package/tools/cli/lib/agent-analyzer.js +0 -97
  48. package/tools/cli/lib/agent-party-generator.js +0 -194
  49. package/tools/cli/lib/xml-handler.js +0 -177
  50. package/tools/cli/lib/xml-to-markdown.js +0 -82
  51. package/tools/cli/lib/yaml-xml-builder.js +0 -572
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.24",
4
+ "version": "6.2.1-next.26",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -18,7 +18,7 @@ module.exports = {
18
18
  'Comma-separated list of tool/IDE IDs to configure (e.g., "claude-code,cursor"). Use "none" to skip tool configuration.',
19
19
  ],
20
20
  ['--custom-content <paths>', 'Comma-separated list of paths to custom modules/agents/workflows'],
21
- ['--action <type>', 'Action type for existing installations: install, update, quick-update, or compile-agents'],
21
+ ['--action <type>', 'Action type for existing installations: install, update, or quick-update'],
22
22
  ['--user-name <name>', 'Name for agents to use (default: system username)'],
23
23
  ['--communication-language <lang>', 'Language for agent communication (default: English)'],
24
24
  ['--document-output-language <lang>', 'Language for document output (default: English)'],
@@ -49,13 +49,6 @@ module.exports = {
49
49
  process.exit(0);
50
50
  }
51
51
 
52
- // Handle compile agents separately
53
- if (config.actionType === 'compile-agents') {
54
- const result = await installer.compileAgents(config);
55
- await prompts.log.info(`Recompiled ${result.agentCount} agents with customizations applied`);
56
- process.exit(0);
57
- }
58
-
59
52
  // Regular install/update flow
60
53
  const result = await installer.install(config);
61
54
 
@@ -6,7 +6,6 @@ const { ModuleManager } = require('../modules/manager');
6
6
  const { IdeManager } = require('../ide/manager');
7
7
  const { FileOps } = require('../../../lib/file-ops');
8
8
  const { Config } = require('../../../lib/config');
9
- const { XmlHandler } = require('../../../lib/xml-handler');
10
9
  const { DependencyResolver } = require('./dependency-resolver');
11
10
  const { ConfigCollector } = require('./config-collector');
12
11
  const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
@@ -25,7 +24,6 @@ class Installer {
25
24
  this.ideManager = new IdeManager();
26
25
  this.fileOps = new FileOps();
27
26
  this.config = new Config();
28
- this.xmlHandler = new XmlHandler();
29
27
  this.dependencyResolver = new DependencyResolver();
30
28
  this.configCollector = new ConfigCollector();
31
29
  this.ideConfigManager = new IdeConfigManager();
@@ -2114,10 +2112,6 @@ class Installer {
2114
2112
  },
2115
2113
  );
2116
2114
 
2117
- // Process agent files to build YAML agents and create customize templates
2118
- const modulePath = path.join(bmadDir, moduleName);
2119
- await this.processAgentFiles(modulePath, moduleName);
2120
-
2121
2115
  // Dependencies are already included in full module install
2122
2116
  }
2123
2117
 
@@ -2227,16 +2221,8 @@ class Installer {
2227
2221
  const sourcePath = getModulePath('core');
2228
2222
  const targetPath = path.join(bmadDir, 'core');
2229
2223
 
2230
- // Copy core files (skip .agent.yaml files like modules do)
2224
+ // Copy core files
2231
2225
  await this.copyCoreFiles(sourcePath, targetPath);
2232
-
2233
- // Compile agents using the same compiler as modules
2234
- const { ModuleManager } = require('../modules/manager');
2235
- const moduleManager = new ModuleManager();
2236
- await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
2237
-
2238
- // Process agent files to inject activation block
2239
- await this.processAgentFiles(targetPath, 'core');
2240
2226
  }
2241
2227
 
2242
2228
  /**
@@ -2254,16 +2240,6 @@ class Installer {
2254
2240
  continue;
2255
2241
  }
2256
2242
 
2257
- // Skip sidecar directories - they are handled separately during agent compilation
2258
- if (
2259
- path
2260
- .dirname(file)
2261
- .split('/')
2262
- .some((dir) => dir.toLowerCase().includes('sidecar'))
2263
- ) {
2264
- continue;
2265
- }
2266
-
2267
2243
  // Skip module.yaml at root - it's only needed at install time
2268
2244
  if (file === 'module.yaml') {
2269
2245
  continue;
@@ -2274,27 +2250,9 @@ class Installer {
2274
2250
  continue;
2275
2251
  }
2276
2252
 
2277
- // Skip .agent.yaml files - they will be compiled separately
2278
- if (file.endsWith('.agent.yaml')) {
2279
- continue;
2280
- }
2281
-
2282
2253
  const sourceFile = path.join(sourcePath, file);
2283
2254
  const targetFile = path.join(targetPath, file);
2284
2255
 
2285
- // Check if this is an agent file
2286
- if (file.startsWith('agents/') && file.endsWith('.md')) {
2287
- // Read the file to check for localskip
2288
- const content = await fs.readFile(sourceFile, 'utf8');
2289
-
2290
- // Check for localskip="true" in the agent tag
2291
- const agentMatch = content.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
2292
- if (agentMatch) {
2293
- await prompts.log.message(` Skipping web-only agent: ${path.basename(file)}`);
2294
- continue; // Skip this agent
2295
- }
2296
- }
2297
-
2298
2256
  // Copy the file with placeholder replacement
2299
2257
  await fs.ensureDir(path.dirname(targetFile));
2300
2258
  await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile);
@@ -2328,58 +2286,6 @@ class Installer {
2328
2286
  return files;
2329
2287
  }
2330
2288
 
2331
- /**
2332
- * Process agent files to build YAML agents and inject activation blocks
2333
- * @param {string} modulePath - Path to module in bmad/ installation
2334
- * @param {string} moduleName - Module name
2335
- */
2336
- async processAgentFiles(modulePath, moduleName) {
2337
- const agentsPath = path.join(modulePath, 'agents');
2338
-
2339
- // Check if agents directory exists
2340
- if (!(await fs.pathExists(agentsPath))) {
2341
- return; // No agents to process
2342
- }
2343
-
2344
- // Determine project directory (parent of bmad/ directory)
2345
- const bmadDir = path.dirname(modulePath);
2346
- const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
2347
-
2348
- // Ensure _config/agents directory exists
2349
- await fs.ensureDir(cfgAgentsDir);
2350
-
2351
- // Get all agent files
2352
- const agentFiles = await fs.readdir(agentsPath);
2353
-
2354
- for (const agentFile of agentFiles) {
2355
- // Skip .agent.yaml files - they should already be compiled by compileModuleAgents
2356
- if (agentFile.endsWith('.agent.yaml')) {
2357
- continue;
2358
- }
2359
-
2360
- // Only process .md files (already compiled from YAML)
2361
- if (!agentFile.endsWith('.md')) {
2362
- continue;
2363
- }
2364
-
2365
- const agentName = agentFile.replace('.md', '');
2366
- const mdPath = path.join(agentsPath, agentFile);
2367
- const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
2368
-
2369
- // For .md files that are already compiled, we don't need to do much
2370
- // Just ensure the customize template exists
2371
- if (!(await fs.pathExists(customizePath))) {
2372
- const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
2373
- if (await fs.pathExists(genericTemplatePath)) {
2374
- await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath);
2375
- if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
2376
- await prompts.log.message(` Created customize: ${moduleName}-${agentName}.customize.yaml`);
2377
- }
2378
- }
2379
- }
2380
- }
2381
- }
2382
-
2383
2289
  /**
2384
2290
  * Private: Update core
2385
2291
  */
@@ -2393,12 +2299,6 @@ class Installer {
2393
2299
  } else {
2394
2300
  // Selective update - preserve user modifications
2395
2301
  await this.fileOps.syncDirectory(sourcePath, targetPath);
2396
-
2397
- // Recompile agents (#1133)
2398
- const { ModuleManager } = require('../modules/manager');
2399
- const moduleManager = new ModuleManager();
2400
- await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
2401
- await this.processAgentFiles(targetPath, 'core');
2402
2302
  }
2403
2303
  }
2404
2304
 
@@ -2643,114 +2543,6 @@ class Installer {
2643
2543
  }
2644
2544
  }
2645
2545
 
2646
- /**
2647
- * Compile agents with customizations only
2648
- * @param {Object} config - Configuration with directory
2649
- * @returns {Object} Compilation result
2650
- */
2651
- async compileAgents(config) {
2652
- // Using @clack prompts
2653
- const { ModuleManager } = require('../modules/manager');
2654
- const { getSourcePath } = require('../../../lib/project-root');
2655
-
2656
- const spinner = await prompts.spinner();
2657
- spinner.start('Recompiling agents with customizations...');
2658
-
2659
- try {
2660
- const projectDir = path.resolve(config.directory);
2661
- const { bmadDir } = await this.findBmadDir(projectDir);
2662
-
2663
- // Check if bmad directory exists
2664
- if (!(await fs.pathExists(bmadDir))) {
2665
- spinner.stop('No BMAD installation found');
2666
- throw new Error(`BMAD not installed at ${bmadDir}. Use regular install for first-time setup.`);
2667
- }
2668
-
2669
- // Detect existing installation
2670
- const existingInstall = await this.detector.detect(bmadDir);
2671
- const installedModules = existingInstall.modules.map((m) => m.id);
2672
-
2673
- // Initialize module manager
2674
- const moduleManager = new ModuleManager();
2675
- moduleManager.setBmadFolderName(path.basename(bmadDir));
2676
-
2677
- let totalAgentCount = 0;
2678
-
2679
- // Get custom module sources from cache
2680
- const customModuleSources = new Map();
2681
- const cacheDir = path.join(bmadDir, '_config', 'custom');
2682
- if (await fs.pathExists(cacheDir)) {
2683
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
2684
-
2685
- for (const cachedModule of cachedModules) {
2686
- if (cachedModule.isDirectory()) {
2687
- const moduleId = cachedModule.name;
2688
- const cachedPath = path.join(cacheDir, moduleId);
2689
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
2690
-
2691
- // Check if this is actually a custom module
2692
- if (await fs.pathExists(moduleYamlPath)) {
2693
- // Check if this is an external official module - skip cache for those
2694
- const isExternal = await this.moduleManager.isExternalModule(moduleId);
2695
- if (isExternal) {
2696
- // External modules are handled via cloneExternalModule, not from cache
2697
- continue;
2698
- }
2699
- customModuleSources.set(moduleId, cachedPath);
2700
- }
2701
- }
2702
- }
2703
- }
2704
-
2705
- // Process each installed module
2706
- for (const moduleId of installedModules) {
2707
- spinner.message(`Recompiling agents in ${moduleId}...`);
2708
-
2709
- // Get source path
2710
- let sourcePath;
2711
- if (moduleId === 'core') {
2712
- sourcePath = getSourcePath('core-skills');
2713
- } else {
2714
- // First check if it's in the custom cache
2715
- if (customModuleSources.has(moduleId)) {
2716
- sourcePath = customModuleSources.get(moduleId);
2717
- } else {
2718
- sourcePath = await moduleManager.findModuleSource(moduleId);
2719
- }
2720
- }
2721
-
2722
- if (!sourcePath) {
2723
- await prompts.log.warn(`Source not found for module ${moduleId}, skipping...`);
2724
- continue;
2725
- }
2726
-
2727
- const targetPath = path.join(bmadDir, moduleId);
2728
-
2729
- // Compile agents for this module
2730
- await moduleManager.compileModuleAgents(sourcePath, targetPath, moduleId, bmadDir, this);
2731
-
2732
- // Count agents (rough estimate based on files)
2733
- const agentsPath = path.join(targetPath, 'agents');
2734
- if (await fs.pathExists(agentsPath)) {
2735
- const agentFiles = await fs.readdir(agentsPath);
2736
- const agentCount = agentFiles.filter((f) => f.endsWith('.md')).length;
2737
- totalAgentCount += agentCount;
2738
- }
2739
- }
2740
-
2741
- spinner.stop('Agent recompilation complete!');
2742
-
2743
- return {
2744
- success: true,
2745
- agentCount: totalAgentCount,
2746
- modules: installedModules,
2747
- };
2748
- } catch (error) {
2749
- spinner.error('Agent recompilation failed');
2750
- throw error;
2751
- }
2752
- }
2753
-
2754
2546
  /**
2755
2547
  * Private: Prompt for update action
2756
2548
  */
@@ -50,29 +50,6 @@ class ManifestGenerator {
50
50
  return getInstallToBmadShared(manifest, filename);
51
51
  }
52
52
 
53
- /**
54
- * Native SKILL.md entrypoints can be packaged as either skills or agents.
55
- * Both need verbatim installation for skill-format IDEs.
56
- * @param {string|null} artifactType - Manifest type resolved for SKILL.md
57
- * @returns {boolean} True when the directory should be installed verbatim
58
- */
59
- isNativeSkillDirType(artifactType) {
60
- return artifactType === 'skill' || artifactType === 'agent';
61
- }
62
-
63
- /**
64
- * Check whether a loaded bmad-skill-manifest.yaml declares a native
65
- * SKILL.md entrypoint, either as a single-entry manifest or a multi-entry map.
66
- * @param {Object|null} manifest - Loaded manifest
67
- * @returns {boolean} True when the manifest contains a native skill/agent entrypoint
68
- */
69
- hasNativeSkillManifest(manifest) {
70
- if (!manifest) return false;
71
- if (manifest.__single) return this.isNativeSkillDirType(manifest.__single.type);
72
-
73
- return Object.values(manifest).some((entry) => this.isNativeSkillDirType(entry?.type));
74
- }
75
-
76
53
  /**
77
54
  * Clean text for CSV output by normalizing whitespace.
78
55
  * Note: Quote escaping is handled by escapeCsv() at write time.
@@ -170,9 +147,9 @@ class ManifestGenerator {
170
147
 
171
148
  /**
172
149
  * Recursively walk a module directory tree, collecting native SKILL.md entrypoints.
173
- * A native entrypoint directory is one that contains both a
174
- * bmad-skill-manifest.yaml with type: skill or type: agent AND a SKILL.md file
175
- * with name/description frontmatter.
150
+ * A directory is discovered as a skill when it contains a SKILL.md file with
151
+ * valid name/description frontmatter (name must match directory name).
152
+ * Manifest YAML is loaded only when present — for install_to_bmad and agent metadata.
176
153
  * Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths).
177
154
  */
178
155
  async collectSkills() {
@@ -193,77 +170,55 @@ class ManifestGenerator {
193
170
  return;
194
171
  }
195
172
 
196
- // Check this directory for skill manifest
197
- const manifest = await this.loadSkillManifest(dir);
198
-
199
- // Determine if this directory is a native SKILL.md entrypoint
173
+ // SKILL.md with valid frontmatter is the primary discovery gate
200
174
  const skillFile = 'SKILL.md';
201
- const artifactType = this.getArtifactType(manifest, skillFile);
202
-
203
- if (this.isNativeSkillDirType(artifactType)) {
204
- const skillMdPath = path.join(dir, 'SKILL.md');
205
- const dirName = path.basename(dir);
206
-
207
- // Validate and parse SKILL.md
208
- const skillMeta = await this.parseSkillMd(skillMdPath, dir, dirName, debug);
209
-
210
- if (skillMeta) {
211
- // Build path relative from module root (points to SKILL.md — the permanent entrypoint)
212
- const relativePath = path.relative(modulePath, dir).split(path.sep).join('/');
213
- const installPath = relativePath
214
- ? `${this.bmadFolderName}/${moduleName}/${relativePath}/${skillFile}`
215
- : `${this.bmadFolderName}/${moduleName}/${skillFile}`;
216
-
217
- // Native SKILL.md entrypoints derive canonicalId from directory name.
218
- // Agent entrypoints may keep canonicalId metadata for compatibility, so
219
- // only warn for non-agent SKILL.md directories.
220
- if (manifest && manifest.__single && manifest.__single.canonicalId && artifactType !== 'agent') {
221
- console.warn(
222
- `Warning: Native entrypoint manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for SKILL.md directories (directory name is the canonical ID)`,
223
- );
224
- }
225
- const canonicalId = dirName;
226
-
227
- this.skills.push({
228
- name: skillMeta.name,
229
- description: this.cleanForCSV(skillMeta.description),
230
- module: moduleName,
231
- path: installPath,
232
- canonicalId,
233
- install_to_bmad: this.getInstallToBmad(manifest, skillFile),
234
- });
235
-
236
- // Add to files list
237
- this.files.push({
238
- type: 'skill',
239
- name: skillMeta.name,
240
- module: moduleName,
241
- path: installPath,
242
- });
243
-
244
- this.skillClaimedDirs.add(dir);
245
-
246
- if (debug) {
247
- console.log(`[DEBUG] collectSkills: claimed skill "${skillMeta.name}" as ${canonicalId} at ${dir}`);
248
- }
175
+ const skillMdPath = path.join(dir, skillFile);
176
+ const dirName = path.basename(dir);
177
+
178
+ const skillMeta = await this.parseSkillMd(skillMdPath, dir, dirName, debug);
179
+
180
+ if (skillMeta) {
181
+ // Load manifest when present (for install_to_bmad and agent metadata)
182
+ const manifest = await this.loadSkillManifest(dir);
183
+ const artifactType = this.getArtifactType(manifest, skillFile);
184
+
185
+ // Build path relative from module root (points to SKILL.md — the permanent entrypoint)
186
+ const relativePath = path.relative(modulePath, dir).split(path.sep).join('/');
187
+ const installPath = relativePath
188
+ ? `${this.bmadFolderName}/${moduleName}/${relativePath}/${skillFile}`
189
+ : `${this.bmadFolderName}/${moduleName}/${skillFile}`;
190
+
191
+ // Native SKILL.md entrypoints derive canonicalId from directory name.
192
+ // Agent entrypoints may keep canonicalId metadata for compatibility, so
193
+ // only warn for non-agent SKILL.md directories.
194
+ if (manifest && manifest.__single && manifest.__single.canonicalId && artifactType !== 'agent') {
195
+ console.warn(
196
+ `Warning: Native entrypoint manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for SKILL.md directories (directory name is the canonical ID)`,
197
+ );
249
198
  }
250
- }
199
+ const canonicalId = dirName;
251
200
 
252
- // Warn if manifest says this is a native entrypoint but the directory was not claimed
253
- if (manifest && !this.skillClaimedDirs.has(dir)) {
254
- let hasNativeSkillType = false;
255
- if (manifest.__single) {
256
- hasNativeSkillType = this.isNativeSkillDirType(manifest.__single.type);
257
- } else {
258
- for (const key of Object.keys(manifest)) {
259
- if (this.isNativeSkillDirType(manifest[key]?.type)) {
260
- hasNativeSkillType = true;
261
- break;
262
- }
263
- }
264
- }
265
- if (hasNativeSkillType && debug) {
266
- console.log(`[DEBUG] collectSkills: dir has native SKILL.md manifest but failed validation: ${dir}`);
201
+ this.skills.push({
202
+ name: skillMeta.name,
203
+ description: this.cleanForCSV(skillMeta.description),
204
+ module: moduleName,
205
+ path: installPath,
206
+ canonicalId,
207
+ install_to_bmad: this.getInstallToBmad(manifest, skillFile),
208
+ });
209
+
210
+ // Add to files list
211
+ this.files.push({
212
+ type: 'skill',
213
+ name: skillMeta.name,
214
+ module: moduleName,
215
+ path: installPath,
216
+ });
217
+
218
+ this.skillClaimedDirs.add(dir);
219
+
220
+ if (debug) {
221
+ console.log(`[DEBUG] collectSkills: claimed skill "${skillMeta.name}" as ${canonicalId} at ${dir}`);
267
222
  }
268
223
  }
269
224
 
@@ -515,7 +470,7 @@ class ManifestGenerator {
515
470
 
516
471
  /**
517
472
  * Get agents from a directory recursively
518
- * Only includes compiled .md files (not .agent.yaml source files)
473
+ * Only includes .md files with agent content
519
474
  */
520
475
  async getAgentsFromDir(dirPath, moduleName, relativePath = '') {
521
476
  // Skip directories claimed by collectSkills
@@ -572,7 +527,7 @@ class ManifestGenerator {
572
527
  const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
573
528
  const subDirAgents = await this.getAgentsFromDir(fullPath, moduleName, newRelativePath);
574
529
  agents.push(...subDirAgents);
575
- } else if (entry.name.endsWith('.md') && !entry.name.endsWith('.agent.yaml') && entry.name.toLowerCase() !== 'readme.md') {
530
+ } else if (entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
576
531
  const content = await fs.readFile(fullPath, 'utf8');
577
532
 
578
533
  // Skip files that don't contain <agent> tag (e.g., README files)
@@ -1384,11 +1339,10 @@ class ManifestGenerator {
1384
1339
  const hasTasks = await fs.pathExists(path.join(modulePath, 'tasks'));
1385
1340
  const hasTools = await fs.pathExists(path.join(modulePath, 'tools'));
1386
1341
 
1387
- // Check for native-entrypoint-only modules: recursive scan for
1388
- // bmad-skill-manifest.yaml with type: skill or type: agent
1342
+ // Check for native-entrypoint-only modules: recursive scan for SKILL.md
1389
1343
  let hasSkills = false;
1390
1344
  if (!hasAgents && !hasWorkflows && !hasTasks && !hasTools) {
1391
- hasSkills = await this._hasSkillManifestRecursive(modulePath);
1345
+ hasSkills = await this._hasSkillMdRecursive(modulePath);
1392
1346
  }
1393
1347
 
1394
1348
  // If it has any of these directories or skill manifests, it's likely a module
@@ -1404,13 +1358,12 @@ class ManifestGenerator {
1404
1358
  }
1405
1359
 
1406
1360
  /**
1407
- * Recursively check if a directory tree contains a bmad-skill-manifest.yaml that
1408
- * declares a native SKILL.md entrypoint (type: skill or type: agent).
1361
+ * Recursively check if a directory tree contains a SKILL.md file.
1409
1362
  * Skips directories starting with . or _.
1410
1363
  * @param {string} dir - Directory to search
1411
- * @returns {boolean} True if a skill manifest is found
1364
+ * @returns {boolean} True if a SKILL.md is found
1412
1365
  */
1413
- async _hasSkillManifestRecursive(dir) {
1366
+ async _hasSkillMdRecursive(dir) {
1414
1367
  let entries;
1415
1368
  try {
1416
1369
  entries = await fs.readdir(dir, { withFileTypes: true });
@@ -1418,15 +1371,14 @@ class ManifestGenerator {
1418
1371
  return false;
1419
1372
  }
1420
1373
 
1421
- // Check for manifest in this directory
1422
- const manifest = await this.loadSkillManifest(dir);
1423
- if (this.hasNativeSkillManifest(manifest)) return true;
1374
+ // Check for SKILL.md in this directory
1375
+ if (entries.some((e) => !e.isDirectory() && e.name === 'SKILL.md')) return true;
1424
1376
 
1425
1377
  // Recurse into subdirectories
1426
1378
  for (const entry of entries) {
1427
1379
  if (!entry.isDirectory()) continue;
1428
1380
  if (entry.name.startsWith('.') || entry.name.startsWith('_')) continue;
1429
- if (await this._hasSkillManifestRecursive(path.join(dir, entry.name))) return true;
1381
+ if (await this._hasSkillMdRecursive(path.join(dir, entry.name))) return true;
1430
1382
  }
1431
1383
 
1432
1384
  return false;