local-first · open source · alpha

You have four agents. You don't have four heads.{}

One manager agent you talk to dispatches the rest across your repos, on your own CLIs. It writes what the fleet learns into a brief that stays in the project. Sessions live in a local daemon, so closing the window never stops the work.

Linux · macOS · Windows · unsigned alpha builds · or build it from source
fleet manager
youfix the sidebar nesting bug in workspacer
dispatchsession:5325a7de · sonnet · worktree wks/sidebar-nesting
·turn ends. no polling
[fleet] worker-finished · 6 min later
Already fixed by a394bd4d. Reverted the logic to prove the test was load-bearing, watched it fail, restored it. No change needed.
brief+1 line → ## Recently
## Now
⚠️2026-08-23 the "dead" worktree is not dead. ~884 lines of uncommitted work that exists nowhere else. No cleanup sweep may remove it.
## Recently
-2026-08-23: the grandchild nesting bug was already fixed by a394bd4d. Recorded so the stale report does not get re-fixed a third time.
Editservices/hub/internal/bus/bus.go
- func (b *Bus) Publish(ev Event) {
+ func (b *Bus) Publish(ev Event) error {
+ return b.fanout(ev)
approval · bash
go test -race ./internal/bus/...
y approve n deny esc interrupt
3 in flight·1 landed 1 waiting
The Fleet Deck: four agents as live cards showing status, model, context %, cost, and a message box on each, one mid-task
the fleet deck: status, model, context and cost for every running agent
4
agent harnesses
5
surfaces, one bus
17
plugins
MIT
licensed
100%
local-first
#built for the babysitting problem

Four agents is one too many to watch. And every one of them starts tomorrow from zero. Attention and amnesia are the same problem wearing two coats. You are the only thing holding the fleet together, and you don't scale.

attention

You can't watch a whole fleet

  • Everything waits on you. An approval here, a question there, a finished branch nobody has looked at. The only way to find out about any of it is to go and look.
  • Looking is the expensive part. Reading yourself back into a task you left twenty minutes ago costs more than the answer you came for.
  • So you run two agents instead of six, because the third one is where watching stops being free.
amnesia

Every session starts from zero

  • The agent that fixed it last week is gone. The next one has no idea the fix was tried, reverted, and tried again a different way.
  • Your instructions file covers something else. It says how the code is built. It says nothing about what happened on Tuesday, or which lead turned out to be a dead end.
  • You are the carrier. Every piece of context that moves between two sessions moves because you typed it again.

work{spacer} answers both with the same move: stop being the orchestrator. One agent takes the dispatching, and writes down what the fleet learns in the project, where the next one will read it.

#your agents forget everything. your projects don't have to.

Every project keeps a brief at .workspacer/brief.md, in the repo, in plain markdown. The manager writes it as work lands. You read the same file, and you can edit it; your words are authoritative and nothing overwrites them.

.workspacer/brief.mdwritten by the manager
read by the next agent
## Now
⚠️2026-08-23 THE "DEAD" WORKTREE IS NOT DEAD. It holds ~884 lines of real uncommitted work that exists nowhere else. Commit it, extract it, or discard it deliberately. No cleanup sweep may remove this worktree.
2026-08-23 The three "candidate fixes" are all closed and none needed the fix it proposed. An incidental candidate-fix note from a worker whose scope excluded it is a LEAD, not a defect. Always dispatch these verify-then-fix.
## Direction
-Delivery mode: local. Ship workers merge into the main checkout's current branch; push on user request.
2026-08-22 RETRACTION. "there is NO lightweight direct-completion primitive in this codebase" IS WRONG. I recorded that this afternoon as a standing infra fact. A later session checked it and found agentTitler.ts was already doing a one-shot.
## Recently
-2026-08-23: the grandchild nesting bug was already fixed earlier the same day by a394bd4d. Proved the coverage is load-bearing rather than just reading it: reverted the logic, watched the test fail, restored, confirmed green. Recorded so the stale bug report does not get re-fixed a third time.
excerpts from this repo's own brief, trimmed for width, wording verbatim
~/Work/.workspacer/brief.mdthe manager's own
## User
-PUSHES NEED AN EXPLICIT GO-AHEAD, on both projects. Workers merge locally and stop there; never push unprompted. The user approves push batches by name.
the fourth heading: standing preferences the manager honours every turn
01

Written as the work lands

The manager writes it at the moment a dispatch reports back. Not you, and not the worker that did the job. One file, two audiences: the next agent is handed it, and so are you.

02

A shape that decays on purpose

## Now is a live list, and a line leaves the moment its work lands. ## Direction is durable. ## Recently is a dated log, newest first, and the only section that grows. Three retention policies in four headings, which is three more than a chat transcript has.

03

It shrinks as well as grows

/checkpoint keeps the newest entries and moves the overflow to brief.archive.md beside it, so nothing is deleted. On the brief board, archiving is a drag. A memory that only ever grows stops being read, and then it may as well not exist.

04

Safe to write while you're writing

A manager updates a brief at the same moment a worker in that project is editing it, because the trigger for both is the same worker finishing. brief_append inserts one line under a lock, re-reading the file inside it, so concurrent updates all land and your own edits are never rewritten.

05

It records why

Rejected designs, and the reasoning that killed them. Beliefs that turned out to be wrong, marked as retractions rather than quietly deleted, plus do-not-re-dispatch markers. A task list records what happened; a brief records what not to try again.

A brief can go stale. This one went stale four times in one day.

In a fast fleet, a line can be wrong within hours: the thing it describes gets fixed by someone else while the note still says it's broken. This repo's own log numbers its misses, and each one cost a dispatch:

“THIRD already-fixed item today”
“FOURTH already-fixed item today”
“REPORT WAS STALE, NOTHING TO FIX, no commit”

So the system builds for correction, not for correctness.

  • Every entry is dated and carries a disposition marker: ✅ closed, ⚠️ waiting on you, ❌ retracted. A wrong line gets contradicted in place, not deleted.
  • Archiving keeps it short enough to read, which is the only thing that keeps it accurate.
  • A lead is dispatched verify-then-fix, never blind-fix. The standing rule: “the cost of a clean no-op is one cheap worker, the cost of a blind fix is a conflicting re-implementation”.
  • Being wrong gets written down too. One worker reverted a fix just to prove the existing test caught it, then recorded the result so the stale report would not be fixed a third time.

The brief is operational memory: who did what, what was decided, what not to try again. It is not a map of your code. work{spacer} doesn't read, index or understand your repo, and when a finding is architectural the manager is told to file it into whatever code-context tooling the project already has. Every brief is also a board you can drag, and a tab on your phone.

#a manager you talk to, not a board you drive

Open the Fleet Manager from the Overview and it comes up as a real agent session on your own CLI, rooted at the parent directory of your projects, holding the work{spacer} tools at the operator tier. You give it work in a sentence and it does the dispatching.

01_delegate

It never does the work

No edits, no builds, no long investigations of its own. Staying available is its job, so every turn ends in seconds and you can always reach it. It picks the model to match the work: a cheap fast one for a rename, the strongest one for a gnarly bug, and never a frontier model on a chore.

02_never_poll

It stops after dispatching

No status loops, no “just checking on the workers”. That isn't monitoring, it's a hang, and it locks you out of the conversation. A turn that ends right after dispatching is the manager working correctly. It can afford that because it doesn't have to look: it gets woken.

03_keep_the_brief

It writes the result down

When a dispatch lands, one dated line goes to the top of that project's ## Recently and the matching line leaves ## Now. Ask it where things stand and it answers from the briefs, including its own, which is how it survives being restarted.

The wake is the mechanism. Five things can reach the manager, and they all arrive through one channel as an ordinary message it can act on:

worker-finisheda dispatch went idle. The wake carries its final message, plus its validated JSON result if the dispatch asked for one. A backstop sweep catches any finish the live signal missed.
worker-escalateda worker stopped on needed authority or a decision. Its strict six-key payload — type, status, reason, requiredAuthorityOrDecision, changed, nextAction — arrives as a validated card instead of a contradictory missing-result error
blockeda worker is sitting on an approval or a question. The manager can answer it, or escalate to you
thresholda limit the manager armed for itself on tokens, cost, or time. In-memory and one-shot by design: it fires once, and it's gone if the app restarts
progressthe worker's own mid-task note, in its own words: “finished phase 1”, “the approach you gave me is wrong”. Rendered as a report, never as a finish

Wakes are parent-keyed, so replacing a manager means the successor adopts its in-flight workers as its first act. Otherwise their results report to a session that's gone. One limit: that manager→worker graph lives in the desktop app's memory. Sessions survive a restart; the parentage doesn't.

The Fleet Manager pane mid-conversation: a one-sentence ask, the manager's dispatch summary naming each worker by session id and worktree, and a [fleet] worker-finished wake card below it carrying that worker's structured result
you ask in a sentence; it dispatches, stops, and is woken when the work lands
#how a task lands

Every dispatch is one of two shapes, and the manager picks. A ship task changes code. A scout task only looks. They are dispatched differently, and that difference is enforced by the hub rather than requested in a prompt.

ship task

Its own worktree

Code changes go to a fresh wks/ branch in a worktree under ~/.workspacer/worktrees, so two agents in one repo never collide. node_modules is linked in and your setup hooks run. It isolates branches and files, not your machine: same dependencies, same ports, same network.

scout task

A read-only token

An investigation is dispatched at the view tier: the hub mints that session its own capability token whose allowlist has no spawn, no approve, no shell. The hub refuses the call itself, so no prompt has to be trusted.

delivery

PR, or local merge

Each project carries how work should land, either a pull request or a local merge once you approve. The manager bakes it into the worker's first message so the instructions and the outcome can't diverge. It is advisory to the manager, not a hard gate.

result

A result you can parse

A dispatch can carry a JSON Schema. The worker ends with a fenced result block, it's validated before the manager ever sees it, and you get a card. If it doesn't parse, the wake says so, instead of leaving the manager to guess that the worker reported nothing.

A validated wks-result rendered as a card in the manager's transcript: a merged flag and commit, the worker's own caveat called out, a changed-files disclosure, and its checks, follow-ups and root cause each under their own heading
a worker's wks-result, validated before the manager sees it and rendered as a card

You stay in the loop by default: the manager resolves in-scope authority for actions inside the repo it dispatched to, and asks you before anything destructive, external, credential-touching, cross-repo, or otherwise unauthorized. Per-project yolo turns prompts off for that project's workers only.

#every worker is a real session

Each one is a full agent you can open, drive, interrupt, re-model and approve, or ignore and let the manager handle it. They are not invisible subagents that return a paragraph and vanish. The delegation is optional; the sessions are real either way.

ambient_awareness

Know who needs you

A status dot per agent and a live N waiting count. The inbox collects everything waiting on you, and stays quiet for the agent you're already watching.

The inbox docked over the fleet: two finished agents waiting for review with open / respawn / snooze / dismiss actions
agent_pane.gui

A real GUI for your agent

Approve or deny in one key, answer questions inline, read diffs as they land, and attach files by drag, paste, or picker.

An agent pane in GUI mode: a work log of the agent's edits, each rendered as an inline diff, with the rest of the fleet's live cards in the sidebar beside it
review.diff

Review and ship

Git status and a unified diff with a file tree, right where the agent finished. Stage, commit, and push without dropping to a shell.

The Review pane: the three files the agent changed as a tree, beside the unified diff of the selected one, with stage, commit and push controls
The sidebar with three dispatched workers indented under the Fleet Manager that dispatched them — one mid-tool-call, one finished, one holding an approval with inline Approve and Reply buttons — and an unrelated agent at top level below them
a manager's crew nests under it, and an agent you started yourself sits beside it
#four harnesses, one fleet

Spawn any of them from the same dialog, yourself or through the manager. Pick a repo, a model, a profile, and go; they all land in the same fleet, on your subscriptions.

Claude Code
hooks + jsonl + statusline
Codex
codex app-server
OpenCode
opencode serve
GitHub Copilot
beta
π
Pi
beta
#and it's still an IDE

Everything an agent needs around it, in the same window. Each agent keeps its own tabs and panes in the daemon, separate from the window, so you can close the app and nothing dies.

terminala real xterm and PTY shell scoped to the agent cwd
editorsandboxed CodeMirror with project search, optional vim mode, and a file tree, or your own $EDITOR in a terminal pane
reviewgit status, diff, stage, commit, and push in the app
boardevery brief the fleet keeps as topic cards, one lane per project. The drag does the work: dropping a card on Archive moves it out of the brief and into brief.archive.md
browserembedded webview with nav, bookmarks, app mode, themed to match
agentsfleet monitor plus live watch panes for subagents and workflows
pluginsinstall, remove, and health-check sidecars that bring their own panes

Arrange it your way: fleet and focus modes flip in one key between watching every agent and quieting the sidebar to the one you're in, without remounting a pane. Plus a command palette, remappable keybindings with leader chords, 18 themes and a theme maker, and layout templates, all in the docs.

#one fleet, every surface, one bus

The same fleet from the desktop, a browser, your phone, a terminal, or another agent. They all talk to the same bus, so they never disagree about what the fleet is doing.

remote

Check on it from anywhere

  • A phone PWA at /m you install from a QR code. It reads as a fleet manager: Fleet, Waiting, Flows, Briefs and Dispatch tabs, with big approve buttons for clearing approvals from the couch.
  • The full app over the bus, the real renderer at /app, with tmux-style layout mirroring between desktop and web.
  • No desktop required. workspacer serve runs the same fleet on a headless box, though the box does have to be on. Nothing runs in a cloud, because there isn't one. Sharing is off by default, and tokens can be scoped view-only.
  • Two machines, one fleet. Federate your desktop's hub with your laptop's and every client shows both machines' agents side by side, approvals routed to the right box, offline peers marked rather than vanished.
The phone PWA Fleet tab: All / Waiting / In flight / Landed filters, and a card per agent with a static identity row (project, task, role) and a separate state row (status, model, context meter, cost) The phone PWA agent transcript, opened from a fleet card: collapsible work cards, diff counts, model and permission pills, and a composer
control_plane

Or drive it from a tool

  • The whole fleet over MCP. List agents, read transcripts, dispatch, prompt, approve, answer, append to a brief, even navigate the app. Any MCP client can run the place. This is the same facade the manager holds, and you can hand it to anything.
  • Scoped, per session. Every agent that gets the tools gets them at a tier: view to observe, triage to clear approvals, operator to dispatch. The hub enforces the tier on every call, so the prompt never has to. Plugins can contribute tools of their own.
  • Jobs that run without you. Recurring or one-off: dispatch an agent with a prompt, run a command, or call a capability. Fire it on a timer, at a time of day, or once. A job can run a script first and hand the output to the agent, and skip waking a model at all when the script finds nothing. The hub owns them, so they keep firing with the app closed. Docs →
  • Or skip the GUI. wks-tui is a Rust client on the same bus, with a ctrl-k palette and vim keys.
desktop client

Electron + React

The primary GUI. Spawns the daemons, renders the panes, forwards events to every client.

session daemon

claudemon [rust]

Owns the sessions and PTYs, runs the per-provider adapters, streams conversation and usage.

control plane

hub [go]

The event bus, capability router, token tiers, plugin system, jobs, and the MCP facade.

Sessions live in the daemon, not the window. Closing a window never kills an agent, and quitting the app doesn't either. Read the internals →

#what this is, and what it isn't

work{spacer} is alpha and built in the open by one person. Here is the list you'd otherwise find out the hard way. Every line of it is something we'd rather tell you now than have you discover in week two.

Alpha, and it means it. Sharp edges, breaking changes between releases, and features that land faster than the docs describing them.
The builds are unsigned. There is no code-signing certificate yet. Windows warns you and lets you click through; macOS is stricter, and you have to clear the quarantine flag by hand before the app will open. The download section has the command.
It's for one person on their own machine. No accounts, no multi-user model, no client separation. The tokens are capabilities, not logins. Federation joins your desktop and your laptop, not a team.
Bring your own CLIs and subscriptions. work{spacer} drives Claude Code, Codex, GitHub Copilot, OpenCode and Pi. It doesn't replace them, resell them, or proxy them.
Nothing runs while your machine is off. workspacer serve is headless, but something still has to be powered on. Your code never leaves it either.
Worktrees isolate branches, not environments. Agents share your machine, dependencies, ports and network. If you need a sandbox, this isn't one.
The harnesses aren't at parity. Codex and GitHub Copilot cost figures are estimates, Pi gets no work{spacer} tools, and Claude Code is where the deepest integration lives.
The manager→worker graph doesn't survive a restart. Sessions do, and the briefs do; the parentage is re-established by hand or by a successor adopting.
Delivery mode is advisory, not a gate. It tells the manager how to land work; it does not stop a worker from doing something else.
Push notifications are still being hardened. The phone PWA can wake you when an agent needs you, but don't bet the farm on the delivery yet.
It doesn't read your code. No indexing, no code intelligence, no answers about your repo. That's a different kind of tool, and work{spacer} is happy to dispatch agents that use one.
MIT, and the whole thing is here. Desktop, both daemons, the TUI, the plugin system and this page. Fork it, gut it, ship your own.
open source
If you don't like something, change it.
~/code
git clone https://github.com/DJTouchette/workspacer
cloned into ./workspacer
make setup
desktop, claudemon (rust), hub (go): ready
./dev
work{spacer} dev → electron + daemons up
  • +Drop in a plugin. A manifest gets you a supervised sidecar, its own panes, and your theme. No fork required.
  • Drive it over MCP. The hub exposes the whole fleet as tiered tools, so anything that speaks MCP can run it, scoped to observe, triage, or operate.
  • Or skip the GUI. wks-tui is a Rust client on the same bus, with a ctrl-k palette and vim keys.
MIT licensed · alpha · unsigned builds

Point it at your projects and let the manager dispatch.

work{spacer} is in alpha and the builds are unsigned, so expect sharp edges and an objection from your OS the first time you open it. Installers for Linux, macOS, and Windows, plus a standalone server bundle for a box with no desktop.

On macOS, one extra step. The mac build is Apple Silicon only, and it is neither signed nor notarized, so Gatekeeper refuses to open it and right-click › Open will not get you past that. Open the .dmg, drag Workspacer to Applications, then clear the quarantine flag yourself:
xattr -dr com.apple.quarantine /Applications/Workspacer.app
installers for linux, macos, and windows, plus the standalone server bundles, land on the releases page.