Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ jobs:
zsh -n bin/off-probe
zsh -n bin/root-probe
zsh -n bin/ctx-probe
zsh -n bin/zoom-probe
zsh -n bin/grid-probe
zsh -n bin/agent-probe
zsh -n install.sh
sh -n bin/office-attn
sh -n bin/office-ctx
sh -n bin/office-cwd
python3 -m py_compile bin/key-probe bin/mouse-probe
python3 -m py_compile bin/key-probe bin/mouse-probe bin/menu-probe

# tmux refuses to parse a config it cannot read, and a broken one would
# otherwise only surface as "the keys do nothing" inside a probe.
Expand All @@ -80,6 +80,10 @@ jobs:
tmux -L cfg source-file "$PWD/theme/office-theme.tmux.conf"
tmux -L cfg list-keys -T prefix | grep -qE '^bind-key +-T prefix +n ' \
|| { echo "prefix n is not bound"; exit 1; }
# the pane toggles are gone: a pane is whatever you pick from n
if tmux -L cfg list-keys -T prefix | grep -qE "^bind-key +-T prefix +[scea] .*office"; then
echo "a pane toggle key is back"; exit 1
fi
# No destructive action may sit behind one hard-coded letter again.
# `confirm-before` takes exactly one key for yes -- y -- and on a
# QWERTZ keyboard the key the hand reaches for sends z, so the prompt
Expand Down Expand Up @@ -135,8 +139,9 @@ jobs:
- name: root-probe
run: bin/root-probe

- name: zoom-probe
run: bin/zoom-probe
# one pane that asks, then the grid: three across, two down, six in all
- name: grid-probe
run: bin/grid-probe

- name: attn-probe
run: bin/attn-probe
Expand All @@ -157,3 +162,7 @@ jobs:

- name: agent-probe
run: bin/agent-probe

# the menu draws only on a real client, so this one attaches one
- name: menu-probe
run: bin/menu-probe
39 changes: 26 additions & 13 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ something that turns out to be out of scope.

A tmux cockpit for running several coding-agent sessions at once. Around 2,100

lines of zsh and tmux config, seven probes that drive a real tmux server, and one
lines of zsh and tmux config, nine probes that drive a real tmux server, and one
iTerm2 profile.

**In scope:** making that faster, clearer or harder to get wrong. Support for
Expand Down Expand Up @@ -113,13 +113,19 @@ hidden copy mode it opened. And a double-click cannot be read back for a full
second: tmux holds it for its own 500ms triple-click window before the binding
starts, so a check that looks sooner reports a working gesture as broken.

Touched anything that reads `pane_left`, `pane_top` or a window size? Run
`bin/zoom-probe`. It builds a throwaway office, zooms a pane, and runs every
command that moves one. **A zoomed pane reports full-window coordinates**, so
every "which column is this in" question in `office.zsh` is answered about a
room that is not on screen — and `_office_layout_ok` then calls a perfectly good
office broken and hands it to `_office_relayout`, which breaks every pane out to
the stash. Reading the code does not show you this; the probe does.
Touched the grid — adding, parking, unparking, moving or closing a pane, or
anything that reads `pane_left`, `pane_top` or a window size? Run
`bin/grid-probe`. It builds a throwaway office and drives the same calls the
menu items and key bindings make (`office new --agent N`, `--shell`, `--edit`,
`--back <window>` — `display-menu` cannot be made to draw headless, so the menu
itself is out of reach), then checks the geometry after every step: at most
three across, at most two rows, the top row filling first and taking the odd
pane (three panes is always "2 1", five is "3 2", never "2 3"), zoom dropped before anything is
measured, and the grid re-fitting itself after a close it did not initiate
through `office.zsh` (the config's `after-kill-pane` hook). Reading the code
does not tell you whether the layout string it built was actually valid; the
probe does — tmux silently refuses one whose checksum does not match, so a bug
here reads as "nothing happened" rather than an error.

Touched `bin/office-attn`, `@office_attn_gate` or a `pane-border-format`? Run
`bin/attn-probe`. It builds a throwaway office and puts fake agents in the desks
Expand Down Expand Up @@ -152,11 +158,18 @@ does not.

Touched `OFFICE_AGENTS`, `_office_new` or the `Ctrl-Space n` menu? Run
`bin/agent-probe`. It builds a throwaway office and drives `office new`
directly — no real client, so it also proves the shape display-menu cannot be
made to draw headless: one agent opens the desk with no menu at all, `--agent
2` (by number) and `--agent <LABEL>` both split the right command and label
out of the array, and 2+ agents with none named opens nothing, because the
picker needs a client this probe deliberately does not attach.
directly — no real client: a named worktree opens agent 1, `--agent 2` (by
number) and `--agent <LABEL>` both split the right command and label out of the
array, and a bare `office new` opens nothing, because the menu needs a client
this probe deliberately does not attach.

Touched `_office_open`, the first pane's wait, the menu, or parking? Run
`bin/menu-probe`. It is the one that DOES attach a client, on a pty, and types
at it: the office opens as one pane whose menu draws by itself, `1` turns that
pane into the first agent, `Ctrl-Space n` then `s` adds a shell, `Ctrl-Space x`
parks it, and the next menu brings it back under `a`. Its rc file is a
throwaway `ZDOTDIR` pointed at this checkout, because every menu item runs
`zsh -ic`, and your own `~/.zshrc` may source a different copy of office.

**A probe must never reach your office.** `TMUX_TMPDIR` alone does not isolate
it: inside a desk, tmux talks to the server in `$TMUX` first. A probe run from a
Expand Down
96 changes: 52 additions & 44 deletions GETTING-STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,35 +55,43 @@ office on
```

```
┌─────────────────────────────┬──────────────┐
│ 1 CLAUDE │ 2 SHELL │
│ │ │
│ your coding agent ├──────────────┤
│ claude / codex / any CLI │ 3 FILE EDITOR│
│ │ │
└─────────────────────────────┴──────────────┘
what should this pane be? any key shows the list again.

› CLAUDE
shell
file editor
```

One pane, asking. Press any key and pick from the list: your agent, a plain
shell, or the file editor. Pick your agent first, then press **Ctrl+Space,
then `n`** twice more and pick shell, then file editor:

```
┌──────────────┬──────────────┬──────────────┐
│ 1 CLAUDE │ 2 SHELL │ 3 FILE EDITOR│
│ │ │ │
│ │ │ │
└──────────────┴──────────────┴──────────────┘
```

Three panes. That is the whole tool.

**1. The agent (left, big).** Your agent, running in your project. Type here the
same way you type in the desktop app. Paste an image, drag a file in, ask it to
change something. This is where you spend your time, which is why it gets the
space.
**1. The agent.** Your agent, running in your project. Type here the same way
you type in the desktop app. Paste an image, drag a file in, ask it to change
something. This is where you spend your time, which is why it gets equal space
with everything else.

**2. The shell (top right).** An ordinary command line, already in your project.
For `git status`, `npm test`, `ls`. You use it to *check* the agent's work: it
says it fixed the tests, so you run them.
**2. The shell.** An ordinary command line, already in your project. For
`git status`, `npm test`, `ls`. You use it to *check* the agent's work: it says
it fixed the tests, so you run them.

**3. The file editor (bottom right).** A file browser that follows the shell pane, so
the two work together: `cd` in the shell to aim, browse and open in the editor.
For reading what the agent did, or a quick manual fix.
**3. The file editor.** A file browser that follows the shell pane, so the two
work together: `cd` in the shell to aim, browse and open in the editor. For
reading what the agent did, or a quick manual fix.

There is a fourth, **AGENT CHAT**, closed until you have something to put in it.
That one is not for a coding agent: it is for an agent *you* build. Once you
have one, this is where you talk to it and give it work, without ever standing
up a dashboard or wiring up Slack. Ignore it on day one, and see
[the README](README.md#bringing-your-own-agent) when you get there.
Running an agent of your own and want to talk to it the same way? See
[the README](README.md#bringing-your-own-agent) when you get there — it is just
one more line, not a different kind of pane.

---

Expand Down Expand Up @@ -125,9 +133,9 @@ operating system with nothing to set up.
This is the part that catches everyone, including people who have used
terminals for years.

Press **Ctrl+Space, then `e`** to open the editor pane. You get a list of files with a
search box. Type a few letters to filter, arrow up and down, **Enter** to open
one.
Press **Ctrl+Space, then `n`**, and pick **file editor** from the list. You get
a list of files with a search box. Type a few letters to filter, arrow up and
down, **Enter** to open one.

**It follows the shell pane.** Whatever directory the shell is standing in is
what the editor shows, listed the way a file tree reads. `cd src` in the shell,
Expand All @@ -144,18 +152,18 @@ Now you are inside a file, and here is the bit nobody remembers:
| **Ctrl+Q** | close the file, back to the file list |
| **Ctrl+Z** | undo |
| **Ctrl+F** | find |
| **Esc** (at the file list) | leave the file list. The pane becomes an ordinary shell, and `Ctrl+Space e` brings the list back |
| **Esc** (at the file list) | leave the file list. The pane becomes an ordinary shell, and says so |

**The editor shows these keys along its bottom edge while a file is open**, so
you do not have to remember them. Look down.

The path out is always the same: `Ctrl+Q` gets you back to the list, `Esc`
leaves the list, `Ctrl+Space e` hides the pane.
leaves the list. To bring the editor back, **Ctrl+Space, then `n`**, and pick
**file editor** again — it is only offered while none is open, so it is always
right there in the menu when you need it.

**Nothing you do in here can lose the pane.** Leave the list and it says so, in
the pane, with the key that brings it back. That key works on every pane in the
strip: if what a pane was running has stopped, its own key starts it again
instead of hiding it.
the pane, and the same `Ctrl+Space n` that opens every pane brings it back.

> **If your editor looks nothing like this** and shows no help at the bottom,
> your `$EDITOR` points somewhere else: vim, or macOS's `nano`, which is really
Expand All @@ -167,9 +175,10 @@ instead of hiding it.

## 6. Running more than one agent

**Ctrl+Space, then `n`** adds a second agent, below the first. Up to four. They share
the left column evenly, and each one is a separate conversation working on a
separate thing.
**Ctrl+Space, then `n`** asks the same question again: pick your agent (or
another one, if you have more than one set up) and it takes the next cell in
the grid — up to six panes total, three across at most — and each one is a
separate conversation working on a separate thing.

Each one also gets **its own checkout** — a git worktree under
`.claude/worktrees/`, made for it if there is not a free one already. Without
Expand Down Expand Up @@ -221,27 +230,26 @@ And `Ctrl+Space`, then:

| | |
|---|---|
| `n` | new agent session |
| `s` `e` `c` | show or hide shell / editor / chat |
| `a` | hide every agent, bring them all back, or open one if you have none |
| `n` | one more pane: pick from the menu — parked panes, every agent, shell, file editor |
| `x` | park this pane: hidden, still running. `n`'s menu brings it back |
| `q` | close this pane for good. A menu opens: click **close it**, or press `c`. Enter, Escape or `k` keeps it |
| `x` | park this pane: hidden, still running |
| `z` | zoom this pane full screen, and back |

And the mouse, with no key at all:

| | |
|---|---|
| drag a pane's title onto another pane | it moves there, the rest shift along |
| drag across text | it is on the clipboard when you let go, nothing to press |
| double-click a word | the same, for one word |
| click in a pane that scrolled | back at the live prompt, typing again (Escape does it too) |

Each pane's top border shows its number and what it is, and the bar along the
bottom carries the keys. **Whatever is bright on that bar is closed.**
bottom carries the keys, including how many panes are parked right now.

And the thing you actually wanted from four agents at once: a desk that has
And the thing you actually wanted from several agents at once: a desk that has
stopped and is waiting on you says **your turn** on its own border, with how long
it has been waiting. You never have to read all four panes to find the one that
it has been waiting. You never have to read every pane to find the one that
finished. Nothing to press, and nothing to set up.

That number keeps counting all night, so a desk you left at midnight says
Expand All @@ -261,8 +269,8 @@ Nothing here ever needs a restart of anything.
|---|---|
| a pane is frozen and its keys do nothing | it is in scroll mode. Press `q` |
| git says a branch is "already used by worktree" | `office cd <branch>` — go to it instead of checking it out |
| the panes are in silly positions | `office layout` |
| you closed something and cannot get it back | its key again, or `office show` |
| the panes are in silly positions | they re-fit on the next pane you add, park, unpark or close |
| you closed something and cannot get it back | `Ctrl+Space n`, or `office show` |
| you have no idea what is running | `office doctor` |
| genuinely wedged | `office off`, then `office on`. Resets everything |

Expand All @@ -277,8 +285,8 @@ which is the real reason to close ones you have finished with.
1. `office on`
2. Ask the agent in pane 1 something small about your project. "What does this
repo do?" is a fine start.
3. When it changes a file, look at it: `Ctrl+Space e`, find the file, read it,
`Ctrl+Q`, `Esc`.
3. When it changes a file, look at it: `Ctrl+Space n`, pick **file editor**,
find the file, read it, `Ctrl+Q`, `Esc`.
4. Run your tests in the shell pane.
5. `Ctrl+Space n`, and give the second agent something unrelated.

Expand Down
Loading
Loading