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.
- package/package.json +1 -1
- package/tools/cli/commands/install.js +1 -8
- package/tools/cli/installers/lib/core/installer.js +1 -209
- package/tools/cli/installers/lib/core/manifest-generator.js +59 -107
- package/tools/cli/installers/lib/custom/handler.js +1 -247
- package/tools/cli/installers/lib/ide/_base-ide.js +0 -16
- package/tools/cli/installers/lib/modules/manager.js +2 -450
- package/tools/cli/lib/ui.js +0 -19
- package/src/bmm-skills/1-analysis/bmad-document-project/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/1-analysis/bmad-product-brief/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/1-analysis/research/bmad-market-research/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-code-review/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-correct-course/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-create-story/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-dev-story/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-retrospective/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/bmad-skill-manifest.yaml +0 -1
- package/src/bmm-skills/4-implementation/bmad-sprint-status/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-advanced-elicitation/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-brainstorming/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-distillator/bmad-skill-manifest.yaml +0 -15
- package/src/core-skills/bmad-editorial-review-prose/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-editorial-review-structure/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-help/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-index-docs/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-init/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-party-mode/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-review-adversarial-general/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-review-edge-case-hunter/bmad-skill-manifest.yaml +0 -1
- package/src/core-skills/bmad-shard-doc/bmad-skill-manifest.yaml +0 -1
- package/tools/cli/lib/activation-builder.js +0 -165
- package/tools/cli/lib/agent/compiler.js +0 -516
- package/tools/cli/lib/agent/installer.js +0 -680
- package/tools/cli/lib/agent/template-engine.js +0 -152
- package/tools/cli/lib/agent-analyzer.js +0 -97
- package/tools/cli/lib/agent-party-generator.js +0 -194
- package/tools/cli/lib/xml-handler.js +0 -177
- package/tools/cli/lib/xml-to-markdown.js +0 -82
- package/tools/cli/lib/yaml-xml-builder.js +0 -572
package/package.json
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
174
|
-
*
|
|
175
|
-
*
|
|
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
|
-
//
|
|
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
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
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') &&
|
|
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.
|
|
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
|
|
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
|
|
1364
|
+
* @returns {boolean} True if a SKILL.md is found
|
|
1412
1365
|
*/
|
|
1413
|
-
async
|
|
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
|
|
1422
|
-
|
|
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.
|
|
1381
|
+
if (await this._hasSkillMdRecursive(path.join(dir, entry.name))) return true;
|
|
1430
1382
|
}
|
|
1431
1383
|
|
|
1432
1384
|
return false;
|