@swarmify/agents-cli 1.5.7 → 1.5.9
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/dist/index.js +235 -118
- package/dist/index.js.map +1 -1
- package/dist/lib/agents.d.ts +12 -12
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +34 -36
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/commands.d.ts +5 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +32 -0
- package/dist/lib/commands.js.map +1 -1
- package/dist/lib/hooks.d.ts +15 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +66 -0
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/skills.d.ts +5 -0
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +61 -0
- package/dist/lib/skills.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,9 +21,9 @@ import { readManifest, writeManifest, createDefaultManifest, MANIFEST_FILENAME,
|
|
|
21
21
|
import { readState, ensureAgentsDir, getRepoLocalPath, getScope, setScope, removeScope, getScopesByPriority, getScopePriority, } from './lib/state.js';
|
|
22
22
|
import { SCOPE_PRIORITIES, DEFAULT_SYSTEM_REPO } from './lib/types.js';
|
|
23
23
|
import { cloneRepo, parseSource } from './lib/git.js';
|
|
24
|
-
import { discoverCommands, resolveCommandSource, installCommand, uninstallCommand, listInstalledCommandsWithScope, promoteCommandToUser, commandExists, } from './lib/commands.js';
|
|
25
|
-
import { discoverHooksFromRepo, installHooks, listInstalledHooksWithScope, promoteHookToUser, removeHook, hookExists, } from './lib/hooks.js';
|
|
26
|
-
import { discoverSkillsFromRepo, installSkill, uninstallSkill, listInstalledSkillsWithScope, promoteSkillToUser, getSkillInfo, getSkillRules, skillExists, } from './lib/skills.js';
|
|
24
|
+
import { discoverCommands, resolveCommandSource, installCommand, uninstallCommand, listInstalledCommandsWithScope, promoteCommandToUser, commandExists, commandContentMatches, } from './lib/commands.js';
|
|
25
|
+
import { discoverHooksFromRepo, installHooks, listInstalledHooksWithScope, promoteHookToUser, removeHook, hookExists, hookContentMatches, getSourceHookEntry, } from './lib/hooks.js';
|
|
26
|
+
import { discoverSkillsFromRepo, installSkill, uninstallSkill, listInstalledSkillsWithScope, promoteSkillToUser, getSkillInfo, getSkillRules, skillExists, skillContentMatches, } from './lib/skills.js';
|
|
27
27
|
import { DEFAULT_REGISTRIES } from './lib/types.js';
|
|
28
28
|
import { search as searchRegistries, getRegistries, setRegistry, removeRegistry, resolvePackage, } from './lib/registry.js';
|
|
29
29
|
const program = new Command();
|
|
@@ -95,10 +95,12 @@ async function checkForUpdates() {
|
|
|
95
95
|
});
|
|
96
96
|
if (answer === 'now') {
|
|
97
97
|
// Run upgrade
|
|
98
|
-
const {
|
|
98
|
+
const { exec } = await import('child_process');
|
|
99
|
+
const { promisify } = await import('util');
|
|
100
|
+
const execAsync = promisify(exec);
|
|
99
101
|
const spinner = ora('Upgrading...').start();
|
|
100
102
|
try {
|
|
101
|
-
|
|
103
|
+
await execAsync('npm install -g @swarmify/agents-cli@latest');
|
|
102
104
|
spinner.succeed(`Upgraded to ${latestVersion}`);
|
|
103
105
|
await showWhatsNew(VERSION, latestVersion);
|
|
104
106
|
}
|
|
@@ -155,7 +157,7 @@ program
|
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
const cwd = process.cwd();
|
|
158
|
-
const cliStates = getAllCliStates();
|
|
160
|
+
const cliStates = await getAllCliStates();
|
|
159
161
|
const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
|
|
160
162
|
const skillAgentsToShow = filterAgentId
|
|
161
163
|
? SKILLS_CAPABLE_AGENTS.filter((id) => id === filterAgentId)
|
|
@@ -172,9 +174,8 @@ program
|
|
|
172
174
|
agent: AGENTS[agentId],
|
|
173
175
|
skills: listInstalledSkillsWithScope(agentId, cwd),
|
|
174
176
|
}));
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
.map((agentId) => ({
|
|
177
|
+
const installedMcpAgents = mcpAgentsToShow.filter((agentId) => cliStates[agentId]?.installed);
|
|
178
|
+
const mcpsData = installedMcpAgents.map((agentId) => ({
|
|
178
179
|
agent: AGENTS[agentId],
|
|
179
180
|
mcps: listInstalledMcpsWithScope(agentId, cwd),
|
|
180
181
|
}));
|
|
@@ -364,6 +365,7 @@ program
|
|
|
364
365
|
const allSkills = discoverSkillsFromRepo(localPath);
|
|
365
366
|
const discoveredHooks = discoverHooksFromRepo(localPath);
|
|
366
367
|
// Determine which agents to sync
|
|
368
|
+
const cliStates = await getAllCliStates();
|
|
367
369
|
let selectedAgents;
|
|
368
370
|
if (agentFilter) {
|
|
369
371
|
// Single agent filter
|
|
@@ -374,7 +376,7 @@ program
|
|
|
374
376
|
selectedAgents = (manifest?.defaults?.agents || ['claude', 'codex', 'gemini']);
|
|
375
377
|
}
|
|
376
378
|
else {
|
|
377
|
-
const installedAgents = ALL_AGENT_IDS.filter((id) =>
|
|
379
|
+
const installedAgents = ALL_AGENT_IDS.filter((id) => cliStates[id]?.installed || id === 'cursor');
|
|
378
380
|
selectedAgents = await checkbox({
|
|
379
381
|
message: 'Select agents to sync:',
|
|
380
382
|
choices: installedAgents.map((id) => ({
|
|
@@ -385,7 +387,7 @@ program
|
|
|
385
387
|
});
|
|
386
388
|
}
|
|
387
389
|
// Filter agents to only installed ones (plus cursor which doesn't need CLI)
|
|
388
|
-
selectedAgents = selectedAgents.filter((id) =>
|
|
390
|
+
selectedAgents = selectedAgents.filter((id) => cliStates[id]?.installed || id === 'cursor');
|
|
389
391
|
if (selectedAgents.length === 0) {
|
|
390
392
|
console.log(chalk.yellow('\nNo agents selected or installed. Nothing to sync.'));
|
|
391
393
|
return;
|
|
@@ -393,6 +395,7 @@ program
|
|
|
393
395
|
// Build resource items with conflict detection
|
|
394
396
|
const newItems = [];
|
|
395
397
|
const existingItems = [];
|
|
398
|
+
const upToDateItems = [];
|
|
396
399
|
// Process commands
|
|
397
400
|
for (const command of allCommands) {
|
|
398
401
|
const applicableAgents = selectedAgents.filter((agentId) => {
|
|
@@ -401,29 +404,55 @@ program
|
|
|
401
404
|
});
|
|
402
405
|
if (applicableAgents.length === 0)
|
|
403
406
|
continue;
|
|
404
|
-
const conflictingAgents = applicableAgents.filter((agentId) => commandExists(agentId, command.name));
|
|
405
407
|
const newAgents = applicableAgents.filter((agentId) => !commandExists(agentId, command.name));
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
408
|
+
const upToDateAgents = applicableAgents.filter((agentId) => {
|
|
409
|
+
if (!commandExists(agentId, command.name))
|
|
410
|
+
return false;
|
|
411
|
+
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
412
|
+
return sourcePath && commandContentMatches(agentId, command.name, sourcePath);
|
|
413
|
+
});
|
|
414
|
+
const conflictingAgents = applicableAgents.filter((agentId) => {
|
|
415
|
+
if (!commandExists(agentId, command.name))
|
|
416
|
+
return false;
|
|
417
|
+
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
418
|
+
return sourcePath && !commandContentMatches(agentId, command.name, sourcePath);
|
|
419
|
+
});
|
|
409
420
|
if (newAgents.length > 0) {
|
|
410
421
|
newItems.push({ type: 'command', name: command.name, agents: newAgents, isNew: true });
|
|
411
422
|
}
|
|
423
|
+
if (upToDateAgents.length > 0) {
|
|
424
|
+
upToDateItems.push({ type: 'command', name: command.name, agents: upToDateAgents, isNew: false });
|
|
425
|
+
}
|
|
426
|
+
if (conflictingAgents.length > 0) {
|
|
427
|
+
existingItems.push({ type: 'command', name: command.name, agents: conflictingAgents, isNew: false });
|
|
428
|
+
}
|
|
412
429
|
}
|
|
413
430
|
// Process skills
|
|
414
431
|
const skillAgents = SKILLS_CAPABLE_AGENTS.filter((id) => selectedAgents.includes(id));
|
|
415
432
|
for (const skill of allSkills) {
|
|
416
|
-
const conflictingAgents = skillAgents.filter((agentId) => skillExists(agentId, skill.name));
|
|
417
433
|
const newAgents = skillAgents.filter((agentId) => !skillExists(agentId, skill.name));
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
434
|
+
const upToDateAgents = skillAgents.filter((agentId) => {
|
|
435
|
+
if (!skillExists(agentId, skill.name))
|
|
436
|
+
return false;
|
|
437
|
+
return skillContentMatches(agentId, skill.name, skill.path);
|
|
438
|
+
});
|
|
439
|
+
const conflictingAgents = skillAgents.filter((agentId) => {
|
|
440
|
+
if (!skillExists(agentId, skill.name))
|
|
441
|
+
return false;
|
|
442
|
+
return !skillContentMatches(agentId, skill.name, skill.path);
|
|
443
|
+
});
|
|
421
444
|
if (newAgents.length > 0) {
|
|
422
445
|
newItems.push({ type: 'skill', name: skill.name, agents: newAgents, isNew: true });
|
|
423
446
|
}
|
|
447
|
+
if (upToDateAgents.length > 0) {
|
|
448
|
+
upToDateItems.push({ type: 'skill', name: skill.name, agents: upToDateAgents, isNew: false });
|
|
449
|
+
}
|
|
450
|
+
if (conflictingAgents.length > 0) {
|
|
451
|
+
existingItems.push({ type: 'skill', name: skill.name, agents: conflictingAgents, isNew: false });
|
|
452
|
+
}
|
|
424
453
|
}
|
|
425
454
|
// Process hooks
|
|
426
|
-
const hookAgents = selectedAgents.filter((id) => HOOKS_CAPABLE_AGENTS.includes(id) &&
|
|
455
|
+
const hookAgents = selectedAgents.filter((id) => HOOKS_CAPABLE_AGENTS.includes(id) && cliStates[id]?.installed);
|
|
427
456
|
const allHookNames = [
|
|
428
457
|
...discoveredHooks.shared,
|
|
429
458
|
...Object.entries(discoveredHooks.agentSpecific)
|
|
@@ -432,25 +461,43 @@ program
|
|
|
432
461
|
];
|
|
433
462
|
const uniqueHookNames = [...new Set(allHookNames)];
|
|
434
463
|
for (const hookName of uniqueHookNames) {
|
|
435
|
-
const conflictingAgents = hookAgents.filter((agentId) => hookExists(agentId, hookName));
|
|
436
464
|
const newAgents = hookAgents.filter((agentId) => !hookExists(agentId, hookName));
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
465
|
+
const upToDateAgents = hookAgents.filter((agentId) => {
|
|
466
|
+
if (!hookExists(agentId, hookName))
|
|
467
|
+
return false;
|
|
468
|
+
const sourceEntry = getSourceHookEntry(localPath, agentId, hookName);
|
|
469
|
+
return sourceEntry && hookContentMatches(agentId, hookName, sourceEntry);
|
|
470
|
+
});
|
|
471
|
+
const conflictingAgents = hookAgents.filter((agentId) => {
|
|
472
|
+
if (!hookExists(agentId, hookName))
|
|
473
|
+
return false;
|
|
474
|
+
const sourceEntry = getSourceHookEntry(localPath, agentId, hookName);
|
|
475
|
+
return !sourceEntry || !hookContentMatches(agentId, hookName, sourceEntry);
|
|
476
|
+
});
|
|
440
477
|
if (newAgents.length > 0) {
|
|
441
478
|
newItems.push({ type: 'hook', name: hookName, agents: newAgents, isNew: true });
|
|
442
479
|
}
|
|
480
|
+
if (upToDateAgents.length > 0) {
|
|
481
|
+
upToDateItems.push({ type: 'hook', name: hookName, agents: upToDateAgents, isNew: false });
|
|
482
|
+
}
|
|
483
|
+
if (conflictingAgents.length > 0) {
|
|
484
|
+
existingItems.push({ type: 'hook', name: hookName, agents: conflictingAgents, isNew: false });
|
|
485
|
+
}
|
|
443
486
|
}
|
|
444
|
-
// Process MCPs
|
|
487
|
+
// Process MCPs (no content comparison - just existence check)
|
|
445
488
|
if (!options.skipMcp && manifest?.mcp) {
|
|
446
489
|
for (const [name, config] of Object.entries(manifest.mcp)) {
|
|
447
490
|
if (config.transport === 'http' || !config.command)
|
|
448
491
|
continue;
|
|
449
|
-
const mcpAgents = config.agents.filter((agentId) => selectedAgents.includes(agentId) &&
|
|
492
|
+
const mcpAgents = config.agents.filter((agentId) => selectedAgents.includes(agentId) && cliStates[agentId]?.installed);
|
|
450
493
|
if (mcpAgents.length === 0)
|
|
451
494
|
continue;
|
|
452
|
-
const
|
|
453
|
-
|
|
495
|
+
const registrationChecks = await Promise.all(mcpAgents.map(async (agentId) => ({
|
|
496
|
+
agentId,
|
|
497
|
+
isRegistered: await isMcpRegistered(agentId, name),
|
|
498
|
+
})));
|
|
499
|
+
const conflictingAgents = registrationChecks.filter((r) => r.isRegistered).map((r) => r.agentId);
|
|
500
|
+
const newAgents = registrationChecks.filter((r) => !r.isRegistered).map((r) => r.agentId);
|
|
454
501
|
if (conflictingAgents.length > 0) {
|
|
455
502
|
existingItems.push({ type: 'mcp', name, agents: conflictingAgents, isNew: false });
|
|
456
503
|
}
|
|
@@ -493,6 +540,31 @@ program
|
|
|
493
540
|
}
|
|
494
541
|
console.log();
|
|
495
542
|
}
|
|
543
|
+
if (upToDateItems.length > 0) {
|
|
544
|
+
console.log(chalk.gray(' UP TO DATE (no changes):\n'));
|
|
545
|
+
const byType = { command: [], skill: [], hook: [], mcp: [] };
|
|
546
|
+
for (const item of upToDateItems)
|
|
547
|
+
byType[item.type].push(item);
|
|
548
|
+
if (byType.command.length > 0) {
|
|
549
|
+
console.log(` Commands:`);
|
|
550
|
+
for (const item of byType.command) {
|
|
551
|
+
console.log(` ${chalk.dim(item.name.padEnd(20))} ${chalk.dim(formatAgentList(item.agents))}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (byType.skill.length > 0) {
|
|
555
|
+
console.log(` Skills:`);
|
|
556
|
+
for (const item of byType.skill) {
|
|
557
|
+
console.log(` ${chalk.dim(item.name.padEnd(20))} ${chalk.dim(formatAgentList(item.agents))}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (byType.hook.length > 0) {
|
|
561
|
+
console.log(` Hooks:`);
|
|
562
|
+
for (const item of byType.hook) {
|
|
563
|
+
console.log(` ${chalk.dim(item.name.padEnd(20))} ${chalk.dim(formatAgentList(item.agents))}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
console.log();
|
|
567
|
+
}
|
|
496
568
|
if (existingItems.length > 0) {
|
|
497
569
|
console.log(chalk.yellow(' EXISTING (conflicts):\n'));
|
|
498
570
|
const byType = { command: [], skill: [], hook: [], mcp: [] };
|
|
@@ -525,7 +597,7 @@ program
|
|
|
525
597
|
console.log();
|
|
526
598
|
}
|
|
527
599
|
if (newItems.length === 0 && existingItems.length === 0) {
|
|
528
|
-
console.log(chalk.gray('
|
|
600
|
+
console.log(chalk.gray(' Already up to date.\n'));
|
|
529
601
|
return;
|
|
530
602
|
}
|
|
531
603
|
if (options.dryRun) {
|
|
@@ -670,9 +742,9 @@ program
|
|
|
670
742
|
continue;
|
|
671
743
|
for (const agentId of item.agents) {
|
|
672
744
|
if (!item.isNew) {
|
|
673
|
-
unregisterMcp(agentId, item.name);
|
|
745
|
+
await unregisterMcp(agentId, item.name);
|
|
674
746
|
}
|
|
675
|
-
const result = registerMcp(agentId, item.name, config.command, config.scope);
|
|
747
|
+
const result = await registerMcp(agentId, item.name, config.command, config.scope);
|
|
676
748
|
if (result.success)
|
|
677
749
|
installed.mcps++;
|
|
678
750
|
}
|
|
@@ -698,7 +770,7 @@ program
|
|
|
698
770
|
const agent = AGENTS[agentId];
|
|
699
771
|
if (!agent || !cliConfig.package)
|
|
700
772
|
continue;
|
|
701
|
-
const currentVersion = getCliVersion(agentId);
|
|
773
|
+
const currentVersion = await getCliVersion(agentId);
|
|
702
774
|
const targetVersion = cliConfig.version;
|
|
703
775
|
if (currentVersion === targetVersion)
|
|
704
776
|
continue;
|
|
@@ -765,7 +837,7 @@ program
|
|
|
765
837
|
const localPath = getRepoLocalPath(scope.source);
|
|
766
838
|
const manifest = readManifest(localPath) || createDefaultManifest();
|
|
767
839
|
console.log(chalk.bold('\nExporting local configuration...\n'));
|
|
768
|
-
const cliStates = getAllCliStates();
|
|
840
|
+
const cliStates = await getAllCliStates();
|
|
769
841
|
let exported = 0;
|
|
770
842
|
for (const agentId of ALL_AGENT_IDS) {
|
|
771
843
|
const agent = AGENTS[agentId];
|
|
@@ -872,10 +944,11 @@ commandsCmd
|
|
|
872
944
|
const agents = options.agents
|
|
873
945
|
? options.agents.split(',')
|
|
874
946
|
: ['claude', 'codex', 'gemini'];
|
|
947
|
+
const cliStates = await getAllCliStates();
|
|
875
948
|
for (const command of commands) {
|
|
876
949
|
console.log(`\n ${chalk.cyan(command.name)}: ${command.description}`);
|
|
877
950
|
for (const agentId of agents) {
|
|
878
|
-
if (!
|
|
951
|
+
if (!cliStates[agentId]?.installed && agentId !== 'cursor')
|
|
879
952
|
continue;
|
|
880
953
|
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
881
954
|
if (sourcePath) {
|
|
@@ -918,14 +991,15 @@ commandsCmd
|
|
|
918
991
|
.command('push <name>')
|
|
919
992
|
.description('Save project-scoped command to user scope')
|
|
920
993
|
.option('-a, --agents <list>', 'Comma-separated agents to push for')
|
|
921
|
-
.action((name, options) => {
|
|
994
|
+
.action(async (name, options) => {
|
|
922
995
|
const cwd = process.cwd();
|
|
923
996
|
const agents = options.agents
|
|
924
997
|
? options.agents.split(',')
|
|
925
998
|
: ALL_AGENT_IDS;
|
|
999
|
+
const cliStates = await getAllCliStates();
|
|
926
1000
|
let pushed = 0;
|
|
927
1001
|
for (const agentId of agents) {
|
|
928
|
-
if (!
|
|
1002
|
+
if (!cliStates[agentId]?.installed && agentId !== 'cursor')
|
|
929
1003
|
continue;
|
|
930
1004
|
const result = promoteCommandToUser(agentId, name, cwd);
|
|
931
1005
|
if (result.success) {
|
|
@@ -1185,11 +1259,12 @@ skillsCmd
|
|
|
1185
1259
|
console.log(` ${chalk.gray(`${skill.ruleCount} rules`)}`);
|
|
1186
1260
|
}
|
|
1187
1261
|
}
|
|
1262
|
+
const cliStates = await getAllCliStates();
|
|
1188
1263
|
const agents = options.agents
|
|
1189
1264
|
? options.agents.split(',')
|
|
1190
1265
|
: await checkbox({
|
|
1191
1266
|
message: 'Select agents to install skills to:',
|
|
1192
|
-
choices: SKILLS_CAPABLE_AGENTS.filter((id) =>
|
|
1267
|
+
choices: SKILLS_CAPABLE_AGENTS.filter((id) => cliStates[id]?.installed || id === 'cursor').map((id) => ({
|
|
1193
1268
|
name: AGENTS[id].name,
|
|
1194
1269
|
value: id,
|
|
1195
1270
|
checked: ['claude', 'codex', 'gemini'].includes(id),
|
|
@@ -1368,12 +1443,14 @@ mcpCmd
|
|
|
1368
1443
|
const agents = options.agent
|
|
1369
1444
|
? [options.agent]
|
|
1370
1445
|
: MCP_CAPABLE_AGENTS;
|
|
1446
|
+
// Collect all data while spinner is active
|
|
1447
|
+
const cliStates = await getAllCliStates();
|
|
1371
1448
|
const agentMcps = agents.map((agentId) => {
|
|
1372
1449
|
const agent = AGENTS[agentId];
|
|
1373
1450
|
if (!agent.capabilities.mcp) {
|
|
1374
1451
|
return { agent, mcps: null };
|
|
1375
1452
|
}
|
|
1376
|
-
if (!
|
|
1453
|
+
if (!cliStates[agentId]?.installed) {
|
|
1377
1454
|
return { agent, mcps: null, notInstalled: true };
|
|
1378
1455
|
}
|
|
1379
1456
|
return {
|
|
@@ -1474,15 +1551,16 @@ mcpCmd
|
|
|
1474
1551
|
.command('remove <name>')
|
|
1475
1552
|
.description('Remove MCP server from agents')
|
|
1476
1553
|
.option('-a, --agents <list>', 'Comma-separated agents')
|
|
1477
|
-
.action((name, options) => {
|
|
1554
|
+
.action(async (name, options) => {
|
|
1478
1555
|
const agents = options.agents
|
|
1479
1556
|
? options.agents.split(',')
|
|
1480
1557
|
: MCP_CAPABLE_AGENTS;
|
|
1558
|
+
const cliStates = await getAllCliStates();
|
|
1481
1559
|
let removed = 0;
|
|
1482
1560
|
for (const agentId of agents) {
|
|
1483
|
-
if (!
|
|
1561
|
+
if (!cliStates[agentId]?.installed)
|
|
1484
1562
|
continue;
|
|
1485
|
-
const result = unregisterMcp(agentId, name);
|
|
1563
|
+
const result = await unregisterMcp(agentId, name);
|
|
1486
1564
|
if (result.success) {
|
|
1487
1565
|
console.log(` ${chalk.red('-')} ${AGENTS[agentId].name}`);
|
|
1488
1566
|
removed++;
|
|
@@ -1508,6 +1586,7 @@ mcpCmd
|
|
|
1508
1586
|
console.log(chalk.yellow('No MCP servers in manifest'));
|
|
1509
1587
|
return;
|
|
1510
1588
|
}
|
|
1589
|
+
const cliStates = await getAllCliStates();
|
|
1511
1590
|
for (const [mcpName, config] of Object.entries(manifest.mcp)) {
|
|
1512
1591
|
// Skip HTTP transport MCPs for now (need different registration)
|
|
1513
1592
|
if (config.transport === 'http' || !config.command) {
|
|
@@ -1516,9 +1595,9 @@ mcpCmd
|
|
|
1516
1595
|
}
|
|
1517
1596
|
console.log(`\n ${chalk.cyan(mcpName)}:`);
|
|
1518
1597
|
for (const agentId of config.agents) {
|
|
1519
|
-
if (!
|
|
1598
|
+
if (!cliStates[agentId]?.installed)
|
|
1520
1599
|
continue;
|
|
1521
|
-
const result = registerMcp(agentId, mcpName, config.command, config.scope);
|
|
1600
|
+
const result = await registerMcp(agentId, mcpName, config.command, config.scope);
|
|
1522
1601
|
if (result.success) {
|
|
1523
1602
|
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
1524
1603
|
}
|
|
@@ -1535,16 +1614,17 @@ mcpCmd
|
|
|
1535
1614
|
.command('push <name>')
|
|
1536
1615
|
.description('Save project-scoped MCP to user scope')
|
|
1537
1616
|
.option('-a, --agents <list>', 'Comma-separated agents to push for')
|
|
1538
|
-
.action((name, options) => {
|
|
1617
|
+
.action(async (name, options) => {
|
|
1539
1618
|
const cwd = process.cwd();
|
|
1540
1619
|
const agents = options.agents
|
|
1541
1620
|
? options.agents.split(',')
|
|
1542
1621
|
: MCP_CAPABLE_AGENTS;
|
|
1622
|
+
const cliStates = await getAllCliStates();
|
|
1543
1623
|
let pushed = 0;
|
|
1544
1624
|
for (const agentId of agents) {
|
|
1545
|
-
if (!
|
|
1625
|
+
if (!cliStates[agentId]?.installed)
|
|
1546
1626
|
continue;
|
|
1547
|
-
const result = promoteMcpToUser(agentId, name, cwd);
|
|
1627
|
+
const result = await promoteMcpToUser(agentId, name, cwd);
|
|
1548
1628
|
if (result.success) {
|
|
1549
1629
|
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
1550
1630
|
pushed++;
|
|
@@ -1571,7 +1651,7 @@ cliCmd
|
|
|
1571
1651
|
.description('List installed agent CLIs')
|
|
1572
1652
|
.action(async () => {
|
|
1573
1653
|
const spinner = ora('Checking installed CLIs...').start();
|
|
1574
|
-
const states = getAllCliStates();
|
|
1654
|
+
const states = await getAllCliStates();
|
|
1575
1655
|
spinner.stop();
|
|
1576
1656
|
console.log(chalk.bold('Agent CLIs\n'));
|
|
1577
1657
|
for (const agentId of ALL_AGENT_IDS) {
|
|
@@ -1589,93 +1669,124 @@ cliCmd
|
|
|
1589
1669
|
}
|
|
1590
1670
|
});
|
|
1591
1671
|
cliCmd
|
|
1592
|
-
.command('add <
|
|
1593
|
-
.description('Install agent CLI
|
|
1672
|
+
.command('add <agents...>')
|
|
1673
|
+
.description('Install agent CLI(s)')
|
|
1594
1674
|
.option('-v, --version <version>', 'Version to install', 'latest')
|
|
1595
1675
|
.option('--manifest-only', 'Only add to manifest, do not install')
|
|
1596
|
-
.action(async (
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1676
|
+
.action(async (agents, options) => {
|
|
1677
|
+
const validAgents = [];
|
|
1678
|
+
for (const agent of agents) {
|
|
1679
|
+
const agentId = agent.toLowerCase();
|
|
1680
|
+
if (!AGENTS[agentId]) {
|
|
1681
|
+
console.log(chalk.red(`Unknown agent: ${agent}`));
|
|
1682
|
+
console.log(chalk.gray(`Available: ${ALL_AGENT_IDS.join(', ')}`));
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
validAgents.push(agentId);
|
|
1602
1686
|
}
|
|
1603
|
-
const
|
|
1604
|
-
const
|
|
1687
|
+
const { exec } = await import('child_process');
|
|
1688
|
+
const { promisify } = await import('util');
|
|
1689
|
+
const execAsync = promisify(exec);
|
|
1605
1690
|
const version = options.version;
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
const { execSync } = await import('child_process');
|
|
1613
|
-
const spinner = ora(`Installing ${agentConfig.name}@${version}...`).start();
|
|
1614
|
-
try {
|
|
1615
|
-
execSync(`npm install -g ${pkg}@${version}`, { stdio: 'pipe' });
|
|
1616
|
-
spinner.succeed(`Installed ${agentConfig.name}@${version}`);
|
|
1691
|
+
for (const agentId of validAgents) {
|
|
1692
|
+
const agentConfig = AGENTS[agentId];
|
|
1693
|
+
const pkg = agentConfig.npmPackage;
|
|
1694
|
+
if (!options.manifestOnly) {
|
|
1695
|
+
if (!pkg) {
|
|
1696
|
+
console.log(chalk.yellow(`${agentConfig.name} has no npm package. Install manually.`));
|
|
1617
1697
|
}
|
|
1618
|
-
|
|
1619
|
-
spinner
|
|
1620
|
-
|
|
1621
|
-
|
|
1698
|
+
else {
|
|
1699
|
+
const spinner = ora(`Installing ${agentConfig.name}@${version}...`).start();
|
|
1700
|
+
try {
|
|
1701
|
+
await execAsync(`npm install -g ${pkg}@${version}`);
|
|
1702
|
+
spinner.succeed(`Installed ${agentConfig.name}@${version}`);
|
|
1703
|
+
}
|
|
1704
|
+
catch (err) {
|
|
1705
|
+
spinner.fail(`Failed to install ${agentConfig.name}`);
|
|
1706
|
+
console.error(chalk.gray(err.message));
|
|
1707
|
+
continue;
|
|
1708
|
+
}
|
|
1622
1709
|
}
|
|
1623
1710
|
}
|
|
1624
1711
|
}
|
|
1625
|
-
// Add to manifest
|
|
1626
1712
|
const source = await ensureSource();
|
|
1627
1713
|
const localPath = getRepoLocalPath(source);
|
|
1628
1714
|
const manifest = readManifest(localPath) || createDefaultManifest();
|
|
1629
1715
|
manifest.clis = manifest.clis || {};
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1716
|
+
for (const agentId of validAgents) {
|
|
1717
|
+
const agentConfig = AGENTS[agentId];
|
|
1718
|
+
manifest.clis[agentId] = {
|
|
1719
|
+
package: agentConfig.npmPackage,
|
|
1720
|
+
version: version,
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1634
1723
|
writeManifest(localPath, manifest);
|
|
1635
|
-
|
|
1724
|
+
if (validAgents.length === 1) {
|
|
1725
|
+
console.log(chalk.green(`Added ${AGENTS[validAgents[0]].name} to manifest`));
|
|
1726
|
+
}
|
|
1727
|
+
else {
|
|
1728
|
+
console.log(chalk.green(`Added ${validAgents.length} agents to manifest`));
|
|
1729
|
+
}
|
|
1636
1730
|
});
|
|
1637
1731
|
cliCmd
|
|
1638
|
-
.command('remove <
|
|
1639
|
-
.description('Uninstall agent CLI
|
|
1732
|
+
.command('remove <agents...>')
|
|
1733
|
+
.description('Uninstall agent CLI(s)')
|
|
1640
1734
|
.option('--manifest-only', 'Only remove from manifest, do not uninstall')
|
|
1641
|
-
.action(async (
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
const pkg = agentConfig.npmPackage;
|
|
1650
|
-
// Uninstall the CLI
|
|
1651
|
-
if (!options.manifestOnly) {
|
|
1652
|
-
if (!pkg) {
|
|
1653
|
-
console.log(chalk.yellow(`${agentConfig.name} has no npm package.`));
|
|
1654
|
-
}
|
|
1655
|
-
else if (!isCliInstalled(agentId)) {
|
|
1656
|
-
console.log(chalk.gray(`${agentConfig.name} is not installed`));
|
|
1735
|
+
.action(async (agents, options) => {
|
|
1736
|
+
const validAgents = [];
|
|
1737
|
+
for (const agent of agents) {
|
|
1738
|
+
const agentId = agent.toLowerCase();
|
|
1739
|
+
if (!AGENTS[agentId]) {
|
|
1740
|
+
console.log(chalk.red(`Unknown agent: ${agent}`));
|
|
1741
|
+
console.log(chalk.gray(`Available: ${ALL_AGENT_IDS.join(', ')}`));
|
|
1742
|
+
return;
|
|
1657
1743
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1744
|
+
validAgents.push(agentId);
|
|
1745
|
+
}
|
|
1746
|
+
const { exec } = await import('child_process');
|
|
1747
|
+
const { promisify } = await import('util');
|
|
1748
|
+
const execAsync = promisify(exec);
|
|
1749
|
+
for (const agentId of validAgents) {
|
|
1750
|
+
const agentConfig = AGENTS[agentId];
|
|
1751
|
+
const pkg = agentConfig.npmPackage;
|
|
1752
|
+
if (!options.manifestOnly) {
|
|
1753
|
+
if (!pkg) {
|
|
1754
|
+
console.log(chalk.yellow(`${agentConfig.name} has no npm package.`));
|
|
1664
1755
|
}
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1756
|
+
else if (!(await isCliInstalled(agentId))) {
|
|
1757
|
+
console.log(chalk.gray(`${agentConfig.name} is not installed`));
|
|
1758
|
+
}
|
|
1759
|
+
else {
|
|
1760
|
+
const spinner = ora(`Uninstalling ${agentConfig.name}...`).start();
|
|
1761
|
+
try {
|
|
1762
|
+
await execAsync(`npm uninstall -g ${pkg}`);
|
|
1763
|
+
spinner.succeed(`Uninstalled ${agentConfig.name}`);
|
|
1764
|
+
}
|
|
1765
|
+
catch (err) {
|
|
1766
|
+
spinner.fail(`Failed to uninstall ${agentConfig.name}`);
|
|
1767
|
+
console.error(chalk.gray(err.message));
|
|
1768
|
+
}
|
|
1668
1769
|
}
|
|
1669
1770
|
}
|
|
1670
1771
|
}
|
|
1671
|
-
// Remove from manifest
|
|
1672
1772
|
const source = await ensureSource();
|
|
1673
1773
|
const localPath = getRepoLocalPath(source);
|
|
1674
1774
|
const manifest = readManifest(localPath);
|
|
1675
|
-
|
|
1676
|
-
|
|
1775
|
+
let removed = 0;
|
|
1776
|
+
for (const agentId of validAgents) {
|
|
1777
|
+
if (manifest?.clis?.[agentId]) {
|
|
1778
|
+
delete manifest.clis[agentId];
|
|
1779
|
+
removed++;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (removed > 0 && manifest) {
|
|
1677
1783
|
writeManifest(localPath, manifest);
|
|
1678
|
-
|
|
1784
|
+
if (removed === 1) {
|
|
1785
|
+
console.log(chalk.green(`Removed ${AGENTS[validAgents[0]].name} from manifest`));
|
|
1786
|
+
}
|
|
1787
|
+
else {
|
|
1788
|
+
console.log(chalk.green(`Removed ${removed} agents from manifest`));
|
|
1789
|
+
}
|
|
1679
1790
|
}
|
|
1680
1791
|
});
|
|
1681
1792
|
cliCmd
|
|
@@ -1695,7 +1806,9 @@ cliCmd
|
|
|
1695
1806
|
console.log(chalk.yellow('No CLIs to upgrade. Add CLIs to manifest or use --latest'));
|
|
1696
1807
|
return;
|
|
1697
1808
|
}
|
|
1698
|
-
const {
|
|
1809
|
+
const { exec } = await import('child_process');
|
|
1810
|
+
const { promisify } = await import('util');
|
|
1811
|
+
const execAsync = promisify(exec);
|
|
1699
1812
|
for (const agentId of agentsToUpgrade) {
|
|
1700
1813
|
const agentConfig = AGENTS[agentId];
|
|
1701
1814
|
if (!agentConfig) {
|
|
@@ -1707,7 +1820,7 @@ cliCmd
|
|
|
1707
1820
|
const pkg = cliConfig?.package || agentConfig.npmPackage;
|
|
1708
1821
|
const spinner = ora(`Upgrading ${agentConfig.name} to ${version}...`).start();
|
|
1709
1822
|
try {
|
|
1710
|
-
|
|
1823
|
+
await execAsync(`npm install -g ${pkg}@${version}`);
|
|
1711
1824
|
spinner.succeed(`${agentConfig.name} upgraded to ${version}`);
|
|
1712
1825
|
}
|
|
1713
1826
|
catch (err) {
|
|
@@ -2122,18 +2235,19 @@ program
|
|
|
2122
2235
|
else {
|
|
2123
2236
|
command = pkg.name || pkg.registry_name;
|
|
2124
2237
|
}
|
|
2238
|
+
const cliStates = await getAllCliStates();
|
|
2125
2239
|
const agents = options.agents
|
|
2126
2240
|
? options.agents.split(',')
|
|
2127
|
-
: MCP_CAPABLE_AGENTS.filter((id) =>
|
|
2241
|
+
: MCP_CAPABLE_AGENTS.filter((id) => cliStates[id]?.installed);
|
|
2128
2242
|
if (agents.length === 0) {
|
|
2129
2243
|
console.log(chalk.yellow('\nNo MCP-capable agents installed.'));
|
|
2130
2244
|
process.exit(1);
|
|
2131
2245
|
}
|
|
2132
2246
|
console.log(chalk.bold('\nInstalling to agents...'));
|
|
2133
2247
|
for (const agentId of agents) {
|
|
2134
|
-
if (!
|
|
2248
|
+
if (!cliStates[agentId]?.installed)
|
|
2135
2249
|
continue;
|
|
2136
|
-
const result = registerMcp(agentId, entry.name, command, 'user');
|
|
2250
|
+
const result = await registerMcp(agentId, entry.name, command, 'user');
|
|
2137
2251
|
if (result.success) {
|
|
2138
2252
|
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
2139
2253
|
}
|
|
@@ -2168,13 +2282,14 @@ program
|
|
|
2168
2282
|
const agents = options.agents
|
|
2169
2283
|
? options.agents.split(',')
|
|
2170
2284
|
: ['claude', 'codex', 'gemini'];
|
|
2285
|
+
const gitCliStates = await getAllCliStates();
|
|
2171
2286
|
// Install commands
|
|
2172
2287
|
if (hasCommands) {
|
|
2173
2288
|
console.log(chalk.bold('\nInstalling commands...'));
|
|
2174
2289
|
let installed = 0;
|
|
2175
2290
|
for (const command of commands) {
|
|
2176
2291
|
for (const agentId of agents) {
|
|
2177
|
-
if (!
|
|
2292
|
+
if (!gitCliStates[agentId]?.installed && agentId !== 'cursor')
|
|
2178
2293
|
continue;
|
|
2179
2294
|
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
2180
2295
|
if (sourcePath) {
|
|
@@ -2236,7 +2351,9 @@ program
|
|
|
2236
2351
|
}
|
|
2237
2352
|
spinner.text = `Upgrading to ${latestVersion}...`;
|
|
2238
2353
|
// Detect package manager
|
|
2239
|
-
const { execSync } = await import('child_process');
|
|
2354
|
+
const { execSync, exec } = await import('child_process');
|
|
2355
|
+
const { promisify } = await import('util');
|
|
2356
|
+
const execAsync = promisify(exec);
|
|
2240
2357
|
let cmd;
|
|
2241
2358
|
// Check if installed globally via npm, bun, or other
|
|
2242
2359
|
try {
|
|
@@ -2263,8 +2380,8 @@ program
|
|
|
2263
2380
|
cmd = 'npm install -g @swarmify/agents-cli@latest';
|
|
2264
2381
|
}
|
|
2265
2382
|
}
|
|
2266
|
-
// Run silently (suppress npm/bun output)
|
|
2267
|
-
|
|
2383
|
+
// Run silently (suppress npm/bun output) - use async to allow spinner to animate
|
|
2384
|
+
await execAsync(cmd);
|
|
2268
2385
|
spinner.succeed(`Upgraded to ${latestVersion}`);
|
|
2269
2386
|
// Show what's new from changelog
|
|
2270
2387
|
await showWhatsNew(currentVersion, latestVersion);
|