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 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 i inspector 1·2·3 lanes x undo ? help q back
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` opens as a Pi-style outline of the whole
74
- session tree: one content-forward row per message and tool step (`● user:` / `○ assistant:` /
75
- `⚙ [bash $ …]`), branches drawn at their fork points with `│ ╰` connectors and folded to their
76
- `⎇` header until you open them. Here a trunk about caching a `/users` endpoint has a squashed
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
  ![tree from the trunk](docs/screenshots/tree-trunk.png)
81
91
 
82
- The DeepSeek-Harness trajectory is one keystroke away, not gone: `i` opens the inspector and
83
- `1/2/3` bring in the Input/Model/Tools lanes:
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
- ![tree with the trajectory panels on](docs/screenshots/tree-trajectory.png)
95
+ ![tree from a branch](docs/screenshots/tree-from-a-branch.png)
86
96
 
87
- From inside a branch, `← here` marks your current step, that branch is expanded, and the rest of
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
- ![tree from a branch](docs/screenshots/tree-from-a-branch.png)
99
+ ![live search](docs/screenshots/search.png)
91
100
 
92
- `→` expands a branch inline (its turns continue the numbering from the fork point):
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
- ![a branch expanded inline](docs/screenshots/tree-expanded.png)
104
+ ![tree with the trajectory lanes on](docs/screenshots/tree-trajectory.png)
95
105
 
96
- `D` decisions · `u` what's filling the context · `?` help · `m` merge · the sidebar card:
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
- | ![decisions](docs/screenshots/decisions.png) | ![consumers](docs/screenshots/consumers.png) |
111
+ | ![consumers](docs/screenshots/consumers.png) | ![decisions](docs/screenshots/decisions.png) |
101
112
  | ![help](docs/screenshots/help.png) | ![merge picker](docs/screenshots/merge-picker.png) |
102
113
 
103
- ![sidebar card](docs/screenshots/sidebar-card.png)
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
- The footer inside `/tree` carries six keys `⏎` go, `b` branch, `m` merge, `c` crop,
114
- `x` undo, `q` back and `?` opens a help overlay with the rest: crop mode (`space` mark,
115
- double for protected, `a` auto, `t` result⇄turn, `⏎` apply), `i` inspector, `u` consumers,
116
- `D` decisions (`E` export), `L` label, `←→` fold/unfold, `[ ]` hop branches, `f` filter,
117
- `/` search, `g/G`, and how to read the screen.
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": {