Moving my agent sessions from tmux to herdr
Why I moved OpenCode sessions into herdr, how the plugin, the nix config and the prefix key fit together, and what still lives in tmux.
I say I moved from tmux to herdr, and that is true for one kind of work. Agent sessions now live in herdr. tmux is still configured in my dotfiles (prefix ctrl+a, in nix/home/terminal.nix), and my own guide, HERDR.md, says tmux stays for servers, SSH and regular work. So this post is about a split, not a replacement. I introduced herdr briefly in the lightning round. Here is what came after.
What tmux did not solve for parallel agents
An agent session is a process I want to leave running and check on by state. I do not want to watch it. tmux keeps the process alive, which is half of that. The other half is knowing which of three OpenCode instances is waiting for an approval, and tmux only gives me window names and an activity flag. To find the blocked one I had to cycle through windows and read screens.
herdr is built around that question. It shows each agent as idle, working, blocked or done in a sidebar, rolls those states up to the workspace, and can resume the exact conversation after a restart. The thesis I am testing is that a multiplexer organised around agent state fits this work better than one organised around windows and panes.
The mental model
My tmux habits map over directly, which is why the move was cheap.
| herdr | tmux | What I put there |
|---|---|---|
| Workspace | Session | One per repo |
| Tab | Window | agents, shell, logs |
| Pane | Pane | One OpenCode instance per pane |
The OpenCode plugin
herdr integration install opencode writes ~/.config/opencode/plugins/herdr-agent-state.js. It does two things. It reports idle, working, blocked or done to the sidebar, and it records the OpenCode session id so herdr can run opencode --session <id> on restore.
Two properties matter for keeping it working. First, the plugin is generated by the herdr CLI, so it is not repo-owned. My repo does not contain the file. In nix/home/ai-tools/opencode.nix an activation step installs it only if the file is missing and the herdr binary exists at the Homebrew path. Second, that means nothing in my repo pins its content to a version of OpenCode. HERDR.md also warns that an opencode started outside a herdr pane sends no state and will not auto-restore.
The migration risk is OpenCode v2. My handoff plan lists the herdr plugin as generated by herdr v0.9.1 and says to check for a newer herdr and re-run the install if it supports v2. I have not done that yet, so I do not know whether the current plugin works on v2.
The config and the prefix conflict
Homebrew installs herdr ("herdr" in nix/hosts/mac/default.nix), and home-manager writes the config inline in nix/home/mac-extras/platform-tools.nix. A file at config/herdr/config.toml is mentioned in older notes, but it does not exist in the repo now. The inline block is the only source.
The prefix changed twice in the history:
340084fadded herdr withctrl+space, chosen because tmux already ownsctrl+a.53f792dmoved it toctrl+b. The commit message saysctrl+spacecollides with the macOS input-source switcher shortcut.
ctrl+b is tmux's stock default and nothing else here uses it, so it stopped fighting the OS. The stale part is documentation. HERDR.md and the lightning round post still show ctrl+space. Below I use the current ctrl+b from the config.
One small fix from the first week was 1776081, which changed split_vertical from pipe to v because herdr does not accept pipe as a key name.
A normal day
cd ~/IdeaProjects/<repo> && herdr
ctrl+b c # new tab
opencode # give it the task
ctrl+b d # detach, the agent keeps working
Then I go back to whatever I was doing. When the sidebar shows blocked or done, I run herdr to reattach and use ctrl+b alt+1 to jump to agent 1. For review, ctrl+b alt+g opens lazygit as a popup and ctrl+b t opens a scratch terminal. ctrl+b alt+n opens the notification feed I built earlier. For parallel work I split with ctrl+b v or ctrl+b minus and start another opencode. ctrl+b g switches workspaces, and the switcher shows the rolled-up state per repo.
What still lives in tmux
Servers, SSH sessions and general shell work. The nix config for tmux is untouched, and the herdr config header says it mirrors tmux habits and does not replace it. I have also not measured how much time this saves, so the claim is about how the work feels, not a number.
Limits and unknowns
- Session restore is configured (
resume_agents_on_restore = true), but my August session notes say I never tested a restart end to end. I have not recorded a test since. - The OpenCode v2 plugin question above is open.
- The state labels come from the plugin. If it stops reporting, the sidebar stops being useful and I would not notice from inside a pane.
- The docs disagree with the config on the prefix, and I have not fixed them yet.