claude-clew-bus
README.md
# claude-clew-bus
Let **Claude Code** and **clew** talk and command each other — two-way. Two
transports in one repo: a simple **MCP message bus** (pull), and a **native
peer adapter** that joins clew's own `/peer` mesh (push, with auto-reply).
```
┌───────────────────────────────┐
│ THE SAME MACHINE │
└───────────────────────────────┘
┌─────────────┐ ┌─────────────┐
│ Claude Code │ │ clew │
│ (this CLI) │ │ /peer … │
└──────┬──────┘ └──────┬──────┘
│ MCP tools │ native peer
│ peer_send / peer_exec / peer_inbox │ /peer-msg,
▼ │ /peer-exec (token)
┌────────────────────────── peer-adapter.mjs ──────────────────────────┐
│ │
│ MCP server :7334/mcp ◄──drives──► peer HTTP server (advertised │
│ as "claude" in ~/.clew/peers) │
│ │
│ SSE receiver ◄── /peer-events ── clew pushes chat/todo live │
│ │
│ AUTO_REPLY=1: clew pushes a task ─► worker (clew -p) does it ─► reply│
└────────────────────────────────────────────────────────────────────────┘
── OR, the simpler pull-based bus ──
┌──────── server.mjs (MCP streamable-http, :7333/mcp) ────────┐
│ tools: send(to,text) · poll(inbox) · peek · history │
└──────────────────────────────────────────────────────────────┘
▲ Claude Code ▲ clew
│ send("clew") / poll("claude") │ send("claude") / poll("clew")
```
| Mode | File | Push? | Native to clew? | Best when |
|------|------|-------|-----------------|-----------|
| **Native peer** | `peer-adapter.mjs` | ✅ (SSE + auto-reply) | ✅ `/peer discover` sees "claude" | real agent↔agent, run commands, auto-reply |
| MCP bus | `server.mjs` + `bridge.mjs` | ⚠️ needs poll/bridge | ❌ generic MCP | quick message passing, no clew peer session |
## Run
```bash
npm install
npm start # listens on http://127.0.0.1:7333/mcp
# override: BUS_PORT=8000 BUS_HOST=0.0.0.0 npm start
```
Health check: `curl http://127.0.0.1:7333/health`
## Connect Claude Code
Add to `.mcp.json` (or `~/.claude.json`):
```json
{
"mcpServers": {
"clew-bus": { "type": "http", "url": "http://127.0.0.1:7333/mcp" }
}
}
```
## Connect clew
```bash
clew mcp add --transport http clew-bus http://127.0.0.1:7333/mcp
```
## Tools
| Tool | Args | Purpose |
|------|------|---------|
| `send` | `to`, `text`, `from?` | Put a message in another agent's inbox |
| `poll` | `inbox`, `max?` | Drain unread messages addressed to you (marks read) |
| `peek` | `inbox` | View unread without draining |
| `history` | `inbox` | Full log for an inbox |
## Conversation pattern
1. Claude Code: `send(to:"clew", text:"build the thing")`
2. clew: `poll(inbox:"clew")` → sees it → does work → `send(to:"claude", text:"done, output: …")`
3. Claude Code: `poll(inbox:"claude")` → sees the reply → continues
Messages persist to `bus/<inbox>.jsonl` so nothing is lost across restarts.
---
## Native peer mode (Claude ↔ clew via clew's own `/peer` mesh)
The MCP bus above is **pull-based** — clew won't see a message until something
polls. `peer-adapter.mjs` instead joins clew's **native peer mesh**, where
messages are **pushed** straight into clew's REPL (like two clew instances).
How it works (from `src/peer/PeerDiscovery.ts` + `PeerServer.ts`): same-machine
discovery is **file-based** — each peer writes `~/.clew/peers/<pid>.json`
(`{id,hostname,ip,port,token,…}`) and scans that dir to find others and learn
their auth token. Protected calls carry that token:
`POST /peer-msg` (chat), `POST /peer-exec` (task), `POST /peer-todo`.
The adapter writes its own peer file (so clew's `/peer discover` sees `claude`),
serves those endpoints, and reads clew's peer file to reach clew.
The daemon runs three things in one process, sharing one inbox:
1. **Peer HTTP server** — serves `/peer-info` `/peer-msg` `/peer-exec` `/peer-todo` so clew reaches us.
2. **MCP server** (`:7334/mcp`) — tools `peer_list` `peer_send` `peer_exec` `peer_todo` `peer_inbox` so **Claude drives the mesh via tools, not bash**.
3. **SSE receiver** — subscribes to each clew's `/peer-events` and pushes incoming events into the inbox live.
```bash
node peer-adapter.mjs # daemon (peer server + MCP + SSE)
node peer-adapter.mjs list # discovered clew peers
node peer-adapter.mjs send [--to name] "hi clew" # chat TO clew
node peer-adapter.mjs exec [--to name] "git branch" # run a command ON clew
```
### Claude connects via MCP
Add to `.mcp.json`:
```json
{ "mcpServers": { "clew-peer": { "type": "http", "url": "http://127.0.0.1:7334/mcp" } } }
```
Then call `peer_list`, `peer_send`, `peer_exec`, `peer_inbox` as native tools.
### Auto-reply — clew pushes a task, Claude's side does it, replies automatically
```bash
AUTO_REPLY=1 node peer-adapter.mjs
```
A clew peer sends a chat message **prefixed with the trigger** (`@claude` by
default). The adapter strips the trigger, runs a **worker** (clew headless `-p`,
full tools) on the rest, and sends the result back to the sender via `/peer-msg`.
```
clew: /peer send claude "@claude summarize the last commit"
│
▼ trigger matched → strip "@claude" → spawn worker → answer
└─────────────────► /peer-msg back to clew: "↩ <answer>"
```
**Loop safety** — auto-reply only fires when:
- the message starts with the trigger (`@claude`); ordinary chatter is ignored, and
- our own replies (prefixed `↩`) are never re-answered, and
- the same sender isn't replied to more than once per cooldown window.
So two agents can chat freely without an infinite ping-pong.
Config:
| env | default | meaning |
|-----|---------|---------|
| `AUTO_REPLY` | off | `1` to enable |
| `AUTO_REPLY_TRIGGER` | `@claude` | required message prefix; set `''` to reply to everything (cooldown still applies) |
| `AUTO_REPLY_COOLDOWN_MS` | `8000` | min gap between replies to the same sender |
| `WORKER_DIR` | `D:/Projects/Github/clew-code` | cwd the worker runs in |
| `WORKER_ENTRY` | `dist/main.js` | worker entrypoint (run with `bun`) |
| `WORKER_TOOLS` | `Bash,Read,Glob,Grep` | tools the worker may use |
> The default target for `send`/`exec`/auto-reply is the **most recently active**
> clew (peer files sorted by mtime), so a stale file from a closed session is
> never picked. Use `--to <name|port>` to target a specific peer.
### Completing the loop (in the clew session)
```
/peer share # clew starts advertising + peer server
/peer discover # clew sees "claude" (the adapter)
/peer send claude "hello" # → lands in peer-inbox.jsonl
/peer swarm claude <cmd> # → adapter runs cmd, returns result
```
> `CLEW_MESH_LAN=1` is only needed for cross-machine UDP discovery. On one
> machine everything works file-based with no env var.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing