@axelgar/opentree 0.2.0 → 1.0.1
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.
- package/README.md +374 -74
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -6,12 +6,13 @@ Think [Conductor](https://conductor.build), but for the terminal.
|
|
|
6
6
|
|
|
7
7
|
opentree is a cross-platform CLI tool that manages multiple AI coding agent sessions. Each session runs in an isolated git worktree with its own branch, orchestrated via tmux. Perfect for working on multiple features/fixes simultaneously without context-switching overhead.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+

|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- **🌳 Isolated Workspaces**: Each workspace = git worktree + branch + tmux window
|
|
14
|
-
- **🤖 Agent Integration**: Launch
|
|
14
|
+
- **🤖 Agent Integration**: Launch your agent automatically in each workspace
|
|
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
|
|
15
16
|
- **📊 TUI Dashboard**: Interactive terminal UI for managing workspaces (press `?` for help)
|
|
16
17
|
- **🔀 Parallel Development**: Work on multiple branches simultaneously without checkout overhead
|
|
17
18
|
- **📝 Diff Viewer**: Review changes before committing
|
|
@@ -26,8 +27,9 @@ opentree is a cross-platform CLI tool that manages multiple AI coding agent sess
|
|
|
26
27
|
|
|
27
28
|
- **Git** (2.5+) - for worktree support
|
|
28
29
|
- **tmux** (3.0+) - for session orchestration
|
|
29
|
-
- **
|
|
30
|
+
- **A coding agent** (optional) - OpenCode (the default), Claude Code, GitHub Copilot CLI or Gemini CLI
|
|
30
31
|
- **GitHub CLI** (`gh`) (optional) - for PR creation and issue fetching ([install](https://cli.github.com/))
|
|
32
|
+
- **Node** (optional) - only to run Claude Code through its ACP adapter
|
|
31
33
|
|
|
32
34
|
## Installation
|
|
33
35
|
|
|
@@ -75,6 +77,8 @@ opentree attach feat/add-auth # Attach to tmux window
|
|
|
75
77
|
opentree diff feat/add-auth # Review changes
|
|
76
78
|
opentree pr feat/add-auth # Create GitHub PR
|
|
77
79
|
opentree delete feat/add-auth # Clean up workspace
|
|
80
|
+
opentree skills list # See every agent skill on this machine
|
|
81
|
+
opentree skills sync # Give every agent and workspace the repo's skills
|
|
78
82
|
```
|
|
79
83
|
|
|
80
84
|
## Usage
|
|
@@ -101,14 +105,180 @@ opentree
|
|
|
101
105
|
- `p` - Create PR for selected workspace (auto-generates title and body from commits)
|
|
102
106
|
- `o` - Open PR in browser
|
|
103
107
|
- `x` - Delete selected workspace (shows diff confirmation if uncommitted changes)
|
|
108
|
+
- `R` - Send the workspace's open PR review comments to its agent
|
|
109
|
+
- `w` - Start or stop the workspace's dev server
|
|
110
|
+
- `b` - Jump to the workspace that has been waiting longest on a permission (press again to cycle)
|
|
104
111
|
- `space` - Toggle multi-select on current workspace
|
|
105
112
|
- `/` - Filter workspaces by name
|
|
106
113
|
- `s` - Cycle sort order (name → age → activity → PR)
|
|
107
114
|
- `E` - Toggle error log
|
|
115
|
+
- `tab` - Switch between Workspaces, Skills and Servers
|
|
108
116
|
- `?` - Toggle full help
|
|
109
117
|
- `q` - Quit
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
Each row also carries what its agent is doing — working, waiting on a
|
|
120
|
+
permission, stopped — plus cost and context use, read live from the chat's
|
|
121
|
+
control socket. Open PRs show **CI check status** badges.
|
|
122
|
+
|
|
123
|
+
### Skills
|
|
124
|
+
|
|
125
|
+
Skills are a filesystem convention rather than anything an agent exposes over
|
|
126
|
+
its API — a directory holding a `SKILL.md` — so opentree reads them directly.
|
|
127
|
+
Press `tab` for the inventory: every skill on the machine, which agents can
|
|
128
|
+
actually use each one, and what each agent will do with it.
|
|
129
|
+
|
|
130
|
+
- `enter` - Open the SKILL.md in `$EDITOR`
|
|
131
|
+
- `a` - Add a skill from a git URL
|
|
132
|
+
- `c` - Copy a skill into another agent's directory
|
|
133
|
+
- `x` - Delete a skill
|
|
134
|
+
- `t` - Switch a skill off for the agents that can be told
|
|
135
|
+
- `l` - Link the repository's skills to every agent and workspace that is missing them
|
|
136
|
+
- `v` - Ask the agent itself what it loaded, and flag anything the list got wrong. Gemini keeps its skills out of the protocol, so it cannot be asked
|
|
137
|
+
|
|
138
|
+
A `git worktree` carries only what git tracks, and most repositories leave
|
|
139
|
+
their skills untracked — so opentree links the repository's skills into each
|
|
140
|
+
workspace it creates. `opentree skills sync` repairs workspaces that predate
|
|
141
|
+
this, and `opentree skills list` prints the same inventory for a script.
|
|
142
|
+
|
|
143
|
+
### Talking to the agent
|
|
144
|
+
|
|
145
|
+
opentree talks to agents over the [Agent Client Protocol](https://agentclientprotocol.com)
|
|
146
|
+
(ACP) and draws the conversation itself, rather than handing the tmux window to
|
|
147
|
+
the agent's own TUI. You get the same worktree-per-branch
|
|
148
|
+
flow, but the agent's turns, tool calls, diffs, what each tool printed, and
|
|
149
|
+
permission prompts are rendered by opentree, which means the dashboard knows
|
|
150
|
+
what every agent is doing without scraping its output.
|
|
151
|
+
|
|
152
|
+
Press `Enter` on a workspace to attach to its chat:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
fix-auth ◆ OpenCode claude-sonnet-4.6 · plan · 12% ctx · $0.0431
|
|
156
|
+
|
|
157
|
+
┃ add a rate limiter to the login handler
|
|
158
|
+
|
|
159
|
+
◆ Adding one keyed by client IP, and a test for the burst case.
|
|
160
|
+
✓ grep -rn rate.Limiter pkg/
|
|
161
|
+
pkg/api/throttle.go:14: var limiter = rate.NewLimiter(rate.Every(time.Minute), 60)
|
|
162
|
+
✓ pkg/auth/login.go +18 -2
|
|
163
|
+
+ limiter := rate.NewLimiter(rate.Every(time.Second), 5)
|
|
164
|
+
⠹ go test ./pkg/auth/
|
|
165
|
+
|
|
166
|
+
╭──────────────────────────────────────╮
|
|
167
|
+
│ go test ./pkg/auth/ │
|
|
168
|
+
│ [a] Allow once │
|
|
169
|
+
│ [A] Always allow │
|
|
170
|
+
│ [d] Reject │
|
|
171
|
+
╰──────────────────────────────────────╯
|
|
172
|
+
permission needed · esc to cancel
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Each agent has its own mark and colour — `◆` for OpenCode, `✻` for Claude Code,
|
|
176
|
+
`◉` for GitHub Copilot, `✦` for Gemini CLI —
|
|
177
|
+
so the chat header and every workspace row in the dashboard say which agent you
|
|
178
|
+
are dealing with without being read word by word. An empty chat opens on the
|
|
179
|
+
agent's own logo, in its own colours:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
▐▛███▜▌ Claude Code
|
|
183
|
+
▝▜█████▛▘ fix-auth
|
|
184
|
+
▘▘ ▝▝ ~/src/myrepo/.opentree/fix-auth
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
| Key | |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| `enter` | send |
|
|
190
|
+
| `ctrl+j` | newline |
|
|
191
|
+
| `/` | slash commands — the agent's own, plus `/resume`, `/login`, `/model` and the rest |
|
|
192
|
+
| `@` | attach a file from this worktree |
|
|
193
|
+
| `ctrl+v` | paste — an image on the clipboard is attached, anything else is text |
|
|
194
|
+
| `esc` | interrupt the current turn |
|
|
195
|
+
| `shift+tab` | cycle the agent's mode (plan / build / …) |
|
|
196
|
+
| `ctrl+g` | settings — model, reasoning effort, anything else the agent declares |
|
|
197
|
+
| `ctrl+o` | show or hide the agent's reasoning |
|
|
198
|
+
| `?` | every key |
|
|
199
|
+
|
|
200
|
+
**Images.** Press `ctrl+v` to attach a screenshot from the clipboard, or drag one
|
|
201
|
+
onto the terminal. Either way the path collapses into `[image · shot.png · 412 KB]`
|
|
202
|
+
in the message you are writing — backspace over it and the attachment goes with
|
|
203
|
+
it — and it travels to the agent as a real image block. On macOS
|
|
204
|
+
that is `ctrl+v` and not `cmd+v`: `cmd+v` is the terminal's own paste, and a
|
|
205
|
+
terminal asked to paste a picture sends nothing at all. An agent that does not
|
|
206
|
+
take images gets the path as a link instead, and the chat says so rather than
|
|
207
|
+
letting the difference go unnoticed.
|
|
208
|
+
|
|
209
|
+
**Earlier conversations.** `/resume` lists what this worktree has already
|
|
210
|
+
talked about — newest first, by what each conversation was about — and picking
|
|
211
|
+
one reopens it in place, history and all. The list is the agent's own where it
|
|
212
|
+
keeps one, merged with what opentree recorded itself, so the command works the
|
|
213
|
+
same whichever agent is running.
|
|
214
|
+
|
|
215
|
+
The agent's live model, mode and effort sit on the right of the input, next to
|
|
216
|
+
the running context and cost. `ctrl+c` takes you back to the workspace list and
|
|
217
|
+
leaves the chat running: the agent keeps working, its row keeps reporting, and
|
|
218
|
+
attaching again drops you straight back into the conversation.
|
|
219
|
+
|
|
220
|
+
**From the dashboard.** You don't have to attach to drive a chat. With a
|
|
221
|
+
workspace selected, `m` sends it a prompt, `a` answers a pending permission
|
|
222
|
+
request, and `c` interrupts the current turn — the row shows what the agent is
|
|
223
|
+
doing, what it's waiting on, and what it has cost. A prompt sent to a busy agent
|
|
224
|
+
is queued rather than refused.
|
|
225
|
+
|
|
226
|
+
**Which agents.** OpenCode, GitHub Copilot CLI and Gemini CLI serve ACP
|
|
227
|
+
themselves, so having the binary is the whole setup. Claude Code is reached
|
|
228
|
+
through the `claude-agent-acp` adapter, which opentree installs on request into
|
|
229
|
+
`~/.opentree/tools` rather than your global npm root — press `A` in the
|
|
230
|
+
dashboard, pick Claude Code, and it offers the download (303MB, needs `node`).
|
|
231
|
+
|
|
232
|
+
Those four are the whole list. opentree drives agents over ACP and nothing else,
|
|
233
|
+
so an agent without an ACP server has no way in — if one ships support, it
|
|
234
|
+
becomes a single registry entry and everything above applies to it unchanged.
|
|
235
|
+
|
|
236
|
+
### Notifications
|
|
237
|
+
|
|
238
|
+
The cost of running four agents at once is that idleness becomes invisible: the
|
|
239
|
+
one workspace blocked on a permission prompt looks exactly like the three that
|
|
240
|
+
are working, unless you are staring at the list. So each chat says something
|
|
241
|
+
when it starts needing you:
|
|
242
|
+
|
|
243
|
+
| Event | |
|
|
244
|
+
| --- | --- |
|
|
245
|
+
| `blocked` | the agent stopped to ask for a permission |
|
|
246
|
+
| `done` | a turn finished |
|
|
247
|
+
| `stopped` | the agent died, failed to start, or its setup commands failed |
|
|
248
|
+
|
|
249
|
+
Two surfaces. In tmux the window's own bell rings, which tmux renders as an
|
|
250
|
+
inverted window name in the status bar until you select that window — no
|
|
251
|
+
configuration, and it clears itself. Outside the terminal, a desktop banner
|
|
252
|
+
(`osascript` on macOS, `notify-send` on Linux) reaches you with the terminal
|
|
253
|
+
behind a browser or closed.
|
|
254
|
+
|
|
255
|
+
Nothing is sent while you are looking at the window it happened in, and nothing
|
|
256
|
+
at all when the chat is not running inside tmux. The banners are signposts
|
|
257
|
+
rather than buttons: pressing `b` in the dashboard is what takes you to the
|
|
258
|
+
workspace that has been waiting longest, and pressing it again walks the rest.
|
|
259
|
+
Each waiting row says how long it has been at it — `blocked 12m`.
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
opentree notify test # one of each, through the surfaces you have
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Worth running once: macOS silently drops notifications sent by `osascript`
|
|
266
|
+
until they have been allowed, which is otherwise a feature with no symptom.
|
|
267
|
+
|
|
268
|
+
```toml
|
|
269
|
+
[notify]
|
|
270
|
+
on = ["blocked", "stopped"] # add "done"; [] switches everything off
|
|
271
|
+
desktop = true # false: tmux bell only
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`blocked` and `stopped` are on by default and `done` is off, because four agents
|
|
275
|
+
finishing turns is a banner every ninety seconds — and a notifier you mute is a
|
|
276
|
+
notifier you deleted.
|
|
277
|
+
|
|
278
|
+
This section is read from `~/.config/opentree/opentree.toml` only. A repository's
|
|
279
|
+
own `opentree.toml` may configure how the project is built; how you like to be
|
|
280
|
+
interrupted is yours, and a cloned repository does not get to start sending you
|
|
281
|
+
desktop banners.
|
|
112
282
|
|
|
113
283
|
### CLI Mode (Direct Commands)
|
|
114
284
|
|
|
@@ -177,6 +347,17 @@ opentree pr feat/user-auth --title "Add user auth" --body "..." # Non-interactiv
|
|
|
177
347
|
|
|
178
348
|
Requires GitHub CLI (`gh`) to be authenticated.
|
|
179
349
|
|
|
350
|
+
#### Send PR Reviews to the Agent
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
opentree review <branch-name>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Fetches the open PR's review comments and sends them to the workspace's agent as
|
|
357
|
+
a prompt, over the chat's control socket. The chat has to be running, but it
|
|
358
|
+
doesn't have to be the window you're looking at — and if the agent is mid-turn
|
|
359
|
+
the command says so rather than reporting a send that went nowhere.
|
|
360
|
+
|
|
180
361
|
#### Delete Workspace
|
|
181
362
|
|
|
182
363
|
```bash
|
|
@@ -206,101 +387,194 @@ base_dir = ".opentree" # Where to store worktrees (relative to repo root)
|
|
|
206
387
|
default_base = "main" # Default base branch
|
|
207
388
|
|
|
208
389
|
[agent]
|
|
209
|
-
command = "opencode" #
|
|
210
|
-
|
|
390
|
+
command = "opencode" # Agent to run: "opencode", "claude", "copilot" or "gemini"
|
|
391
|
+
|
|
392
|
+
[workspace]
|
|
393
|
+
seed = [".env", ".npmrc"] # Untracked files to link into each new worktree
|
|
394
|
+
setup = ["pnpm install --frozen-lockfile"] # Commands run before the agent starts
|
|
395
|
+
run = "pnpm dev" # Dev server, started on demand, PORT exported
|
|
211
396
|
|
|
212
397
|
[tmux]
|
|
213
398
|
session_prefix = "opentree" # Prefix for the tmux session name
|
|
214
399
|
|
|
215
400
|
[github]
|
|
216
401
|
auto_push = true # Push branch before creating a PR (set false to push manually)
|
|
402
|
+
|
|
403
|
+
[notify] # Global config only — see Notifications
|
|
404
|
+
on = ["blocked", "stopped"]
|
|
405
|
+
desktop = true
|
|
217
406
|
```
|
|
218
407
|
|
|
219
|
-
###
|
|
408
|
+
### Seeding a Worktree
|
|
220
409
|
|
|
221
|
-
|
|
410
|
+
A git worktree carries only what git tracks, so a fresh one has no `.env` and no
|
|
411
|
+
`.npmrc` — and the agent's first turn goes on discovering that. List the
|
|
412
|
+
untracked files a worktree needs and opentree links them in as it creates one:
|
|
222
413
|
|
|
223
414
|
```toml
|
|
224
|
-
[
|
|
225
|
-
|
|
226
|
-
|
|
415
|
+
[workspace]
|
|
416
|
+
seed = [".env", ".npmrc", "config/local.json"]
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Each entry is a path relative to the repository root, and it lands at the same
|
|
420
|
+
path inside the worktree. They are symlinks rather than copies: one credential
|
|
421
|
+
set, shared, so rotating a token in the repository rotates it in every worktree
|
|
422
|
+
instead of in one out of five.
|
|
423
|
+
|
|
424
|
+
Files only. A directory is refused — `node_modules` is the output of an install,
|
|
425
|
+
not a file to link, and a worktree that deletes a linked one has just emptied
|
|
426
|
+
your main checkout's. A path that leaves the repository, by `..` or through a
|
|
427
|
+
symlink, is refused when the workspace is created rather than seeded quietly.
|
|
428
|
+
|
|
429
|
+
A file the repository does not have is skipped, and one the branch tracks itself
|
|
430
|
+
is left alone: git checking it out is the signal that the branch has its own.
|
|
431
|
+
|
|
432
|
+
When one branch has to change a shared file, detach it — the link becomes that
|
|
433
|
+
worktree's own copy, keeping what was in it:
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
opentree seed detach feat/add-dark-mode .env
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
That can also happen by accident: tools that save by renaming over a file
|
|
440
|
+
replace the link with an ordinary one. `opentree setup <branch> --check` reports
|
|
441
|
+
which seeded files are still linked and which have quietly detached.
|
|
442
|
+
|
|
443
|
+
### Setting Up a Worktree
|
|
444
|
+
|
|
445
|
+
Seeding puts config where git could not. Setup is the other half — what has to
|
|
446
|
+
be built rather than copied:
|
|
447
|
+
|
|
448
|
+
```toml
|
|
449
|
+
[workspace]
|
|
450
|
+
setup = ["pnpm install --frozen-lockfile"]
|
|
227
451
|
```
|
|
228
452
|
|
|
229
|
-
|
|
453
|
+
The commands run as the first phase of the chat, in the worktree, with their
|
|
454
|
+
output streaming into the window. The agent starts when they finish. That is the
|
|
455
|
+
point of running them there: an agent that starts against a worktree with no
|
|
456
|
+
`node_modules` spends its first turn discovering it, and may "fix" your lockfile
|
|
457
|
+
on the way.
|
|
230
458
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
459
|
+
While they run the dashboard shows the workspace as `setting up…`. Nothing is
|
|
460
|
+
timed out — a warm install is two seconds and a cold `cargo build` is twenty
|
|
461
|
+
minutes — so `esc` is how a hung one ends, and it stops the whole process tree
|
|
462
|
+
rather than just the shell. If a command fails, the panel offers `[r]` to try
|
|
463
|
+
again and `[s]` to start the agent anyway, and the failure is recorded in the
|
|
464
|
+
dashboard's error log (`E`). It is never pasted into the conversation: whether
|
|
465
|
+
the agent should see it is your call.
|
|
235
466
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
467
|
+
Setup runs once per worktree. It runs again when you edit the commands, and not
|
|
468
|
+
otherwise — losing a chat window relaunches one, and reinstalling on every attach
|
|
469
|
+
would make attaching cost a minute.
|
|
470
|
+
|
|
471
|
+
Not sure what to put in the block? opentree will read the project and propose
|
|
472
|
+
one, from `package.json` or a `Procfile`:
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
opentree setup --suggest
|
|
241
476
|
```
|
|
242
477
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
hour ago doesn't look the same as one that just landed:
|
|
478
|
+
It prints; it never writes. What lands in `opentree.toml` is committed, runs on
|
|
479
|
+
every machine that clones the repository, and is approved by a prompt that means
|
|
480
|
+
nothing if opentree wrote the thing being approved.
|
|
247
481
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
| `in_progress` | recent | `working…` | actively generating |
|
|
251
|
-
| `in_progress` | stale | `stalled · 40m ago` | turn never ended — likely dead session |
|
|
252
|
-
| `needs_input` | recent | `waiting · your turn`| just stopped — your move |
|
|
253
|
-
| `needs_input` | stale | `idle · 2h ago` | parked; nobody's touched it |
|
|
482
|
+
To repair a worktree, or run a setup you skipped, without restarting a chat and
|
|
483
|
+
tearing down a live conversation:
|
|
254
484
|
|
|
255
|
-
|
|
485
|
+
```bash
|
|
486
|
+
opentree setup feat/add-dark-mode # re-seed, then run the commands here
|
|
487
|
+
opentree setup feat/add-dark-mode --check # report what is seeded and what has run
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Both paths write the same marker, so a worktree prepared from the terminal is one
|
|
491
|
+
the chat will not prepare again.
|
|
256
492
|
|
|
257
|
-
|
|
258
|
-
`in_progress` with a quiet status file but a still-active pane stays
|
|
259
|
-
`working…` instead of reading `stalled`, and a `needs_input` with pane output
|
|
260
|
-
*after* the status write (e.g. you approved a permission prompt, which isn't
|
|
261
|
-
a new top-level message) reads `working…` again instead of staying wedged on
|
|
262
|
-
`waiting`.
|
|
493
|
+
#### Approving what it runs
|
|
263
494
|
|
|
264
|
-
|
|
495
|
+
`opentree.toml` is tracked in git, so `setup` and `run` are executable code that
|
|
496
|
+
arrives with a clone, from whoever last had commit rights. opentree asks before
|
|
497
|
+
running them the first time, in the chat, showing exactly what it is about to
|
|
498
|
+
run. The answer is recorded per machine, per repository, and per exact text — an
|
|
499
|
+
edited command is asked about again.
|
|
500
|
+
|
|
501
|
+
From the command line, for CI or to answer ahead of time:
|
|
265
502
|
|
|
266
503
|
```bash
|
|
267
|
-
opentree
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
`message` is for hand-written or custom hooks.
|
|
284
|
-
|
|
285
|
-
**GitHub Copilot** and **Pi** can't be fully automated (Copilot has no
|
|
286
|
-
"waiting for input" event; Pi's notify config holds a single script) — running
|
|
287
|
-
`opentree agents setup gh` / `pi` prints tailored manual instructions.
|
|
288
|
-
|
|
289
|
-
**Manual wiring / other agents.** Any agent can drive the badge by writing the
|
|
290
|
-
status file itself. Point it at `$OPENTREE_STATUS_FILE` (exported by opentree),
|
|
291
|
-
e.g. a Claude Code hook in `~/.claude/settings.json`:
|
|
292
|
-
|
|
293
|
-
```jsonc
|
|
294
|
-
{
|
|
295
|
-
"hooks": {
|
|
296
|
-
"UserPromptSubmit": [{ "hooks": [{ "type": "command",
|
|
297
|
-
"command": "[ -n \"$OPENTREE_STATUS_FILE\" ] && printf '{\"status\":\"in_progress\"}' > \"$OPENTREE_STATUS_FILE\"" }] }],
|
|
298
|
-
"Notification": [{ "hooks": [{ "type": "command",
|
|
299
|
-
"command": "[ -n \"$OPENTREE_STATUS_FILE\" ] && printf '{\"status\":\"needs_input\"}' > \"$OPENTREE_STATUS_FILE\"" }] }]
|
|
300
|
-
}
|
|
301
|
-
}
|
|
504
|
+
opentree trust # approve what opentree.toml now says
|
|
505
|
+
opentree trust show # print those commands, and whether they are approved
|
|
506
|
+
opentree trust revoke # drop this repository's approvals
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Approvals live in `~/.opentree/trust.json`, never in the repository — a
|
|
510
|
+
repository cannot vouch for itself.
|
|
511
|
+
|
|
512
|
+
### Dev Servers
|
|
513
|
+
|
|
514
|
+
Five worktrees of one project all want port 3000. Give opentree the command and
|
|
515
|
+
each gets a port of its own instead:
|
|
516
|
+
|
|
517
|
+
```toml
|
|
518
|
+
[workspace]
|
|
519
|
+
run = "pnpm dev"
|
|
302
520
|
```
|
|
303
521
|
|
|
522
|
+
`opentree prune`, which already reaps workspaces whose worktree was deleted
|
|
523
|
+
outside opentree, also stops server windows with no workspace left behind them.
|
|
524
|
+
|
|
525
|
+
Servers start on demand, never on creation — five worktrees each running
|
|
526
|
+
`next dev` is several gigabytes nobody asked for. Press `w` on a workspace row
|
|
527
|
+
to start or stop one, or open the **Servers** tab (`tab`) for the full list:
|
|
528
|
+
every workspace, what its server is doing, and its address.
|
|
529
|
+
|
|
530
|
+
Each workspace is assigned a port between 20000 and 32000 once, and keeps it —
|
|
531
|
+
so an OAuth redirect URI registered against `localhost:20431` keeps working. The
|
|
532
|
+
port arrives as `PORT`; opentree never rewrites your command, so a stack that
|
|
533
|
+
ignores `PORT` can be told `--port $PORT` in the command itself.
|
|
534
|
+
|
|
535
|
+
The server runs in its own tmux window (`<branch>:run`), so `enter` in the
|
|
536
|
+
Servers tab attaches to it and all of its output is there. Deleting a workspace
|
|
537
|
+
stops its server.
|
|
538
|
+
|
|
539
|
+
#### Names instead of ports, with portless
|
|
540
|
+
|
|
541
|
+
If [portless](https://github.com/vercel-labs/portless) is installed and its
|
|
542
|
+
proxy is running, opentree starts servers behind it and the Servers tab shows
|
|
543
|
+
`https://<branch>.<repo>.localhost` — which reads as "this branch of this
|
|
544
|
+
project" — with the port still listed beside it.
|
|
545
|
+
|
|
546
|
+
The name is passed explicitly rather than left to portless's own inference,
|
|
547
|
+
which reads `package.json` or the git root and so infers the same name for every
|
|
548
|
+
worktree of one repository.
|
|
549
|
+
|
|
550
|
+
opentree never installs or starts portless itself. Getting its proxy running
|
|
551
|
+
means a certificate authority, an `/etc/hosts` entry and a root-owned service,
|
|
552
|
+
and it asks for those with a sudo prompt — which in a detached tmux window
|
|
553
|
+
nobody would see. If portless is installed but its proxy is down, the tab says
|
|
554
|
+
so and serves on ports meanwhile.
|
|
555
|
+
|
|
556
|
+
### Using Different Agents
|
|
557
|
+
|
|
558
|
+
To use one of the others instead of OpenCode:
|
|
559
|
+
|
|
560
|
+
```toml
|
|
561
|
+
[agent]
|
|
562
|
+
command = "claude" # or "copilot", or "gemini"
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
Or press `A` in the dashboard to pick from the agents you have installed — it
|
|
566
|
+
writes the same config, and offers to fetch the ACP adapter if the agent needs
|
|
567
|
+
one. From the CLI:
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
opentree agents list # what's installed, and which is active
|
|
571
|
+
opentree agents use claude # switch this repo (--global for everywhere)
|
|
572
|
+
opentree agents setup claude # fetch its ACP adapter, if it needs one
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
An agent opentree has no ACP spec for is refused up front, when you create a
|
|
576
|
+
workspace, rather than later inside a chat that cannot start.
|
|
577
|
+
|
|
304
578
|
## How It Works
|
|
305
579
|
|
|
306
580
|
1. **Worktrees**: Git worktrees allow multiple checkouts of the same repo in different directories. Each workspace lives in `.opentree/<branch-name>/`.
|
|
@@ -311,6 +585,8 @@ e.g. a Claude Code hook in `~/.claude/settings.json`:
|
|
|
311
585
|
|
|
312
586
|
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.
|
|
313
587
|
|
|
588
|
+
5. **The Chat**: The tmux window runs `opentree chat`, never the agent's own TUI. It holds one JSON-RPC connection to the agent over stdio and renders the conversation, so opentree sees every turn, tool call and permission request as structured data instead of scraped terminal output. The dashboard reaches a running chat over a Unix socket, which is how `m`, `a` and `c` work without attaching. Session IDs are kept in `state.json` so conversations survive closing the window.
|
|
589
|
+
|
|
314
590
|
## Workflow Example
|
|
315
591
|
|
|
316
592
|
```bash
|
|
@@ -362,6 +638,30 @@ opentree sets the agent's environment via `tmux new-window -e`, which needs tmux
|
|
|
362
638
|
|
|
363
639
|
Install OpenCode from [github.com/anomalyco/opencode](https://github.com/anomalyco/opencode), or configure a different agent in `opentree.toml`.
|
|
364
640
|
|
|
641
|
+
### The chat says the agent needs an adapter
|
|
642
|
+
|
|
643
|
+
Claude Code speaks ACP through `claude-agent-acp`. Press `A` in the dashboard,
|
|
644
|
+
select Claude Code, and accept the download — it installs to `~/.opentree/tools`
|
|
645
|
+
and needs `node` on your PATH. If you already have the package installed
|
|
646
|
+
globally, opentree uses that instead of fetching a second copy.
|
|
647
|
+
|
|
648
|
+
### The chat says the agent needs credentials
|
|
649
|
+
|
|
650
|
+
The chat's stopped panel offers `[l]`. What that does depends on how the agent
|
|
651
|
+
logs in, and opentree takes the agent's word for it in this order: a command the
|
|
652
|
+
agent names itself (Copilot sends its own path and `login`), the command opentree
|
|
653
|
+
has recorded for it (`opencode auth login`, `claude auth login`), or the login
|
|
654
|
+
performed over the protocol. Gemini CLI takes the last route and offers four
|
|
655
|
+
ways in — Google account, Gemini API key, Vertex AI, gateway — so `[l]` opens a
|
|
656
|
+
picker. A terminal login hands the window to the agent and restarts it when it
|
|
657
|
+
finishes; a protocol login happens inside the running agent and needs no restart.
|
|
658
|
+
|
|
659
|
+
Credentials also go wrong while an agent is perfectly happy to answer: a token
|
|
660
|
+
expires, a key is revoked, a login lands on the wrong account. `/login` reaches
|
|
661
|
+
the same picker mid-conversation, and the conversation survives it.
|
|
662
|
+
|
|
663
|
+
Anything else that stops an agent offers `[r]` to restart it.
|
|
664
|
+
|
|
365
665
|
### "Error: gh not found"
|
|
366
666
|
|
|
367
667
|
Install GitHub CLI from [cli.github.com](https://cli.github.com/), then authenticate:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axelgar/opentree",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Git worktree manager CLI for orchestrating parallel AI coding sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"bin/opentree"
|
|
16
16
|
],
|
|
17
17
|
"optionalDependencies": {
|
|
18
|
-
"@axelgar/opentree-linux-x64": "0.
|
|
19
|
-
"@axelgar/opentree-linux-arm64": "0.
|
|
20
|
-
"@axelgar/opentree-darwin-x64": "0.
|
|
21
|
-
"@axelgar/opentree-darwin-arm64": "0.
|
|
18
|
+
"@axelgar/opentree-linux-x64": "1.0.1",
|
|
19
|
+
"@axelgar/opentree-linux-arm64": "1.0.1",
|
|
20
|
+
"@axelgar/opentree-darwin-x64": "1.0.1",
|
|
21
|
+
"@axelgar/opentree-darwin-arm64": "1.0.1"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=18"
|