Sessions
Your panes run in a background server, not in your terminal. This page
covers how that server starts and stops, how you attach to it and detach
from it, how to run separate named servers or skip the server entirely, and
how ws saves your session and brings it back after a crash or a reboot.
The prefix key is Ctrl-Space: prefix d means press Ctrl-Space, then
d.
The background server
Section titled “The background server”The first time you run ws, it starts a server in the background and
attaches your terminal to it:
wsThe server is the same ws binary, run as ws __serve in its own session,
so a hangup from your terminal doesn’t reach it. It owns every pane and every
program running in them, and it draws the screen. Your terminal runs a thin
client that forwards keys and mouse events and shows what the server draws.
See Concepts.
When a server is already running, ws attaches to it. Given a command, ws
also opens that command in a new tab, in the directory you ran it from:
ws claude --model opusWhen no server is running, ws <command> starts a new server with that
command in its first pane, in a workspace named after the directory the
server started in. Plain ws (no command) does the same with a shell, and
also opens the Dashboard as the first
workspace and shows it, unless [dashboard] enabled = false. A restored
session comes back as it was, Dashboard or not.
Programs in panes inherit the environment of the server, which is the environment of the terminal you started it from. Variables you export later, in another shell, don’t reach panes until the server restarts.
If the server fails to start within 10 seconds, ws says so and points you
at the log, ~/.local/state/ws/ws.log.
Attach
Section titled “Attach”ws attachws attach attaches to the running server. Plain ws does the same, but
ws attach never starts a server. If none is running, it stops with this
error:
no ws server is running; `ws` starts oneIt also never offers to restore a saved session; use plain ws for that.
Only one client is attached at a time. If you attach from a second terminal, it takes over, and the first terminal prints:
[detached: attached from another terminal]The screen takes the size of the terminal that is attached.
Detach
Section titled “Detach”There are two ways to detach:
- Press
prefix d. - Close the terminal window or tab.
Either way, only the client exits. The server and every program in it keep
running, including agents in the middle of a turn. After prefix d, your
terminal prints:
[detached; `ws` reattaches]Run ws (or ws attach) from any terminal to get the same screen back.
Quitting stops the server and everything in it. There are three ways:
| How | Asks first |
|---|---|
prefix q, or “Quit wrkstrms…” in the menu |
Yes: “Quit wrkstrms and stop N panes?” |
ws kill-server, from any shell |
No |
ws kill-server --keep or ws restart |
No; the session is kept (see Restart) |
Close the last pane (prefix x, or the program exits) |
prefix x on the last pane asks; a program exiting doesn’t |
ws kill-serverws kill-server waits until the server has stopped every pane’s programs
and exited (programs that ignore the request to stop are killed after a
second and a half), then prints Stopped the ws server; its panes were closed. If that takes more than five seconds, it says the server is still
shutting down instead. If no server is running, it says so and exits with
an error.
ws kill-server is a clean quit, like prefix q: it clears the saved
session. The agents that were running are not offered for restore by the
next ws, so let them finish (or write a project handoff)
first. To stop the server without losing the session, use
ws kill-server --keep or ws restart, below.
When the server quits, it stops whole process trees, not just the
program each pane started. Closing a terminal only sends a hangup, and a
program that ignores it, or a job running in the background of a shell,
would outlive the pane. So ws finds every descendant process of every pane,
sends each one SIGTERM, waits up to a second and a half, and sends
SIGKILL to whatever is still running. Dev servers, watchers and agents’
subprocesses all stop.
A quit is a clean end: the saved session is cleared, and the next ws
starts fresh. See Save and restore.
Closing a single pane, tab or workspace stops the process trees of its panes in the same way.
Restart, keeping the session
Section titled “Restart, keeping the session”ws restart stops the server, starts a new one and attaches to it, and
the new server puts everything back: workspaces, tabs, splits, directories,
and agents resuming their conversations. Use it after installing or building
a new ws, to switch to it without losing your place.
ws restartWhat happens, in order:
wsasks the server to save the session and quit. The server saves before it stops any pane, while each pane’s directory and each agent’s session id can still be read.- The server stops every pane’s process tree, as a normal quit does, but leaves the saved session on disk instead of clearing it.
wswaits (up to about ten seconds) for the old server to go away, starts a new one, which restores the session without asking, and attaches.
Shells come back as fresh shells in the same directory; whatever was running
in them (a dev server, a build) is not restarted. Agents come back resuming
their conversation (claude --resume <id>, codex resume <id>). See
How each pane comes back.
ws kill-server --keep does steps 1 and 2 only:
ws kill-server --keepIt prints Stopped the ws server and saved the session; `ws` restores it.
The next ws then offers the saved session the usual way.
Both work with named servers: ws -L dev restart. Like every command that
starts a server, ws restart refuses to run from inside a pane of the server
it would restart.
If the running server is older than ws restart itself, it doesn’t know the
save-and-quit message, and you get the “too old” error from
After you upgrade ws. Then the only way to switch
is ws kill-server and ws, which doesn’t keep the session.
Named servers
Section titled “Named servers”You can run several independent servers, each with its own panes, socket and
saved session, the way tmux -L works:
ws -L devEvery command takes -L <name> (or --socket-name <name>), before or after
the subcommand. These are the same:
ws -L dev kill-serverws kill-server -L devYou can also set the name in the environment:
export WS_SOCKET_NAME=devWithout a name, ws uses the server named default. Names are used as file
names: letters, digits, - and _ are kept, and any other character becomes
_.
| Server name | Socket | Saved session |
|---|---|---|
| (none) | <socket dir>/default.sock |
~/.local/state/ws/state-default.db |
dev |
<socket dir>/dev.sock |
~/.local/state/ws/state-dev.db |
The socket directory is $XDG_RUNTIME_DIR/ws when XDG_RUNTIME_DIR is set,
and $TMPDIR/ws-<uid> otherwise (the usual case on macOS). See
Configuration.
A named server is handy for trying a development build of ws without
touching the server your agents run in.
Running without a server: --local
Section titled “Running without a server: --local”ws --local--local runs ws inside the current terminal, with no background server:
- Nothing survives closing the terminal. Every pane stops with it.
- The session isn’t saved, so there is nothing to restore.
prefix dhas nothing to detach from and says so.- It listens on a socket of its own,
<socket dir>/ws-<pid>.sock, so hooks,ws openand the scripting commands still work from its panes.
--local works with a command (ws --local claude) and with ws new
(ws new --local <template> <name>).
Running ws inside ws
Section titled “Running ws inside ws”Every pane has WS_SOCKET set to the socket of the server that owns it.
From inside a pane:
-
ws,ws attachandws <command>for the same server are refused, since the pane would show itself inside itself:already inside this ws: use prefix t for a new tab (or `ws -L <name>` for another server) -
Attaching to another server (
ws -L other) works, like nested tmux. Your prefix key goes to the outerws;prefix Ctrl-Spacesends a literalCtrl-Spaceto the inner one. -
Commands that act on the server, such as
ws new,ws open,ws view --split,ws project continueand the scripting commands (ws tab new,ws split,ws pane send), go to the server that owns the pane. Pass-L <name>tows new,ws project continueor the scripting commands to target another server instead (if it isn’t running,ws newandws project continuestart it).ws openandws view --splitact on the pane you’re in, so they always go to its server.
After you upgrade ws
Section titled “After you upgrade ws”ws identifies each build by its binary’s path and modification time, so
every rebuild or reinstall is a new build. A running server keeps running the
build it started with. When you attach to a server from a different build,
the top bar shows:
this server is from another ws build: `ws restart` switches, keeping the sessionTo switch, run:
ws restartEvery pane’s processes stop, and the new server brings the layout back with agents resuming their conversations (Restart). Anything running in a shell (a dev server, a test watcher) needs starting again.
Until you switch, most things keep working. A newer ws command still talks
to an older server for every message that server knows. For a command the
old server doesn’t know, you get:
the running ws server is too old for this command (...); install the new build, then `ws kill-server` and `ws`This includes ws restart itself on a server from before it existed. In
that case ws kill-server and ws is the way to switch, and it doesn’t keep
the session, so finish or park your agents’ work first.
Save and restore
Section titled “Save and restore”What’s saved
Section titled “What’s saved”While it runs, the server saves the session to a SQLite file, one per server
name: ~/.local/state/ws/state-<name>.db. It checks for changes once a
second and saves at least every five seconds, so directory changes are picked
up. The file holds:
- every workspace, with its tabs and the active tab;
- each tab’s name, layout, workstream, Neovim socket and linked project;
- each pane’s command, current directory, label, and role (shell, editor or command);
- each agent’s kind, model, session id and last status.
Scrollback and screen contents aren’t saved. Agents keep their own transcripts, and resuming brings their conversation back.
The same file keeps the history of agent hook events and status changes that
the inspector and ws debug events show. That history stays after a quit.
When ws offers to restore
Section titled “When ws offers to restore”A clean quit clears the saved session. If the server stops any other way (a
crash, kill -9, a reboot, the machine losing power) or with
ws kill-server --keep, the saved session is still there. The next ws that
has to start a server offers to bring it back (ws restart restores without
asking):
ws: the last session was saved: 2 workspaces, 5 tabs, 9 panes (3 agents).Restore it? [Y/n]- Press Enter or
yto restore. - Answer
nto start fresh. The saved session is then forgotten. - If
wshas no terminal to ask on (its input isn’t a terminal), it restores without asking, since losing work is worse.
The offer comes from ws, ws <command>, ws new and ws project continue (without --workspace, --layout or --split), whenever they
start a server. If you asked for a command or a
workstream, it opens on top of the restored session.
ws doctor tells you whether a saved session is waiting to be restored.
How each pane comes back
Section titled “How each pane comes back”| Pane | Comes back as |
|---|---|
| Claude Code | claude --resume <session>, with the flags it was started with (such as --model). Uses --continue if the session id never arrived. |
| Codex | codex resume <session>, or codex resume --last if the session id never arrived. |
| An agent started from a shell | A shell that runs the resume command, then stays open as a login shell, so exiting the agent leaves you at a prompt. For Claude Code, the saved model is passed with --model. |
| The tab’s Neovim | nvim on the same socket. A socket file left by the crash is removed first. |
| Anything else (dev servers, lazygit) | Its command again. |
| Shells | A fresh shell. |
Every pane starts in the directory it was last in. If that directory is gone, a workstream pane falls back to the workstream’s directory. Manual pane labels come back too.
If a pane’s program fails to start, ws starts a shell in its place so the
layout stays whole. Tabs whose saved layout is damaged are dropped. Either
way, the top bar says restored, with problems: ..., and the details are in
the log. Otherwise it says how many workspaces and panes came back.
A workstream whose worktree was deleted can’t come back where it was. ws doctor warns about these before you restore.
Where state lives
Section titled “Where state lives”| Path | What |
|---|---|
~/.local/state/ws/state-<name>.db |
Saved session and event history, per server name. |
~/.local/state/ws/ws.log |
The log, shared by all servers. |
~/.local/state/ws/crash-<time>.log |
A backtrace, written when the server panics. |
<socket dir>/<name>.sock |
The server’s socket. |
~/.local/state is $XDG_STATE_HOME when that is set. The full list of
files, and the environment variables that move them, is in
Configuration.