herdr vs tmux and Zellij — the terminal problem that starts at your third agent
A multiplexer keeps terminals alive. It cannot tell you which of them is waiting for an answer. That single gap is the entire case for herdr, and it is both narrower and more interesting than the pitch.
The previous comparison on this site answered which terminal agent to pick. This one is about a problem that does not exist until you have picked, and then run a second and a third one at the same time.
With one agent, the terminal is not a topic. You start it, you watch it, you answer it. With three, the watching is the work. You have three panes in front of you and no way to see, without reading each one, which one finished, which one is still thinking, and which one has been sitting on a permission prompt since before lunch.
The obvious answer is a multiplexer, and it is a good answer to the wrong half of the problem.
What tmux already solved, and what it did not
Persistence has been a solved problem since long before coding agents existed. tmux runs a server that owns real PTYs; you attach a client, you detach it, the processes underneath never notice. Close the laptop, lose the SSH connection, come back tomorrow — the work is where you left it. Zellij does the same in Rust with a friendlier default UI. Between them, “my agent died when my Wi-Fi did” is a solved problem, and any comparison that sells persistence as new is selling you 2007.
What tmux does not have is any notion of what is inside a pane. It knows the pane’s foreground command — #{pane_current_command} — and it knows whether bytes are arriving. That second signal is more useful than it sounds, and you can build something from it:
# tmux: mark a window that has produced no output for 30 seconds
set-option -w monitor-silence 30
set-hook -w alert-silence 'run-shell "notify-send \"pane went quiet\""'
That is a real, working “the agent probably stopped” alarm, and for a single agent it is often enough.
Now put three agents behind it. monitor-silence fires for the agent that finished successfully, for the agent that crashed, and for the agent that is displaying Do you want to allow this edit? (y/n) and will sit there forever. All three produced no output for thirty seconds. Silence is not a state. It is the absence of one, and the two cases you most need to tell apart — done and blocked — are exactly the two that silence collapses together.
That gap is the entire reason herdr exists.
What herdr adds
herdr is in the tmux lineage and does not hide it — the default prefix is ctrl+b, and ctrl+b q detaches. A background server owns the panes; the TUI you look at is one client of it, the CLI is another, and a plain SSH session is a third. Detaching every client leaves the agents running.
On top of that inheritance it adds one thing: it knows which panes contain agents, and gives each one a state.
| State | Meaning |
|---|---|
blocked |
The agent needs input, approval, or a decision |
working |
The agent is actively running |
done |
It finished and you have not looked yet |
idle |
Finished or waiting, and already seen |
unknown |
herdr cannot classify it confidently |
Those states roll upward. A blocked agent makes its pane, its tab and its whole workspace read as blocked in the sidebar, so with nine agents across four repositories you look at one column instead of nine panes. That is the product.
The second-order consequence is more interesting than the sidebar. Once state is a real value, it is something a program can wait on:
herdr agent wait w1:p1 --until blocked
herdr agent wait w1:p1 --until done
That is a different primitive from anything a multiplexer offers. tmux’s wait-for waits on a channel that something else has to signal, or on a hook event — you can wait for silence, but not for meaning. herdr’s wait is server-owned and event-driven, and it pins the pane’s current occupant so that a replacement process cannot accidentally satisfy the wait.
herdr ships a skill file for this — skills/herdr/SKILL.md, installable with npx skills add herdrdev/herdr --skill herdr -g — that teaches an agent to drive the CLI: split a pane, run a command in it without stealing focus, read another pane’s output, wait until a sibling agent is genuinely blocked. Its first rule is a guardrail worth noticing: if HERDR_ENV=1 is not set, the agent must stop and say it is not running inside a herdr pane, so an agent outside herdr cannot start driving a session it does not own.
Where the state actually comes from
This is the part the marketing pages skip, and it is the part that should decide whether you install it.
herdr determines a pane’s state through one of two paths, and the docs list which agents get which.
Lifecycle hooks or plugins. The agent itself reports what it is doing. This is authoritative: herdr uses the reports for idle, working, blocked and session identity, and deliberately does not also run screen matching for that pane, to avoid two competing sources of truth. As of v0.9.0 this path covers Pi, OpenCode, OMP, Kilo Code CLI, Kimi Code CLI and MastraCode — when their integration is installed.
Screen manifests. For everything else, herdr identifies the foreground process and evaluates TOML rules against a snapshot of the live bottom of the pane buffer — the region where a TUI draws its prompt — optionally using the terminal title and OSC progress sequences as extra evidence. This path covers Claude Code, Codex, Cursor Agent CLI, GitHub Copilot CLI, Grok CLI, Devin CLI, Droid, Qwen Code, Qoder CLI, Amp, Antigravity CLI, Hermes Agent and others. Gemini CLI and Cline are detected but, in the project’s own words, less thoroughly tested.
Read those two lists again with your own setup in mind. For the two most widely used agents in this category, the flagship feature is pattern-matching on rendered text. That is not a criticism of the implementation — there is no other way to do it for an agent that exposes no hooks — but it has consequences you should price in:
- A permission prompt herdr has not seen before shows as
idle, notblocked. The docs say so plainly, and the fallback even has a name in the diagnostic output:default_known_agent_idle_fallback. Blocked detection is strict on purpose; the failure mode is under-reporting, not phantom approvals. - Detection therefore has to track other people’s UI changes. herdr’s answer is to fetch updated manifests from herdr.dev in the background and hot-reload them into the running server. If a machine that quietly downloads behaviour rules is a problem in your environment,
[update] manifest_check = falseturns it off, and a file in~/.config/herdr/agent-detection/overrides any manifest locally. - When a pane shows the wrong thing,
herdr agent explain <target>prints the matched rule, the manifest source and version, and the evidence — which is the right tool to ship alongside a heuristic.
The honest summary: the state is a well-engineered inference for most agents and a genuine report for a few. It is much better than silence. It is not the same thing as the agent telling you.
What actually survives a restart
There is a claim in circulation that you can restart the machine and herdr will bring your agents back. The documentation is more precise than that, and the precision matters.
| Processes keep running | Layout returns | Scrollback returns | Conversation resumes | |
|---|---|---|---|---|
| Detach and reattach | Yes | Yes | Yes, it is the live terminal | Yes — it never stopped |
| Server or machine restart | No | Yes | Only if you enabled pane history | Only for agents with native session restore |
Detach is the strong path, and it is the tmux inheritance: nothing stopped, so nothing has to be reconstructed. A server restart is a different animal. The original pane processes are gone. herdr restores the shape — workspaces, tabs, panes, working directories, focus — and panes that cannot do better come back as fresh shells in their saved directories.
Two mechanisms soften that, and both have a catch:
Pane screen history replays recent terminal contents after a restart. It is off by default, and the reason is the right one: pane output contains secrets, tokens and command output, so turning it on means treating herdr’s session directory like shell history. Zellij made the same call for the same reason — its session resurrection serializes layout and the command per pane by default, and only serializes the viewport and scrollback if you ask.
Native agent session restore is the part that is genuinely new. Agents that report a session reference through a current integration are restarted with their own resume flag — claude --resume <id>, codex resume <id>, opencode --session <id>, pi --session <path-or-id> and a dozen more. It is on by default. The conversation comes back; the half-finished cargo build in the pane next to it does not.
Worth noting that Zellij’s session resurrection already covers the layout half, serialized to a human-readable KDL layout every second, and it does one thing herdr’s restore does not: it puts a “Press ENTER to run…” banner in front of each restored command, on the grounds that silently re-running whatever was in the pane is a bad idea when the pane might have held an rm -rf. Different defaults, both defensible.
The category next door: apps that replace your terminal
cmux (26,970 stars, Swift, macOS, built on Ghostty) and Warp answer the same “which agent needs me” question by being the terminal. That buys a rendering surface they fully control — real notifications, vertical tabs, a UI that does not have to live inside a character grid — and costs you your terminal, your config, and the machines the app does not ship for.
herdr’s position is the opposite trade: it changes nothing about your terminal and owns what runs inside it, which is why it also works over plain SSH into a box that will never run a desktop app. One wrinkle worth knowing if you nest things: herdr runs fine inside tmux, but if your shell config auto-starts tmux inside a herdr pane, herdr sees tmux as the pane process and the agent behind it disappears from detection.
The numbers
Checked against the GitHub API on 2026-09-10:
| herdr | tmux | Zellij | cmux | |
|---|---|---|---|---|
| Repository | herdrdev/herdr |
tmux/tmux |
zellij-org/zellij |
manaflow-ai/cmux |
| Stars | 37,215 | 49,164 | 35,352 | 26,970 |
| Language | Rust | C | Rust | Swift |
| Licence | Apache-2.0 | ISC | MIT | Other |
| Repo created | 2026-03-27 | mirror since 2015 | 2020-09-01 | 2026-01-28 |
| Latest release | v0.9.0, 2026-09-07 | — | — | — |
One row deserves comment. herdr reached 37,215 stars in under six months, which tells you the problem is widely felt and tells you nothing about whether the tool is finished. It is pre-1.0, the docs carry twenty-odd published versions, live handoff is explicitly experimental, and pane history sits under [experimental]. That is the normal shape of a fast-moving young project, not a warning — but if you are standardising a team on something, “the config file changed twice this quarter” is a real cost, and tmux’s ISC-licensed stability is the thing on the other side of that scale.
Choosing
Stay on tmux or Zellij if you run one agent at a time; if a monitor-silence alarm covers what you need; if you will not add a second daemon that holds every terminal you own; or if long-term config stability outweighs a better sidebar.
Add herdr if you routinely have three or more agents in flight; if you have caught one sitting on an approval prompt for half an hour; if your agents run on a remote box you SSH into and disconnect from; or if you want agents to coordinate with each other through agent wait instead of you brokering every handoff.
Look at cmux or Warp instead if you are happy to move into a new terminal on a supported platform and would rather have a real GUI than a TUI sidebar.
Check the supported-agents table first, whichever way you lean. If your agent is on the lifecycle-hook list, you get reported state. If it is on the screen-manifest list — and Claude Code and Codex are — you get a good inference with a documented failure mode. Both are useful. They are not the same purchase.
A note on how this was written
Every behavioural claim here comes from herdr’s own repository and documentation — the README, and the concepts, agents, session-state, agent-skill and socket-api pages of herdr.dev/docs at v0.9.0 — and from the tmux manual page and Zellij’s session-resurrection documentation for the comparisons. Star counts, licences and creation dates came from the GitHub API on 2026-09-10 and will drift.
One correction is worth recording, because the overstatement is common: several write-ups say that herdr survives a machine restart and resumes the agents. herdr’s own documentation is narrower and clearer — after a server restart the original processes do not survive; the layout returns, and supported agents can resume their conversation through their own resume flag. That is a useful feature and a different claim.
herdr has not been through an extended hands-on trial by the author. Where this page makes a judgement rather than reports a fact, it reasons from documented design decisions and says so.
Both illustrations are generated rather than photographed. The two diagrams are compiled from a typed specification of the facts above, so they carry no claim this page does not make in words; the opening image is a metaphor and carries none at all.
Tools covered in this guide
- herdrA background server that owns your agents' terminals, and knows which one is blocked.
- Claude CodeTerminal-based coding agent that runs commands and edits files directly.
- Codex CLIOpenAI's open-source terminal coding agent, rewritten in Rust.
- opencodeOpen-source terminal coding agent that runs on whichever model you point it at.
- PiA minimal terminal coding harness you extend in TypeScript instead of configuring.