agent-relay 2.1.11 → 2.1.12

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 (66) hide show
  1. package/dist/index.cjs +64 -6
  2. package/dist/src/cli/index.d.ts.map +1 -1
  3. package/dist/src/cli/index.js +395 -1
  4. package/dist/src/cli/index.js.map +1 -1
  5. package/package.json +18 -18
  6. package/packages/acp-bridge/README.md +171 -0
  7. package/packages/acp-bridge/dist/acp-agent.d.ts +118 -0
  8. package/packages/acp-bridge/dist/acp-agent.d.ts.map +1 -0
  9. package/packages/acp-bridge/dist/acp-agent.js +713 -0
  10. package/packages/acp-bridge/dist/acp-agent.js.map +1 -0
  11. package/packages/acp-bridge/dist/cli.d.ts +18 -0
  12. package/packages/acp-bridge/dist/cli.d.ts.map +1 -0
  13. package/packages/acp-bridge/dist/cli.js +125 -0
  14. package/packages/acp-bridge/dist/cli.js.map +1 -0
  15. package/packages/acp-bridge/dist/index.d.ts +24 -0
  16. package/packages/acp-bridge/dist/index.d.ts.map +1 -0
  17. package/packages/acp-bridge/dist/index.js +22 -0
  18. package/packages/acp-bridge/dist/index.js.map +1 -0
  19. package/packages/acp-bridge/dist/types.d.ts +144 -0
  20. package/packages/acp-bridge/dist/types.d.ts.map +1 -0
  21. package/packages/acp-bridge/dist/types.js +6 -0
  22. package/packages/acp-bridge/dist/types.js.map +1 -0
  23. package/packages/acp-bridge/package.json +60 -0
  24. package/packages/acp-bridge/src/acp-agent.ts +826 -0
  25. package/packages/acp-bridge/src/cli.ts +137 -0
  26. package/packages/acp-bridge/src/index.ts +34 -0
  27. package/packages/acp-bridge/src/types.ts +142 -0
  28. package/packages/acp-bridge/tsconfig.json +11 -0
  29. package/packages/api-types/package.json +1 -1
  30. package/packages/benchmark/package.json +4 -4
  31. package/packages/bridge/package.json +8 -8
  32. package/packages/cli-tester/package.json +1 -1
  33. package/packages/config/package.json +2 -2
  34. package/packages/continuity/package.json +2 -2
  35. package/packages/daemon/package.json +12 -12
  36. package/packages/hooks/package.json +4 -4
  37. package/packages/mcp/dist/client.d.ts.map +1 -1
  38. package/packages/mcp/dist/client.js +10 -2
  39. package/packages/mcp/dist/client.js.map +1 -1
  40. package/packages/mcp/dist/install.d.ts.map +1 -1
  41. package/packages/mcp/dist/install.js +75 -9
  42. package/packages/mcp/dist/install.js.map +1 -1
  43. package/packages/mcp/dist/tools/relay-connected.d.ts.map +1 -1
  44. package/packages/mcp/dist/tools/relay-connected.js +15 -1
  45. package/packages/mcp/dist/tools/relay-connected.js.map +1 -1
  46. package/packages/mcp/dist/tools/relay-who.d.ts.map +1 -1
  47. package/packages/mcp/dist/tools/relay-who.js +17 -2
  48. package/packages/mcp/dist/tools/relay-who.js.map +1 -1
  49. package/packages/mcp/package.json +4 -4
  50. package/packages/mcp/src/client.ts +10 -2
  51. package/packages/mcp/src/install.ts +85 -9
  52. package/packages/mcp/src/tools/relay-connected.ts +16 -1
  53. package/packages/mcp/src/tools/relay-who.ts +18 -2
  54. package/packages/memory/package.json +2 -2
  55. package/packages/policy/package.json +2 -2
  56. package/packages/protocol/package.json +1 -1
  57. package/packages/resiliency/package.json +1 -1
  58. package/packages/sdk/package.json +3 -3
  59. package/packages/spawner/package.json +1 -1
  60. package/packages/state/package.json +1 -1
  61. package/packages/storage/package.json +2 -2
  62. package/packages/telemetry/package.json +1 -1
  63. package/packages/trajectory/package.json +2 -2
  64. package/packages/user-directory/package.json +2 -2
  65. package/packages/utils/package.json +3 -3
  66. package/packages/wrapper/package.json +6 -6
package/dist/index.cjs CHANGED
@@ -64724,8 +64724,59 @@ function getEditorConfig(editorKey) {
64724
64724
  return editors[editorKey];
64725
64725
  }
64726
64726
  function stripJsonComments(content) {
64727
- let result = content.replace(/\/\/.*$/gm, "");
64728
- result = result.replace(/\/\*[\s\S]*?\*\//g, "");
64727
+ let result = "";
64728
+ let inString = false;
64729
+ let inSingleLineComment = false;
64730
+ let inMultiLineComment = false;
64731
+ let i = 0;
64732
+ while (i < content.length) {
64733
+ const char = content[i];
64734
+ const nextChar = content[i + 1];
64735
+ if (!inSingleLineComment && !inMultiLineComment) {
64736
+ if (char === '"' && (i === 0 || content[i - 1] !== "\\")) {
64737
+ inString = !inString;
64738
+ result += char;
64739
+ i++;
64740
+ continue;
64741
+ }
64742
+ if (inString) {
64743
+ result += char;
64744
+ i++;
64745
+ continue;
64746
+ }
64747
+ if (char === "/" && nextChar === "/") {
64748
+ inSingleLineComment = true;
64749
+ i += 2;
64750
+ continue;
64751
+ }
64752
+ if (char === "/" && nextChar === "*") {
64753
+ inMultiLineComment = true;
64754
+ i += 2;
64755
+ continue;
64756
+ }
64757
+ result += char;
64758
+ i++;
64759
+ continue;
64760
+ }
64761
+ if (inSingleLineComment) {
64762
+ if (char === "\n") {
64763
+ inSingleLineComment = false;
64764
+ result += char;
64765
+ }
64766
+ i++;
64767
+ continue;
64768
+ }
64769
+ if (inMultiLineComment) {
64770
+ if (char === "*" && nextChar === "/") {
64771
+ inMultiLineComment = false;
64772
+ i += 2;
64773
+ continue;
64774
+ }
64775
+ i++;
64776
+ continue;
64777
+ }
64778
+ i++;
64779
+ }
64729
64780
  return result;
64730
64781
  }
64731
64782
  function readConfigFile(configPath, format) {
@@ -64823,12 +64874,19 @@ function installForEditor(editorKey, options = {}) {
64823
64874
  const defaultConfig = buildServerConfig();
64824
64875
  const serverConfig = {
64825
64876
  command: options.command || defaultConfig.command,
64826
- args: options.args || [...defaultConfig.args]
64877
+ args: options.args || [...defaultConfig.args],
64878
+ env: options.env ? { ...options.env } : void 0
64827
64879
  };
64828
- if (options.env) {
64829
- serverConfig.env = { ...options.env };
64830
- }
64831
64880
  const isProjectLocal = !options.global && options.projectDir;
64881
+ if (isProjectLocal) {
64882
+ const projectSocket = (0, import_node_path16.join)(options.projectDir, ".agent-relay", "relay.sock");
64883
+ serverConfig.env = {
64884
+ ...serverConfig.env || {},
64885
+ RELAY_SOCKET: serverConfig.env?.RELAY_SOCKET || projectSocket,
64886
+ RELAY_PROJECT: serverConfig.env?.RELAY_PROJECT || options.projectDir,
64887
+ AGENT_RELAY_PROJECT: serverConfig.env?.AGENT_RELAY_PROJECT || options.projectDir
64888
+ };
64889
+ }
64832
64890
  if (isProjectLocal) {
64833
64891
  serverConfig.args = [...serverConfig.args, "--project", options.projectDir];
64834
64892
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AA8JH;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqC7H"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AA8VH;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqC7H"}
@@ -28,6 +28,7 @@ import { initTelemetry, track, enableTelemetry, disableTelemetry, getStatus, isD
28
28
  import { installMcpConfig } from '@agent-relay/mcp';
29
29
  import fs from 'node:fs';
30
30
  import path from 'node:path';
31
+ import { homedir } from 'node:os';
31
32
  import readline from 'node:readline';
32
33
  import { promisify } from 'node:util';
33
34
  import { exec, execSync, spawn as spawnProcess } from 'node:child_process';
@@ -145,6 +146,163 @@ function startDashboardViaNpx(options) {
145
146
  });
146
147
  return { process: dashboardProcess, port: options.port, ready };
147
148
  }
149
+ /**
150
+ * Strip JSONC comments while preserving strings that contain // or /* sequences.
151
+ * Uses a state machine to track whether we're inside a string literal.
152
+ */
153
+ function stripJsonComments(content) {
154
+ let result = '';
155
+ let inString = false;
156
+ let inSingleLineComment = false;
157
+ let inMultiLineComment = false;
158
+ let i = 0;
159
+ while (i < content.length) {
160
+ const char = content[i];
161
+ const nextChar = content[i + 1];
162
+ // Handle string state (only when not in a comment)
163
+ if (!inSingleLineComment && !inMultiLineComment) {
164
+ if (char === '"' && (i === 0 || content[i - 1] !== '\\')) {
165
+ inString = !inString;
166
+ result += char;
167
+ i++;
168
+ continue;
169
+ }
170
+ // If in string, just copy the character
171
+ if (inString) {
172
+ result += char;
173
+ i++;
174
+ continue;
175
+ }
176
+ // Check for single-line comment start
177
+ if (char === '/' && nextChar === '/') {
178
+ inSingleLineComment = true;
179
+ i += 2;
180
+ continue;
181
+ }
182
+ // Check for multi-line comment start
183
+ if (char === '/' && nextChar === '*') {
184
+ inMultiLineComment = true;
185
+ i += 2;
186
+ continue;
187
+ }
188
+ // Not in any comment or string, copy the character
189
+ result += char;
190
+ i++;
191
+ continue;
192
+ }
193
+ // Handle single-line comment end
194
+ if (inSingleLineComment) {
195
+ if (char === '\n') {
196
+ inSingleLineComment = false;
197
+ result += char; // Preserve newline
198
+ }
199
+ i++;
200
+ continue;
201
+ }
202
+ // Handle multi-line comment end
203
+ if (inMultiLineComment) {
204
+ if (char === '*' && nextChar === '/') {
205
+ inMultiLineComment = false;
206
+ i += 2;
207
+ continue;
208
+ }
209
+ i++;
210
+ continue;
211
+ }
212
+ i++;
213
+ }
214
+ return result;
215
+ }
216
+ function readJsonWithComments(filePath) {
217
+ if (!fs.existsSync(filePath))
218
+ return {};
219
+ try {
220
+ const raw = fs.readFileSync(filePath, 'utf-8');
221
+ const sanitized = stripJsonComments(raw).trim();
222
+ if (!sanitized)
223
+ return {};
224
+ return JSON.parse(sanitized);
225
+ }
226
+ catch (err) {
227
+ throw new Error(`Failed to read ${filePath}: ${err.message}`);
228
+ }
229
+ }
230
+ function writeJson(filePath, data) {
231
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
232
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
233
+ }
234
+ function getZedSettingsPath(customPath) {
235
+ if (customPath)
236
+ return path.resolve(customPath);
237
+ const configDir = process.env.ZED_CONFIG_DIR || process.env.ZED_HOME || path.join(homedir(), '.config', 'zed');
238
+ return path.join(configDir, 'settings.json');
239
+ }
240
+ function findLocalAcpBridge(projectRoot) {
241
+ const candidates = [
242
+ path.join(projectRoot, 'packages', 'acp-bridge', 'dist', 'cli.js'),
243
+ path.join(projectRoot, 'node_modules', '@agent-relay', 'acp-bridge', 'dist', 'cli.js'),
244
+ ];
245
+ for (const candidate of candidates) {
246
+ if (fs.existsSync(candidate)) {
247
+ return candidate;
248
+ }
249
+ }
250
+ return null;
251
+ }
252
+ function buildZedAgentServerConfig(options) {
253
+ const bridgeName = options.bridgeName || 'zed-bridge';
254
+ const baseArgs = ['--name', bridgeName, '--socket', options.socketPath];
255
+ if (options.enableDebug)
256
+ baseArgs.push('--debug');
257
+ const localCli = findLocalAcpBridge(options.projectRoot);
258
+ if (localCli) {
259
+ return {
260
+ command: process.execPath,
261
+ args: [localCli, ...baseArgs],
262
+ env: {},
263
+ };
264
+ }
265
+ return {
266
+ command: 'relay-acp',
267
+ args: baseArgs,
268
+ env: {},
269
+ };
270
+ }
271
+ function configureZedAgentServer(options) {
272
+ const key = options.agentServerName || 'Agent Relay';
273
+ const config = readJsonWithComments(options.settingsPath);
274
+ const created = !fs.existsSync(options.settingsPath);
275
+ // Ensure root object
276
+ const normalizedConfig = typeof config === 'object' && config !== null ? { ...config } : {};
277
+ const agentServers = (normalizedConfig.agent_servers && typeof normalizedConfig.agent_servers === 'object'
278
+ ? { ...normalizedConfig.agent_servers }
279
+ : {});
280
+ normalizedConfig.agent_servers = agentServers;
281
+ const serverConfig = buildZedAgentServerConfig({
282
+ projectRoot: options.projectRoot,
283
+ socketPath: options.socketPath,
284
+ bridgeName: 'zed-bridge',
285
+ enableDebug: options.enableDebug,
286
+ });
287
+ const targetEntry = {
288
+ type: 'custom',
289
+ command: serverConfig.command,
290
+ args: serverConfig.args,
291
+ env: serverConfig.env,
292
+ };
293
+ const existingEntry = agentServers[key];
294
+ const unchanged = existingEntry && JSON.stringify(existingEntry) === JSON.stringify(targetEntry);
295
+ if (!unchanged) {
296
+ agentServers[key] = targetEntry;
297
+ writeJson(options.settingsPath, normalizedConfig);
298
+ }
299
+ return {
300
+ updated: !unchanged,
301
+ created,
302
+ path: options.settingsPath,
303
+ key,
304
+ };
305
+ }
148
306
  /**
149
307
  * Install agent-relay-snippet to markdown files using prpm.
150
308
  * Installs to CLAUDE.md, GEMINI.md, and AGENTS.md.
@@ -470,6 +628,9 @@ program
470
628
  .option('--watch', 'Auto-restart daemon on crash (supervisor mode)')
471
629
  .option('--max-restarts <n>', 'Max restarts in 60s before giving up (default: 5)', '5')
472
630
  .option('--verbose', 'Enable verbose logging (show debug output in console)')
631
+ .option('--zed', 'Add Agent Relay entry to Zed agent_servers for this workspace')
632
+ .option('--zed-config <path>', 'Custom path to Zed settings.json (defaults to ~/.config/zed/settings.json)')
633
+ .option('--zed-name <name>', 'Display name for the Zed agent server (default: Agent Relay)')
473
634
  .action(async (options) => {
474
635
  // If --watch is specified, run in supervisor mode
475
636
  if (options.watch) {
@@ -494,6 +655,12 @@ program
494
655
  args.push('--no-spawn');
495
656
  if (options.verbose)
496
657
  args.push('--verbose');
658
+ if (options.zed)
659
+ args.push('--zed');
660
+ if (options.zedConfig)
661
+ args.push('--zed-config', options.zedConfig);
662
+ if (options.zedName)
663
+ args.push('--zed-name', options.zedName);
497
664
  console.log(`[supervisor] Starting daemon...`);
498
665
  child = spawnProcess(process.execPath, [process.argv[1], ...args], {
499
666
  stdio: 'inherit',
@@ -569,6 +736,25 @@ program
569
736
  }
570
737
  console.log('');
571
738
  }
739
+ // Optionally configure Zed agent_servers with the ACP bridge
740
+ if (options.zed) {
741
+ try {
742
+ const zedSettingsPath = getZedSettingsPath(options.zedConfig);
743
+ const zedDebug = options.verbose === undefined ? true : Boolean(options.verbose);
744
+ const result = configureZedAgentServer({
745
+ settingsPath: zedSettingsPath,
746
+ socketPath,
747
+ projectRoot: paths.projectRoot,
748
+ agentServerName: options.zedName,
749
+ enableDebug: zedDebug,
750
+ });
751
+ const status = result.updated ? (result.created ? 'created' : 'updated') : 'already configured';
752
+ console.log(`[zed] ${status}: ${result.path} (${result.key})`);
753
+ }
754
+ catch (err) {
755
+ console.log(`[zed] Failed to configure Zed agent_servers: ${err.message}`);
756
+ }
757
+ }
572
758
  // Set up log file to avoid console output polluting TUI terminals
573
759
  // Only set if not already configured via environment
574
760
  // Skip if --verbose is set (logs go to console in verbose mode)
@@ -956,6 +1142,214 @@ program
956
1142
  }
957
1143
  }
958
1144
  });
1145
+ // uninstall - Remove agent-relay from the current project
1146
+ program
1147
+ .command('uninstall')
1148
+ .description('Remove agent-relay data and configuration from the current project')
1149
+ .option('--keep-data', 'Keep message history and database (only remove runtime files)')
1150
+ .option('--zed', 'Also remove Zed editor configuration')
1151
+ .option('--zed-name <name>', 'Name of the Zed agent server entry to remove (default: Agent Relay)')
1152
+ .option('--snippets', 'Also remove agent-relay snippets from CLAUDE.md, GEMINI.md, AGENTS.md')
1153
+ .option('--force', 'Skip confirmation prompt')
1154
+ .option('--dry-run', 'Show what would be removed without actually removing')
1155
+ .action(async (options) => {
1156
+ const paths = getProjectPaths();
1157
+ const readline = await import('node:readline');
1158
+ const filesToRemove = [];
1159
+ const dirsToRemove = [];
1160
+ const actions = [];
1161
+ // Check if .agent-relay directory exists
1162
+ if (!fs.existsSync(paths.dataDir)) {
1163
+ console.log('Agent Relay is not installed in this project.');
1164
+ console.log(`(No ${paths.dataDir} directory found)`);
1165
+ return;
1166
+ }
1167
+ // Stop daemon if running
1168
+ const pidPath = pidFilePathForSocket(paths.socketPath);
1169
+ if (fs.existsSync(pidPath)) {
1170
+ const pid = Number(fs.readFileSync(pidPath, 'utf-8').trim());
1171
+ try {
1172
+ process.kill(pid, 0); // Check if running
1173
+ actions.push(`Stop daemon (pid: ${pid})`);
1174
+ if (!options.dryRun) {
1175
+ try {
1176
+ process.kill(pid, 'SIGTERM');
1177
+ // Wait briefly for graceful shutdown
1178
+ await new Promise(resolve => setTimeout(resolve, 2000));
1179
+ }
1180
+ catch { /* ignore */ }
1181
+ }
1182
+ }
1183
+ catch { /* not running */ }
1184
+ }
1185
+ // Collect files to remove
1186
+ if (options.keepData) {
1187
+ // Only remove runtime files, keep database
1188
+ const runtimeFiles = ['relay.sock', 'runtime.json', 'daemon.pid', '.project'];
1189
+ for (const file of runtimeFiles) {
1190
+ const filePath = path.join(paths.dataDir, file);
1191
+ if (fs.existsSync(filePath)) {
1192
+ filesToRemove.push(filePath);
1193
+ }
1194
+ }
1195
+ // Remove mcp-identity-* files
1196
+ try {
1197
+ const files = fs.readdirSync(paths.dataDir);
1198
+ for (const file of files) {
1199
+ if (file.startsWith('mcp-identity')) {
1200
+ filesToRemove.push(path.join(paths.dataDir, file));
1201
+ }
1202
+ }
1203
+ }
1204
+ catch { /* ignore */ }
1205
+ actions.push('Remove runtime files (keeping database and message history)');
1206
+ }
1207
+ else {
1208
+ // Remove entire .agent-relay directory
1209
+ dirsToRemove.push(paths.dataDir);
1210
+ actions.push(`Remove ${paths.dataDir}/ directory (including message history)`);
1211
+ }
1212
+ // Zed configuration
1213
+ if (options.zed) {
1214
+ const zedSettingsPath = getZedSettingsPath();
1215
+ if (fs.existsSync(zedSettingsPath)) {
1216
+ const zedNameToRemove = options.zedName || 'Agent Relay';
1217
+ actions.push(`Remove "${zedNameToRemove}" (and any relay-acp entries) from Zed settings`);
1218
+ }
1219
+ }
1220
+ // Snippets
1221
+ if (options.snippets) {
1222
+ const snippetFiles = ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md'];
1223
+ for (const file of snippetFiles) {
1224
+ const filePath = path.join(paths.projectRoot, file);
1225
+ if (fs.existsSync(filePath)) {
1226
+ const content = fs.readFileSync(filePath, 'utf-8');
1227
+ if (content.includes('agent-relay-snippet') || content.includes('agent-relay-protocol')) {
1228
+ actions.push(`Remove agent-relay snippets from ${file}`);
1229
+ }
1230
+ }
1231
+ }
1232
+ }
1233
+ // Show what will be done
1234
+ console.log('');
1235
+ console.log('Agent Relay Uninstall');
1236
+ console.log('=====================');
1237
+ console.log('');
1238
+ console.log('The following actions will be performed:');
1239
+ console.log('');
1240
+ for (const action of actions) {
1241
+ console.log(` • ${action}`);
1242
+ }
1243
+ console.log('');
1244
+ if (options.dryRun) {
1245
+ console.log('[dry-run] No changes made.');
1246
+ return;
1247
+ }
1248
+ // Confirm unless --force
1249
+ if (!options.force) {
1250
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1251
+ const answer = await new Promise(resolve => {
1252
+ rl.question('Continue? [y/N] ', resolve);
1253
+ });
1254
+ rl.close();
1255
+ if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
1256
+ console.log('Aborted.');
1257
+ return;
1258
+ }
1259
+ }
1260
+ // Perform removal
1261
+ console.log('');
1262
+ // Remove files
1263
+ for (const file of filesToRemove) {
1264
+ try {
1265
+ fs.unlinkSync(file);
1266
+ console.log(` ✓ Removed ${path.relative(paths.projectRoot, file)}`);
1267
+ }
1268
+ catch (err) {
1269
+ console.log(` ✗ Failed to remove ${path.relative(paths.projectRoot, file)}: ${err.message}`);
1270
+ }
1271
+ }
1272
+ // Remove directories
1273
+ for (const dir of dirsToRemove) {
1274
+ try {
1275
+ fs.rmSync(dir, { recursive: true, force: true });
1276
+ console.log(` ✓ Removed ${path.relative(paths.projectRoot, dir)}/`);
1277
+ }
1278
+ catch (err) {
1279
+ console.log(` ✗ Failed to remove ${path.relative(paths.projectRoot, dir)}/: ${err.message}`);
1280
+ }
1281
+ }
1282
+ // Remove Zed config
1283
+ if (options.zed) {
1284
+ const zedSettingsPath = getZedSettingsPath();
1285
+ if (fs.existsSync(zedSettingsPath)) {
1286
+ try {
1287
+ const config = readJsonWithComments(zedSettingsPath);
1288
+ const agentServers = config.agent_servers;
1289
+ if (agentServers) {
1290
+ const removedEntries = [];
1291
+ const zedNameToRemove = options.zedName || 'Agent Relay';
1292
+ // Remove the specified entry by name
1293
+ if (agentServers[zedNameToRemove]) {
1294
+ delete agentServers[zedNameToRemove];
1295
+ removedEntries.push(zedNameToRemove);
1296
+ }
1297
+ // Also scan for any entries that look like relay-acp bridge
1298
+ // (command/args contain 'acp-bridge' or 'relay-acp')
1299
+ for (const [key, value] of Object.entries(agentServers)) {
1300
+ if (key === zedNameToRemove)
1301
+ continue; // Already handled
1302
+ if (typeof value === 'object' && value !== null) {
1303
+ const entry = value;
1304
+ const command = entry.command;
1305
+ const args = entry.args;
1306
+ const isRelayAcp = (command && (command.includes('acp-bridge') || command.includes('relay-acp'))) ||
1307
+ (args && args.some(arg => arg.includes('acp-bridge') || arg.includes('relay-acp')));
1308
+ if (isRelayAcp) {
1309
+ delete agentServers[key];
1310
+ removedEntries.push(key);
1311
+ }
1312
+ }
1313
+ }
1314
+ if (removedEntries.length > 0) {
1315
+ writeJson(zedSettingsPath, config);
1316
+ for (const entry of removedEntries) {
1317
+ console.log(` ✓ Removed "${entry}" from Zed settings`);
1318
+ }
1319
+ }
1320
+ }
1321
+ }
1322
+ catch (err) {
1323
+ console.log(` ✗ Failed to update Zed settings: ${err.message}`);
1324
+ }
1325
+ }
1326
+ }
1327
+ // Remove snippets
1328
+ if (options.snippets) {
1329
+ const snippetFiles = ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md'];
1330
+ for (const file of snippetFiles) {
1331
+ const filePath = path.join(paths.projectRoot, file);
1332
+ if (fs.existsSync(filePath)) {
1333
+ try {
1334
+ let content = fs.readFileSync(filePath, 'utf-8');
1335
+ const originalLength = content.length;
1336
+ // Remove prpm snippet blocks
1337
+ content = content.replace(/<!-- prpm:snippet:start @agent-relay\/agent-relay-snippet.*?<!-- prpm:snippet:end @agent-relay\/agent-relay-snippet[^\n]*\n?/gs, '');
1338
+ content = content.replace(/<!-- prpm:snippet:start @agent-relay\/agent-relay-protocol.*?<!-- prpm:snippet:end @agent-relay\/agent-relay-protocol[^\n]*\n?/gs, '');
1339
+ if (content.length !== originalLength) {
1340
+ fs.writeFileSync(filePath, content);
1341
+ console.log(` ✓ Removed agent-relay snippets from ${file}`);
1342
+ }
1343
+ }
1344
+ catch (err) {
1345
+ console.log(` ✗ Failed to update ${file}: ${err.message}`);
1346
+ }
1347
+ }
1348
+ }
1349
+ }
1350
+ console.log('');
1351
+ console.log('Uninstall complete.');
1352
+ });
959
1353
  program
960
1354
  .command('status')
961
1355
  .description('Check daemon status')
@@ -1794,7 +2188,7 @@ function loadAgents(agentsPath) {
1794
2188
  }
1795
2189
  const STALE_THRESHOLD_MS = 30_000;
1796
2190
  // Internal agents that should be hidden from `agents` and `who` by default
1797
- const INTERNAL_AGENTS = new Set(['cli', 'Dashboard']);
2191
+ const INTERNAL_AGENTS = new Set(['cli', 'Dashboard', 'zed-bridge']);
1798
2192
  function isInternalAgent(name) {
1799
2193
  if (!name)
1800
2194
  return true;