My main driver is Claude Code, with Codex bolted on for the hard parts and reviews. The split works surprisingly well. But after a while I noticed something dumb: I had become a human copy-paste relay between two AIs.
Copy what Claude Code wrote, paste it into Codex for review, copy Codex's feedback, paste it back into Claude Code. Dozens of times a day. It's tedious, it breaks focus, and I'd occasionally paste the wrong thing. I had two capable agents sitting side by side and was personally doing the dumbest job in the loop β carrying messages between them.
Agents should just be able to message each other directly. So I built agmsg
and put it on GitHub: https://github.com/fujibee/agmsg
You're an engineer; you'd rather run it than read about it.
bash <(curl -fsSL https://raw.githubusercontent.com/fujibee/agmsg/main/setup.sh)
Then restart Claude Code / Codex and run /agmsg
($agmsg
on Codex). It asks for a team name and an agent name on first use. The README has the full quickstart and copy-paste commands.
Once two agents join the same "team," they can message each other. After that you just talk to your agent: "tell alice the review is done," "any messages?" β that's it.
The only dependencies are bash and sqlite3. No daemon, no network, no Python.
This started by accident. Claude Code (Opus 4.6) got stuck on a gnarly implementation β kept going in circles no matter how I rephrased it. On a whim I handed the exact same spec to Codex (GPT-5.3), and it produced a correct version almost instantly. That was a quiet shock.
Since then I've split the roles:
Neither is "better" β they have different temperaments, so they're good at different moments. And the instant I started running both, the carrier-pigeon problem began.
In one line: CLI AI agents message each other through a shared SQLite database. Claude Code, Codex, Gemini CLI β any CLI agent works.
It's built as an Agent Skill, so you install it as a skill and don't touch the agent itself. It turned out better than I expected, so I open-sourced it.
The order I went through, and why.
I checked the built-ins first. I didn't want to reinvent anything, so I looked at Claude Code's team / subagent features. They're designed around short-lived sessions β great for spinning off a one-off subtask, but not for two agents holding an ongoing conversation.
I started with plain text files. The built-in team agent was file-based and simple, so I did the same. Fine for short-lived use β but the moment you want persistent + concurrent, it falls apart: write conflicts, corrupted files.
So I moved to SQLite (WAL mode). Multiple readers + one writer, no conflicts; transactions that survive concurrent access; message history for free.
But keep dependencies minimal. This is the part I cared about most. You could make it as rich as you want, but I wanted it to run the instant you drop it in, on anyone's machine. The result: it runs anywhere bash and sqlite3 run. No daemon of its own (agmsg has no resident process; only monitor
mode piggybacks on Claude Code's built-in Monitor), no network (all local), no Python.
There are roughly three ways a message reaches an agent:
/agmsg
, or ask the agent to "check the inbox." Reliable, but you have to remember.Honestly, monitor is what tipped it from toy to useful. With manual/hook there's a gap β the other side already replied, but you don't notice until you poke it. With monitor, the conversation just flows.
The mechanism (riding Claude Code's Monitor on top of a blocking SQLite read) is interesting enough that I want to write it up separately.
And here's the fun part. Put two monitor-mode Claude Code instances on the same team and leave them alone, and they keep talking with zero human input. I told two of them to "play tic-tac-toe," and they sent moves back and forth and played the whole game out. Not useful, exactly β but watching two agents volley autonomously is genuinely fun:
Two monitor-mode Claude Code instances playing tic-tac-toe over agmsg, no human in the loop (real game, static gaps trimmed, ~2.5x).
The thing I fought hardest while building this was that Claude Code and Codex have completely different hook and config models. Claude Code has Monitor and rich SessionStart hooks; Codex has neither. Splitting delivery into monitor / turn is there to absorb that gap, and the config files live in different places and formats for each. Getting the same "notice when a message arrives" to work across two agents with different foundations took the most thought.
How to smooth over those heterogeneous-agent seams β and the monitor internals I mentioned β are what I'll write about next.
For now: install it and let Claude Code and Codex talk to each other directly.