A home for your Claude Code sessions. roost runs every Claude Code session in its own window of a terminal multiplexer, reads each one's live state from Claude Code's hook events, and brings all of them back, resumed where they stopped, after a reboot, a power cut or a dropped SSH / RDP connection.
Install · How it works · Commands · FAQ · For AI agents
roost is an independent community project. It is not made by or affiliated with Anthropic.
Agent sessions are long-lived, but the terminal that hosts them is not. Close a tab, lose Wi-Fi or reboot for an update, and the context of a dozen sessions is scattered across transcripts you have to find and resume by hand. And while they run, the one that is blocked on a permission prompt looks exactly like the ones that are working.
roost fixes both: sessions live in tmux (Linux) or psmux (Windows), a registry fed by Claude Code hooks knows the state and id of every session, and one command (or the boot itself) puts them all back.
- Know which session needs you. Every window carries its state, from
●working to?blocked on a decision to*finished and waiting for your next message. The status bar lists only the sessions that need you, and the terminal tab says🐦 roost ?1 *2, or🐦 roost@host ?1 *2when you came in over SSH.Ctrl+B ajumps to the next one: decisions first, then the ones waiting for you. - Keep groups of sessions apart in workspaces. Map folders to workspaces in one small file, and each workspace
gets a multiplexer session of its own: its own dashboard, status bar,
Ctrl+B aand tab title (🐦 roost:api ?1). The dashboard's top line lists every workspace with what waits for you there, and the status bar names the other workspaces that want you. Tab, a click orCtrl+B (/)switch;roost move NAME WSputs a session elsewhere. - Survive closed terminals, dropped connections and reboots. Close the terminal or drop SSH and the sessions keep running. After a
reboot or a power cut, roost reopens each one with
claude --resume <id>. Linux does it from systemd before anyone logs in, Windows at sign-in. Sessions interrupted mid-task get a short "continue where you stopped" prompt; idle ones just reopen. - Restore exactly what you had. The permission mode and the claude.ai connector setting of each session come back
with it.
bypassPermissionsis never restored automatically. - See what each session is running. A Remote Control column shows which sessions you can reach from another device.
MODEL, EFFORT and ADVISOR columns show what each session last ran with, including mid-session
/model,/effortand/advisorchanges. - Stay out of the way. roost never passes
--modelor effort flags and never edits your Claude Code settings beyond onehookssection, written once, with a backup. The hooks print nothing, always exit 0, and do nothing in sessions roost did not start. - Run on plain shell. bash and PowerShell, tmux and psmux,
jqon Linux. No daemon, no service of its own on Windows, nothing downloaded at install time.
| Linux | Windows | |
|---|---|---|
| Sessions survive closing the terminal, SSH / RDP drops | ✓ | ✓ |
| Live state per session from Claude Code hooks | ✓ | ✓ |
| Status bar with only the sessions that need you, tab title with counts | ✓ | ✓ |
Jump to the next waiting session (roost next, Ctrl+B a) |
✓ | ✓ |
| Workspaces: a multiplexer session, dashboard and status bar per group of sessions | ✓ | ✓ |
Resume after reboot / power loss with claude --resume |
✓ before login (systemd) | ✓ at sign-in |
| Continue prompt only for interrupted sessions, crash-loop guard | ✓ | ✓ |
| Permission mode and connectors restored | ✓ | ✓ |
| Remote Control column | ✓ | ✓ |
| Model, effort and advisor columns | ✓ | ✓ |
| Dashboard | watch-style list, click to jump |
interactive: click, arrows, keys, wheel |
| Detects a claude that exited and leaves its window as a plain shell | ✓ SHELL |
✓ × exited |
| Self-test, health check, uninstaller | ✓ | ✓ |
flowchart LR
claude(["claude<br/>one per window"]) -- "hook event + JSON" --> hook["roost-hook<br/>silent · always exit 0"]
hook -- "write, then rename" --> reg[("registry<br/>sessions/NAME.json")]
hook -- "state glyph" --> win["tmux / psmux<br/>window"]
reg --> ui["dashboard<br/>status bar · tab title"]
cc[("Claude Code files<br/>sessions/PID.json<br/>transcripts")] -.->|read-only| ui
reg --> restore["roost restore"]
restore -- "claude --resume ID" --> claude
Hooks. Claude Code calls roost-hook on SessionStart, UserPromptSubmit, Stop, Notification, PostToolUse and
SessionEnd. The hook writes one small JSON file per session (name, directory, session id, transcript, state, permission
mode, connectors) by write-then-rename, so a reader never sees half a file, and it puts the state glyph on the window.
PostToolUse fires after every tool call, so on Windows it runs asynchronously behind a 30 ms check and starts
PowerShell only while the session shows ?.
stateDiagram-v2
direction LR
state "quiet ○" as quiet
state "working ●" as working
state "needs you ?" as ask
state "your turn *" as turn
state "parked ◌" as parked
[*] --> quiet: SessionStart
quiet --> working: UserPromptSubmit
working --> ask: permission prompt or question
ask --> working: answered (PostToolUse)
working --> turn: Stop
turn --> working: UserPromptSubmit
working --> parked: reboot, power loss, window closed
parked --> working: roost restore, was busy (+ continue prompt)
parked --> quiet: roost restore, was idle
turn --> [*]: /exit
Two state values. state (busy or idle) decides whether a restored session gets the continue prompt. The display
state (working, needs you, your turn, quiet) drives the glyphs. A permission prompt changes only the display, so a
session that was working when the power went out still counts as interrupted.
Liveness without guesswork. A session counts as open only while claude really runs in its window. Linux asks tmux
what the pane runs. Windows matches the window's process id and its start time, so a reused PID is never mistaken for
a running session. Only /exit or /logout forgets a session; everything else leaves it parked for restore.
Restore pipeline (roost restore --boot on Linux, the Startup entry on Windows):
- Wait until
api.anthropic.comanswers, so sessions do not resume into a dead network. - Crash-loop guard: after three boots within 30 minutes, sessions are resumed without the continue prompt, so a machine that keeps crashing does not keep re-running the task that crashed it.
- Reopen every parked or exited session with
claude --resume <id>, its permission mode and connectors, staggered a few seconds apart. - Append the continue prompt only for sessions whose registry state is busy.
What roost reads from Claude Code, always read-only and failing soft (an empty cell, never an error):
- Hook payloads: session id, transcript path, permission mode, notification type.
~/.claude/sessions/<pid>.json: Remote Control.bridgeSessionIdis set while a session is connected; pid and process start time are matched, and a session handed to a background job follows the job's process.- The end of each transcript:
message.model,effortandadvisorModelof the last main-thread assistant entry, read from the last 64 KB (more when an entry is larger). Hooks carry no advisor and the model only on some starts; the transcript has all three.
Needs tmux 3.2+, jq and claude. Tested on Ubuntu 24.04 with tmux 3.4.
git clone https://github.com/ExpectedArgument/roost && cd roost/linux
./install.sh # command + hooks + tmux config
./install.sh --systemd # also: start at boot and resume after power loss, before login (sudo; takes over at the next boot if tmux already runs)
roost new api ~/src/api # first session (add -c to continue the last chat in that directory)
roost # dashboard; Ctrl-b a next waiting; Ctrl-b w all sessions; Ctrl-b d detach
roost doctor ; roost selftestRemote: ssh -t HOST roost. Optional hard-stall recovery (hardware watchdog, panic reboot, NVMe power-state
workaround) lives in linux/extras/recovery; it is hardware specific and off by default.
Needs PowerShell 7, claude and psmux (winget install psmux --version 3.4.0; verify the
hash on the release page). Best in Windows Terminal. The Windows port is newer than the Linux one; please report problems.
git clone https://github.com/ExpectedArgument/roost; cd roost
pwsh -File windows\install.ps1 # scripts, hooks, sign-in restore, `roost` command, Windows Terminal profile
roost selftest ; roost doctor
roost new api C:\src\api
roost # dashboard: click, arrows + Enter or a session's key; Ctrl+B a next waiting; Ctrl+B d detachinstall.ps1 also adds a roost profile with the 🐦 icon to the Windows Terminal new-tab menu. Sessions reopen when
you sign in (Startup folder, no admin rights). Windows has no boot service without a sign-in unless you enable auto
sign-in yourself.
| Command | |
|---|---|
roost |
open the dashboard (attach to the multiplexer session) |
roost new NAME [DIR] [claude args] |
start a session; extra args go to claude (-c, --model sonnet, …). DIR defaults to $ROOST_PROJECT_ROOT/NAME when it exists. --connectors keeps claude.ai connectors on, --ws WS puts it in workspace WS |
roost NAME |
switch to a session (reopens it first if it is parked or exited), in whichever workspace it is |
roost ls [WS] |
list sessions with state, age, RC, model, effort and advisor, under a heading per workspace (or one workspace's) |
roost restore [NAME] [--ws WS] |
reopen parked and exited sessions, each in its workspace |
roost next |
jump to the next session of this workspace that waits for you (? first, then *); bound to Ctrl+B a |
roost ws [WS] |
list the workspaces, or open the screen of one; Ctrl+B ( / ) switch to the previous / next |
roost move NAME [WS] / --all |
put a session in workspace WS (or, without WS, where its folder says); an open one is reopened there with --resume, never while it works or waits for a decision |
roost close NAME |
/exit the session cleanly, close its window, forget it |
roost forget NAME |
drop the registry entry only |
roost adopt NAME |
Linux: register a tmux window where claude already runs |
roost doctor |
health check: tools, hooks, restore at boot / sign-in |
roost selftest |
test the scripts against a throw-away setup, touching no real session |
| Linux | Windows | |
|---|---|---|
| default claude flags | ~/.config/roost/claude-flags (--permission-mode auto) |
%LOCALAPPDATA%\roost\config\claude-flags |
| continue prompt | ~/.config/roost/resume-prompt.txt |
…\config\resume-prompt.txt |
sessions that keep claude.ai connectors (or *) |
~/.config/roost/connectors-allow |
…\config\connectors-allow |
workspaces: WORKSPACE FOLDER per line |
~/.config/roost/workspaces |
…\config\workspaces |
| state and registry | ~/.local/state/roost |
%LOCALAPPDATA%\roost |
Sessions not listed in connectors-allow start with ENABLE_CLAUDEAI_MCP_SERVERS=false, which saves tokens when you do
not need them. The default is *, so nothing changes until you edit it.
# ~/.config/roost/workspaces (Windows: %LOCALAPPDATA%\roost\config\workspaces)
work ~/src/work
personal ~/src/personal
A session belongs to the workspace whose folder holds its directory (the longest folder wins); every other session is
in default. roost new NAME DIR --ws WS and roost move NAME WS override that for one session. Each workspace is a
multiplexer session of its own, roost for default and roost-WS for the others, so Ctrl+B a, the status bar and
the tab title only count its sessions, and the dashboard lists only them under a line of all workspaces. A session
moved while it runs is closed the way a reboot would close it and resumed in its new workspace; one that works or waits
for a decision is not moved. With the file empty everything stays in default and nothing changes. After editing the
file, roost move --all reopens open sessions in the workspace they now belong to.
Environment: ROOST_PROJECT_ROOT, ROOST_AUTOCONTINUE=0 (never send the continue prompt), ROOST_CLAUDE_BIN,
ROOST_STAGGER, ROOST_NET_WAIT, and on Windows ROOST_MOUSE=0 (the dashboard leaves the mouse alone).
- Your settings stay yours. The installers add one
hookssection to~/.claude/settings.jsonafter backing it up, detect existing entries and leave the file untouched when nothing is missing.model,effortLeveland everything else are never written. - Hooks cannot hurt a session. They print nothing (hook output would reach the model's context), always exit 0, never block on anything and ignore sessions roost did not start.
- Nothing is assumed about Claude Code internals. Its per-process files and transcripts are undocumented, so every read is defensive: a missing file or field means an empty cell, never a failed command.
- No surprises with permissions. A restored session gets back its own permission mode, except
bypassPermissions, which is never restored automatically.roost closesends Esc before/exit, so a pending permission prompt is cancelled, never answered. - No telemetry, no daemon. The only network access is the reachability check before a boot-time restore.
linux/bin/roost selftest # own tmux socket, state dir and a fake claude: new, hooks, ls, title, next, restore, SHELL, close, RC, workspaces
pwsh -File windows\roost.ps1 selftest # temp home, dry-run psmux, roost-run.ps1 with a fake claude: hooks, restore, run, next, transcripts, RC, workspaces
pwsh -File windows\probe-statusbar.ps1 -Click … # renders the psmux status bar through a pseudoconsole and clicks its labels
Tested on Ubuntu 24.04 with tmux 3.4, and on Windows 11 with PowerShell 7.6, psmux 3.4.0 and Windows Terminal 1.24.
What happens when the machine reboots or loses power?
Every registered session is parked. On Linux with --systemd, the tmux server and the restore unit start at boot, before
anyone logs in. On Windows, restore runs when you sign in. Each session comes back with claude --resume, in its own
directory, with its permission mode. Those that were working get the continue prompt.
Does roost change my Claude Code configuration?
Only the hooks section, once, with a backup. Model, effort, advisor, permissions and every other setting are left
alone. Extra flags you give roost new are passed to claude unchanged.
The dashboard is crowded. Can I split it?
Yes: put the folders of each group in the workspaces file (see Workspaces). Every workspace gets its
own dashboard, status bar and Ctrl+B a; the top line of each dashboard still shows what waits for you elsewhere.
A session shows × exited or SHELL. What happened?
claude quit, for example after a crash or a Ctrl+C, and its window stayed open as a plain shell. roost restore NAME
resumes it in a fresh window, and so do picking it on the dashboard and roost NAME.
Does it work remotely?
Yes. Detach, disconnect, come back: ssh -t HOST roost on Linux. On Windows, sessions survive a dropped Remote Desktop
connection. With Claude Code's Remote Control, the RC column shows which sessions you can reach from another device.
Why psmux on Windows?
It is a native tmux-compatible multiplexer for Windows, so sessions run in real Windows processes, with no WSL. roost
works around a few psmux 3.4.0 quirks, documented in windows/CLAUDE.md.
Pasted text loses letters on Windows (for example every i on a Turkish Q keyboard).
That is psmux 3.4.0 bug #884: a pasted letter that the layout types with
AltGr arrives as a Tab. It is fixed in psmux after 3.4.0. Until then, roost attaches with PSMUX_VT_INPUT=1 when your
keyboard layout is affected, which avoids it. roost doctor tells you when that applies.
How do I remove it?
linux/uninstall.sh, or pwsh -File windows\uninstall.ps1 [-DryRun]. Both back up settings.json and remove only
roost's hook entries. Your conversations are never touched.
Read CLAUDE.md first (shared design: hook events, state values, registry, invariants), then
linux/CLAUDE.md or windows/CLAUDE.md for the port you change. Mirror a
behaviour change in both ports unless it is platform specific. Never test against the user's live multiplexer session:
both self-tests and the recipes in the port notes use throw-away sessions, homes and a fake claude.
MIT