A TUI plugin for the OpenCode 2.0 CLI that shows live tokens-per-second in the footer line below the input, right beside the context indicator.
While the model streams, the footer shows a live estimate; when the step finishes, it shows the exact average from the provider's token accounting.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ... 30.5K (3%) β
β 33.3 tok/s Β· total 15k tok ctrl+p ... β β streaming
β
β ... 30.5K (3%) β
β β 200 tok/s Β· 15k tok Β· total 15k tok ctrl+p ... β β last step
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- OpenCode 2.0 beta (
opencode2) β the TUI plugin API is beta and pinned to@opencode-ai/plugin@0.0.0-next-17055. - Bun β₯ 1.0 (only needed to build from source, not to use the plugin).
OpenCode 2.0 automatically installs npm plugin packages into an isolated
cache. Add the package to your config β the opencode2 TUI reads
~/.config/opencode/cli.json (the legacy tui.json is migrated into it once,
on first start):
Or as a bare specifier, which is also what a string entry means in
opencode.json(c):
{
"plugins": ["live-token-meter"],
}You can pin a version:
{
"plugins": ["live-token-meter@0.1.0"],
}Restart opencode2 and start a prompt β the footer below the input shows the
live readout next to the context indicator.
Point package at the entry file β the TUI host resolves file plugins as
plain paths (no exports map), so give it a file, not a directory:
{
"plugins": [
{
"package": "/absolute/path/to/live-token-meter/src/index.tsx",
"options": { "charsPerToken": 4 },
},
],
}A directory spec is also supported if the repo provides a tui/ entry point
(one is shipped at tui/index.ts).
| Option | Default | Description |
|---|---|---|
enabled |
true |
Set to false to disable the plugin. |
charsPerToken |
4 |
Chars per token for the live estimate. Tune per provider (Anthropic β 4, OpenAI β 4.2). |
showTotals |
true |
Show the session-wide cumulative token count. |
Built against the OpenCode 2.0 TUI plugin API (@opencode-ai/plugin/tui,
define({ id, setup })):
- Registers the
prompt.footer.endslot (the footer line below the input, next to the context indicator) viacontext.ui.slot. - Subscribes to the server event stream with
context.data.on:session.step.startedβ start the timing windowsession.text.delta/session.reasoning.deltaβ live estimate from character deltas (chars / charsPerToken, default 4)session.step.endedβ exacttokens.output + tokens.reasoningdivided by the step durationsession.step.failedβ error state
- Per-session reactive state lives in a solid-js store via
context.storage.memory, so the slot re-renders on every delta. - The session-wide token total is summed from
context.data.session.message.list.
bun install
bun run typecheck # tsc --noEmit
bun run test # bun:test unit + wiring + loader tests
bun run build # bundle the TUI plugin into dist/ (what gets published)To test against a live TUI: add the plugin to cli.json (see Install), open a
session, and start a prompt β the footer below the input shows the live
readout next to the context indicator.
bun run prepublishOnly # typecheck + build + test
npm publishThe package manifest follows the plugin packaging contract:
exports["."]andexports["./tui"]both resolve to the bundleddist/tui.jsβ the host loads the TUI entry directly, no TSX at runtime.oc-plugin: ["tui"]marks the package as a TUI plugin.@opencode-ai/pluginand@opentui/solidare runtime dependencies so the isolated plugin cache can resolve the imports the bundle keeps external.
- The live value is an estimate (chars /
charsPerToken); the settled value after each step is exact (provider usage accounting). - Each open session is tracked independently via its
sessionID. - Pinned to the
@opencode-ai/plugin@0.0.0-next-17055API (matches theopencode2nextbinary). The TUI plugin API is beta and may change β bump the pinned versions when you upgrade opencode2.
MIT