agent-comms 1.0.0 → 1.0.4

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 (51) hide show
  1. package/README.md +108 -0
  2. package/bin.js +29 -0
  3. package/dist/bridges/claude-code/channel.d.ts +17 -0
  4. package/dist/bridges/claude-code/channel.d.ts.map +1 -0
  5. package/dist/bridges/claude-code/channel.js +143 -0
  6. package/dist/bridges/claude-code/channel.js.map +1 -0
  7. package/dist/bridges/codex/stop_hook.d.ts +13 -0
  8. package/dist/bridges/codex/stop_hook.d.ts.map +1 -0
  9. package/dist/bridges/codex/stop_hook.js +99 -0
  10. package/dist/bridges/codex/stop_hook.js.map +1 -0
  11. package/dist/bridges/codex/tool.d.ts +14 -0
  12. package/dist/bridges/codex/tool.d.ts.map +1 -0
  13. package/dist/bridges/codex/tool.js +71 -0
  14. package/dist/bridges/codex/tool.js.map +1 -0
  15. package/dist/bridges/opencode/plugin.d.ts +23 -0
  16. package/dist/bridges/opencode/plugin.d.ts.map +1 -0
  17. package/dist/bridges/opencode/plugin.js +73 -0
  18. package/dist/bridges/opencode/plugin.js.map +1 -0
  19. package/dist/bridges/pi/index.d.ts +11 -0
  20. package/dist/bridges/pi/index.d.ts.map +1 -0
  21. package/dist/bridges/pi/index.js +140 -0
  22. package/dist/bridges/pi/index.js.map +1 -0
  23. package/dist/cli.d.ts +16 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +505 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/core/bridge.d.ts +77 -0
  28. package/dist/core/bridge.d.ts.map +1 -0
  29. package/dist/core/bridge.js +224 -0
  30. package/dist/core/bridge.js.map +1 -0
  31. package/dist/core/index.d.ts +13 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +11 -0
  34. package/dist/core/index.js.map +1 -0
  35. package/dist/core/nanoid.d.ts +6 -0
  36. package/dist/core/nanoid.d.ts.map +1 -0
  37. package/dist/core/nanoid.js +19 -0
  38. package/dist/core/nanoid.js.map +1 -0
  39. package/dist/core/store.d.ts +60 -0
  40. package/dist/core/store.d.ts.map +1 -0
  41. package/dist/core/store.js +431 -0
  42. package/dist/core/store.js.map +1 -0
  43. package/dist/core/tool.d.ts +40 -0
  44. package/dist/core/tool.d.ts.map +1 -0
  45. package/dist/core/tool.js +210 -0
  46. package/dist/core/tool.js.map +1 -0
  47. package/dist/core/types.d.ts +308 -0
  48. package/dist/core/types.d.ts.map +1 -0
  49. package/dist/core/types.js +168 -0
  50. package/dist/core/types.js.map +1 -0
  51. package/package.json +73 -8
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Agent Comms — pi bridge extension.
3
+ *
4
+ * Provides the `agent_comms` tool and watches the delivery queue,
5
+ * pushing incoming messages via sendUserMessage().
6
+ *
7
+ * Install: add bridge path to ~/.pi/agent/settings.json extensions array
8
+ */
9
+ import * as fs from "node:fs";
10
+ import * as path from "node:path";
11
+ import * as os from "node:os";
12
+ import { Type } from "typebox";
13
+ import { StringEnum } from "@mariozechner/pi-ai";
14
+ import { BusStore, BusTool, buildAction, ensureRegistered, drainAndFormat, } from "../../core/index.js";
15
+ import { nanoid } from "../../core/nanoid.js";
16
+ const BUS_ROOT = path.join(os.homedir(), ".agents", "bus");
17
+ export default function (pi) {
18
+ const store = new BusStore(BUS_ROOT);
19
+ const tool = new BusTool(store);
20
+ let agentId;
21
+ let watcher;
22
+ // -----------------------------------------------------------------------
23
+ // Lifecycle
24
+ // -----------------------------------------------------------------------
25
+ pi.on("session_start", async (_event, ctx) => {
26
+ const reg = await ensureRegistered({
27
+ store,
28
+ harness: "pi",
29
+ defaultName: `pi-${nanoid(4)}`,
30
+ });
31
+ agentId = reg.agentId;
32
+ if (!reg.isNew) {
33
+ ctx.ui.notify(`Agent Comms: resumed as ${reg.agentId}`, "info");
34
+ await drainAndPush();
35
+ }
36
+ startWatching();
37
+ });
38
+ pi.on("session_shutdown", async () => {
39
+ watcher?.close();
40
+ if (agentId) {
41
+ await store.setAgentOffline(agentId);
42
+ }
43
+ });
44
+ // -----------------------------------------------------------------------
45
+ // Delivery watcher
46
+ // -----------------------------------------------------------------------
47
+ function startWatching() {
48
+ if (!agentId)
49
+ return;
50
+ const deliveryDir = store.deliveryDir(agentId);
51
+ fs.mkdirSync(deliveryDir, { recursive: true });
52
+ watcher = fs.watch(deliveryDir, (event, filename) => {
53
+ if (event !== "rename" || !filename?.endsWith(".json"))
54
+ return;
55
+ void drainAndPush();
56
+ });
57
+ }
58
+ async function drainAndPush() {
59
+ if (!agentId)
60
+ return;
61
+ const lines = await drainAndFormat(store, agentId);
62
+ for (const line of lines) {
63
+ pi.sendUserMessage(`📬 ${line}`, { deliverAs: "followUp" });
64
+ }
65
+ }
66
+ // -----------------------------------------------------------------------
67
+ // Tool registration
68
+ // -----------------------------------------------------------------------
69
+ pi.registerTool({
70
+ name: "agent_comms",
71
+ label: "Agent Comms",
72
+ description: [
73
+ "Cross-harness agent communication bus. Send messages to rooms and DM other agents.",
74
+ "Actions: register, update, whoami, create_room, list_rooms, join_room, leave_room,",
75
+ "send, dm, list_agents, read_room, invite, kick, destroy_room.",
76
+ "Register first, then join or create rooms to communicate.",
77
+ ].join(" "),
78
+ promptSnippet: "Communicate with other LLM agents via rooms and DMs",
79
+ promptGuidelines: [
80
+ "Use agent_comms to coordinate with other running agents. Register on session start, join rooms for collaboration.",
81
+ ],
82
+ parameters: Type.Object({
83
+ action: StringEnum([
84
+ "register",
85
+ "update",
86
+ "whoami",
87
+ "create_room",
88
+ "list_rooms",
89
+ "join_room",
90
+ "leave_room",
91
+ "send",
92
+ "dm",
93
+ "list_agents",
94
+ "read_room",
95
+ "invite",
96
+ "kick",
97
+ "destroy_room",
98
+ ], { description: "Action to perform" }),
99
+ name: Type.Optional(Type.String({
100
+ description: "Agent display name (for register/update)",
101
+ })),
102
+ visibility: Type.Optional(StringEnum(["visible", "hidden", "ghost"], {
103
+ description: "Visibility to other agents",
104
+ })),
105
+ tags: Type.Optional(Type.Array(Type.String(), { description: "Agent capability tags" })),
106
+ status: Type.Optional(StringEnum(["active", "idle", "busy"], {
107
+ description: "Agent status (for update)",
108
+ })),
109
+ room: Type.Optional(Type.String({ description: "Room name/ID" })),
110
+ type: Type.Optional(StringEnum(["public", "private", "secret"], {
111
+ description: "Room type (for create_room)",
112
+ })),
113
+ description: Type.Optional(Type.String({ description: "Room description (for create_room)" })),
114
+ target: Type.Optional(Type.String({ description: "Target room name or agent ID" })),
115
+ content: Type.Optional(Type.String({ description: "Message content" })),
116
+ replyTo: Type.Optional(Type.String({ description: "Message ID to reply to" })),
117
+ agent: Type.Optional(Type.String({ description: "Target agent ID (for invite/kick)" })),
118
+ since: Type.Optional(Type.String({
119
+ description: "ISO timestamp to read messages since",
120
+ })),
121
+ }),
122
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
123
+ if (!agentId) {
124
+ return {
125
+ content: [{ type: "text", text: "Error: not registered" }],
126
+ details: {},
127
+ isError: true,
128
+ };
129
+ }
130
+ const action = buildAction(params);
131
+ const result = await tool.handle({ agentId, harness: "pi", pid: process.pid }, action);
132
+ return {
133
+ content: [{ type: "text", text: result.content }],
134
+ details: { action: params.action },
135
+ isError: result.isError,
136
+ };
137
+ },
138
+ });
139
+ }
140
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/bridges/pi/index.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * agent-comms — cross-harness LLM agent communication bus.
3
+ *
4
+ * Usage:
5
+ * npx agent-comms # setup (auto-detect and configure)
6
+ * npx agent-comms setup # same as above
7
+ * npx agent-comms status # check current configuration
8
+ * npx agent-comms remove # undo configuration
9
+ * npx agent-comms bridge <id> # run a bridge (used by harness configs)
10
+ *
11
+ * The bridge subcommand lets harnesses invoke the bridge via npx:
12
+ * .mcp.json: { "command": "npx", "args": ["agent-comms", "bridge", "claude-code"] }
13
+ * config.toml: command = "npx", args = ["agent-comms", "bridge", "codex"]
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
package/dist/cli.js ADDED
@@ -0,0 +1,505 @@
1
+ /**
2
+ * agent-comms — cross-harness LLM agent communication bus.
3
+ *
4
+ * Usage:
5
+ * npx agent-comms # setup (auto-detect and configure)
6
+ * npx agent-comms setup # same as above
7
+ * npx agent-comms status # check current configuration
8
+ * npx agent-comms remove # undo configuration
9
+ * npx agent-comms bridge <id> # run a bridge (used by harness configs)
10
+ *
11
+ * The bridge subcommand lets harnesses invoke the bridge via npx:
12
+ * .mcp.json: { "command": "npx", "args": ["agent-comms", "bridge", "claude-code"] }
13
+ * config.toml: command = "npx", args = ["agent-comms", "bridge", "codex"]
14
+ */
15
+ import * as fs from "node:fs";
16
+ import * as path from "node:path";
17
+ import * as os from "node:os";
18
+ import { execSync, spawn } from "node:child_process";
19
+ import { z } from "zod";
20
+ // ---------------------------------------------------------------------------
21
+ // Zod schemas for config files
22
+ // ---------------------------------------------------------------------------
23
+ const PiSettingsSchema = z
24
+ .object({
25
+ extensions: z.array(z.string()).optional(),
26
+ })
27
+ .loose();
28
+ const McpServersSchema = z
29
+ .object({
30
+ mcpServers: z.record(z.string(), z
31
+ .object({
32
+ command: z.string(),
33
+ args: z.array(z.string()).optional(),
34
+ })
35
+ .loose()),
36
+ })
37
+ .loose();
38
+ const HooksSchema = z
39
+ .object({
40
+ hooks: z
41
+ .object({
42
+ Stop: z
43
+ .array(z
44
+ .object({
45
+ hooks: z
46
+ .array(z
47
+ .object({
48
+ type: z.string(),
49
+ command: z.string(),
50
+ timeout: z.number().optional(),
51
+ })
52
+ .loose())
53
+ .optional(),
54
+ })
55
+ .loose())
56
+ .optional(),
57
+ })
58
+ .optional(),
59
+ })
60
+ .loose();
61
+ const OpenCodeConfigSchema = z
62
+ .object({
63
+ plugin: z.array(z.string()).optional(),
64
+ })
65
+ .loose();
66
+ // ---------------------------------------------------------------------------
67
+ // Config I/O
68
+ // ---------------------------------------------------------------------------
69
+ function readJsonFile(filePath) {
70
+ try {
71
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
72
+ }
73
+ catch {
74
+ return undefined;
75
+ }
76
+ }
77
+ function writeJsonFile(filePath, data) {
78
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
79
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
80
+ console.log(` → Wrote ${filePath}`);
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Constants
84
+ // ---------------------------------------------------------------------------
85
+ const HOME = os.homedir();
86
+ const PKG_DIR = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
87
+ // ---------------------------------------------------------------------------
88
+ // Harness definitions
89
+ // ---------------------------------------------------------------------------
90
+ const harnesses = [
91
+ {
92
+ id: "pi",
93
+ detect: () => fs.existsSync(path.join(HOME, ".pi", "agent", "extensions")),
94
+ configure: () => {
95
+ configurePi();
96
+ },
97
+ remove: () => {
98
+ removePi();
99
+ },
100
+ check: () => checkPi(),
101
+ },
102
+ {
103
+ id: "claude-code",
104
+ detect: () => {
105
+ try {
106
+ execSync("which claude", { stdio: "pipe" });
107
+ return true;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ },
113
+ configure: () => {
114
+ configureClaudeCode();
115
+ },
116
+ remove: () => {
117
+ removeClaudeCode();
118
+ },
119
+ check: () => checkClaudeCode(),
120
+ },
121
+ {
122
+ id: "codex",
123
+ detect: () => {
124
+ try {
125
+ execSync("which codex", { stdio: "pipe" });
126
+ return true;
127
+ }
128
+ catch {
129
+ return false;
130
+ }
131
+ },
132
+ configure: () => {
133
+ configureCodex();
134
+ },
135
+ remove: () => {
136
+ removeCodex();
137
+ },
138
+ check: () => checkCodex(),
139
+ },
140
+ {
141
+ id: "opencode",
142
+ detect: () => {
143
+ try {
144
+ execSync("which opencode", { stdio: "pipe" });
145
+ return true;
146
+ }
147
+ catch {
148
+ return false;
149
+ }
150
+ },
151
+ configure: () => {
152
+ configureOpenCode();
153
+ },
154
+ remove: () => {
155
+ removeOpenCode();
156
+ },
157
+ check: () => checkOpenCode(),
158
+ },
159
+ ];
160
+ // ---------------------------------------------------------------------------
161
+ // Main
162
+ // ---------------------------------------------------------------------------
163
+ const command = process.argv[2] ?? "setup";
164
+ switch (command) {
165
+ case "setup":
166
+ setup();
167
+ break;
168
+ case "status":
169
+ status();
170
+ break;
171
+ case "remove":
172
+ remove();
173
+ break;
174
+ case "bridge": {
175
+ const bridgeId = process.argv[3];
176
+ if (!bridgeId) {
177
+ console.error("Usage: agent-comms bridge <id>");
178
+ process.exit(1);
179
+ }
180
+ runBridge(bridgeId);
181
+ break;
182
+ }
183
+ default:
184
+ console.log("Usage: agent-comms [setup|status|remove|bridge <id>]");
185
+ process.exit(1);
186
+ }
187
+ // ---------------------------------------------------------------------------
188
+ // Bridge runner
189
+ // ---------------------------------------------------------------------------
190
+ function runBridge(id) {
191
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", id);
192
+ const entryPoints = [
193
+ path.join(bridgeDir, "channel.ts"),
194
+ path.join(bridgeDir, "tool.ts"),
195
+ path.join(bridgeDir, "index.ts"),
196
+ path.join(bridgeDir, "plugin.ts"),
197
+ path.join(bridgeDir, "stop_hook.ts"),
198
+ ];
199
+ const entry = entryPoints.find((f) => fs.existsSync(f));
200
+ if (!entry) {
201
+ console.error(`Unknown bridge: ${id}`);
202
+ process.exit(1);
203
+ }
204
+ const cmd = process.execPath;
205
+ const args = [entry, ...process.argv.slice(4)];
206
+ const child = spawn(cmd, args, { stdio: "inherit" });
207
+ child.on("exit", (code) => process.exit(code ?? 0));
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // Commands
211
+ // ---------------------------------------------------------------------------
212
+ function setup() {
213
+ console.log("🔌 Agent Comms setup\n");
214
+ const detected = harnesses.filter((h) => h.detect());
215
+ console.log("Detected harnesses:");
216
+ for (const h of harnesses) {
217
+ console.log(` ${detected.includes(h) ? "✓" : "✗"} ${h.id}`);
218
+ }
219
+ if (detected.length === 0) {
220
+ console.log("\nNo supported harnesses found. Install one of: pi, Claude Code, Codex, OpenCode");
221
+ process.exit(1);
222
+ }
223
+ console.log();
224
+ for (const h of detected) {
225
+ console.log(`Configuring ${h.id}...`);
226
+ h.configure();
227
+ }
228
+ const plural = detected.length === 1 ? "" : "es";
229
+ console.log(`\n✓ Done! ${String(detected.length)} harness${plural} configured.`);
230
+ console.log("\nBridges run via: npx agent-comms bridge <id>");
231
+ }
232
+ function status() {
233
+ console.log("🔌 Agent Comms status\n");
234
+ for (const h of harnesses) {
235
+ const installed = h.detect();
236
+ const result = installed
237
+ ? h.check()
238
+ : { configured: false, details: Array() };
239
+ if (!installed) {
240
+ console.log(` ✗ ${h.id} — not found`);
241
+ }
242
+ else if (result.configured) {
243
+ console.log(` ✓ ${h.id} — configured`);
244
+ for (const detail of result.details)
245
+ console.log(` ${detail}`);
246
+ }
247
+ else {
248
+ console.log(` ⚠ ${h.id} — detected but not configured`);
249
+ for (const detail of result.details)
250
+ console.log(` ${detail}`);
251
+ }
252
+ }
253
+ }
254
+ function remove() {
255
+ console.log("🔌 Agent Comms removal\n");
256
+ for (const h of harnesses) {
257
+ if (h.detect()) {
258
+ console.log(`Removing ${h.id}...`);
259
+ h.remove();
260
+ }
261
+ }
262
+ console.log("\n✓ Done!");
263
+ }
264
+ // ---------------------------------------------------------------------------
265
+ // pi
266
+ // ---------------------------------------------------------------------------
267
+ function configurePi() {
268
+ const settingsPath = path.join(HOME, ".pi", "agent", "settings.json");
269
+ const parsed = readJsonFile(settingsPath);
270
+ const settings = PiSettingsSchema.parse(parsed ?? {});
271
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", "pi");
272
+ const extensions = settings.extensions ?? [];
273
+ if (!extensions.includes(bridgeDir)) {
274
+ extensions.push(bridgeDir);
275
+ settings.extensions = extensions;
276
+ writeJsonFile(settingsPath, settings);
277
+ }
278
+ else {
279
+ console.log(` → Already in ${settingsPath}`);
280
+ }
281
+ }
282
+ function removePi() {
283
+ const settingsPath = path.join(HOME, ".pi", "agent", "settings.json");
284
+ const parsed = readJsonFile(settingsPath);
285
+ const settings = PiSettingsSchema.parse(parsed ?? {});
286
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", "pi");
287
+ const extensions = settings.extensions;
288
+ if (extensions) {
289
+ settings.extensions = extensions.filter((e) => e !== bridgeDir);
290
+ writeJsonFile(settingsPath, settings);
291
+ }
292
+ }
293
+ function checkPi() {
294
+ const settingsPath = path.join(HOME, ".pi", "agent", "settings.json");
295
+ const parsed = readJsonFile(settingsPath);
296
+ const settings = PiSettingsSchema.parse(parsed ?? {});
297
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", "pi");
298
+ const configured = (settings.extensions ?? []).includes(bridgeDir);
299
+ return {
300
+ configured,
301
+ details: configured
302
+ ? [`Extension: ${bridgeDir}`]
303
+ : [`Not in ${settingsPath} extensions`],
304
+ };
305
+ }
306
+ // ---------------------------------------------------------------------------
307
+ // Claude Code
308
+ // ---------------------------------------------------------------------------
309
+ function configureClaudeCode() {
310
+ const mcpPath = path.join(process.cwd(), ".mcp.json");
311
+ const parsed = readJsonFile(mcpPath);
312
+ const config = McpServersSchema.parse(parsed ?? {});
313
+ config.mcpServers["agent-comms"] = {
314
+ command: "npx",
315
+ args: ["agent-comms", "bridge", "claude-code"],
316
+ };
317
+ writeJsonFile(mcpPath, config);
318
+ }
319
+ function removeClaudeCode() {
320
+ const mcpPath = path.join(process.cwd(), ".mcp.json");
321
+ const parsed = readJsonFile(mcpPath);
322
+ if (parsed === undefined)
323
+ return;
324
+ const config = McpServersSchema.safeParse(parsed);
325
+ if (!config.success)
326
+ return;
327
+ if ("agent-comms" in config.data.mcpServers) {
328
+ delete config.data.mcpServers["agent-comms"];
329
+ writeJsonFile(mcpPath, config.data);
330
+ console.log(` Removed agent-comms from ${mcpPath}`);
331
+ }
332
+ }
333
+ function checkClaudeCode() {
334
+ const mcpPath = path.join(process.cwd(), ".mcp.json");
335
+ const parsed = readJsonFile(mcpPath);
336
+ const config = McpServersSchema.safeParse(parsed ?? {});
337
+ const entry = config.success
338
+ ? config.data.mcpServers["agent-comms"]
339
+ : undefined;
340
+ const configured = entry !== undefined;
341
+ return {
342
+ configured,
343
+ details: configured
344
+ ? [`Config: ${mcpPath}`, `Command: ${entry.command}`]
345
+ : [`Not in ${mcpPath}`],
346
+ };
347
+ }
348
+ // ---------------------------------------------------------------------------
349
+ // Codex
350
+ // ---------------------------------------------------------------------------
351
+ function configureCodex() {
352
+ const configDir = path.join(HOME, ".codex");
353
+ // Add MCP server to config.toml
354
+ const tomlPath = path.join(configDir, "config.toml");
355
+ if (fs.existsSync(tomlPath)) {
356
+ const content = fs.readFileSync(tomlPath, "utf-8");
357
+ if (!content.includes("agent-comms")) {
358
+ const block = [
359
+ "",
360
+ "# Agent Comms MCP tool server",
361
+ "[mcp_servers.agent-comms]",
362
+ 'command = "npx"',
363
+ 'args = ["agent-comms", "bridge", "codex"]',
364
+ ].join("\n");
365
+ fs.writeFileSync(tomlPath, content.trimEnd() + block + "\n");
366
+ console.log(` → Appended MCP server to ${tomlPath}`);
367
+ }
368
+ else {
369
+ console.log(` → Already in ${tomlPath}`);
370
+ }
371
+ }
372
+ else {
373
+ const content = [
374
+ "# Agent Comms MCP tool server",
375
+ "[mcp_servers.agent-comms]",
376
+ 'command = "npx"',
377
+ 'args = ["agent-comms", "bridge", "codex"]',
378
+ ].join("\n");
379
+ fs.mkdirSync(configDir, { recursive: true });
380
+ fs.writeFileSync(tomlPath, content + "\n");
381
+ console.log(` → Created ${tomlPath}`);
382
+ }
383
+ // Add Stop hook for delivery push
384
+ const hooksPath = path.join(configDir, "hooks.json");
385
+ const parsed = readJsonFile(hooksPath);
386
+ const hooks = HooksSchema.parse(parsed ?? {});
387
+ const hookEntry = {
388
+ type: "command",
389
+ command: `npx agent-comms bridge codex-stop`,
390
+ timeout: 10,
391
+ };
392
+ const stopHooks = hooks.hooks?.Stop ?? [{ hooks: [] }];
393
+ const firstHook = stopHooks[0];
394
+ if (firstHook === undefined)
395
+ return;
396
+ const innerHooks = firstHook.hooks ?? [];
397
+ const alreadyHasHook = innerHooks.some((h) => h.command.includes("agent-comms"));
398
+ if (!alreadyHasHook) {
399
+ innerHooks.push(hookEntry);
400
+ firstHook.hooks = innerHooks;
401
+ hooks.hooks = { Stop: stopHooks };
402
+ writeJsonFile(hooksPath, hooks);
403
+ }
404
+ else {
405
+ console.log(` → Hook already in ${hooksPath}`);
406
+ }
407
+ }
408
+ function removeCodex() {
409
+ const configDir = path.join(HOME, ".codex");
410
+ // Remove from config.toml
411
+ const tomlPath = path.join(configDir, "config.toml");
412
+ if (fs.existsSync(tomlPath)) {
413
+ const content = fs.readFileSync(tomlPath, "utf-8");
414
+ const lines = content
415
+ .split("\n")
416
+ .filter((line) => !line.includes("agent-comms") && !line.includes("Agent Comms"));
417
+ fs.writeFileSync(tomlPath, lines.join("\n").trimEnd() + "\n");
418
+ console.log(` Removed agent-comms from ${tomlPath}`);
419
+ }
420
+ // Remove from hooks.json
421
+ const hooksPath = path.join(configDir, "hooks.json");
422
+ const parsed = readJsonFile(hooksPath);
423
+ if (parsed === undefined)
424
+ return;
425
+ const hooks = HooksSchema.safeParse(parsed);
426
+ if (!hooks.success)
427
+ return;
428
+ const stopHooks = hooks.data.hooks?.Stop;
429
+ if (stopHooks?.[0]?.hooks) {
430
+ stopHooks[0].hooks = stopHooks[0].hooks.filter((h) => !h.command.includes("agent-comms"));
431
+ writeJsonFile(hooksPath, hooks.data);
432
+ console.log(` Removed agent-comms hook from ${hooksPath}`);
433
+ }
434
+ }
435
+ function checkCodex() {
436
+ const details = [];
437
+ let tomlOk = false;
438
+ let hookOk = false;
439
+ const tomlPath = path.join(HOME, ".codex", "config.toml");
440
+ if (fs.existsSync(tomlPath)) {
441
+ const content = fs.readFileSync(tomlPath, "utf-8");
442
+ tomlOk = content.includes("agent-comms");
443
+ details.push(tomlOk ? `MCP server in ${tomlPath}` : `Not in ${tomlPath}`);
444
+ }
445
+ else {
446
+ details.push(`${tomlPath} not found`);
447
+ }
448
+ const hooksPath = path.join(HOME, ".codex", "hooks.json");
449
+ const parsed = readJsonFile(hooksPath);
450
+ const hooks = HooksSchema.safeParse(parsed ?? {});
451
+ if (hooks.success && hooks.data.hooks?.Stop?.[0]?.hooks) {
452
+ hookOk = hooks.data.hooks.Stop[0].hooks.some((h) => h.command.includes("agent-comms"));
453
+ details.push(hookOk ? `Stop hook in ${hooksPath}` : `No hook in ${hooksPath}`);
454
+ }
455
+ else {
456
+ details.push(`${hooksPath} not configured`);
457
+ }
458
+ return { configured: tomlOk && hookOk, details };
459
+ }
460
+ // ---------------------------------------------------------------------------
461
+ // OpenCode
462
+ // ---------------------------------------------------------------------------
463
+ function configureOpenCode() {
464
+ const configPath = path.join(process.cwd(), "opencode.json");
465
+ const parsed = readJsonFile(configPath);
466
+ const config = OpenCodeConfigSchema.parse(parsed ?? {});
467
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", "opencode", "plugin.ts");
468
+ const plugins = config.plugin ?? [];
469
+ if (!plugins.includes(bridgeDir)) {
470
+ plugins.push(bridgeDir);
471
+ config.plugin = plugins;
472
+ writeJsonFile(configPath, config);
473
+ }
474
+ else {
475
+ console.log(` → Already in ${configPath}`);
476
+ }
477
+ }
478
+ function removeOpenCode() {
479
+ const configPath = path.join(process.cwd(), "opencode.json");
480
+ const parsed = readJsonFile(configPath);
481
+ if (parsed === undefined)
482
+ return;
483
+ const config = OpenCodeConfigSchema.safeParse(parsed);
484
+ if (!config.success)
485
+ return;
486
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", "opencode", "plugin.ts");
487
+ const plugins = config.data.plugin;
488
+ if (plugins) {
489
+ config.data.plugin = plugins.filter((p) => p !== bridgeDir);
490
+ writeJsonFile(configPath, config.data);
491
+ console.log(` Removed agent-comms from ${configPath}`);
492
+ }
493
+ }
494
+ function checkOpenCode() {
495
+ const configPath = path.join(process.cwd(), "opencode.json");
496
+ const parsed = readJsonFile(configPath);
497
+ const config = OpenCodeConfigSchema.safeParse(parsed ?? {});
498
+ const bridgeDir = path.join(PKG_DIR, "src", "bridges", "opencode", "plugin.ts");
499
+ const configured = config.success && (config.data.plugin ?? []).includes(bridgeDir);
500
+ return {
501
+ configured,
502
+ details: configured ? [`Plugin: ${bridgeDir}`] : [`Not in ${configPath}`],
503
+ };
504
+ }
505
+ //# sourceMappingURL=cli.js.map