Concepts
wrkstrms (the ws command) runs many terminal programs, most of them AI
coding agents, side by side, and keeps track of which one needs you. This
page explains the pieces and how they fit together: the server and the
client, the workspace, tab and pane hierarchy, workstreams, agents and their
statuses, projects, saved sessions, and scripting. Read it once and the rest
of the guide will make sense.
The prefix key is Ctrl-Space: prefix d means press Ctrl-Space, let go, then press d. Every key is in Keys and mouse.
The big picture
Section titled “The big picture”server (one per name: "default", or -L <name>)└── workspace "Design System" a group of related work ├── tab "pr-101" often a workstream: worktree + branch │ ├── pane claude --model opus an agent (status: blocked / done / working / idle) │ ├── pane nvim --listen ... the tab's Neovim │ └── pane zsh a shell └── tab "Button a11y" linked to a project └── pane codex an agent
projects (plain files on disk, outside the server)└── button-a11y/ brief.md plan.md decisions.md log.md project.tomlA server holds workspaces. A workspace holds tabs. A tab holds panes in a split layout. Each pane runs one program on its own pseudo-terminal. Some panes are agents. A tab can be a workstream (opened from a template, with its own git worktree and branch) and can be linked to a project (a goal and its notes, kept in files).
Server and client
Section titled “Server and client”ws is split into two processes, the way tmux is:
- The server owns every pane. It runs each program on a PTY, keeps the terminal emulator for each pane, tracks agent status, saves the session, and draws the whole screen: sidebar, tabs, pane frames, dialogs.
- The client is what runs in your terminal when you type
ws. It is thin: it puts your terminal in raw mode, sends your keys and mouse events to the server, and writes the server’s output to the screen.
Because the server does the drawing, the client has no view state of its
own. When you detach (prefix d) or close the terminal, only the client
exits. The server and every program in it keep running, and the next ws
attaches to the same screen.
Only one client is attached at a time. Attaching from a second terminal takes over, and the first one is told it was detached. The screen takes the size of the attached terminal.
The server is the same ws binary, started in the background in its own
session, so a terminal hangup doesn’t reach it. It listens on a Unix socket.
Everything else talks to it over that socket: the client, agent hooks, and
commands such as ws new, ws open and the scripting commands.
your terminal background server agent CLI in a pane┌──────────────┐ keys, mouse ┌──────────────────────────┐ PTY ┌────────────────────┐│ ws (client) │ ───────────────► │ ws __serve │ ◄──────► │ claude / codex ││ │ ◄─────────────── │ panes, emulators, │ │ │└──────────────┘ screen output │ status, session saver │ │ hook fires: │ │ │ │ ws report │ other shells │ listens on │ report │ --agent claude │┌──────────────┐ ws new, ws tab, │ <socket dir>/<name>.sock│ ◄─────── │ Stop ││ ws <cmd> │ ───────────────► │ │ └────────────────────┘└──────────────┘ ws pane send... └────────────┬─────────────┘ │ saves as it changes ▼ ~/.local/state/ws/state-<name>.dbYou can run several independent servers side by side by name, with
ws -L <name>. ws --local skips the server and runs everything inside the
current terminal. Sessions covers all of this.
Why a server: it owns every terminal, so closing the window you’re looking through doesn’t stop what’s running in it. It also draws the whole interface and streams it to the terminal you attach from, the way tmux does, so any terminal can attach with nothing to install. One terminal is attached at a time; attaching from another takes over the screen.
Workspaces, tabs and panes
Section titled “Workspaces, tabs and panes”Panes are real terminals. Each runs one program (claude, codex,
nvim, lazygit, a dev server, a shell) with TERM=xterm-256color and
COLORTERM=truecolor. ws also sets a few environment variables in every
pane (WS_SOCKET, WS_PANE_ID, WS_WORKSPACE, and WS_WORKSTREAM in
workstream panes), which is how hooks and scripts find their way back to the
server. See Configuration.
Tabs hold panes in a tiled layout. You split, swap, zoom, resize and move panes between tabs. See Panes and layouts.
Workspaces group related tabs, for example one product or one repo family. The sidebar lists them. The first workspace is named after the directory where the server started.
Things close from the bottom up. A pane closes when its program exits. A tab closes when its last pane does, a workspace when its last tab does, and the server stops when its last workspace closes.
Workstreams
Section titled “Workstreams”A workstream is one unit of work, opened as a tab from a template:
- a git worktree and branch, created from the template’s repo and base
branch (or an existing branch with
--branch, or a pull request with--pr); - the template’s panes (for example an agent, Neovim and a shell) in the template’s layout;
- the template’s setup commands (for example
pnpm install), run once in a shell pane with the output left visible.
ws new design-system-pr pr-101Templates are TOML files in ~/.config/ws/templates/. Worktrees go under
~/.local/share/ws/worktrees/<repo>/<name>. A template without a repo makes
a scratch workstream with no worktree or branch.
For a workstream with a branch, ws asks gh about the branch’s pull
request in the background and shows its number and check status in the
status line and on the tab’s chip in the sidebar. prefix p opens the PR in
your browser.
Opening a workstream that is already open jumps to its tab. See Workstreams and the template format.
Agents and their status
Section titled “Agents and their status”An agent is a pane whose program is an AI CLI. ws gives every agent one
of four statuses, ordered by how much it needs you:
| Status | Mark | Meaning |
|---|---|---|
| Blocked | ● red | Waiting on a permission prompt or a question. |
| Done | ✓ green | Finished a turn you haven’t looked at yet. |
| Working | ◐ yellow | Running, with elapsed time and the current tool. |
| Idle | ○ | Finished and seen, or not started. |
The status colors the pane’s frame, its tab and its sidebar row. The sidebar
lists every agent, most urgent first, and the top bar shows the totals.
prefix a jumps to the next agent that needs you. When an agent blocks while
you are looking elsewhere, you get a desktop notification.
ws learns about agents in two ways:
- Hooks give the real status. Claude Code and Codex run a hook at each
lifecycle event: session start, prompt submitted, tool use, a permission
request or notification, stop, interrupt, session end. Each hook runs
ws report, which sends the event to the server throughWS_SOCKET, tagged with the pane’sWS_PANE_ID. The server turns events into a status: a submitted prompt or tool use means working, a permission request means blocked,Stopmeans done. Hooks also carry the session id, model and prompts, whichwsuses for labels and for resuming the agent after a restore. You install hooks once withws integrate claudeandws integrate codex. - Process detection finds agents without hooks. Every two seconds,
wslooks at the program running in the foreground of each pane. Known agent CLIs (Claude Code, Codex, OpenCode, aider, Gemini, goose,ollama runand others) are listed as agents with “no status” until their hooks report. When a pane goes back to a shell prompt, the agent is gone.
A done agent becomes idle once you look at its pane. When a status
looks wrong, the inspector (prefix I, or i on an agent in the sidebar)
shows what set it. See Agents.
Projects
Section titled “Projects”A project is a goal plus the notes needed to finish it, kept in plain Markdown files that any agent can read and write:
| File | Holds |
|---|---|
brief.md |
Goal, scope, done-when, constraints. |
plan.md |
A task checklist; the first open task is “next”. |
decisions.md |
What was decided and why. |
log.md |
Handoff entries, one per session, newest last. |
project.toml |
Name, goal, status, workspace, linked workstreams. |
context/ |
Notes for this project only. |
Projects live in ~/.local/share/ws/projects/<slug>/, outside the server.
They outlive everything else:
- An agent’s own resume (
claude --resume) only works inside one CLI. - A workstream’s worktree can be removed.
- A server can be quit.
The project files survive all three. Work started in Claude Code can be picked up in Codex, or in a fresh Claude session days later, from the same files.
Handoffs carry the work across sessions. ws project continue opens a
tab with an agent told to read the brief, plan, decisions and recent log
entries, work on the next task, and append a handoff entry to log.md before
it stops. ws project handoff asks a project’s agents that aren’t busy to
write their entry now. If an agent’s session ends without adding to
log.md, ws appends an entry of its own: the agent, model, session id,
first and last prompt, duration, and the commits and uncommitted changes
since the session began.
Tabs and workstreams can be linked to a project. The sidebar has a Projects section, and the top bar shows the current tab’s project. See Projects.
Sessions: save and restore
Section titled “Sessions: save and restore”The server saves the session as it changes: workspaces, tabs, layouts, each pane’s command and current directory, and each agent’s kind, model and session id. It goes to a SQLite file, one per server name.
- A clean quit (
prefix q,ws kill-server, or closing the last pane) clears the saved session. - If the server stops without a clean quit (a crash,
kill -9, a reboot) or withws kill-server --keep, the nextwsoffers to restore it.ws restartrestores it straight away. Agents come back resumed (claude --resume <id>,codex resume <id>), Neovim comes back on the same socket, other commands run again, and shells start fresh in the directory they were in.
Scrollback isn’t saved; agents keep their own transcripts. See Sessions.
Scripting
Section titled “Scripting”Everything you can build by hand, you can build from a script. The scripting commands talk to the running server and print what they made, so the next command can build on it:
pane=$(ws tab new --workspace Games --name Snake -- nvim plan.md)ws split --pane "$pane" -- claudeInside a pane, these commands go to the server that owns the pane (through
WS_SOCKET). Elsewhere they go to the default server, or to a named one with
-L <name>. See Scripting and the CLI reference.
How it fits together
Section titled “How it fits together”- You run
ws. The client starts the server if none answers on the socket, offering to restore a saved session first, then attaches. - The server starts your shell or command in a pane, with
WS_SOCKETandWS_PANE_IDset. - You start
claudein the pane. Its hooks runws report, which sends each event to the server overWS_SOCKET. - The server updates the agent’s status, redraws the frame, tab, sidebar and top bar, records the event for the inspector, and notifies you if the agent blocked while you were elsewhere.
- The server saves the session as it changes, so after a crash
wscan bring back each pane, resuming agents by session id. - Project files sit outside all of this, so the work can continue in any agent, in any later session.