overview

work{spacer} is a local-first control plane and IDE for coding agents: a place to run many long-lived agent sessions side by side, watch every one of them, and review and ship the code they write. You run it on your own machine. Nothing here is a hosted service.

the problem

One claude session is easy. You sit in the terminal it runs in and watch it. That breaks the moment you have more than one going at once. Tabbing between terminals, you lose track of which agent is working, which one is blocked on an approval, and which one finished ten minutes ago and is sitting idle. You can't babysit a whole fleet at the same time, and most of the switching you do doesn't pay off.

work{spacer} keeps the ambient state of every agent in front of you so you only switch when it's worth it. The sidebar shows a per-agent state dot, context %, and token/cost, with an "N need you / N working" header. A Triage Inbox drawer collects the approvals and questions waiting on you across all agents, and you can jump straight to the next agent that needs you. OS notifications fire on needs-approval / needs-input (and optionally on done), suppressed for the agent you're already watching. The point is to make switching a decision you make, not a thing you do constantly out of fear of missing something.

the mental model

The core unit is the agent workspace:

  • one agent = one long-lived session = one workspace. Each agent is a single claudemon session, keyed by its working directory. It has its own tabs and panes (the Claude pane, plus terminals, a browser, a git review/diff pane, a per-agent notes scratchpad, and more).
  • sessions live in the daemon, not the window. The session runs in claudemon, independent of any UI. Close the desktop window and the agent keeps running. A session spawned from somewhere else (the MCP facade, another agent) gets auto-adopted and shows up as a card on its own.
  • one card per session, everywhere. The same agent shows up exactly once whether you're looking from the desktop app, the terminal client, or a remote device, never doubled, never out of sync.

You move between agents in the sidebar, and flip the whole chrome between two UI modes: fleet (the full cockpit, the default) and focus (a distraction-free chat rail with a compact attention badge). Switching modes never remounts your live panes. There's also a Fleet Deck, a cross-agent radar of live agent cards, as a higher-altitude view over the same sessions.

what's running on your machine

You launch one thing, the desktop app, but a few processes run behind it. You never start them by hand; the app does, and it restarts them if they crash.

  • the desktop app: the window you click. It's just a client. It doesn't hold your sessions, it displays and drives them.
  • the session daemon (claudemon): a background process that actually owns every agent session and terminal. It keeps running whether or not a window is open, which is why closing the window never kills an agent.
  • the hub: a background bus that lets other clients, the terminal app, your phone, a second PC, see and drive the same fleet, and that hosts plugins.

You can also drive the same fleet from a terminal with wks-tui, or from a phone/laptop over the network (see remote and multi-client). Because every client talks to the same hub and the same session daemon underneath, a session started in one place can be watched and driven from anywhere else.

getting started

Install work{spacer} and spawn your first agent. The getting started section walks through it. If you're building from source or writing a plugin, see the build & internals page instead.

getting started

This walks through a first run: get the app, spawn your first agent, and find your way around. Building from source and packaging are covered on the build & internals page.

install and launch

Install the desktop app and open it. On first launch it starts its background daemons for you, wires Claude's hooks into ~/.claude/settings.json automatically (that's how the live conversation view, state dots, and approval forwarding light up), and, if there's nothing to restore, shows the session picker, your list of saved sessions, with "new session" to start clean.

One concept to keep in mind from the start: your window is disposable. Agents run in the background session daemon, not in the window. Close work{spacer}, your agents keep working; reopen it (or the terminal app, or your phone) and you reconnect to the same live sessions. The cockpit is detachable from the planes.

spawn your first agent

Hit Ctrl+Shift+N (or the + in the sidebar) to open the spawn dialog. Pick a working directory (the project you want the agent to work in), optionally name it, choose an agent backend (Claude Code, Codex, or OpenCode), and spawn. The agent shows up as a card in the sidebar and opens its workspace. Type in the composer at the bottom and you're off.

Spawn a few more in other directories. Each one is its own workspace with its own tabs and panes. The sidebar shows every agent's live state at a glance, and the "N need you / N working" header tells you who's blocked on a decision. See running agents for every field in the spawn dialog.

find your way around

  • Command palette (Ctrl+Shift+P): a fuzzy launcher for everything, new panes, dialogs, your saved prompts and skills, plugin panes.
  • Switch agents in the sidebar, or Ctrl+Tab / Ctrl+Shift+Tab.
  • Jump to whoever needs you (Ctrl+Shift+Space), or open the Triage Inbox (Ctrl+Shift+I) to clear approvals and questions across the whole fleet like email.
  • Fleet Deck (Ctrl+Shift+F): a cross-agent radar showing every agent at once, with the ones that need you floated to the front.
  • Keyboard cheatsheet: press F1 any time.

The rest of these docs go pane by pane and feature by feature. A good order from here: running agentsthe agent paneattention and notifications.

running agents

work{spacer} drives four coding agents, not one: Claude Code, Codex, OpenCode, and Pi (beta). You pick the backend when you spawn an agent, and the rest of the app, the sidebar, the Fleet Deck, the inspector, the conversation view, treats them the same way: same state dots, same live transcript, same token/cost readouts, same approval prompts.

the spawn dialog

Open it with Ctrl+Shift+N or the + in the sidebar. It always asks for:

  • Working directory: type a path or hit Browse…. This is the agent's project folder, and the default folder for every pane it opens (its terminals, its git review, etc.).
  • Name (optional): defaults to the folder name. You can rename it later.
  • Agent: pick Claude Code, Codex, OpenCode, or Pi (beta).
  • Permissions: a permission-mode picker, shared across providers. The shipped default is Full access (bypass permissions), so a fresh agent runs without approval prompts; pick a stricter mode and approvals come to you instead. You can also switch the mode live later, from the composer.

If you pick Claude Code, a few more fields appear:

  • Resume session (optional): if you've run Claude in this folder before, you can pick a previous conversation to continue instead of starting fresh. Each is listed by how long ago it ran plus a short summary. The default is to start fresh.
  • Model: defaults to claude.defaultModel from config (shipped: opus[1m]); pick a Latest alias, pick a model you've used before, or type a custom id (e.g. opus).
  • Profile (optional): a saved bundle of model + config dir + extra args + a default set of MCP servers (see configuration). Default if you don't pick one.
  • MCP servers (optional): tick which MCP servers from your Library to expose to this session. Only the ones you check are loaded.
  • Transport: headless (the default) drives Claude over its stream-json protocol via claudemon — a structured GUI-only session with no terminal view. terminal runs the classic interactive CLI in a PTY, giving you both the GUI and a raw terminal view.

For Codex, OpenCode, and Pi the extra Claude fields drop away. You get a free-text Model (optional) in that provider's own id format (e.g. gpt-5.4 for Codex, anthropic/claude-sonnet-4 for OpenCode; blank = the provider's default), and the same permission-mode picker, whose bypass option is that provider's YOLO mode: on, the agent accepts every approval itself; off, approvals come to you.

Enter in any field spawns; Esc cancels.

terminal-style vs managed agents

There's one difference worth knowing between the backends, in how their pane behaves:

  • Claude on the terminal transport has both a rich GUI conversation view and a raw terminal view, and you can flip between them (see the agent pane).
  • Everything else is managed — Claude on the default headless transport, Codex, OpenCode, and Pi: the session daemon drives the agent's own protocol directly and renders the conversation, so they're GUI-only, there's no terminal to flip to. They still surface the same state, telemetry, and approval prompts.

Whatever the backend, approvals work the same: when an agent wants to run something it can't on its own, the request comes to you (in the pane, the sidebar card, or the Triage Inbox) unless that agent is in a bypass / YOLO permission mode — which is the shipped default, so tighten the mode at spawn (or live from the composer) if you want to be asked.

agents started elsewhere show up automatically

You don't have to start every agent from the window. A session spawned by a supervisor, by the MCP facade, by the terminal app, or on another machine, gets auto-adopted: it appears as its own card as soon as it's running, deduped so you never see the same session twice across clients. If it was spawned as a child of another agent, it nests under that parent in the sidebar.

supervisor agents

A supervisor is a Claude agent that can see and coordinate the rest of your fleet (it's given the work{spacer} tools). You spawn one from the Ask pane ("ask the fleet"). It renders nested under whatever it's watching, fleet-wide, or scoped to a single agent. Use it for things like a cross-fleet standup, a triage sweep, or a cost rollup. See the Ask pane for the presets.

respawn, terminate, rename

  • Respawn restarts a stopped agent with the same folder, backend, profile, model, and settings, and resumes the prior conversation when it can. Its panes re-point at the fresh session.
  • Terminate ends the agent's session and removes the card. The pinned Overview workspace can't be terminated. Closing an agent's last tab or pane also terminates it.
  • Rename changes the display name (it defaults to the folder name).

Because sessions live in the background daemon, switching between agents never starts or stops one. Only an explicit terminate (or the agent ending on its own) tears a session down.

the agent pane

The agent pane is where you actually talk to a coding agent. Each agent workspace has one, backed by a long-lived claudemon session that lives in the daemon, not in the window. Close the tab, the session keeps running.

It has two surfaces: a rich GUI view and a raw terminal view. A Claude agent on the terminal transport gets both and can flip between them. Everything else — Claude on the default headless transport, Codex, OpenCode, and Pi — runs as a managed session through claudemon's adapters, so it is GUI-only (no PTY to show). Plain terminal-only agents lock to the terminal view.

The toggle lives at the bottom-right of the pane: GUI / Term. It only appears when the provider offers both surfaces. Both views stay mounted at once, so switching never loses your scroll position, your draft, or the PTY.

gui mode vs terminal mode

Terminal mode is the real thing: a live xterm view of the agent's PTY, exactly what you'd see in a normal terminal. Standard terminal keys work, plus:

  • Ctrl+C copies when there's a selection, sends SIGINT when there isn't.
  • Ctrl+V / Ctrl+Shift+V paste (bracketed-paste aware).
  • Ctrl+Shift+C copies the selection.

GUI mode is a parsed conversation view built from the session's hooks and transcript. Instead of raw scrollback you get:

  • Messages as a clean back-and-forth timeline.
  • Tool runs collapsed into a work card between the things the agent said. A card reads as "N steps", shows files-changed counts and a +added −removed line, and expands to the individual steps. The most recent card stays open; older ones collapse.
  • Inline diffs for Edit / MultiEdit / NotebookEdit and inline read previews, rendered right inside the expanded work card.
  • A Changed files card at the end of each completed turn: a frozen per-turn summary of what that turn touched, expandable to the diffs.
  • Approvals and questions docked where you're about to type (see below).
  • A composer at the bottom and an optional inspector rail on the right.

Which view opens first: a pane spawned with a seed prompt always opens in GUI, and a managed (headless / Codex / OpenCode / Pi) session is always GUI. For a terminal-transport Claude pane it honours claude.defaultView, which defaults to terminal. Set the default under Settings → Session, or via the config key claude.defaultView (gui | terminal).

the composer

The composer is the input box at the bottom of the GUI view. It's a multi-line, auto-growing textarea (grows to about 8 lines, then scrolls).

  • Enter sends. Shift+Enter inserts a newline. Those two hints sit right under the box.
  • The send button () is shown by default; hide it with ui.showComposerSend: false and Enter still sends.
  • Sends go through claudemon's message endpoint, which writes the whole line atomically and buffers a message sent while the agent is mid-turn or still starting, flushing it the moment the prompt is ready. So you can fire a follow-up without waiting.
  • Your message shows up immediately (optimistic), before the transcript catches up.

Session pills. Above the box sit three control pills — the model, the reasoning effort, and the permission mode (e.g. "Opus 4.5 ▾ · High ▾ · Full access ▾"). Each is a live switch: change the model or the permission mode mid-session and it takes effect without a respawn, on every provider that supports it.

Attaching files. Three ways, all land as chips above the box:

  • Drag and drop anywhere onto the active pane.
  • Paste file paths into the composer.
  • The + button (in the composer or the status bar) opens a file picker rooted at the agent's cwd.

Attaching a file auto-switches you to GUI mode. Files are prepended to your message as a path prefix when you send.

Cancel. While the agent is working, a streaming indicator shows with a Cancel button. Esc cancels too (it sends Escape to the session). Cancelling suppresses the streaming UI until the next activity cycle.

the inspector rail

A 320px rail on the right edge of the pane. Toggle it with the PanelRight button in the status bar or with Ctrl+Shift+E (configurable as the toggle-inspector shortcut). Its open/closed state persists across sessions. The rail is a sibling of the content area, so it stays put whether you're in GUI or terminal mode.

Five tabs, each with a count badge:

  • Files: every path the agent touched this session, most-recent first, with a +/~ marker (write vs edit) and a ×N touch count. Click a row to open it in the Review pane.
  • Plan: the agent's live todo list, with a done/total progress badge.
  • Flows ("Workflows"), workflow runs, with a "N running" header when any are live.
  • Agents: subagents spawned this session.
  • Usage: model, a context-window bar, 5-hour / 7-day / monthly rate-limit bars (with reset times when known, plus any rate-limit warning), and totals for input tokens, output tokens, cost, and tool calls.

The rail opens on whatever's active: a running workflow wins, then a running subagent, else Files.

subagent and workflow telemetry

Subagents and workflows are surfaced live from the daemon's workflow watcher, both inline in the timeline and in the inspector rail.

  • Subagent rows show a spinner while running (then a ), the agent type and description, and per-agent tools and tok counts. While running, the last tool it ran shows on a line underneath.
  • Workflow run cards show a finished/total agents count, total tokens, and elapsed time. Expand to see the agents, grouped by phase once phase titles land. Each agent row shows a status icon (queued / running / failed / done), model, tokens, and duration, plus its current tool while running. A run auto-collapses when it finishes cleanly; a failed run stays open so you can see which agent failed.

Inline in the timeline, the work card that spawned a run surfaces that run's rich card instead of a flood of raw tool calls. Anything a hook reports before the transcript catches up shows in a live work-log section at the bottom.

The pane's bottom status bar also shows running counts: total tool calls, live subagent count, and attached-file count.

approvals and questions

When the agent needs a permission or asks you something, it surfaces in the needs-you dock pinned right above the composer (not buried in scrollback). The approval gate is enabled per session as soon as it connects, so PreToolUse hooks get parked for the UI to resolve.

  • Approvals show the tool name and its input as JSON, with Allow and Deny buttons. One click resolves it.
  • Questions (the agent's AskUserQuestion) render as a picker: numbered option buttons (press the number, or click), multi-select where the question allows it, plus an "Or type a custom answer" box for single questions. Multi-part questions get a stepper with dot progress and a back affordance. There's also a Decline & stop button that cancels the agent's turn instead of answering, for when the question itself is off the rails.

A pending question always wins over an approval, since the agent can fire both in the same turn and the approval is stale once the picker is up. Responses dismiss optimistically so the UI feels instant while the answer round-trips. Every resolved question leaves a persistent answered card in the scrollback — a durable record of what was asked and what you chose (including a decline).

You don't have to be in the pane. The same prompts also appear in the sidebar agent cards and in the top-level Triage Inbox drawer, both reusing the exact same ApprovalPrompt / QuestionPicker so resolution behaves identically wherever you answer. To get to the next agent waiting on you without hunting, press Ctrl+Shift+Space (the next-attention shortcut).

handing off to another agent

Any agent can hand its work to a fresh one, including across providers (Claude → Codex, Codex → OpenCode, whatever). A handoff asks claudemon to build a brief — a structured summary of the task, the state, and the loose ends, written to ~/.workspacer/handoffs/ — either deterministically from the session or authored by the outgoing agent itself. The successor spawns with its composer pre-filled to read the brief, so it starts with context instead of a cold prompt.

pane types

Every agent workspace is a set of tabs, and every tab holds one or more panes. A tab with several panes auto-tiles them. Adding or removing a pane never re-parents the others, so live panes (terminals, agent sessions) keep running. Most panes are opened from the command palette, from a pane's split button, or from a sidebar action. Here is every pane type you can open.

terminal

A real shell in an xterm.js view backed by a PTY in claudemon. The PTY spawns in the agent's working directory by default, so it shares the cwd of whatever agent the tab belongs to. You can set a different shell per pane (shell), and the per-directory script buttons drop a command straight into a fresh PTY (initialCommand).

The PTY lives in the daemon, not the window. Closing the window or switching agents does not kill it. Off-screen terminals stay mounted (their scrollback survives); the disposal-on-hide optimization exists in the code but is gated off by default to avoid losing scrollback.

browser

An embedded Electron <webview>. Two modes:

  • Browsing mode (default): a URL bar with back/forward/reload, plus a bookmarks row. Bookmarks and the homepage come from config.browser (Settings → Browser). Bare hostnames get https:// prepended.
  • App mode (appMode): the chrome is hidden, so the page fills the pane like a standalone app. App-mode (and plugin) webviews get theme injection: the current --wks-* tokens are pushed in via insertCSS + executeJavaScript, so the embedded page matches your theme. Regular browsing is never restyled.

Hibernation (hibernated) parks a pane so it stops rendering until you focus it. This is renderer-only today. The browser pane is also the substrate for plugin panes (see below).

editor

For reading code an agent just wrote and making quick edits. There are two engines, set in Settings → Editor (config.editor.engine):

  • codemirror (default) opens the sandboxed editor plugin (workspacer.editor), not a built-in pane. It is a plugin webview rooted at the project. Open it with ctrl+shift+d (open-editor hotkey), from the command palette ("Open Editor"), or by clicking a file in the Review pane's tree (which routes through requestOpenInEditor). It runs CodeMirror 6 in the webview with: line numbers, active-line highlight, history (undo/redo), multi-selection, bracket matching, auto-close brackets, code folding, selection-match highlight, indentOnInput, and syntax highlighting for js/ts/tsx, py, rs, go, json, css, html, md, c/cpp, java, php, xml, yaml, sql. Keys: Mod-s save, Mod-p project search, Mod-b toggle sidebar, plus the standard search keymap (find/replace). It has a lazy file tree, project-wide search (search.project, ripgrep-backed via a hub capability), and external-change detection: the open file is watched (fs.watch), and if the agent rewrites it the editor reloads when clean or prompts when dirty. Plugin settings: vimMode, tabSize (1–8), lineWrap. It reaches the filesystem only through agent-cwd-scoped hub capabilities (fs.read/write/listEntries/watch), not direct disk access.
  • terminal opens your $EDITOR in a PTY pane instead. Command defaults to nvim (config.editor.terminalCommand); it runs <cmd> <file> when a file is given, else <cmd> . in the cwd. This is the real-IDE path for heavier work.

(A leftover codemirror editor pane from an old saved session just shows a note pointing you back to the plugin.)

review / diff

A git review surface bound to the agent's cwd. It shows porcelain status split into staged / unstaged / untracked sections in a file tree, with per-file numstat (added/removed lines). Selecting a file renders a unified diff (untracked files render as an all-added diff via --no-index). Very large diffs (over ~1.5M chars) need an explicit click to render.

Mutating git actions run from here against claudemon's git API: stage / unstage (a path, or everything when no path is given), commit (with a message box), and push. Failures (nothing staged, no upstream, conflicts) surface git's stderr verbatim. Click a file to open it in the editor.

notes

A per-agent markdown scratchpad. Whatever you type is persisted onto the pane config and saved with the session, like a browser pane's URL. It survives agent switches and restores on reload (debounced 400ms, flushed on unmount so nothing is lost). Three views via the toolbar: write, split, preview (your choice is remembered). Tab inserts two spaces instead of leaving the textarea. It uses the app's own lightweight markdown renderer, no external editor.

library

CRUD for reusable items you fire into sessions. Four kinds: prompt, skill, agent, mcp. Each item has a scope (global or project), tags, a body, and an action: insert (drop the text into the composer), spawn (start a new agent from it), or copy. MCP items carry a server config (stdio / http / sse, with command/args/env or url/headers) and can be attached to an agent at spawn.

Bodies support variable templating, resolved at insert time:

  • Auto context vars: {{cwd}}, {{sessionId}}, {{selection}}, {{clipboard}}.
  • Prompt-for-input fields: {{?Label}}, with a default ({{?Label:default}}) and a type suffix: |text (single line), |area (paragraph, the default), |select:a,b,c (dropdown, first is default), |toggle:on,off (checkbox). These are collected in a small form before the text is used.

There's also a quick-pick library panel (Ctrl+L).

analytics

Cost and token history, read from claudemon's per-session SQLite store. Top tiles show totals: sessions, cost (USD), input/output tokens, tool calls, duration, workflow runs. Below that, breakdown buckets by project, by model, and by day, each with sessions / cost / tokens. Then a list of recent sessions with model, branch, cost, tokens, duration, and status (active / ended). Model names are shortened (claude- prefix and trailing date dropped).

overview

The cross-agent dashboard, lives in the pinned global "Overview" workspace. It shows:

  • A rate-limit card: your account-wide 5h and 7d usage windows (percent used + reset time), scanned across all live session snapshots (they're account-global, so it picks the worst one).
  • Cross-agent stats and plugin status (the same sidecar.* health colors as the Plugins Manager).
  • Favourite and recent directories to spawn into. Star a dir to favourite it; clicking one publishes a command.spawn_agent on the hub bus to start a new agent there. Favourites/recents live in config.directories.

agents / agent watch

Fleet-monitoring panes. The Agents pane is a live timeline of a session's subagents. Clicking a running subagent or workflow in the inspector opens a dedicated watch pane that follows just that run — its status, tools, and tokens — while the parent keeps working. There's also a standalone inspector pane: the same card as the inspector rail, pinnable as a tab (it's what a Fleet Deck card expands into, too).

markdown preview

A read-only rendered view of a markdown file (mdpreview), opened from file links in chat and tool cards. File paths in the conversation are clickable: markdown opens here, anything else opens externally or reveals in the file manager.

ask ("ask the fleet")

Turns a question into a supervisor agent. You type a question (or pick a preset chip: Standup, Triage, Audit, Cost), and it spawns a Claude with the workspacer MCP facade attached so it can observe and coordinate the rest of the fleet. The supervisor is rendered nested under its parent. If the pane was opened scoped to one agent (scopeAgentId), it pre-fills "About \<name\>: " and spawns the supervisor as that agent's child; otherwise it's fleet-wide. Existing supervisors are listed so you can jump back to them. The presets reference each session as session:<id>, which the pane resolves into clickable links.

plugins manager

Lists installed plugins with live sidecar health (healthy/running green, unhealthy amber, crashed red, stopped/disabled grey) fed from sidecar.* bus events. Per-plugin actions: Update, Enable/Disable, Remove. Install new plugins (from a GitHub URL or tarball) via the install dialog.

plugin pane

A generic webview host for a plugin's own UI, built on the browser pane. This is how any plugin surface appears (agent dashboards, the rules-engine editor, the sandboxed editor, etc.). For an agent-scoped pane (it has both a pluginId and a cwd), the pane mints an ephemeral bus token confined to that agent's directory on mount and revokes it on unmount, so the plugin reaches only that project's files instead of the broader static per-plugin token. If there's nothing to scope, or minting isn't available (web build, hub momentarily down), it loads the URL with its baked-in static token, so the webview always loads. Plugins get --wks-* theme tokens and their settings injected.

settings

A searchable settings pane with these sections: Appearance, Layout, Terminal, Keybindings, Notifications, Session, Browser, Editor, Apps, Claude Profiles, Supervisor, Updates, Plugins. All settings persist to config. The search box filters sections by keyword.

bottom terminal drawer

Not a tab pane. A VS Code-style integrated terminal that slides up from the bottom across the full content width (it follows the sidebar's left edge). Toggle it with Ctrl+\ (also a close button; Esc does not close it, since Esc is a real key inside a terminal). Height is 40vh`. It spawns in the current cwd, captured at first open. It's mounted on first open and then kept mounted forever (toggling just slides it off-screen), so the PTY keeps running and scrollback survives.

layout and navigation

work{spacer} keeps every agent's tabs and panes mounted at all times. The whole navigation layer (UI modes, the Fleet Deck, the command palette, keybindings) is about getting you to the pane you want without ever tearing down the live session underneath it.

ui modes

One agent's tabs render as a horizontal strip of tab cards, the classic editor layout. On top of that sit two UI modes, lenses that decide how much chrome you see. Set with config.ui.mode (default fleet), toggled with the toggle-ui-mode action (default Ctrl+Shift+M):

  • fleet (default): the full cockpit — full sidebar, inspector rail, Fleet Deck, the full attention chrome.
  • focus: chat-first — the sidebar collapses to a thin rail, the inspector rail and Fleet Deck hide, and attention shrinks to a compact "N need you" badge you can still click to triage.

The important guarantee: switching UI mode never remounts a pane. A mode only changes which chrome is visible around the same mounted tab cards, so a running terminal, a streaming conversation, or a loaded browser keeps going untouched. Inactive agents stay mounted too (hidden with display:none), so switching agents doesn't remount either.

the fleet deck

The Fleet Deck is a cross-agent radar, a separate altitude that sits orthogonal to UI mode. Inside an agent you arrange that agent's panes; the deck shows all your agents at once.

  • Stored as config.panes.viewLevel: piloting (inside one agent, the default) vs fleet (the deck).
  • Toggle with the toggle-fleet action, default Ctrl+Shift+F. Esc exits the deck back to piloting (when the inbox isn't capturing keys).
  • It renders as an overlay over the still-mounted agent workspaces, so opening or leaving the deck never remounts a pane. Agents keep running underneath; clicking a card just reveals that agent (sets it active and drops you back to piloting).
  • Cards are ordered by the Attention Router, so agents that need you float to the front and pulse. There's a cards/list toggle, persisted in localStorage under wks-fleet-view.

command palette

A fuzzy launcher for everything. Open it with the command-palette action, default Ctrl+Shift+P. Type to filter; matching is a case-insensitive substring match against each item's name and description.

It unifies these categories into one list:

  • actions: New Claude Code, New Terminal, New Browser, Review Changes, Notes, Library.
  • commands: toggles and dialogs (Layouts, Manage Plugins, Install Plugin, Switch session, and more).
  • apps: your custom app launcher entries (see below).
  • plugins: panes contributed by installed plugins.
  • library: your reusable prompts and skills, fired straight into a session.

Rows show the matching keybinding as a badge. "Keyboard Shortcuts" is pinned at the top regardless of your query. There's also a library-only quick-pick mode (the right-side Library panel, default Ctrl+Shift+L).

keybindings

Bindings come in three shapes, all defined in config.keybindings:

  • direct combos: reserved for terminal-safe keys so a focused terminal still gets Ctrl+C, Ctrl+L, etc.
  • digit-range: a modifier plus any of 19 (e.g. jump-tab = Ctrl+1-9).
  • prefix chords: press the prefix (default Ctrl+Space), then a key or a short sequence. Chords can nest into submenus (New ▸, Tab ▸, Pane ▸).

The default workspace bindings are vim-flavored: pane navigation is h/j/k/l, tab/pane ops live under leader chords. Note there is no separate "vim mode" toggle for the workspace layer anymore. The one Vim toggle in the app is config.editor.vim, which enables Vim keybindings inside the CodeMirror editor only and is independent of these workspace bindings.

Default bindings (direct):

  • command-palette Ctrl+Shift+P
  • next-agent Ctrl+Tab, prev-agent Ctrl+Shift+Tab
  • next-attention Ctrl+Shift+Space (jump to the next agent waiting on you)
  • toggle-ui-mode Ctrl+Shift+M (flip fleet ⇄ focus)
  • spawn-agent Ctrl+Shift+N
  • settings Ctrl+,
  • save-session Ctrl+Shift+S
  • open-file Ctrl+Shift+O
  • open-review Ctrl+Shift+G
  • toggle-help F1
  • toggle-terminal ` Ctrl+ ``
  • toggle-sidebar Ctrl+Shift+B
  • toggle-inbox Ctrl+Shift+I
  • toggle-fleet Ctrl+Shift+F
  • toggle-inspector Ctrl+Shift+E
  • library-picker Ctrl+Shift+L

Default bindings (digit-range):

  • jump-tab Ctrl+1-9, move-tab Ctrl+Shift+1-9

Default chords (press Ctrl+Space then…):

  • New ▸ n t terminal, n c Claude, n b browser
  • Tab ▸ t [ prev, t ] next, t , move left, t . move right, t r rename, t w close pane
  • Pane ▸ p s split, p c quick split, p h/j/k/l focus left/down/up/right

Remap capture. Settings ▸ Keybindings lets you rebind any action by clicking its field and pressing the keys. Press a combo for a direct binding, or press the prefix first then a key to record a chord. Digit-range actions capture as "modifier + digit". Each row has a reset-to-default. The prefix itself is rebindable (it must be a real combo, not a bare modifier).

Shortcut overlay. F1 (toggle-help) opens a full cheatsheet grouped by section. There's also a chord hint: the moment you press the prefix, a which-key style indicator appears bottom-right and follows you into submenus, listing the keys available at the current level. Turn it down to a minimal indicator with Settings ▸ Keybindings ▸ Chord hints (config.keybindings.chordHints, default on).

themes

18 built-in themes, each a set of color tokens injected as --wks-* CSS variables on the document root (and into plugin webviews). Set with config.ui.theme, default dark. The shipped set: dark, light, midnight, solarized-dark, dracula, nord, monokai, github-dark, gruvbox, tokyo-night, one-dark, rose-pine, cyberpunk, catppuccin-mocha, everforest, kanagawa, ayu-dark, synthwave. Corner style and radius are derived from the theme too (rounded / soft / square).

Beyond the built-ins there's a theme maker (Settings ▸ Appearance): start from any built-in, edit the tokens, and save it as a custom theme. Custom themes live in config under ui.customThemes, keyed as custom:<slug>, and are selectable like any built-in.

layout templates

Save and restore named arrangements of directories plus panes. Open via the command palette ("Layouts…") or the Layouts dialog. "Save current" captures your non-global agents (their cwds and panes) under a name. Restoring a layout spawns fresh agents for its directories, so a template is a reusable starting point, not a live snapshot of running sessions. Backed by layoutsSave / layoutsList / layoutsDelete.

session save and restore

Your whole session (agents and their tabs) is saved and can be brought back on launch.

  • auto-resume: Settings ▸ Session ▸ "Restore my last session on launch" (config.session.autoResume). When on, the most recent session reopens straight away. The shipped default is off, which means startup shows the session picker instead.
  • session picker: lists saved sessions with name, age ("5m ago"), and pane/agent counts, with resume / new / delete. The picker is also the crash-recovery and switch path: open it mid-session from the command palette ("Switch session"), where it's dismissable (Esc or Cancel returns you to the running app). Old session files are migrated forward on load.

Sessions live in the daemons, not the window, so agents keep running across window restarts. On launch, if the hub already had a shared layout, work{spacer} mirrors that and skips the local picker.

custom app launcher

Define your own external app/site shortcuts in Settings ▸ Apps (config.apps). Each entry is a name, a url, and an optional icon (an emoji, or an http(s) URL that renders as a favicon image). They show up as launchable items in the command palette under the apps category, opening in a browser pane (app-mode webview).

attention and notifications

You run a lot of agents at once. Most of the time you're looking at one of them. work{spacer} keeps you honest about the rest: every agent's state is visible in the sidebar, the ones that are blocked on you get collected in a single inbox, and the OS taps you on the shoulder when an agent you're not watching needs a decision.

per-agent status in the sidebar

Each agent row in the sidebar carries a live status dot and a small set of numbers, all derived from the agent's session snapshot (the same source the agent pane's status bar uses, so they never disagree).

The dot color tracks the agent's ambient state:

  • green: idle
  • blue (busy/accent), thinking or streaming (working). The dot pulses (wks-pulse, 1.6s) and gets a soft glow while working.
  • amber (warning), waiting on you: needs approval or waiting for input
  • faint grey: stopped (no session)

If the agent has an open attention item, the dot is tinted to that item's kind instead and a tiny glyph rides next to it: ! approval, ? question, × error, stuck, ± big diff, finished.

Next to the dot is a context bar with a percentage. The number is the share of the context window used, colored green under 70%, amber from 70%, red from 90%. Hover a row and the tooltip spells out the rest: NN% context · N.Nk tok · $N.NN · model. Tokens and cost only show when the daemon has reported them.

the "N need you / N working" header

The "Agents" header in the sidebar shows one status pill:

  • N need you (amber, pulsing dot) when any agent has an open blocking item. "Need you" counts approval, question, stuck, and error items. Click the pill to open the Triage Inbox.
  • N working (blue) when nobody needs you but agents are still running. "Working" counts agents whose ambient state is thinking or streaming.
  • all clear (green) flashes briefly when you hit jump-to-next-attention and there's nothing waiting.

the triage inbox

The Triage Inbox is a right-side drawer you can open from any agent, so you never have to dig into a workspace to clear a request. Toggle it with ctrl+shift+i (action toggle-inbox), or click the "N need you" header pill.

It's a pure projection of the live attention feed. The feed is built in the renderer from each agent's session snapshot, so items appear and disappear as the underlying session changes. No live panes live in the drawer, so opening it can never remount a terminal, webview, or Claude viewer. Items are sorted most-urgent first by kind priority (approval 100, question 95, error 80, stuck 70, big diff 40, done 20), and within a tier the oldest floats up.

You clear it top-down, like email. The whole drawer is keyboard-driven while it's open and you're not typing in a field:

  • j / k (or arrows), move the selection
  • y / Enter, approve the selected approval, n, deny
  • 19, answer the selected question by option number
  • o, open that agent's full workspace (drops to piloting, closes the drawer)
  • e, dismiss the item
  • s, snooze it (30 minutes, then it resurfaces)
  • Esc, close the drawer

Every action addresses the agent by session id (approve / answer / message / write straight to the daemon), so the inbox can resolve any agent's request without owning that agent's pane. Approvals prefer the daemon's /approve endpoint and fall back to driving Claude's permission menu over the PTY; answers are written to the picker directly. Opening an agent counts as triaging it: that agent's items are cleared from the inbox (the live prompt still shows in its own pane, and any genuinely new request resurfaces later).

When the inbox is empty you get "Inbox zero". The drawer header mirrors the sidebar: "N need you" (amber), else "N to review" (green), else "all clear".

jump to the next agent that needs you

Press ctrl+shift+space (action next-attention) to jump straight to the next agent in the attention feed without opening the inbox. It walks the sorted feed in priority order, starting just after the agent you're on, so pressing it repeatedly cycles through everything that wants your eyes (approvals, questions, stuck, error, done). If nothing needs you, the sidebar header flashes "all clear" for about a second.

OS notifications

When an agent you're not watching changes state, work{spacer} fires a desktop notification so you can babysit a fleet without staring at it. The notifier is driven off the session store on every ambient-state transition and fires at most one notification per meaningful transition:

  • needs approval: title <agent> needs approval
  • waiting for input: title <agent> is waiting for input
  • finished (working → idle), title <agent> finished, only when notifyDone is on

The agent label is the basename of its working directory. Clicking a notification restores and focuses the window and jumps the renderer to that agent.

Suppressed for the watched agent. With onlyWhenUnwatched on (the default), no notification fires for the exact session you currently have on screen with the window focused. The renderer keeps the main process informed of which session is active as you switch agents.

Taskbar flash. On a needs-you event, when the window isn't focused, work{spacer} flashes the taskbar (the Windows "attention" signal). The flash stops as soon as you focus the window.

There is no tray icon or taskbar overlay badge today. Away from the desk entirely, the mobile PWA has background push wired up for the same needs-approval / needs-input alerts, though it's still being stabilized.

notification config

The notifications block in config (Settings ▸ Notifications) has four keys, all booleans:

  • enabled (default true), master switch for OS notifications and taskbar flash. UI label "Desktop notifications".
  • notifyDone (default true), also notify when an agent finishes (working → idle), not just when it's blocked. "Notify when an agent finishes".
  • onlyWhenUnwatched (default true), suppress notifications for the agent currently on screen. "Only when I'm not watching that agent".
  • sound (default false), play a sound; otherwise notifications are silent. "Play a sound".

When enabled is off, the other three toggles are disabled in the UI.

remote and multi-client

Because your sessions live in the background daemon rather than in any one window, you can watch and drive the same fleet from more than one place at once: the desktop app, the terminal app, your phone, a second PC, or a full web app in a browser. They all see the same agents, and a layout change on one mirrors to the others like tmux clients on one session. Most of the time you'll just use the desktop app; this section is about reaching your fleet from somewhere else.

remote sharing is opt-in

By default nothing about your fleet is reachable from off your machine, everything stays on localhost. Sharing is a runtime toggle: open Remote control (the phone icon in the Hub status row) and hit Start sharing. Two environment variables cover the launch-time cases:

  • WORKSPACER_REMOTE_SHARE=1 forces sharing on from launch (useful for testing, or a machine that should always share).
  • WORKSPACER_REMOTE_ADDR=host:port (optional) pins the address it listens on (default 0.0.0.0:7895). Set it to your tailnet IP so it's only reachable over your private network, not the whole LAN.

When sharing is on, access is gated by a secret token (generated once and stored in your config dir). Anyone with the token-bearing URL gets full control, so treat it like a password.

The intended transport is a Tailscale tailnet, not the open internet. The connection isn't encrypted on its own, so keep both devices on the same trusted private network — or use the one-tap HTTPS via Tailscale option in the share dialog, which fronts the hub at https://<your-node>.ts.net via tailscale serve.

the share dialog (QR)

Open Remote control from the phone icon in the Hub status row at the bottom of the sidebar.

  • Sharing off: a Start sharing button binds the hub to your network on the spot, no relaunch needed.
  • Sharing on: it shows a QR code of the ready-to-use URL, point your phone camera at it and tap, plus copy buttons for the URL and the token (masked behind a show/hide eye). Pick what the QR points at with the Full app / Mobile tabs.
  • HTTPS via Tailscale: an Enable button runs tailscale serve for you, so the QR and URLs switch to https://<node>.ts.net/…. This is what gives the mobile PWA a secure context, which installing it and push notifications require.

The same URL and token also show in the Hub status row, where the dot turns green once the bus is connected.

the web clients

Three browser clients, nothing to install on the remote device (though the mobile one is installable if you want it on your home screen):

  • Mobile (/m): a phone-first, decision-first client — a live fleet list, a "Needs You" queue of approvals and questions with big approve/answer buttons, per-agent chat, and spawn. It's an installable PWA: add it to your home screen over the HTTPS/Tailscale option. Background push notifications (an alert when an agent needs you, even with the browser closed) are wired up but still being stabilized — don't rely on them yet. This is what the QR points at by default.
  • Terminal mirror (/remote): a lightweight client with the agent list, chat, approvals, and a live mirror of the agent's real terminal. Works offline over Tailscale.
  • Full app (/app): the complete work{spacer} interface running in the browser, with full desktop parity. Available when sharing is on and a web build is present.

what mirrors, and what stays put

  • Layout mirrors. Your agent cards, tabs, and active tab are shared, so every client shows the same arrangement. With one person driving at a time, that just works; the last change wins.
  • One card per agent. The same session never doubles up across clients, the desktop, the web app, and your phone all show one card for it.
  • Terminals stream on demand. A live terminal is only mirrored to a client while that client is actually watching it, so you're not streaming build logs to a phone in your pocket.
  • No window needed. Remote clients keep working even with the desktop app closed, the background daemons serve them on their own.

plugins, mcp, and the tui

There are three ways to push work{spacer} past the built-in panes: install plugins that add new panes and automations, let an agent drive the whole fleet over MCP, or run the terminal client for a keyboard-only cockpit. If you want to write a plugin rather than just install one, that's on the build & internals page.

plugins

Plugins add their own panes (dashboards, editors, rule editors), command-palette entries, and hotkeys. Manage them from the Plugins Manager (command palette → "Manage Plugins").

  • Install from a GitHub repo (owner/repo or a full URL) or a tarball URL, via the install dialog. work{spacer} downloads it, runs any build step, and loads it.
  • Enable / Disable a plugin without uninstalling it, and Update or Remove it.
  • Each plugin shows live health, green when it's running fine, amber/red when it's struggling or crashed, so you can tell at a glance if one is misbehaving.

Plugin panes are themed to match your current theme, and an agent-scoped plugin pane only gets access to that agent's project folder, never your whole disk. A few plugins ship as examples (an event→action rules engine, an agent-grid dashboard, a minimal clock demo); see the build page for the catalog and how to write your own.

Installing a plugin downloads and runs code from the internet, like a VS Code extension. Only install plugins you trust.

let an agent drive the fleet (MCP)

work{spacer} can expose your whole fleet to a coding agent as MCP tools, so an agent (or any MCP client) can list your agents, read a transcript, spawn new agents, send prompts, approve/answer prompts, and post notifications, on your behalf. This is what powers the supervisor agents you spawn from the Ask pane, but you can also attach it to a Claude Code session of your own.

The core tools an MCP client gets:

  • list_agents, the running agents with their state, usage, and any pending asks.
  • get_transcript, read a session's conversation.
  • spawn_agent / create_terminal, start a new agent or shell.
  • send_message, send a prompt to an agent.
  • approve / answer, resolve a permission prompt or answer a question.
  • signal / terminal_input, interrupt/stop an agent or type into a shell.
  • notify, raise a desktop notification.

That's the driving set; the facade exposes the hub's whole capability surface — around 38 tools in all, covering session snapshots and conversations, config and profiles, saved sessions and layouts, the library, analytics, and path-scoped file/search access. The setup and the exact endpoint to point an MCP client at live on the build & internals page. It keeps working even with the desktop window closed.

the terminal client (wks-tui)

wks-tui is a full terminal cockpit over the same fleet, for when you'd rather stay on the keyboard or you're on a box with no GUI. It sees and drives every agent the desktop app does. Start it standalone, it brings up whatever background daemons it needs on its own, or point it at a remote machine's bus.

  • Dashboard + sidebar. A pinned row with agent count, needs-you / working / idle, total cost, and rate-limit windows, plus a sidebar of every live session.
  • Per agent. Open one and it has tabs: the agent session (raw terminal by default, t toggles the parsed transcript with approve/answer buttons), plus a terminal tab (T), git review (R), and per-project notes (N).
  • Control. c spawn, y/n/a approve/deny/always, 19 answer a question, x/X interrupt/stop, e rename, S respawn.
  • Navigation is vim-first. Ctrl-K for a fuzzy command palette, j/k to move, m to jump to the next agent needing you, a space leader/which-key menu, Ctrl-w splits (tile up to 4 agents), harpoon pins, / to filter, a : command line, and vim counts. Press ? for the active bindings. Config (theme + key remaps) lives at ~/.config/workspacer/tui.json.

See the build & internals page for how to launch it from source and the daemon/bus flags.

configuration

work{spacer} keeps almost everything in one config directory. Most settings have a UI in the Settings pane, but they all land in plain files on disk you can read, diff, and edit by hand.

where config lives

The desktop app's config dir is resolved like this:

  • Linux: $XDG_CONFIG_HOME/workspacer, falling back to ~/.config/workspacer
  • macOS: ~/.config/workspacer (same XDG fallback)
  • Windows: %APPDATA%\workspacer (else ~/AppData/Roaming/workspacer)

Everything below lives under that directory:

  • config.yaml, the main settings file (YAML). Written with defaults on first run, deep-merged with your edits on load.
  • claude-profiles.json, named Claude profiles (see below).
  • plugin-settings.json, per-plugin settings contributed by installed plugins.
  • library/, your reusable prompts/skills/agents (one .md per item).
  • layouts/, saved layout templates.
  • sessions/, saved sessions (one .yaml per session, used by save/restore and auto-resume).
  • plugins/, installed plugins.
  • remote-token, the hub bus bearer token (created on first run, reused for remote sharing).
  • workspacer.db, SQLite analytics history (per-session model, cost, tokens, tools, branch).

The Claude session daemon claudemon keeps its own SQLite store separately at $XDG_DATA_HOME/claudemon/state.db, falling back to ~/.claudemon/state.db. Override it with claudemon serve --db-path <file>.

config.yaml, the settings file

Edit it directly or use the Settings pane (Ctrl+,). Missing keys fall back to defaults, so a partial file is fine. The notable sections and their defaults:

  • ui: animations: false, theme: dark, mode: fleet (fleet | focus, the UI-mode lens), customThemes (the theme maker's saved themes, keyed custom:<slug>), fontFamily: "Inter, system-ui, sans-serif", fontSize: 14, borderRadius: 8, navBarHeight: 34, paneHeaderHeight: 22, showComposerSend: true, guiFontScale: 1.15, diffView: stacked (stacked | inline | split). cornerStyle and borderColor are blank by default (use the theme's own).
  • terminal: shell: '' (use $SHELL), fontSize: 14, scrollback: 1500, cursorBlink: true, cursorStyle: block. shells is the picker list (platform-specific defaults). Default terminal font is JetBrainsMono Nerd Font with Caskaydia/Cascadia fallbacks.
  • browser: homepage: https://google.com, hibernateAfter: 300 (seconds), plus a bookmarks list.
  • panes: defaultWidth: 800, gap: 0, peek: 80, insertPosition: after, tabPosition: top (top | left), viewLevel: piloting (piloting | fleet), plus a default pane list.
  • keybindings: prefix: ctrl+space, chordHints: true, and a shortcuts map (see keybindings below).
  • notifications: enabled: true, notifyDone: true, onlyWhenUnwatched: true, sound: false.
  • claude: transport: stream (stream = headless stream-json, GUI-only | pty = the classic terminal CLI), defaultModel: 'opus[1m]', seenModels: [] (auto-collected from transcripts to enrich the spawn dropdown), defaultPermissionMode: bypassPermissions and skipPermissionsDefault: true (a fresh agent runs with full access — tighten these if you want approval prompts by default), defaultView: terminal (gui | terminal, terminal-transport panes only), workLog: cards, settingsOverlay: false (experimental: pass hooks via a private --settings file instead of editing ~/.claude/settings.json), plus per-session budgets (a cost ceiling that fires a one-shot notification when crossed).
  • agents: defaultProvider, defaultCwd, and per-provider binaries overrides for the spawn dialog.
  • supervisor: model: '' (app default), summarizerModel: sonnet, pollSeconds: 45.
  • editor: engine: codemirror (codemirror | terminal), terminalCommand: nvim (the file path is appended as the last arg), optional vim: true for CodeMirror.
  • session: autoResume: false (restore the most recent session on launch, skipping the picker).
  • updates: enabled / channel for the app's update check (Settings ▸ Updates).
  • directories: recent / favourites lists surfaced in the Overview pane.
  • scripts: per-directory script buttons keyed by normalized cwd.
  • apps: your custom external app/site launcher entries (name, url, icon).

the Settings pane

Open it with Ctrl+,. There's a filter box at the top. The pane renders these sections, each one writing straight into config.yaml (the plugins section is contributed by installed plugins, and Claude profiles write to claude-profiles.json):

appearance (including the theme maker), layout, terminal, keybindings, notifications, session, browser, editor, apps, Claude profiles, supervisor, updates, plugins.

default keybindings

Bindings are either direct combos (terminal-safe keys) or prefix chords. A chord is the workspace prefix (Ctrl+Space by default) followed by one or more keys. Remap them under keybindings.shortcuts. Direct defaults:

  • command-palette, ctrl+shift+p
  • next-agent / prev-agent, ctrl+tab / ctrl+shift+tab
  • next-attention, ctrl+shift+space
  • toggle-ui-mode, ctrl+shift+m
  • spawn-agent, ctrl+shift+n
  • settings, ctrl+,
  • save-session, ctrl+shift+s
  • open-file, ctrl+shift+o
  • toggle-help, f1
  • toggle-terminal, ` ctrl+ ``
  • toggle-sidebar, ctrl+shift+b
  • toggle-inbox, ctrl+shift+i
  • toggle-fleet, ctrl+shift+f
  • toggle-inspector, ctrl+shift+e
  • library-picker, ctrl+shift+l
  • open-review, ctrl+shift+g

Prefix chords (press Ctrl+Space, then the keys): new-terminal (n t), new-claude (n c), new-browser (n b); prev-tab/next-tab (t [ / t ]), move-tab-left/move-tab-right (t , / t .), rename-tab (t r), close-pane (t w); split (p s), quick-split (p c), pane nav nav-left/down/up/right (p h/j/k/l).

On upgrade, an old keybindings schema (the mode/leader style) is migrated once to this prefix scheme. If you had Vim keybinding mode on, it carries over as the editor's vim: true.

Claude profiles

Profiles live in claude-profiles.json and are managed in the Settings pane's Claude profiles section. Each profile has:

  • name
  • configDir, a custom CLAUDE_CONFIG_DIR (blank = the default ~/.claude). A leading ~ is expanded to your home dir at spawn.
  • extraArgs, extra CLI args, e.g. ["--dangerously-skip-permissions"].
  • mcpItemIds, library items (kind mcp) to pre-load when spawning with this profile.
  • isDefault

There's always a Default profile; you can't delete it.

claudemon init, hook wiring

For work{spacer} to observe Claude sessions, Claude's hooks POST lifecycle events to claudemon. The desktop app wires this for you: on every launch it runs claudemon init, which merges the hook config into ~/.claude/settings.json. There's nothing to do by hand.

The CLI still exists for headless or custom setups (a machine that only runs the daemon, a nonstandard hook port):

claudemon init                  # merge hooks into ~/.claude/settings.json
claudemon init --dry-run        # print the merged document, write nothing
claudemon init --hook-port 8888 # match a non-default hook port

The default ports the background daemons bind: hook ingress 7890, session API 7891, hub bus 7895, MCP facade 7897. You won't normally touch these unless something else on your machine wants the same port.

environment variables

  • WORKSPACER_DISABLE_GPU=1, disable GPU rendering. Use this if the window renders broken on Wayland.
  • WORKSPACER_REMOTE_SHARE=1, force remote sharing on from launch. (Sharing is otherwise a runtime toggle: Remote control → Start sharing.) When on, the hub binds off loopback and requires the bus token; off means loopback-only.
  • WORKSPACER_REMOTE_ADDR=host:port, bind address when sharing is on (default 0.0.0.0:7895). Pin it to your tailnet IP to avoid exposing it to the whole LAN.
  • CLAUDE_CONFIG_DIR, set per spawn from a profile's configDir, so each profile can point at its own ~/.claude-style dir.
  • XDG_CONFIG_HOME, moves the desktop config dir (Linux).
  • XDG_DATA_HOME, moves claudemon's state.db directory.

library and analytics on disk

  • Library items live as Markdown under library/ in the config dir. The app also reads project-scoped items: <cwd>/.workspacer/library, and Claude's own <cwd>/.claude/skills and <cwd>/.claude/agents.
  • Analytics history is a SQLite database at workspacer.db in the config dir, one upserted row per session (model, cost, tokens, tools, branch), powering the Analytics and Overview panes.
  • Sessions are saved as .yaml files under sessions/; with session.autoResume: true the most recent one is restored on launch.
work{spacer} :: local-first control plane for coding agents :: source-available (BSL 1.1)