You've got three Claude Code windows open. One renames an API route, another is still calling the old one, and you're the messenger — alt-tabbing, copy-pasting "hey I renamed /auth/login" between terminals like it's 2003.
agentwire is the wire between them. A tiny localhost service your agents talk to directly: they message each other, share state, claim files so they don't collide, and wake each other up when one goes idle or gets closed.
backend ▶ "renamed /auth/login -> /auth/signin, update your calls" ──┐
│
frontend ◀───────── [agentwire inbox] surfaces it mid-turn ◀──────────┘
(you never touched the keyboard)
Not an orchestrator. Not a cloud service. Just the missing message bus between agents already running on your machine.
- Direct messaging — any agent messages any other by name, or
@all. Durable, survives restarts. - Push delivery — Claude Code sees peer messages mid-turn via hooks. No polling, no tokens burned while idle.
- Shared state —
kv set/kv geta value once, every session reads it. Compare-and-set for safe writes. - File claims — claim
src/auth/**so other agents back off. Advisory, TTL'd, conflict-reported. - Wake engine — nudge an idle session by typing into its tmux pane; respawn a dead one headless. The part nobody else has.
- Multi-harness — first-class Claude Code, plus Codex, opencode, and Gemini CLI via MCP.
- Zero-config identity — sessions name themselves by walking the process tree. Any number per project stay isolated.
- Local-only & durable — binds
127.0.0.1, every request authenticated, SQLite WAL, all files0600.
Needs Bun (the CLI uses bun:sqlite and Bun.serve).
# global command — gives you `agentwire` and the short alias `aw`
bun add -g @codeprakhar25/agentwire
# or run once, no install
bunx @codeprakhar25/agentwire doctor
# or from a clone
git clone https://github.com/codeprakhar25/agentwire && cd agentwire
bun install && bun linkThe daemon auto-spawns on first use: SQLite at ~/.agentwire/agentwire.db, HTTP on
127.0.0.1:7777, authenticated with a per-user secret at ~/.agentwire/secret (all
0600). Override home/port with $AGENTWIRE_HOME / $AGENTWIRE_PORT.
agentwire register backend --harness claude-code
agentwire register frontend --harness claude-code
# frontend steers backend
agentwire send backend "API renamed /auth/login -> /auth/signin" --urgency steer --as frontend
agentwire inbox --drain --as backend
# [steer] from frontend: API renamed /auth/login -> /auth/signin
agentwire agents
# ● backend claude-code active "wiring auth"
# ● frontend claude-code active
agentwire log --follow # live event feedFrom a clone without installing, swap agentwire for bun src/cli.ts.
cd your-project
agentwire init claude-code # or --global for all projectsRestart your Claude sessions, and from then on peer messages surface
automatically as [agentwire inbox] blocks — mid-turn, no polling. The model
sends with plain agentwire send ... through its Bash tool.
What init writes (idempotent — re-running edits only its own marked block):
.claude/settings.jsongets three hooks, each tagged with anagentwire-hookmarker:PostToolUse+Stop→ drain inbox to stderr, exit 2, so pending peer messages land mid-turn in the model's context.UserPromptSubmit→ same drain, to stdout / exit 0, surfacing messages at the top of a new turn.
CLAUDE.mdgets a managed guidance block between<!-- agentwire:start -->/<!-- agentwire:end -->telling the model how to coordinate.
Identity resolves by walking the process tree to the owning claude process, so any
number of sessions in one project stay isolated with zero config. Sessions get
deterministic names (cc-<dir>-<pid>); agentwire rename backend gives a friendly
one. Renames survive — hooks resolve by (harness, pid), not name.
To remove: delete the agentwire-hook entries from settings.json and the marked
block from CLAUDE.md.
agentwire kv set SERVER_IP 50.19.186.215 # write once...
agentwire kv get SERVER_IP # ...every session reads it
agentwire kv cas SERVER_IP 1 10.0.0.9 # compare-and-set by version
agentwire claim "src/auth/**" --ttl 3600 # advisory exclusive claim
agentwire claims # who holds what
agentwire release "src/auth/**"Claims are advisory (like flock): overlap is checked by the pattern's literal path
prefix, conflicts report the holder, TTLs prevent immortal locks.
Normal delivery reaches active sessions. The wake engine reaches the rest:
- Idle session (sitting at the prompt, no hooks firing) — a steer/followup
message types a nudge into the session's tmux pane; the injected prompt fires
the
UserPromptSubmithook, which drains the real inbox into context. - Dead session (process gone) — a steer message respawns it headless via
claude --resume <session_id> -p, one turn to process the inbox.session_idis captured automatically from hook stdin.
Both tiers type into your terminals / spawn processes, so they are off by
default, urgency-gated, rate-limited (1/min tmux, 1/5min resume), guarded
(injection only when the pane's foreground command is a known harness), and
ledger-logged (wake.tmux, wake.resume, wake.failed):
agentwire config wake.tmux true # idle nudge (needs claude running inside tmux)
agentwire config wake.resume true # dead respawnNote: the idle nudge requires the target
claudeto be running inside a tmux pane — that's the only channel another process can type into. The dead-respawn tier does not. Live regression:bun scripts/wake-e2e.ts $PWD(needs tmux).
cd your-project
agentwire init codex # ~/.codex/config.toml + AGENTS.md
agentwire init opencode # ~/.config/opencode/opencode.json + AGENTS.md
agentwire init gemini # ~/.gemini/settings.json + GEMINI.mdThese harnesses speak MCP and have no push channel, so delivery rides on tool
responses: every agentwire tool response is prefixed with an [INBOX] block, and the
guidance builds a call-check_messages-each-turn habit (latency bounded to one
turn). Acks are confirm-on-next-call — a message is only acked once the next tool call
proves the previous response reached the model, so a dropped response re-delivers
instead of vanishing.
agentwire mcp is harness-agnostic — any MCP client gets the same five tools:
check_messages, send_message, list_agents, set_summary, rename.
| Command | What |
|---|---|
agentwire register <name> |
register an identity (--harness, --project) |
agentwire agents |
who's registered: status (● active / ○ idle / ✗ dead), project, summary |
agentwire send <to|@all> <msg> |
message an agent; --urgency steer|followup|info |
agentwire inbox [--drain] |
read pending messages; --drain leases + acks them |
agentwire summary <text> |
publish what you're working on |
agentwire rename <name> |
take a friendly name |
agentwire kv set/get/cas |
shared key-value state across sessions |
agentwire claim / claims / release |
advisory file claims |
agentwire log [--follow] |
event ledger; --follow tails over SSE |
agentwire config wake.<tier> <bool> |
toggle wake tiers |
agentwire doctor |
check daemon health, perms, adapters, wake config |
agentwire init <harness> |
wire up claude-code / codex / opencode / gemini |
agentwire status / daemon |
daemon health / run in foreground |
Identity comes from --as <name>, else $AGENTWIRE_AGENT_NAME, else human.
- Durable — messages survive daemon and machine restarts (SQLite WAL).
- At-least-once — lease + ack with a 60s reclaim window; re-registering a name clears stale leases so nothing is lost to a dead session.
- Local-only — binds
127.0.0.1, every request authenticated, all files0600.
bun test # store + daemon HTTP suites
bun run build # single binary at dist/agentwire
bun run typecheck # tsc --noEmit
# isolated playground
AGENTWIRE_HOME=/tmp/x AGENTWIRE_PORT=7811 bun src/cli.ts ...See SPEC.md for the full design.
MIT — see LICENSE.