agentloom 0.1.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/LICENSE +21 -0
- package/README.md +234 -0
- package/ThirdPartyNoticeText.txt +3 -0
- package/bin/cli.mjs +8 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +61 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.js +62 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +188 -0
- package/dist/commands/skills.d.ts +1 -0
- package/dist/commands/skills.js +11 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +25 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +71 -0
- package/dist/core/agents.d.ts +7 -0
- package/dist/core/agents.js +67 -0
- package/dist/core/argv.d.ts +5 -0
- package/dist/core/argv.js +52 -0
- package/dist/core/copy.d.ts +16 -0
- package/dist/core/copy.js +167 -0
- package/dist/core/fs.d.ts +13 -0
- package/dist/core/fs.js +70 -0
- package/dist/core/importer.d.ts +21 -0
- package/dist/core/importer.js +201 -0
- package/dist/core/lockfile.d.ts +4 -0
- package/dist/core/lockfile.js +25 -0
- package/dist/core/manifest.d.ts +3 -0
- package/dist/core/manifest.js +17 -0
- package/dist/core/mcp.d.ts +4 -0
- package/dist/core/mcp.js +73 -0
- package/dist/core/scope.d.ts +9 -0
- package/dist/core/scope.js +64 -0
- package/dist/core/settings.d.ts +6 -0
- package/dist/core/settings.js +54 -0
- package/dist/core/sources.d.ts +20 -0
- package/dist/core/sources.js +162 -0
- package/dist/core/version-notifier.d.ts +8 -0
- package/dist/core/version-notifier.js +142 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +25 -0
- package/dist/sync/index.d.ts +15 -0
- package/dist/sync/index.js +482 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.js +8 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agentloom contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# agentloom
|
|
2
|
+
|
|
3
|
+
`agentloom` is a unified CLI for managing agent definitions and MCP configuration across multiple AI coding tools.
|
|
4
|
+
|
|
5
|
+
It uses `.agents` as the canonical source of truth and syncs provider-native files for:
|
|
6
|
+
|
|
7
|
+
- Cursor
|
|
8
|
+
- Claude
|
|
9
|
+
- Codex
|
|
10
|
+
- OpenCode
|
|
11
|
+
- Gemini
|
|
12
|
+
- Copilot
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm i -g agentloom
|
|
18
|
+
# or
|
|
19
|
+
npx agentloom --help
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Canonical layout
|
|
23
|
+
|
|
24
|
+
Project scope:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
.agents/
|
|
28
|
+
agents/
|
|
29
|
+
reviewer.md
|
|
30
|
+
debugger.md
|
|
31
|
+
mcp.json
|
|
32
|
+
agents.lock.json
|
|
33
|
+
settings.local.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Global scope uses `~/.agents` with the same file layout.
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
### `agentloom skills ...`
|
|
41
|
+
Pass-through wrapper to `npx skills ...` from
|
|
42
|
+
[`vercel-labs/skills`](https://github.com/vercel-labs/skills).
|
|
43
|
+
|
|
44
|
+
### `agentloom add <source>`
|
|
45
|
+
Import canonical agents/MCP from:
|
|
46
|
+
|
|
47
|
+
- local repo path
|
|
48
|
+
- GitHub slug (`owner/repo`)
|
|
49
|
+
- generic git URL
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
|
|
53
|
+
- `--ref <ref>`: git ref (branch/tag/commit) for remote sources
|
|
54
|
+
- `--subdir <path>`: subdirectory inside source repo
|
|
55
|
+
- `--rename <name>`: rename imported agent when importing a single agent
|
|
56
|
+
- `--local | --global`: choose destination scope
|
|
57
|
+
- `--yes`: skip interactive conflict prompts
|
|
58
|
+
- `--no-sync`: skip post-import sync
|
|
59
|
+
- `--providers <csv>`: limit post-import sync providers
|
|
60
|
+
- `--dry-run`: show sync changes without writing provider files
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
agentloom add vercel-labs/skills --subdir skills
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `agentloom update`
|
|
69
|
+
Refresh lockfile sources (`agents.lock.json`) and re-import changed revisions.
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
|
|
73
|
+
- `--local | --global`: choose lockfile scope
|
|
74
|
+
- `--yes`: skip conflict prompts during re-import
|
|
75
|
+
- `--no-sync`: skip post-update sync
|
|
76
|
+
- `--providers <csv>`: limit post-update sync providers
|
|
77
|
+
- `--dry-run`: show sync changes without writing provider files
|
|
78
|
+
|
|
79
|
+
### `agentloom sync`
|
|
80
|
+
Generate provider-specific outputs from canonical `.agents` data.
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
|
|
84
|
+
- `--local | --global`: choose canonical scope
|
|
85
|
+
- `--providers <csv>`: limit sync providers
|
|
86
|
+
- `--yes`: auto-delete stale generated files
|
|
87
|
+
- `--dry-run`: show planned changes without writing files
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
agentloom sync --providers codex,claude,cursor
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `agentloom mcp add|list|delete`
|
|
96
|
+
Manage canonical MCP servers in `.agents/mcp.json`.
|
|
97
|
+
|
|
98
|
+
`mcp add` options:
|
|
99
|
+
|
|
100
|
+
- `--url <url>` or `--command <cmd>`: required transport config
|
|
101
|
+
- `--arg <value>`: repeatable command arg
|
|
102
|
+
- `--env KEY=VALUE`: repeatable environment variable
|
|
103
|
+
- `--providers <csv>`: provider-specific assignment
|
|
104
|
+
- `--local | --global`: choose canonical scope
|
|
105
|
+
- `--no-sync`: skip post-change sync
|
|
106
|
+
|
|
107
|
+
`mcp list` options:
|
|
108
|
+
|
|
109
|
+
- `--json`: print raw canonical JSON
|
|
110
|
+
- `--local | --global`: choose canonical scope
|
|
111
|
+
|
|
112
|
+
`mcp delete` options:
|
|
113
|
+
|
|
114
|
+
- `--local | --global`: choose canonical scope
|
|
115
|
+
- `--no-sync`: skip post-change sync
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
agentloom mcp add browser-tools --command npx --arg browser-tools-mcp
|
|
121
|
+
agentloom mcp list
|
|
122
|
+
agentloom mcp delete browser-tools
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Top-level help
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
agentloom --help
|
|
129
|
+
agentloom add --help
|
|
130
|
+
agentloom update --help
|
|
131
|
+
agentloom sync --help
|
|
132
|
+
agentloom mcp --help
|
|
133
|
+
agentloom mcp add --help
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Version update notice
|
|
137
|
+
|
|
138
|
+
`agentloom` now performs a best-effort npm version check and shows an update hint when a newer release is available.
|
|
139
|
+
|
|
140
|
+
- check is cached (`~/.agents/.agentloom-version-cache.json`)
|
|
141
|
+
- check runs at most once every 12 hours
|
|
142
|
+
- check is skipped in non-interactive sessions
|
|
143
|
+
- disable via:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
AGENTLOOM_DISABLE_UPDATE_NOTIFIER=1
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Scope resolution
|
|
150
|
+
|
|
151
|
+
If neither `--local` nor `--global` is provided:
|
|
152
|
+
|
|
153
|
+
- if `.agents/` exists in current directory, `agentloom` prompts for scope in interactive terminals
|
|
154
|
+
- in non-interactive mode, local scope is selected when `.agents/` exists
|
|
155
|
+
- otherwise global scope (`~/.agents`) is used
|
|
156
|
+
|
|
157
|
+
## Agent schema
|
|
158
|
+
|
|
159
|
+
Canonical agents are markdown files with YAML frontmatter.
|
|
160
|
+
|
|
161
|
+
```md
|
|
162
|
+
---
|
|
163
|
+
name: code-reviewer
|
|
164
|
+
description: Review changes and report issues.
|
|
165
|
+
claude:
|
|
166
|
+
model: sonnet
|
|
167
|
+
codex:
|
|
168
|
+
model: gpt-5.3-codex
|
|
169
|
+
reasoningEffort: low
|
|
170
|
+
webSearch: true
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
You are a strict reviewer...
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## MCP schema
|
|
177
|
+
|
|
178
|
+
Canonical MCP file format:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"version": 1,
|
|
183
|
+
"mcpServers": {
|
|
184
|
+
"browser": {
|
|
185
|
+
"base": {
|
|
186
|
+
"command": "npx",
|
|
187
|
+
"args": ["browser-tools-mcp"]
|
|
188
|
+
},
|
|
189
|
+
"providers": {
|
|
190
|
+
"codex": {
|
|
191
|
+
"args": ["browser-tools-mcp", "--codex"]
|
|
192
|
+
},
|
|
193
|
+
"gemini": false
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Codex multi-agent output
|
|
201
|
+
|
|
202
|
+
For Codex, `agentloom sync` writes role-based multi-agent config:
|
|
203
|
+
|
|
204
|
+
- `.codex/config.toml` (`[features].multi_agent = true`, `[agents.<role>]`)
|
|
205
|
+
- `.codex/agents/<role>.toml`
|
|
206
|
+
- `.codex/agents/<role>.instructions.md`
|
|
207
|
+
|
|
208
|
+
This follows official Codex multi-agent guidance.
|
|
209
|
+
|
|
210
|
+
## Development
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pnpm install
|
|
214
|
+
pnpm check
|
|
215
|
+
pnpm build
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Release and publish
|
|
219
|
+
|
|
220
|
+
The GitHub Actions publish workflow is defined in `.github/workflows/release.yml`.
|
|
221
|
+
|
|
222
|
+
- Push to `main` with `[patch]` or `[minor]` in commit subjects to trigger an automated npm release.
|
|
223
|
+
- Run the workflow manually (`workflow_dispatch`) and choose `patch` or `minor` to force a release.
|
|
224
|
+
- Push a `v*` tag (for example `v0.2.0`) to publish an already-versioned commit without bumping.
|
|
225
|
+
- Automated bump releases create a `vX.Y.Z` commit/tag via `npm version`, push both to GitHub, then publish to npm with provenance.
|
|
226
|
+
|
|
227
|
+
Required GitHub configuration:
|
|
228
|
+
|
|
229
|
+
- Secret: `NPM_TOKEN` (automation token from the npm account that should own `agentloom`).
|
|
230
|
+
- Repository setting: `Actions -> General -> Workflow permissions -> Read and write permissions` (required so CI can push version commits/tags).
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT
|
package/bin/cli.mjs
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runCli(argv: string[]): Promise<void>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { parseArgs } from "./core/argv.js";
|
|
2
|
+
import { runAddCommand } from "./commands/add.js";
|
|
3
|
+
import { runMcpCommand } from "./commands/mcp.js";
|
|
4
|
+
import { runSkillsPassthrough } from "./commands/skills.js";
|
|
5
|
+
import { runSyncCommand } from "./commands/sync.js";
|
|
6
|
+
import { runUpdateCommand } from "./commands/update.js";
|
|
7
|
+
import { formatUnknownCommandError, getRootHelpText } from "./core/copy.js";
|
|
8
|
+
import { maybeNotifyVersionUpdate } from "./core/version-notifier.js";
|
|
9
|
+
import { getCliVersion } from "./core/version.js";
|
|
10
|
+
export async function runCli(argv) {
|
|
11
|
+
const command = argv[0];
|
|
12
|
+
const version = getCliVersion();
|
|
13
|
+
if (!command ||
|
|
14
|
+
command === "help" ||
|
|
15
|
+
command === "--help" ||
|
|
16
|
+
command === "-h") {
|
|
17
|
+
printHelp();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
21
|
+
console.log(version);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (command === "skills") {
|
|
25
|
+
runSkillsPassthrough(argv.slice(1));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const parsed = parseArgs(argv);
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
if (!parsed.help) {
|
|
31
|
+
await maybeNotifyVersionUpdate({
|
|
32
|
+
command,
|
|
33
|
+
currentVersion: version,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
switch (command) {
|
|
37
|
+
case "add":
|
|
38
|
+
await runAddCommand(parsed, cwd);
|
|
39
|
+
return;
|
|
40
|
+
case "update":
|
|
41
|
+
await runUpdateCommand(parsed, cwd);
|
|
42
|
+
return;
|
|
43
|
+
case "sync":
|
|
44
|
+
await runSyncCommand(parsed, cwd);
|
|
45
|
+
return;
|
|
46
|
+
case "mcp":
|
|
47
|
+
await runMcpCommand(parsed, cwd);
|
|
48
|
+
return;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(formatUnknownCommandError(command));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function printHelp() {
|
|
54
|
+
console.log(getRootHelpText());
|
|
55
|
+
}
|
|
56
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
57
|
+
runCli(process.argv.slice(2)).catch((err) => {
|
|
58
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { importSource, NonInteractiveConflictError } from "../core/importer.js";
|
|
2
|
+
import { resolveScope } from "../core/scope.js";
|
|
3
|
+
import { updateLastScope } from "../core/settings.js";
|
|
4
|
+
import { parseProvidersFlag } from "../core/argv.js";
|
|
5
|
+
import { formatUsageError, getAddHelpText } from "../core/copy.js";
|
|
6
|
+
import { formatSyncSummary, syncFromCanonical } from "../sync/index.js";
|
|
7
|
+
export async function runAddCommand(argv, cwd) {
|
|
8
|
+
if (argv.help) {
|
|
9
|
+
console.log(getAddHelpText());
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const source = argv._[1];
|
|
13
|
+
if (typeof source !== "string" || source.trim() === "") {
|
|
14
|
+
throw new Error(formatUsageError({
|
|
15
|
+
issue: "Missing required <source>.",
|
|
16
|
+
usage: "agentloom add <source> [--ref <ref>] [--subdir <path>] [options]",
|
|
17
|
+
example: "agentloom add vercel-labs/skills --subdir skills",
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
const nonInteractive = !(process.stdin.isTTY && process.stdout.isTTY);
|
|
21
|
+
const paths = await resolveScope({
|
|
22
|
+
cwd,
|
|
23
|
+
global: Boolean(argv.global),
|
|
24
|
+
local: Boolean(argv.local),
|
|
25
|
+
interactive: !nonInteractive,
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
const summary = await importSource({
|
|
29
|
+
source,
|
|
30
|
+
ref: typeof argv.ref === "string" ? argv.ref : undefined,
|
|
31
|
+
subdir: typeof argv.subdir === "string" ? argv.subdir : undefined,
|
|
32
|
+
rename: typeof argv.rename === "string" ? argv.rename : undefined,
|
|
33
|
+
yes: Boolean(argv.yes),
|
|
34
|
+
nonInteractive,
|
|
35
|
+
paths,
|
|
36
|
+
});
|
|
37
|
+
console.log(`Imported source: ${summary.source}`);
|
|
38
|
+
console.log(`Source type: ${summary.sourceType}`);
|
|
39
|
+
console.log(`Resolved commit: ${summary.resolvedCommit}`);
|
|
40
|
+
console.log(`Imported agents: ${summary.importedAgents.length}`);
|
|
41
|
+
console.log(`Imported MCP servers: ${summary.importedMcpServers.length}`);
|
|
42
|
+
updateLastScope(paths.settingsPath, paths.scope);
|
|
43
|
+
if (!argv["no-sync"]) {
|
|
44
|
+
const syncSummary = await syncFromCanonical({
|
|
45
|
+
paths,
|
|
46
|
+
providers: parseProvidersFlag(argv.providers),
|
|
47
|
+
yes: Boolean(argv.yes),
|
|
48
|
+
nonInteractive,
|
|
49
|
+
dryRun: Boolean(argv["dry-run"]),
|
|
50
|
+
});
|
|
51
|
+
console.log("");
|
|
52
|
+
console.log(formatSyncSummary(syncSummary, paths.agentsRoot));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (err instanceof NonInteractiveConflictError) {
|
|
57
|
+
console.error(err.message);
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { getStringArrayFlag, parseProvidersFlag } from "../core/argv.js";
|
|
2
|
+
import { formatUsageError, getMcpAddHelpText, getMcpDeleteHelpText, getMcpHelpText, getMcpListHelpText, } from "../core/copy.js";
|
|
3
|
+
import { readCanonicalMcp, writeCanonicalMcp } from "../core/mcp.js";
|
|
4
|
+
import { resolveScope } from "../core/scope.js";
|
|
5
|
+
import { formatSyncSummary, syncFromCanonical } from "../sync/index.js";
|
|
6
|
+
export async function runMcpCommand(argv, cwd) {
|
|
7
|
+
const action = argv._[1];
|
|
8
|
+
if (argv.help) {
|
|
9
|
+
if (action === "add") {
|
|
10
|
+
console.log(getMcpAddHelpText());
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (action === "list") {
|
|
14
|
+
console.log(getMcpListHelpText());
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (action === "delete") {
|
|
18
|
+
console.log(getMcpDeleteHelpText());
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
console.log(getMcpHelpText());
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (action !== "add" && action !== "list" && action !== "delete") {
|
|
25
|
+
throw new Error(formatUsageError({
|
|
26
|
+
issue: "Invalid mcp command.",
|
|
27
|
+
usage: "agentloom mcp <add|list|delete> [options]",
|
|
28
|
+
example: "agentloom mcp add browser --command npx --arg browser-tools-mcp",
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
const nonInteractive = !(process.stdin.isTTY && process.stdout.isTTY);
|
|
32
|
+
const paths = await resolveScope({
|
|
33
|
+
cwd,
|
|
34
|
+
global: Boolean(argv.global),
|
|
35
|
+
local: Boolean(argv.local),
|
|
36
|
+
interactive: !nonInteractive,
|
|
37
|
+
});
|
|
38
|
+
if (action === "list") {
|
|
39
|
+
runMcpList(paths, Boolean(argv.json));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (action === "add") {
|
|
43
|
+
const name = argv._[2];
|
|
44
|
+
if (typeof name !== "string" || !name.trim()) {
|
|
45
|
+
throw new Error(formatUsageError({
|
|
46
|
+
issue: "Missing required MCP server name.",
|
|
47
|
+
usage: "agentloom mcp add <name> (--url <url> | --command <cmd>) [options]",
|
|
48
|
+
example: "agentloom mcp add browser --command npx --arg browser-tools-mcp",
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
runMcpAdd(paths, argv, name.trim());
|
|
52
|
+
if (!argv["no-sync"]) {
|
|
53
|
+
const summary = await syncFromCanonical({
|
|
54
|
+
paths,
|
|
55
|
+
providers: parseProvidersFlag(argv.providers),
|
|
56
|
+
yes: Boolean(argv.yes),
|
|
57
|
+
nonInteractive,
|
|
58
|
+
});
|
|
59
|
+
console.log("");
|
|
60
|
+
console.log(formatSyncSummary(summary, paths.agentsRoot));
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (action === "delete") {
|
|
65
|
+
const name = argv._[2];
|
|
66
|
+
if (typeof name !== "string" || !name.trim()) {
|
|
67
|
+
throw new Error(formatUsageError({
|
|
68
|
+
issue: "Missing required MCP server name.",
|
|
69
|
+
usage: "agentloom mcp delete <name> [options]",
|
|
70
|
+
example: "agentloom mcp delete browser",
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
const mcp = readCanonicalMcp(paths);
|
|
74
|
+
if (!(name in mcp.mcpServers)) {
|
|
75
|
+
throw new Error(formatUsageError({
|
|
76
|
+
issue: `MCP server "${name}" was not found in canonical config.`,
|
|
77
|
+
usage: "agentloom mcp list [--json] [--local|--global]",
|
|
78
|
+
example: "agentloom mcp list --json",
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
delete mcp.mcpServers[name];
|
|
82
|
+
writeCanonicalMcp(paths, mcp);
|
|
83
|
+
console.log(`Deleted MCP server: ${name}`);
|
|
84
|
+
if (!argv["no-sync"]) {
|
|
85
|
+
const summary = await syncFromCanonical({
|
|
86
|
+
paths,
|
|
87
|
+
providers: parseProvidersFlag(argv.providers),
|
|
88
|
+
yes: Boolean(argv.yes),
|
|
89
|
+
nonInteractive,
|
|
90
|
+
});
|
|
91
|
+
console.log("");
|
|
92
|
+
console.log(formatSyncSummary(summary, paths.agentsRoot));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function runMcpList(paths, asJson) {
|
|
97
|
+
const mcp = readCanonicalMcp(paths);
|
|
98
|
+
const names = Object.keys(mcp.mcpServers).sort();
|
|
99
|
+
if (asJson) {
|
|
100
|
+
console.log(JSON.stringify(mcp, null, 2));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (names.length === 0) {
|
|
104
|
+
console.log("No MCP servers configured.");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const name of names) {
|
|
108
|
+
const server = mcp.mcpServers[name];
|
|
109
|
+
const transport = server.base && typeof server.base === "object"
|
|
110
|
+
? server.base.url
|
|
111
|
+
? "http"
|
|
112
|
+
: server.base.command
|
|
113
|
+
? "stdio"
|
|
114
|
+
: "custom"
|
|
115
|
+
: server.url
|
|
116
|
+
? "http"
|
|
117
|
+
: server.command
|
|
118
|
+
? "stdio"
|
|
119
|
+
: "custom";
|
|
120
|
+
console.log(`${name} (${transport})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function runMcpAdd(paths, argv, name) {
|
|
124
|
+
const mcp = readCanonicalMcp(paths);
|
|
125
|
+
const baseConfig = {};
|
|
126
|
+
if (typeof argv.url === "string" && argv.url.trim()) {
|
|
127
|
+
baseConfig.url = argv.url.trim();
|
|
128
|
+
}
|
|
129
|
+
if (typeof argv.command === "string" && argv.command.trim()) {
|
|
130
|
+
baseConfig.command = argv.command.trim();
|
|
131
|
+
}
|
|
132
|
+
if (!baseConfig.url && !baseConfig.command) {
|
|
133
|
+
throw new Error(formatUsageError({
|
|
134
|
+
issue: "Missing MCP transport. Use --url or --command.",
|
|
135
|
+
usage: "agentloom mcp add <name> (--url <url> | --command <cmd>) [options]",
|
|
136
|
+
example: "agentloom mcp add browser --command npx --arg browser-tools-mcp",
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
const args = getStringArrayFlag(argv.arg);
|
|
140
|
+
if (args.length > 0) {
|
|
141
|
+
baseConfig.args = args;
|
|
142
|
+
}
|
|
143
|
+
const envPairs = getStringArrayFlag(argv.env);
|
|
144
|
+
if (envPairs.length > 0) {
|
|
145
|
+
const env = {};
|
|
146
|
+
for (const pair of envPairs) {
|
|
147
|
+
const separator = pair.indexOf("=");
|
|
148
|
+
if (separator <= 0) {
|
|
149
|
+
throw new Error(formatUsageError({
|
|
150
|
+
issue: `Invalid --env value "${pair}".`,
|
|
151
|
+
usage: "agentloom mcp add <name> ... --env KEY=VALUE [--env KEY2=VALUE2]",
|
|
152
|
+
example: "agentloom mcp add browser --command npx --env API_KEY=secret",
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
const key = pair.slice(0, separator).trim();
|
|
156
|
+
const value = pair.slice(separator + 1);
|
|
157
|
+
env[key] = value;
|
|
158
|
+
}
|
|
159
|
+
baseConfig.env = env;
|
|
160
|
+
}
|
|
161
|
+
const providers = parseProvidersFlag(argv.providers);
|
|
162
|
+
if (!providers || providers.length === 0) {
|
|
163
|
+
mcp.mcpServers[name] = {
|
|
164
|
+
base: baseConfig,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const providerMap = {};
|
|
169
|
+
const allProviders = [
|
|
170
|
+
"cursor",
|
|
171
|
+
"claude",
|
|
172
|
+
"codex",
|
|
173
|
+
"opencode",
|
|
174
|
+
"gemini",
|
|
175
|
+
"copilot",
|
|
176
|
+
];
|
|
177
|
+
for (const provider of allProviders) {
|
|
178
|
+
providerMap[provider] = providers.includes(provider)
|
|
179
|
+
? { ...baseConfig }
|
|
180
|
+
: false;
|
|
181
|
+
}
|
|
182
|
+
mcp.mcpServers[name] = {
|
|
183
|
+
providers: providerMap,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
writeCanonicalMcp(paths, mcp);
|
|
187
|
+
console.log(`Added MCP server: ${name}`);
|
|
188
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSkillsPassthrough(args: string[]): never;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
export function runSkillsPassthrough(args) {
|
|
3
|
+
const child = spawnSync("npx", ["skills", ...args], {
|
|
4
|
+
stdio: "inherit",
|
|
5
|
+
shell: false,
|
|
6
|
+
});
|
|
7
|
+
if (child.error) {
|
|
8
|
+
throw child.error;
|
|
9
|
+
}
|
|
10
|
+
process.exit(child.status ?? 1);
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { parseProvidersFlag } from "../core/argv.js";
|
|
2
|
+
import { getSyncHelpText } from "../core/copy.js";
|
|
3
|
+
import { resolveScope } from "../core/scope.js";
|
|
4
|
+
import { formatSyncSummary, syncFromCanonical } from "../sync/index.js";
|
|
5
|
+
export async function runSyncCommand(argv, cwd) {
|
|
6
|
+
if (argv.help) {
|
|
7
|
+
console.log(getSyncHelpText());
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const nonInteractive = !(process.stdin.isTTY && process.stdout.isTTY);
|
|
11
|
+
const paths = await resolveScope({
|
|
12
|
+
cwd,
|
|
13
|
+
global: Boolean(argv.global),
|
|
14
|
+
local: Boolean(argv.local),
|
|
15
|
+
interactive: !nonInteractive,
|
|
16
|
+
});
|
|
17
|
+
const summary = await syncFromCanonical({
|
|
18
|
+
paths,
|
|
19
|
+
providers: parseProvidersFlag(argv.providers),
|
|
20
|
+
yes: Boolean(argv.yes),
|
|
21
|
+
nonInteractive,
|
|
22
|
+
dryRun: Boolean(argv["dry-run"]),
|
|
23
|
+
});
|
|
24
|
+
console.log(formatSyncSummary(summary, paths.agentsRoot));
|
|
25
|
+
}
|