Paul Wade.dev
NOW READING · CLAUDE CODE AGENTS DIFIT GATE
All posts

One terminal, 10+ Claude Code agents, and a difit gate before every PR

Obstacle 98 min read

How I run 10+ Claude Code agents from one orchestrator session with native cross-session messaging, and review every change, PR reply, and new issue in a customized difit fork before anything reaches GitHub.

I work from one terminal. A Claude Code session acts as the orchestrator, starts its own background agents, and talks to them with Claude Code's built-in cross-session messaging. Every code change, every PR comment reply, and every new issue then comes to me in a customized fork of difit, where I approve or deny it and my comments go straight back to the agent that did the work.

Most days I have one terminal open. The Claude Code session in it is my orchestrator, and it starts the other sessions itself, usually ten or more. They run in the background as sub-agents, each with its own chat, and all of them run with --dangerously-skip-permissions so nobody stops halfway through a task to ask me something. What keeps that from being reckless is the gate at the end: nothing becomes a pull request, a PR reply, or a GitHub comment until I've looked at it in my fork of difit.

How the agents talk

All of this is native to Claude Code. I don't run a separate harness and there's no shared markdown file the agents read and write to stay in sync. A shared file only works if every agent remembers to check it at the right moment. Claude Code also has agent teams, which I only found out about recently, and for what I do an orchestrator plus background sessions is simpler.

I've also tried AgentsRoom. It's a cool program, but there's a lot going on in it. The UI is very busy, and it reads like an app that was built mostly with AI, which is fine, I just didn't need that much on screen. It also isn't entirely free for a setup my size. The free tier stops at 2 projects and 6 agents, and past that the Pro plan is €7.99 a month. That's cheap, but my setup costs nothing on top of the Claude Code plan I already pay for.

The bigger difference for me was how the agents talk. AgentsRoom adds the messaging on from the outside, after the fact. Claude Code's agents already know how to message each other, and in my experience that shows. The handoffs and actions in AgentsRoom felt a little unnatural for the model, and the native messaging doesn't.

The piece doing the work is cross-session messaging. Each session has its own inbox. Claude finds the other sessions with ListAgents and sends with SendMessage, and I never call those tools by hand. I tell the orchestrator what needs to happen and it handles who hears about it. Per the docs, a message to a busy session is read between tool calls and a message to an idle one starts a new turn, so nothing has to poll.

A couple of details from the docs made me more comfortable running it this way. On the same machine, messages go over a local socket for each session and don't pass through Anthropic's servers. The receiving session knows a message came from another session and not from me, so it won't change settings or CLAUDE.md just because another agent asked.

The difit gate

Stock difit is a CLI that opens your local git diff in a browser viewer that looks like GitHub's "Files changed" tab. You can comment on lines, and it already has some agent support built in. My fork is built for my job and stays private, so I'll stick to what it does.

When an agent finishes a change, it opens a difit session on its diff. I read it like I'd read any PR. Each change has approve and deny buttons, and I can leave notes. When I comment on a line, that comment shows up as an event in the terminal of the agent that started the session. That's the agent that wrote the code and still has the context, so I don't have to figure out which of the ten sessions touched a file or relay anything through the orchestrator. It answers in the thread or makes a fix, I refresh, and the new diff is there with its reply next to my comment. When I'm happy I approve the whole change, or I deny it.

It goes both directions. The orchestrator can comment in difit too, and the agents get those events the same way they get mine, right on the lines they changed.

The loop for one change:

output
agent finishes a change
  -> opens a difit session on its diff
  -> I (or the orchestrator) comment on a line
  -> event lands in that agent's terminal
  -> agent replies in the thread or makes a fix
  -> I refresh and read the new diff
  -> approve, deny, or comment again
  -> approved work moves on to a PR

Because difit mirrors GitHub's PR review UI, I'm not switching between two ways of reading code all day. It also means what I approve looks the way the reviewer on GitHub is going to see it, so there are fewer surprises once the PR is up.

Reply drafts

When someone comments on one of my PRs, a reply gets drafted and difit holds it before anything is posted to GitHub. I review them in a batch. I read all the drafts at once, fix the wording on any that need it, send the ones I pick, and deny the rest. If the whole batch is off I deny all of it. Nothing goes out under my name that I haven't read.

New issues come through difit as well. It already has a server running, so the fork piggybacks on it and shows new issues next to everything else instead of me checking GitHub separately.

The today board

Any running difit server has a /today page. It reads the same status files the reviews use and shows what's active and which PR drafts are waiting. That's where I start. If there's a code gate waiting I open it, if there's a batch of reply drafts I open that, and most of the time that's a click and a read, which makes the whole thing very quick.

The agents don't keep the board up to date by remembering to. One Claude Code hook does it. It fires on SessionStart, Stop, SessionEnd, TaskCreated, TaskCompleted and TeammateIdle, and writes each session's latest state to a small JSON file: repo, branch, current task, last event and when. It runs async and never fails a turn. The page shows a session as working after it starts or picks up a task, and idle after it stops.

Everything else on the page skips hooks. Gates and reply drafts write their own small files when they open, and my approve and deny clicks write theirs, so the page reads those folders directly and a new gate shows up right away. GitHub and the work board are cached for a few minutes. The page server checks a fingerprint of the content every few seconds and refreshes the open tab only when something actually changed.

It has gaps I know about. Nothing reports mid-turn, so a session that's been busy for an hour still shows where it started, and a session that gets a new prompt after stopping keeps showing idle until it stops again. Adding the same hook on UserPromptSubmit should fix the second one.

Rules the agents can't skip

Reviewing everything only works if the diffs I get are mostly sane. The other half of this is checkyourvibe, my project for compile-time type checks. I write a constraint in plain language and it becomes a check the code has to pass at compile time, so the agent can't write the code I don't want in the first place. These are type checks at compile time, so I don't lump it in with linters. Between that and the difit gate I'm a lot more relaxed about agents running without permission prompts. If you want more background on refusing an agent's write, I covered a smaller version with plain ESLint in Writing an ESLint rule that can refuse an agent's write.

What I don't know yet

I haven't measured any of this. "Very quick" is how it feels to me. I don't have review times, approve and deny counts, or a comparison against a harness or shared-file setup, and if I start tracking those I'll post the numbers.

Cost is the thing I keep an eye on. Every background agent is a full Claude Code session with its own context, and the docs say a delivered message counts toward usage like a prompt I typed. With ten or more sessions talking to each other that adds up. The docs also mention that the inbox throttles repeated messages and caps its queue, which keeps two agents from getting stuck replying to each other forever.

And the gate is only as good as my reading. It means I looked at every change. That isn't the same as catching every problem, which is why the compile-time checks and CI still matter.

If you want to try something like this without my fork, stock difit gets you a good part of the way. It ships agent skills (npx skills add yoshiko-pg/difit), can stay running with --keep-alive, and lets an agent read your comments with difit comment get. What my fork adds is the push: my comment lands in the right agent's terminal the moment I write it, plus the approve and deny buttons, reply drafts, issues, and the /today board on top.