Projects
A project is a goal plus the memory needed to finish it, kept in plain files that any agent can read and write. Work you start with Claude Code can be picked up by Codex, or by a fresh Claude session days later, from the same files. Agent resume only works inside one CLI, and a workstream’s worktree is eventually deleted; a project outlives both. This page covers the project files, how to create, link and continue projects, handoffs, the workbench layout and the PROJECTS sidebar section.
The prefix key is Ctrl-Space: prefix N means press Ctrl-Space, let go, then press N. Every key is in Keys and mouse.
What a project is
Section titled “What a project is”Each project is a folder in ~/.local/share/ws/projects/<slug>/ (or under $XDG_DATA_HOME/ws/projects/). The slug comes from the name: lowercase, with runs of other characters turned into -, up to 60 characters. “Button a11y” becomes button-a11y. Commands and scripts refer to a project by its slug.
| File | Holds |
|---|---|
project.toml |
Name, goal, status, workspace, linked workstreams, and what ws project continue remembers |
brief.md |
Goal, scope, done-when and constraints |
plan.md |
The task checklist |
decisions.md |
What was decided and why, so the next session doesn’t relitigate it |
log.md |
Handoff entries, one per session, newest last |
context/ |
Notes for this project only |
plan.md
Section titled “plan.md”Tasks are checklist lines: - [ ] task for open and - [x] task for done (* [ ] works too). Tasks are numbered in order from 1, counting done ones. The first open task is the project’s “next” task. Agents tick tasks as they finish them.
# Plan
- [x] Audit every button for keyboard focus- [ ] Add a visible focus ring to Button- [ ] Dropdown keyboard navigationHere task 2 is next.
project.toml
Section titled “project.toml”ws writes this file; you rarely need to edit it.
| Key | Meaning |
|---|---|
name |
The display name |
goal |
One line, from --goal |
status |
active, paused or done. Archiving sets done. |
workspace |
The workspace it was created in |
workstreams |
Names of workstreams linked to it |
dir |
Where its agents work; remembered from ws project continue --dir |
run |
The command the workbench’s run pane runs; remembered from --run |
run_rows |
How many rows the run pane starts with; remembered from --run-rows |
created |
The date it was created |
Creating a project
Section titled “Creating a project”From any shell:
ws project new "Button a11y" --goal "Every button keyboard reachable"| Flag | Effect |
|---|---|
--goal <GOAL> |
The goal, written to project.toml and the brief |
--workspace <WORKSPACE> |
The workspace to record in project.toml |
--no-edit |
Don’t open the brief afterwards |
ws creates the folder with a starting brief, plan, decisions file and log, then opens brief.md in your editor if you’re at a terminal. Fill in the brief and the plan, then continue the project with an agent.
Inside wrkstrms, press prefix N, or choose “New project…” in the main menu (right-click the top bar) or a workspace’s right-click menu. Type a name and press Enter. ws creates the project in the current workspace and opens a tab on it with the brief, plan and log in your editor.
Which editor
Section titled “Which editor”ws picks the editor for project files in this order:
$WS_EDITOR, if setnvim, if it’s on yourPATH$VISUAL, then$EDITORvi
Neovim comes before $EDITOR because wrkstrms is built around it, and because EDITOR=vim with a shell alias vim=nvim would otherwise start the real vim. Set WS_EDITOR to use something else.
Linking tabs and workstreams
Section titled “Linking tabs and workstreams”A tab linked to a project shows the project’s name in the top bar (◆ Button a11y). When an agent in a linked tab ends its session, ws can write a handoff entry for it (see ws’s own entry), and ws project handoff reaches it.
There are three ways to link:
ws project attach button-a11y # the tab you run this inws new design-system-pr pr-101 --project button-a11y # a new workstreamws tab new --workspace Games --project tetris -- claude # a new tab from a scriptws project attachmust run in a wrkstrms pane. It links that pane’s tab.- When the tab is a workstream, the workstream’s name is also added to
workstreamsinproject.toml. - Each command checks that the project exists first.
See Workstreams for ws new and Scripting for ws tab new.
Continuing a project with an agent
Section titled “Continuing a project with an agent”ws project continue opens a tab with an agent that picks the project up from its files:
ws project continue button-a11yws project continue button-a11y --agent codex --model gpt-5 --task 4ws project continue tetris --dir ~/code/tetris --workspace Games --layout workbench --run "cargo run"| Flag | Default | Effect |
|---|---|---|
--agent <AGENT> |
claude |
claude or codex |
--model <MODEL> |
none | Passed to the agent as --model <MODEL> |
--task <N> |
the next open task | The task number in plan.md to work on |
--dir <DIR> |
see below | Where the agent works |
--workspace <WORKSPACE> |
the workspace on screen | Put the tab in this workspace. The workspace must exist. |
--layout <LAYOUT> |
agent |
agent, split or workbench |
--split |
The same as --layout split |
|
--run <COMMAND> |
With --layout workbench: what the run pane runs, e.g. cargo run |
|
--run-rows <N> |
half the height | With --layout workbench: how many rows the run pane starts with |
--focus |
on without --workspace |
Move the view to the new tab |
Layouts:
| Layout | Tab |
|---|---|
agent |
One pane: the agent |
split |
A shell on the left, the agent on the right |
workbench |
The project’s files on the left, the agent top right, a run pane below it; see The workbench |
Two ways it runs
Section titled “Two ways it runs”How continue behaves depends on whether you pass --workspace, --layout or --split.
Without them, it opens one tab running the agent:
- Inside wrkstrms, the tab opens in the current workspace. Outside, ws starts (or attaches to) wrkstrms with the tab open.
- The agent works in
--dir, else the directory remembered inproject.toml, else the directory you ran the command from.--diris saved toproject.toml. - There is no run pane, so
--runand--run-rowsare ignored, with the warningws: --run and --run-rows apply to --layout workbench; ignoring them. - The tab is named after the project and the task number, e.g.
Button a11y · 4.
With any of them, it builds the tab in the running wrkstrms (the one your pane belongs to, or the default server, or -L <name>), and prints the new pane ids:
- The agent works in
--dir, else the directory remembered inproject.toml, else the directory you ran the command from. --dir,--runand--run-rowsare saved toproject.toml, so later runs (and the sidebar’s workbench) reuse them.- The tab is named after the project.
In both cases the tab is linked to the project.
What the agent is told
Section titled “What the agent is told”ws starts the agent with a first message, the resume prompt. It says:
- which project it’s continuing and where its files are
- to read
brief.md,plan.md,decisions.mdand the last 3 entries oflog.mdbefore doing anything - its task:
task <N>: <text>, or the next open task inplan.mdwhen none is open - the directory to work in
- to tick tasks in
plan.md, record decisions indecisions.md, and append a handoff entry tolog.mdbefore it stops
For Claude Code, ws also passes standing instructions with --append-system-prompt: that it’s working in the project, where the files are, and the same rules about ticking tasks, recording decisions and writing a handoff. Codex gets the resume prompt only.
Only claude and codex are supported. For any other agent, ws prints the resume prompt so you can start the agent yourself and paste it.
The workbench
Section titled “The workbench”--layout workbench gives a project tab this shape:
┌───────┬───────┬──────────┐│ brief │ plan │ agent │├───────┴───────┤ ││ log ├──────────┤│ │ run │└───────────────┴──────────┘- Left: the project’s files, opened in the project folder. With a vim-family editor (the command ends in
vim), one editor holds all three:brief.mdandplan.mdside by side on top,log.mdacross the bottom, with the brief focused. Other editors are given the three files. The editor is chosen as in Which editor. - Top right: the agent, continuing the project in its working directory.
- Bottom right: the run pane, in the same directory.
The run pane runs the project’s run command, such as cargo run, through a login shell. When the program exits, the pane shows its exit code and waits: Enter runs it again, and Ctrl-C leaves you at a shell. Without a run command, the run pane is a shell.
By default the run pane gets half the column. --run-rows 24 starts it at 24 rows and gives the agent the rest, for programs such as terminal games that need a minimum height.
scripts/launch-games.sh in the repo shows the workbench in use. It opens a Games workspace with one workbench tab per game, and skips games that already have a tab, so it’s safe to run twice:
ws workspace new Games --exists-okws project continue tetris --dir ~/code/tetris --workspace Games \ --layout workbench --run "cargo run" --run-rows 24Handoffs
Section titled “Handoffs”Each agent session ends by appending a handoff entry to log.md, so the next session knows where things stand. The log starts with the template, inside an HTML comment:
## YYYY-MM-DD HH:MM · <agent> · <model> · <workstream>Done: what this session finishedState: branch, uncommitted work, whether tests passNext: the next taskOpen questions: anything the next session should decide or askws reads the ## headings outside comments. The first two parts, the time and the agent, show up in ws project list, ws project show and the sidebar.
Asking for handoffs: ws project handoff
Section titled “Asking for handoffs: ws project handoff”ws project handoff button-a11yThis asks every agent in a tab linked to the project that isn’t working to write its entry now. ws types the request into the agent’s pane and presses Enter:
Write your handoff entry now: append it to <log.md path> using the template at its top (Done, State, Next, Open questions). Then stop.Working agents are skipped. The top bar says how many agents were asked. The command needs a running wrkstrms.
ws’s own entry
Section titled “ws’s own entry”If an agent’s session ends without adding anything to log.md, ws appends an entry itself, from what it knows for certain. A session starts with the first hook event from an agent in a linked tab, and ends when:
- the agent sends
SessionEnd(Codex’sInterruptonly stops a turn, so it doesn’t end the session) - the agent exits and its pane goes back to a shell
- the pane is closed
ws compares the size of log.md with its size when the session started. If it grew, the agent wrote its own entry and ws does nothing. Otherwise it appends an entry like this:
## 2026-09-25 14:10 · claude · opus · pr-101Written by ws: the session ended without a handoff entry.Session: 3f2a… (42 min); transcript /Users/you/.claude/projects/…/3f2a….jsonlFirst prompt: You're continuing the project "Button a11y". Its files are in …Last prompt: run the tests againCommits:- a1b2c3d Add focus ring to ButtonUncommitted: 2 files changed, 14 insertions(+), 3 deletions(-)| Line | Contents |
|---|---|
| Heading | Local time, agent, model, and the workstream (or the workspace, for a tab that isn’t a workstream) |
| Written by ws | How the session ended: ended, exited or was closed |
| Session | Session id, minutes, and the transcript path when known |
| First prompt, Last prompt | The first line of each, up to 120 characters |
| Commits | git log --oneline for commits made in the agent’s directory since the session started, or none |
| Uncommitted | git diff --shortstat, when there are changes |
This needs the agent’s hooks (see Agents). An agent without hooks never starts a session, so ws writes no entry for it.
In memory
Section titled “In memory”ws also indexes decisions.md, plan.md and log.md into
agent memory, so project decisions and
handoffs are searchable with everything else (ws mem search) and reach
agents with the rest of their context. The files stay the source of truth.
The PROJECTS sidebar section
Section titled “The PROJECTS sidebar section”The sidebar’s PROJECTS section, under the spaces, lists projects that aren’t archived. Each row shows:
◆and the project’s namenext: <N> · <task>, orno open tasks- at the right, the agent that wrote the last handoff entry
ws re-reads the project folders every 10 seconds, so edits to plan.md and log.md show up on their own.
With the sidebar focused:
| Key | Action |
|---|---|
Enter |
Go to a tab already on the project, or open a tab with its files |
r |
Rename the project (its slug stays) |
x |
Delete the project and its folder (asks first) |
z or Tab |
Fold or unfold the section |
Like AGENTS, the section has its own heading: click it to fold, drag it (═) to resize, and scroll with the wheel. The finder (prefix f) finds projects too; start the query with project to search only projects.
The project menu
Section titled “The project menu”Right-click a project in the sidebar for its menu:
| Item | Does |
|---|---|
| Open workbench with Claude | A workbench tab in the current workspace: the files on the left, Claude continuing the next open task at the top right, and the run pane below. Claude works in the project’s remembered dir, or in the project folder if there isn’t one. The run pane uses the remembered run and run_rows. |
| Open files | The same as Enter: the project’s tab, or a new tab with its brief, plan and log |
| Rename project… | Asks for a new name (the same as r) |
| Archive (hide, keep files) | Sets the status to done. The project leaves the sidebar; its files stay. |
| Delete project… | Asks, then deletes the folder (the same as x). Tabs linked to it keep running, unlinked. |
Managing projects from the shell
Section titled “Managing projects from the shell”ws project list # every project: status, next task, last handoffws project show button-a11y # details, open tasks and recent handoffsws project rename button-a11y "Button accessibility"ws project rename button-a11y "Button accessibility" --slugws project archive button-a11yws project delete button-a11y| Command | Does |
|---|---|
ws project list |
Lists every project, archived ones included, with its slug, status, name, next task and last handoff |
ws project show <slug> |
Shows the name, slug, status, goal, workspace, linked workstreams and folder; how many tasks are open and the first 5 of them; and how many handoffs there are, with the last 3 |
ws project rename <slug> <name> |
Changes the name. If brief.md starts with # <old name>, its title changes too. The slug stays. |
ws project rename … --slug |
Also moves the folder to the new name’s slug. Tabs linked to the old slug in the running wrkstrms follow it. |
ws project archive <slug> |
Sets the status to done: hidden from the sidebar, files kept |
ws project delete <slug> |
Deletes the project’s folder, after asking. --yes skips the question, and is required when there’s no terminal to ask in. |
ws project delete only removes a folder that is directly inside the projects folder and has a project.toml.
See also
Section titled “See also”- Workstreams: the worktrees and branches that projects link to
- Agents: hooks, which handoffs depend on
- Scripting: building workspaces and tabs from scripts
- CLI reference: every command and flag