Three Things That Broke While Setting Up herdr
herdr is a terminal workspace manager for running several coding agents at once. I’d had the binary sitting on disk unused and finally wired it up properly.
Installation is a handful of commands. Three things still stopped me, and none of them announced what was wrong.
1. compinit -C silently ignores new completions
Standard zsh completion setup:
herdr completion zsh > ~/.zsh/completions/_herdr
Then in .zshrc:
fpath=("$HOME/.zsh/completions" $fpath)
autoload -Uz compinit && compinit -C
-C skips the cache-validity check to speed up shell startup. Plenty of “make your zsh fast” posts recommend it. It also means zsh never scans for completion files it hasn’t already seen.
The file is there. Nothing errors. Tab completion just does nothing.
To check whether a completion actually registered:
echo ${_comps[herdr]}
With -C, that’s empty. Drop the flag, open a new shell, and you get _herdr.
Don’t use
-Cin the same change where you add a new directory tofpath. Once the cache has been rebuilt you can put it back.
An accidental discovery while fixing this: my .zshrc had no compinit call at all, meaning the zsh completion system had never been initialised. Adding it brought back completions for a bunch of other tools I’d assumed just didn’t ship any.
2. The TUI needs a TTY. The server doesn’t.
Running herdr opens a TUI, which requires a TTY. That rules it out of scripts, CI, and any background context — which looked like a wall for automating the setup.
The client and server are separable:
nohup herdr server > /dev/null 2>&1 &
herdr server is headless and starts without a TTY. Bring the server up however you like; a human attaches later with herdr.
To confirm it actually detached, check the parent PID:
ps -o pid,ppid,command -p <PID>
PPID 1 means it’s been reparented to init and will outlive the shell that started it.
Where things live:
~/.config/herdr/herdr.sock srw------- (owner only)
~/.config/herdr/herdr-server.log
~/.config/herdr/session.json persisted workspace state
Stop it with herdr server stop.
The general lesson: before concluding an interactive tool can’t be automated, check whether it has a daemon mode hiding behind the interface.
3. The workspace ID isn’t in the id field
herdr’s control commands return JSON. I created a workspace, parsed the response for its ID, and kept getting nonsense downstream.
herdr workspace list | python3 -m json.tool
The identifier is workspace_id. The id field in the same payload is a request echo, containing things like cli:workspace:list.
workspace_id : w1 ← the actual ID
id : cli:workspace:list ← echo of your request
The docs say to read IDs from responses rather than constructing them, which is good advice that quietly assumes you’re reading the right key. id is a common enough name that you don’t stop to check.
The model, once it clicked
| Concept | Example ID | What it is |
|---|---|---|
| workspace | w1 |
Top level — one context |
| tab | w1:t1 |
A tab inside a workspace |
| pane | w1:p1 |
An actual terminal — shell, tests, server |
| agent | — | A coding agent detected inside a pane |
IDs are opaque and stable, and closed ones are never reused.
One behaviour worth knowing: agent start requires an existing available shell pane. It won’t create or split a layout for you. Get the pane first.
Agent lifecycle states:
idle— waitingworking— runningblocked— an approval or question prompt was detecteddone— a background task finished while you weren’t lookingunknown— no evidence either way
Distinguishing blocked from done is the whole point when several agents are running. You stop hunting through windows for the one that’s waiting on you.
Checking an integration before installing it
The Claude Code integration is one command:
herdr integration install claude
It edits ~/.claude/settings.json to add a SessionStart entry. I already had several hooks there, so I checked whether they survived. They did.
The installed hook also exits immediately unless HERDR_ENV=1, so running Claude Code outside herdr is unaffected. Removal is herdr integration uninstall claude.
Those are the two things worth verifying whenever a tool offers to modify your shell config or editor settings: does it preserve what’s already there, and is there a clean way out. Back up first regardless:
cp ~/.zshrc ~/.zshrc.bak.$(date +%Y%m%d)
Takeaways
All three failures were of the “the config is right, so why isn’t it working” kind, which is the slowest kind to diagnose.
- Completion not firing? Check
${_comps[command]}before touching the file again. Empty means it never registered — suspect yourcompinitflags - Interactive-only tool? Look for a headless server mode before writing it off
- Parsing JSON? Confirm the key names. A field named
idmay not be the id you want