Claude bridge
Use when orchestrating multiple Claude Code chats, reading other chats' history, sending messages between chats, or searching across chat sessions. Triggers on phrases like "peer chat", "sister chat", "other chat", "cross-chat", "what is the other agent doing", "find in chats", "manager agent", "worker agent", "agent-to-agent communication", and similar multi-chat coordination requests.From its SKILL.md
npx -y skills add michalekz/claude-bridge --skill claude-bridgeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
10.8 KB, as published. Nobody here has run it
claude-bridge — Multi-chat orchestration
The claude-bridge plugin gives you twelve MCP tools to coordinate with other Claude Code chats running on the same machine: see them, message them, read their transcripts, search across them, monitor context usage, and protect peers from autocompact. All file-based, no servers, no API keys. Tools are exposed as mcp__plugin_claude-bridge_claude-bridge__<tool>.
For role-specific playbooks see the bundled role skills:
claude-bridge-role-manager— orchestrator of 2-N worker peersclaude-bridge-role-memory-keeper— single-writer for shared agent memory
Decision tree — which tool to reach for
I want to…
├─ see what other chats are alive → peer_list
├─ message another chat:
│ ├─ initiate → peer_ask
│ └─ respond to incoming message → peer_reply (with inReplyTo=<msgId>)
├─ explicitly drain my own inbox → peer_inbox_read (rarely needed — piggyback drains on every tool call)
├─ read another chat's transcript:
│ ├─ last N messages → peer_chat_read { lastN }
│ ├─ what they're doing right now → peer_chat_read { sinceLastUserPrompt: true }
│ ├─ find a specific topic in one chat → peer_chat_read { query, contextLines }
│ └─ read a dead/historical session by id → peer_chat_read { to: <uuid>, crossProject: true }
├─ search across many chats:
│ ├─ within current project → peer_chat_search { query }
│ └─ across all projects → peer_chat_search { query, scope: 'all-projects' }
└─ navigate JSONL history:
├─ list all projects → list_projects
├─ list sessions (with active/title/counts) → list_sessions { includeMeta: true }
└─ inspect one session's event mix → session_stats { sessionId }
Identity — id vs name
Every peer has two identifiers:
id— Claude Code sessionId UUID. Always unique, never collides. Use this when you need certainty.name— display slug derived from ai-title or cwd. May collide if two chats share an ai-title. Ifpeer_ask { to: <name> }returnsambiguous_peer, switch toid.
When in doubt, prefer id.
Common workflows
Workflow 1 — Manager dispatches to workers
You are the manager chat A coordinating workers B and C.
peer_list
// → see workers B, C
peer_ask { "to": "worker-b", "content": "Audit src/auth/* for regressions." }
peer_ask { "to": "worker-c", "content": "Run integration tests and report failures." }
// (workers act asynchronously; their replies arrive via piggyback or push)
peer_inbox_read // optional — pulls explicit if you've been idle
Workers should respond with peer_reply { inReplyTo: <msgId>, content: ... }.
Workflow 2 — Inspect a sister chat without disturbing it
// Just the latest exchange in their chat:
peer_chat_read { "to": "sister-chat", "sinceLastUserPrompt": true }
// Or last 5 messages regardless of structure:
peer_chat_read { "to": "sister-chat", "lastN": 5 }
sinceLastUserPrompt: true is the semantic anchor — it returns the most recent user prompt plus every assistant turn after it. Far better than guessing lastN.
Workflow 3 — Find a specific decision or discussion
// In one specific chat:
peer_chat_read { "to": "expert-chat", "query": "auth migration", "contextLines": 1 }
// Across the current project (don't know which chat):
peer_chat_search { "query": "auth migration", "contextLines": 1 }
// Across every project on this machine:
peer_chat_search { "query": "auth migration", "scope": "all-projects" }
contextLines: N includes ±N neighbor messages around each match — useful for context.
Workflow 4 — Read an archived chat that's no longer running
// Find it first:
list_sessions { "includeMeta": true, "limit": 20 }
// → pick a sessionId from the list (look at aiTitle + userPrompts)
peer_chat_read {
"to": "<sessionId-uuid>",
"crossProject": true,
"lastN": 50
}
crossProject: true lifts the active-peer requirement — works for any JSONL session under ~/.claude/projects/, dead or alive.
Workflow 5 — Survey activity across the machine
list_sessions { "includeMeta": true, "limit": 100 }
// → group by project, sort by userPrompts to find busy chats
Each session returns active, aiTitle, userPrompts, assistantReplies — enough to pick where to dig deeper.
Output formats for peer_chat_read
| Format | Best for |
|---|---|
markdown (default) | Reading transcripts naturally — you or a human |
compact | Skimming many messages (≤180 chars per line, ideal for lastN: 50+) |
json | Programmatic consumption or further filtering |
Switch with format: 'compact' or format: 'json'.
What gets searched, what doesn't
peer_chat_read.queryandpeer_chat_search.querymatch text content only — notthinkingblocks, nottool_useinputs, nottool_resultcontents. That keeps results signal-heavy.- IDE-injected noise (
<ide_opened_file>,<ide_selection>,<system-reminder>) is always stripped before display — never appears in transcripts. - Sessions older than 30 days are excluded from
peer_chat_searchby default. No way to override yet (the 30-day cutoff is a deliberate relevance heuristic). - Self session is excluded from
peer_chat_search— your own context is already loaded, no point re-reading it.
Counting messages — be precise
Naive grep counting "type":"user" + "type":"assistant" lines in a JSONL file overcounts dramatically. The real metrics in list_sessions { includeMeta: true }:
userPrompts— real user inputs. Excludes tool_result wrappers (which are inflated by ~10× in tool-heavy sessions).assistantReplies— assistant events withstop_reason='end_turn'. One per "agent finished, your turn" moment.
A naive count of 2000 events might map to 90 actual prompts and 125 actual replies. When reporting activity, use these properly-scoped counts.
Common errors and what they mean
| Error code | Meaning | Fix |
|---|---|---|
peer_not_found | No active peer with that id/name | Run peer_list to see who's actually online. In VS Code Extension, the target chat must have been activated (clicked) since the last reload. |
ambiguous_peer | Multiple peers share that name | Switch from name to id (UUID from peer_list). |
original_not_found | peer_reply can't find the original | Since v0.3.1 checks both pending/ and done/ — if still missing, the msgId is wrong (typo). |
session_file_not_found | Peer exists but its JSONL doesn't | Brand new chat without a first prompt — wait until they send something. |
invalid_query_regex | queryRegex: true got an uncompilable pattern | Read the message's detail (it includes the regex compile error). |
scope_too_large | peer_chat_search filtered scope > 200 MB | Narrow with scope: 'project' instead of all-projects, or refine query to be more selective. FTS5 backend planned for very large scopes. |
self_send | Tried to peer_ask self | Use direct reasoning instead. |
Delivery model — push vs piggyback
Messages are delivered in one of two modes:
- Push channel — if the user has
channelsEnabled: trueplus the plugin inallowedChannelPlugins(admin-controlled), messages arrive in real time as a<channel>tag in the receiver's context. - Piggyback fallback — without push, messages wait in
pending/and surface in the receiver's tool output on their next MCP call. Always reliable, just with latency tied to the receiver's activity.
You don't choose — the plugin uses push when available, piggyback otherwise. From the consumer side it's seamless. If push fires and shows you the message inline, the same message will NOT be re-rendered in the piggyback block on the next tool call (deduplicated since v0.3.2).
Anti-patterns — don't do these
- Don't poll
peer_inbox_readin a loop. Piggyback drains on every tool call. Polling adds nothing. - Don't use
peer_chat_searchwhen you know the peer. Usepeer_chat_read { query }— same matching, one session scope, faster, less noise. - Don't reflexively re-read your own session. Since v0.6.1
peer_chat_read({to: <selfUUID>})works (legitimate after autocompact / /clear when on-disk JSONL holds detail no longer in memory). But your own history is usually already loaded — only re-read it if context was truncated. - Don't
peer_askand then immediatelypeer_chat_readto see the reply. Wait for the reply to come back via piggyback or push; reading the peer's chat right after a send shows you their state from BEFORE they processed your message (race-prone). - Don't load
peer_chat_readwithincludeThinking: trueunless you specifically need chain-of-thought. Thinking blocks are often 5–10× the size of the visible answer. - Don't gate
peer_chat_searchresults without context. If you get 50 matches, usecontextLines: 1to surface enough surrounding text to judge relevance — bare matches without context are usually too thin to evaluate.
Data layout (for debugging)
The plugin lives entirely in ~/.claude-bridge/ (its own namespace; never touches Claude Code internals):
~/.claude-bridge/
├── inbox/<sessionId>/
│ ├── pending/<msg-id>.json ← incoming messages not yet drained
│ └── done/<msg-id>.json ← already consumed, available for peer_reply
└── status/<sessionId>.json ← heartbeat (refreshed every 5s)
If peer_list looks empty or wrong, ls ~/.claude-bridge/status/ shows the raw heartbeat files — anything within 30 seconds of now is considered active.
Performance characteristics
peer_list— single directory scan, <10 ms.peer_ask,peer_reply— atomic file write, <50 ms.peer_chat_read— stream parse one JSONL, 50–200 ms per MB.peer_chat_search(current project, typical dev box) — ~350 ms across 30 sessions.peer_chat_search(all projects, large notebook) — may hitscope_too_largeif >200 MB after 30-day filter.list_sessions { includeMeta: true }— streams every JSONL once. Several seconds for 50+ sessions.
When orchestrating, lean on cheap tools (peer_list, default list_sessions) before expensive ones (includeMeta, full search).