@axelgar/opentree 1.1.0 โ†’ 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +437 -38
  2. package/package.json +5 -5
package/README.md CHANGED
@@ -15,12 +15,17 @@ opentree is a cross-platform CLI tool that manages multiple AI coding agent sess
15
15
  - **๐Ÿ’ฌ Built-in Chat**: Every agent speaks the [Agent Client Protocol](https://agentclientprotocol.com) and runs inside opentree's own chat view โ€” answer permissions, watch diffs, send images, and drive the agent from the dashboard without attaching
16
16
  - **๐Ÿ“Š TUI Dashboard**: Interactive terminal UI for managing workspaces (press `?` for help)
17
17
  - **๐Ÿ”€ Parallel Development**: Work on multiple branches simultaneously without checkout overhead
18
- - **๐Ÿ“ Diff Viewer**: Review changes before committing
18
+ - **๐Ÿ“ Diff Viewer**: Read a workspace's changes with a file tree, search and syntax colour โ€” and leave notes on lines that go to its agent as a prompt
19
19
  - **๐Ÿš€ PR Creation**: Create GitHub PRs directly from the TUI with auto-generated title and body
20
+ - **โœˆ๏ธ Autopilot**: After each agent turn, run your check command, feed failures back, and publish the PR when it passes โ€” per workspace, opt-in
21
+ - **๐Ÿ“ฆ Dispatch**: `opentree dispatch 42 --headless` turns an issue into a PR with nobody watching, exiting with a code a script can branch on
22
+ - **โ‘‚ Fan-out**: `opentree new feat/x --agents claude,opencode,gemini` races the same task across agents โ€” grouped in the dashboard, compared side by side, the winner promoted and the rest deleted
20
23
  - **๐Ÿ› Issue Workflow**: Create a workspace directly from a GitHub issue number
21
24
  - **โœ… CI Status**: Live CI check status displayed per workspace
22
25
  - **๐Ÿ” Filter & Sort**: Filter workspaces by name, sort by name/age/activity/PR status
23
- - **๐Ÿงน Clean Lifecycle**: Archive workspaces after merge, keeping your repo tidy
26
+ - **๐Ÿ”Œ Agent Plugins**: Install a plugin from the open [Agent Plugins](https://agent-plugins.org) standard once, and every agent in every worktree can use the skills it bundles
27
+ - **๐Ÿ—‚ ACP Registry**: `opentree agents add <id>`, or `a` on the dashboard's Agents tab, installs any agent the [ACP Registry](https://agentclientprotocol.com/get-started/registry) lists, and it becomes first-class everywhere the built-in four are โ€” chats, fan-outs, per-workspace overrides
28
+ - **๐Ÿงน Clean Lifecycle**: A merged PR marks its row `merged ยท ready to delete`, and `opentree delete --merged` clears them all at once
24
29
  - **โŒจ๏ธ Shell Completion**: Tab completion for workspace names in bash, zsh, and fish
25
30
 
26
31
  ## Requirements
@@ -66,9 +71,9 @@ go install github.com/axelgar/opentree/cmd/opentree@latest
66
71
  opentree uninstall
67
72
  ```
68
73
 
69
- Removes what opentree wrote into your home directory: the agent adapters under `~/.opentree/tools` (a few hundred megabytes each), the record of approved setup and run commands, the shell completion script and the global config file. It lists all of it with sizes and asks before removing anything โ€” `--dry-run` lists and stops, `--yes` answers the question from a script.
74
+ Removes what opentree wrote into your home directory: the agent adapters under `~/.opentree/tools` (a few hundred megabytes each), the agents installed from the ACP Registry under `~/.opentree/registry` along with its cached index, the plugins installed under `~/.opentree/plugins`, the record of approved setup and run commands, the shell completion script and the global config file. It lists all of it with sizes and asks before removing anything โ€” `--dry-run` lists and stops, `--yes` answers the question from a script.
70
75
 
71
- It never touches a repository. The worktrees under `<repo>/.opentree` are your own work in progress, and `opentree delete <branch>` is what removes those. The binary belongs to whichever of brew, npm or `go install` put it there, so the command that removes it is printed at the end.
76
+ It never touches a repository, and it never touches a worktree. The worktrees under `~/.opentree/worktrees` (or wherever `base_dir` points) are your own work in progress, and `opentree delete <branch>` is what removes those. The binary belongs to whichever of brew, npm or `go install` put it there, so the command that removes it is printed at the end.
72
77
 
73
78
  ## Quick Start
74
79
 
@@ -82,13 +87,16 @@ opentree
82
87
  # Or use CLI commands directly
83
88
  opentree new feat/add-auth # Create workspace
84
89
  opentree issue 42 # Create workspace from GitHub issue #42
85
- opentree list # List all workspaces
90
+ opentree dispatch 42 --headless # Issue #42 โ†’ agent โ†’ checks โ†’ PR, unattended
91
+ opentree list # List all workspaces, with their paths
86
92
  opentree attach feat/add-auth # Attach to tmux window
93
+ opentree shell feat/add-auth # A shell in the worktree, beside the chat
87
94
  opentree diff feat/add-auth # Review changes
88
95
  opentree pr feat/add-auth # Create GitHub PR
89
96
  opentree delete feat/add-auth # Clean up workspace
90
97
  opentree skills list # See every agent skill on this machine
91
98
  opentree skills sync # Give every agent and workspace the repo's skills
99
+ opentree plugins add <git-url> # Install an Agent Plugin once, for every agent
92
100
  ```
93
101
 
94
102
  ## Usage
@@ -111,31 +119,88 @@ opentree
111
119
  - `n` - Create new workspace (prompts for branch name, then base branch)
112
120
  - `i` - Create workspace from a GitHub issue number
113
121
  - `Enter` - Attach to selected workspace
114
- - `d` - Show diff for selected workspace
122
+ - `d` - Open the workspace's diff (see *Reviewing a diff* below)
123
+ - `D` - Compare a fan-out group: every sibling's diff in one scroll
124
+ - `W` - Promote a fan-out's winner: keep this sibling, delete the rest
115
125
  - `p` - Create PR for selected workspace (auto-generates title and body from commits)
116
126
  - `o` - Open PR in browser
117
127
  - `x` - Delete selected workspace (shows diff confirmation if uncommitted changes)
118
128
  - `R` - Send the workspace's open PR review comments to its agent
129
+ - `P` - Switch the workspace's autopilot on or off
119
130
  - `w` - Start or stop the workspace's dev server
131
+ - `u` - Merge the base branch in (fetched from origin first); conflicts are offered to the agent
132
+ - `t` - Open a shell in the workspace's worktree, in a tmux window beside its chat
133
+ - `y` - Copy the worktree's path to the clipboard
134
+ - `e` - Open the worktree in `$VISUAL`/`$EDITOR`
120
135
  - `b` - Jump to the workspace that has been waiting longest on a permission (press again to cycle)
121
136
  - `space` - Toggle multi-select on current workspace
122
137
  - `/` - Filter workspaces by name
123
138
  - `s` - Cycle sort order (name โ†’ age โ†’ activity โ†’ PR)
124
139
  - `E` - Toggle error log
125
- - `tab` - Switch between Workspaces, Skills and Servers
140
+ - `tab` - Switch between Workspaces, Agents, Skills, Plugins and Servers
126
141
  - `?` - Toggle full help
127
142
  - `q` - Quit
128
143
 
144
+ The mouse works too: the wheel scrolls, a click selects a row, and a
145
+ double-click attaches to it.
146
+
147
+ ### Reviewing a diff
148
+
149
+ `d` opens the workspace's changes โ€” committed and uncommitted, each under its
150
+ own heading โ€” with the changed files down the left and the code on the right,
151
+ syntax-coloured, added and removed lines on a green and a red band. There is a
152
+ cursor, and everything else is a question about where it is. Press `?` inside
153
+ the view for the full card.
154
+
155
+ - `j`/`k`, `pgup`/`pgdn`, `g`/`G` move; `]`/`[` jump between hunks, `n`/`p`
156
+ between files; a click on a file in the tree jumps to it, a click on a line
157
+ takes the cursor there
158
+ - `space` ticks the file under the cursor as reviewed; `t` hides the tree
159
+ - `/` finds: type, `enter` keeps the query, and `n`/`N` step its matches until
160
+ `esc` clears it (with no query, `n` steps files again)
161
+ - `L` shows line numbers, `w` wraps long lines, `W` marks the words that
162
+ changed within a changed line
163
+ - `a` or `enter` writes a note on the line under the cursor, `A` on the whole
164
+ file; `@` lists them, `x` deletes the one under the cursor
165
+ - `s` sends every note to the workspace's agent as one prompt: the path and
166
+ line, the code quoted, and what you said about it. A note that ends in `?`
167
+ (or carries `??` anywhere) is asked as a question rather than given as an
168
+ instruction โ€” *why is this recomputed every frame?* gets an answer, *cache
169
+ this* gets a change.
170
+ - `esc` closes; with notes unsent it warns first and closes on the second press
171
+
172
+ `D` opens a fan-out group the same way, every sibling's diff in one view with
173
+ the tree grouped by sibling. Notes cannot be sent from there โ€” three agents,
174
+ one prompt, no right answer about who gets it.
175
+
129
176
  Each row also carries what its agent is doing โ€” working, waiting on a
130
177
  permission, stopped โ€” plus cost and context use, read live from the chat's
131
178
  control socket. Open PRs show **CI check status** badges.
132
179
 
180
+ ### Agents
181
+
182
+ Press `tab` once for every agent this machine knows โ€” the built-in four and
183
+ whatever was installed from the ACP Registry โ€” with its readiness, where it
184
+ came from, and which one this repository uses. It is `opentree agents` as a
185
+ place rather than a set of commands:
186
+
187
+ - `enter` - Use this agent for the repository (`g` for everywhere)
188
+ - `a` - Browse the ACP Registry and install an agent (`/` filters the list)
189
+ - `u` - Update the selected registry agent (`U` checks every one)
190
+ - `i` - Fetch a built-in agent's ACP adapter
191
+ - `x` - Remove a registry agent, or clear a broken install
192
+ - `r` - Rescan the store, without touching the network
193
+
194
+ Every install shows the same consent card the command line prints โ€” what will
195
+ run or what will be downloaded, and where โ€” before anything is fetched. See
196
+ [Agents from the ACP Registry](#agents-from-the-acp-registry).
197
+
133
198
  ### Skills
134
199
 
135
200
  Skills are a filesystem convention rather than anything an agent exposes over
136
201
  its API โ€” a directory holding a `SKILL.md` โ€” so opentree reads them directly.
137
- Press `tab` for the inventory: every skill on the machine, which agents can
138
- actually use each one, and what each agent will do with it.
202
+ Press `tab` twice for the inventory: every skill on the machine, which agents
203
+ can actually use each one, and what each agent will do with it.
139
204
 
140
205
  - `enter` - Open the SKILL.md in `$EDITOR`
141
206
  - `a` - Add a skill from a git URL
@@ -150,6 +215,30 @@ their skills untracked โ€” so opentree links the repository's skills into each
150
215
  workspace it creates. `opentree skills sync` repairs workspaces that predate
151
216
  this, and `opentree skills list` prints the same inventory for a script.
152
217
 
218
+ ### Plugins
219
+
220
+ opentree is a client of the open [Agent Plugins](https://agent-plugins.org)
221
+ standard: a plugin is a directory with a `plugin.json` manifest, skills under
222
+ `skills/`, and optionally an `mcp.json` naming MCP servers.
223
+
224
+ ```bash
225
+ opentree plugins add https://github.com/someone/their-plugin
226
+ opentree plugins list # what each plugin declares, secrets masked
227
+ opentree plugins remove <name> # the store entry and every link into it
228
+ ```
229
+
230
+ Install one and every agent in every worktree can use the skills it bundles:
231
+ the clone lands once per machine in `~/.opentree/plugins`, is validated
232
+ against the spec โ€” a broken manifest refuses the whole plugin, a broken skill
233
+ or server entry costs only itself and is reported โ€” and its skills are linked
234
+ into each agent's own user-scope tree. On the Skills tab they wear their
235
+ provenance (`plugin:<name>` and `ro`); the Plugins tab shows each plugin as a
236
+ unit, with `a` to install, `x` to remove, and every declared MCP server named.
237
+
238
+ Declared is as far as it goes: opentree lists a plugin's MCP servers with
239
+ their env and header values masked, and neither launches them nor writes them
240
+ into any agent's own configuration. Nothing a plugin ships is executed.
241
+
153
242
  ### Talking to the agent
154
243
 
155
244
  opentree talks to agents over the [Agent Client Protocol](https://agentclientprotocol.com)
@@ -191,7 +280,7 @@ agent's own logo, in its own colours:
191
280
  ```
192
281
  โ–โ–›โ–ˆโ–ˆโ–ˆโ–œโ–Œ Claude Code
193
282
  โ–โ–œโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–›โ–˜ fix-auth
194
- โ–˜โ–˜ โ–โ– ~/src/myrepo/.opentree/fix-auth
283
+ โ–˜โ–˜ โ–โ– ~/.opentree/worktrees/myrepo/fix-auth
195
284
  ```
196
285
 
197
286
  | Key | |
@@ -199,15 +288,34 @@ agent's own logo, in its own colours:
199
288
  | `enter` | send |
200
289
  | `shift+enter` | newline โ€” `ctrl+j` where the terminal cannot report modifiers |
201
290
  | `โ†‘` / `โ†“` | walk back through the messages already sent, and forward again |
202
- | `/` | slash commands โ€” the agent's own, plus `/resume`, `/login`, `/model` and the rest |
291
+ | `/` | slash commands โ€” the agent's own, plus `/resume`, `/login`, `/model`, `/shell`, `/export` and the rest |
203
292
  | `@` | attach a file from this worktree |
204
293
  | `ctrl+v` | paste โ€” an image on the clipboard is attached, anything else is text |
205
- | `esc` | interrupt the current turn |
206
- | `shift+tab` | cycle the agent's mode (plan / build / โ€ฆ) |
294
+ | `esc` | interrupt the current turn โ€” or clear an unsent message (`โ†‘` brings it back) |
295
+ | `shift+tab` | cycle the agent's mode (plan / build / โ€ฆ) โ€” Claude Code's plan mode and accept-edits included |
207
296
  | `ctrl+g` | settings โ€” model, reasoning effort, anything else the agent declares |
208
297
  | `ctrl+o` | show or hide the agent's reasoning |
298
+ | `ctrl+x` | expand what the last tool call held back, and fold it again |
299
+ | `ctrl+r` | retry a failed turn โ€” the same message, pasted images included |
300
+ | `ctrl+y` | copy โ€” the last reply, any code block in it, the last tool's output, or the whole conversation as markdown |
301
+ | `ctrl+f` | find in the conversation โ€” `ctrl+n` / `ctrl+p` step through the matches, `esc` leaves the log where it stands |
302
+ | `drag` | select text โ€” it goes to the clipboard when the button comes up; double-click takes a word, triple-click a row. `shift+drag` (`option+drag` in iTerm2) is the terminal's own selection, which still works |
303
+ | `click` | a permission option answers it; a `โ€ฆ 42 more lines` row opens the way `ctrl+x` does |
209
304
  | `?` | every key |
210
305
 
306
+ **Prose.** The agent's replies render as markdown while they stream: emphasis,
307
+ headings, lists, quotes, and fenced code on its own background, syntax-coloured
308
+ when the fence names a language โ€” code is never rewrapped, so its indentation
309
+ keeps meaning. A half-arrived fence already reads
310
+ as code and never snaps back to prose; a lone `**` stays two asterisks until
311
+ its closer arrives. Tables render as the text they are.
312
+
313
+ **Tool output.** A tool row shows a few lines of what it did โ€” the diff, or
314
+ what it printed โ€” and holds the rest back behind `โ€ฆ 42 more lines ยท ctrl+x`.
315
+ `ctrl+x` opens the most recent held-back row where you are reading, up to 500
316
+ lines; the same key folds it again. There is no cursor to place: the row you
317
+ want open is the one that just said how much it was hiding.
318
+
211
319
  **Newlines.** `shift+enter` breaks the line instead of sending it, with nothing
212
320
  to configure. A terminal left to itself sends a bare carriage return for
213
321
  `shift+enter` โ€” the same byte `enter` sends, and nothing downstream can tell the
@@ -236,6 +344,17 @@ forward again, and coming back past the newest returns whatever was half typed
236
344
  when you started looking โ€” so a prompt worth repeating, or repeating with one
237
345
  word changed, is a keypress away rather than a retype. Inside a message the
238
346
  arrows still move the cursor: they only recall from its first and last row.
347
+ They are kept per workspace, under `~/.opentree/history`, so a window closed
348
+ and reopened โ€” or a chat restarted after its agent died โ€” still has the last
349
+ two hundred.
350
+
351
+ **Messages you have not sent yet.** Enter while the agent is still working
352
+ queues the message instead of dropping it: it waits as a `โณ` line above the
353
+ box, one fires after each finished turn โ€” so each answer still gets read
354
+ before the next question goes โ€” and backspace on an empty box takes the
355
+ newest one back to be edited, pasted images and all. A failed turn drops the
356
+ queue by name rather than firing into a broken session; `โ†‘` still has every
357
+ message.
239
358
 
240
359
  **Earlier conversations.** `/resume` lists what this worktree has already
241
360
  talked about โ€” newest first, by what each conversation was about โ€” and picking
@@ -243,6 +362,10 @@ one reopens it in place, history and all. The list is the agent's own where it
243
362
  keeps one, merged with what opentree recorded itself, so the command works the
244
363
  same whichever agent is running.
245
364
 
365
+ **Getting the conversation out.** `/export` writes it as markdown โ€” what was
366
+ said, what was run and what it printed โ€” to `~/.opentree/exports`, never into
367
+ the worktree, and says where. `ctrl+y` puts the same document on the clipboard.
368
+
246
369
  The agent's live model, mode and effort sit on the right of the input, next to
247
370
  the running context and cost. `ctrl+c` takes you back to the workspace list and
248
371
  leaves the chat running: the agent keeps working, its row keeps reporting, and
@@ -257,12 +380,132 @@ is queued rather than refused.
257
380
  **Which agents.** OpenCode, GitHub Copilot CLI and Gemini CLI serve ACP
258
381
  themselves, so having the binary is the whole setup. Claude Code is reached
259
382
  through the `claude-agent-acp` adapter, which opentree installs on request into
260
- `~/.opentree/tools` rather than your global npm root โ€” press `A` in the
261
- dashboard, pick Claude Code, and it offers the download (340MB, needs `node`).
383
+ `~/.opentree/tools` rather than your global npm root โ€” open the dashboard's
384
+ Agents tab, pick Claude Code, and it offers the download (340MB, needs `node`).
385
+
386
+ Those four ship with opentree; the rest of the ecosystem comes from the
387
+ [ACP Registry](https://agentclientprotocol.com/get-started/registry).
388
+ `opentree agents add <id>` installs any agent it lists, and the install is
389
+ first-class everywhere the four are โ€” the picker, chats, fan-outs,
390
+ per-workspace overrides. opentree drives agents over ACP and nothing else, so
391
+ an agent without an ACP server has no way in โ€” but shipping support and one
392
+ registry entry is now the whole path in. See
393
+ [Agents from the ACP Registry](#agents-from-the-acp-registry).
394
+
395
+ ### Autopilot
396
+
397
+ The dashboard shows everything, but without autopilot you are still the event
398
+ loop: watch the badge, forward the failure, press `p`. Autopilot closes the
399
+ loop per workspace โ€” when a turn ends, the project's check command decides
400
+ whether the work is done:
401
+
402
+ ```toml
403
+ [workspace]
404
+ check = "make test" # the same thing a contributor runs before pushing
405
+ ```
406
+
407
+ - The check runs in the worktree, streaming into the chat log. A failure goes
408
+ back to the agent as the next prompt โ€” the tail of the output, where the
409
+ test runner's summary is โ€” and the loop repeats.
410
+ - A pass publishes: push what origin is missing, then create the PR with a
411
+ generated title and body, or bring the existing one up to date. Never a
412
+ duplicate โ€” if the agent already pushed or opened the PR itself, publishing
413
+ notices and stands down.
414
+ - You get a `pr_ready` notification when the PR exists, through the same
415
+ surfaces as `blocked`.
416
+
417
+ Switch it per workspace: `P` in the dashboard, `/autopilot` in the chat, or
418
+
419
+ ```bash
420
+ opentree auto feat/add-dark-mode on # off; bare reports where the loop stands
421
+ ```
422
+
423
+ The row shows `auto` while the loop owns a workspace, and `checkingโ€ฆ` /
424
+ `publishingโ€ฆ` while it works.
425
+
426
+ Autopilot knows when to stand down. A cancelled or refused turn never triggers
427
+ the check. Your queued message always runs first, and any message from you
428
+ resets the loop. Five autopilot-fed turns without a green check and it halts โ€”
429
+ the row says `auto ยท halted`, the error log says why, and your next message
430
+ starts it again. `check` is executable code from a tracked file, so it sits
431
+ behind the same trust gate as `setup` and `run`: the first run asks, once,
432
+ showing the exact text.
433
+
434
+ Without a `check` command autopilot still pushes and keeps the PR current
435
+ after each turn โ€” for projects whose CI is the check.
436
+
437
+ **Once the PR exists, autopilot watches it.** Every two minutes the chat asks
438
+ GitHub what is new: a failing check gets forwarded with the tail of its
439
+ Actions log, new review comments get forwarded the way `R` sends them โ€” each
440
+ as its own turn, CI before reviews, the moment the agent is free. Nothing is
441
+ sent twice: the watermarks live in `state.json`, keyed on the commit a failure
442
+ was reported for and the fingerprint of the review set, so a new push re-arms
443
+ CI forwarding by itself and a reopened window does not repeat its
444
+ predecessor. `opentree ci <branch>` sends the same CI report by hand,
445
+ autopilot or not.
446
+
447
+ ### Dispatch
448
+
449
+ The whole pipeline in one command:
450
+
451
+ ```bash
452
+ opentree dispatch 42 # issue #42 โ†’ workspace โ†’ agent โ†’ checks โ†’ PR
453
+ opentree dispatch "fix the login race" # the prompt is the task
454
+ opentree dispatch 42 --headless # no attach: wait, print the PR URL, exit
455
+ ```
456
+
457
+ Dispatch creates the workspace (branch `auto-<slug>` in prompt mode), starts
458
+ the agent in its tmux window, switches autopilot on and sends the task. By
459
+ default it attaches so you can watch; `--headless` waits on the chat's socket
460
+ instead and exits with a code a script can branch on:
461
+
462
+ | Code | Meaning |
463
+ | --- | --- |
464
+ | 0 | the PR was published; its URL is on stdout |
465
+ | 1 | autopilot halted (the check kept failing) or reported an error |
466
+ | 2 | the agent stopped, or the chat became unreachable |
467
+ | 3 | blocked on a permission only a human can answer |
468
+ | 4 | `--timeout` (default 30m) elapsed; the workspace is still working |
469
+
470
+ Every failure leaves the workspace alive โ€” `opentree attach` picks up exactly
471
+ where it stopped. Headless can ask nothing, so the repository's `setup` and
472
+ `check` commands must be approved ahead of time with `opentree trust`, and a
473
+ tmux server must be running (`tmux new-session -d` in CI).
474
+
475
+ ### Fan-out
476
+
477
+ Four agents through one protocol makes a comparison no single-agent tool can
478
+ run: the same task, raced.
479
+
480
+ ```bash
481
+ opentree new feat/x --agents claude,opencode,gemini --prompt "add dark mode"
482
+ git log --oneline | opentree new fix/y --agents claude,gemini # or pipe the task in
483
+ ```
262
484
 
263
- Those four are the whole list. opentree drives agents over ACP and nothing else,
264
- so an agent without an ACP server has no way in โ€” if one ships support, it
265
- becomes a single registry entry and everything above applies to it unchanged.
485
+ One sibling workspace per agent โ€” `feat/x-claude`, `feat/x-opencode`,
486
+ `feat/x-gemini` โ€” all from the same base, each running its own agent, every
487
+ one handed the same prompt (queued until its agent is ready). A name a
488
+ sibling would have taken is stepped past with a numeric suffix rather than
489
+ refused. Without `--prompt` or a pipe the siblings start idle, and `m` in the
490
+ dashboard messages whichever you like.
491
+
492
+ The dashboard shows the group as one thing: siblings sort together under
493
+ every sort mode, each row wears a `โ‘‚ feat/x` badge, and the cost, context
494
+ and diff numbers already on every row become the scoreboard. `D` opens the
495
+ comparison โ€” every sibling's diff in one view, the file tree grouped by agent.
496
+
497
+ Then pick:
498
+
499
+ ```bash
500
+ opentree promote feat/x-claude # or W on the row in the dashboard
501
+ ```
502
+
503
+ The winner stays, every other sibling is deleted โ€” worktree, branch, window โ€”
504
+ and the group dissolves. Dirty losers show their diffs and ask first, the way
505
+ delete does. **The winner keeps its suffixed branch name**: `feat/x-claude`
506
+ does not become `feat/x`, because its worktree, chat and any open PR are all
507
+ keyed on the name it has. Rename it on the PR page if the suffix bothers you,
508
+ or not at all.
266
509
 
267
510
  ### Notifications
268
511
 
@@ -276,6 +519,7 @@ when it starts needing you:
276
519
  | `blocked` | the agent stopped to ask for a permission |
277
520
  | `done` | a turn finished |
278
521
  | `stopped` | the agent died, failed to start, or its setup commands failed |
522
+ | `pr_ready` | autopilot opened or updated a pull request |
279
523
 
280
524
  Two surfaces. In tmux the window's own bell rings, which tmux renders as an
281
525
  inverted window name in the status bar until you select that window โ€” no
@@ -298,13 +542,14 @@ until they have been allowed, which is otherwise a feature with no symptom.
298
542
 
299
543
  ```toml
300
544
  [notify]
301
- on = ["blocked", "stopped"] # add "done"; [] switches everything off
302
- desktop = true # false: tmux bell only
545
+ on = ["blocked", "stopped", "pr_ready"] # add "done"; [] switches everything off
546
+ desktop = true # false: tmux bell only
303
547
  ```
304
548
 
305
- `blocked` and `stopped` are on by default and `done` is off, because four agents
306
- finishing turns is a banner every ninety seconds โ€” and a notifier you mute is a
307
- notifier you deleted.
549
+ `blocked`, `stopped` and `pr_ready` are on by default and `done` is off,
550
+ because four agents finishing turns is a banner every ninety seconds โ€” and a
551
+ notifier you mute is a notifier you deleted. `pr_ready` cannot spam: it fires
552
+ only from autopilot, which is opt-in, and only when a publish moved something.
308
553
 
309
554
  This section is read from `~/.config/opentree/opentree.toml` only. A repository's
310
555
  own `opentree.toml` may configure how the project is built; how you like to be
@@ -321,11 +566,20 @@ opentree new <branch-name> [flags]
321
566
  # Examples
322
567
  opentree new feat/user-auth # Create workspace with branch
323
568
  opentree new fix/login-bug --base dev # Branch off 'dev' instead of 'main'
569
+ opentree new feat/x --agent claude # Run claude here, whatever the config says
570
+ opentree new feat/x --agents claude,gemini --prompt "task" # Fan out โ€” see Fan-out
571
+ opentree new feat/x --no-fetch # Branch from the local base as it is
324
572
  ```
325
573
 
574
+ The base is fetched from origin first, and the branch made from `origin/<base>`:
575
+ a `main` last pulled yesterday would otherwise start the workspace a day behind,
576
+ and its PR would carry or conflict with commits already merged. Offline, the
577
+ command says so and branches from the local base; `--no-fetch` skips the fetch
578
+ on purpose. `issue` and `dispatch` do the same.
579
+
326
580
  Creates:
327
581
 
328
- 1. Git worktree at `.opentree/<branch-name>/`
582
+ 1. Git worktree at `~/.opentree/worktrees/<repo>/<branch-name>/` (see [Where worktrees live](#where-worktrees-live))
329
583
  2. New branch (or checks out existing)
330
584
  3. tmux window in `opentree-<repo>` session
331
585
  4. Launches the configured coding agent in the workspace
@@ -358,6 +612,22 @@ opentree attach <branch-name>
358
612
 
359
613
  Attaches to the workspace's tmux window. Detach with `Ctrl+b d`.
360
614
 
615
+ #### Get Into the Worktree
616
+
617
+ ```bash
618
+ opentree path <branch-name> # print the worktree's directory
619
+ cd "$(opentree path feat/x)" # โ€ฆwhich is what it is for
620
+ opentree shell <branch-name> # a shell there, in a tmux window beside the chat
621
+ ```
622
+
623
+ The chat's window is opentree's, holding the conversation. When the agent asks
624
+ for something only a person at a prompt can do โ€” run the tests, check a URL,
625
+ paste back what a command printed โ€” `shell` opens a window of your own in the
626
+ worktree (`<branch>:sh`, reused while it lives) and takes you to it; `/shell`
627
+ in the chat and `t` in the dashboard do the same. `path` prints the directory
628
+ and nothing else, because a branch's directory is not its name: `feat/x` lives
629
+ at `feat-x`, under `~/.opentree/worktrees/<repo>` by default.
630
+
361
631
  #### Show Diff
362
632
 
363
633
  ```bash
@@ -387,7 +657,33 @@ opentree review <branch-name>
387
657
  Fetches the open PR's review comments and sends them to the workspace's agent as
388
658
  a prompt, over the chat's control socket. The chat has to be running, but it
389
659
  doesn't have to be the window you're looking at โ€” and if the agent is mid-turn
390
- the command says so rather than reporting a send that went nowhere.
660
+ the prompt is queued and runs when the turn ends, which the row's badge shows.
661
+
662
+ #### Send CI Failures to the Agent
663
+
664
+ ```bash
665
+ opentree ci <branch-name>
666
+ ```
667
+
668
+ The dashboard's badge says CI is red; this is how the agent learns why: the
669
+ failing checks by name, and the tail of each GitHub Actions log โ€” where the
670
+ test runner's summary is. Same delivery as `review`, over the control socket.
671
+ With autopilot on, this happens by itself.
672
+
673
+ #### Merge the Base In
674
+
675
+ ```bash
676
+ opentree sync <branch-name> # fetch origin's main and merge it into the branch
677
+ opentree sync <branch-name> --ask # โ€ฆand on conflicts, hand the files to the agent
678
+ ```
679
+
680
+ The dashboard's row says `PR open ยท conflicts`; this is what to do about it.
681
+ The base is fetched from origin first โ€” offline, the local one is merged and
682
+ the command says so โ€” and merged rather than rebased, because the branch may
683
+ already be pushed and under review. Conflicts are not a failure: they are
684
+ listed, the merge is left in progress in the worktree with its markers, and
685
+ `--ask` (or `y` in the dashboard's dialog) sends the agent a prompt naming the
686
+ files and asking it to finish the merge.
391
687
 
392
688
  #### Delete Workspace
393
689
 
@@ -396,9 +692,23 @@ opentree delete <branch-name>
396
692
 
397
693
  # Examples
398
694
  opentree delete feat/user-auth
695
+ opentree delete --merged # every workspace whose PR has merged
399
696
  ```
400
697
 
401
- Removes the worktree, kills the tmux window, and deletes the branch. If uncommitted changes are detected, a diff is shown and confirmation is required before proceeding.
698
+ Removes the worktree, kills its tmux windows, and deletes the branch. If uncommitted changes are detected, a diff is shown and confirmation is required before proceeding. `--merged` does the same for every workspace the dashboard has seen merge โ€” one question each for any that still hold something โ€” which is how a week's worth of `merged ยท ready to delete` rows is cleared at once.
699
+
700
+ #### Promote a Fan-out Winner
701
+
702
+ ```bash
703
+ opentree promote <branch-name>
704
+
705
+ # Example
706
+ opentree promote feat/x-claude # keep this sibling; delete feat/x-gemini, feat/x-opencode
707
+ ```
708
+
709
+ Keeps the named sibling, deletes every other member of its fan-out group, and
710
+ dissolves the group. Losers with uncommitted or unpushed work show their diffs
711
+ and ask for confirmation first. The winner keeps its suffixed branch name.
402
712
 
403
713
  #### Install Shell Completion
404
714
 
@@ -414,7 +724,7 @@ Create `opentree.toml` in your repo root or `~/.config/opentree/opentree.toml`.
414
724
 
415
725
  ```toml
416
726
  [worktree]
417
- base_dir = ".opentree" # Where to store worktrees (relative to repo root)
727
+ base_dir = ".opentree" # Where worktrees go โ€” unset: ~/.opentree/worktrees/<repo>; relative: inside the repo
418
728
  default_base = "main" # Default base branch
419
729
 
420
730
  [agent]
@@ -424,6 +734,7 @@ command = "opencode" # Agent to run: "opencode", "claude", "copilot" or
424
734
  seed = [".env", ".npmrc"] # Untracked files to link into each new worktree
425
735
  setup = ["pnpm install --frozen-lockfile"] # Commands run before the agent starts
426
736
  run = "pnpm dev" # Dev server, started on demand, PORT exported
737
+ check = "pnpm test" # What autopilot runs after each agent turn
427
738
 
428
739
  [tmux]
429
740
  session_prefix = "opentree" # Prefix for the tmux session name
@@ -436,6 +747,44 @@ on = ["blocked", "stopped"]
436
747
  desktop = true
437
748
  ```
438
749
 
750
+ ### Where worktrees live
751
+
752
+ Outside the repository, by default: `~/.opentree/worktrees/<repo>/<branch>`,
753
+ where `<repo>` is the name of the repository's directory. Worktrees used to go
754
+ under `<repo>/.opentree`, and every tool that walks a project found the extra
755
+ checkouts โ€” test runners collected their tests twice, `tsc` compiled them,
756
+ linters and formatters walked them, watchers rebuilt on every save an agent
757
+ made, and a worktree without its own `node_modules` quietly resolved the
758
+ parent's. Git was the only tool told to look away. Out of the working tree,
759
+ none of them can see a worktree at all.
760
+
761
+ Two clones with the same directory name get separate directories: the first
762
+ claims `<repo>` with a small `.repo` marker naming it, and the second gets
763
+ `<repo>-<hash>`.
764
+
765
+ To keep worktrees inside the repository โ€” where a plain `find` will meet them โ€”
766
+ set `base_dir` to a relative path, in the repository's own `opentree.toml` or
767
+ globally:
768
+
769
+ ```toml
770
+ [worktree]
771
+ base_dir = ".opentree" # inside the repository, as before
772
+ ```
773
+
774
+ An absolute path, or one under `~`, is accepted from the global config only: a
775
+ cloned repository does not get to point opentree at the rest of your
776
+ filesystem. Workspaces made before a change of `base_dir` stay where they were
777
+ made โ€” opentree finds them through git โ€” and `opentree doctor` says where the
778
+ worktrees of the current setting go, and whether that is inside the working
779
+ tree.
780
+
781
+ The state lives outside the repository too, at
782
+ `~/.opentree/state/opentree-<hash>/state.json`. Nothing of opentree's is written
783
+ into the working tree, and nothing in `.git` is touched, so pre-commit hooks
784
+ that stage everything and automations that refuse a dirty checkout never meet
785
+ opentree. A `state.json` an older release left under `<repo>/.opentree` is
786
+ moved out the first time any command runs.
787
+
439
788
  ### Seeding a Worktree
440
789
 
441
790
  A git worktree carries only what git tracks, so a fresh one has no `.env` and no
@@ -468,7 +817,7 @@ opentree seed detach feat/add-dark-mode .env
468
817
  ```
469
818
 
470
819
  That can also happen by accident: tools that save by renaming over a file
471
- replace the link with an ordinary one. `opentree setup <branch> --check` reports
820
+ replace the link with an ordinary one. `opentree setup <branch> --dry-run` reports
472
821
  which seeded files are still linked and which have quietly detached.
473
822
 
474
823
  ### Setting Up a Worktree
@@ -515,7 +864,7 @@ tearing down a live conversation:
515
864
 
516
865
  ```bash
517
866
  opentree setup feat/add-dark-mode # re-seed, then run the commands here
518
- opentree setup feat/add-dark-mode --check # report what is seeded and what has run
867
+ opentree setup feat/add-dark-mode --dry-run # report what is seeded and what has run
519
868
  ```
520
869
 
521
870
  Both paths write the same marker, so a worktree prepared from the terminal is one
@@ -593,9 +942,9 @@ To use one of the others instead of OpenCode:
593
942
  command = "claude" # or "copilot", or "gemini"
594
943
  ```
595
944
 
596
- Or press `A` in the dashboard to pick from the agents you have installed โ€” it
597
- writes the same config, and offers to fetch the ACP adapter if the agent needs
598
- one. From the CLI:
945
+ Or press `tab` in the dashboard for the Agents tab and pick one โ€” it writes
946
+ the same config, and offers to fetch the ACP adapter if the agent needs one.
947
+ From the CLI:
599
948
 
600
949
  ```bash
601
950
  opentree agents list # what's installed, and which is active
@@ -606,13 +955,56 @@ opentree agents setup claude # fetch its ACP adapter, if it needs one
606
955
  An agent opentree has no ACP spec for is refused up front, when you create a
607
956
  workspace, rather than later inside a chat that cannot start.
608
957
 
958
+ ### Agents from the ACP Registry
959
+
960
+ The four built-in agents are a curated list, not a boundary. The
961
+ [ACP Registry](https://agentclientprotocol.com/get-started/registry) โ€” the
962
+ same index Zed and JetBrains install from โ€” lists every agent that ships an
963
+ ACP server, and opentree installs from it:
964
+
965
+ ```bash
966
+ opentree agents search # what the registry has (add a term to filter)
967
+ opentree agents add devin # install one, into ~/.opentree/registry
968
+ opentree agents use devin # it is a normal agent from here on
969
+ opentree agents update # re-resolve every install against a fresh index
970
+ opentree agents remove devin # delete the install
971
+ ```
972
+
973
+ Installing executes code, so nothing is fetched before you have seen exactly
974
+ what will happen: an npm-distributed agent shows the full install command โ€”
975
+ pinned version, opentree's own prefix, npm's install scripts disabled, the
976
+ same posture as the Claude Code adapter โ€” and a binary-distributed one shows
977
+ the archive URL and the sha256 it will be held to. Each install lands in its
978
+ own directory under `~/.opentree/registry`, wears a `registry` tag in
979
+ `agents list` and the version the index pinned; `agents update` builds the
980
+ new version beside the old and swaps it in only when complete, so a failed
981
+ update leaves the old agent working.
982
+
983
+ The dashboard's Agents tab is the same feature without leaving the
984
+ terminal you are already in: `a` fetches the index and lists it, `enter` on
985
+ an entry shows the same consent card `agents add` prints and installs on
986
+ `y`, `u` and `U` are `agents update`, `x` is `agents remove`. An agent
987
+ installed there is a row the moment the install finishes โ€” no restart.
988
+
989
+ Everything else is indistinguishable from the built-in four:
990
+ `--agents claude,devin,goose` races them, a workspace remembers which one it
991
+ runs, and `opentree doctor` reports them.
992
+ Two honest gaps: a registry entry does not say where its agent keeps skills,
993
+ so the Skills tab leaves registry agents out rather than guessing; and the
994
+ few agents distributed only via PyPI's `uvx` are listed by `agents search`
995
+ but not installable yet.
996
+
997
+ Ordinary commands never touch the network โ€” the loader reads installed
998
+ agents from disk, and only `agents search`, `add` and `update` fetch the
999
+ index. Offline, the last index this machine saw answers, with its age noted.
1000
+
609
1001
  ## How It Works
610
1002
 
611
- 1. **Worktrees**: Git worktrees allow multiple checkouts of the same repo in different directories. Each workspace lives in `.opentree/<branch-name>/`.
1003
+ 1. **Worktrees**: Git worktrees allow multiple checkouts of the same repo in different directories. Each workspace lives in `~/.opentree/worktrees/<repo>/<branch-name>/` โ€” outside the working tree, so the project's own tools never see it โ€” unless `base_dir` says otherwise.
612
1004
 
613
1005
  2. **tmux Orchestration**: A single tmux session (`opentree-<repo>`) manages all workspaces. Each workspace = one tmux window. Attach to work, detach to switch.
614
1006
 
615
- 3. **State Persistence**: Workspace metadata (branch, created time, agent, issue number) stored in `.opentree/state.json`.
1007
+ 3. **State Persistence**: Workspace metadata (branch, created time, agent, issue number) stored in `~/.opentree/state/<repo-key>/state.json`, outside the repository.
616
1008
 
617
1009
  4. **Agent Integration**: When creating a workspace, opentree launches your configured agent inside the tmux window, ready to code. With no agent configured, it uses the first supported agent found on your PATH.
618
1010
 
@@ -637,6 +1029,11 @@ opentree new fix/header-overflow
637
1029
  # (work on bugfix...)
638
1030
  # (detach)
639
1031
 
1032
+ # Not sure which agent will do a refactor best? Race them
1033
+ opentree new refactor/auth --agents claude,opencode,gemini --prompt "extract the auth middleware"
1034
+ # (press D in the dashboard to compare, then promote the winner)
1035
+ opentree promote refactor/auth-claude
1036
+
640
1037
  # Review changes for first feature
641
1038
  opentree diff feat/add-dark-mode
642
1039
 
@@ -695,8 +1092,8 @@ Install OpenCode from [github.com/anomalyco/opencode](https://github.com/anomaly
695
1092
 
696
1093
  ### The chat says the agent needs an adapter
697
1094
 
698
- Claude Code speaks ACP through `claude-agent-acp`. Press `A` in the dashboard,
699
- select Claude Code, and accept the download โ€” it installs to `~/.opentree/tools`
1095
+ Claude Code speaks ACP through `claude-agent-acp`. Open the dashboard's Agents
1096
+ tab, select Claude Code, and accept the download โ€” it installs to `~/.opentree/tools`
700
1097
  and needs `node` on your PATH. If you already have the package installed
701
1098
  globally, opentree uses that instead of fetching a second copy.
702
1099
 
@@ -727,7 +1124,7 @@ gh auth login
727
1124
 
728
1125
  ### Workspaces not appearing in TUI
729
1126
 
730
- State file might be corrupted. Check `.opentree/state.json` or delete and recreate workspaces.
1127
+ State file might be corrupted. `opentree doctor` prints where it is (under `~/.opentree/state`); delete it and recreate workspaces.
731
1128
 
732
1129
  ## Contributing
733
1130
 
@@ -749,7 +1146,7 @@ go build -o opentree ./cmd/opentree
749
1146
 
750
1147
  | package | what it owns |
751
1148
  | --- | --- |
752
- | `tui` | the dashboard: the workspace list, the Skills and Servers tabs |
1149
+ | `tui` | the dashboard: the workspace list, the Agents, Skills, Plugins and Servers tabs |
753
1150
  | `chat` | the conversation view, and the control socket the dashboard reaches it through |
754
1151
  | `acp` | the Agent Client Protocol client โ€” the agent subprocess and its stdio |
755
1152
  | `workspace` | a workspace's lifecycle, over the four below it |
@@ -759,6 +1156,8 @@ go build -o opentree ./cmd/opentree
759
1156
  | `github` | `gh`, for PRs, issues and CI status |
760
1157
  | `bootstrap` | seeding a worktree, running its setup, and the trust gate over those commands |
761
1158
  | `skills` | propagating agent skills into worktrees |
1159
+ | `plugins` | the Agent Plugins store: install, validate, list, remove |
1160
+ | `registry` | the ACP Registry client: the index, its cache, and installed agents |
762
1161
  | `config` | `opentree.toml` and the agent registry |
763
1162
  | `notify`, `diag`, `ui`, `fsutil`, `gitutil` | the small shared pieces |
764
1163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axelgar/opentree",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Git worktree manager CLI for orchestrating parallel AI coding sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,10 +17,10 @@
17
17
  "NOTICE"
18
18
  ],
19
19
  "optionalDependencies": {
20
- "@axelgar/opentree-linux-x64": "1.1.0",
21
- "@axelgar/opentree-linux-arm64": "1.1.0",
22
- "@axelgar/opentree-darwin-x64": "1.1.0",
23
- "@axelgar/opentree-darwin-arm64": "1.1.0"
20
+ "@axelgar/opentree-linux-x64": "1.3.0",
21
+ "@axelgar/opentree-linux-arm64": "1.3.0",
22
+ "@axelgar/opentree-darwin-x64": "1.3.0",
23
+ "@axelgar/opentree-darwin-arm64": "1.3.0"
24
24
  },
25
25
  "engines": {
26
26
  "node": ">=18"