Mark completed stages:
[ ]→[x].
The plugin shows the "invisible" parts of the model context:
/context usage— visualization of context window usage: a 14×14 cell map by category (system prompt, tools, user/agent/thinking messages, tool output, compacted data, auto-compact buffer, free space), zoom (z) for large windows, content preview onEnter./context injections— tree of hidden injections: base prompt, tool definitions, skills, context files, injections from other extensions — with token estimates and raw previews.- Key mechanic: capture an Initial context snapshot on the first real turn or via a "silent probe" if a view opens before any turn; filtering of synthetic messages; strict privacy (raw content only via Enter, never logged).
The plugin uses both targets (dual-entrypoint package):
| Concern | OpenCode 2 equivalent |
|---|---|
| Per-dispatch capture | Server plugin: Plugin.define({ id, setup }) from @opencode-ai/plugin, export ./server |
| Context event (final prompt + tools) | ctx.session.hook("context", ...) — gives system, messages, the tools record right before model dispatch |
/context command + fullscreen TUI |
TUI plugin: export ./tui, types from @opencode-ai/plugin/tui; slash command via api.keymap.registerLayer({ commands: [{ slashName: "context", namespace: "palette", run }] }), page via api.route.register([...]) |
| Rendering | JSX through @opentui/solid (host peer-dep), api.ui.Dialog*, api.ui.toast |
| Reported usage | Provider tokens — api.client.session.messages() (usage of the last assistant message); model window — limit.context |
V2 notes:
- TUI plugin config lives in
tui.json(orcli.json→plugins), server plugins inopencode.json(c)→plugins; auto-discovery in.opencode/plugins/. - Server and TUI are separate processes; they need a data channel.
- The plugin API is beta: keep a thin adapter layer, version against an OpenCode release.
- A single module cannot export both
serverandtui.
Primary option — snapshots on disk. On every session.hook("context")
the server plugin writes a snapshot (system prompt, active tools, message
classification) to:
~/.cache/opencode/context-view/<hash(directory)>/<sessionID>.json
atomically (tmp + rename). The TUI plugin reads the current session's file when the view opens.
Fallback (degraded mode): without a snapshot (no turns yet), the TUI side
builds an estimate from api.client.session.messages() plus heuristics for the
prompt/tools, with a [Degraded: …] indicator.
opencode-context-view/
├── package.json # exports: "./server" → src/server/index.ts, "./tui" → src/tui/index.tsx
├── src/
│ ├── shared/ # pure types and logic, host-free
│ │ ├── types.ts # categories, snapshot, usage
│ │ ├── classify.ts # classify messages/system/tools into categories
│ │ ├── estimate.ts # token estimation (~4 chars/token heuristic; optional tokenizer)
│ │ └── privacy.ts # sanitizing, preview rules
│ ├── server/
│ │ └── index.ts # Plugin.define: session.hook("context") → snapshot on disk
│ └── tui/
│ ├── index.tsx # TuiPlugin: /context command, routes
│ ├── usage-view.tsx # map + category legend + block stream
│ ├── injections-view.tsx
│ ├── map.tsx # map geometry, Window/Fit scale
│ └── preview.tsx # fullscreen block view
├── test/ # unit tests for pure modules (classify, estimate, map)
└── doc/
Pure modules (shared/) stay isolated from hosts — easier to test.
Mark completed items in this file.
- Project scaffold (TypeScript; bun + bun test instead of pnpm/vitest)
- Dual exports (
./server,./tui) inpackage.json - Dependencies:
@opencode-ai/plugin@beta(deliberate deviation from the template's^1.18.21: session hooks only exist in the v2 branch API);solid-js/@opentui/*types as peer/dev - Verify server-side loading in a live OpenCode (
opencode2 api get /api/plugin)
- Category types (System Prompt, Tools built-in/MCP/custom, User/Agent/Thinking/ Tool-call Messages, Tool Output, Skills/Instructions, Compacted, Free Space)
- Classifier for the
session.hook("context")payload → categories - Token estimator: character heuristic (~4 chars/token)
- Reconcile totals with provider-reported usage — deferred: show estimates only (
≈) - Unit tests over payload fixtures
-
ctx.session.hook("context")hook - Snapshot persistence (atomic tmp+rename, 0600 permissions, keyed by sessionID)
- Compaction awareness (metadata compact/summary heuristic → "Compacted")
- Automatic pruning of old snapshots (>7 days, hourly check)
- Privacy: raw content only in the local snapshot, never logged
-
/contextslash command (palette, slash arguments[usage|injections]) registered through a keymap layer mounted in theappslot (keymap.layer requires the Keymap.Provider — it only works inside a component, same as internal TUI plugins) - Usage view:
used/window (%)header, composition bar and per-category entry list - Navigation ↑↓, Enter preview, Esc via keymap layer (jk/PgUp/PgDn — under M4)
- Degraded mode without a snapshot ("No captured dispatch yet…")
- 14×14 map: Window/Fit scale via
z, auto-compaction buffer⛝ - Block stream with
┃gutter, block caps,Enter - View Content - Injections view: prompt/tool tree with raw previews
- Responsive rules (3 width tiers; narrow fallback below 52 columns)
- Mouse/scroll support, post-preview position restore
- Override-only config: category colors as OpenCode theme keys (no hex/ANSI)
- README (install, commands, privacy; no demo GIF yet)
- Release process (changesets), npm publishing
- Install:
opencode2 plugin add …
- Normal turns are unaffected while inspection is not invoked; no provider requests.
- Raw content only after explicit Enter; never logged and never sent to the model.
- Parent/child contributions are never double-counted; every rendered line fits the width; views reflow.
- Thinking-signature bytes are never rendered or persisted — size only.
- Beta API —
session.hook("context")and the TUI keymap/route APIs may change; a thin adapter layer is mandatory. - No public API for the system prompt/tools outside a dispatch — hence
server-side capture; if the hook proves insufficient, inspect the
http.requesthook as the source of truth. - Compaction — V2 summary semantics differ from other agents; verify the actual post-compaction message shape on a live session.
- Tokenization — the heuristic diverges from providers; solved by an
optional tokenizer dependency and rendering values as estimates (
≈).