@swarmify/agents-cli 1.0.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/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @swarmify/agents-cli
2
+
3
+ Dotfiles manager for AI coding agents. Sync skills, MCP servers, and CLI versions across machines.
4
+
5
+ Part of the [Swarmify](https://github.com/muqsitnawaz/swarmify) multi-agent toolkit.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @swarmify/agents-cli
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # See what's installed
17
+ agents status
18
+
19
+ # Sync from your .agents repo
20
+ agents pull gh:username/.agents
21
+
22
+ # Or use a local directory
23
+ agents pull ~/dotfiles/.agents
24
+ ```
25
+
26
+ ## Commands
27
+
28
+ ### Status & Sync
29
+
30
+ | Command | Description |
31
+ |---------|-------------|
32
+ | `agents status` | Show CLIs, skills, sync source |
33
+ | `agents pull <source>` | Pull and install from repo |
34
+ | `agents push` | Export local config to repo |
35
+ | `agents sync <source>` | Alias for pull |
36
+
37
+ ### Skills
38
+
39
+ | Command | Description |
40
+ |---------|-------------|
41
+ | `agents skills list` | List installed skills |
42
+ | `agents skills list --agent claude` | Filter by agent |
43
+ | `agents skills add gh:user/skills` | Install from git repo |
44
+ | `agents skills remove <name>` | Uninstall skill |
45
+
46
+ ### MCP Servers
47
+
48
+ | Command | Description |
49
+ |---------|-------------|
50
+ | `agents mcp list` | Show registration status |
51
+ | `agents mcp add <name> <command>` | Add to manifest |
52
+ | `agents mcp remove <name>` | Unregister from agents |
53
+ | `agents mcp register` | Register all from manifest |
54
+
55
+ ### CLI Management
56
+
57
+ | Command | Description |
58
+ |---------|-------------|
59
+ | `agents cli list` | Show versions and paths |
60
+ | `agents cli add <agent>` | Add to manifest |
61
+ | `agents cli remove <agent>` | Remove from manifest |
62
+ | `agents cli upgrade` | Upgrade all to manifest versions |
63
+ | `agents cli upgrade --latest` | Upgrade to latest |
64
+
65
+ ## Repo Structure
66
+
67
+ Your `.agents` repo should look like:
68
+
69
+ ```
70
+ .agents/
71
+ agents.yaml # Manifest with CLIs, MCP, defaults
72
+ shared/
73
+ commands/ # Skills shared across all agents
74
+ claude/
75
+ commands/ # Claude-specific skills
76
+ hooks/ # Claude hooks
77
+ codex/
78
+ prompts/ # Codex-specific skills
79
+ gemini/
80
+ prompts/ # Gemini skills (TOML format)
81
+ ```
82
+
83
+ ## Manifest Format
84
+
85
+ ```yaml
86
+ clis:
87
+ claude:
88
+ package: "@anthropic-ai/claude-code"
89
+ version: "2.0.65"
90
+ codex:
91
+ package: "@openai/codex"
92
+ version: "0.88.0"
93
+
94
+ mcp:
95
+ swarm:
96
+ command: "npx @swarmify/agents-mcp"
97
+ transport: stdio
98
+ scope: user
99
+ agents: [claude, codex]
100
+
101
+ defaults:
102
+ method: symlink
103
+ scope: global
104
+ agents: [claude, codex, gemini]
105
+ ```
106
+
107
+ ## Supported Agents
108
+
109
+ | Agent | CLI | Config Directory |
110
+ |-------|-----|------------------|
111
+ | Claude | `claude` | `~/.claude/` |
112
+ | Codex | `codex` | `~/.codex/` |
113
+ | Gemini | `gemini` | `~/.gemini/` |
114
+ | Cursor | `cursor-agent` | `~/.cursor-agent/` |
115
+ | OpenCode | `opencode` | `~/.opencode/` |
116
+ | Trae | `trae-cli` | `~/.trae/` |
117
+
118
+ ## Options
119
+
120
+ | Flag | Description |
121
+ |------|-------------|
122
+ | `-y, --yes` | Skip interactive prompts |
123
+ | `-f, --force` | Force overwrite |
124
+ | `--dry-run` | Preview changes |
125
+ | `--skip-mcp` | Skip MCP registration |
126
+
127
+ ## State
128
+
129
+ Local state is stored in `~/.agents/`:
130
+
131
+ ```
132
+ ~/.agents/
133
+ state.json # Sync state, last sync time
134
+ repos/ # Cloned .agents repos
135
+ packages/ # External skill packages
136
+ ```
137
+
138
+ ## Related Packages
139
+
140
+ | Package | Description |
141
+ |---------|-------------|
142
+ | [@swarmify/agents-mcp](https://www.npmjs.com/package/@swarmify/agents-mcp) | MCP server for multi-agent orchestration. Spawn Claude, Codex, Gemini agents in parallel. |
143
+
144
+ ## License
145
+
146
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,548 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import { checkbox, select } from '@inquirer/prompts';
6
+ import { AGENTS, ALL_AGENT_IDS, MCP_CAPABLE_AGENTS, getAllCliStates, isCliInstalled, isMcpRegistered, registerMcp, unregisterMcp, } from './lib/agents.js';
7
+ import { readManifest, writeManifest, createDefaultManifest, MANIFEST_FILENAME, } from './lib/manifest.js';
8
+ import { readState, writeState, ensureAgentsDir, getRepoLocalPath, } from './lib/state.js';
9
+ import { cloneRepo } from './lib/git.js';
10
+ import { discoverSkills, resolveSkillSource, installSkill, uninstallSkill, listInstalledSkills, } from './lib/skills.js';
11
+ const program = new Command();
12
+ program
13
+ .name('agents')
14
+ .description('Dotfiles manager for AI coding agents')
15
+ .version('1.0.0');
16
+ // =============================================================================
17
+ // STATUS COMMAND
18
+ // =============================================================================
19
+ program
20
+ .command('status')
21
+ .description('Show sync status, CLI versions, installed skills and MCP servers')
22
+ .action(() => {
23
+ const state = readState();
24
+ const cliStates = getAllCliStates();
25
+ console.log(chalk.bold('\nAgent CLIs\n'));
26
+ for (const agentId of ALL_AGENT_IDS) {
27
+ const agent = AGENTS[agentId];
28
+ const cli = cliStates[agentId];
29
+ const status = cli?.installed
30
+ ? chalk.green(cli.version || 'installed')
31
+ : chalk.gray('not installed');
32
+ console.log(` ${agent.name.padEnd(14)} ${status}`);
33
+ }
34
+ console.log(chalk.bold('\nInstalled Skills\n'));
35
+ for (const agentId of ALL_AGENT_IDS) {
36
+ const agent = AGENTS[agentId];
37
+ const skills = listInstalledSkills(agentId);
38
+ if (skills.length > 0) {
39
+ console.log(` ${agent.name}: ${chalk.cyan(skills.length)} skills`);
40
+ }
41
+ }
42
+ if (state.source) {
43
+ console.log(chalk.bold('\nSync Source\n'));
44
+ console.log(` ${state.source}`);
45
+ if (state.lastSync) {
46
+ console.log(` Last sync: ${new Date(state.lastSync).toLocaleString()}`);
47
+ }
48
+ }
49
+ console.log();
50
+ });
51
+ // =============================================================================
52
+ // PULL COMMAND
53
+ // =============================================================================
54
+ program
55
+ .command('pull [source]')
56
+ .description('Pull and sync from remote .agents repo')
57
+ .option('-y, --yes', 'Skip interactive prompts')
58
+ .option('-f, --force', 'Overwrite local changes')
59
+ .option('--dry-run', 'Show what would change')
60
+ .option('--skip-clis', 'Skip CLI installation')
61
+ .option('--skip-mcp', 'Skip MCP registration')
62
+ .action(async (source, options) => {
63
+ const state = readState();
64
+ const targetSource = source || state.source;
65
+ if (!targetSource) {
66
+ console.log(chalk.red('No source specified. Usage: agents pull <source>'));
67
+ console.log(chalk.gray(' Example: agents pull gh:username/.agents'));
68
+ process.exit(1);
69
+ }
70
+ const spinner = ora('Cloning repository...').start();
71
+ try {
72
+ const { localPath, commit, isNew } = await cloneRepo(targetSource);
73
+ spinner.succeed(isNew ? 'Repository cloned' : 'Repository updated');
74
+ const manifest = readManifest(localPath);
75
+ if (!manifest) {
76
+ console.log(chalk.yellow(`No ${MANIFEST_FILENAME} found in repository`));
77
+ }
78
+ const skills = discoverSkills(localPath);
79
+ console.log(chalk.bold(`\nFound ${skills.length} skills\n`));
80
+ for (const skill of skills.slice(0, 10)) {
81
+ const source = skill.isShared ? 'shared' : skill.agentSpecific;
82
+ console.log(` ${chalk.cyan(skill.name.padEnd(20))} ${chalk.gray(source)}`);
83
+ }
84
+ if (skills.length > 10) {
85
+ console.log(chalk.gray(` ... and ${skills.length - 10} more`));
86
+ }
87
+ if (options.dryRun) {
88
+ console.log(chalk.yellow('\nDry run - no changes made'));
89
+ return;
90
+ }
91
+ let selectedAgents;
92
+ let method;
93
+ if (options.yes) {
94
+ selectedAgents = (manifest?.defaults?.agents || ['claude', 'codex', 'gemini']);
95
+ method = manifest?.defaults?.method || 'symlink';
96
+ }
97
+ else {
98
+ const installedAgents = ALL_AGENT_IDS.filter((id) => isCliInstalled(id) || id === 'cursor');
99
+ selectedAgents = await checkbox({
100
+ message: 'Select agents to sync:',
101
+ choices: installedAgents.map((id) => ({
102
+ name: AGENTS[id].name,
103
+ value: id,
104
+ checked: (manifest?.defaults?.agents || ['claude', 'codex', 'gemini']).includes(id),
105
+ })),
106
+ });
107
+ method = await select({
108
+ message: 'Installation method:',
109
+ choices: [
110
+ { name: 'Symlink (updates automatically)', value: 'symlink' },
111
+ { name: 'Copy (independent files)', value: 'copy' },
112
+ ],
113
+ default: manifest?.defaults?.method || 'symlink',
114
+ });
115
+ }
116
+ const defaultAgents = selectedAgents;
117
+ const installSpinner = ora('Installing skills...').start();
118
+ let installed = 0;
119
+ for (const skill of skills) {
120
+ for (const agentId of defaultAgents) {
121
+ if (!isCliInstalled(agentId) && agentId !== 'cursor')
122
+ continue;
123
+ const sourcePath = resolveSkillSource(localPath, skill.name, agentId);
124
+ if (sourcePath) {
125
+ installSkill(sourcePath, agentId, skill.name, method);
126
+ installed++;
127
+ }
128
+ }
129
+ }
130
+ installSpinner.succeed(`Installed ${installed} skill instances`);
131
+ if (!options.skipMcp && manifest?.mcp) {
132
+ const mcpSpinner = ora('Registering MCP servers...').start();
133
+ let registered = 0;
134
+ for (const [name, config] of Object.entries(manifest.mcp)) {
135
+ for (const agentId of config.agents) {
136
+ if (!isCliInstalled(agentId))
137
+ continue;
138
+ if (isMcpRegistered(agentId, name))
139
+ continue;
140
+ const result = registerMcp(agentId, name, config.command, config.scope);
141
+ if (result.success)
142
+ registered++;
143
+ }
144
+ }
145
+ mcpSpinner.succeed(`Registered ${registered} MCP servers`);
146
+ }
147
+ writeState({
148
+ ...state,
149
+ source: targetSource,
150
+ lastSync: new Date().toISOString(),
151
+ });
152
+ console.log(chalk.green('\nSync complete.'));
153
+ }
154
+ catch (err) {
155
+ spinner.fail('Failed to sync');
156
+ console.error(chalk.red(err.message));
157
+ process.exit(1);
158
+ }
159
+ });
160
+ // =============================================================================
161
+ // PUSH COMMAND
162
+ // =============================================================================
163
+ program
164
+ .command('push')
165
+ .description('Export local skills to .agents repo for manual commit')
166
+ .option('--export-only', 'Only export skills, do not update manifest')
167
+ .action(async (options) => {
168
+ const state = readState();
169
+ if (!state.source) {
170
+ console.log(chalk.red('No .agents repo configured. Run: agents pull <source>'));
171
+ process.exit(1);
172
+ }
173
+ const localPath = getRepoLocalPath(state.source);
174
+ const manifest = readManifest(localPath) || createDefaultManifest();
175
+ console.log(chalk.bold('\nExporting local configuration...\n'));
176
+ const cliStates = getAllCliStates();
177
+ let exported = 0;
178
+ for (const agentId of ALL_AGENT_IDS) {
179
+ const agent = AGENTS[agentId];
180
+ const cli = cliStates[agentId];
181
+ if (cli?.installed && cli.version) {
182
+ manifest.clis = manifest.clis || {};
183
+ manifest.clis[agentId] = {
184
+ package: agent.npmPackage,
185
+ version: cli.version,
186
+ };
187
+ console.log(` ${chalk.green('+')} ${agent.name} @ ${cli.version}`);
188
+ exported++;
189
+ }
190
+ }
191
+ if (!options.exportOnly) {
192
+ writeManifest(localPath, manifest);
193
+ console.log(chalk.bold(`\nUpdated ${MANIFEST_FILENAME}`));
194
+ }
195
+ console.log(chalk.bold('\nNext steps:'));
196
+ console.log(chalk.gray(` cd ${localPath}`));
197
+ console.log(chalk.gray(' git add -A'));
198
+ console.log(chalk.gray(' git commit -m "Update agent configuration"'));
199
+ console.log(chalk.gray(' git push'));
200
+ console.log();
201
+ });
202
+ // =============================================================================
203
+ // SYNC COMMAND
204
+ // =============================================================================
205
+ program
206
+ .command('sync [source]')
207
+ .description('Bidirectional sync with remote .agents repo')
208
+ .option('-y, --yes', 'Skip interactive prompts')
209
+ .option('-f, --force', 'Overwrite local changes')
210
+ .action(async (source, options) => {
211
+ const args = ['pull'];
212
+ if (source)
213
+ args.push(source);
214
+ if (options.yes)
215
+ args.push('-y');
216
+ if (options.force)
217
+ args.push('-f');
218
+ await program.commands.find((c) => c.name() === 'pull')?.parseAsync(args, { from: 'user' });
219
+ });
220
+ // =============================================================================
221
+ // SKILLS COMMANDS
222
+ // =============================================================================
223
+ const skillsCmd = program
224
+ .command('skills')
225
+ .description('Manage skills/commands');
226
+ skillsCmd
227
+ .command('list')
228
+ .description('List installed skills')
229
+ .option('-a, --agent <agent>', 'Show skills for specific agent')
230
+ .action((options) => {
231
+ console.log(chalk.bold('\nInstalled Skills\n'));
232
+ const agents = options.agent
233
+ ? [options.agent]
234
+ : ALL_AGENT_IDS;
235
+ for (const agentId of agents) {
236
+ const agent = AGENTS[agentId];
237
+ const skills = listInstalledSkills(agentId);
238
+ if (skills.length === 0) {
239
+ console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('none')}`);
240
+ }
241
+ else {
242
+ console.log(` ${chalk.bold(agent.name)}:`);
243
+ for (const skill of skills) {
244
+ console.log(` ${chalk.cyan(skill)}`);
245
+ }
246
+ }
247
+ console.log();
248
+ }
249
+ });
250
+ skillsCmd
251
+ .command('add <source>')
252
+ .description('Add skill from Git repo or local path')
253
+ .option('-a, --agents <list>', 'Comma-separated agents to install to')
254
+ .action(async (source, options) => {
255
+ const spinner = ora('Fetching skill...').start();
256
+ try {
257
+ const { localPath } = await cloneRepo(source);
258
+ const skills = discoverSkills(localPath);
259
+ spinner.succeed(`Found ${skills.length} skills`);
260
+ const agents = options.agents
261
+ ? options.agents.split(',')
262
+ : ['claude', 'codex', 'gemini'];
263
+ for (const skill of skills) {
264
+ console.log(`\n ${chalk.cyan(skill.name)}: ${skill.description}`);
265
+ for (const agentId of agents) {
266
+ if (!isCliInstalled(agentId) && agentId !== 'cursor')
267
+ continue;
268
+ const sourcePath = resolveSkillSource(localPath, skill.name, agentId);
269
+ if (sourcePath) {
270
+ installSkill(sourcePath, agentId, skill.name, 'symlink');
271
+ console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
272
+ }
273
+ }
274
+ }
275
+ console.log(chalk.green('\nSkills installed.'));
276
+ }
277
+ catch (err) {
278
+ spinner.fail('Failed to add skill');
279
+ console.error(chalk.red(err.message));
280
+ process.exit(1);
281
+ }
282
+ });
283
+ skillsCmd
284
+ .command('remove <name>')
285
+ .description('Remove a skill from all agents')
286
+ .option('-a, --agents <list>', 'Comma-separated agents to remove from')
287
+ .action((name, options) => {
288
+ const agents = options.agents
289
+ ? options.agents.split(',')
290
+ : ALL_AGENT_IDS;
291
+ let removed = 0;
292
+ for (const agentId of agents) {
293
+ if (uninstallSkill(agentId, name)) {
294
+ console.log(` ${chalk.red('-')} ${AGENTS[agentId].name}`);
295
+ removed++;
296
+ }
297
+ }
298
+ if (removed === 0) {
299
+ console.log(chalk.yellow(`Skill '${name}' not found`));
300
+ }
301
+ else {
302
+ console.log(chalk.green(`\nRemoved from ${removed} agents.`));
303
+ }
304
+ });
305
+ // =============================================================================
306
+ // MCP COMMANDS
307
+ // =============================================================================
308
+ const mcpCmd = program
309
+ .command('mcp')
310
+ .description('Manage MCP servers');
311
+ mcpCmd
312
+ .command('list')
313
+ .description('List MCP servers and registration status')
314
+ .action(() => {
315
+ console.log(chalk.bold('\nMCP Servers\n'));
316
+ for (const agentId of MCP_CAPABLE_AGENTS) {
317
+ const agent = AGENTS[agentId];
318
+ if (!isCliInstalled(agentId)) {
319
+ console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('CLI not installed')}`);
320
+ continue;
321
+ }
322
+ console.log(` ${chalk.bold(agent.name)}:`);
323
+ const swarmRegistered = isMcpRegistered(agentId, 'Swarm');
324
+ console.log(` Swarm: ${swarmRegistered ? chalk.green('registered') : chalk.gray('not registered')}`);
325
+ console.log();
326
+ }
327
+ });
328
+ mcpCmd
329
+ .command('add <name> <command>')
330
+ .description('Add MCP server to manifest')
331
+ .option('-a, --agents <list>', 'Comma-separated agents', 'claude,codex,gemini')
332
+ .option('-s, --scope <scope>', 'Scope: user or project', 'user')
333
+ .action((name, command, options) => {
334
+ const state = readState();
335
+ if (!state.source) {
336
+ console.log(chalk.yellow('No .agents repo configured. Run: agents pull <source>'));
337
+ return;
338
+ }
339
+ const localPath = getRepoLocalPath(state.source);
340
+ const manifest = readManifest(localPath) || createDefaultManifest();
341
+ manifest.mcp = manifest.mcp || {};
342
+ manifest.mcp[name] = {
343
+ command,
344
+ transport: 'stdio',
345
+ scope: options.scope,
346
+ agents: options.agents.split(','),
347
+ };
348
+ writeManifest(localPath, manifest);
349
+ console.log(chalk.green(`Added MCP server '${name}' to manifest`));
350
+ console.log(chalk.gray('Run: agents mcp register to apply'));
351
+ });
352
+ mcpCmd
353
+ .command('remove <name>')
354
+ .description('Remove MCP server from agents')
355
+ .option('-a, --agents <list>', 'Comma-separated agents')
356
+ .action((name, options) => {
357
+ const agents = options.agents
358
+ ? options.agents.split(',')
359
+ : MCP_CAPABLE_AGENTS;
360
+ let removed = 0;
361
+ for (const agentId of agents) {
362
+ if (!isCliInstalled(agentId))
363
+ continue;
364
+ const result = unregisterMcp(agentId, name);
365
+ if (result.success) {
366
+ console.log(` ${chalk.red('-')} ${AGENTS[agentId].name}`);
367
+ removed++;
368
+ }
369
+ }
370
+ if (removed === 0) {
371
+ console.log(chalk.yellow(`MCP '${name}' not found or not registered`));
372
+ }
373
+ else {
374
+ console.log(chalk.green(`\nRemoved from ${removed} agents.`));
375
+ }
376
+ });
377
+ mcpCmd
378
+ .command('register [name]')
379
+ .description('Register MCP server(s) with agent CLIs')
380
+ .option('-a, --agents <list>', 'Comma-separated agents')
381
+ .action((name, options) => {
382
+ const state = readState();
383
+ if (!name) {
384
+ if (!state.source) {
385
+ console.log(chalk.yellow('No .agents repo configured. Run: agents pull <source>'));
386
+ return;
387
+ }
388
+ const localPath = getRepoLocalPath(state.source);
389
+ const manifest = readManifest(localPath);
390
+ if (!manifest?.mcp) {
391
+ console.log(chalk.yellow('No MCP servers in manifest'));
392
+ return;
393
+ }
394
+ for (const [mcpName, config] of Object.entries(manifest.mcp)) {
395
+ console.log(`\n ${chalk.cyan(mcpName)}:`);
396
+ for (const agentId of config.agents) {
397
+ if (!isCliInstalled(agentId))
398
+ continue;
399
+ const result = registerMcp(agentId, mcpName, config.command, config.scope);
400
+ if (result.success) {
401
+ console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
402
+ }
403
+ else {
404
+ console.log(` ${chalk.red('x')} ${AGENTS[agentId].name}: ${result.error}`);
405
+ }
406
+ }
407
+ }
408
+ return;
409
+ }
410
+ console.log(chalk.yellow('Single MCP registration not yet implemented'));
411
+ });
412
+ // =============================================================================
413
+ // CLI COMMANDS
414
+ // =============================================================================
415
+ const cliCmd = program
416
+ .command('cli')
417
+ .description('Manage agent CLIs');
418
+ cliCmd
419
+ .command('list')
420
+ .description('List installed agent CLIs')
421
+ .action(() => {
422
+ console.log(chalk.bold('\nAgent CLIs\n'));
423
+ const states = getAllCliStates();
424
+ for (const agentId of ALL_AGENT_IDS) {
425
+ const agent = AGENTS[agentId];
426
+ const state = states[agentId];
427
+ if (state?.installed) {
428
+ console.log(` ${agent.name.padEnd(14)} ${chalk.green(state.version || 'installed')}`);
429
+ if (state.path) {
430
+ console.log(` ${''.padEnd(14)} ${chalk.gray(state.path)}`);
431
+ }
432
+ }
433
+ else {
434
+ console.log(` ${agent.name.padEnd(14)} ${chalk.gray('not installed')}`);
435
+ }
436
+ }
437
+ console.log();
438
+ });
439
+ cliCmd
440
+ .command('add <agent>')
441
+ .description('Add agent CLI to manifest')
442
+ .option('-v, --version <version>', 'Version to pin', 'latest')
443
+ .action((agent, options) => {
444
+ const state = readState();
445
+ if (!state.source) {
446
+ console.log(chalk.yellow('No .agents repo configured. Run: agents pull <source>'));
447
+ return;
448
+ }
449
+ const agentId = agent.toLowerCase();
450
+ if (!AGENTS[agentId]) {
451
+ console.log(chalk.red(`Unknown agent: ${agent}`));
452
+ console.log(chalk.gray(`Available: ${ALL_AGENT_IDS.join(', ')}`));
453
+ return;
454
+ }
455
+ const localPath = getRepoLocalPath(state.source);
456
+ const manifest = readManifest(localPath) || createDefaultManifest();
457
+ manifest.clis = manifest.clis || {};
458
+ manifest.clis[agentId] = {
459
+ package: AGENTS[agentId].npmPackage,
460
+ version: options.version,
461
+ };
462
+ writeManifest(localPath, manifest);
463
+ console.log(chalk.green(`Added ${AGENTS[agentId].name} CLI to manifest`));
464
+ });
465
+ cliCmd
466
+ .command('remove <agent>')
467
+ .description('Remove agent CLI from manifest')
468
+ .action((agent) => {
469
+ const state = readState();
470
+ if (!state.source) {
471
+ console.log(chalk.yellow('No .agents repo configured. Run: agents pull <source>'));
472
+ return;
473
+ }
474
+ const agentId = agent.toLowerCase();
475
+ const localPath = getRepoLocalPath(state.source);
476
+ const manifest = readManifest(localPath);
477
+ if (manifest?.clis?.[agentId]) {
478
+ delete manifest.clis[agentId];
479
+ writeManifest(localPath, manifest);
480
+ console.log(chalk.green(`Removed ${AGENTS[agentId]?.name || agent} CLI from manifest`));
481
+ }
482
+ else {
483
+ console.log(chalk.yellow(`${agent} not in manifest`));
484
+ }
485
+ });
486
+ cliCmd
487
+ .command('upgrade [agent]')
488
+ .description('Upgrade agent CLI(s) to version in manifest')
489
+ .option('--latest', 'Upgrade to latest version (ignore manifest)')
490
+ .action(async (agent, options) => {
491
+ const state = readState();
492
+ const localPath = state.source ? getRepoLocalPath(state.source) : null;
493
+ const manifest = localPath ? readManifest(localPath) : null;
494
+ const agentsToUpgrade = agent
495
+ ? [agent.toLowerCase()]
496
+ : ALL_AGENT_IDS.filter((id) => manifest?.clis?.[id] || options.latest);
497
+ if (agentsToUpgrade.length === 0) {
498
+ console.log(chalk.yellow('No CLIs to upgrade. Add CLIs to manifest or use --latest'));
499
+ return;
500
+ }
501
+ const { execSync } = await import('child_process');
502
+ for (const agentId of agentsToUpgrade) {
503
+ const agentConfig = AGENTS[agentId];
504
+ if (!agentConfig) {
505
+ console.log(chalk.red(`Unknown agent: ${agentId}`));
506
+ continue;
507
+ }
508
+ const cliConfig = manifest?.clis?.[agentId];
509
+ const version = options.latest ? 'latest' : (cliConfig?.version || 'latest');
510
+ const pkg = cliConfig?.package || agentConfig.npmPackage;
511
+ const spinner = ora(`Upgrading ${agentConfig.name} to ${version}...`).start();
512
+ try {
513
+ execSync(`npm install -g ${pkg}@${version}`, { stdio: 'pipe' });
514
+ spinner.succeed(`${agentConfig.name} upgraded to ${version}`);
515
+ }
516
+ catch (err) {
517
+ spinner.fail(`Failed to upgrade ${agentConfig.name}`);
518
+ console.error(chalk.gray(err.message));
519
+ }
520
+ }
521
+ });
522
+ // =============================================================================
523
+ // INIT COMMAND
524
+ // =============================================================================
525
+ program
526
+ .command('init')
527
+ .description('Initialize a new .agents repo')
528
+ .action(() => {
529
+ ensureAgentsDir();
530
+ const manifest = createDefaultManifest();
531
+ console.log(chalk.bold('\nDefault agents.yaml:\n'));
532
+ console.log(chalk.gray('clis:'));
533
+ console.log(chalk.gray(' claude:'));
534
+ console.log(chalk.gray(' package: "@anthropic-ai/claude-code"'));
535
+ console.log(chalk.gray(' version: "latest"'));
536
+ console.log(chalk.gray(' codex:'));
537
+ console.log(chalk.gray(' package: "@openai/codex"'));
538
+ console.log(chalk.gray(' version: "latest"'));
539
+ console.log();
540
+ console.log(chalk.green('Create a new repo with this structure:'));
541
+ console.log(chalk.gray(' .agents/'));
542
+ console.log(chalk.gray(' agents.yaml'));
543
+ console.log(chalk.gray(' shared/commands/'));
544
+ console.log(chalk.gray(' claude/hooks/'));
545
+ console.log();
546
+ });
547
+ program.parse();
548
+ //# sourceMappingURL=index.js.map