bmad-method 6.2.1-next.15 → 6.2.1-next.17

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.15",
4
+ "version": "6.2.1-next.17",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -82,11 +82,11 @@ class DependencyResolver {
82
82
  // Check if this is a source directory (has 'src' subdirectory)
83
83
  const srcDir = path.join(bmadDir, 'src');
84
84
  if (await fs.pathExists(srcDir)) {
85
- // Source directory structure: src/core or src/bmm
85
+ // Source directory structure: src/core-skills or src/bmm-skills
86
86
  if (module === 'core') {
87
- moduleDir = path.join(srcDir, 'core');
87
+ moduleDir = path.join(srcDir, 'core-skills');
88
88
  } else if (module === 'bmm') {
89
- moduleDir = path.join(srcDir, 'bmm');
89
+ moduleDir = path.join(srcDir, 'bmm-skills');
90
90
  }
91
91
  }
92
92
 
@@ -401,8 +401,8 @@ class DependencyResolver {
401
401
  const bmadPath = dep.dependency.replace(/^bmad\//, '');
402
402
 
403
403
  // Try to resolve as if it's in src structure
404
- // bmad/core/tasks/foo.md -> src/core/tasks/foo.md
405
- // bmad/bmm/tasks/bar.md -> src/bmm/tasks/bar.md (bmm is directly under src/)
404
+ // bmad/core/tasks/foo.md -> src/core-skills/tasks/foo.md
405
+ // bmad/bmm/tasks/bar.md -> src/bmm-skills/tasks/bar.md (bmm is directly under src/)
406
406
  // bmad/cis/agents/bar.md -> src/modules/cis/agents/bar.md
407
407
 
408
408
  if (bmadPath.startsWith('core/')) {
@@ -584,11 +584,11 @@ class DependencyResolver {
584
584
  const relative = path.relative(bmadDir, filePath);
585
585
  const parts = relative.split(path.sep);
586
586
 
587
- // Handle source directory structure (src/core, src/bmm, or src/modules/xxx)
587
+ // Handle source directory structure (src/core-skills, src/bmm-skills, or src/modules/xxx)
588
588
  if (parts[0] === 'src') {
589
- if (parts[1] === 'core') {
589
+ if (parts[1] === 'core-skills') {
590
590
  return 'core';
591
- } else if (parts[1] === 'bmm') {
591
+ } else if (parts[1] === 'bmm-skills') {
592
592
  return 'bmm';
593
593
  } else if (parts[1] === 'modules' && parts.length > 2) {
594
594
  return parts[2];
@@ -631,11 +631,11 @@ class DependencyResolver {
631
631
  let moduleBase;
632
632
 
633
633
  // Check if file is in source directory structure
634
- if (file.includes('/src/core/') || file.includes('/src/bmm/')) {
634
+ if (file.includes('/src/core-skills/') || file.includes('/src/bmm-skills/')) {
635
635
  if (module === 'core') {
636
- moduleBase = path.join(bmadDir, 'src', 'core');
636
+ moduleBase = path.join(bmadDir, 'src', 'core-skills');
637
637
  } else if (module === 'bmm') {
638
- moduleBase = path.join(bmadDir, 'src', 'bmm');
638
+ moduleBase = path.join(bmadDir, 'src', 'bmm-skills');
639
639
  }
640
640
  } else {
641
641
  moduleBase = module === 'core' ? path.join(bmadDir, 'core') : path.join(bmadDir, 'modules', module);
@@ -1789,8 +1789,8 @@ class Installer {
1789
1789
  .filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs' && entry.name !== '_memory')
1790
1790
  .map((entry) => entry.name);
1791
1791
 
1792
- // Add core module to scan (it's installed at root level as _config, but we check src/core)
1793
- const coreModulePath = getSourcePath('core');
1792
+ // Add core module to scan (it's installed at root level as _config, but we check src/core-skills)
1793
+ const coreModulePath = getSourcePath('core-skills');
1794
1794
  const modulePaths = new Map();
1795
1795
 
1796
1796
  // Map all module source paths
@@ -2709,7 +2709,7 @@ class Installer {
2709
2709
  // Get source path
2710
2710
  let sourcePath;
2711
2711
  if (moduleId === 'core') {
2712
- sourcePath = getSourcePath('core');
2712
+ sourcePath = getSourcePath('core-skills');
2713
2713
  } else {
2714
2714
  // First check if it's in the custom cache
2715
2715
  if (customModuleSources.has(moduleId)) {
@@ -50,6 +50,29 @@ 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
+
53
76
  /**
54
77
  * Clean text for CSV output by normalizing whitespace.
55
78
  * Note: Quote escaping is handled by escapeCsv() at write time.
@@ -146,9 +169,10 @@ class ManifestGenerator {
146
169
  }
147
170
 
148
171
  /**
149
- * Recursively walk a module directory tree, collecting skill directories.
150
- * A skill directory is one that contains both a bmad-skill-manifest.yaml with
151
- * type: skill AND a SKILL.md file with name/description frontmatter.
172
+ * 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.
152
176
  * Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths).
153
177
  */
154
178
  async collectSkills() {
@@ -172,11 +196,11 @@ class ManifestGenerator {
172
196
  // Check this directory for skill manifest
173
197
  const manifest = await this.loadSkillManifest(dir);
174
198
 
175
- // Determine if this directory is a skill (type: skill in manifest)
199
+ // Determine if this directory is a native SKILL.md entrypoint
176
200
  const skillFile = 'SKILL.md';
177
201
  const artifactType = this.getArtifactType(manifest, skillFile);
178
202
 
179
- if (artifactType === 'skill' || artifactType === 'agent') {
203
+ if (this.isNativeSkillDirType(artifactType)) {
180
204
  const skillMdPath = path.join(dir, 'SKILL.md');
181
205
  const dirName = path.basename(dir);
182
206
 
@@ -190,11 +214,12 @@ class ManifestGenerator {
190
214
  ? `${this.bmadFolderName}/${moduleName}/${relativePath}/${skillFile}`
191
215
  : `${this.bmadFolderName}/${moduleName}/${skillFile}`;
192
216
 
193
- // Skills derive canonicalId from directory name — never from manifest
194
- // (agent-type skills legitimately use canonicalId for agent-manifest mapping, so skip warning)
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.
195
220
  if (manifest && manifest.__single && manifest.__single.canonicalId && artifactType !== 'agent') {
196
221
  console.warn(
197
- `Warning: Skill manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for skills (directory name is the canonical ID)`,
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)`,
198
223
  );
199
224
  }
200
225
  const canonicalId = dirName;
@@ -224,21 +249,21 @@ class ManifestGenerator {
224
249
  }
225
250
  }
226
251
 
227
- // Warn if manifest says type:skill but directory was not claimed
252
+ // Warn if manifest says this is a native entrypoint but the directory was not claimed
228
253
  if (manifest && !this.skillClaimedDirs.has(dir)) {
229
- let hasSkillType = false;
254
+ let hasNativeSkillType = false;
230
255
  if (manifest.__single) {
231
- hasSkillType = manifest.__single.type === 'skill' || manifest.__single.type === 'agent';
256
+ hasNativeSkillType = this.isNativeSkillDirType(manifest.__single.type);
232
257
  } else {
233
258
  for (const key of Object.keys(manifest)) {
234
- if (manifest[key]?.type === 'skill' || manifest[key]?.type === 'agent') {
235
- hasSkillType = true;
259
+ if (this.isNativeSkillDirType(manifest[key]?.type)) {
260
+ hasNativeSkillType = true;
236
261
  break;
237
262
  }
238
263
  }
239
264
  }
240
- if (hasSkillType && debug) {
241
- console.log(`[DEBUG] collectSkills: dir has type:skill manifest but failed validation: ${dir}`);
265
+ if (hasNativeSkillType && debug) {
266
+ console.log(`[DEBUG] collectSkills: dir has native SKILL.md manifest but failed validation: ${dir}`);
242
267
  }
243
268
  }
244
269
 
@@ -1359,7 +1384,8 @@ class ManifestGenerator {
1359
1384
  const hasTasks = await fs.pathExists(path.join(modulePath, 'tasks'));
1360
1385
  const hasTools = await fs.pathExists(path.join(modulePath, 'tools'));
1361
1386
 
1362
- // Check for skill-only modules: recursive scan for bmad-skill-manifest.yaml with type: skill
1387
+ // Check for native-entrypoint-only modules: recursive scan for
1388
+ // bmad-skill-manifest.yaml with type: skill or type: agent
1363
1389
  let hasSkills = false;
1364
1390
  if (!hasAgents && !hasWorkflows && !hasTasks && !hasTools) {
1365
1391
  hasSkills = await this._hasSkillManifestRecursive(modulePath);
@@ -1378,7 +1404,8 @@ class ManifestGenerator {
1378
1404
  }
1379
1405
 
1380
1406
  /**
1381
- * Recursively check if a directory tree contains a bmad-skill-manifest.yaml with type: skill.
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).
1382
1409
  * Skips directories starting with . or _.
1383
1410
  * @param {string} dir - Directory to search
1384
1411
  * @returns {boolean} True if a skill manifest is found
@@ -1393,10 +1420,7 @@ class ManifestGenerator {
1393
1420
 
1394
1421
  // Check for manifest in this directory
1395
1422
  const manifest = await this.loadSkillManifest(dir);
1396
- if (manifest) {
1397
- const type = this.getArtifactType(manifest, 'workflow.md');
1398
- if (type === 'skill') return true;
1399
- }
1423
+ if (this.hasNativeSkillManifest(manifest)) return true;
1400
1424
 
1401
1425
  // Recurse into subdirectories
1402
1426
  for (const entry of entries) {
@@ -764,10 +764,10 @@ class Manifest {
764
764
  const configs = {};
765
765
 
766
766
  for (const moduleName of modules) {
767
- // Handle core module differently - it's in src/core not src/modules/core
767
+ // Handle core module differently - it's in src/core-skills not src/modules/core
768
768
  const configPath =
769
769
  moduleName === 'core'
770
- ? path.join(process.cwd(), 'src', 'core', 'config.yaml')
770
+ ? path.join(process.cwd(), 'src', 'core-skills', 'config.yaml')
771
771
  : path.join(process.cwd(), 'src', 'modules', moduleName, 'config.yaml');
772
772
 
773
773
  try {
@@ -630,7 +630,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
630
630
  }
631
631
 
632
632
  /**
633
- * Install verbatim skill directories (type: skill entries from skill-manifest.csv).
633
+ * Install verbatim native SKILL.md directories from skill-manifest.csv.
634
634
  * Copies the entire source directory as-is into the IDE skill directory.
635
635
  * The source SKILL.md is used directly — no frontmatter transformation or file generation.
636
636
  * @param {string} projectDir - Project directory
@@ -146,13 +146,13 @@ When running any workflow:
146
146
  transformWorkflowPath(workflowPath) {
147
147
  let transformed = workflowPath;
148
148
 
149
- if (workflowPath.includes('/src/bmm/')) {
150
- const match = workflowPath.match(/\/src\/bmm\/(.+)/);
149
+ if (workflowPath.includes('/src/bmm-skills/')) {
150
+ const match = workflowPath.match(/\/src\/bmm-skills\/(.+)/);
151
151
  if (match) {
152
152
  transformed = `{project-root}/${this.bmadFolderName}/bmm/${match[1]}`;
153
153
  }
154
- } else if (workflowPath.includes('/src/core/')) {
155
- const match = workflowPath.match(/\/src\/core\/(.+)/);
154
+ } else if (workflowPath.includes('/src/core-skills/')) {
155
+ const match = workflowPath.match(/\/src\/core-skills\/(.+)/);
156
156
  if (match) {
157
157
  transformed = `{project-root}/${this.bmadFolderName}/core/${match[1]}`;
158
158
  }
@@ -187,7 +187,7 @@ class ModuleManager {
187
187
 
188
188
  /**
189
189
  * List all available modules (excluding core which is always installed)
190
- * bmm is the only built-in module, directly under src/bmm
190
+ * bmm is the only built-in module, directly under src/bmm-skills
191
191
  * All other modules come from external-official-modules.yaml
192
192
  * @returns {Object} Object with modules array and customModules array
193
193
  */
@@ -195,10 +195,10 @@ class ModuleManager {
195
195
  const modules = [];
196
196
  const customModules = [];
197
197
 
198
- // Add built-in bmm module (directly under src/bmm)
199
- const bmmPath = getSourcePath('bmm');
198
+ // Add built-in bmm module (directly under src/bmm-skills)
199
+ const bmmPath = getSourcePath('bmm-skills');
200
200
  if (await fs.pathExists(bmmPath)) {
201
- const bmmInfo = await this.getModuleInfo(bmmPath, 'bmm', 'src/bmm');
201
+ const bmmInfo = await this.getModuleInfo(bmmPath, 'bmm', 'src/bmm-skills');
202
202
  if (bmmInfo) {
203
203
  modules.push(bmmInfo);
204
204
  }
@@ -251,7 +251,8 @@ class ModuleManager {
251
251
  }
252
252
 
253
253
  // Mark as custom if it's using custom.yaml OR if it's outside src/bmm or src/core
254
- const isCustomSource = sourceDescription !== 'src/bmm' && sourceDescription !== 'src/core' && sourceDescription !== 'src/modules';
254
+ const isCustomSource =
255
+ sourceDescription !== 'src/bmm-skills' && sourceDescription !== 'src/core-skills' && sourceDescription !== 'src/modules';
255
256
  const moduleInfo = {
256
257
  id: defaultName,
257
258
  path: modulePath,
@@ -300,9 +301,9 @@ class ModuleManager {
300
301
  return this.customModulePaths.get(moduleCode);
301
302
  }
302
303
 
303
- // Check for built-in bmm module (directly under src/bmm)
304
+ // Check for built-in bmm module (directly under src/bmm-skills)
304
305
  if (moduleCode === 'bmm') {
305
- const bmmPath = getSourcePath('bmm');
306
+ const bmmPath = getSourcePath('bmm-skills');
306
307
  if (await fs.pathExists(bmmPath)) {
307
308
  return bmmPath;
308
309
  }
@@ -1141,10 +1142,10 @@ class ModuleManager {
1141
1142
  const projectRoot = path.dirname(bmadDir);
1142
1143
  const emptyResult = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
1143
1144
 
1144
- // Special handling for core module - it's in src/core not src/modules
1145
+ // Special handling for core module - it's in src/core-skills not src/modules
1145
1146
  let sourcePath;
1146
1147
  if (moduleName === 'core') {
1147
- sourcePath = getSourcePath('core');
1148
+ sourcePath = getSourcePath('core-skills');
1148
1149
  } else {
1149
1150
  sourcePath = await this.findModuleSource(moduleName, { silent: true });
1150
1151
  if (!sourcePath) {
@@ -16,7 +16,7 @@ function findProjectRoot(startPath = __dirname) {
16
16
  try {
17
17
  const pkg = fs.readJsonSync(packagePath);
18
18
  // Check if this is the BMAD project
19
- if (pkg.name === 'bmad-method' || fs.existsSync(path.join(currentPath, 'src', 'core'))) {
19
+ if (pkg.name === 'bmad-method' || fs.existsSync(path.join(currentPath, 'src', 'core-skills'))) {
20
20
  return currentPath;
21
21
  }
22
22
  } catch {
@@ -24,8 +24,8 @@ function findProjectRoot(startPath = __dirname) {
24
24
  }
25
25
  }
26
26
 
27
- // Also check for src/core as a marker
28
- if (fs.existsSync(path.join(currentPath, 'src', 'core', 'agents'))) {
27
+ // Also check for src/core-skills as a marker
28
+ if (fs.existsSync(path.join(currentPath, 'src', 'core-skills', 'agents'))) {
29
29
  return currentPath;
30
30
  }
31
31
 
@@ -61,10 +61,10 @@ function getSourcePath(...segments) {
61
61
  */
62
62
  function getModulePath(moduleName, ...segments) {
63
63
  if (moduleName === 'core') {
64
- return getSourcePath('core', ...segments);
64
+ return getSourcePath('core-skills', ...segments);
65
65
  }
66
66
  if (moduleName === 'bmm') {
67
- return getSourcePath('bmm', ...segments);
67
+ return getSourcePath('bmm-skills', ...segments);
68
68
  }
69
69
  return getSourcePath('modules', moduleName, ...segments);
70
70
  }
@@ -495,7 +495,7 @@ class YamlXmlBuilder {
495
495
 
496
496
  // Extract module from path (e.g., /path/to/modules/bmm/agents/pm.yaml -> bmm)
497
497
  // or /path/to/bmad/bmm/agents/pm.yaml -> bmm
498
- // or /path/to/src/bmm/agents/pm.yaml -> bmm
498
+ // or /path/to/src/bmm-skills/agents/pm.yaml -> bmm
499
499
  let module = 'core'; // default to core
500
500
  const pathParts = agentYamlPath.split(path.sep);
501
501
 
@@ -515,10 +515,12 @@ class YamlXmlBuilder {
515
515
  module = potentialModule;
516
516
  }
517
517
  } else if (srcIndex !== -1 && pathParts[srcIndex + 1]) {
518
- // Path contains /src/{module}/ (bmm and core are directly under src/)
518
+ // Path contains /src/{module}/ (bmm-skills and core-skills are directly under src/)
519
519
  const potentialModule = pathParts[srcIndex + 1];
520
- if (potentialModule === 'bmm' || potentialModule === 'core') {
521
- module = potentialModule;
520
+ if (potentialModule === 'bmm-skills') {
521
+ module = 'bmm';
522
+ } else if (potentialModule === 'core-skills') {
523
+ module = 'core';
522
524
  }
523
525
  }
524
526