@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.
Files changed (63) hide show
  1. package/LICENSE +195 -0
  2. package/README.md +103 -0
  3. package/agents/argent-environment-inspector.md +106 -0
  4. package/agents/references/quality-control-checklist.md +45 -0
  5. package/bin/ax-service +0 -0
  6. package/bin/simulator-server +0 -0
  7. package/dist/Argent.tracetemplate +0 -0
  8. package/dist/auto-screenshot.d.ts +22 -0
  9. package/dist/auto-screenshot.js +79 -0
  10. package/dist/auto-screenshot.js.map +1 -0
  11. package/dist/cli/constants.d.ts +6 -0
  12. package/dist/cli/constants.js +12 -0
  13. package/dist/cli/constants.js.map +1 -0
  14. package/dist/cli/init.d.ts +2 -0
  15. package/dist/cli/init.js +466 -0
  16. package/dist/cli/init.js.map +1 -0
  17. package/dist/cli/mcp-configs.d.ts +38 -0
  18. package/dist/cli/mcp-configs.js +724 -0
  19. package/dist/cli/mcp-configs.js.map +1 -0
  20. package/dist/cli/uninstall.d.ts +13 -0
  21. package/dist/cli/uninstall.js +389 -0
  22. package/dist/cli/uninstall.js.map +1 -0
  23. package/dist/cli/update.d.ts +1 -0
  24. package/dist/cli/update.js +126 -0
  25. package/dist/cli/update.js.map +1 -0
  26. package/dist/cli/utils.d.ts +27 -0
  27. package/dist/cli/utils.js +158 -0
  28. package/dist/cli/utils.js.map +1 -0
  29. package/dist/cli.d.ts +13 -0
  30. package/dist/cli.js +80 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/content.d.ts +31 -0
  33. package/dist/content.js +59 -0
  34. package/dist/content.js.map +1 -0
  35. package/dist/launcher.d.ts +8 -0
  36. package/dist/launcher.js +183 -0
  37. package/dist/launcher.js.map +1 -0
  38. package/dist/mcp-server.d.ts +1 -0
  39. package/dist/mcp-server.js +228 -0
  40. package/dist/mcp-server.js.map +1 -0
  41. package/dist/tool-server.cjs +51288 -0
  42. package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
  43. package/dylibs/libKeyboardPatch.dylib +0 -0
  44. package/dylibs/libNativeDevtoolsIos.dylib +0 -0
  45. package/package.json +53 -0
  46. package/rules/argent.md +121 -0
  47. package/scripts/postinstall.cjs +34 -0
  48. package/skills/argent-create-flow/SKILL.md +213 -0
  49. package/skills/argent-ios-profiler/SKILL.md +103 -0
  50. package/skills/argent-metro-debugger/SKILL.md +117 -0
  51. package/skills/argent-metro-debugger/references/failure-scenarios.md +10 -0
  52. package/skills/argent-metro-debugger/references/source-maps.md +27 -0
  53. package/skills/argent-react-native-app-workflow/SKILL.md +237 -0
  54. package/skills/argent-react-native-optimization/SKILL.md +64 -0
  55. package/skills/argent-react-native-optimization/references/fix-reference.md +14 -0
  56. package/skills/argent-react-native-optimization/references/lint-rules.md +55 -0
  57. package/skills/argent-react-native-optimization/references/semantic-checklist.md +34 -0
  58. package/skills/argent-react-native-profiler/SKILL.md +160 -0
  59. package/skills/argent-react-native-profiler/references/diagnostic-tools.md +94 -0
  60. package/skills/argent-simulator-interact/SKILL.md +272 -0
  61. package/skills/argent-simulator-interact/references/gesture-examples.md +114 -0
  62. package/skills/argent-simulator-setup/SKILL.md +19 -0
  63. 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