Terminals That Outlive the Laptop
This is a work in progress. It runs on my desk, against one remote machine, and has for about a day. I am writing it up now rather than later because the interesting part is not the code — there is not much of it — but the realization that every piece needed already existed, and that the remaining work was to notice how they fit.
What I want
I want to open a terminal on any machine I have access to — the laptop in front of me, the Mac on the desk, a VPS, whatever a cloud provider rents by the hour — and then stop thinking about where it is. It should appear on my desktop as a window, tiled alongside every other window. Closing the lid, rebooting, walking to a different network: none of that should end what is running in it. When I come back, the window comes back, and what was running is still running.
Increasingly, what is running is a coding agent, and the thing I most want to know about a coding agent is whether it is waiting on me. A window that is busy can stay in the background. A window that is asking a question should say so before I go looking.
Who solved what
The surviving part was solved long ago. GNU Screen established the idea in 1987 that a terminal session belongs to a server process, not to the window you happen to be looking at it through; tmux refined it into the tool most of us actually use, and blazed the path everything below walks along: detach, reattach, sessions that outlive connections. Nothing in this post would make sense to someone who had not first internalized what tmux does.
The knowing part is newer. Herdr, by Can Celik, is a terminal runtime built around coding agents. Like tmux it keeps panes alive in a background server you attach to and detach from. Unlike tmux it watches what is on the screen and classifies it: this pane holds a Claude Code session, and that session is working, or idle, or blocked waiting on an approval, or done with work you have not yet looked at. It knows this for a couple of dozen agents, from screen evidence alone or from hooks the agents themselves can report through, and it publishes all of it — the panes, their state, and every change — over a small JSON socket API with a documented stability contract.
The place to put it together is Omarchy, DHH's opinionated Arch and Hyprland setup, whose fourth version replaced its status bar with a shell built on Quickshell and gave it a plugin system: a bar widget is a directory of QML, installed from any git URL, hot-reloaded on save, running inside the shell process with the same UI components the built-in widgets use. Omarchy already ships Herdr as a base package, with a keybinding to launch it and a set of shell helpers for laying out panes, so the two projects are well aware of each other.
And there were already people ahead of me. When I went looking for prior art I found five Omarchy plugins for Herdr, all from the last two months:
- carlotran4/omarchy-herdr talks to Herdr's socket directly from QML and subscribes to its events, so it never polls; it colours a counter per agent state and clicking focuses the agent inside the Herdr window.
- jankeesvw/omarchy-herdr lists every Herdr session on the machine, running or stopped, with the agents inside each, and does the window management: find the window showing a session, or open one.
- nytafar/gjetr puts the agents on a second touchscreen as cards.
- finna/omarchy-herdr-hud is a draggable overlay for prompting agents without leaving a game.
- bengous/agent-notifier-omarchy is a completion notifier.
Each of them treats the Herdr window as the window: a multiplexer TUI you raise and then navigate inside. That is what Herdr is designed to be, and it is a good design. It was just not quite what I wanted.
The one primitive that changes the picture
Herdr can attach a raw terminal to a single pane, with none of its own
chrome: herdr terminal attach <id>. Run that inside a terminal emulator
and you get that pane's screen, and only that pane's screen, in that
window. Close the window and you have detached; the pane carries on.
Run it through ssh -t and the pane can be on any machine you can reach.
That is the whole trick. If every pane can be its own window, then Hyprland is the multiplexer — it already tiles, focuses, and moves windows better than any TUI will — and Herdr is reduced to the two things only it does: keep the panes alive, and know what the agents in them are doing. The status bar is where you find out; the window is where you go.
Reaching a remote Herdr from the desktop turned out to be equally mundane. Its API is newline-delimited JSON over a unix socket, and OpenSSH will forward a unix socket as readily as a port:
ssh -N -L /run/user/1000/agent-windows/mini.sock:/Users/rubys/.config/herdr/herdr.sock mini.local
To the plugin, that forwarded socket is indistinguishable from a local
one. I subscribed to events through it, created and closed a tab on the
Mac, and watched tab_created and tab_closed arrive on the laptop
about 280 milliseconds later — most of which was the SSH round-trip of
the command that caused them.
What exists now
rubys/omarchy-agent-windows is an Omarchy bar widget, about six hundred lines of QML and JavaScript plus three small bash scripts:
- A list of machines, each either local or an SSH target. For each, one QML object holds a request socket and a subscription socket to that machine's Herdr, and for SSH machines a child process that keeps the socket forward alive with backoff across sleep and network changes.
- A bar badge counting agents that are blocked, done, and working, that disappears entirely when everything is quiet. The panel behind it lists every pane on every machine, most urgent first.
- Click a pane and its window comes to the front; if it has none, one is
opened running the direct attach, over
ssh -tif need be. Each machine chip has a + that creates a fresh shell there and opens it as a window in the same click. - Windows are coloured by status: a red border when the agent is waiting
on you, green when it finished while you were elsewhere. Focusing such
a window tells Herdr the pane was seen, which is what clears both the
border and the badge. This is done with Hyprland window tags and two
window rules, so it costs one
hyprctlcall per change and nothing when nothing changes.
Nothing polls on a timer. Nothing upstream had to change: every call the plugin makes is in Herdr's released 0.9.0, and every UI piece is in Omarchy's public plugin contract. And it is an honest borrower: the socket lifecycle is adapted from carlotran4's model, the focus-workspace-then-window sequence and terminal launch from jankeesvw's script, and the repository says so file by file. Anyone is welcome to take anything back in the other direction.
What it is not, yet
It has been tested on exactly one topology: this laptop running Omarchy
4.0.3 and the foot terminal, talking to a MacBook running Herdr 0.9.0
in its default session. Named sessions, Linux remotes, other terminals:
untested. The red border has been exercised with synthetic tags but not
yet by an agent actually asking me something, though the event that
would trigger it is the same one I watched arrive from the Mac.
The known sharp edge is one tmux users will recognize: if the Mac's own Herdr client is attached to the same pane, the two clients contend for the grid size. The fix is not to do that, or to let the desktop take over.
And there is a symmetry question I have not settled. Closing a pane on the server closes its window here, which I like. Closing the window here only detaches, which is the point of the whole exercise — but it means "close" does two different things depending on which side you do it from. That is probably right. I am not sure yet.
Where this goes
The next machine is a Mac mini rather than the MacBook, and after that something rented. The plugin does not care; it is a list of SSH targets. What I want to learn is whether the status bar is the right surface for "which of my agents needs me" once there are more than a handful of them, and whether panes-as-windows holds up when a Hyprland workspace has a dozen of them from three machines.
None of this required anyone to accept a patch. Herdr's maintainers run
their project with agents they control and do not take unsolicited pull
requests, which is their call to make; a working plugin built on the
public API is a better way to show them a usage pattern than a proposal
would be, and the only thing I would ask of them is that terminal attach and the socket contract stay as stable as their documentation
already promises. Omarchy made it possible to ship a bar widget without
asking anyone at all. And tmux, again, showed everybody that a terminal
is something a server owns and a window merely visits.
Most of the code was written with Claude, in a Herdr pane on the laptop, while its counterpart on the Mac worked on something else in the window below — busy the whole time, as it happens, and never once red. I am still waiting for that.