agent-comms 1.21.2 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +19 -19
- package/.claude-plugin/plugin.json +2 -1
- package/README.md +2 -2
- package/commands/agents.md +5 -0
- package/commands/dm.md +6 -0
- package/commands/hide.md +5 -0
- package/commands/join.md +6 -0
- package/commands/leave.md +6 -0
- package/commands/read.md +6 -0
- package/commands/rooms.md +5 -0
- package/commands/send.md +6 -0
- package/commands/show.md +5 -0
- package/commands/status.md +6 -0
- package/commands/whoami.md +5 -0
- package/dist/bridges/claude-code/channel.d.ts +5 -2
- package/dist/bridges/claude-code/channel.d.ts.map +1 -1
- package/dist/bridges/claude-code/channel.js +136 -10
- package/dist/bridges/claude-code/channel.js.map +1 -1
- package/hooks/drain.sh +65 -0
- package/hooks/hooks.json +39 -0
- package/package.json +4 -2
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
"name": "agent-comms",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "ExaDev"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "Cross-harness communication mesh for LLM agents",
|
|
8
|
+
"homepage": "https://github.com/ExaDev/agent-comms#readme"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "agent-comms",
|
|
13
|
+
"source": {
|
|
14
|
+
"source": "url",
|
|
15
|
+
"url": "https://github.com/ExaDev/agent-comms.git"
|
|
16
|
+
},
|
|
17
|
+
"description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
|
|
18
|
+
"version": "1.23.0"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
21
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-comms",
|
|
3
3
|
"description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.23.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ExaDev"
|
|
7
7
|
},
|
|
8
8
|
"homepage": "https://github.com/ExaDev/agent-comms#readme",
|
|
9
9
|
"repository": "https://github.com/ExaDev/agent-comms",
|
|
10
10
|
"license": "MIT",
|
|
11
|
+
"hooks": "./hooks/hooks.json",
|
|
11
12
|
"mcpServers": {
|
|
12
13
|
"agent-comms": {
|
|
13
14
|
"command": "npx",
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/ExaDev/agent-comms)
|
|
4
4
|
[](https://www.npmjs.com/package/agent-comms)
|
|
5
|
-
[](https://github.com/ExaDev/agent-comms/releases/tag/v1.23.0)
|
|
6
6
|
[](https://github.com/ExaDev/agent-comms/actions)
|
|
7
7
|
|
|
8
8
|
Cross-harness communication mesh for LLM agents: rooms, DMs, presence, and visibility over TCP with zero filesystem dependencies.
|
|
@@ -217,7 +217,7 @@ agent_comms({ action: "update", visibility: "hidden" })
|
|
|
217
217
|
|
|
218
218
|
When `streamingBehavior` is absent, each bridge falls back to its existing heuristic: actionable events (DMs, room messages, invites) are treated as `steer`; status changes and membership events are treated as `info`.
|
|
219
219
|
|
|
220
|
-
**
|
|
220
|
+
**Claude Code delivery mechanism**: Events are written to `~/.agents/bus/pending/claude-code--<cwd-slug>.jsonl`. Three Claude Code hooks (`PostToolUse`, `Stop`, `UserPromptSubmit`) invoke `hooks/drain.sh`, which atomically renames the file, writes its content to stderr, and exits 2. Claude Code's `asyncRewake` mechanism wraps the stderr in a `<system-reminder>` and wakes idle Claude. When the `agent_comms` tool is called directly, the tool handler drains the same file via the same atomic rename — concurrent drains never duplicate because rename is the synchronisation primitive. The `[STEER]` and `[FOLLOWUP]` markers and `meta.streamingBehavior` carry timing intent; acting on them is down to the receiving agent. The pi bridge honours the hint natively via `deliverAs`.
|
|
221
221
|
|
|
222
222
|
## Room types
|
|
223
223
|
|
package/commands/dm.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Send a direct message to an agent
|
|
3
|
+
argument-hint: <agent-id-or-name> <message>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Parse `$ARGUMENTS` as `<target> <message>` — first whitespace-separated token is the target agent id or name; everything after is the message body. Call the `agent_comms` MCP tool with `action: "dm"`, `target: <target>`, `content: <message>`, and `streamingBehavior: "steer"`. Confirm with a single line containing the returned message id. If the target is unknown, surface the error verbatim.
|
package/commands/hide.md
ADDED
package/commands/join.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Join a room
|
|
3
|
+
argument-hint: <room-name>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Call the `agent_comms` MCP tool with `action: "join_room"` and `room: "$ARGUMENTS"`. Confirm with a single line — room name and current member count. If the room doesn't exist, surface the error verbatim.
|
package/commands/read.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Read recent messages from a room
|
|
3
|
+
argument-hint: <room>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Call the `agent_comms` MCP tool with `action: "read_room"` and `room: "$ARGUMENTS"`. Present each message on its own line in chronological order, using the format `[HH:MM] <sender>: <content>`. If `<sender>` is a long fingerprint, truncate to the last 8 characters. No commentary.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: List all rooms in the mesh
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Call the `agent_comms` MCP tool with `action: "list_rooms"` and present the result as a compact list — one room per line — showing the name, type, and member count, with a `[✓]` marker for rooms this session has joined. Don't add commentary.
|
package/commands/send.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Broadcast a message to a room
|
|
3
|
+
argument-hint: <room> <message>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Parse `$ARGUMENTS` as `<room> <message>` — first whitespace-separated token is the room name; everything after is the message body. Call the `agent_comms` MCP tool with `action: "send"`, `target: <room>`, `content: <message>`, and `streamingBehavior: "info"`. Confirm with a single line containing the returned message id. If the room isn't joined, surface the error verbatim.
|
package/commands/show.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Set this session's presence status
|
|
3
|
+
argument-hint: <active|idle|busy>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Validate that `$ARGUMENTS` is one of `active`, `idle`, or `busy`. If invalid, print an error and stop. Otherwise call the `agent_comms` MCP tool with `action: "update"` and `status: "$ARGUMENTS"`. Confirm with a single line: the new status.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show this session's agent identity, status, and room memberships
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Call the `agent_comms` MCP tool with `action: "whoami"` and present the result concisely: agent ID (short form), name, current status, visibility, and the list of joined rooms. No commentary.
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* Agent Comms — Claude Code channel bridge.
|
|
3
3
|
*
|
|
4
4
|
* MCP channel server that provides the "agent_comms" tool and pushes
|
|
5
|
-
* incoming messages into Claude's context via <channel> events.
|
|
5
|
+
* incoming messages into Claude's context via <channel> events and hooks.
|
|
6
6
|
* Uses TCP mesh for real-time delivery — no filesystem polling.
|
|
7
7
|
*
|
|
8
8
|
* Run via: npx agent-comms bridge claude-code
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
|
+
* Pending events are persisted to ~/.agents/bus/pending/claude-code--<slug>.jsonl
|
|
11
|
+
* so the asyncRewake hook scripts (hooks/drain.sh) can drain them out-of-process
|
|
12
|
+
* and wake idle Claude via exit code 2.
|
|
10
13
|
*/
|
|
11
14
|
export declare function run(): Promise<void>;
|
|
12
15
|
//# sourceMappingURL=channel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA4IH,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAwHzC"}
|
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
* Agent Comms — Claude Code channel bridge.
|
|
3
3
|
*
|
|
4
4
|
* MCP channel server that provides the "agent_comms" tool and pushes
|
|
5
|
-
* incoming messages into Claude's context via <channel> events.
|
|
5
|
+
* incoming messages into Claude's context via <channel> events and hooks.
|
|
6
6
|
* Uses TCP mesh for real-time delivery — no filesystem polling.
|
|
7
7
|
*
|
|
8
8
|
* Run via: npx agent-comms bridge claude-code
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
|
+
* Pending events are persisted to ~/.agents/bus/pending/claude-code--<slug>.jsonl
|
|
11
|
+
* so the asyncRewake hook scripts (hooks/drain.sh) can drain them out-of-process
|
|
12
|
+
* and wake idle Claude via exit code 2.
|
|
10
13
|
*/
|
|
14
|
+
import { execFileSync } from "node:child_process";
|
|
15
|
+
import * as fs from "node:fs";
|
|
16
|
+
import * as os from "node:os";
|
|
17
|
+
import * as path from "node:path";
|
|
11
18
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
19
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
20
|
import { MeshStore, CommsTool, buildAction, ensureRegistered, extractStreamingBehavior, formatDeliveryEvent, isActionableEvent, MCP_TOOL_PARAMS, } from "../../core/index.js";
|
|
@@ -18,6 +25,120 @@ import { nanoid } from "../../core/nanoid.js";
|
|
|
18
25
|
function isRecord(value) {
|
|
19
26
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
27
|
}
|
|
28
|
+
function isErrnoException(err) {
|
|
29
|
+
return typeof err === "object" && err !== null && "code" in err;
|
|
30
|
+
}
|
|
31
|
+
function cwdSlug(cwd) {
|
|
32
|
+
return cwd.replace(/[^a-zA-Z0-9]/g, "_");
|
|
33
|
+
}
|
|
34
|
+
function pendingDir() {
|
|
35
|
+
return path.join(os.homedir(), ".agents", "bus", "pending");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Walk up the process tree to find the Claude Code CLI PID.
|
|
39
|
+
*
|
|
40
|
+
* The bridge runs ~3 hops below claude (tsx wrapper → tsx loader → bridge),
|
|
41
|
+
* so process.ppid alone isn't enough. We walk until we find an ancestor
|
|
42
|
+
* whose command basename is "claude".
|
|
43
|
+
*
|
|
44
|
+
* Two Claude Code instances in the same cwd would otherwise share one
|
|
45
|
+
* pending file and consume each other's messages. Keying by Claude PID
|
|
46
|
+
* isolates them. Returns undefined if no claude ancestor can be located
|
|
47
|
+
* within 10 hops — caller falls back to cwd-only path.
|
|
48
|
+
*/
|
|
49
|
+
function findClaudeCodePid() {
|
|
50
|
+
let pid = process.ppid;
|
|
51
|
+
for (let i = 0; i < 10 && pid > 1; i++) {
|
|
52
|
+
let out;
|
|
53
|
+
try {
|
|
54
|
+
out = execFileSync("ps", ["-o", "ppid=,comm=", "-p", String(pid)], {
|
|
55
|
+
encoding: "utf-8",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const trimmed = out.trim();
|
|
62
|
+
const match = /^(\d+)\s+(.+)$/.exec(trimmed);
|
|
63
|
+
if (!match)
|
|
64
|
+
return undefined;
|
|
65
|
+
const parentPid = Number.parseInt(match[1] ?? "", 10);
|
|
66
|
+
const comm = (match[2] ?? "").trim();
|
|
67
|
+
if (/(^|\/)claude$/.test(comm))
|
|
68
|
+
return pid;
|
|
69
|
+
if (!Number.isFinite(parentPid))
|
|
70
|
+
return undefined;
|
|
71
|
+
pid = parentPid;
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
function pendingFilePath(cwd, claudePid) {
|
|
76
|
+
const slug = cwdSlug(cwd);
|
|
77
|
+
const name = claudePid !== undefined
|
|
78
|
+
? `claude-code--${slug}--${String(claudePid)}.jsonl`
|
|
79
|
+
: `claude-code--${slug}.jsonl`;
|
|
80
|
+
return path.join(pendingDir(), name);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Drop pending files in this cwd whose owning Claude Code PID is no longer
|
|
84
|
+
* alive. Prevents accumulation when sessions exit without graceful shutdown.
|
|
85
|
+
*/
|
|
86
|
+
function cleanupStalePendingFiles(cwd) {
|
|
87
|
+
const dir = pendingDir();
|
|
88
|
+
const slug = cwdSlug(cwd);
|
|
89
|
+
const prefix = `claude-code--${slug}--`;
|
|
90
|
+
let entries;
|
|
91
|
+
try {
|
|
92
|
+
entries = fs.readdirSync(dir);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (isErrnoException(err) && err.code === "ENOENT")
|
|
96
|
+
return;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
if (!entry.startsWith(prefix) || !entry.endsWith(".jsonl"))
|
|
101
|
+
continue;
|
|
102
|
+
const pidStr = entry.slice(prefix.length, -".jsonl".length);
|
|
103
|
+
const pid = Number.parseInt(pidStr, 10);
|
|
104
|
+
if (!Number.isFinite(pid) || pid <= 1)
|
|
105
|
+
continue;
|
|
106
|
+
try {
|
|
107
|
+
process.kill(pid, 0);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
if (isErrnoException(err) && err.code === "ESRCH") {
|
|
111
|
+
try {
|
|
112
|
+
fs.unlinkSync(path.join(dir, entry));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// best-effort cleanup, ignore failures
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function appendPending(filePath, line) {
|
|
122
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
123
|
+
fs.appendFileSync(filePath, line + "\n");
|
|
124
|
+
}
|
|
125
|
+
function drainPending(filePath) {
|
|
126
|
+
const drainPath = `${filePath}.draining-${String(process.pid)}-${String(Date.now())}`;
|
|
127
|
+
try {
|
|
128
|
+
fs.renameSync(filePath, drainPath);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (isErrnoException(err) && err.code === "ENOENT")
|
|
132
|
+
return [];
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
const content = fs.readFileSync(drainPath, "utf-8");
|
|
136
|
+
fs.unlinkSync(drainPath);
|
|
137
|
+
return content
|
|
138
|
+
.split("\n")
|
|
139
|
+
.map((l) => l.trim())
|
|
140
|
+
.filter((l) => l.length > 0);
|
|
141
|
+
}
|
|
21
142
|
export async function run() {
|
|
22
143
|
const identity = generateIdentity();
|
|
23
144
|
const store = new MeshStore();
|
|
@@ -25,21 +146,26 @@ export async function run() {
|
|
|
25
146
|
store.setTransport(new TlsTransport(store.events, identity));
|
|
26
147
|
const tool = new CommsTool(store, store.discovery);
|
|
27
148
|
let agentId;
|
|
28
|
-
const
|
|
149
|
+
const claudeCodePid = findClaudeCodePid();
|
|
150
|
+
if (claudeCodePid === undefined) {
|
|
151
|
+
process.stderr.write("agent-comms bridge: could not locate Claude Code PID; using shared cwd pending file (concurrent sessions in the same cwd will share messages)\n");
|
|
152
|
+
}
|
|
153
|
+
cleanupStalePendingFiles(process.cwd());
|
|
154
|
+
const pendingFile = pendingFilePath(process.cwd(), claudeCodePid);
|
|
29
155
|
const mcp = new McpServer({ name: "agent-comms", version: "0.2.0" }, {
|
|
30
156
|
capabilities: {
|
|
31
157
|
experimental: { "claude/channel": {} },
|
|
32
158
|
},
|
|
33
159
|
});
|
|
34
|
-
// All events
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
160
|
+
// All events are written to the pending file so the out-of-process hook
|
|
161
|
+
// drain script can surface them to idle Claude via asyncRewake exit 2.
|
|
162
|
+
// Actionable events also get an eager channel push for mid-turn delivery
|
|
163
|
+
// when channels are enabled (--dangerously-load-development-channels).
|
|
38
164
|
store.onDelivery = async (_targetId, event) => {
|
|
39
165
|
const line = formatDeliveryEvent(event);
|
|
40
166
|
const hint = extractStreamingBehavior(event);
|
|
41
167
|
const shouldPush = isActionableEvent(event) && hint !== "info";
|
|
42
|
-
|
|
168
|
+
appendPending(pendingFile, line);
|
|
43
169
|
if (shouldPush) {
|
|
44
170
|
await mcp.server.notification({
|
|
45
171
|
method: "notifications/claude/channel",
|
|
@@ -58,7 +184,7 @@ export async function run() {
|
|
|
58
184
|
"Cross-harness agent communication mesh. Actions:",
|
|
59
185
|
"register, update, whoami, create_room, list_rooms, join_room, leave_room,",
|
|
60
186
|
"send, dm, list_agents, read_room, invite, decline_invite, kick, destroy_room.",
|
|
61
|
-
'Incoming messages appear as <channel source="agent-comms"> events.',
|
|
187
|
+
'Incoming messages appear as <channel source="agent-comms"> events or [comms] Pending prefix.',
|
|
62
188
|
"Use streamingBehavior on send/dm: steer (act now), followUp (act when idle), info (whenever, default).",
|
|
63
189
|
].join(" "),
|
|
64
190
|
inputSchema: MCP_TOOL_PARAMS,
|
|
@@ -84,7 +210,7 @@ export async function run() {
|
|
|
84
210
|
cwd: process.cwd(),
|
|
85
211
|
pid: process.pid,
|
|
86
212
|
}, action);
|
|
87
|
-
const pending =
|
|
213
|
+
const pending = drainPending(pendingFile);
|
|
88
214
|
const prefix = pending.length > 0
|
|
89
215
|
? `[comms] Pending:\n${pending.map((l) => ` 📬 ${l}`).join("\n")}\n\n`
|
|
90
216
|
: "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"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"}
|
package/hooks/drain.sh
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Drain pending agent-comms events and exit 2 to wake idle Claude via asyncRewake.
|
|
3
|
+
#
|
|
4
|
+
# Called by Claude Code hooks (PostToolUse, Stop). If there are pending events,
|
|
5
|
+
# writes them to stderr and exits 2 — Claude Code wraps the stderr in a
|
|
6
|
+
# <system-reminder> and enqueues it as a task-notification, waking idle Claude.
|
|
7
|
+
#
|
|
8
|
+
# Uses atomic rename to drain so concurrent drains (in-process tool drain vs
|
|
9
|
+
# this out-of-process hook) never duplicate or lose events.
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SLUG="${PWD//[^a-zA-Z0-9]/_}"
|
|
14
|
+
|
|
15
|
+
# Walk up the process tree to find the Claude Code PID. Two Claude Code
|
|
16
|
+
# instances in the same cwd would otherwise share one pending file and consume
|
|
17
|
+
# each other's messages. Each Claude Code session has a unique PID; the bridge
|
|
18
|
+
# discovers and uses the same value, so each session gets its own file.
|
|
19
|
+
find_claude_pid() {
|
|
20
|
+
local pid=$PPID
|
|
21
|
+
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
22
|
+
[ "$pid" -le 1 ] && return 1
|
|
23
|
+
local info ppid comm
|
|
24
|
+
info=$(ps -p "$pid" -o ppid=,comm= 2>/dev/null) || return 1
|
|
25
|
+
info=${info#"${info%%[![:space:]]*}"}
|
|
26
|
+
[ -z "$info" ] && return 1
|
|
27
|
+
ppid=${info%%[[:space:]]*}
|
|
28
|
+
comm=${info#*[[:space:]]}
|
|
29
|
+
comm=${comm#"${comm%%[![:space:]]*}"}
|
|
30
|
+
case "$comm" in
|
|
31
|
+
*/claude|claude) echo "$pid"; return 0 ;;
|
|
32
|
+
esac
|
|
33
|
+
pid=$ppid
|
|
34
|
+
done
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
CLAUDE_PID=$(find_claude_pid 2>/dev/null || true)
|
|
39
|
+
if [ -n "$CLAUDE_PID" ]; then
|
|
40
|
+
PENDING="$HOME/.agents/bus/pending/claude-code--${SLUG}--${CLAUDE_PID}.jsonl"
|
|
41
|
+
else
|
|
42
|
+
echo "agent-comms drain.sh: could not locate Claude Code PID, falling back to shared cwd file" >&2
|
|
43
|
+
PENDING="$HOME/.agents/bus/pending/claude-code--${SLUG}.jsonl"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
DRAINING="${PENDING}.draining-$$-$(date +%s%N 2>/dev/null || date +%s)"
|
|
47
|
+
|
|
48
|
+
# Atomic drain — if rename fails (file absent), nothing to surface.
|
|
49
|
+
if ! mv "$PENDING" "$DRAINING" 2>/dev/null; then
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
CONTENT=$(cat "$DRAINING")
|
|
54
|
+
rm -f "$DRAINING"
|
|
55
|
+
|
|
56
|
+
if [ -z "$CONTENT" ]; then
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
echo "Pending agent-comms messages:" >&2
|
|
61
|
+
while IFS= read -r line; do
|
|
62
|
+
[ -n "$line" ] && echo " 📬 $line" >&2
|
|
63
|
+
done <<< "$CONTENT"
|
|
64
|
+
|
|
65
|
+
exit 2
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"PostToolUse": [
|
|
3
|
+
{
|
|
4
|
+
"matcher": "*",
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/drain.sh",
|
|
9
|
+
"timeout": 5
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"Stop": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "*",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/drain.sh",
|
|
21
|
+
"timeout": 5
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"UserPromptSubmit": [
|
|
27
|
+
{
|
|
28
|
+
"matcher": "*",
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/drain.sh",
|
|
33
|
+
"asyncRewake": true,
|
|
34
|
+
"timeout": 5
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-comms",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Cross-harness communication mesh for LLM agents — rooms, DMs, presence, and real-time push delivery over TCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.33.0",
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"dist",
|
|
37
|
-
".claude-plugin/"
|
|
37
|
+
".claude-plugin/",
|
|
38
|
+
"commands/",
|
|
39
|
+
"hooks/"
|
|
38
40
|
],
|
|
39
41
|
"bin": {
|
|
40
42
|
"agent-comms": "dist/cli.js"
|