@manuelfedele/postino 0.1.0 → 0.2.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 -0
- package/README.md +80 -16
- package/dist/index.js +16 -2
- package/dist/valkey.js +4 -0
- package/package.json +5 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "postino",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Manuel Fedele",
|
|
5
|
+
"url": "https://github.com/manuelfedele"
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
{
|
|
9
|
+
"name": "postino",
|
|
10
|
+
"description": "Inter-agent messaging, broadcasts, and real-time web GUI for Claude Code. Let your agents talk to each other across tabs and processes.",
|
|
11
|
+
"category": "productivity",
|
|
12
|
+
"source": {
|
|
13
|
+
"source": "url",
|
|
14
|
+
"url": "https://github.com/manuelfedele/postino.git"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/manuelfedele/postino"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/README.md
CHANGED
|
@@ -35,11 +35,22 @@
|
|
|
35
35
|
|
|
36
36
|
## Quick Start
|
|
37
37
|
|
|
38
|
+
### As a Claude Code plugin (recommended)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
claude plugin marketplace add manuelfedele/postino
|
|
42
|
+
claude plugin install postino
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or from within Claude Code: `/plugin marketplace add manuelfedele/postino` then `/plugin install postino`.
|
|
46
|
+
|
|
47
|
+
### Via npx
|
|
48
|
+
|
|
38
49
|
```bash
|
|
39
50
|
npx @manuelfedele/postino install
|
|
40
51
|
```
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
Restart Claude Code after either method. Your agent is online.
|
|
43
54
|
|
|
44
55
|
> **Prerequisite:** Valkey or Redis running on `localhost:6379`
|
|
45
56
|
|
|
@@ -122,28 +133,81 @@ Updates in real-time via Server-Sent Events. When an agent sends a message from
|
|
|
122
133
|
|
|
123
134
|
## How It Works
|
|
124
135
|
|
|
136
|
+
### 1-to-1 Messaging
|
|
137
|
+
|
|
138
|
+
Messages work like a queue: send pushes, read pops.
|
|
139
|
+
|
|
140
|
+
```mermaid
|
|
141
|
+
sequenceDiagram
|
|
142
|
+
participant A as Tab 1 (agent-A)
|
|
143
|
+
participant V as Valkey
|
|
144
|
+
participant B as Tab 2 (agent-B)
|
|
145
|
+
|
|
146
|
+
A->>V: msg_send(to=B, "run tests")
|
|
147
|
+
V-->>B: pub/sub notify
|
|
148
|
+
Note over B: hook fires on next prompt
|
|
149
|
+
B->>V: msg_check()
|
|
150
|
+
V-->>B: "1 unread message"
|
|
151
|
+
B->>V: msg_read()
|
|
152
|
+
V-->>B: [{from: A, body: "run tests"}]
|
|
153
|
+
Note over V: message consumed
|
|
125
154
|
```
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
|
|
156
|
+
### Broadcasts
|
|
157
|
+
|
|
158
|
+
Broadcasts are shared. Every agent reads independently via a per-agent cursor.
|
|
159
|
+
|
|
160
|
+
```mermaid
|
|
161
|
+
sequenceDiagram
|
|
162
|
+
participant A as Tab 1 (agent-A)
|
|
163
|
+
participant V as Valkey
|
|
164
|
+
participant B as Tab 2 (agent-B)
|
|
165
|
+
participant C as Tab 3 (agent-C)
|
|
166
|
+
|
|
167
|
+
A->>V: msg_broadcast("deploy freeze")
|
|
168
|
+
V-->>B: SSE event
|
|
169
|
+
V-->>C: SSE event
|
|
170
|
+
B->>V: msg_broadcasts()
|
|
171
|
+
V-->>B: [{from: A, body: "deploy freeze"}]
|
|
172
|
+
Note over V: cursor advanced for B
|
|
173
|
+
C->>V: msg_broadcasts()
|
|
174
|
+
V-->>C: [{from: A, body: "deploy freeze"}]
|
|
175
|
+
Note over V: cursor advanced for C
|
|
176
|
+
Note over V: message still exists (TTL expiry)
|
|
138
177
|
```
|
|
139
178
|
|
|
140
|
-
|
|
179
|
+
### Architecture
|
|
180
|
+
|
|
181
|
+
```mermaid
|
|
182
|
+
graph LR
|
|
183
|
+
subgraph Claude Code
|
|
184
|
+
T1[Tab 1<br/>MCP client] -->|stdio| M1[Postino<br/>MCP server]
|
|
185
|
+
T2[Tab 2<br/>MCP client] -->|stdio| M2[Postino<br/>MCP server]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
M1 -->|ioredis| VK[(Valkey)]
|
|
189
|
+
M2 -->|ioredis| VK
|
|
190
|
+
|
|
191
|
+
M1 -->|Hono :3333| GUI[Web GUI]
|
|
192
|
+
VK -->|pub/sub| GUI
|
|
193
|
+
GUI -->|SSE| Browser
|
|
194
|
+
|
|
195
|
+
H[Hook<br/>check-messages.sh] -->|curl /api/check| M1
|
|
196
|
+
|
|
197
|
+
style VK fill:#e63030,color:#fff,stroke:none
|
|
198
|
+
style GUI fill:#2563eb,color:#fff,stroke:none
|
|
199
|
+
style H fill:#d97706,color:#fff,stroke:none
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Under the Hood
|
|
203
|
+
|
|
204
|
+
**Messages** are Valkey lists (one per inbox). `msg_send` pushes, `msg_read` pops. Unread messages expire after 24h (configurable).
|
|
141
205
|
|
|
142
|
-
**Broadcasts** are a shared Valkey list. Each agent tracks a cursor (last-seen index). Reading
|
|
206
|
+
**Broadcasts** are a shared Valkey list. Each agent tracks a cursor (last-seen index). Reading advances the cursor without deleting, so every agent sees every broadcast.
|
|
143
207
|
|
|
144
208
|
**Agent presence** uses Valkey keys with a 30-second TTL, refreshed by a heartbeat. If a process dies, it goes offline within 30 seconds.
|
|
145
209
|
|
|
146
|
-
**The hook** (`UserPromptSubmit`) calls `GET /api/check/:agent` via curl.
|
|
210
|
+
**The hook** (`UserPromptSubmit`) calls `GET /api/check/:agent` via curl. Zero output when there's nothing new (zero token cost). One-line hint when messages arrive.
|
|
147
211
|
|
|
148
212
|
---
|
|
149
213
|
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,21 @@ const server = new McpServer({
|
|
|
16
16
|
});
|
|
17
17
|
registerMessagingTools(server, config.agentName);
|
|
18
18
|
async function main() {
|
|
19
|
-
|
|
19
|
+
try {
|
|
20
|
+
await connect();
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const url = config.valkeyUrl;
|
|
24
|
+
process.stderr.write(`\n postino: cannot connect to Valkey/Redis at ${url}\n\n`);
|
|
25
|
+
process.stderr.write(` Postino requires Valkey or Redis. Start one with:\n\n`);
|
|
26
|
+
process.stderr.write(` docker run -d --name valkey -p 6379:6379 valkey/valkey:8\n\n`);
|
|
27
|
+
process.stderr.write(` Or install natively:\n\n`);
|
|
28
|
+
process.stderr.write(` macOS: brew install valkey && brew services start valkey\n`);
|
|
29
|
+
process.stderr.write(` Linux: apt install valkey-server\n\n`);
|
|
30
|
+
process.stderr.write(` To use a different host/port:\n\n`);
|
|
31
|
+
process.stderr.write(` POSTINO_VALKEY_URL=redis://host:port\n\n`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
20
34
|
await registerAgent(config.agentName);
|
|
21
35
|
const transport = new StdioServerTransport();
|
|
22
36
|
await server.connect(transport);
|
|
@@ -33,6 +47,6 @@ async function main() {
|
|
|
33
47
|
process.on("SIGTERM", shutdown);
|
|
34
48
|
}
|
|
35
49
|
main().catch((err) => {
|
|
36
|
-
|
|
50
|
+
process.stderr.write(`postino: ${err.message || err}\n`);
|
|
37
51
|
process.exit(1);
|
|
38
52
|
});
|
package/dist/valkey.js
CHANGED
|
@@ -5,10 +5,12 @@ export const valkey = new Redis(config.valkeyUrl, {
|
|
|
5
5
|
lazyConnect: true,
|
|
6
6
|
maxRetriesPerRequest: 3,
|
|
7
7
|
});
|
|
8
|
+
valkey.on("error", () => { }); // Handled in connect()
|
|
8
9
|
export const valkeySub = new Redis(config.valkeyUrl, {
|
|
9
10
|
lazyConnect: true,
|
|
10
11
|
maxRetriesPerRequest: 3,
|
|
11
12
|
});
|
|
13
|
+
valkeySub.on("error", () => { }); // Handled in connect()
|
|
12
14
|
const prefix = config.keyPrefix;
|
|
13
15
|
export const keys = {
|
|
14
16
|
inbox: (agent) => `${prefix}inbox:${agent}`,
|
|
@@ -21,6 +23,8 @@ export const keys = {
|
|
|
21
23
|
};
|
|
22
24
|
export async function connect() {
|
|
23
25
|
await valkey.connect();
|
|
26
|
+
// Verify the connection actually works
|
|
27
|
+
await valkey.ping();
|
|
24
28
|
await valkeySub.connect();
|
|
25
29
|
}
|
|
26
30
|
export async function disconnect() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manuelfedele/postino",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Inter-agent messaging and broadcast system for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"multi-agent"
|
|
31
31
|
],
|
|
32
32
|
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/manuelfedele/postino"
|
|
36
|
+
},
|
|
33
37
|
"engines": {
|
|
34
38
|
"node": ">=18.0.0"
|
|
35
39
|
},
|