@swarmify/agents-cli 1.5.13 → 1.5.14
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 +101 -16
- package/dist/index.js +306 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +19 -2
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/artifact-actions.d.ts +22 -0
- package/dist/lib/artifact-actions.d.ts.map +1 -0
- package/dist/lib/artifact-actions.js +55 -0
- package/dist/lib/artifact-actions.js.map +1 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +3 -5
- package/dist/lib/commands.js.map +1 -1
- package/dist/lib/instructions.d.ts +31 -0
- package/dist/lib/instructions.d.ts.map +1 -0
- package/dist/lib/instructions.js +161 -0
- package/dist/lib/instructions.js.map +1 -0
- package/dist/lib/template.d.ts +24 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +57 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# @swarmify/agents-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**One config for all your AI coding agents.** Sync CLIs, MCP servers, commands, hooks, and skills across Claude, Codex, Gemini, and Cursor.
|
|
4
4
|
|
|
5
|
-
Homepage
|
|
6
|
-
NPM: https://www.npmjs.com/package/@swarmify/agents-cli
|
|
7
|
-
VS Code Extension: [Agents](https://marketplace.visualstudio.com/items?itemName=swarmify.swarm-ext) - full-screen agent terminals with sub-agent spawning
|
|
5
|
+
[Homepage](https://swarmify.co/#agents-cli) | [NPM](https://www.npmjs.com/package/@swarmify/agents-cli) | [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=swarmify.swarm-ext)
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npm install -g @swarmify/agents-cli
|
|
@@ -12,11 +10,39 @@ npm install -g @swarmify/agents-cli
|
|
|
12
10
|
|
|
13
11
|
## The Problem
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
Each agent stores config differently. Different paths, different formats:
|
|
14
|
+
|
|
15
|
+
| What | Claude | Codex | Gemini |
|
|
16
|
+
|------|--------|-------|--------|
|
|
17
|
+
| Commands | `~/.claude/commands/` (md) | `~/.codex/prompts/` (md) | `~/.gemini/commands/` (TOML) |
|
|
18
|
+
| MCP config | `~/.claude/settings.json` | `~/.codex/config.json` | `~/.gemini/settings.json` |
|
|
19
|
+
| Hooks | `~/.claude/hooks/` | - | `~/.gemini/hooks/` |
|
|
20
|
+
|
|
21
|
+
You spend hours configuring Claude Code - MCP servers, slash commands, hooks, skills. Then you switch to Codex and start from scratch. Get a new machine and lose everything.
|
|
16
22
|
|
|
17
23
|
## The Solution
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
```
|
|
26
|
+
.agents repo (GitHub)
|
|
27
|
+
|
|
|
28
|
+
+----------------+----------------+
|
|
29
|
+
| | |
|
|
30
|
+
agents.yaml commands/ hooks/
|
|
31
|
+
(CLIs, MCPs) (slash cmds) (scripts)
|
|
32
|
+
| | |
|
|
33
|
+
+----------------+----------------+
|
|
34
|
+
|
|
|
35
|
+
agents pull
|
|
36
|
+
|
|
|
37
|
+
+-------------------+-------------------+
|
|
38
|
+
| | |
|
|
39
|
+
~/.claude/ ~/.codex/ ~/.gemini/
|
|
40
|
+
- commands/ (md) - prompts/ (md) - commands/ (TOML)
|
|
41
|
+
- hooks/ - config.json - hooks/
|
|
42
|
+
- settings.json - settings.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
One repo. One command. All agents configured.
|
|
20
46
|
|
|
21
47
|
```bash
|
|
22
48
|
# New machine? One command.
|
|
@@ -30,25 +56,26 @@ agents status
|
|
|
30
56
|
Agent CLIs
|
|
31
57
|
|
|
32
58
|
Claude Code 2.0.65
|
|
59
|
+
Codex 1.0.3
|
|
60
|
+
Gemini CLI 0.1.15
|
|
33
61
|
|
|
34
62
|
Installed Commands
|
|
35
63
|
|
|
36
|
-
Claude Code:
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
Claude Code: clean, debug, plan, ship, test
|
|
65
|
+
Codex: clean, debug, plan, ship, test
|
|
66
|
+
Gemini CLI: clean, debug, plan, ship, test
|
|
39
67
|
|
|
40
|
-
Installed
|
|
68
|
+
Installed MCP Servers
|
|
41
69
|
|
|
42
|
-
|
|
43
|
-
User: remotion-best-practices, vercel-react-best-practices
|
|
70
|
+
All agents: Swarm, filesystem, memory
|
|
44
71
|
|
|
45
|
-
Installed
|
|
72
|
+
Installed Hooks
|
|
46
73
|
|
|
47
|
-
Claude Code:
|
|
48
|
-
|
|
74
|
+
Claude Code: pre-commit, post-tool
|
|
75
|
+
Gemini CLI: pre-commit, post-tool
|
|
49
76
|
```
|
|
50
77
|
|
|
51
|
-
Your `.agents` repo becomes the source of truth
|
|
78
|
+
Write commands once in markdown - auto-converts to TOML for Gemini. Define MCP servers once - installs to all agents. Your `.agents` repo becomes the single source of truth.
|
|
52
79
|
|
|
53
80
|
## What Gets Synced
|
|
54
81
|
|
|
@@ -266,6 +293,64 @@ agents registry config mcp myregistry --api-key KEY
|
|
|
266
293
|
|
|
267
294
|
Format conversion is automatic. Write commands in markdown, they're converted to TOML for Gemini.
|
|
268
295
|
|
|
296
|
+
## Roadmap: Context Drives
|
|
297
|
+
|
|
298
|
+
Sync your docs, research, and chat history across machines and teams.
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
~/.agents/
|
|
302
|
+
drives/ # Context drives (synced)
|
|
303
|
+
work/
|
|
304
|
+
.context # Per-drive settings
|
|
305
|
+
docs/
|
|
306
|
+
research/
|
|
307
|
+
specs/
|
|
308
|
+
personal/
|
|
309
|
+
.context
|
|
310
|
+
notes/
|
|
311
|
+
|
|
312
|
+
sessions/ # Agent chat history (synced)
|
|
313
|
+
claude/
|
|
314
|
+
codex/
|
|
315
|
+
gemini/
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Why not just Google Drive?**
|
|
319
|
+
|
|
320
|
+
| Feature | Google Drive | Context Drives |
|
|
321
|
+
|---------|--------------|----------------|
|
|
322
|
+
| Sync files | Yes | Yes |
|
|
323
|
+
| Real-time collab | Yes (Docs only) | Yes (CRDT for all files) |
|
|
324
|
+
| Agent session sync | No | Yes |
|
|
325
|
+
| Checkpointing | No | Yes (snapshot & rollback) |
|
|
326
|
+
| Per-directory conflict strategy | No | Yes (`.context` file) |
|
|
327
|
+
| Designed for AI agents | No | Yes |
|
|
328
|
+
|
|
329
|
+
**Conflict resolution strategies** (configurable per directory):
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
# .context file
|
|
333
|
+
strategy: crdt # Auto-merge (like Google Docs)
|
|
334
|
+
# strategy: git # Branch/PR/merge (for code)
|
|
335
|
+
# strategy: lock # Exclusive access
|
|
336
|
+
# strategy: last-write-wins # Don't care about conflicts
|
|
337
|
+
|
|
338
|
+
sync: realtime # or: on-demand, ignore
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Planned commands:**
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
agents drive create <name>
|
|
345
|
+
agents drive list
|
|
346
|
+
agents drive use <name>
|
|
347
|
+
agents drive sync
|
|
348
|
+
agents drive checkpoint "before refactor"
|
|
349
|
+
agents drive rollback <checkpoint>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Multi-agent coordination:** When multiple agents (or developers) work on the same drive, the drive acts as a coordination layer - checkout files, see who's working on what, avoid conflicts.
|
|
353
|
+
|
|
269
354
|
## Related
|
|
270
355
|
|
|
271
356
|
- [@swarmify/agents-mcp](https://www.npmjs.com/package/@swarmify/agents-mcp) - MCP server for sub-agent spawning
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { cloneRepo, parseSource } from './lib/git.js';
|
|
|
24
24
|
import { discoverCommands, resolveCommandSource, installCommand, uninstallCommand, listInstalledCommandsWithScope, promoteCommandToUser, commandExists, commandContentMatches, } from './lib/commands.js';
|
|
25
25
|
import { discoverHooksFromRepo, installHooks, listInstalledHooksWithScope, promoteHookToUser, removeHook, hookExists, hookContentMatches, getSourceHookEntry, } from './lib/hooks.js';
|
|
26
26
|
import { discoverSkillsFromRepo, installSkill, uninstallSkill, listInstalledSkillsWithScope, promoteSkillToUser, getSkillInfo, getSkillRules, skillExists, skillContentMatches, } from './lib/skills.js';
|
|
27
|
+
import { discoverInstructionsFromRepo, resolveInstructionsSource, installInstructions, uninstallInstructions, listInstalledInstructionsWithScope, promoteInstructionsToUser, instructionsExists, instructionsContentMatches, getInstructionsContent, } from './lib/instructions.js';
|
|
27
28
|
import { DEFAULT_REGISTRIES } from './lib/types.js';
|
|
28
29
|
import { search as searchRegistries, getRegistries, setRegistry, removeRegistry, resolvePackage, } from './lib/registry.js';
|
|
29
30
|
const program = new Command();
|
|
@@ -179,6 +180,10 @@ program
|
|
|
179
180
|
agent: AGENTS[agentId],
|
|
180
181
|
mcps: listInstalledMcpsWithScope(agentId, cwd),
|
|
181
182
|
}));
|
|
183
|
+
const instructionsData = agentsToShow.map((agentId) => ({
|
|
184
|
+
agent: AGENTS[agentId],
|
|
185
|
+
instructions: listInstalledInstructionsWithScope(agentId, cwd),
|
|
186
|
+
}));
|
|
182
187
|
const scopes = filterAgentId ? [] : getScopesByPriority();
|
|
183
188
|
spinner.stop();
|
|
184
189
|
// Helper to format MCP with version
|
|
@@ -249,6 +254,23 @@ program
|
|
|
249
254
|
}
|
|
250
255
|
}
|
|
251
256
|
}
|
|
257
|
+
console.log(chalk.bold('\nInstalled Instructions\n'));
|
|
258
|
+
for (const { agent, instructions } of instructionsData) {
|
|
259
|
+
const userInstr = instructions.find((i) => i.scope === 'user' && i.exists);
|
|
260
|
+
const projectInstr = instructions.find((i) => i.scope === 'project' && i.exists);
|
|
261
|
+
if (!userInstr && !projectInstr) {
|
|
262
|
+
console.log(` ${chalk.bold(agent.name)}: ${chalk.gray('none')}`);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
console.log(` ${chalk.bold(agent.name)}:`);
|
|
266
|
+
if (userInstr) {
|
|
267
|
+
console.log(` ${chalk.gray('User:')} ${chalk.cyan(agent.instructionsFile)}`);
|
|
268
|
+
}
|
|
269
|
+
if (projectInstr) {
|
|
270
|
+
console.log(` ${chalk.gray('Project:')} ${chalk.yellow(agent.instructionsFile)}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
252
274
|
// Only show scopes when not filtering by agent
|
|
253
275
|
if (!filterAgentId) {
|
|
254
276
|
if (scopes.length > 0) {
|
|
@@ -364,6 +386,7 @@ program
|
|
|
364
386
|
const allCommands = discoverCommands(localPath);
|
|
365
387
|
const allSkills = discoverSkillsFromRepo(localPath);
|
|
366
388
|
const discoveredHooks = discoverHooksFromRepo(localPath);
|
|
389
|
+
const allInstructions = discoverInstructionsFromRepo(localPath);
|
|
367
390
|
// Determine which agents to sync
|
|
368
391
|
const cliStates = await getAllCliStates();
|
|
369
392
|
let selectedAgents;
|
|
@@ -506,12 +529,27 @@ program
|
|
|
506
529
|
}
|
|
507
530
|
}
|
|
508
531
|
}
|
|
532
|
+
// Process instructions
|
|
533
|
+
for (const instr of allInstructions) {
|
|
534
|
+
if (!selectedAgents.includes(instr.agentId))
|
|
535
|
+
continue;
|
|
536
|
+
const hasExisting = instructionsExists(instr.agentId, 'user');
|
|
537
|
+
if (!hasExisting) {
|
|
538
|
+
newItems.push({ type: 'instructions', name: AGENTS[instr.agentId].instructionsFile, agents: [instr.agentId], isNew: true });
|
|
539
|
+
}
|
|
540
|
+
else if (instructionsContentMatches(instr.agentId, instr.sourcePath, 'user')) {
|
|
541
|
+
upToDateItems.push({ type: 'instructions', name: AGENTS[instr.agentId].instructionsFile, agents: [instr.agentId], isNew: false });
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
existingItems.push({ type: 'instructions', name: AGENTS[instr.agentId].instructionsFile, agents: [instr.agentId], isNew: false });
|
|
545
|
+
}
|
|
546
|
+
}
|
|
509
547
|
// Display overview
|
|
510
548
|
console.log(chalk.bold('\nOverview\n'));
|
|
511
549
|
const formatAgentList = (agents) => agents.map((id) => AGENTS[id].name).join(', ');
|
|
512
550
|
if (newItems.length > 0) {
|
|
513
551
|
console.log(chalk.green(' NEW (will install):\n'));
|
|
514
|
-
const byType = { command: [], skill: [], hook: [], mcp: [] };
|
|
552
|
+
const byType = { command: [], skill: [], hook: [], mcp: [], instructions: [] };
|
|
515
553
|
for (const item of newItems)
|
|
516
554
|
byType[item.type].push(item);
|
|
517
555
|
if (byType.command.length > 0) {
|
|
@@ -538,11 +576,17 @@ program
|
|
|
538
576
|
console.log(` ${chalk.cyan(item.name.padEnd(20))} ${chalk.gray(formatAgentList(item.agents))}`);
|
|
539
577
|
}
|
|
540
578
|
}
|
|
579
|
+
if (byType.instructions.length > 0) {
|
|
580
|
+
console.log(` Instructions:`);
|
|
581
|
+
for (const item of byType.instructions) {
|
|
582
|
+
console.log(` ${chalk.cyan(item.name.padEnd(20))} ${chalk.gray(formatAgentList(item.agents))}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
541
585
|
console.log();
|
|
542
586
|
}
|
|
543
587
|
if (upToDateItems.length > 0) {
|
|
544
588
|
console.log(chalk.gray(' UP TO DATE (no changes):\n'));
|
|
545
|
-
const byType = { command: [], skill: [], hook: [], mcp: [] };
|
|
589
|
+
const byType = { command: [], skill: [], hook: [], mcp: [], instructions: [] };
|
|
546
590
|
for (const item of upToDateItems)
|
|
547
591
|
byType[item.type].push(item);
|
|
548
592
|
if (byType.command.length > 0) {
|
|
@@ -563,11 +607,17 @@ program
|
|
|
563
607
|
console.log(` ${chalk.dim(item.name.padEnd(20))} ${chalk.dim(formatAgentList(item.agents))}`);
|
|
564
608
|
}
|
|
565
609
|
}
|
|
610
|
+
if (byType.instructions.length > 0) {
|
|
611
|
+
console.log(` Instructions:`);
|
|
612
|
+
for (const item of byType.instructions) {
|
|
613
|
+
console.log(` ${chalk.dim(item.name.padEnd(20))} ${chalk.dim(formatAgentList(item.agents))}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
566
616
|
console.log();
|
|
567
617
|
}
|
|
568
618
|
if (existingItems.length > 0) {
|
|
569
619
|
console.log(chalk.yellow(' EXISTING (conflicts):\n'));
|
|
570
|
-
const byType = { command: [], skill: [], hook: [], mcp: [] };
|
|
620
|
+
const byType = { command: [], skill: [], hook: [], mcp: [], instructions: [] };
|
|
571
621
|
for (const item of existingItems)
|
|
572
622
|
byType[item.type].push(item);
|
|
573
623
|
if (byType.command.length > 0) {
|
|
@@ -594,6 +644,12 @@ program
|
|
|
594
644
|
console.log(` ${chalk.yellow(item.name.padEnd(20))} ${chalk.gray(formatAgentList(item.agents))}`);
|
|
595
645
|
}
|
|
596
646
|
}
|
|
647
|
+
if (byType.instructions.length > 0) {
|
|
648
|
+
console.log(` Instructions:`);
|
|
649
|
+
for (const item of byType.instructions) {
|
|
650
|
+
console.log(` ${chalk.yellow(item.name.padEnd(20))} ${chalk.gray(formatAgentList(item.agents))}`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
597
653
|
console.log();
|
|
598
654
|
}
|
|
599
655
|
if (newItems.length === 0 && existingItems.length === 0) {
|
|
@@ -666,8 +722,8 @@ program
|
|
|
666
722
|
}
|
|
667
723
|
// Install new items (no conflicts)
|
|
668
724
|
console.log();
|
|
669
|
-
let installed = { commands: 0, skills: 0, hooks: 0, mcps: 0 };
|
|
670
|
-
let skipped = { commands: 0, skills: 0, hooks: 0, mcps: 0 };
|
|
725
|
+
let installed = { commands: 0, skills: 0, hooks: 0, mcps: 0, instructions: 0 };
|
|
726
|
+
let skipped = { commands: 0, skills: 0, hooks: 0, mcps: 0, instructions: 0 };
|
|
671
727
|
// Install commands
|
|
672
728
|
const cmdSpinner = ora('Installing commands...').start();
|
|
673
729
|
for (const item of [...newItems, ...existingItems].filter((i) => i.type === 'command')) {
|
|
@@ -679,8 +735,13 @@ program
|
|
|
679
735
|
for (const agentId of item.agents) {
|
|
680
736
|
const sourcePath = resolveCommandSource(localPath, item.name, agentId);
|
|
681
737
|
if (sourcePath) {
|
|
682
|
-
installCommand(sourcePath, agentId, item.name, method);
|
|
683
|
-
|
|
738
|
+
const result = installCommand(sourcePath, agentId, item.name, method);
|
|
739
|
+
if (result.error) {
|
|
740
|
+
console.log(chalk.yellow(`\n Warning: ${item.name} (${AGENTS[agentId].name}): ${result.error}`));
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
installed.commands++;
|
|
744
|
+
}
|
|
684
745
|
}
|
|
685
746
|
}
|
|
686
747
|
}
|
|
@@ -759,6 +820,39 @@ program
|
|
|
759
820
|
mcpSpinner.info('No MCP servers to register');
|
|
760
821
|
}
|
|
761
822
|
}
|
|
823
|
+
// Install instructions
|
|
824
|
+
const instructionItems = [...newItems, ...existingItems].filter((i) => i.type === 'instructions');
|
|
825
|
+
if (instructionItems.length > 0) {
|
|
826
|
+
const instrSpinner = ora('Installing instructions...').start();
|
|
827
|
+
for (const item of instructionItems) {
|
|
828
|
+
const decision = item.isNew ? 'overwrite' : decisions.get(`instructions:${item.name}`);
|
|
829
|
+
if (decision === 'skip') {
|
|
830
|
+
skipped.instructions++;
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
for (const agentId of item.agents) {
|
|
834
|
+
const sourcePath = resolveInstructionsSource(localPath, agentId);
|
|
835
|
+
if (sourcePath) {
|
|
836
|
+
const result = installInstructions(sourcePath, agentId, method);
|
|
837
|
+
if (result.error) {
|
|
838
|
+
console.log(chalk.yellow(`\n Warning: ${item.name} (${AGENTS[agentId].name}): ${result.error}`));
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
installed.instructions++;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (skipped.instructions > 0) {
|
|
847
|
+
instrSpinner.succeed(`Installed ${installed.instructions} instructions (skipped ${skipped.instructions})`);
|
|
848
|
+
}
|
|
849
|
+
else if (installed.instructions > 0) {
|
|
850
|
+
instrSpinner.succeed(`Installed ${installed.instructions} instructions`);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
instrSpinner.info('No instructions to install');
|
|
854
|
+
}
|
|
855
|
+
}
|
|
762
856
|
// Sync CLI versions (user scope only)
|
|
763
857
|
if (isUserScope && !options.skipClis && manifest?.clis) {
|
|
764
858
|
const cliSpinner = ora('Checking CLI versions...').start();
|
|
@@ -852,6 +946,44 @@ program
|
|
|
852
946
|
exported++;
|
|
853
947
|
}
|
|
854
948
|
}
|
|
949
|
+
// Export MCP servers from installed agents
|
|
950
|
+
console.log();
|
|
951
|
+
let mcpExported = 0;
|
|
952
|
+
const mcpByName = new Map();
|
|
953
|
+
for (const agentId of MCP_CAPABLE_AGENTS) {
|
|
954
|
+
if (!cliStates[agentId]?.installed)
|
|
955
|
+
continue;
|
|
956
|
+
const mcps = listInstalledMcpsWithScope(agentId);
|
|
957
|
+
for (const mcp of mcps) {
|
|
958
|
+
if (mcp.scope !== 'user')
|
|
959
|
+
continue; // Only export user-scoped MCPs
|
|
960
|
+
const existing = mcpByName.get(mcp.name);
|
|
961
|
+
if (existing) {
|
|
962
|
+
if (!existing.agents.includes(agentId)) {
|
|
963
|
+
existing.agents.push(agentId);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
mcpByName.set(mcp.name, {
|
|
968
|
+
command: mcp.command || '',
|
|
969
|
+
agents: [agentId],
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (mcpByName.size > 0) {
|
|
975
|
+
manifest.mcp = manifest.mcp || {};
|
|
976
|
+
for (const [name, config] of mcpByName) {
|
|
977
|
+
manifest.mcp[name] = {
|
|
978
|
+
command: config.command,
|
|
979
|
+
transport: 'stdio',
|
|
980
|
+
agents: config.agents,
|
|
981
|
+
scope: 'user',
|
|
982
|
+
};
|
|
983
|
+
console.log(` ${chalk.green('+')} MCP: ${name} (${config.agents.map(id => AGENTS[id].name).join(', ')})`);
|
|
984
|
+
mcpExported++;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
855
987
|
if (!options.exportOnly) {
|
|
856
988
|
writeManifest(localPath, manifest);
|
|
857
989
|
console.log(chalk.bold(`\nUpdated ${MANIFEST_FILENAME}`));
|
|
@@ -952,8 +1084,13 @@ commandsCmd
|
|
|
952
1084
|
continue;
|
|
953
1085
|
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
954
1086
|
if (sourcePath) {
|
|
955
|
-
installCommand(sourcePath, agentId, command.name, 'symlink');
|
|
956
|
-
|
|
1087
|
+
const result = installCommand(sourcePath, agentId, command.name, 'symlink');
|
|
1088
|
+
if (result.error) {
|
|
1089
|
+
console.log(` ${chalk.yellow('!')} ${AGENTS[agentId].name}: ${result.error}`);
|
|
1090
|
+
}
|
|
1091
|
+
else {
|
|
1092
|
+
console.log(` ${chalk.green('+')} ${AGENTS[agentId].name}`);
|
|
1093
|
+
}
|
|
957
1094
|
}
|
|
958
1095
|
}
|
|
959
1096
|
}
|
|
@@ -1427,6 +1564,152 @@ skillsCmd
|
|
|
1427
1564
|
}
|
|
1428
1565
|
});
|
|
1429
1566
|
// =============================================================================
|
|
1567
|
+
// INSTRUCTIONS COMMANDS
|
|
1568
|
+
// =============================================================================
|
|
1569
|
+
const instructionsCmd = program
|
|
1570
|
+
.command('instructions')
|
|
1571
|
+
.alias('instr')
|
|
1572
|
+
.description('Manage agent instructions (CLAUDE.md, GEMINI.md, etc.)');
|
|
1573
|
+
instructionsCmd
|
|
1574
|
+
.command('list')
|
|
1575
|
+
.description('List installed instructions files')
|
|
1576
|
+
.option('-a, --agent <agent>', 'Filter by agent')
|
|
1577
|
+
.action(async (options) => {
|
|
1578
|
+
const cwd = process.cwd();
|
|
1579
|
+
const agents = options.agent
|
|
1580
|
+
? [resolveAgentName(options.agent)].filter(Boolean)
|
|
1581
|
+
: ALL_AGENT_IDS;
|
|
1582
|
+
console.log(chalk.bold('Installed Instructions\n'));
|
|
1583
|
+
for (const agentId of agents) {
|
|
1584
|
+
const agent = AGENTS[agentId];
|
|
1585
|
+
const installed = listInstalledInstructionsWithScope(agentId, cwd);
|
|
1586
|
+
const userInstr = installed.find((i) => i.scope === 'user');
|
|
1587
|
+
const projectInstr = installed.find((i) => i.scope === 'project');
|
|
1588
|
+
const userStatus = userInstr?.exists ? chalk.green(agent.instructionsFile) : chalk.gray('none');
|
|
1589
|
+
const projectStatus = projectInstr?.exists ? chalk.yellow(agent.instructionsFile) : chalk.gray('none');
|
|
1590
|
+
console.log(` ${chalk.bold(agent.name)}:`);
|
|
1591
|
+
console.log(` ${chalk.gray('User:')} ${userStatus}`);
|
|
1592
|
+
console.log(` ${chalk.gray('Project:')} ${projectStatus}`);
|
|
1593
|
+
console.log();
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
instructionsCmd
|
|
1597
|
+
.command('view [agent]')
|
|
1598
|
+
.alias('show')
|
|
1599
|
+
.description('View instructions content for an agent')
|
|
1600
|
+
.option('-s, --scope <scope>', 'Scope: user or project', 'user')
|
|
1601
|
+
.action(async (agentArg, options) => {
|
|
1602
|
+
const cwd = process.cwd();
|
|
1603
|
+
let agentId;
|
|
1604
|
+
if (agentArg) {
|
|
1605
|
+
agentId = resolveAgentName(agentArg) || undefined;
|
|
1606
|
+
if (!agentId) {
|
|
1607
|
+
console.log(chalk.red(`Unknown agent: ${agentArg}`));
|
|
1608
|
+
process.exit(1);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
else {
|
|
1612
|
+
const choices = ALL_AGENT_IDS.filter((id) => instructionsExists(id, 'user', cwd) || instructionsExists(id, 'project', cwd));
|
|
1613
|
+
if (choices.length === 0) {
|
|
1614
|
+
console.log(chalk.yellow('No instructions files found.'));
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
agentId = await select({
|
|
1618
|
+
message: 'Select agent:',
|
|
1619
|
+
choices: choices.map((id) => ({ name: AGENTS[id].name, value: id })),
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
const scope = (options?.scope || 'user');
|
|
1623
|
+
const content = getInstructionsContent(agentId, scope, cwd);
|
|
1624
|
+
if (!content) {
|
|
1625
|
+
console.log(chalk.yellow(`No ${scope} instructions found for ${AGENTS[agentId].name}`));
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
console.log(chalk.bold(`\n${AGENTS[agentId].name} Instructions (${scope}):\n`));
|
|
1629
|
+
console.log(content);
|
|
1630
|
+
});
|
|
1631
|
+
instructionsCmd
|
|
1632
|
+
.command('diff [agent]')
|
|
1633
|
+
.description('Show differences between local and repo instructions')
|
|
1634
|
+
.action(async (agentArg) => {
|
|
1635
|
+
const cwd = process.cwd();
|
|
1636
|
+
const meta = readState();
|
|
1637
|
+
const scopes = getScopesByPriority();
|
|
1638
|
+
if (scopes.length === 0) {
|
|
1639
|
+
console.log(chalk.yellow('No repo configured. Run: agents repo add <source>'));
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
const agents = agentArg
|
|
1643
|
+
? [resolveAgentName(agentArg)].filter(Boolean)
|
|
1644
|
+
: ALL_AGENT_IDS;
|
|
1645
|
+
const diff = await import('diff');
|
|
1646
|
+
for (const { name: scopeName, config } of scopes) {
|
|
1647
|
+
const localPath = getRepoLocalPath(config.source);
|
|
1648
|
+
const repoInstructions = discoverInstructionsFromRepo(localPath);
|
|
1649
|
+
for (const agentId of agents) {
|
|
1650
|
+
const repoInstr = repoInstructions.find((i) => i.agentId === agentId);
|
|
1651
|
+
if (!repoInstr)
|
|
1652
|
+
continue;
|
|
1653
|
+
const installedContent = getInstructionsContent(agentId, 'user', cwd);
|
|
1654
|
+
if (!installedContent) {
|
|
1655
|
+
console.log(`${chalk.bold(AGENTS[agentId].name)}: ${chalk.green('NEW')} (not installed)`);
|
|
1656
|
+
continue;
|
|
1657
|
+
}
|
|
1658
|
+
const repoContent = fs.readFileSync(repoInstr.sourcePath, 'utf-8');
|
|
1659
|
+
if (installedContent.trim() === repoContent.trim()) {
|
|
1660
|
+
console.log(`${chalk.bold(AGENTS[agentId].name)}: ${chalk.gray('up to date')}`);
|
|
1661
|
+
continue;
|
|
1662
|
+
}
|
|
1663
|
+
console.log(`${chalk.bold(AGENTS[agentId].name)}:`);
|
|
1664
|
+
const changes = diff.diffLines(installedContent, repoContent);
|
|
1665
|
+
for (const change of changes) {
|
|
1666
|
+
if (change.added) {
|
|
1667
|
+
process.stdout.write(chalk.green(change.value));
|
|
1668
|
+
}
|
|
1669
|
+
else if (change.removed) {
|
|
1670
|
+
process.stdout.write(chalk.red(change.value));
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
console.log();
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
instructionsCmd
|
|
1678
|
+
.command('push <agent>')
|
|
1679
|
+
.description('Save project-scoped instructions to user scope')
|
|
1680
|
+
.action((agentArg) => {
|
|
1681
|
+
const cwd = process.cwd();
|
|
1682
|
+
const agentId = resolveAgentName(agentArg);
|
|
1683
|
+
if (!agentId) {
|
|
1684
|
+
console.log(chalk.red(`Unknown agent: ${agentArg}`));
|
|
1685
|
+
process.exit(1);
|
|
1686
|
+
}
|
|
1687
|
+
const result = promoteInstructionsToUser(agentId, cwd);
|
|
1688
|
+
if (result.success) {
|
|
1689
|
+
console.log(chalk.green(`Pushed ${AGENTS[agentId].instructionsFile} to user scope`));
|
|
1690
|
+
}
|
|
1691
|
+
else {
|
|
1692
|
+
console.log(chalk.red(result.error || 'Failed to push instructions'));
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
instructionsCmd
|
|
1696
|
+
.command('remove <agent>')
|
|
1697
|
+
.description('Remove user-scoped instructions for an agent')
|
|
1698
|
+
.action((agentArg) => {
|
|
1699
|
+
const agentId = resolveAgentName(agentArg);
|
|
1700
|
+
if (!agentId) {
|
|
1701
|
+
console.log(chalk.red(`Unknown agent: ${agentArg}`));
|
|
1702
|
+
process.exit(1);
|
|
1703
|
+
}
|
|
1704
|
+
const result = uninstallInstructions(agentId);
|
|
1705
|
+
if (result) {
|
|
1706
|
+
console.log(chalk.green(`Removed ${AGENTS[agentId].instructionsFile}`));
|
|
1707
|
+
}
|
|
1708
|
+
else {
|
|
1709
|
+
console.log(chalk.yellow(`No instructions file found for ${AGENTS[agentId].name}`));
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
// =============================================================================
|
|
1430
1713
|
// MCP COMMANDS
|
|
1431
1714
|
// =============================================================================
|
|
1432
1715
|
const mcpCmd = program
|
|
@@ -2300,18 +2583,29 @@ program
|
|
|
2300
2583
|
if (hasCommands) {
|
|
2301
2584
|
console.log(chalk.bold('\nInstalling commands...'));
|
|
2302
2585
|
let installed = 0;
|
|
2586
|
+
let failed = 0;
|
|
2303
2587
|
for (const command of commands) {
|
|
2304
2588
|
for (const agentId of agents) {
|
|
2305
2589
|
if (!gitCliStates[agentId]?.installed && agentId !== 'cursor')
|
|
2306
2590
|
continue;
|
|
2307
2591
|
const sourcePath = resolveCommandSource(localPath, command.name, agentId);
|
|
2308
2592
|
if (sourcePath) {
|
|
2309
|
-
installCommand(sourcePath, agentId, command.name, 'symlink');
|
|
2310
|
-
|
|
2593
|
+
const result = installCommand(sourcePath, agentId, command.name, 'symlink');
|
|
2594
|
+
if (result.error) {
|
|
2595
|
+
failed++;
|
|
2596
|
+
}
|
|
2597
|
+
else {
|
|
2598
|
+
installed++;
|
|
2599
|
+
}
|
|
2311
2600
|
}
|
|
2312
2601
|
}
|
|
2313
2602
|
}
|
|
2314
|
-
|
|
2603
|
+
if (failed > 0) {
|
|
2604
|
+
console.log(` Installed ${installed} command instances (${failed} failed)`);
|
|
2605
|
+
}
|
|
2606
|
+
else {
|
|
2607
|
+
console.log(` Installed ${installed} command instances`);
|
|
2608
|
+
}
|
|
2315
2609
|
}
|
|
2316
2610
|
// Install skills
|
|
2317
2611
|
if (hasSkills) {
|