opencode-context-indicator 1.0.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 (6) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/index.js +2367 -0
  4. package/lib/dedup.js +120 -0
  5. package/package.json +57 -0
  6. package/tui.tsx +180 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 opencode-context-indicator contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # opencode-context-indicator
2
+
3
+ Real-time **context-window usage** indicator for [OpenCode](https://opencode.ai),
4
+ with a **per-category token breakdown** and an optional **terminal-UI sidebar**.
5
+
6
+ It tells you how full the current model's context window is, and *what* is
7
+ filling it: system prompt, tool schemas, user/assistant text, reasoning, tool
8
+ arguments and the residual "other" bucket.
9
+
10
+ The package ships a **dual V1 + V2 plugin** (one entry works in both OpenCode
11
+ generations) plus an optional TUI sidebar module exposed as the `./tui` entry.
12
+
13
+ ---
14
+
15
+ ## What it shows
16
+
17
+ ### Per-category breakdown
18
+
19
+ For every served request the plugin estimates how the context window is split:
20
+
21
+ ```
22
+ [2026-…] session=ses_abc model=… ctx=45.2k/120k (38%) … input=44.1k
23
+ user 4.2k (est tokens)
24
+ assistant 3.1k (est tokens)
25
+ reasoning 0.8k (est tokens) (exact 1.2k)
26
+ tool args 0.3k (est tokens, input only)
27
+ system 2.1k (est tokens)
28
+ tool schemas 1.4k (est tokens)
29
+ other 5.6k (= input - sum of estimates)
30
+ source context-hook
31
+ subagents 2 session(s): in=8.1k out=2.3k r=1.0k worst=12%
32
+ ```
33
+
34
+ * Token counts are **estimates** from a unicode-aware heuristic
35
+ (Cyrillic ≈ 2.5, CJK ≈ 1.5, latin/ASCII ≈ 4 chars/token) — not a tokenizer.
36
+ `reasoning` also carries the exact value reported by the model when present.
37
+ * `other` is the residual (`input − sum of estimates`).
38
+ * Tool **results** are deliberately not counted; only tool **call arguments**
39
+ (results are not part of the sent prompt).
40
+ * `ctx` is OpenCode's native overflow count: `tokens.total` when present, else
41
+ `input + output + cache.read + cache.write`. Percentages are **not clamped**
42
+ at 100% — exceeding the window stays visible.
43
+
44
+ ### Toast (V1 only)
45
+
46
+ On OpenCode 1.x, after each assistant message a throttled toast appears:
47
+
48
+ ```
49
+ ctx 45.2k / 120k (38%) · r 1.2k · c 3.4k
50
+ ```
51
+
52
+ `r` = reasoning tokens, `c` = cache-read tokens (both only when nonzero); cost
53
+ is appended only when the model config carries explicit pricing.
54
+
55
+ ### TUI sidebar (V2, terminal only)
56
+
57
+ On the OpenCode 2.x terminal TUI the `./tui` entry renders a compact sidebar
58
+ panel with the same breakdown:
59
+
60
+ ```
61
+ Context
62
+ gpt-4o · 45.2k / 120k (38%)
63
+ user 4.2k 9%
64
+ assistant 3.1k 7%
65
+ reasoning 0.8k 2%
66
+ tool args 0.3k 1%
67
+ system 2.1k 5%
68
+ tool schemas 1.4k 3%
69
+ other 5.6k 12%
70
+ updated 12:30:01
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Surface matrix
76
+
77
+ | Surface | Toast | TUI sidebar | Breakdown log file |
78
+ | --- | :---: | :---: | :---: |
79
+ | OpenCode **1.x** — Desktop | ✅ | — | ✅ |
80
+ | OpenCode **1.x** — TUI | ✅ | — | ✅ |
81
+ | OpenCode **2.x** — TUI | — | ✅ | ✅ |
82
+ | OpenCode **2.x** — Desktop | — | — | ✅ |
83
+
84
+ Notes — these reflect what the plugin API actually exposes today:
85
+
86
+ * **V2 has no toast channel on the server side.** The V2 plugin API
87
+ (`ctx.*`) exposes no TUI/toast method (opencode issue `#49380`); the V2 path
88
+ therefore delivers everything through the log file and the TUI sidebar. The
89
+ sidebar is a TUI-process slot and does not exist in the Desktop app.
90
+ * **V1 has no sidebar.** The CLI-plugin / slot API (and the `./tui` entry) is a
91
+ V2 feature; OpenCode 1.x has no equivalent, so V1 is toast + log only.
92
+ * The Desktop app is TUI-less; its V2 surface is the log file only.
93
+
94
+ ---
95
+
96
+ ## Log / state file locations
97
+
98
+ All paths use the OS temp directory (`os.tmpdir()`), i.e. `%TEMP%` on Windows
99
+ and `$TMPDIR` (usually `/tmp`) elsewhere:
100
+
101
+ | File | Purpose |
102
+ | --- | --- |
103
+ | `context-breakdown.log` | Human-readable live snapshot + bounded final summaries |
104
+ | `opencode-context-indicator-state.json` | Machine-readable snapshot consumed by the TUI sidebar |
105
+ | `context-events.log` | Raw event tap — **only** when `DEBUG_EVENTS` is flipped to `true` in the source (off by default) |
106
+
107
+ The live `context-breakdown.log` snapshot is **rewritten** (never grows); final
108
+ summaries (on `session.idle`) and compaction notes are **appended** — log writes
109
+ use plain `appendFileSync`, not atomic replacement. Only the machine-readable
110
+ `opencode-context-indicator-state.json` is written **atomically** (temp file +
111
+ rename), so concurrent plugin instances cannot corrupt it. Cross-instance
112
+ duplicate final summaries are prevented separately by an atomic claim marker
113
+ (see `lib/dedup.js`).
114
+
115
+ ---
116
+
117
+ ## Installation
118
+
119
+ ### OpenCode 2.x (V2)
120
+
121
+ Per the [OpenCode v2 plugin docs](https://opencode.ai/v2/docs/plugins), npm
122
+ plugins are listed under the `plugins` (plural) config key. Use the CLI:
123
+
124
+ ```sh
125
+ opencode plugin add opencode-context-indicator
126
+ ```
127
+
128
+ or add it to `opencode.jsonc`:
129
+
130
+ ```jsonc
131
+ {
132
+ "$schema": "https://opencode.ai/config.json",
133
+ "plugins": ["opencode-context-indicator"]
134
+ }
135
+ ```
136
+
137
+ The `./tui` sidebar entry is loaded automatically alongside the main plugin in
138
+ the terminal TUI. For a CLI-only setup against remote servers, the package can
139
+ also be listed in [`cli.json`](https://opencode.ai/v2/docs/cli/plugins):
140
+
141
+ ```json
142
+ { "plugins": ["opencode-context-indicator"] }
143
+ ```
144
+
145
+ ### OpenCode 1.x (V1)
146
+
147
+ Add the package name to the `plugin` array in your `opencode.json`:
148
+
149
+ ```json
150
+ {
151
+ "plugin": ["opencode-context-indicator"]
152
+ }
153
+ ```
154
+
155
+ ### Local file (no npm)
156
+
157
+ Copy `index.js` and `lib/dedup.js` into `~/.config/opencode/plugins/`, keeping
158
+ the `lib/` subdirectory next to the plugin file so the plugin's `import` of the
159
+ helper resolves. The `lib/` folder is support code, not a separate plugin entry:
160
+
161
+ ```
162
+ ~/.config/opencode/plugins/context-indicator.js
163
+ ~/.config/opencode/plugins/lib/dedup.js
164
+ ```
165
+
166
+ To use the sidebar locally as well, place `tui.tsx` where your OpenCode
167
+ installation resolves the package's `./tui` entry (npm install is the
168
+ recommended path for the sidebar).
169
+
170
+ ---
171
+
172
+ ## Requirements
173
+
174
+ * **Node.js ≥ 18** (the main plugin uses only Node built-ins). `@opencode/plugin`
175
+ is an **optional** peer dependency: OpenCode resolves it at runtime, and
176
+ `index.js` itself does not import it (the V2 `define` helper is inlined).
177
+ * **OpenCode ≥ 1.18.29** for the V1 path. ⚠️ The V1 path relies on
178
+ `experimental.chat.*` hooks, which are **experimental** and may change or stop
179
+ firing in future OpenCode 1.x releases; if they do, the indicator degrades
180
+ gracefully (the breakdown falls back to throttled `session.messages` fetches
181
+ and toasts keep working).
182
+ * **OpenCode ≥ 2.0.16** for the V2 path (built and verified against
183
+ **2.0.18**).
184
+ * **TUI sidebar**: terminal TUI only, requires the OpenTUI rendering stack that
185
+ ships with OpenCode (`@opentui/core`, `@opentui/solid`, `solid-js`). These are
186
+ declared as **optional** peer dependencies, so the main plugin installs and
187
+ runs fine without them (the sidebar simply is not available).
188
+
189
+ ---
190
+
191
+ ## Configuration
192
+
193
+ There are **no user-facing plugin options**. The plugin reads the merged
194
+ OpenCode configuration only to *harvest* each model's context limits and
195
+ explicit pricing (the `config` hook on the V1 path; the model registry on the
196
+ V2 path). It does not register configurable keys of its own.
197
+
198
+ ---
199
+
200
+ ## How it works
201
+
202
+ The package default export is a dual plugin:
203
+
204
+ * `setup(ctx)` — V2 entry (OpenCode ≥ 2.0.16). Registers a `session.hook("context", …)`
205
+ to capture the final messages / system prompt / tool schemas right before each
206
+ model request, subscribes to the event stream (`session.created`,
207
+ `session.step.ended`, `session.idle`, `session.compacted`, failure events) and
208
+ records limits from the model registry. Read-only: the context is never
209
+ mutated.
210
+ * `server({ client })` — V1 entry (OpenCode ≥ 1.18.29). Classic event handler +
211
+ experimental transform hooks + toasts.
212
+
213
+ Both paths write the same breakdown log; the V2 path additionally feeds the
214
+ sidebar through `opencode-context-indicator-state.json`. Cross-instance
215
+ duplicate final summaries are suppressed with an atomic claim marker
216
+ (`lib/dedup.js`). Every file / estimate / subagent path is fault-tolerant: a
217
+ failure is logged and never breaks the main event or toast path.
218
+
219
+ This plugin does not touch `opencode-token-monitor`
220
+ (`token_stats` / `token_history` / `token_export` keep working unchanged).
221
+
222
+ ---
223
+
224
+ ## Screenshots
225
+
226
+ <!-- TODO: add screenshots -->
227
+
228
+ _(to be added)_
229
+
230
+ ---
231
+
232
+ ## Contributing
233
+
234
+ Issues and pull requests are welcome. Please keep changes minimal and
235
+ fault-tolerant: any code on the event / hook path must never throw into the
236
+ caller.
237
+
238
+ ---
239
+
240
+ ## License
241
+
242
+ [MIT](./LICENSE)