ma-agents 2.16.0 → 2.16.1

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 (58) hide show
  1. package/.agent/workflows/bmad-agent-bmad-master.md +15 -0
  2. package/.agent/workflows/bmad-agent-bmm-analyst.md +15 -0
  3. package/.agent/workflows/bmad-agent-bmm-architect.md +15 -0
  4. package/.agent/workflows/bmad-agent-bmm-dev.md +15 -0
  5. package/.agent/workflows/bmad-agent-bmm-pm.md +15 -0
  6. package/.agent/workflows/bmad-agent-bmm-qa.md +15 -0
  7. package/.agent/workflows/bmad-agent-bmm-quick-flow-solo-dev.md +15 -0
  8. package/.agent/workflows/bmad-agent-bmm-sm.md +15 -0
  9. package/.agent/workflows/bmad-agent-bmm-tech-writer.md +15 -0
  10. package/.agent/workflows/bmad-agent-bmm-ux-designer.md +15 -0
  11. package/.agent/workflows/bmad-agent-cis-brainstorming-coach.md +15 -0
  12. package/.agent/workflows/bmad-agent-cis-creative-problem-solver.md +15 -0
  13. package/.agent/workflows/bmad-agent-cis-design-thinking-coach.md +15 -0
  14. package/.agent/workflows/bmad-agent-cis-innovation-strategist.md +15 -0
  15. package/.agent/workflows/bmad-agent-cis-presentation-master.md +15 -0
  16. package/.agent/workflows/bmad-agent-cis-storyteller.md +15 -0
  17. package/.agent/workflows/bmad-bmm-check-implementation-readiness.md +6 -0
  18. package/.agent/workflows/bmad-bmm-code-review.md +14 -0
  19. package/.agent/workflows/bmad-bmm-correct-course.md +14 -0
  20. package/.agent/workflows/bmad-bmm-create-architecture.md +6 -0
  21. package/.agent/workflows/bmad-bmm-create-epics-and-stories.md +6 -0
  22. package/.agent/workflows/bmad-bmm-create-prd.md +6 -0
  23. package/.agent/workflows/bmad-bmm-create-product-brief.md +6 -0
  24. package/.agent/workflows/bmad-bmm-create-story.md +14 -0
  25. package/.agent/workflows/bmad-bmm-create-ux-design.md +6 -0
  26. package/.agent/workflows/bmad-bmm-dev-story.md +14 -0
  27. package/.agent/workflows/bmad-bmm-document-project.md +14 -0
  28. package/.agent/workflows/bmad-bmm-domain-research.md +6 -0
  29. package/.agent/workflows/bmad-bmm-edit-prd.md +6 -0
  30. package/.agent/workflows/bmad-bmm-generate-project-context.md +6 -0
  31. package/.agent/workflows/bmad-bmm-market-research.md +6 -0
  32. package/.agent/workflows/bmad-bmm-qa-generate-e2e-tests.md +14 -0
  33. package/.agent/workflows/bmad-bmm-quick-dev.md +6 -0
  34. package/.agent/workflows/bmad-bmm-quick-spec.md +6 -0
  35. package/.agent/workflows/bmad-bmm-retrospective.md +14 -0
  36. package/.agent/workflows/bmad-bmm-sprint-planning.md +14 -0
  37. package/.agent/workflows/bmad-bmm-sprint-status.md +14 -0
  38. package/.agent/workflows/bmad-bmm-technical-research.md +6 -0
  39. package/.agent/workflows/bmad-bmm-validate-prd.md +6 -0
  40. package/.agent/workflows/bmad-brainstorming.md +6 -0
  41. package/.agent/workflows/bmad-cis-design-thinking.md +14 -0
  42. package/.agent/workflows/bmad-cis-innovation-strategy.md +14 -0
  43. package/.agent/workflows/bmad-cis-problem-solving.md +14 -0
  44. package/.agent/workflows/bmad-cis-storytelling.md +14 -0
  45. package/.agent/workflows/bmad-editorial-review-prose.md +10 -0
  46. package/.agent/workflows/bmad-editorial-review-structure.md +10 -0
  47. package/.agent/workflows/bmad-help.md +10 -0
  48. package/.agent/workflows/bmad-index-docs.md +10 -0
  49. package/.agent/workflows/bmad-party-mode.md +6 -0
  50. package/.agent/workflows/bmad-review-adversarial-general.md +10 -0
  51. package/.agent/workflows/bmad-review-edge-case-hunter.md +10 -0
  52. package/.agent/workflows/bmad-shard-doc.md +10 -0
  53. package/README.md +43 -27
  54. package/bin/cli.js +37 -11
  55. package/lib/agents.js +18 -1
  56. package/lib/bmad.js +33 -8
  57. package/lib/installer.js +112 -38
  58. package/package.json +3 -3
package/lib/installer.js CHANGED
@@ -5,7 +5,7 @@ const prompts = require('prompts');
5
5
  const { getAgent, getAllAgents } = require('./agents');
6
6
 
7
7
  const MANIFEST_FILE = '.ma-agents.json';
8
- const MANIFEST_VERSION = '1.0.0';
8
+ const MANIFEST_VERSION = '1.1.0';
9
9
 
10
10
  function getPackageVersion() {
11
11
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
@@ -37,13 +37,33 @@ function ensureManifest(installPath, agentId, scope) {
37
37
  manifest = {
38
38
  manifestVersion: MANIFEST_VERSION,
39
39
  agent: agentId,
40
+ agents: [agentId],
40
41
  scope: scope,
41
42
  skills: {}
42
43
  };
44
+ } else {
45
+ // Migrate v1.0.0 manifests: add agents array if missing
46
+ if (!manifest.agents) {
47
+ manifest.agents = manifest.agent ? [manifest.agent] : [];
48
+ }
49
+ // Add current agent if not already present
50
+ if (agentId && !manifest.agents.includes(agentId)) {
51
+ manifest.agents.push(agentId);
52
+ }
53
+ // Keep backward-compat agent field as first agent
54
+ manifest.agent = manifest.agents[0] || null;
43
55
  }
44
56
  return manifest;
45
57
  }
46
58
 
59
+ function getManifestAgents(manifest) {
60
+ if (!manifest) return [];
61
+ if (manifest.agents && Array.isArray(manifest.agents)) {
62
+ return manifest.agents;
63
+ }
64
+ return manifest.agent ? [manifest.agent] : [];
65
+ }
66
+
47
67
  function getInstalledSkillInfo(installPath, skillId) {
48
68
  const manifest = readManifest(installPath);
49
69
  if (!manifest || !manifest.skills || !manifest.skills[skillId]) {
@@ -52,13 +72,13 @@ function getInstalledSkillInfo(installPath, skillId) {
52
72
  return manifest.skills[skillId];
53
73
  }
54
74
 
55
- async function generateSkillsManifest(installPath, agent) {
75
+ async function generateSkillsManifest(installPath) {
56
76
  const skills = listSkills();
57
77
  const manifest = readManifest(installPath);
58
78
  if (!manifest || !manifest.skills) return;
59
79
 
60
80
  const manifestYamlPath = path.join(installPath, 'MANIFEST.yaml');
61
- let yamlContent = '# skills/MANIFEST.yaml\n\nskills:\n';
81
+ let yamlContent = '# MANIFEST.yaml\n\nskills:\n';
62
82
 
63
83
  const skillIds = Object.keys(manifest.skills).sort();
64
84
  for (const skillId of skillIds) {
@@ -66,7 +86,7 @@ async function generateSkillsManifest(installPath, agent) {
66
86
  if (!skill) continue;
67
87
 
68
88
  yamlContent += ` - id: ${skillId}\n`;
69
- yamlContent += ` file: skills/${skillId}/SKILL.md\n`;
89
+ yamlContent += ` file: ${skillId}/SKILL.md\n`;
70
90
  yamlContent += ` description: ${skill.description}\n`;
71
91
 
72
92
  if (skill.applies_when && Array.isArray(skill.applies_when)) {
@@ -91,7 +111,7 @@ async function updateAgentInstructions(agent, projectRoot) {
91
111
  if (!agent.instructionFiles || agent.instructionFiles.length === 0) return;
92
112
 
93
113
  const agentProjectPath = agent.getProjectPath();
94
- const relManifestPath = path.relative(process.cwd(), path.join(agentProjectPath, 'MANIFEST.yaml')).replace(/\\/g, '/');
114
+ const relManifestPath = path.relative(projectRoot, path.join(agentProjectPath, 'MANIFEST.yaml')).replace(/\\/g, '/');
95
115
 
96
116
  const planningInstruction = `
97
117
  # AI Agent Skills - Planning Instruction
@@ -259,16 +279,26 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
259
279
 
260
280
  console.log(chalk.cyan(`\nInstalling skill: ${skill.name} v${skill.version}`));
261
281
 
282
+ // Group agents by their resolved install path to avoid redundant installs
283
+ const pathGroups = new Map();
262
284
  for (const agentId of agentIds) {
263
285
  const agent = getAgent(agentId);
264
-
265
286
  if (!agent) {
266
287
  console.log(chalk.yellow(` Warning: Skipping unknown agent: ${agentId}`));
267
288
  continue;
268
289
  }
290
+ const installPath = customPath || (scope === 'global' ? agent.getGlobalPath() : agent.getProjectPath());
291
+ if (!pathGroups.has(installPath)) {
292
+ pathGroups.set(installPath, []);
293
+ }
294
+ pathGroups.get(installPath).push({ agentId, agent });
295
+ }
296
+
297
+ for (const [installPath, agentEntries] of pathGroups) {
298
+ const primaryAgent = agentEntries[0].agent;
299
+ const agentNames = agentEntries.map(e => e.agent.name).join(', ');
269
300
 
270
301
  try {
271
- const installPath = customPath || (scope === 'global' ? agent.getGlobalPath() : agent.getProjectPath());
272
302
  await fs.ensureDir(installPath);
273
303
 
274
304
  const installed = getInstalledSkillInfo(installPath, skillId);
@@ -284,7 +314,7 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
284
314
  action = batchState.globalAction;
285
315
  } else if (cmp > 0) {
286
316
  // Upgrade available
287
- console.log(chalk.yellow(` ${skill.name} v${installed.version} → v${skill.version} update available for ${agent.name}`));
317
+ console.log(chalk.yellow(` ${skill.name} v${installed.version} → v${skill.version} update available for ${agentNames}`));
288
318
  const { choice } = await prompts({
289
319
  type: 'select',
290
320
  name: 'choice',
@@ -301,7 +331,7 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
301
331
  action = choice;
302
332
  } else if (cmp === 0) {
303
333
  // Same version
304
- console.log(chalk.gray(` ${skill.name} v${installed.version} already installed for ${agent.name}`));
334
+ console.log(chalk.gray(` ${skill.name} v${installed.version} already installed for ${agentNames}`));
305
335
  const { choice } = await prompts({
306
336
  type: 'select',
307
337
  name: 'choice',
@@ -316,7 +346,7 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
316
346
  action = choice;
317
347
  } else {
318
348
  // Downgrade
319
- console.log(chalk.yellow(` ${skill.name} v${installed.version} installed, package has v${skill.version} for ${agent.name}`));
349
+ console.log(chalk.yellow(` ${skill.name} v${installed.version} installed, package has v${skill.version} for ${agentNames}`));
320
350
  const { choice } = await prompts({
321
351
  type: 'select',
322
352
  name: 'choice',
@@ -353,15 +383,17 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
353
383
 
354
384
  if (action === 'remove') {
355
385
  await performUninstall(skillId, installPath);
356
- const manifest = ensureManifest(installPath, agentId, scope);
386
+ const manifest = ensureManifest(installPath, agentEntries[0].agentId, scope);
357
387
  delete manifest.skills[skillId];
358
388
  writeManifest(installPath, manifest);
359
- console.log(chalk.green(` - Removed ${skill.name} from ${agent.name}`));
389
+ console.log(chalk.green(` - Removed ${skill.name} from ${agentNames}`));
360
390
 
361
391
  // Generate MANIFEST.yaml and update agent instruction files
362
- await generateSkillsManifest(installPath, agent);
392
+ await generateSkillsManifest(installPath);
363
393
  if (scope === 'project') {
364
- await updateAgentInstructions(agent, process.cwd());
394
+ for (const entry of agentEntries) {
395
+ await updateAgentInstructions(entry.agent, process.cwd());
396
+ }
365
397
  }
366
398
  continue;
367
399
  }
@@ -374,14 +406,22 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
374
406
  }
375
407
 
376
408
  if (!installed || force) {
377
- console.log(chalk.gray(` Installing for ${agent.name}...`));
409
+ console.log(chalk.gray(` Installing for ${agentNames}...`));
378
410
  }
379
411
 
380
- const success = await performInstall(skillId, skill, agent, installPath);
412
+ // Perform the install ONCE for this shared path
413
+ const success = await performInstall(skillId, skill, primaryAgent, installPath);
381
414
 
382
415
  if (success) {
383
- // Update manifest
384
- const manifest = ensureManifest(installPath, agentId, scope);
416
+ // Update manifest with ALL agents that share this path
417
+ const manifest = ensureManifest(installPath, agentEntries[0].agentId, scope);
418
+ for (const entry of agentEntries) {
419
+ if (!manifest.agents.includes(entry.agentId)) {
420
+ manifest.agents.push(entry.agentId);
421
+ }
422
+ }
423
+ manifest.agent = manifest.agents[0];
424
+
385
425
  const now = new Date().toISOString();
386
426
  const existing = manifest.skills[skillId];
387
427
  manifest.skills[skillId] = {
@@ -389,14 +429,16 @@ async function installSkill(skillId, agentIds, customPath = '', scope = 'project
389
429
  installedAt: existing ? existing.installedAt : now,
390
430
  updatedAt: now,
391
431
  installerVersion: getPackageVersion(),
392
- agentVersion: agent.version
432
+ agentVersion: primaryAgent.version
393
433
  };
394
434
  writeManifest(installPath, manifest);
395
435
 
396
- // Generate MANIFEST.yaml and update agent instruction files
397
- await generateSkillsManifest(installPath, agent);
436
+ // Generate MANIFEST.yaml and update instruction files for ALL agents
437
+ await generateSkillsManifest(installPath);
398
438
  if (scope === 'project') {
399
- await updateAgentInstructions(agent, process.cwd());
439
+ for (const entry of agentEntries) {
440
+ await updateAgentInstructions(entry.agent, process.cwd());
441
+ }
400
442
  }
401
443
  }
402
444
  } catch (error) {
@@ -417,41 +459,65 @@ async function performUninstall(skillId, installPath) {
417
459
  async function uninstallSkill(skillId, agentIds, customPath = '', scope = 'project') {
418
460
  console.log(chalk.cyan(`\nUninstalling skill: ${skillId}`));
419
461
 
462
+ // Group agents by their resolved install path
463
+ const pathGroups = new Map();
420
464
  for (const agentId of agentIds) {
421
465
  const agent = getAgent(agentId);
422
-
423
466
  if (!agent) {
424
467
  console.log(chalk.yellow(` Warning: Skipping unknown agent: ${agentId}`));
425
468
  continue;
426
469
  }
470
+ const installPath = customPath || (scope === 'global' ? agent.getGlobalPath() : agent.getProjectPath());
471
+ if (!pathGroups.has(installPath)) {
472
+ pathGroups.set(installPath, []);
473
+ }
474
+ pathGroups.get(installPath).push({ agentId, agent });
475
+ }
427
476
 
477
+ for (const [installPath, agentEntries] of pathGroups) {
428
478
  try {
429
- const installPath = customPath || (scope === 'global' ? agent.getGlobalPath() : agent.getProjectPath());
430
479
  const installed = getInstalledSkillInfo(installPath, skillId);
431
480
 
432
481
  if (!installed) {
433
482
  const skillDir = path.join(installPath, skillId);
434
483
  if (fs.existsSync(skillDir)) {
435
484
  await performUninstall(skillId, installPath);
436
- console.log(chalk.green(` - Removed ${skillId} from ${agent.name} (legacy install)`));
485
+ console.log(chalk.green(` - Removed ${skillId} from ${installPath} (legacy install)`));
437
486
  } else {
438
- console.log(chalk.gray(` ${skillId} is not installed for ${agent.name}`));
487
+ console.log(chalk.gray(` ${skillId} is not installed at ${installPath}`));
439
488
  }
440
489
  continue;
441
490
  }
442
491
 
443
- await performUninstall(skillId, installPath);
492
+ // Read current manifest to check which agents still need this path
493
+ const manifest = readManifest(installPath) || { agents: [], skills: {} };
494
+ const currentAgents = getManifestAgents(manifest);
444
495
 
445
- const manifest = ensureManifest(installPath, agentId, scope);
446
- delete manifest.skills[skillId];
447
- writeManifest(installPath, manifest);
496
+ // Remove the requested agents from the manifest's agent list
497
+ const agentIdsToRemove = new Set(agentEntries.map(e => e.agentId));
498
+ const remainingAgents = currentAgents.filter(id => !agentIdsToRemove.has(id));
499
+
500
+ if (remainingAgents.length === 0) {
501
+ // No agents left that need this skill at this path -> delete the files
502
+ await performUninstall(skillId, installPath);
503
+ delete manifest.skills[skillId];
504
+ console.log(chalk.green(` - Removed ${skillId} v${installed.version} from ${installPath}`));
505
+ } else {
506
+ // Other agents still need the files, just update the manifest
507
+ console.log(chalk.gray(` ${skillId} still needed by ${remainingAgents.join(', ')} at ${installPath}, keeping files`));
508
+ }
448
509
 
449
- console.log(chalk.green(` - Removed ${skillId} v${installed.version} from ${agent.name}`));
510
+ // Update the agents list in manifest
511
+ manifest.agents = remainingAgents;
512
+ manifest.agent = remainingAgents[0] || null;
513
+ writeManifest(installPath, manifest);
450
514
 
451
- // Generate MANIFEST.yaml and update agent instruction files
452
- await generateSkillsManifest(installPath, agent);
515
+ // Regenerate MANIFEST.yaml and update instruction files
516
+ await generateSkillsManifest(installPath);
453
517
  if (scope === 'project') {
454
- await updateAgentInstructions(agent, process.cwd());
518
+ for (const entry of agentEntries) {
519
+ await updateAgentInstructions(entry.agent, process.cwd());
520
+ }
455
521
  }
456
522
  } catch (error) {
457
523
  console.log(chalk.red(` x Failed: ${error.message}`));
@@ -472,13 +538,20 @@ function getStatus(agentIds, customPath = '', scope = 'project') {
472
538
  const projectPath = agent.getProjectPath();
473
539
  const globalPath = agent.getGlobalPath();
474
540
 
475
- // Check both scopes unless custom path is specified
476
- const pathsToCheck = customPath
477
- ? [{ path: customPath, scope: 'custom' }]
478
- : [
541
+ // Filter paths based on scope
542
+ let pathsToCheck;
543
+ if (customPath) {
544
+ pathsToCheck = [{ path: customPath, scope: 'custom' }];
545
+ } else if (scope === 'global') {
546
+ pathsToCheck = [{ path: globalPath, scope: 'global' }];
547
+ } else if (scope === 'project') {
548
+ pathsToCheck = [{ path: projectPath, scope: 'project' }];
549
+ } else {
550
+ pathsToCheck = [
479
551
  { path: projectPath, scope: 'project' },
480
552
  { path: globalPath, scope: 'global' }
481
553
  ];
554
+ }
482
555
 
483
556
  for (const { path: checkPath, scope: checkScope } of pathsToCheck) {
484
557
  const manifest = readManifest(checkPath);
@@ -506,5 +579,6 @@ module.exports = {
506
579
  getStatus,
507
580
  readManifest,
508
581
  getInstalledSkillInfo,
582
+ getManifestAgents,
509
583
  compareSemver
510
584
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ma-agents",
3
- "version": "2.16.0",
3
+ "version": "2.16.1",
4
4
  "description": "NPX tool to install skills for AI coding agents (Claude Code, Gemini, Copilot, Kilocode, Cline, Cursor)",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -30,6 +30,6 @@
30
30
  "fs-extra": "^11.1.1"
31
31
  },
32
32
  "engines": {
33
- "node": ">=14.0.0"
33
+ "node": ">=16.0.0"
34
34
  }
35
- }
35
+ }