opencode-context-tree 0.1.1 → 0.2.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/CHANGELOG.md +38 -0
- package/README.md +35 -25
- package/dist/server.js +6 -2
- package/dist/tui.js +1310 -490
- package/docs/USAGE.md +52 -21
- package/package.json +1 -1
- package/src/core/actions.ts +1 -0
- package/src/core/consumers.ts +55 -13
- package/src/core/decision.ts +62 -0
- package/src/core/lanes.ts +166 -5
- package/src/core/navigation.ts +39 -10
- package/src/core/tokens.ts +22 -4
- package/src/core/tree.ts +98 -5
- package/src/server/index.ts +3 -2
- package/src/shared/version.ts +3 -0
- package/src/tui/actions.ts +131 -11
- package/src/tui/index.tsx +14 -6
- package/src/tui/route.tsx +556 -181
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.1 — 2026-09-03
|
|
4
|
+
|
|
5
|
+
- Lanes: the cursor now highlights the thinking pills folded into the selected row (they never
|
|
6
|
+
lit up before); the `?` legend explains the lane colours (Input green you / grey context ·
|
|
7
|
+
Model purple answer / grey thinking · Tools orange call / red failed).
|
|
8
|
+
- `?` help and `/ctree status` print the running plugin version.
|
|
9
|
+
- Docs: how to upgrade (OpenCode pins the version it installed and does not re-check `@latest`;
|
|
10
|
+
pin a version with `opencode plugin opencode-context-tree@<version> -g --force`), and a note
|
|
11
|
+
that the trajectory panels are off by default (`1/2/3` show them, `i` the inspector).
|
|
12
|
+
|
|
13
|
+
## 0.2.0 — 2026-09-03 — UI/UX pass from a four-lens review
|
|
14
|
+
|
|
15
|
+
Findings from reviewers with lazygit/tig, k9s/btop, Helix/fzf and agent-tooling PM backgrounds,
|
|
16
|
+
working from real screenshots.
|
|
17
|
+
|
|
18
|
+
- Context bands are relative to the model window (<25 / 60 / 85 %), with a `▓▓░░░` bar; the
|
|
19
|
+
gauge, tree header and sidebar card show one identical string.
|
|
20
|
+
- Thinking steps fold into their assistant row (`· 9.8s thought`) instead of being half the outline.
|
|
21
|
+
- From inside a branch, trunk rows past the fork point are dimmed under a
|
|
22
|
+
`── not in this branch's context ──` separator: they are not sent to the model.
|
|
23
|
+
- Decision records render as wrapped text (no raw `**`, no mid-word clipping).
|
|
24
|
+
- Per-panel footer and scoped keys: `b`/`m`/`u` no longer fire from the help, decisions or
|
|
25
|
+
consumers panels.
|
|
26
|
+
- `/` filters as you type with match highlighting, an honest zero-result state and a `4/17`
|
|
27
|
+
position; `n`/`N` step through matches.
|
|
28
|
+
- Confirmations: Discard is gated and Esc cancels it; `⏎` names what it will do for the selected
|
|
29
|
+
row and confirms session switches; the merge picker names the trunk with turn and token counts.
|
|
30
|
+
- Consumers show percent of the window, expand into their entries, and crop a tool result in
|
|
31
|
+
place; provider reasoning is labelled as not croppable.
|
|
32
|
+
- Header dedupes the branch title and truncates to fit; crop marks survive mode changes and crop
|
|
33
|
+
mode is exclusive.
|
|
34
|
+
- Keys aligned with vim: `u` undo (`x` kept), `s` consumers, `gg`/`G`, `ctrl+d`/`ctrl+u`,
|
|
35
|
+
`Tab` toggles a fold, `f` opens a filter picker (`F` reverses).
|
|
36
|
+
- Lanes are an event strip like DeepSeek Harness's, not a bar chart: one pill per event on a
|
|
37
|
+
shared axis (Input / Model / Tools), a gap between neighbours, width by duration in Duration
|
|
38
|
+
mode, categorical colours, red for a failed tool call, the selected step inverted, an empty
|
|
39
|
+
lane says so, and nothing is scaled by tokens (the row column already has them).
|
|
40
|
+
|
|
3
41
|
## 0.1.1 — 2026-09-03
|
|
4
42
|
|
|
5
43
|
- Install with OpenCode's own `opencode plugin opencode-context-tree [-g]`, which registers
|
package/README.md
CHANGED
|
@@ -26,7 +26,9 @@ opencode plugin opencode-context-tree # for the current project only (.op
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Restart OpenCode. `/tree` (or `ctrl+q`) opens the tree; `/branch`, `/merge`, `/decisions` and the
|
|
29
|
-
headless `/ctree` commands are available too.
|
|
29
|
+
headless `/ctree` commands are available too. The trajectory panels are off by default so the
|
|
30
|
+
first screen is the plain tree: inside `/tree` press `1`, `2` or `3` for the Input/Model/Tools
|
|
31
|
+
timeline (`0` hides it) and `i` for the inspector.
|
|
30
32
|
|
|
31
33
|
If you only see `/ctree` and no `/tree`, the TUI half is not registered: run the command above.
|
|
32
34
|
To register by hand instead, the package name must be listed in **both** files:
|
|
@@ -38,6 +40,16 @@ To register by hand instead, the package name must be listed in **both** files:
|
|
|
38
40
|
{ "plugin": ["opencode-context-tree"] }
|
|
39
41
|
```
|
|
40
42
|
|
|
43
|
+
**Upgrading.** OpenCode keeps the version it installed and does not re-check `@latest` on restart.
|
|
44
|
+
To move to a new release, pin it (this also rewrites both config entries):
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
opencode plugin opencode-context-tree@0.2.0 -g --force # or without -g for the project
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
or delete `~/.cache/opencode/packages/opencode-context-tree@latest` and restart. `?` in `/tree`
|
|
51
|
+
and `/ctree status` show the version you are running.
|
|
52
|
+
|
|
41
53
|
Options go in either file: `[["opencode-context-tree", { "storage": "global", "jumpSummary": "never" }]]`
|
|
42
54
|
(see [docs/USAGE.md](docs/USAGE.md)). To hack on it from a checkout, see "Try it from source" below.
|
|
43
55
|
|
|
@@ -58,7 +70,7 @@ is one content-forward row and branches are drawn at their fork points:
|
|
|
58
70
|
│ │ ● user: the bun test is flaky, find the race ~0.4k
|
|
59
71
|
│ │ ⚙ [bash $ bun test src/foo.test.ts] ⚠ ~4.7k
|
|
60
72
|
│ ◆ Decision: try-redis · Outcome: switched to a write-through cache… ~0.9k
|
|
61
|
-
└ ⏎ go b branch m merge c crop
|
|
73
|
+
└ ⏎ go b branch m merge c crop u undo s consumers ? help q back
|
|
62
74
|
```
|
|
63
75
|
|
|
64
76
|
Every message and tool call is a row; the gutter draws each branch at the message it was forked
|
|
@@ -70,38 +82,35 @@ OpenCode's own `/fork` are adopted into the tree automatically.
|
|
|
70
82
|
|
|
71
83
|
## Screenshots
|
|
72
84
|
|
|
73
|
-
Real OpenCode 1.18 TUI, gemma4 via ollama. `/tree`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`try-redis` branch (its ◆ decision record is the leaf), a rejected `try-lru`, and two native
|
|
78
|
-
`/fork` sessions:
|
|
85
|
+
Real OpenCode 1.18 TUI, gemma4 via ollama. `/tree` is a Pi-style outline of the whole session
|
|
86
|
+
tree: one row per message or tool step, thinking folded into its assistant row, branches drawn at
|
|
87
|
+
their fork points and folded to their `⎇` header until opened, the context bar and band in the
|
|
88
|
+
header, and a footer that says what `⏎` will do for the selected row:
|
|
79
89
|
|
|
80
90
|

|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
From inside a branch, `← here` marks your step and the trunk rows past the fork point are dimmed
|
|
93
|
+
under `── not in this branch's context ──`: they are not sent to the model.
|
|
84
94
|
|
|
85
|
-

|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
the tree stays visible and folded — you always see the whole tree:
|
|
97
|
+
`/` filters as you type, highlights matches and counts rows; `n`/`N` step through them:
|
|
89
98
|
|
|
90
|
-

|
|
91
100
|
|
|
92
|
-
|
|
101
|
+
The DeepSeek-Harness trajectory is one keystroke away: `1/2/3` bring in the event-strip lanes
|
|
102
|
+
(one pill per event, width by duration, red for a failed tool call) and `i` the inspector:
|
|
93
103
|
|
|
94
|
-

|
|
95
105
|
|
|
96
|
-
`
|
|
106
|
+
`s` shows what is filling the context, by share of the tree and of the model window, expandable
|
|
107
|
+
into entries you can crop in place; `D` renders the decision records; `?` opens the help pane:
|
|
97
108
|
|
|
98
109
|
| | |
|
|
99
110
|
|---|---|
|
|
100
|
-
|  |  |
|
|
101
112
|
|  |  |
|
|
102
113
|
|
|
103
|
-

|
|
104
|
-
|
|
105
114
|
## Try it from source
|
|
106
115
|
|
|
107
116
|
```sh
|
|
@@ -110,11 +119,12 @@ bun install && bun run build
|
|
|
110
119
|
# tui.json → "plugin": ["/abs/path/opencode-tree/dist/tui.js"]
|
|
111
120
|
```
|
|
112
121
|
|
|
113
|
-
|
|
114
|
-
`
|
|
115
|
-
|
|
116
|
-
`
|
|
117
|
-
|
|
122
|
+
Keys inside `/tree` (vim-aligned): `j k` `ctrl+d ctrl+u` `gg G` move · `[ ]` hop between
|
|
123
|
+
branches · `h l` `Tab` fold/unfold · `⏎` go (the footer says what it will do for the selected row)
|
|
124
|
+
· `b` branch · `m` merge · `c` crop mode (`space` mark, `a` auto, `t` result⇄turn, `⏎` apply) ·
|
|
125
|
+
`u` undo (`x` too) · `/` live search, `n N` next/prev · `f` filter picker · `i` inspector ·
|
|
126
|
+
`1 2 3` lanes, `0` off · `s` what's filling the context · `D` decisions · `L` label · `y` copy ·
|
|
127
|
+
`?` help · `q`.
|
|
118
128
|
|
|
119
129
|
## Commands
|
|
120
130
|
|
package/dist/server.js
CHANGED
|
@@ -19399,6 +19399,9 @@ ${records.map(one).join(`
|
|
|
19399
19399
|
`;
|
|
19400
19400
|
}
|
|
19401
19401
|
|
|
19402
|
+
// src/shared/version.ts
|
|
19403
|
+
var PLUGIN_VERSION = "0.2.1";
|
|
19404
|
+
|
|
19402
19405
|
// src/core/adopt.ts
|
|
19403
19406
|
var FORK_TITLE = /^(.+) \(fork #(\d+)\)$/;
|
|
19404
19407
|
function parseForkTitle(title) {
|
|
@@ -19590,7 +19593,8 @@ ${text}
|
|
|
19590
19593
|
case "help":
|
|
19591
19594
|
return say(output2, `${cmd.error ? `error: ${cmd.error}
|
|
19592
19595
|
|
|
19593
|
-
` : ""}${CTREE_HELP}
|
|
19596
|
+
` : ""}${CTREE_HELP}
|
|
19597
|
+
(opencode-context-tree ${PLUGIN_VERSION})`);
|
|
19594
19598
|
case "status": {
|
|
19595
19599
|
await adopt();
|
|
19596
19600
|
const state = store.stateForSession(sessionID);
|
|
@@ -19603,7 +19607,7 @@ ${text}
|
|
|
19603
19607
|
const title = me && !me.name ? (await client.session.get({ path: { id: sessionID }, query: { directory } }).catch(() => {
|
|
19604
19608
|
return;
|
|
19605
19609
|
}))?.data?.title : undefined;
|
|
19606
|
-
return say(output2, [`tree ${state.treeId}`, me ? `this session is \u2387 ${me.name ?? title ?? "branch"} (${me.status}) of ${me.parentSessionID}${me.note ? ` \u2014 ${me.note}` : ""}` : "this session is the trunk", `${branches.length} branch(es) from here: ${branches.map((b) => `${b.name ?? b.sessionID} [${b.status}]`).join(", ") || "none"}`, `${crops.length} active crop(s), ~${hidden} tokens hidden`, `${Object.values(state.decisions).filter((d) => d.sessionID === sessionID && !d.hidden).length} decision record(s) here`].join(`
|
|
19610
|
+
return say(output2, [`opencode-context-tree ${PLUGIN_VERSION} \xB7 tree ${state.treeId}`, me ? `this session is \u2387 ${me.name ?? title ?? "branch"} (${me.status}) of ${me.parentSessionID}${me.note ? ` \u2014 ${me.note}` : ""}` : "this session is the trunk", `${branches.length} branch(es) from here: ${branches.map((b) => `${b.name ?? b.sessionID} [${b.status}]`).join(", ") || "none"}`, `${crops.length} active crop(s), ~${hidden} tokens hidden`, `${Object.values(state.decisions).filter((d) => d.sessionID === sessionID && !d.hidden).length} decision record(s) here`].join(`
|
|
19607
19611
|
`));
|
|
19608
19612
|
}
|
|
19609
19613
|
case "branch": {
|