tmux-resurrect for herdr. Snapshot your whole herd —
workspaces, tabs, panes, working directories, the programs running in each pane,
and your AI agents — to durable, versioned files on disk, and bring it all back
after a crash, reboot, or herdr server stop.
Think tmux-resurrect + tmux-continuum, adapted to herdr's socket API.
Partly — and this plugin is designed to fill the gap, not duplicate what's built in.
herdr already does, natively, after a server restart or crash:
| Built-in | What it restores |
|---|---|
| Session shape restore | workspaces, tabs, panes, cwd, layout, focus |
[session] resume_agents_on_restore |
resumes supported agents into their native conversation session — only when the integration reports a session ref |
[experimental] pane_history (off by default) |
recent on-screen text (visual only) |
What herdr does not do — the tmux-resurrect niche this plugin adds:
- Re-runs the programs that were live in each pane. herdr brings panes back as
bare shells in their saved directory. Your
npm run dev,nvim,tail -f,htop,psqlare not relaunched. This plugin relaunches them (allowlisted). - Periodic, versioned, durable snapshots (continuum-style) you can list and restore from — a belt-and-suspenders history that survives even if herdr's own internal state is lost, plus multiple restore points instead of one implicit shape.
- An explicit "resurrect everything" action + keybinding, and a dry-run so you can see exactly what will happen before it touches your session.
It complements the built-ins: after a crash, herdr restores the shape, and this plugin rehydrates those bare panes by re-running what was in them.
Requires Node.js (used by the plugin scripts; no npm dependencies) and herdr ≥ 0.7.0.
# from GitHub / the herdr plugin marketplace:
herdr plugin install ntindle/herdr-resurrect
# or from a local checkout (while iterating):
herdr plugin link /path/to/herdr-resurrect
herdr plugin list # confirm it registeredRun from the herdr command palette (or bind keys, below):
| Action | Does |
|---|---|
Resurrect: save snapshot |
write a snapshot now |
Resurrect: restore last snapshot |
rebuild / rehydrate from the newest snapshot |
Resurrect: preview restore (dry run) |
show the plan, change nothing |
Resurrect: list snapshots |
list saved snapshots |
Invoke without keybindings via the CLI too:
herdr plugin action invoke ntindle.herdr-resurrect.save
herdr plugin action invoke ntindle.herdr-resurrect.restore-previewAdd to your config.toml (herdr → config file path shown by herdr --help):
[[keys.command]]
key = "prefix+ctrl+s"
type = "plugin_action"
command = "ntindle.herdr-resurrect.save"
description = "resurrect: save"
[[keys.command]]
key = "prefix+ctrl+r"
type = "plugin_action"
command = "ntindle.herdr-resurrect.restore"
description = "resurrect: restore"Then herdr server reload-config. (Prefix defaults to ctrl+b, tmux-style.)
- Event-driven (on by default): the plugin snapshots whenever the session shape changes (workspace/pane created or closed, agent detected), debounced to at most one write per 20s. Nothing to enable.
- Timer (continuum-style): open the bundled
resurrect autosavepane once and leave it running; it snapshots everyHERDR_RESURRECT_INTERVALseconds (default 900) and once more on exit.
- Start herdr again. It restores your workspace/tab/pane shape (as bare shells).
- Run
Resurrect: restore last snapshot(or your keybinding). By default it rehydrates the panes herdr already brought back — re-running the commands and relaunching the agents — instead of creating duplicates. If a workspace is missing entirely, it's recreated from scratch.
Restore is idempotent: it only fills panes that are currently idle shells, so running it twice does nothing the second time.
Set autoRestore: true in settings.json (see Configuration) to skip step 2 — the
plugin then rehydrates automatically the first time herdr fires an event after a
server (re)start, so a crash + relaunch brings your commands and agents back on its
own.
How it works: herdr has no "server ready" plugin event yet, so the plugin detects a
fresh boot from herdr's socket file (rewritten on every start) and lets the first
event handler claim that boot, rehydrate once, and stand the others down — reading the
good pre-crash snapshot before autosave can overwrite it. It's off by default so it
never surprises you (tmux-continuum's auto-restore is opt-in too). A first-class
server.ready event would be cleaner — see Upstream below.
When relaunching an agent pane, the plugin uses the agent CLI's own resume/continue
flags where known — e.g. claude --resume <id> when herdr captured a native session
ref, else claude --continue; codex resume. Unknown agents relaunch fresh (and
herdr's own resume_agents_on_restore may still rejoin the session). Agents are
relaunched by short name (claude) so they run in both PowerShell and POSIX shells.
Override per agent via agentResumeCommands in settings.json.
| Mode | Flag | Behavior |
|---|---|---|
| auto (default) | — | rehydrate workspaces that already exist; recreate the rest |
| rehydrate | --rehydrate |
only fill existing panes; never create workspaces |
| recreate | --recreate |
always build everything from scratch |
| dry run | --dry-run |
print the plan, touch nothing |
| pick file | --file <path> |
restore a specific snapshot instead of the newest |
(CLI form, e.g. node bin/restore.js --recreate --dry-run, when running scripts directly.)
Settings — $HERDR_PLUGIN_CONFIG_DIR/settings.json, seeded on first run:
| Key | Default | Meaning |
|---|---|---|
autoRestore |
false |
rehydrate automatically on the first event after a server (re)start |
autoRestoreSettleMs |
2500 |
wait this long after the first boot event before rehydrating |
agentResume |
true |
use agent CLI resume/continue flags when relaunching agents |
agentResumeCommands |
{} |
per-agent overrides, e.g. { "claude": { "continue": "--continue" } } |
HERDR_RESURRECT_AUTO_RESTORE=1 overrides autoRestore without editing the file.
Allowlist — which non-agent programs get relaunched. Editable copy is created
on first run at $HERDR_PLUGIN_CONFIG_DIR/allowlist.txt (seeded from
config/allowlist.default.txt). One program name per line; a lone * restores
everything. Agents are handled automatically and are not listed here.
Environment variables
| Var | Default | Meaning |
|---|---|---|
HERDR_RESURRECT_INTERVAL |
900 |
autosave-pane interval, seconds |
HERDR_RESURRECT_DEBOUNCE |
20000 |
min ms between event-driven saves |
HERDR_RESURRECT_KEEP |
20 |
snapshots to retain before pruning |
Snapshots live under $HERDR_PLUGIN_STATE_DIR/snapshots/, with the newest also at
.../last.json.
- Command capture no longer relies on herdr's
pane process-infoalone (which on Windows only surfaces the console's foreground process-group leader). When process-info reports just the shell, the plugin walks the pane shell's process tree — one bulk query per snapshot (CIM on Windows,pson Linux/macOS) — and captures the oldest real child — sonode server.js,npm run dev,ping -tetc. are captured on Windows too, and the idle check is hardened everywhere. The program's own cwd isn't knowable this way (the pane's cwd is used), and the cmdline is the OS-recorded one (a leading quoted absolute path is rewritten to the program's short name at restore time so PowerShell executes it). - Agent conversation resume is best-effort but has two layers: the native
agent_sessionref when herdr reported one, else the session id is recovered from the agent CLI's own session store keyed by the pane's cwd (claude:~/.claude/projects/, codex:~/.codex/sessions/, copilot:~/.copilot/session-state/, cursor:~/.cursor/chats/). Resume/continue flags are known for claude, codex, gemini, copilot, cursor; unknown agents relaunch fresh (herdr's ownresume_agents_on_restoremay still rejoin the conversation). - Layout geometry is reconstructed from saved rects via sequential splits; simple rows/columns come back faithfully, deeply nested grids are approximated.
- Complex shell one-liners: commands with unbalanced-looking brackets/quotes can be mangled by PowerShell's PSReadLine when relaunched. Typical agent and dev-server commands are unaffected.
herdr-plugin.toml manifest: actions, autosave pane, event hooks
bin/save.js snapshot now (manual action + autosave pane)
bin/on-event.js event handler: debounced autosave + opt-in boot auto-restore
bin/restore.js rehydrate / recreate from a snapshot
bin/list.js list saved snapshots
bin/autosave.js continuum-style timer loop (runs in a pane)
lib/herdr.js thin wrapper over the herdr CLI (JSON in/out, retry)
lib/snapshot.js build + persist the enriched snapshot model
lib/restore.js the planner + executor
lib/allowlist.js which programs are safe to relaunch
lib/agents.js agent resume/continue command construction
lib/agent-sessions.js recover session ids from the agent CLIs' own stores
lib/pstree.js process-tree capture (CIM on Windows, ps on Unix)
lib/settings.js settings.json (autoRestore, agentResume, …)
lib/boot.js per-boot detection + claim-once coordination
lib/paths.js state/config locations
config/allowlist.default.txt seed allowlist
Auto-restore currently piggybacks on the first pane.created/workspace.created
after a restart, guarded by a socket-derived boot token. A first-class server.ready
plugin event (fired once, after the server restores the session) would be a cleaner,
race-free trigger. That's a new-API change, and herdr's CONTRIBUTING.md asks for a
GitHub Discussion first (not a cold PR) for new features — so this is proposed
there rather than pushed as a surprise PR. The implementation is small and localized
(emit once at the top of HeadlessServer::run, add the kind to PLUGIN_HOOK_EVENT_KINDS).
MIT — this plugin is a standalone process that talks to herdr over its CLI/socket, so it is not a derivative of herdr's AGPL-licensed core.