@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 +146 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +548 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.d.ts.map +1 -0
- package/dist/lib/agents.js +198 -0
- package/dist/lib/agents.js.map +1 -0
- package/dist/lib/convert.d.ts +11 -0
- package/dist/lib/convert.d.ts.map +1 -0
- package/dist/lib/convert.js +45 -0
- package/dist/lib/convert.js.map +1 -0
- package/dist/lib/git.d.ts +22 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +108 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/manifest.d.ts +8 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +49 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/skills.d.ts +14 -0
- package/dist/lib/skills.d.ts.map +1 -0
- package/dist/lib/skills.js +120 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/state.d.ts +12 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +79 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/types.d.ts +78 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +69 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|