@swmansion/argent 0.5.1
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 +195 -0
- package/README.md +103 -0
- package/agents/argent-environment-inspector.md +106 -0
- package/agents/references/quality-control-checklist.md +45 -0
- package/bin/ax-service +0 -0
- package/bin/simulator-server +0 -0
- package/dist/Argent.tracetemplate +0 -0
- package/dist/auto-screenshot.d.ts +22 -0
- package/dist/auto-screenshot.js +79 -0
- package/dist/auto-screenshot.js.map +1 -0
- package/dist/cli/constants.d.ts +6 -0
- package/dist/cli/constants.js +12 -0
- package/dist/cli/constants.js.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.js +466 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mcp-configs.d.ts +38 -0
- package/dist/cli/mcp-configs.js +724 -0
- package/dist/cli/mcp-configs.js.map +1 -0
- package/dist/cli/uninstall.d.ts +13 -0
- package/dist/cli/uninstall.js +389 -0
- package/dist/cli/uninstall.js.map +1 -0
- package/dist/cli/update.d.ts +1 -0
- package/dist/cli/update.js +126 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/utils.d.ts +27 -0
- package/dist/cli/utils.js +158 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +80 -0
- package/dist/cli.js.map +1 -0
- package/dist/content.d.ts +31 -0
- package/dist/content.js +59 -0
- package/dist/content.js.map +1 -0
- package/dist/launcher.d.ts +8 -0
- package/dist/launcher.js +183 -0
- package/dist/launcher.js.map +1 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +228 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/tool-server.cjs +51288 -0
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +53 -0
- package/rules/argent.md +121 -0
- package/scripts/postinstall.cjs +34 -0
- package/skills/argent-create-flow/SKILL.md +213 -0
- package/skills/argent-ios-profiler/SKILL.md +103 -0
- package/skills/argent-metro-debugger/SKILL.md +117 -0
- package/skills/argent-metro-debugger/references/failure-scenarios.md +10 -0
- package/skills/argent-metro-debugger/references/source-maps.md +27 -0
- package/skills/argent-react-native-app-workflow/SKILL.md +237 -0
- package/skills/argent-react-native-optimization/SKILL.md +64 -0
- package/skills/argent-react-native-optimization/references/fix-reference.md +14 -0
- package/skills/argent-react-native-optimization/references/lint-rules.md +55 -0
- package/skills/argent-react-native-optimization/references/semantic-checklist.md +34 -0
- package/skills/argent-react-native-profiler/SKILL.md +160 -0
- package/skills/argent-react-native-profiler/references/diagnostic-tools.md +94 -0
- package/skills/argent-simulator-interact/SKILL.md +272 -0
- package/skills/argent-simulator-interact/references/gesture-examples.md +114 -0
- package/skills/argent-simulator-setup/SKILL.md +19 -0
- package/skills/argent-test-ui-flow/SKILL.md +84 -0
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { MCP_SERVER_KEY, MCP_BINARY_NAME, PERMISSION_RULE, CURSOR_ALLOWLIST_PATTERN, } from "./constants.js";
|
|
6
|
+
import { readJson, writeJson, dirExists, readToml, writeToml } from "./utils.js";
|
|
7
|
+
const TOOL_SERVER_BUNDLE = path.join(import.meta.dirname, "..", "tool-server.cjs");
|
|
8
|
+
function getAvailableToolIds() {
|
|
9
|
+
const out = execFileSync("node", [TOOL_SERVER_BUNDLE, "-t"], { encoding: "utf8" });
|
|
10
|
+
const tools = JSON.parse(out);
|
|
11
|
+
return tools.map((t) => t.id);
|
|
12
|
+
}
|
|
13
|
+
// ── Shared helpers ────────────────────────────────────────────────────────────
|
|
14
|
+
function buildMcpEntry() {
|
|
15
|
+
const logFile = path.join(homedir(), ".argent", "mcp-calls.log");
|
|
16
|
+
return {
|
|
17
|
+
command: MCP_BINARY_NAME,
|
|
18
|
+
args: ["mcp"],
|
|
19
|
+
env: { ARGENT_MCP_LOG: logFile },
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function getMcpEntry() {
|
|
23
|
+
return buildMcpEntry();
|
|
24
|
+
}
|
|
25
|
+
function removeDirIfEmpty(dirPath) {
|
|
26
|
+
try {
|
|
27
|
+
if (!fs.existsSync(dirPath))
|
|
28
|
+
return;
|
|
29
|
+
if (!fs.statSync(dirPath).isDirectory())
|
|
30
|
+
return;
|
|
31
|
+
if (fs.readdirSync(dirPath).length > 0)
|
|
32
|
+
return;
|
|
33
|
+
fs.rmdirSync(dirPath);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// non-fatal
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function pruneEmptyConfig(value) {
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
return value.length > 0 ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
if (value && typeof value === "object") {
|
|
44
|
+
const cleaned = {};
|
|
45
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
46
|
+
const next = pruneEmptyConfig(entry);
|
|
47
|
+
if (next !== undefined)
|
|
48
|
+
cleaned[key] = next;
|
|
49
|
+
}
|
|
50
|
+
return Object.keys(cleaned).length > 0 ? cleaned : undefined;
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
function isRecord(value) {
|
|
55
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
function writeJsonOrRemove(filePath, data) {
|
|
58
|
+
const cleaned = pruneEmptyConfig(data);
|
|
59
|
+
if (!isRecord(cleaned)) {
|
|
60
|
+
fs.rmSync(filePath, { force: true });
|
|
61
|
+
removeDirIfEmpty(path.dirname(filePath));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
writeJson(filePath, cleaned);
|
|
65
|
+
}
|
|
66
|
+
function writeTomlOrRemove(filePath, data) {
|
|
67
|
+
const cleaned = pruneEmptyConfig(data);
|
|
68
|
+
if (!isRecord(cleaned)) {
|
|
69
|
+
fs.rmSync(filePath, { force: true });
|
|
70
|
+
removeDirIfEmpty(path.dirname(filePath));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
writeToml(filePath, cleaned);
|
|
74
|
+
}
|
|
75
|
+
// ── Cursor adapter ────────────────────────────────────────────────────────────
|
|
76
|
+
// MARK: Cursor
|
|
77
|
+
// Format: { mcpServers: { argent: { command, args, env } } }
|
|
78
|
+
const cursorAdapter = {
|
|
79
|
+
name: "Cursor",
|
|
80
|
+
detect() {
|
|
81
|
+
return (dirExists(path.join(homedir(), ".cursor")) || dirExists(path.join(process.cwd(), ".cursor")));
|
|
82
|
+
},
|
|
83
|
+
projectPath(root) {
|
|
84
|
+
return path.join(root, ".cursor", "mcp.json");
|
|
85
|
+
},
|
|
86
|
+
globalPath() {
|
|
87
|
+
return path.join(homedir(), ".cursor", "mcp.json");
|
|
88
|
+
},
|
|
89
|
+
write(configPath, entry) {
|
|
90
|
+
const config = readJson(configPath);
|
|
91
|
+
const servers = (config.mcpServers ?? {});
|
|
92
|
+
servers[MCP_SERVER_KEY] = {
|
|
93
|
+
command: entry.command,
|
|
94
|
+
args: entry.args,
|
|
95
|
+
env: entry.env,
|
|
96
|
+
};
|
|
97
|
+
config.mcpServers = servers;
|
|
98
|
+
writeJson(configPath, config);
|
|
99
|
+
},
|
|
100
|
+
remove(configPath) {
|
|
101
|
+
if (!fs.existsSync(configPath))
|
|
102
|
+
return false;
|
|
103
|
+
const config = readJson(configPath);
|
|
104
|
+
const servers = config.mcpServers;
|
|
105
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
106
|
+
return false;
|
|
107
|
+
delete servers[MCP_SERVER_KEY];
|
|
108
|
+
writeJsonOrRemove(configPath, config);
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
addAllowlist() {
|
|
112
|
+
const permPath = path.join(homedir(), ".cursor", "permissions.json");
|
|
113
|
+
const config = readJson(permPath);
|
|
114
|
+
const list = (config.mcpAllowlist ?? []);
|
|
115
|
+
if (!list.includes(CURSOR_ALLOWLIST_PATTERN)) {
|
|
116
|
+
list.push(CURSOR_ALLOWLIST_PATTERN);
|
|
117
|
+
config.mcpAllowlist = list;
|
|
118
|
+
writeJson(permPath, config);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
removeAllowlist() {
|
|
122
|
+
const permPath = path.join(homedir(), ".cursor", "permissions.json");
|
|
123
|
+
if (!fs.existsSync(permPath))
|
|
124
|
+
return;
|
|
125
|
+
const config = readJson(permPath);
|
|
126
|
+
const list = config.mcpAllowlist;
|
|
127
|
+
if (!Array.isArray(list))
|
|
128
|
+
return;
|
|
129
|
+
const idx = list.indexOf(CURSOR_ALLOWLIST_PATTERN);
|
|
130
|
+
if (idx === -1)
|
|
131
|
+
return;
|
|
132
|
+
list.splice(idx, 1);
|
|
133
|
+
config.mcpAllowlist = list;
|
|
134
|
+
writeJsonOrRemove(permPath, config);
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
// ── Claude Code adapter ───────────────────────────────────────────────────────
|
|
138
|
+
// MARK: Claude
|
|
139
|
+
// Format: { mcpServers: { argent: { type: "stdio", command, args, env } } }
|
|
140
|
+
// Project: .mcp.json Global: ~/.claude.json
|
|
141
|
+
// Also manages permissions in .claude/settings.json
|
|
142
|
+
const claudeAdapter = {
|
|
143
|
+
name: "Claude Code",
|
|
144
|
+
detect() {
|
|
145
|
+
return (fs.existsSync(path.join(process.cwd(), ".mcp.json")) ||
|
|
146
|
+
fs.existsSync(path.join(homedir(), ".claude.json")) ||
|
|
147
|
+
dirExists(path.join(process.cwd(), ".claude")) ||
|
|
148
|
+
dirExists(path.join(homedir(), ".claude")));
|
|
149
|
+
},
|
|
150
|
+
projectPath(root) {
|
|
151
|
+
return path.join(root, ".mcp.json");
|
|
152
|
+
},
|
|
153
|
+
globalPath() {
|
|
154
|
+
return path.join(homedir(), ".claude.json");
|
|
155
|
+
},
|
|
156
|
+
write(configPath, entry) {
|
|
157
|
+
const config = readJson(configPath);
|
|
158
|
+
const servers = (config.mcpServers ?? {});
|
|
159
|
+
servers[MCP_SERVER_KEY] = {
|
|
160
|
+
type: "stdio",
|
|
161
|
+
command: entry.command,
|
|
162
|
+
args: entry.args,
|
|
163
|
+
env: entry.env,
|
|
164
|
+
};
|
|
165
|
+
config.mcpServers = servers;
|
|
166
|
+
writeJson(configPath, config);
|
|
167
|
+
},
|
|
168
|
+
remove(configPath) {
|
|
169
|
+
if (!fs.existsSync(configPath))
|
|
170
|
+
return false;
|
|
171
|
+
const config = readJson(configPath);
|
|
172
|
+
const servers = config.mcpServers;
|
|
173
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
174
|
+
return false;
|
|
175
|
+
delete servers[MCP_SERVER_KEY];
|
|
176
|
+
writeJsonOrRemove(configPath, config);
|
|
177
|
+
return true;
|
|
178
|
+
},
|
|
179
|
+
addAllowlist(root, scope) {
|
|
180
|
+
addClaudePermission(root, scope);
|
|
181
|
+
},
|
|
182
|
+
removeAllowlist(root, scope) {
|
|
183
|
+
removeClaudePermission(root, scope);
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
// ── VS Code adapter ──────────────────────────────────────────────────────────
|
|
187
|
+
// MARK: VSCode
|
|
188
|
+
// Format: { servers: { argent: { type: "stdio", command, args, env } } }
|
|
189
|
+
// Project only: .vscode/mcp.json
|
|
190
|
+
const vscodeAdapter = {
|
|
191
|
+
name: "VS Code",
|
|
192
|
+
detect() {
|
|
193
|
+
return (dirExists(path.join(process.cwd(), ".vscode")) || dirExists(path.join(homedir(), ".vscode")));
|
|
194
|
+
},
|
|
195
|
+
projectPath(root) {
|
|
196
|
+
return path.join(root, ".vscode", "mcp.json");
|
|
197
|
+
},
|
|
198
|
+
globalPath() {
|
|
199
|
+
return null;
|
|
200
|
+
},
|
|
201
|
+
write(configPath, entry) {
|
|
202
|
+
const config = readJson(configPath);
|
|
203
|
+
const servers = (config.servers ?? {});
|
|
204
|
+
servers[MCP_SERVER_KEY] = {
|
|
205
|
+
type: "stdio",
|
|
206
|
+
command: entry.command,
|
|
207
|
+
args: entry.args,
|
|
208
|
+
env: entry.env,
|
|
209
|
+
};
|
|
210
|
+
config.servers = servers;
|
|
211
|
+
writeJson(configPath, config);
|
|
212
|
+
},
|
|
213
|
+
remove(configPath) {
|
|
214
|
+
if (!fs.existsSync(configPath))
|
|
215
|
+
return false;
|
|
216
|
+
const config = readJson(configPath);
|
|
217
|
+
const servers = config.servers;
|
|
218
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
219
|
+
return false;
|
|
220
|
+
delete servers[MCP_SERVER_KEY];
|
|
221
|
+
writeJsonOrRemove(configPath, config);
|
|
222
|
+
return true;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
// ── Windsurf adapter ─────────────────────────────────────────────────────────
|
|
226
|
+
// MARK: Windsurf
|
|
227
|
+
// Format: { mcpServers: { argent: { command, args, env } } }
|
|
228
|
+
// Global only: ~/.codeium/windsurf/mcp_config.json
|
|
229
|
+
const windsurfAdapter = {
|
|
230
|
+
name: "Windsurf",
|
|
231
|
+
detect() {
|
|
232
|
+
return dirExists(path.join(homedir(), ".codeium", "windsurf"));
|
|
233
|
+
},
|
|
234
|
+
projectPath() {
|
|
235
|
+
return null;
|
|
236
|
+
},
|
|
237
|
+
globalPath() {
|
|
238
|
+
return path.join(homedir(), ".codeium", "windsurf", "mcp_config.json");
|
|
239
|
+
},
|
|
240
|
+
write(configPath, entry) {
|
|
241
|
+
const config = readJson(configPath);
|
|
242
|
+
const servers = (config.mcpServers ?? {});
|
|
243
|
+
servers[MCP_SERVER_KEY] = {
|
|
244
|
+
command: entry.command,
|
|
245
|
+
args: entry.args,
|
|
246
|
+
env: entry.env,
|
|
247
|
+
};
|
|
248
|
+
config.mcpServers = servers;
|
|
249
|
+
writeJson(configPath, config);
|
|
250
|
+
},
|
|
251
|
+
remove(configPath) {
|
|
252
|
+
if (!fs.existsSync(configPath))
|
|
253
|
+
return false;
|
|
254
|
+
const config = readJson(configPath);
|
|
255
|
+
const servers = config.mcpServers;
|
|
256
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
257
|
+
return false;
|
|
258
|
+
delete servers[MCP_SERVER_KEY];
|
|
259
|
+
writeJsonOrRemove(configPath, config);
|
|
260
|
+
return true;
|
|
261
|
+
},
|
|
262
|
+
addAllowlist() {
|
|
263
|
+
const configPath = path.join(homedir(), ".codeium", "windsurf", "mcp_config.json");
|
|
264
|
+
const config = readJson(configPath);
|
|
265
|
+
const servers = (config.mcpServers ?? {});
|
|
266
|
+
const entry = servers[MCP_SERVER_KEY];
|
|
267
|
+
if (!entry)
|
|
268
|
+
return;
|
|
269
|
+
entry.alwaysAllow = ["*"];
|
|
270
|
+
writeJson(configPath, config);
|
|
271
|
+
},
|
|
272
|
+
removeAllowlist() {
|
|
273
|
+
const configPath = path.join(homedir(), ".codeium", "windsurf", "mcp_config.json");
|
|
274
|
+
if (!fs.existsSync(configPath))
|
|
275
|
+
return;
|
|
276
|
+
const config = readJson(configPath);
|
|
277
|
+
const servers = (config.mcpServers ?? {});
|
|
278
|
+
const entry = servers[MCP_SERVER_KEY];
|
|
279
|
+
if (!entry?.alwaysAllow)
|
|
280
|
+
return;
|
|
281
|
+
delete entry.alwaysAllow;
|
|
282
|
+
writeJsonOrRemove(configPath, config);
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
// ── Zed adapter ──────────────────────────────────────────────────────────────
|
|
286
|
+
// MARK: Zed
|
|
287
|
+
// Format: merges { context_servers: { argent: { source: "custom", command, args, env } } }
|
|
288
|
+
// Into existing settings.json
|
|
289
|
+
const zedAdapter = {
|
|
290
|
+
name: "Zed",
|
|
291
|
+
detect() {
|
|
292
|
+
return dirExists(path.join(homedir(), ".config", "zed"));
|
|
293
|
+
},
|
|
294
|
+
projectPath(root) {
|
|
295
|
+
return path.join(root, ".zed", "settings.json");
|
|
296
|
+
},
|
|
297
|
+
globalPath() {
|
|
298
|
+
return path.join(homedir(), ".config", "zed", "settings.json");
|
|
299
|
+
},
|
|
300
|
+
write(configPath, entry) {
|
|
301
|
+
const config = readJson(configPath);
|
|
302
|
+
const servers = (config.context_servers ?? {});
|
|
303
|
+
servers[MCP_SERVER_KEY] = {
|
|
304
|
+
source: "custom",
|
|
305
|
+
command: entry.command,
|
|
306
|
+
args: entry.args,
|
|
307
|
+
env: entry.env,
|
|
308
|
+
};
|
|
309
|
+
config.context_servers = servers;
|
|
310
|
+
writeJson(configPath, config);
|
|
311
|
+
},
|
|
312
|
+
remove(configPath) {
|
|
313
|
+
if (!fs.existsSync(configPath))
|
|
314
|
+
return false;
|
|
315
|
+
const config = readJson(configPath);
|
|
316
|
+
const servers = config.context_servers;
|
|
317
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
318
|
+
return false;
|
|
319
|
+
delete servers[MCP_SERVER_KEY];
|
|
320
|
+
writeJsonOrRemove(configPath, config);
|
|
321
|
+
return true;
|
|
322
|
+
},
|
|
323
|
+
// Zed doesn't support server-level wildcards for MCP tools — each tool
|
|
324
|
+
// would need its own entry. Setting the global default to "allow" is the
|
|
325
|
+
// documented opt-in; built-in security rules still protect against
|
|
326
|
+
// destructive operations.
|
|
327
|
+
addAllowlist(root, scope) {
|
|
328
|
+
const settingsPath = scope === "global"
|
|
329
|
+
? path.join(homedir(), ".config", "zed", "settings.json")
|
|
330
|
+
: path.join(root, ".zed", "settings.json");
|
|
331
|
+
const config = readJson(settingsPath);
|
|
332
|
+
const agent = (config.agent ?? {});
|
|
333
|
+
const perms = (agent.tool_permissions ?? {});
|
|
334
|
+
perms.default = "allow";
|
|
335
|
+
agent.tool_permissions = perms;
|
|
336
|
+
config.agent = agent;
|
|
337
|
+
writeJson(settingsPath, config);
|
|
338
|
+
},
|
|
339
|
+
removeAllowlist(root, scope) {
|
|
340
|
+
const settingsPath = scope === "global"
|
|
341
|
+
? path.join(homedir(), ".config", "zed", "settings.json")
|
|
342
|
+
: path.join(root, ".zed", "settings.json");
|
|
343
|
+
if (!fs.existsSync(settingsPath))
|
|
344
|
+
return;
|
|
345
|
+
const config = readJson(settingsPath);
|
|
346
|
+
const perms = config.agent?.tool_permissions;
|
|
347
|
+
if (!perms || perms.default !== "allow")
|
|
348
|
+
return;
|
|
349
|
+
perms.default = "confirm";
|
|
350
|
+
writeJsonOrRemove(settingsPath, config);
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
// ── Gemini CLI adapter ────────────────────────────────────────────────────────
|
|
354
|
+
// MARK: Gemini
|
|
355
|
+
// Format: { mcpServers: { argent: { command, args, env } } }
|
|
356
|
+
// Project: <root>/.gemini/settings.json Global: ~/.gemini/settings.json
|
|
357
|
+
const geminiAdapter = {
|
|
358
|
+
name: "Gemini",
|
|
359
|
+
detect() {
|
|
360
|
+
return (dirExists(path.join(homedir(), ".gemini")) || dirExists(path.join(process.cwd(), ".gemini")));
|
|
361
|
+
},
|
|
362
|
+
projectPath(root) {
|
|
363
|
+
return path.join(root, ".gemini", "settings.json");
|
|
364
|
+
},
|
|
365
|
+
globalPath() {
|
|
366
|
+
return path.join(homedir(), ".gemini", "settings.json");
|
|
367
|
+
},
|
|
368
|
+
write(configPath, entry) {
|
|
369
|
+
const config = readJson(configPath);
|
|
370
|
+
const servers = (config.mcpServers ?? {});
|
|
371
|
+
servers[MCP_SERVER_KEY] = {
|
|
372
|
+
command: entry.command,
|
|
373
|
+
args: entry.args,
|
|
374
|
+
env: entry.env,
|
|
375
|
+
};
|
|
376
|
+
config.mcpServers = servers;
|
|
377
|
+
writeJson(configPath, config);
|
|
378
|
+
},
|
|
379
|
+
remove(configPath) {
|
|
380
|
+
if (!fs.existsSync(configPath))
|
|
381
|
+
return false;
|
|
382
|
+
const config = readJson(configPath);
|
|
383
|
+
const servers = config.mcpServers;
|
|
384
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
385
|
+
return false;
|
|
386
|
+
delete servers[MCP_SERVER_KEY];
|
|
387
|
+
writeJsonOrRemove(configPath, config);
|
|
388
|
+
return true;
|
|
389
|
+
},
|
|
390
|
+
addAllowlist(root, scope) {
|
|
391
|
+
const configPath = scope === "global" ? this.globalPath() : this.projectPath(root);
|
|
392
|
+
if (!configPath) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const config = readJson(configPath);
|
|
396
|
+
const servers = (config.mcpServers ?? {});
|
|
397
|
+
const entry = servers[MCP_SERVER_KEY];
|
|
398
|
+
if (!entry)
|
|
399
|
+
return;
|
|
400
|
+
entry.trust = true;
|
|
401
|
+
writeJson(configPath, config);
|
|
402
|
+
},
|
|
403
|
+
removeAllowlist(root, scope) {
|
|
404
|
+
const configPath = scope === "global" ? this.globalPath() : this.projectPath(root);
|
|
405
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const config = readJson(configPath);
|
|
409
|
+
const servers = config.mcpServers;
|
|
410
|
+
const entry = servers?.[MCP_SERVER_KEY];
|
|
411
|
+
if (!entry?.trust)
|
|
412
|
+
return;
|
|
413
|
+
delete entry.trust;
|
|
414
|
+
writeJsonOrRemove(configPath, config);
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
// ── Codex CLI adapter ────────────────────────────────────────────────────────
|
|
418
|
+
// MARK: Codex
|
|
419
|
+
// Format (TOML): [mcp_servers.argent] command = "argent" args = ["mcp"] env = { ... }
|
|
420
|
+
// Project: <root>/.codex/config.toml Global: ~/.codex/config.toml
|
|
421
|
+
const CODEX_FILENAME = ".codex";
|
|
422
|
+
const codexAdapter = {
|
|
423
|
+
name: "Codex",
|
|
424
|
+
detect() {
|
|
425
|
+
return (dirExists(path.join(homedir(), CODEX_FILENAME)) ||
|
|
426
|
+
dirExists(path.join(process.cwd(), CODEX_FILENAME)));
|
|
427
|
+
},
|
|
428
|
+
projectPath(root) {
|
|
429
|
+
return path.join(root, CODEX_FILENAME, "config.toml");
|
|
430
|
+
},
|
|
431
|
+
globalPath() {
|
|
432
|
+
return path.join(homedir(), CODEX_FILENAME, "config.toml");
|
|
433
|
+
},
|
|
434
|
+
write(configPath, entry) {
|
|
435
|
+
const config = readToml(configPath);
|
|
436
|
+
const servers = (config.mcp_servers ?? {});
|
|
437
|
+
servers[MCP_SERVER_KEY] = {
|
|
438
|
+
command: entry.command,
|
|
439
|
+
args: entry.args,
|
|
440
|
+
env: entry.env,
|
|
441
|
+
};
|
|
442
|
+
config.mcp_servers = servers;
|
|
443
|
+
writeToml(configPath, config);
|
|
444
|
+
},
|
|
445
|
+
remove(configPath) {
|
|
446
|
+
if (!fs.existsSync(configPath))
|
|
447
|
+
return false;
|
|
448
|
+
const config = readToml(configPath);
|
|
449
|
+
const servers = config.mcp_servers;
|
|
450
|
+
if (!servers?.[MCP_SERVER_KEY])
|
|
451
|
+
return false;
|
|
452
|
+
delete servers[MCP_SERVER_KEY];
|
|
453
|
+
writeTomlOrRemove(configPath, config);
|
|
454
|
+
return true;
|
|
455
|
+
},
|
|
456
|
+
addAllowlist(root, scope) {
|
|
457
|
+
const configPath = scope === "global" ? this.globalPath() : this.projectPath(root);
|
|
458
|
+
if (!configPath) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const tools = getAvailableToolIds();
|
|
462
|
+
const config = readToml(configPath);
|
|
463
|
+
config.mcp_servers ??= {};
|
|
464
|
+
config.mcp_servers.argent ??= {};
|
|
465
|
+
config.mcp_servers.argent.tools ??= {};
|
|
466
|
+
const toolsConfig = config.mcp_servers.argent.tools;
|
|
467
|
+
for (const tool of tools) {
|
|
468
|
+
toolsConfig[tool] = {
|
|
469
|
+
approval_mode: "approve",
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
writeToml(configPath, config);
|
|
473
|
+
},
|
|
474
|
+
removeAllowlist(root, scope) {
|
|
475
|
+
const configPath = scope === "global" ? this.globalPath() : this.projectPath(root);
|
|
476
|
+
if (!configPath) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const tools = getAvailableToolIds();
|
|
480
|
+
const config = readToml(configPath);
|
|
481
|
+
const toolsConfig = config?.mcp_servers?.argent?.tools;
|
|
482
|
+
if (toolsConfig === undefined) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
for (const tool of tools) {
|
|
486
|
+
if (tool in toolsConfig) {
|
|
487
|
+
delete toolsConfig[tool];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
writeToml(configPath, config);
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
// ── Registry ──────────────────────────────────────────────────────────────────
|
|
494
|
+
// MARK: Registry
|
|
495
|
+
export const ALL_ADAPTERS = [
|
|
496
|
+
cursorAdapter,
|
|
497
|
+
claudeAdapter,
|
|
498
|
+
vscodeAdapter,
|
|
499
|
+
windsurfAdapter,
|
|
500
|
+
zedAdapter,
|
|
501
|
+
geminiAdapter,
|
|
502
|
+
codexAdapter,
|
|
503
|
+
];
|
|
504
|
+
export function detectAdapters() {
|
|
505
|
+
return ALL_ADAPTERS.filter((a) => a.detect());
|
|
506
|
+
}
|
|
507
|
+
export function getAdapterByName(name) {
|
|
508
|
+
return ALL_ADAPTERS.find((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
509
|
+
}
|
|
510
|
+
// ── Claude permissions helpers ────────────────────────────────────────────────
|
|
511
|
+
export function addClaudePermission(root, scope) {
|
|
512
|
+
const settingsPath = scope === "global"
|
|
513
|
+
? path.join(homedir(), ".claude", "settings.json")
|
|
514
|
+
: path.join(root, ".claude", "settings.json");
|
|
515
|
+
const config = readJson(settingsPath);
|
|
516
|
+
const permissions = (config.permissions ?? {});
|
|
517
|
+
const allow = (permissions.allow ?? []);
|
|
518
|
+
if (!allow.includes(PERMISSION_RULE)) {
|
|
519
|
+
allow.push(PERMISSION_RULE);
|
|
520
|
+
permissions.allow = allow;
|
|
521
|
+
config.permissions = permissions;
|
|
522
|
+
writeJson(settingsPath, config);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
export function removeClaudePermission(root, scope) {
|
|
526
|
+
const settingsPath = scope === "global"
|
|
527
|
+
? path.join(homedir(), ".claude", "settings.json")
|
|
528
|
+
: path.join(root, ".claude", "settings.json");
|
|
529
|
+
if (!fs.existsSync(settingsPath))
|
|
530
|
+
return;
|
|
531
|
+
const config = readJson(settingsPath);
|
|
532
|
+
const allow = config?.permissions?.allow;
|
|
533
|
+
if (!Array.isArray(allow))
|
|
534
|
+
return;
|
|
535
|
+
const idx = allow.indexOf(PERMISSION_RULE);
|
|
536
|
+
if (idx === -1)
|
|
537
|
+
return;
|
|
538
|
+
allow.splice(idx, 1);
|
|
539
|
+
writeJsonOrRemove(settingsPath, config);
|
|
540
|
+
}
|
|
541
|
+
function formatManagedPathLabel(targetPath, root) {
|
|
542
|
+
const home = homedir();
|
|
543
|
+
if (targetPath === home || targetPath.startsWith(`${home}${path.sep}`)) {
|
|
544
|
+
return `~${targetPath.slice(home.length)}`;
|
|
545
|
+
}
|
|
546
|
+
const relative = path.relative(root, targetPath);
|
|
547
|
+
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
548
|
+
return relative;
|
|
549
|
+
}
|
|
550
|
+
return targetPath;
|
|
551
|
+
}
|
|
552
|
+
function addManagedTarget(targets, editorName, targetPath, root) {
|
|
553
|
+
targets.push({
|
|
554
|
+
editorName,
|
|
555
|
+
targetPath,
|
|
556
|
+
label: formatManagedPathLabel(targetPath, root),
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
function getAdapterBasePath(adapter, root, scope) {
|
|
560
|
+
const configPath = scope === "global" ? adapter.globalPath() : adapter.projectPath(root);
|
|
561
|
+
return configPath ? path.dirname(configPath) : null;
|
|
562
|
+
}
|
|
563
|
+
export function getManagedContentTargets(adapters, root, scope) {
|
|
564
|
+
const targets = {
|
|
565
|
+
skillTargets: [],
|
|
566
|
+
ruleTargets: [],
|
|
567
|
+
agentTargets: [],
|
|
568
|
+
codexConfigTargets: [],
|
|
569
|
+
skillsLockTargets: [],
|
|
570
|
+
};
|
|
571
|
+
const workspaceBase = scope === "global" ? homedir() : root;
|
|
572
|
+
addManagedTarget(targets.skillTargets, "skills", path.join(workspaceBase, ".agents", "skills"), root);
|
|
573
|
+
addManagedTarget(targets.skillsLockTargets, "skills", path.join(workspaceBase, "skills-lock.json"), root);
|
|
574
|
+
for (const adapter of adapters) {
|
|
575
|
+
switch (adapter.name) {
|
|
576
|
+
case "Cursor": {
|
|
577
|
+
const base = getAdapterBasePath(adapter, root, scope);
|
|
578
|
+
if (!base)
|
|
579
|
+
break;
|
|
580
|
+
addManagedTarget(targets.skillTargets, adapter.name, path.join(base, "skills"), root);
|
|
581
|
+
addManagedTarget(targets.ruleTargets, adapter.name, path.join(base, "rules"), root);
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
case "Claude Code": {
|
|
585
|
+
const claudeBase = scope === "global" ? path.join(homedir(), ".claude") : path.join(root, ".claude");
|
|
586
|
+
addManagedTarget(targets.skillTargets, adapter.name, path.join(claudeBase, "skills"), root);
|
|
587
|
+
addManagedTarget(targets.ruleTargets, adapter.name, path.join(claudeBase, "rules"), root);
|
|
588
|
+
addManagedTarget(targets.agentTargets, adapter.name, path.join(claudeBase, "agents"), root);
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
case "Gemini": {
|
|
592
|
+
const geminiBase = scope === "global" ? path.join(homedir(), ".gemini") : path.join(root, ".gemini");
|
|
593
|
+
addManagedTarget(targets.ruleTargets, adapter.name, path.join(geminiBase, "rules"), root);
|
|
594
|
+
addManagedTarget(targets.agentTargets, adapter.name, path.join(geminiBase, "agents"), root);
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case "Codex": {
|
|
598
|
+
const configPath = scope === "global" ? adapter.globalPath() : adapter.projectPath(root);
|
|
599
|
+
if (!configPath)
|
|
600
|
+
break;
|
|
601
|
+
addManagedTarget(targets.codexConfigTargets, adapter.name, configPath, root);
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return targets;
|
|
607
|
+
}
|
|
608
|
+
// ── Codex developer_instructions helpers ─────────────────────────────────────
|
|
609
|
+
// Codex has no rules/ directory for model instructions. Instead we inject
|
|
610
|
+
// rule content into the `developer_instructions` field of config.toml,
|
|
611
|
+
// delimited by markers so we can update/remove without touching user content.
|
|
612
|
+
const ARGENT_RULES_START = "# --- argent rules (managed by argent init — do not edit) ---";
|
|
613
|
+
const ARGENT_RULES_END = "# --- end argent rules ---";
|
|
614
|
+
function stripFrontmatter(content) {
|
|
615
|
+
const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
616
|
+
return match ? content.slice(match[0].length).trim() : content.trim();
|
|
617
|
+
}
|
|
618
|
+
function readAndConcatRules(rulesDir) {
|
|
619
|
+
if (!fs.existsSync(rulesDir))
|
|
620
|
+
return null;
|
|
621
|
+
const files = fs
|
|
622
|
+
.readdirSync(rulesDir)
|
|
623
|
+
.filter((f) => f.endsWith(".md"))
|
|
624
|
+
.sort();
|
|
625
|
+
if (files.length === 0)
|
|
626
|
+
return null;
|
|
627
|
+
const parts = [];
|
|
628
|
+
for (const file of files) {
|
|
629
|
+
const raw = fs.readFileSync(path.join(rulesDir, file), "utf8");
|
|
630
|
+
const stripped = stripFrontmatter(raw);
|
|
631
|
+
if (stripped)
|
|
632
|
+
parts.push(stripped);
|
|
633
|
+
}
|
|
634
|
+
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
635
|
+
}
|
|
636
|
+
function injectArgentSection(existing, rules) {
|
|
637
|
+
const section = `${ARGENT_RULES_START}\n${rules}\n${ARGENT_RULES_END}`;
|
|
638
|
+
if (!existing)
|
|
639
|
+
return section;
|
|
640
|
+
// Replace existing argent section if present
|
|
641
|
+
const re = new RegExp(`${escapeRegExp(ARGENT_RULES_START)}[\\s\\S]*?${escapeRegExp(ARGENT_RULES_END)}`);
|
|
642
|
+
if (re.test(existing))
|
|
643
|
+
return existing.replace(re, section);
|
|
644
|
+
// Append after user content
|
|
645
|
+
return `${existing}\n\n${section}`;
|
|
646
|
+
}
|
|
647
|
+
function removeArgentSection(existing) {
|
|
648
|
+
const re = new RegExp(`\\n*${escapeRegExp(ARGENT_RULES_START)}[\\s\\S]*?${escapeRegExp(ARGENT_RULES_END)}\\n*`);
|
|
649
|
+
return existing.replace(re, "").trim();
|
|
650
|
+
}
|
|
651
|
+
function escapeRegExp(s) {
|
|
652
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
653
|
+
}
|
|
654
|
+
export function injectCodexRules(configPath, rulesDir) {
|
|
655
|
+
const rules = readAndConcatRules(rulesDir);
|
|
656
|
+
if (!rules)
|
|
657
|
+
return null;
|
|
658
|
+
const config = readToml(configPath);
|
|
659
|
+
const existing = config.developer_instructions;
|
|
660
|
+
config.developer_instructions = injectArgentSection(existing, rules);
|
|
661
|
+
writeToml(configPath, config);
|
|
662
|
+
return configPath;
|
|
663
|
+
}
|
|
664
|
+
export function removeCodexRules(configPath) {
|
|
665
|
+
if (!fs.existsSync(configPath))
|
|
666
|
+
return false;
|
|
667
|
+
const config = readToml(configPath);
|
|
668
|
+
const existing = config.developer_instructions;
|
|
669
|
+
if (!existing || !existing.includes(ARGENT_RULES_START))
|
|
670
|
+
return false;
|
|
671
|
+
const cleaned = removeArgentSection(existing);
|
|
672
|
+
if (cleaned) {
|
|
673
|
+
config.developer_instructions = cleaned;
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
delete config.developer_instructions;
|
|
677
|
+
}
|
|
678
|
+
writeTomlOrRemove(configPath, config);
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
// ── Copy orchestrator ────────────────────────────────────────────────────────
|
|
682
|
+
// MARK: Copy orchestrator
|
|
683
|
+
export function copyRulesAndAgents(adapters, root, scope, rulesDir, agentsDir) {
|
|
684
|
+
const results = [];
|
|
685
|
+
const managedTargets = getManagedContentTargets(adapters, root, scope);
|
|
686
|
+
for (const target of managedTargets.ruleTargets) {
|
|
687
|
+
try {
|
|
688
|
+
if (fs.existsSync(rulesDir)) {
|
|
689
|
+
fs.mkdirSync(target.targetPath, { recursive: true });
|
|
690
|
+
fs.cpSync(rulesDir, target.targetPath, { recursive: true });
|
|
691
|
+
results.push(` Copied rules to ${target.targetPath}`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
catch (err) {
|
|
695
|
+
results.push(` Could not copy rules to ${target.targetPath}: ${err}`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
for (const target of managedTargets.agentTargets) {
|
|
699
|
+
try {
|
|
700
|
+
if (fs.existsSync(agentsDir)) {
|
|
701
|
+
fs.mkdirSync(target.targetPath, { recursive: true });
|
|
702
|
+
fs.cpSync(agentsDir, target.targetPath, { recursive: true });
|
|
703
|
+
results.push(` Copied agents to ${target.targetPath}`);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch (err) {
|
|
707
|
+
results.push(` Could not copy agents to ${target.targetPath}: ${err}`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Codex: inject rules into developer_instructions in config.toml
|
|
711
|
+
for (const target of managedTargets.codexConfigTargets) {
|
|
712
|
+
try {
|
|
713
|
+
const injected = injectCodexRules(target.targetPath, rulesDir);
|
|
714
|
+
if (injected) {
|
|
715
|
+
results.push(` Injected rules into ${target.targetPath} (developer_instructions)`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
catch (err) {
|
|
719
|
+
results.push(` Could not inject rules into ${target.targetPath}: ${err}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return results;
|
|
723
|
+
}
|
|
724
|
+
//# sourceMappingURL=mcp-configs.js.map
|