@swarmify/agents-cli 1.5.6 → 1.5.8
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/CHANGELOG.md +2 -2
- package/README.md +6 -1
- package/dist/index.js +229 -113
- 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
|
}
|
|
@@ -133,14 +135,11 @@ program.hook('preAction', async () => {
|
|
|
133
135
|
program
|
|
134
136
|
.command('status [agent]')
|
|
135
137
|
.description('Show sync status, CLI versions, installed commands and MCP servers')
|
|
136
|
-
.action((agentFilter) => {
|
|
137
|
-
const
|
|
138
|
-
const cliStates = getAllCliStates();
|
|
139
|
-
const cwd = process.cwd();
|
|
138
|
+
.action(async (agentFilter) => {
|
|
139
|
+
const spinner = ora({ text: 'Loading...', isSilent: !process.stdout.isTTY }).start();
|
|
140
140
|
// Resolve agent filter to AgentId
|
|
141
141
|
let filterAgentId;
|
|
142
142
|
if (agentFilter) {
|
|
143
|
-
// Map common names to AgentId
|
|
144
143
|
const agentMap = {
|
|
145
144
|
claude: 'claude',
|
|
146
145
|
'claude-code': 'claude',
|
|
@@ -151,11 +150,14 @@ program
|
|
|
151
150
|
};
|
|
152
151
|
filterAgentId = agentMap[agentFilter.toLowerCase()];
|
|
153
152
|
if (!filterAgentId) {
|
|
153
|
+
spinner.stop();
|
|
154
154
|
console.log(chalk.red(`Unknown agent: ${agentFilter}`));
|
|
155
155
|
console.log(chalk.gray(`Valid agents: claude, codex, gemini, cursor, opencode`));
|
|
156
156
|
process.exit(1);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
+
const cwd = process.cwd();
|
|
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)
|
|
@@ -163,6 +165,22 @@ program
|
|
|
163
165
|
const mcpAgentsToShow = filterAgentId
|
|
164
166
|
? MCP_CAPABLE_AGENTS.filter((id) => id === filterAgentId)
|
|
165
167
|
: MCP_CAPABLE_AGENTS;
|
|
168
|
+
// Collect all data while spinner is active
|
|
169
|
+
const commandsData = agentsToShow.map((agentId) => ({
|
|
170
|
+
agent: AGENTS[agentId],
|
|
171
|
+
commands: listInstalledCommandsWithScope(agentId, cwd),
|
|
172
|
+
}));
|
|
173
|
+
const skillsData = skillAgentsToShow.map((agentId) => ({
|
|
174
|
+
agent: AGENTS[agentId],
|
|
175
|
+
skills: listInstalledSkillsWithScope(agentId, cwd),
|
|
176
|
+
}));
|
|
177
|
+
const installedMcpAgents = mcpAgentsToShow.filter((agentId) => cliStates[agentId]?.installed);
|
|
178
|
+
const mcpsData = installedMcpAgents.map((agentId) => ({
|
|
179
|
+
agent: AGENTS[agentId],
|
|
180
|
+
mcps: listInstalledMcpsWithScope(agentId, cwd),
|
|
181
|
+
}));
|
|
182
|
+
const scopes = filterAgentId ? [] : getScopesByPriority();
|
|
183
|
+
spinner.stop();
|
|
166
184
|
// Helper to format MCP with version
|
|
167
185
|
const formatMcp = (m, color) => {
|
|
168
186
|
return m.version ? color(`${m.name}@${m.version}`) : color(m.name);
|
|
@@ -177,9 +195,7 @@ program
|
|
|
177
195
|
console.log(` ${agent.name.padEnd(14)} ${status}`);
|
|
178
196
|
}
|
|
179
197
|
console.log(chalk.bold('\nInstalled Commands\n'));
|
|
180
|
-
for (const
|
|
181
|
-
const agent = AGENTS[agentId];
|
|
182
|
-
const commands = listInstalledCommandsWithScope(agentId, cwd);
|
|
198
|
+
for (const { agent, commands } of commandsData) {
|
|
183
199
|
const userCommands = commands.filter((c) => c.scope === 'user');
|
|
184
200
|
const projectCommands = commands.filter((c) => c.scope === 'project');
|
|
185
201
|
if (commands.length === 0) {
|
|
@@ -195,11 +211,9 @@ program
|
|
|
195
211
|
}
|
|
196
212
|
}
|
|
197
213
|
}
|
|
198
|
-
if (
|
|
214
|
+
if (skillsData.length > 0) {
|
|
199
215
|
console.log(chalk.bold('\nInstalled Skills\n'));
|
|
200
|
-
for (const
|
|
201
|
-
const agent = AGENTS[agentId];
|
|
202
|
-
const skills = listInstalledSkillsWithScope(agentId, cwd);
|
|
216
|
+
for (const { agent, skills } of skillsData) {
|
|
203
217
|
const userSkills = skills.filter((s) => s.scope === 'user');
|
|
204
218
|
const projectSkills = skills.filter((s) => s.scope === 'project');
|
|
205
219
|
if (skills.length === 0) {
|
|
@@ -216,13 +230,9 @@ program
|
|
|
216
230
|
}
|
|
217
231
|
}
|
|
218
232
|
}
|
|
219
|
-
if (
|
|
233
|
+
if (mcpsData.length > 0) {
|
|
220
234
|
console.log(chalk.bold('\nInstalled MCP Servers\n'));
|
|
221
|
-
for (const
|
|
222
|
-
const agent = AGENTS[agentId];
|
|
223
|
-
if (!isCliInstalled(agentId))
|
|
224
|
-
continue;
|
|
225
|
-
const mcps = listInstalledMcpsWithScope(agentId, cwd);
|
|
235
|
+
for (const { agent, mcps } of mcpsData) {
|
|
226
236
|
const userMcps = mcps.filter((m) => m.scope === 'user');
|
|
227
237
|
const projectMcps = mcps.filter((m) => m.scope === 'project');
|
|
228
238
|
if (mcps.length === 0) {
|
|
@@ -241,7 +251,6 @@ program
|
|
|
241
251
|
}
|
|
242
252
|
// Only show scopes when not filtering by agent
|
|
243
253
|
if (!filterAgentId) {
|
|
244
|
-
const scopes = getScopesByPriority();
|
|
245
254
|
if (scopes.length > 0) {
|
|
246
255
|
console.log(chalk.bold('\nConfigured Scopes\n'));
|
|
247
256
|
for (const { name, config } of scopes) {
|
|
@@ -258,7 +267,6 @@ program
|
|
|
258
267
|
console.log(chalk.gray(' Run: agents repo add <source>'));
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
|
-
console.log();
|
|
262
270
|
});
|
|
263
271
|
// =============================================================================
|
|
264
272
|
// PULL COMMAND
|
|
@@ -357,6 +365,7 @@ program
|
|
|
357
365
|
const allSkills = discoverSkillsFromRepo(localPath);
|
|
358
366
|
const discoveredHooks = discoverHooksFromRepo(localPath);
|
|
359
367
|
// Determine which agents to sync
|
|
368
|
+
const cliStates = await getAllCliStates();
|
|
360
369
|
let selectedAgents;
|
|
361
370
|
if (agentFilter) {
|
|
362
371
|
// Single agent filter
|
|
@@ -367,7 +376,7 @@ program
|
|
|
367
376
|
selectedAgents = (manifest?.defaults?.agents || ['claude', 'codex', 'gemini']);
|
|
368
377
|
}
|
|
369
378
|
else {
|
|
370
|
-
const installedAgents = ALL_AGENT_IDS.filter((id) =>
|
|
379
|
+
const installedAgents = ALL_AGENT_IDS.filter((id) => cliStates[id]?.installed || id === 'cursor');
|
|
371
380
|
selectedAgents = await checkbox({
|
|
372
381
|
message: 'Select agents to sync:',
|
|
373
382
|
choices: installedAgents.map((id) => ({
|
|
@@ -378,7 +387,7 @@ program
|
|
|
378
387
|
});
|
|
379
388
|
}
|
|
380
389
|
// Filter agents to only installed ones (plus cursor which doesn't need CLI)
|
|
381
|
-
selectedAgents = selectedAgents.filter((id) =>
|
|
390
|
+
selectedAgents = selectedAgents.filter((id) => cliStates[id]?.installed || id === 'cursor');
|
|
382
391
|
if (selectedAgents.length === 0) {
|
|
383
392
|
console.log(chalk.yellow('\nNo agents selected or installed. Nothing to sync.'));
|
|
384
393
|
return;
|
|
@@ -386,6 +395,7 @@ program
|
|
|
386
395
|
// Build resource items with conflict detection
|
|
387
396
|
const newItems = [];
|
|
388
397
|
const existingItems = [];
|
|
398
|
+
const upToDateItems = [];
|
|
389
399
|
// Process commands
|
|
390
400
|
for (const command of allCommands) {
|
|
391
401
|
const applicableAgents = selectedAgents.filter((agentId) => {
|
|
@@ -394,29 +404,55 @@ program
|
|
|
394
404
|
});
|
|
395
405
|
if (applicableAgents.length === 0)
|
|
396
406
|
continue;
|
|
397
|
-
const conflictingAgents = applicableAgents.filter((agentId) => commandExists(agentId, command.name));
|
|
398
407
|
const newAgents = applicableAgents.filter((agentId) => !commandExists(agentId, command.name));
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
+
});
|
|
402
420
|
if (newAgents.length > 0) {
|
|
403
421
|
newItems.push({ type: 'command', name: command.name, agents: newAgents, isNew: true });
|
|
404
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
|
+
}
|
|
405
429
|
}
|
|
406
430
|
// Process skills
|
|
407
431
|
const skillAgents = SKILLS_CAPABLE_AGENTS.filter((id) => selectedAgents.includes(id));
|
|
408
432
|
for (const skill of allSkills) {
|
|
409
|
-
const conflictingAgents = skillAgents.filter((agentId) => skillExists(agentId, skill.name));
|
|
410
433
|
const newAgents = skillAgents.filter((agentId) => !skillExists(agentId, skill.name));
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
+
});
|
|
414
444
|
if (newAgents.length > 0) {
|
|
415
445
|
newItems.push({ type: 'skill', name: skill.name, agents: newAgents, isNew: true });
|
|
416
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
|
+
}
|
|
417
453
|
}
|
|
418
454
|
// Process hooks
|
|
419
|
-
const hookAgents = selectedAgents.filter((id) => HOOKS_CAPABLE_AGENTS.includes(id) &&
|
|
455
|
+
const hookAgents = selectedAgents.filter((id) => HOOKS_CAPABLE_AGENTS.includes(id) && cliStates[id]?.installed);
|
|
420
456
|
const allHookNames = [
|
|
421
457
|
...discoveredHooks.shared,
|
|
422
458
|
...Object.entries(discoveredHooks.agentSpecific)
|
|
@@ -425,25 +461,43 @@ program
|
|
|
425
461
|
];
|
|
426
462
|
const uniqueHookNames = [...new Set(allHookNames)];
|
|
427
463
|
for (const hookName of uniqueHookNames) {
|
|
428
|
-
const conflictingAgents = hookAgents.filter((agentId) => hookExists(agentId, hookName));
|
|
429
464
|
const newAgents = hookAgents.filter((agentId) => !hookExists(agentId, hookName));
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
+
});
|
|
433
477
|
if (newAgents.length > 0) {
|
|
434
478
|
newItems.push({ type: 'hook', name: hookName, agents: newAgents, isNew: true });
|
|
435
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
|
+
}
|
|
436
486
|
}
|
|
437
|
-
// Process MCPs
|
|
487
|
+
// Process MCPs (no content comparison - just existence check)
|
|
438
488
|
if (!options.skipMcp && manifest?.mcp) {
|
|
439
489
|
for (const [name, config] of Object.entries(manifest.mcp)) {
|
|
440
490
|
if (config.transport === 'http' || !config.command)
|
|
441
491
|
continue;
|
|
442
|
-
const mcpAgents = config.agents.filter((agentId) => selectedAgents.includes(agentId) &&
|
|
492
|
+
const mcpAgents = config.agents.filter((agentId) => selectedAgents.includes(agentId) && cliStates[agentId]?.installed);
|
|
443
493
|
if (mcpAgents.length === 0)
|
|
444
494
|
continue;
|
|
445
|
-
const
|
|
446
|
-
|
|
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);
|
|
447
501
|
if (conflictingAgents.length > 0) {
|
|
448
502
|
existingItems.push({ type: 'mcp', name, agents: conflictingAgents, isNew: false });
|
|
449
503
|
}
|
|
@@ -486,6 +540,31 @@ program
|
|
|
486
540
|
}
|
|
487
541
|
console.log();
|
|
488
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
|
+
}
|
|
489
568
|
if (existingItems.length > 0) {
|
|
490
569
|
console.log(chalk.yellow(' EXISTING (conflicts):\n'));
|
|
491
570
|
const byType = { command: [], skill: [], hook: [], mcp: [] };
|
|
@@ -518,7 +597,7 @@ program
|
|
|
518
597
|
console.log();
|
|
519
598
|
}
|
|
520
599
|
if (newItems.length === 0 && existingItems.length === 0) {
|
|
521
|
-
console.log(chalk.gray('
|
|
600
|
+
console.log(chalk.gray(' Already up to date.\n'));
|
|
522
601
|
return;
|
|
523
602
|
}
|
|
524
603
|
if (options.dryRun) {
|
|
@@ -663,9 +742,9 @@ program
|
|
|
663
742
|
continue;
|
|
664
743
|
for (const agentId of item.agents) {
|
|
665
744
|
if (!item.isNew) {
|
|
666
|
-
unregisterMcp(agentId, item.name);
|
|
745
|
+
await unregisterMcp(agentId, item.name);
|
|
667
746
|
}
|
|
668
|
-
const result = registerMcp(agentId, item.name, config.command, config.scope);
|
|
747
|
+
const result = await registerMcp(agentId, item.name, config.command, config.scope);
|
|
669
748
|
if (result.success)
|
|
670
749
|
installed.mcps++;
|
|
671
750
|
}
|
|
@@ -691,7 +770,7 @@ program
|
|
|
691
770
|
const agent = AGENTS[agentId];
|
|
692
771
|
if (!agent || !cliConfig.package)
|
|
693
772
|
continue;
|
|
694
|
-
const currentVersion = getCliVersion(agentId);
|
|
773
|
+
const currentVersion = await getCliVersion(agentId);
|
|
695
774
|
const targetVersion = cliConfig.version;
|
|
696
775
|
if (currentVersion === targetVersion)
|
|
697
776
|
continue;
|
|
@@ -758,7 +837,7 @@ program
|
|
|
758
837
|
const localPath = getRepoLocalPath(scope.source);
|
|
759
838
|
const manifest = readManifest(localPath) || createDefaultManifest();
|
|
760
839
|
console.log(chalk.bold('\nExporting local configuration...\n'));
|
|
761
|
-
const cliStates = getAllCliStates();
|
|
840
|
+
const cliStates = await getAllCliStates();
|
|
762
841
|
let exported = 0;
|
|
763
842
|
for (const agentId of ALL_AGENT_IDS) {
|
|
764
843
|
const agent = AGENTS[agentId];
|
|
@@ -813,18 +892,20 @@ commandsCmd
|
|
|
813
892
|
.description('List installed commands')
|
|
814
893
|
.option('-a, --agent <agent>', 'Filter by agent')
|
|
815
894
|
.option('-s, --scope <scope>', 'Filter by scope: user, project, or all', 'all')
|
|
816
|
-
.action((options) => {
|
|
817
|
-
|
|
895
|
+
.action(async (options) => {
|
|
896
|
+
const spinner = ora({ text: 'Loading...', isSilent: !process.stdout.isTTY }).start();
|
|
818
897
|
const cwd = process.cwd();
|
|
819
898
|
const agents = options.agent
|
|
820
899
|
? [options.agent]
|
|
821
900
|
: ALL_AGENT_IDS;
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
901
|
+
// Collect all data while spinner is active
|
|
902
|
+
const agentCommands = agents.map((agentId) => ({
|
|
903
|
+
agent: AGENTS[agentId],
|
|
904
|
+
commands: listInstalledCommandsWithScope(agentId, cwd).filter((c) => options.scope === 'all' || c.scope === options.scope),
|
|
905
|
+
}));
|
|
906
|
+
spinner.stop();
|
|
907
|
+
console.log(chalk.bold('Installed Commands\n'));
|
|
908
|
+
for (const { agent, commands } of agentCommands) {
|
|
828
909
|
if (commands.length === 0) {
|
|
829
910
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('none')}`);
|
|
830
911
|
}
|
|
@@ -863,10 +944,11 @@ commandsCmd
|
|
|
863
944
|
const agents = options.agents
|
|
864
945
|
? options.agents.split(',')
|
|
865
946
|
: ['claude', 'codex', 'gemini'];
|
|
947
|
+
const cliStates = await getAllCliStates();
|
|
866
948
|
for (const command of commands) {
|
|
867
949
|
console.log(`\n ${chalk.cyan(command.name)}: ${command.description}`);
|
|
868
950
|
for (const agentId of agents) {
|
|
869
|
-
if (!
|
|
951
|
+
if (!cliStates[agentId]?.installed && agentId !== 'cursor')
|
|
870
952
|
continue;
|
|
871
953
|
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
872
954
|
if (sourcePath) {
|
|
@@ -909,14 +991,15 @@ commandsCmd
|
|
|
909
991
|
.command('push <name>')
|
|
910
992
|
.description('Save project-scoped command to user scope')
|
|
911
993
|
.option('-a, --agents <list>', 'Comma-separated agents to push for')
|
|
912
|
-
.action((name, options) => {
|
|
994
|
+
.action(async (name, options) => {
|
|
913
995
|
const cwd = process.cwd();
|
|
914
996
|
const agents = options.agents
|
|
915
997
|
? options.agents.split(',')
|
|
916
998
|
: ALL_AGENT_IDS;
|
|
999
|
+
const cliStates = await getAllCliStates();
|
|
917
1000
|
let pushed = 0;
|
|
918
1001
|
for (const agentId of agents) {
|
|
919
|
-
if (!
|
|
1002
|
+
if (!cliStates[agentId]?.installed && agentId !== 'cursor')
|
|
920
1003
|
continue;
|
|
921
1004
|
const result = promoteCommandToUser(agentId, name, cwd);
|
|
922
1005
|
if (result.success) {
|
|
@@ -940,23 +1023,27 @@ hooksCmd
|
|
|
940
1023
|
.description('List installed hooks')
|
|
941
1024
|
.option('-a, --agent <agent>', 'Filter by agent')
|
|
942
1025
|
.option('-s, --scope <scope>', 'Filter by scope: user, project, or all', 'all')
|
|
943
|
-
.action((options) => {
|
|
944
|
-
|
|
1026
|
+
.action(async (options) => {
|
|
1027
|
+
const spinner = ora({ text: 'Loading...', isSilent: !process.stdout.isTTY }).start();
|
|
945
1028
|
const cwd = process.cwd();
|
|
946
1029
|
const agents = options.agent
|
|
947
1030
|
? [options.agent]
|
|
948
1031
|
: Array.from(HOOKS_CAPABLE_AGENTS);
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1032
|
+
// Collect all data while spinner is active
|
|
1033
|
+
const agentHooks = agents.map((agentId) => ({
|
|
1034
|
+
agent: AGENTS[agentId],
|
|
1035
|
+
hooks: AGENTS[agentId].supportsHooks
|
|
1036
|
+
? listInstalledHooksWithScope(agentId, cwd).filter((h) => options.scope === 'all' || h.scope === options.scope)
|
|
1037
|
+
: null,
|
|
1038
|
+
}));
|
|
1039
|
+
spinner.stop();
|
|
1040
|
+
console.log(chalk.bold('Installed Hooks\n'));
|
|
1041
|
+
for (const { agent, hooks } of agentHooks) {
|
|
1042
|
+
if (hooks === null) {
|
|
952
1043
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('hooks not supported')}`);
|
|
953
1044
|
console.log();
|
|
954
1045
|
continue;
|
|
955
1046
|
}
|
|
956
|
-
let hooks = listInstalledHooksWithScope(agentId, cwd);
|
|
957
|
-
if (options.scope !== 'all') {
|
|
958
|
-
hooks = hooks.filter((h) => h.scope === options.scope);
|
|
959
|
-
}
|
|
960
1047
|
if (hooks.length === 0) {
|
|
961
1048
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('none')}`);
|
|
962
1049
|
}
|
|
@@ -1104,23 +1191,27 @@ skillsCmd
|
|
|
1104
1191
|
.description('List installed Agent Skills')
|
|
1105
1192
|
.option('-a, --agent <agent>', 'Filter by agent')
|
|
1106
1193
|
.option('-s, --scope <scope>', 'Filter by scope: user, project, or all', 'all')
|
|
1107
|
-
.action((options) => {
|
|
1108
|
-
|
|
1194
|
+
.action(async (options) => {
|
|
1195
|
+
const spinner = ora({ text: 'Loading...', isSilent: !process.stdout.isTTY }).start();
|
|
1109
1196
|
const cwd = process.cwd();
|
|
1110
1197
|
const agents = options.agent
|
|
1111
1198
|
? [options.agent]
|
|
1112
1199
|
: SKILLS_CAPABLE_AGENTS;
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1200
|
+
// Collect all data while spinner is active
|
|
1201
|
+
const agentSkills = agents.map((agentId) => ({
|
|
1202
|
+
agent: AGENTS[agentId],
|
|
1203
|
+
skills: AGENTS[agentId].capabilities.skills
|
|
1204
|
+
? listInstalledSkillsWithScope(agentId, cwd).filter((s) => options.scope === 'all' || s.scope === options.scope)
|
|
1205
|
+
: null,
|
|
1206
|
+
}));
|
|
1207
|
+
spinner.stop();
|
|
1208
|
+
console.log(chalk.bold('Installed Agent Skills\n'));
|
|
1209
|
+
for (const { agent, skills } of agentSkills) {
|
|
1210
|
+
if (skills === null) {
|
|
1116
1211
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('skills not supported')}`);
|
|
1117
1212
|
console.log();
|
|
1118
1213
|
continue;
|
|
1119
1214
|
}
|
|
1120
|
-
let skills = listInstalledSkillsWithScope(agentId, cwd);
|
|
1121
|
-
if (options.scope !== 'all') {
|
|
1122
|
-
skills = skills.filter((s) => s.scope === options.scope);
|
|
1123
|
-
}
|
|
1124
1215
|
if (skills.length === 0) {
|
|
1125
1216
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('none')}`);
|
|
1126
1217
|
}
|
|
@@ -1168,11 +1259,12 @@ skillsCmd
|
|
|
1168
1259
|
console.log(` ${chalk.gray(`${skill.ruleCount} rules`)}`);
|
|
1169
1260
|
}
|
|
1170
1261
|
}
|
|
1262
|
+
const cliStates = await getAllCliStates();
|
|
1171
1263
|
const agents = options.agents
|
|
1172
1264
|
? options.agents.split(',')
|
|
1173
1265
|
: await checkbox({
|
|
1174
1266
|
message: 'Select agents to install skills to:',
|
|
1175
|
-
choices: SKILLS_CAPABLE_AGENTS.filter((id) =>
|
|
1267
|
+
choices: SKILLS_CAPABLE_AGENTS.filter((id) => cliStates[id]?.installed || id === 'cursor').map((id) => ({
|
|
1176
1268
|
name: AGENTS[id].name,
|
|
1177
1269
|
value: id,
|
|
1178
1270
|
checked: ['claude', 'codex', 'gemini'].includes(id),
|
|
@@ -1345,26 +1437,38 @@ mcpCmd
|
|
|
1345
1437
|
.description('List MCP servers and registration status')
|
|
1346
1438
|
.option('-a, --agent <agent>', 'Filter by agent')
|
|
1347
1439
|
.option('-s, --scope <scope>', 'Filter by scope: user, project, or all', 'all')
|
|
1348
|
-
.action((options) => {
|
|
1349
|
-
|
|
1440
|
+
.action(async (options) => {
|
|
1441
|
+
const spinner = ora({ text: 'Loading...', isSilent: !process.stdout.isTTY }).start();
|
|
1350
1442
|
const cwd = process.cwd();
|
|
1351
1443
|
const agents = options.agent
|
|
1352
1444
|
? [options.agent]
|
|
1353
1445
|
: MCP_CAPABLE_AGENTS;
|
|
1354
|
-
|
|
1446
|
+
// Collect all data while spinner is active
|
|
1447
|
+
const cliStates = await getAllCliStates();
|
|
1448
|
+
const agentMcps = agents.map((agentId) => {
|
|
1355
1449
|
const agent = AGENTS[agentId];
|
|
1356
1450
|
if (!agent.capabilities.mcp) {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1451
|
+
return { agent, mcps: null };
|
|
1452
|
+
}
|
|
1453
|
+
if (!cliStates[agentId]?.installed) {
|
|
1454
|
+
return { agent, mcps: null, notInstalled: true };
|
|
1360
1455
|
}
|
|
1361
|
-
|
|
1456
|
+
return {
|
|
1457
|
+
agent,
|
|
1458
|
+
mcps: listInstalledMcpsWithScope(agentId, cwd).filter((m) => options.scope === 'all' || m.scope === options.scope),
|
|
1459
|
+
};
|
|
1460
|
+
});
|
|
1461
|
+
spinner.stop();
|
|
1462
|
+
console.log(chalk.bold('MCP Servers\n'));
|
|
1463
|
+
for (const { agent, mcps, notInstalled } of agentMcps) {
|
|
1464
|
+
if (mcps === null && notInstalled) {
|
|
1362
1465
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('CLI not installed')}`);
|
|
1363
1466
|
continue;
|
|
1364
1467
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1468
|
+
if (mcps === null) {
|
|
1469
|
+
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('mcp not supported')}`);
|
|
1470
|
+
console.log();
|
|
1471
|
+
continue;
|
|
1368
1472
|
}
|
|
1369
1473
|
if (mcps.length === 0) {
|
|
1370
1474
|
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('none')}`);
|
|
@@ -1447,15 +1551,16 @@ mcpCmd
|
|
|
1447
1551
|
.command('remove <name>')
|
|
1448
1552
|
.description('Remove MCP server from agents')
|
|
1449
1553
|
.option('-a, --agents <list>', 'Comma-separated agents')
|
|
1450
|
-
.action((name, options) => {
|
|
1554
|
+
.action(async (name, options) => {
|
|
1451
1555
|
const agents = options.agents
|
|
1452
1556
|
? options.agents.split(',')
|
|
1453
1557
|
: MCP_CAPABLE_AGENTS;
|
|
1558
|
+
const cliStates = await getAllCliStates();
|
|
1454
1559
|
let removed = 0;
|
|
1455
1560
|
for (const agentId of agents) {
|
|
1456
|
-
if (!
|
|
1561
|
+
if (!cliStates[agentId]?.installed)
|
|
1457
1562
|
continue;
|
|
1458
|
-
const result = unregisterMcp(agentId, name);
|
|
1563
|
+
const result = await unregisterMcp(agentId, name);
|
|
1459
1564
|
if (result.success) {
|
|
1460
1565
|
console.log(` ${chalk.red('-')} ${AGENTS[agentId].name}`);
|
|
1461
1566
|
removed++;
|
|
@@ -1481,6 +1586,7 @@ mcpCmd
|
|
|
1481
1586
|
console.log(chalk.yellow('No MCP servers in manifest'));
|
|
1482
1587
|
return;
|
|
1483
1588
|
}
|
|
1589
|
+
const cliStates = await getAllCliStates();
|
|
1484
1590
|
for (const [mcpName, config] of Object.entries(manifest.mcp)) {
|
|
1485
1591
|
// Skip HTTP transport MCPs for now (need different registration)
|
|
1486
1592
|
if (config.transport === 'http' || !config.command) {
|
|
@@ -1489,9 +1595,9 @@ mcpCmd
|
|
|
1489
1595
|
}
|
|
1490
1596
|
console.log(`\n ${chalk.cyan(mcpName)}:`);
|
|
1491
1597
|
for (const agentId of config.agents) {
|
|
1492
|
-
if (!
|
|
1598
|
+
if (!cliStates[agentId]?.installed)
|
|
1493
1599
|
continue;
|
|
1494
|
-
const result = registerMcp(agentId, mcpName, config.command, config.scope);
|
|
1600
|
+
const result = await registerMcp(agentId, mcpName, config.command, config.scope);
|
|
1495
1601
|
if (result.success) {
|
|
1496
1602
|
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
1497
1603
|
}
|
|
@@ -1508,16 +1614,17 @@ mcpCmd
|
|
|
1508
1614
|
.command('push <name>')
|
|
1509
1615
|
.description('Save project-scoped MCP to user scope')
|
|
1510
1616
|
.option('-a, --agents <list>', 'Comma-separated agents to push for')
|
|
1511
|
-
.action((name, options) => {
|
|
1617
|
+
.action(async (name, options) => {
|
|
1512
1618
|
const cwd = process.cwd();
|
|
1513
1619
|
const agents = options.agents
|
|
1514
1620
|
? options.agents.split(',')
|
|
1515
1621
|
: MCP_CAPABLE_AGENTS;
|
|
1622
|
+
const cliStates = await getAllCliStates();
|
|
1516
1623
|
let pushed = 0;
|
|
1517
1624
|
for (const agentId of agents) {
|
|
1518
|
-
if (!
|
|
1625
|
+
if (!cliStates[agentId]?.installed)
|
|
1519
1626
|
continue;
|
|
1520
|
-
const result = promoteMcpToUser(agentId, name, cwd);
|
|
1627
|
+
const result = await promoteMcpToUser(agentId, name, cwd);
|
|
1521
1628
|
if (result.success) {
|
|
1522
1629
|
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
1523
1630
|
pushed++;
|
|
@@ -1544,7 +1651,7 @@ cliCmd
|
|
|
1544
1651
|
.description('List installed agent CLIs')
|
|
1545
1652
|
.action(async () => {
|
|
1546
1653
|
const spinner = ora('Checking installed CLIs...').start();
|
|
1547
|
-
const states = getAllCliStates();
|
|
1654
|
+
const states = await getAllCliStates();
|
|
1548
1655
|
spinner.stop();
|
|
1549
1656
|
console.log(chalk.bold('Agent CLIs\n'));
|
|
1550
1657
|
for (const agentId of ALL_AGENT_IDS) {
|
|
@@ -1560,7 +1667,6 @@ cliCmd
|
|
|
1560
1667
|
console.log(` ${agent.name.padEnd(14)} ${chalk.gray('not installed')}`);
|
|
1561
1668
|
}
|
|
1562
1669
|
}
|
|
1563
|
-
console.log();
|
|
1564
1670
|
});
|
|
1565
1671
|
cliCmd
|
|
1566
1672
|
.command('add <agent>')
|
|
@@ -1583,10 +1689,12 @@ cliCmd
|
|
|
1583
1689
|
console.log(chalk.yellow(`${agentConfig.name} has no npm package. Install manually.`));
|
|
1584
1690
|
}
|
|
1585
1691
|
else {
|
|
1586
|
-
const {
|
|
1692
|
+
const { exec } = await import('child_process');
|
|
1693
|
+
const { promisify } = await import('util');
|
|
1694
|
+
const execAsync = promisify(exec);
|
|
1587
1695
|
const spinner = ora(`Installing ${agentConfig.name}@${version}...`).start();
|
|
1588
1696
|
try {
|
|
1589
|
-
|
|
1697
|
+
await execAsync(`npm install -g ${pkg}@${version}`);
|
|
1590
1698
|
spinner.succeed(`Installed ${agentConfig.name}@${version}`);
|
|
1591
1699
|
}
|
|
1592
1700
|
catch (err) {
|
|
@@ -1626,14 +1734,16 @@ cliCmd
|
|
|
1626
1734
|
if (!pkg) {
|
|
1627
1735
|
console.log(chalk.yellow(`${agentConfig.name} has no npm package.`));
|
|
1628
1736
|
}
|
|
1629
|
-
else if (!isCliInstalled(agentId)) {
|
|
1737
|
+
else if (!(await isCliInstalled(agentId))) {
|
|
1630
1738
|
console.log(chalk.gray(`${agentConfig.name} is not installed`));
|
|
1631
1739
|
}
|
|
1632
1740
|
else {
|
|
1633
|
-
const {
|
|
1741
|
+
const { exec } = await import('child_process');
|
|
1742
|
+
const { promisify } = await import('util');
|
|
1743
|
+
const execAsync = promisify(exec);
|
|
1634
1744
|
const spinner = ora(`Uninstalling ${agentConfig.name}...`).start();
|
|
1635
1745
|
try {
|
|
1636
|
-
|
|
1746
|
+
await execAsync(`npm uninstall -g ${pkg}`);
|
|
1637
1747
|
spinner.succeed(`Uninstalled ${agentConfig.name}`);
|
|
1638
1748
|
}
|
|
1639
1749
|
catch (err) {
|
|
@@ -1669,7 +1779,9 @@ cliCmd
|
|
|
1669
1779
|
console.log(chalk.yellow('No CLIs to upgrade. Add CLIs to manifest or use --latest'));
|
|
1670
1780
|
return;
|
|
1671
1781
|
}
|
|
1672
|
-
const {
|
|
1782
|
+
const { exec } = await import('child_process');
|
|
1783
|
+
const { promisify } = await import('util');
|
|
1784
|
+
const execAsync = promisify(exec);
|
|
1673
1785
|
for (const agentId of agentsToUpgrade) {
|
|
1674
1786
|
const agentConfig = AGENTS[agentId];
|
|
1675
1787
|
if (!agentConfig) {
|
|
@@ -1681,7 +1793,7 @@ cliCmd
|
|
|
1681
1793
|
const pkg = cliConfig?.package || agentConfig.npmPackage;
|
|
1682
1794
|
const spinner = ora(`Upgrading ${agentConfig.name} to ${version}...`).start();
|
|
1683
1795
|
try {
|
|
1684
|
-
|
|
1796
|
+
await execAsync(`npm install -g ${pkg}@${version}`);
|
|
1685
1797
|
spinner.succeed(`${agentConfig.name} upgraded to ${version}`);
|
|
1686
1798
|
}
|
|
1687
1799
|
catch (err) {
|
|
@@ -2096,18 +2208,19 @@ program
|
|
|
2096
2208
|
else {
|
|
2097
2209
|
command = pkg.name || pkg.registry_name;
|
|
2098
2210
|
}
|
|
2211
|
+
const cliStates = await getAllCliStates();
|
|
2099
2212
|
const agents = options.agents
|
|
2100
2213
|
? options.agents.split(',')
|
|
2101
|
-
: MCP_CAPABLE_AGENTS.filter((id) =>
|
|
2214
|
+
: MCP_CAPABLE_AGENTS.filter((id) => cliStates[id]?.installed);
|
|
2102
2215
|
if (agents.length === 0) {
|
|
2103
2216
|
console.log(chalk.yellow('\nNo MCP-capable agents installed.'));
|
|
2104
2217
|
process.exit(1);
|
|
2105
2218
|
}
|
|
2106
2219
|
console.log(chalk.bold('\nInstalling to agents...'));
|
|
2107
2220
|
for (const agentId of agents) {
|
|
2108
|
-
if (!
|
|
2221
|
+
if (!cliStates[agentId]?.installed)
|
|
2109
2222
|
continue;
|
|
2110
|
-
const result = registerMcp(agentId, entry.name, command, 'user');
|
|
2223
|
+
const result = await registerMcp(agentId, entry.name, command, 'user');
|
|
2111
2224
|
if (result.success) {
|
|
2112
2225
|
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
2113
2226
|
}
|
|
@@ -2142,13 +2255,14 @@ program
|
|
|
2142
2255
|
const agents = options.agents
|
|
2143
2256
|
? options.agents.split(',')
|
|
2144
2257
|
: ['claude', 'codex', 'gemini'];
|
|
2258
|
+
const gitCliStates = await getAllCliStates();
|
|
2145
2259
|
// Install commands
|
|
2146
2260
|
if (hasCommands) {
|
|
2147
2261
|
console.log(chalk.bold('\nInstalling commands...'));
|
|
2148
2262
|
let installed = 0;
|
|
2149
2263
|
for (const command of commands) {
|
|
2150
2264
|
for (const agentId of agents) {
|
|
2151
|
-
if (!
|
|
2265
|
+
if (!gitCliStates[agentId]?.installed && agentId !== 'cursor')
|
|
2152
2266
|
continue;
|
|
2153
2267
|
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
2154
2268
|
if (sourcePath) {
|
|
@@ -2210,7 +2324,9 @@ program
|
|
|
2210
2324
|
}
|
|
2211
2325
|
spinner.text = `Upgrading to ${latestVersion}...`;
|
|
2212
2326
|
// Detect package manager
|
|
2213
|
-
const { execSync } = await import('child_process');
|
|
2327
|
+
const { execSync, exec } = await import('child_process');
|
|
2328
|
+
const { promisify } = await import('util');
|
|
2329
|
+
const execAsync = promisify(exec);
|
|
2214
2330
|
let cmd;
|
|
2215
2331
|
// Check if installed globally via npm, bun, or other
|
|
2216
2332
|
try {
|
|
@@ -2237,8 +2353,8 @@ program
|
|
|
2237
2353
|
cmd = 'npm install -g @swarmify/agents-cli@latest';
|
|
2238
2354
|
}
|
|
2239
2355
|
}
|
|
2240
|
-
// Run silently (suppress npm/bun output)
|
|
2241
|
-
|
|
2356
|
+
// Run silently (suppress npm/bun output) - use async to allow spinner to animate
|
|
2357
|
+
await execAsync(cmd);
|
|
2242
2358
|
spinner.succeed(`Upgraded to ${latestVersion}`);
|
|
2243
2359
|
// Show what's new from changelog
|
|
2244
2360
|
await showWhatsNew(currentVersion, latestVersion);
|