ma-agents 3.3.0 → 3.4.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.
- package/.opencode/skills/.ma-agents.json +99 -99
- package/.roo/skills/.ma-agents.json +99 -99
- package/README.md +19 -1
- package/bin/cli.js +55 -0
- package/lib/agents.js +23 -0
- package/lib/bmad-cache/cache-manifest.json +1 -1
- package/lib/bmad-customizations/bmm-demerzel.customize.yaml +36 -0
- package/lib/bmad-customizations/demerzel.md +32 -0
- package/lib/bmad-extension/module-help.csv +13 -0
- package/lib/bmad-extension/skills/bmad-ma-agent-ml/.gitkeep +0 -0
- package/lib/bmad-extension/skills/bmad-ma-agent-ml/SKILL.md +59 -0
- package/lib/bmad-extension/skills/bmad-ma-agent-ml/bmad-skill-manifest.yaml +11 -0
- package/lib/bmad-extension/skills/generate-backlog/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-advise/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-advise/SKILL.md +76 -0
- package/lib/bmad-extension/skills/ml-advise/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-advise/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-analysis/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-analysis/SKILL.md +60 -0
- package/lib/bmad-extension/skills/ml-analysis/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-analysis/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-architecture/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-architecture/SKILL.md +55 -0
- package/lib/bmad-extension/skills/ml-architecture/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-architecture/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-detailed-design/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-detailed-design/SKILL.md +67 -0
- package/lib/bmad-extension/skills/ml-detailed-design/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-detailed-design/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-eda/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-eda/SKILL.md +56 -0
- package/lib/bmad-extension/skills/ml-eda/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-eda/scripts/baseline_classifier.py +522 -0
- package/lib/bmad-extension/skills/ml-eda/scripts/class_weights_calculator.py +295 -0
- package/lib/bmad-extension/skills/ml-eda/scripts/clustering_explorer.py +383 -0
- package/lib/bmad-extension/skills/ml-eda/scripts/eda_analyzer.py +654 -0
- package/lib/bmad-extension/skills/ml-eda/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-experiment/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-experiment/SKILL.md +74 -0
- package/lib/bmad-extension/skills/ml-experiment/assets/advanced_trainer_configs.py +430 -0
- package/lib/bmad-extension/skills/ml-experiment/assets/quick_trainer_setup.py +233 -0
- package/lib/bmad-extension/skills/ml-experiment/assets/template_datamodule.py +219 -0
- package/lib/bmad-extension/skills/ml-experiment/assets/template_gnn_module.py +341 -0
- package/lib/bmad-extension/skills/ml-experiment/assets/template_lightning_module.py +158 -0
- package/lib/bmad-extension/skills/ml-experiment/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-experiment/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-hparam/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-hparam/SKILL.md +81 -0
- package/lib/bmad-extension/skills/ml-hparam/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-hparam/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-ideation/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-ideation/SKILL.md +50 -0
- package/lib/bmad-extension/skills/ml-ideation/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-ideation/scripts/validate_ml_prd.py +287 -0
- package/lib/bmad-extension/skills/ml-ideation/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-infra/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-infra/SKILL.md +58 -0
- package/lib/bmad-extension/skills/ml-infra/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-infra/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-retrospective/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-retrospective/SKILL.md +63 -0
- package/lib/bmad-extension/skills/ml-retrospective/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-retrospective/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-revision/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-revision/SKILL.md +82 -0
- package/lib/bmad-extension/skills/ml-revision/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-revision/skill.json +7 -0
- package/lib/bmad-extension/skills/ml-techspec/.gitkeep +0 -0
- package/lib/bmad-extension/skills/ml-techspec/SKILL.md +80 -0
- package/lib/bmad-extension/skills/ml-techspec/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/ml-techspec/skill.json +7 -0
- package/lib/bmad.js +85 -8
- package/lib/skill-authoring.js +1 -1
- package/package.json +2 -2
- package/test/agent-injection-strategy.test.js +4 -4
- package/test/bmad-version-bump.test.js +34 -34
- package/test/build-bmad-args.test.js +13 -6
- package/test/convert-agents-to-skills.test.js +11 -1
- package/test/extension-module-restructure.test.js +31 -7
- package/test/migration-validation.test.js +14 -11
package/lib/bmad.js
CHANGED
|
@@ -7,6 +7,23 @@ const chalk = require('chalk');
|
|
|
7
7
|
const BMAD_DIR = '_bmad';
|
|
8
8
|
const CONFIG_DIR = path.join(BMAD_DIR, '_config', 'agents');
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Run a shell command, relaying output to stdout/stderr.
|
|
12
|
+
* When MA_AGENTS_LOG_ACTIVE is set, uses pipe mode so the
|
|
13
|
+
* tee hooks in cli.js capture subprocess output in the log file.
|
|
14
|
+
* Otherwise falls back to inherit for direct pass-through.
|
|
15
|
+
*/
|
|
16
|
+
function runCommand(command, options = {}) {
|
|
17
|
+
if (process.env.MA_AGENTS_LOG_ACTIVE) {
|
|
18
|
+
const result = execSync(command, { ...options, stdio: 'pipe' });
|
|
19
|
+
if (result && result.length > 0) {
|
|
20
|
+
process.stdout.write(result);
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
execSync(command, { ...options, stdio: 'inherit' });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
10
27
|
function getBmadCommand(args) {
|
|
11
28
|
try {
|
|
12
29
|
const wrapperPath = require.resolve('bmad-method/tools/bmad-npx-wrapper.js');
|
|
@@ -63,9 +80,13 @@ function buildBmadArgs(ctx) {
|
|
|
63
80
|
parts.push('--output-folder', `"${ctx.outputFolder}"`);
|
|
64
81
|
}
|
|
65
82
|
|
|
66
|
-
// Extension module — only
|
|
83
|
+
// Extension module — only on fresh installs.
|
|
84
|
+
// During updates, the cached copy in _bmad/_config/custom/ is used by
|
|
85
|
+
// bmad-method automatically; passing --custom-content on update causes
|
|
86
|
+
// "Source for module 'custom' is not available" when the cached directory
|
|
87
|
+
// name doesn't match the module code.
|
|
67
88
|
const extensionPath = path.join(__dirname, 'bmad-extension');
|
|
68
|
-
if (fs.existsSync(extensionPath)) {
|
|
89
|
+
if (ctx.action !== 'update' && fs.existsSync(extensionPath)) {
|
|
69
90
|
parts.push('--custom-content', `"${extensionPath}"`);
|
|
70
91
|
}
|
|
71
92
|
|
|
@@ -101,7 +122,7 @@ async function installBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = p
|
|
|
101
122
|
|
|
102
123
|
console.log(chalk.gray(` Running: ${command}`));
|
|
103
124
|
try {
|
|
104
|
-
|
|
125
|
+
runCommand(command, { cwd: projectRoot, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
105
126
|
await deployMethodology(projectRoot, force);
|
|
106
127
|
return true;
|
|
107
128
|
} catch (error) {
|
|
@@ -136,7 +157,7 @@ async function runMigration(modules, tools, projectRoot, force, { userName, comm
|
|
|
136
157
|
|
|
137
158
|
console.log(chalk.gray(` Running: ${command}`));
|
|
138
159
|
try {
|
|
139
|
-
|
|
160
|
+
runCommand(command, { cwd: projectRoot, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
140
161
|
} catch (error) {
|
|
141
162
|
// Rollback on failure
|
|
142
163
|
console.error(chalk.red(` BMAD update failed: ${error.message}`));
|
|
@@ -183,7 +204,7 @@ async function updateBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = pr
|
|
|
183
204
|
|
|
184
205
|
console.log(chalk.gray(` Running: ${command}`));
|
|
185
206
|
try {
|
|
186
|
-
|
|
207
|
+
runCommand(command, { cwd: projectRoot, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
187
208
|
await deployMethodology(projectRoot, force);
|
|
188
209
|
return true;
|
|
189
210
|
} catch (error) {
|
|
@@ -206,7 +227,8 @@ async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm'
|
|
|
206
227
|
'bmm-sre': 'sre.md',
|
|
207
228
|
'bmm-devops': 'devops.md',
|
|
208
229
|
'bmm-cyber': 'cyber.md',
|
|
209
|
-
'bmm-mil498': 'mil498.md'
|
|
230
|
+
'bmm-mil498': 'mil498.md',
|
|
231
|
+
'bmm-demerzel': 'demerzel.md'
|
|
210
232
|
};
|
|
211
233
|
|
|
212
234
|
// 1. Apply YAML customizations (filtered by selected agents)
|
|
@@ -259,7 +281,7 @@ async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm'
|
|
|
259
281
|
|
|
260
282
|
console.log(chalk.gray(` Running: ${command}`));
|
|
261
283
|
try {
|
|
262
|
-
|
|
284
|
+
runCommand(command, { cwd: projectRoot, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
263
285
|
} catch (error) {
|
|
264
286
|
console.error(chalk.red(` BMAD recompile failed: ${error.message}`));
|
|
265
287
|
}
|
|
@@ -330,6 +352,11 @@ async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm'
|
|
|
330
352
|
if (selectedAgentIds.length === 0 || selectedAgentIds.includes('bmm-mil498')) {
|
|
331
353
|
await registerMilWorkflows(projectRoot);
|
|
332
354
|
}
|
|
355
|
+
|
|
356
|
+
// 8. Register ML workflows
|
|
357
|
+
if (selectedAgentIds.length === 0 || selectedAgentIds.includes('bmm-demerzel')) {
|
|
358
|
+
await registerMlWorkflows(projectRoot);
|
|
359
|
+
}
|
|
333
360
|
}
|
|
334
361
|
|
|
335
362
|
/**
|
|
@@ -411,6 +438,55 @@ async function registerMilWorkflows(projectRoot) {
|
|
|
411
438
|
}
|
|
412
439
|
}
|
|
413
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Append ML lifecycle workflow entries to the BMAD CSV registries
|
|
443
|
+
* so they appear as available slash commands for Demerzel.
|
|
444
|
+
*/
|
|
445
|
+
async function registerMlWorkflows(projectRoot) {
|
|
446
|
+
const mlEntries = [
|
|
447
|
+
{ name: 'ML Ideation & PRD', code: 'MLI', command: 'ml-ideation', description: 'Frame ML research problem, define requirements, and produce Research Thesis and PRD' },
|
|
448
|
+
{ name: 'ML EDA & Research', code: 'MLE', command: 'ml-eda', description: 'Perform exploratory data analysis, establish baselines, and produce EDA report' },
|
|
449
|
+
{ name: 'ML Architecture Design', code: 'MLA', command: 'ml-architecture', description: 'Design model architecture, stack, and experiment tracking strategy' },
|
|
450
|
+
{ name: 'ML Detailed Design', code: 'MLD', command: 'ml-detailed-design', description: 'Break down infrastructure and experiment tasks from architecture' },
|
|
451
|
+
{ name: 'ML TechSpec (Contract)', code: 'MLS', command: 'ml-techspec', description: 'Lock experiment parameters and performance tiers before training' },
|
|
452
|
+
{ name: 'ML Infra & Sync', code: 'MLNF', command: 'ml-infra', description: 'Build ML infrastructure, manage dependencies, and run smoke tests' },
|
|
453
|
+
{ name: 'ML Experiment', code: 'MLX', command: 'ml-experiment', description: 'Execute training experiments against locked TechSpec and log metrics' },
|
|
454
|
+
{ name: 'ML Results Analysis', code: 'MLAN', command: 'ml-analysis', description: 'Evaluate experiment outcomes against TechSpec success tiers' },
|
|
455
|
+
{ name: 'ML HPO (Tuning)', code: 'MLH', command: 'ml-hparam', description: 'Perform automated hyperparameter optimization' },
|
|
456
|
+
{ name: 'ML Iterative Revision', code: 'MLR', command: 'ml-revision', description: 'Amend hypothesis and requirements based on experiment findings' },
|
|
457
|
+
{ name: 'ML Advise & Search', code: 'MLAD', command: 'ml-advise', description: 'Search past experiments and surface findings or failure warnings' },
|
|
458
|
+
{ name: 'ML Retrospective', code: 'MLRT', command: 'ml-retrospective', description: 'Capture session learnings and update project context' }
|
|
459
|
+
];
|
|
460
|
+
|
|
461
|
+
// Append or update entries in module-help.csv
|
|
462
|
+
const moduleHelpPath = path.join(projectRoot, BMAD_DIR, 'bmm', 'module-help.csv');
|
|
463
|
+
if (fs.existsSync(moduleHelpPath)) {
|
|
464
|
+
let content = await fs.readFile(moduleHelpPath, 'utf-8');
|
|
465
|
+
for (const entry of mlEntries) {
|
|
466
|
+
const row = `bmm,anytime,${entry.name},${entry.code},,skills/${entry.command}/SKILL.md,${entry.command},false,bmm-demerzel,Create Mode,${entry.description},planning_artifacts,document,`;
|
|
467
|
+
// Remove any existing line for this command, then append the current one
|
|
468
|
+
const lines = content.split('\n').filter(line => !line.includes(entry.command));
|
|
469
|
+
content = lines.join('\n').trimEnd() + '\n' + row + '\n';
|
|
470
|
+
}
|
|
471
|
+
await fs.writeFile(moduleHelpPath, content, 'utf-8');
|
|
472
|
+
console.log(chalk.cyan(` + Registered ML workflows in module-help.csv`));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Append or update entries in bmad-help.csv
|
|
476
|
+
const bmadHelpPath = path.join(projectRoot, BMAD_DIR, '_config', 'bmad-help.csv');
|
|
477
|
+
if (fs.existsSync(bmadHelpPath)) {
|
|
478
|
+
let content = await fs.readFile(bmadHelpPath, 'utf-8');
|
|
479
|
+
for (const entry of mlEntries) {
|
|
480
|
+
const row = `bmm,anytime,${entry.name},${entry.code},,skills/${entry.command}/SKILL.md,${entry.command},false,bmm-demerzel,bmad:Machine Learning Lifecycle:agent:demerzel,Demerzel,🧪 ML Scientist,Create Mode,${entry.description},planning_artifacts,document`;
|
|
481
|
+
// Remove any existing line for this command, then append the current one
|
|
482
|
+
const lines = content.split('\n').filter(line => !line.includes(entry.command));
|
|
483
|
+
content = lines.join('\n').trimEnd() + '\n' + row + '\n';
|
|
484
|
+
}
|
|
485
|
+
await fs.writeFile(bmadHelpPath, content, 'utf-8');
|
|
486
|
+
console.log(chalk.cyan(` + Registered ML workflows in bmad-help.csv`));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
414
490
|
/**
|
|
415
491
|
* Deploy the methodology presentation to the target project's _bmad-output/methodology/ directory.
|
|
416
492
|
* Version-aware: skips if target is same or newer; overwrites only with --force.
|
|
@@ -521,7 +597,7 @@ async function prePopulateBmadCache(force = false) {
|
|
|
521
597
|
|
|
522
598
|
// ── Migration constants ─────────────────────────────────────────────────────
|
|
523
599
|
|
|
524
|
-
const BMAD_TARGET_VERSION = '6.2.
|
|
600
|
+
const BMAD_TARGET_VERSION = '6.2.2';
|
|
525
601
|
const BMAD_MIGRATION_THRESHOLD = '6.2.0'; // versions below this need migration
|
|
526
602
|
const BACKUP_DIR_NAME = '.backup-pre-migration';
|
|
527
603
|
|
|
@@ -1018,4 +1094,5 @@ module.exports = {
|
|
|
1018
1094
|
mergeUserCustomizations,
|
|
1019
1095
|
cleanupLegacyArtifacts,
|
|
1020
1096
|
parseCustomizeYaml,
|
|
1097
|
+
registerMlWorkflows,
|
|
1021
1098
|
};
|
package/lib/skill-authoring.js
CHANGED
|
@@ -405,7 +405,7 @@ async function handleSetMandatory(args) {
|
|
|
405
405
|
|
|
406
406
|
// ─── Story 3.4: BMAD Persona Customization Tooling ───────────────────────────
|
|
407
407
|
|
|
408
|
-
const CUSTOM_AGENTS = ['bmm-sre', 'bmm-devops', 'bmm-cyber', 'bmm-mil498'];
|
|
408
|
+
const CUSTOM_AGENTS = ['bmm-sre', 'bmm-devops', 'bmm-cyber', 'bmm-mil498', 'bmm-demerzel'];
|
|
409
409
|
const BUILTIN_AGENTS = ['bmm-pm', 'bmm-architect', 'bmm-dev', 'bmm-qa', 'bmm-sm', 'bmm-tech-writer', 'bmm-ux-designer'];
|
|
410
410
|
|
|
411
411
|
const MANDATORY_CRITICAL_ACTIONS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ma-agents",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "NPX tool to install skills for AI coding agents (Claude Code, Gemini, Copilot, Kilocode, Cline, Cursor, Roo Code)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"author": "",
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"bmad-method": "6.2.
|
|
30
|
+
"bmad-method": "6.2.2",
|
|
31
31
|
"prompts": "^2.4.2",
|
|
32
32
|
"chalk": "^4.1.2",
|
|
33
33
|
"fs-extra": "^11.1.1"
|
|
@@ -66,15 +66,15 @@ test('3.3: installer.js contains NO agent-name-specific logic', () => {
|
|
|
66
66
|
console.log('\nTask 4 — Registry completeness');
|
|
67
67
|
|
|
68
68
|
const EXPECTED_IDE = ['claude-code', 'gemini', 'copilot', 'kilocode', 'cline', 'cursor', 'antigravity', 'opencode'];
|
|
69
|
-
const EXPECTED_BMAD = ['bmm-sre', 'bmm-devops', 'bmm-cyber', 'bmm-mil498'];
|
|
69
|
+
const EXPECTED_BMAD = ['bmm-sre', 'bmm-devops', 'bmm-cyber', 'bmm-mil498', 'bmm-demerzel'];
|
|
70
70
|
const EXPECTED_ALL = [...EXPECTED_IDE, ...EXPECTED_BMAD];
|
|
71
71
|
|
|
72
|
-
test('4.1: registry has at least
|
|
73
|
-
assert.ok(allAgents.length >=
|
|
72
|
+
test('4.1: registry has at least 13 agents (8+ IDE + 5 BMAD)', () => {
|
|
73
|
+
assert.ok(allAgents.length >= 13, `Expected at least 13 agents, got ${allAgents.length}`);
|
|
74
74
|
const ideCount = allAgents.filter(a => a.category === 'ide').length;
|
|
75
75
|
const bmadCount = allAgents.filter(a => a.category === 'bmad').length;
|
|
76
76
|
assert.ok(ideCount >= 8, `Expected at least 8 IDE agents, got ${ideCount}`);
|
|
77
|
-
assert.strictEqual(bmadCount,
|
|
77
|
+
assert.strictEqual(bmadCount, 5, `Expected 5 BMAD agents, got ${bmadCount}`);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
test('4.2: every agent has well-shaped injectionStrategy property', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Tests for Story 15.1: Bump bmad-method to 6.2.
|
|
3
|
+
* Tests for Story 15.1: Bump bmad-method to 6.2.2 and Update Cache
|
|
4
4
|
*
|
|
5
5
|
* Task 1: Verify package.json dependency updated
|
|
6
6
|
* Task 2: Verify build:bmad-cache compatibility
|
|
@@ -31,22 +31,22 @@ function test(name, fn) {
|
|
|
31
31
|
|
|
32
32
|
const projectRoot = path.resolve(__dirname, '..');
|
|
33
33
|
|
|
34
|
-
console.log('\nStory 15.1: Bump bmad-method to 6.2.
|
|
34
|
+
console.log('\nStory 15.1: Bump bmad-method to 6.2.2 and Update Cache\n');
|
|
35
35
|
|
|
36
36
|
// ---- Task 1: package.json dependency ----
|
|
37
37
|
|
|
38
38
|
console.log('Task 1: package.json dependency');
|
|
39
39
|
|
|
40
|
-
test('package.json pins bmad-method at 6.2.
|
|
40
|
+
test('package.json pins bmad-method at 6.2.2', () => {
|
|
41
41
|
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
|
42
|
-
assert.strictEqual(pkg.dependencies['bmad-method'], '6.2.
|
|
42
|
+
assert.strictEqual(pkg.dependencies['bmad-method'], '6.2.2');
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
test('installed bmad-method is version 6.2.
|
|
45
|
+
test('installed bmad-method is version 6.2.2', () => {
|
|
46
46
|
const bmadPkg = JSON.parse(
|
|
47
47
|
fs.readFileSync(path.join(projectRoot, 'node_modules', 'bmad-method', 'package.json'), 'utf8')
|
|
48
48
|
);
|
|
49
|
-
assert.strictEqual(bmadPkg.version, '6.2.
|
|
49
|
+
assert.strictEqual(bmadPkg.version, '6.2.2');
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
test('require.resolve finds bmad-npx-wrapper.js', () => {
|
|
@@ -59,13 +59,13 @@ test('require.resolve finds bmad-npx-wrapper.js', () => {
|
|
|
59
59
|
|
|
60
60
|
console.log('\nTask 2: build:bmad-cache compatibility');
|
|
61
61
|
|
|
62
|
-
test('external-official-modules.yaml exists in 6.2.
|
|
62
|
+
test('external-official-modules.yaml exists in 6.2.2', () => {
|
|
63
63
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
64
64
|
const yamlPath = path.join(bmadDir, 'tools', 'cli', 'external-official-modules.yaml');
|
|
65
65
|
assert.ok(fs.existsSync(yamlPath), `YAML not found at ${yamlPath}`);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
test('YAML parser extracts 5 modules from 6.2.
|
|
68
|
+
test('YAML parser extracts 5 modules from 6.2.2', () => {
|
|
69
69
|
// Import the parser from build-bmad-cache.js by reading and evaluating it
|
|
70
70
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
71
71
|
const yamlPath = path.join(bmadDir, 'tools', 'cli', 'external-official-modules.yaml');
|
|
@@ -133,10 +133,10 @@ test('cache-manifest.json exists and lists 5 modules', () => {
|
|
|
133
133
|
assert.deepStrictEqual(moduleCodes.sort(), ['bmb', 'cis', 'gds', 'tea', 'wds']);
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
test('cache-manifest.json records bmadMethodVersion as 6.2.
|
|
136
|
+
test('cache-manifest.json records bmadMethodVersion as 6.2.2', () => {
|
|
137
137
|
const manifestPath = path.join(projectRoot, 'lib', 'bmad-cache', 'cache-manifest.json');
|
|
138
138
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
139
|
-
assert.strictEqual(manifest.bmadMethodVersion, '6.2.
|
|
139
|
+
assert.strictEqual(manifest.bmadMethodVersion, '6.2.2');
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
test('each cached module has a commit SHA in manifest', () => {
|
|
@@ -171,7 +171,7 @@ console.log('\nTask 3: bmad.js CLI compatibility');
|
|
|
171
171
|
|
|
172
172
|
const bmad = require('../lib/bmad');
|
|
173
173
|
|
|
174
|
-
test('getBmadCommand resolves bmad-npx-wrapper.js from 6.2.
|
|
174
|
+
test('getBmadCommand resolves bmad-npx-wrapper.js from 6.2.2', () => {
|
|
175
175
|
const result = bmad.buildBmadArgs({
|
|
176
176
|
projectRoot: '/test',
|
|
177
177
|
modules: ['bmm'],
|
|
@@ -192,82 +192,82 @@ test('buildBmadArgs generates valid --action update flag', () => {
|
|
|
192
192
|
assert.ok(result.includes('--action update'), `Expected --action update in: ${result}`);
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
-
test('6.2.
|
|
195
|
+
test('6.2.2 CLI accepts --directory flag', () => {
|
|
196
196
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
197
197
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
198
198
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
199
|
-
assert.ok(content.includes('--directory'), '6.2.
|
|
199
|
+
assert.ok(content.includes('--directory'), '6.2.2 install.js missing --directory flag');
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
test('6.2.
|
|
202
|
+
test('6.2.2 CLI accepts --modules flag', () => {
|
|
203
203
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
204
204
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
205
205
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
206
|
-
assert.ok(content.includes('--modules'), '6.2.
|
|
206
|
+
assert.ok(content.includes('--modules'), '6.2.2 install.js missing --modules flag');
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
-
test('6.2.
|
|
209
|
+
test('6.2.2 CLI accepts --tools flag', () => {
|
|
210
210
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
211
211
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
212
212
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
213
|
-
assert.ok(content.includes('--tools'), '6.2.
|
|
213
|
+
assert.ok(content.includes('--tools'), '6.2.2 install.js missing --tools flag');
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
-
test('6.2.
|
|
216
|
+
test('6.2.2 CLI accepts --custom-content flag', () => {
|
|
217
217
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
218
218
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
219
219
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
220
|
-
assert.ok(content.includes('--custom-content'), '6.2.
|
|
220
|
+
assert.ok(content.includes('--custom-content'), '6.2.2 install.js missing --custom-content flag');
|
|
221
221
|
});
|
|
222
222
|
|
|
223
|
-
test('6.2.
|
|
223
|
+
test('6.2.2 CLI accepts --action flag', () => {
|
|
224
224
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
225
225
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
226
226
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
227
|
-
assert.ok(content.includes('--action'), '6.2.
|
|
227
|
+
assert.ok(content.includes('--action'), '6.2.2 install.js missing --action flag');
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
-
test('6.2.
|
|
230
|
+
test('6.2.2 CLI accepts --yes flag', () => {
|
|
231
231
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
232
232
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
233
233
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
234
|
-
assert.ok(content.includes('--yes'), '6.2.
|
|
234
|
+
assert.ok(content.includes('--yes'), '6.2.2 install.js missing --yes flag');
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
test('6.2.
|
|
237
|
+
test('6.2.2 CLI accepts --user-name flag', () => {
|
|
238
238
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
239
239
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
240
240
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
241
|
-
assert.ok(content.includes('--user-name'), '6.2.
|
|
241
|
+
assert.ok(content.includes('--user-name'), '6.2.2 install.js missing --user-name flag');
|
|
242
242
|
});
|
|
243
243
|
|
|
244
|
-
test('6.2.
|
|
244
|
+
test('6.2.2 CLI accepts --communication-language flag', () => {
|
|
245
245
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
246
246
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
247
247
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
248
|
-
assert.ok(content.includes('--communication-language'), '6.2.
|
|
248
|
+
assert.ok(content.includes('--communication-language'), '6.2.2 install.js missing --communication-language flag');
|
|
249
249
|
});
|
|
250
250
|
|
|
251
|
-
test('6.2.
|
|
251
|
+
test('6.2.2 CLI accepts --document-output-language flag', () => {
|
|
252
252
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
253
253
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
254
254
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
255
|
-
assert.ok(content.includes('--document-output-language'), '6.2.
|
|
255
|
+
assert.ok(content.includes('--document-output-language'), '6.2.2 install.js missing --document-output-language flag');
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
-
test('6.2.
|
|
258
|
+
test('6.2.2 CLI accepts --output-folder flag', () => {
|
|
259
259
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
260
260
|
const installCmd = path.join(bmadDir, 'tools', 'cli', 'commands', 'install.js');
|
|
261
261
|
const content = fs.readFileSync(installCmd, 'utf8');
|
|
262
|
-
assert.ok(content.includes('--output-folder'), '6.2.
|
|
262
|
+
assert.ok(content.includes('--output-folder'), '6.2.2 install.js missing --output-folder flag');
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
-
test('module.yaml custom-content format is accepted by 6.2.
|
|
265
|
+
test('module.yaml custom-content format is accepted by 6.2.2', () => {
|
|
266
266
|
const bmadDir = path.dirname(require.resolve('bmad-method/package.json'));
|
|
267
267
|
const handlerPath = path.join(bmadDir, 'tools', 'cli', 'installers', 'lib', 'custom', 'handler.js');
|
|
268
268
|
const content = fs.readFileSync(handlerPath, 'utf8');
|
|
269
|
-
// 6.2.
|
|
270
|
-
assert.ok(content.includes('module.yaml'), '6.2.
|
|
269
|
+
// 6.2.2 still looks for module.yaml files
|
|
270
|
+
assert.ok(content.includes('module.yaml'), '6.2.2 handler.js no longer references module.yaml');
|
|
271
271
|
});
|
|
272
272
|
|
|
273
273
|
// ---- Task 4: prePopulateBmadCache compatibility ----
|
|
@@ -218,7 +218,7 @@ test('buildBmadArgs quotes path with spaces in --directory', () => {
|
|
|
218
218
|
assert.ok(result.includes('--directory "D:\\My Projects\\test project"'), `Expected quoted path in: ${result}`);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
test('buildBmadArgs includes --custom-content when extension exists', () => {
|
|
221
|
+
test('buildBmadArgs includes --custom-content on fresh install when extension exists', () => {
|
|
222
222
|
const fs = require('fs');
|
|
223
223
|
const extensionPath = path.join(__dirname, '..', 'lib', 'bmad-extension');
|
|
224
224
|
// This test requires lib/bmad-extension/ to exist (it does in this repo)
|
|
@@ -234,6 +234,16 @@ test('buildBmadArgs includes --custom-content when extension exists', () => {
|
|
|
234
234
|
assert.ok(result.includes(`"${extensionPath}"`), `Expected quoted extension path in: ${result}`);
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
test('buildBmadArgs omits --custom-content on update to avoid stale cache conflict', () => {
|
|
238
|
+
const result = bmad.buildBmadArgs({
|
|
239
|
+
projectRoot: '/test',
|
|
240
|
+
modules: ['bmm'],
|
|
241
|
+
tools: [],
|
|
242
|
+
action: 'update',
|
|
243
|
+
});
|
|
244
|
+
assert.ok(!result.includes('--custom-content'), `Expected NO --custom-content on update, got: ${result}`);
|
|
245
|
+
});
|
|
246
|
+
|
|
237
247
|
test('buildBmadArgs includes all params in correct combined command', () => {
|
|
238
248
|
const result = bmad.buildBmadArgs({
|
|
239
249
|
projectRoot: '/test/project',
|
|
@@ -268,14 +278,11 @@ test('buildBmadArgs starts with node and bmad-npx-wrapper path', () => {
|
|
|
268
278
|
assert.ok(result.includes('bmad-npx-wrapper.js'), `Expected bmad-npx-wrapper.js in: ${result}`);
|
|
269
279
|
});
|
|
270
280
|
|
|
271
|
-
// ---- M4:
|
|
281
|
+
// ---- M4: --custom-content conditional tests ----
|
|
272
282
|
|
|
273
283
|
console.log('\nCode review fixes: edge cases');
|
|
274
284
|
|
|
275
|
-
test('buildBmadArgs
|
|
276
|
-
// We can't remove the real dir, but we verify the conditional logic by checking
|
|
277
|
-
// that the function correctly includes it when present (tested above).
|
|
278
|
-
// This test validates the conditional branch exists by checking the output format.
|
|
285
|
+
test('buildBmadArgs includes exactly one --custom-content on fresh install', () => {
|
|
279
286
|
const result = bmad.buildBmadArgs({
|
|
280
287
|
projectRoot: '/test',
|
|
281
288
|
modules: ['bmm'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Tests for Story 15-3: Convert
|
|
3
|
+
* Tests for Story 15-3: Convert 5 Custom Agents to BMAD 6.2.0 Skill Folders
|
|
4
4
|
*
|
|
5
5
|
* Verifies:
|
|
6
6
|
* - Each agent has SKILL.md and bmad-skill-manifest.yaml
|
|
@@ -74,6 +74,16 @@ const AGENTS = [
|
|
|
74
74
|
oldPaths: ['bmm/workflows/mil498/', '_bmad/bmm/workflows/mil498/'],
|
|
75
75
|
chatTopic: 'MIL-STD-498',
|
|
76
76
|
menuItemCount: 10 // MH, CH, GS, GD, GP, GO, SS, GT, SD, DA
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
folder: 'bmad-ma-agent-ml',
|
|
80
|
+
name: 'bmad-ma-agent-ml',
|
|
81
|
+
displayName: 'Demerzel',
|
|
82
|
+
title: 'ML Scientist',
|
|
83
|
+
skillNames: ['ml-ideation', 'ml-eda', 'ml-architecture', 'ml-detailed-design', 'ml-techspec', 'ml-infra', 'ml-experiment', 'ml-analysis', 'ml-hparam', 'ml-revision', 'ml-advise', 'ml-retrospective'],
|
|
84
|
+
oldPaths: ['bmm/workflows/ml/', '_bmad/bmm/workflows/ml/'],
|
|
85
|
+
chatTopic: 'Machine Learning',
|
|
86
|
+
menuItemCount: 15 // MH, CH, MLI, MLE, MLA, MLD, MLS, MLNF, MLX, MLAN, MLH, MLR, MLAD, MLRT, DA
|
|
77
87
|
}
|
|
78
88
|
];
|
|
79
89
|
|
|
@@ -139,6 +139,7 @@ const expectedAgentSkills = [
|
|
|
139
139
|
'bmad-ma-agent-devops',
|
|
140
140
|
'bmad-ma-agent-cyber',
|
|
141
141
|
'bmad-ma-agent-mil498',
|
|
142
|
+
'bmad-ma-agent-ml',
|
|
142
143
|
];
|
|
143
144
|
|
|
144
145
|
const expectedMil498Skills = [
|
|
@@ -166,6 +167,13 @@ const expectedCyberSkills = [
|
|
|
166
167
|
const expectedExtensionSkills = [
|
|
167
168
|
'create-bug-story', 'add-sprint', 'modify-sprint',
|
|
168
169
|
'add-to-sprint', 'project-context-expansion', 'sprint-status-view',
|
|
170
|
+
'cleanup-done', 'generate-backlog', 'prioritize-backlog', 'remove-from-sprint',
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
const expectedMlSkills = [
|
|
174
|
+
'ml-ideation', 'ml-eda', 'ml-architecture', 'ml-detailed-design',
|
|
175
|
+
'ml-techspec', 'ml-infra', 'ml-experiment', 'ml-analysis',
|
|
176
|
+
'ml-hparam', 'ml-revision', 'ml-advise', 'ml-retrospective',
|
|
169
177
|
];
|
|
170
178
|
|
|
171
179
|
const allExpectedSkills = [
|
|
@@ -175,16 +183,17 @@ const allExpectedSkills = [
|
|
|
175
183
|
...expectedDevopsSkills,
|
|
176
184
|
...expectedCyberSkills,
|
|
177
185
|
...expectedExtensionSkills,
|
|
186
|
+
...expectedMlSkills,
|
|
178
187
|
];
|
|
179
188
|
|
|
180
|
-
test('skills/ has exactly
|
|
189
|
+
test('skills/ has exactly 53 subdirectories', () => {
|
|
181
190
|
const entries = fs.readdirSync(skillsDir).filter(e =>
|
|
182
191
|
fs.statSync(path.join(skillsDir, e)).isDirectory()
|
|
183
192
|
);
|
|
184
|
-
assert.strictEqual(entries.length,
|
|
193
|
+
assert.strictEqual(entries.length, 53, `Expected 53 skill dirs, found ${entries.length}`);
|
|
185
194
|
});
|
|
186
195
|
|
|
187
|
-
test('all
|
|
196
|
+
test('all 5 agent skill directories exist', () => {
|
|
188
197
|
for (const skill of expectedAgentSkills) {
|
|
189
198
|
const p = path.join(skillsDir, skill);
|
|
190
199
|
assert.ok(fs.existsSync(p), `Missing agent skill dir: ${skill}`);
|
|
@@ -219,7 +228,14 @@ test('all 7 Cyber skill directories exist', () => {
|
|
|
219
228
|
}
|
|
220
229
|
});
|
|
221
230
|
|
|
222
|
-
test('all
|
|
231
|
+
test('all 12 ML skill directories exist', () => {
|
|
232
|
+
for (const skill of expectedMlSkills) {
|
|
233
|
+
const p = path.join(skillsDir, skill);
|
|
234
|
+
assert.ok(fs.existsSync(p), `Missing ML skill dir: ${skill}`);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('all 10 extension workflow skill directories exist', () => {
|
|
223
239
|
for (const skill of expectedExtensionSkills) {
|
|
224
240
|
const p = path.join(skillsDir, skill);
|
|
225
241
|
assert.ok(fs.existsSync(p), `Missing extension skill dir: ${skill}`);
|
|
@@ -259,8 +275,8 @@ test('module-help.csv has correct header columns', () => {
|
|
|
259
275
|
}
|
|
260
276
|
});
|
|
261
277
|
|
|
262
|
-
test('module-help.csv has
|
|
263
|
-
assert.strictEqual(csvData.rows.length,
|
|
278
|
+
test('module-help.csv has 53 skill entries (excluding header)', () => {
|
|
279
|
+
assert.strictEqual(csvData.rows.length, 53, `Expected 53 rows, found ${csvData.rows.length}`);
|
|
264
280
|
});
|
|
265
281
|
|
|
266
282
|
test('all CSV entries use module code ma-skills', () => {
|
|
@@ -310,7 +326,7 @@ test('CSV has entries for all 7 Cyber skills', () => {
|
|
|
310
326
|
}
|
|
311
327
|
});
|
|
312
328
|
|
|
313
|
-
test('CSV has entries for all
|
|
329
|
+
test('CSV has entries for all 10 extension workflow skills', () => {
|
|
314
330
|
const codeIdx = csvData.headers.indexOf('code');
|
|
315
331
|
const codes = csvData.rows.map(r => r[codeIdx]);
|
|
316
332
|
for (const skill of expectedExtensionSkills) {
|
|
@@ -318,6 +334,14 @@ test('CSV has entries for all 6 extension workflow skills', () => {
|
|
|
318
334
|
}
|
|
319
335
|
});
|
|
320
336
|
|
|
337
|
+
test('CSV has entries for all 12 ML skills', () => {
|
|
338
|
+
const codeIdx = csvData.headers.indexOf('code');
|
|
339
|
+
const codes = csvData.rows.map(r => r[codeIdx]);
|
|
340
|
+
for (const skill of expectedMlSkills) {
|
|
341
|
+
assert.ok(codes.includes(skill), `Missing CSV entry for ML skill: ${skill}`);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
321
345
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
322
346
|
// Task 4: Directory state after Story 15.6 migration
|
|
323
347
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -348,11 +348,11 @@ test('5.2: module.yaml has extends-module: bmm', () => {
|
|
|
348
348
|
assert.ok(content.includes('extends-module: bmm'), 'module.yaml must declare extends-module: bmm');
|
|
349
349
|
});
|
|
350
350
|
|
|
351
|
-
test('5.3: skills/ contains exactly
|
|
351
|
+
test('5.3: skills/ contains exactly 53 skill folders', () => {
|
|
352
352
|
const folders = fs.readdirSync(SKILLS_DIR).filter(f =>
|
|
353
353
|
fs.statSync(path.join(SKILLS_DIR, f)).isDirectory());
|
|
354
|
-
assert.strictEqual(folders.length,
|
|
355
|
-
`Expected
|
|
354
|
+
assert.strictEqual(folders.length, 53,
|
|
355
|
+
`Expected 53 skill folders, found ${folders.length}: ${folders.join(', ')}`);
|
|
356
356
|
});
|
|
357
357
|
|
|
358
358
|
test('5.4: every skill folder has SKILL.md', () => {
|
|
@@ -373,12 +373,12 @@ test('5.5: every skill folder has bmad-skill-manifest.yaml', () => {
|
|
|
373
373
|
}
|
|
374
374
|
});
|
|
375
375
|
|
|
376
|
-
test('5.6: module-help.csv exists and lists all
|
|
376
|
+
test('5.6: module-help.csv exists and lists all 53 skills', () => {
|
|
377
377
|
const csv = fs.readFileSync(path.join(EXT_DIR, 'module-help.csv'), 'utf-8');
|
|
378
378
|
const lines = csv.split('\n').filter(l => l.trim() && !l.startsWith('module,'));
|
|
379
|
-
// Should have a line for each of the
|
|
380
|
-
assert.strictEqual(lines.length,
|
|
381
|
-
`Expected
|
|
379
|
+
// Should have a line for each of the 53 skills (header excluded)
|
|
380
|
+
assert.strictEqual(lines.length, 53,
|
|
381
|
+
`Expected 53 data rows in module-help.csv, found ${lines.length}`);
|
|
382
382
|
});
|
|
383
383
|
|
|
384
384
|
test('5.7: module-help.csv references module code ma-skills', () => {
|
|
@@ -390,7 +390,7 @@ test('5.7: module-help.csv references module code ma-skills', () => {
|
|
|
390
390
|
}
|
|
391
391
|
});
|
|
392
392
|
|
|
393
|
-
test('5.8: skill folder breakdown:
|
|
393
|
+
test('5.8: skill folder breakdown: 5 agents + 7 mil498 + 7 sre + 5 devops + 7 cyber + 12 ml + 10 extension', () => {
|
|
394
394
|
const folders = fs.readdirSync(SKILLS_DIR).filter(f =>
|
|
395
395
|
fs.statSync(path.join(SKILLS_DIR, f)).isDirectory());
|
|
396
396
|
|
|
@@ -399,16 +399,19 @@ test('5.8: skill folder breakdown: 4 agents + 7 mil498 + 7 sre + 5 devops + 7 cy
|
|
|
399
399
|
const sre = folders.filter(f => f.startsWith('sre-'));
|
|
400
400
|
const devops = folders.filter(f => f.startsWith('devops-'));
|
|
401
401
|
const cyber = folders.filter(f => f.startsWith('cyber-'));
|
|
402
|
+
const ml = folders.filter(f => f.startsWith('ml-'));
|
|
402
403
|
const ext = folders.filter(f =>
|
|
403
404
|
!f.startsWith('bmad-ma-agent-') && !f.startsWith('mil498-') &&
|
|
404
|
-
!f.startsWith('sre-') && !f.startsWith('devops-') && !f.startsWith('cyber-')
|
|
405
|
+
!f.startsWith('sre-') && !f.startsWith('devops-') && !f.startsWith('cyber-') &&
|
|
406
|
+
!f.startsWith('ml-'));
|
|
405
407
|
|
|
406
|
-
assert.strictEqual(agents.length,
|
|
408
|
+
assert.strictEqual(agents.length, 5, `Expected 5 agent folders, found ${agents.length}`);
|
|
407
409
|
assert.strictEqual(mil498.length, 7, `Expected 7 mil498 folders, found ${mil498.length}`);
|
|
408
410
|
assert.strictEqual(sre.length, 7, `Expected 7 sre folders, found ${sre.length}`);
|
|
409
411
|
assert.strictEqual(devops.length, 5, `Expected 5 devops folders, found ${devops.length}`);
|
|
410
412
|
assert.strictEqual(cyber.length, 7, `Expected 7 cyber folders, found ${cyber.length}`);
|
|
411
|
-
assert.strictEqual(
|
|
413
|
+
assert.strictEqual(ml.length, 12, `Expected 12 ml folders, found ${ml.length}`);
|
|
414
|
+
assert.strictEqual(ext.length, 10, `Expected 10 extension folders, found ${ext.length}`);
|
|
412
415
|
});
|
|
413
416
|
|
|
414
417
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|