Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Obvious Grid

Obvious: Unskippable, inevitable, bold, visible dashboard even when you are far, very far from the screen. Good when you are vibing while agents are coding.

Grid: monitor all active sessions on the same screen.

Open the page in any browser and get a full-screen, color-coded status of your agents plus per-session live metrics: tokens, cost, cache rates, request-speed graphs, git branch, and one-click deep links into web sessions. Notifications (ntfy push + alarm sound) are built in.

Zero infrastructure. No separate server, no database, no WebSocket backend — the plugin itself serves the page on http://localhost:8765 (bound to 0.0.0.0, so it's reachable from any device on the network). Install, restart opencode, open the URL.

Runs where opencode runs: WSL, native Linux, macOS, and native Windows. Originally built for opencode inside WSL; since v2.2.0 state lives in the platform temp dir and v2.3.0 completed the cross-platform seams (web-port discovery fallback, per-platform alarm sound, WSL-guarded path translation).

Install

Install from npm (global config) with opencode's own command:

opencode plugin opencode-obvious-grid -g

(-g installs into your global config ~/.config/opencode/opencode.json; omit it for a project-level install, -f replaces an existing version.)

Alternatively, add it to opencode.json (global or project-level) manually:

{
  "plugin": ["opencode-obvious-grid"]
}

opencode installs npm plugins automatically with Bun at startup (cached in ~/.cache/opencode/node_modules/). Restart opencode, and the status page is live at http://localhost:8765 (bound to 0.0.0.0, so it's reachable from any device on your LAN). To use a different port, set OBVIOUS_GRID_PORT (e.g. OBVIOUS_GRID_PORT=9000 opencode) — every instance should use the same port so they share one page.

Requirements: opencode ≥ the June 2026 plugin refactor (V2 plugin format) — older versions fail to load the plugin.

One install covers every instance: the page aggregates all opencode instances on the machine (CLI and opencode web), so a single install and one open URL is all it takes.

Features at a Glance

  • Aggregate status page — one glance tells you if any agent is working (orange), waiting for your answer (blue), errored (red), or everything is idle (green).
  • Per-session cards — state, model, per-request and total tokens, context %, cache hit rates, cost, finish reasons, token & speed graphs, session title, working directory + git branch, run/wait/idle time breakdown.
  • Sub-agent visibility — sub-agent sessions (spawned via the task tool) get their own ↳-marked cards, and their work is rolled into the parent session's card (totals + a ▸ per-agent breakdown chip).
  • All instances, all modes — CLI and opencode web sessions on the same machine, web sessions deep-linkable into the exact opencode web UI session.
  • Browser-based alerts — Web Audio beeps when a run finishes or blocks on you (per-card toggle), plus ntfy.sh push + alarm sound (global toggle).
  • Self-contained page — a single HTML file, no external assets, no backend beyond the plugin itself.

Screenshot

Obvious Status page view

Grid
Four opencode instances on one page — the grid scales from a single full-page card up to N×N, keeping every agent's status readable at a glance even from across the room:

Grid of four opencode instances

Parallel sub-agents
Many cards at once when several sub-agents run in parallel — each sub-agent gets its own ↳-marked card while the parent card rolls their work up into its totals:

Sub-agents grid view

Status Colors

Color Meaning
Orange At least one opencode instance is running (agent working / streaming) — card shows a scrolling "RUNNING" marquee
Blue No instance running, but at least one is waiting for user input (a permission request or a question tool prompt) — card pulses/blinks
Red No instance running/waiting, but at least one hit a session.error
Green All instances idle (default / waiting for input)

Aggregation rule: error > waiting > running > idle. If any card is waiting for your approval, the page is blue regardless of others. Each session is shown as a card with its live metrics (see below). Cards are per-session: CLI mode has one session per opencode process, web mode can have several.

Card layout: 1 card = full page; 2 = side by side. For more, both the cards per line and the number of lines are ceil(sqrt(x)) (flex-wrap): 3 → 2×2, 4 → 2×2, 5 → 3×2, 8 → 3×3, 9 → 3×3, 16 → 4×4. The last line is shared evenly among its cards, so the last line's cards are wider than the others (e.g. 8 cards → rows of 3, 3, 2 with the last two at 1.5× width; 5 → 3 + 2 at 1.5× width; 7 → 3 + 3 + 1 full-width). All lines get equal height.

Card Contents

Each card shows, from top to bottom:

  • Watermark / marquee: when running, a giant low-opacity RUNNING marquee scrolls across the card background (rows alternate left/right, speed adapts to token rate: 90s per half-cycle at <100 t/s, 60s at 100–200, 30s at >200). When idle/waiting, a dim watermark shows the state word (IDLE, WAITING, ERROR).
  • Session title (sess), falling back to session <first-8-of-session-id>. Sub-agent (child) cards are prefixed with ↳ and show a parent: <parent session title> line underneath (hover for details). For sessions from the web UI it's a link (dotted underline) that opens that exact session in the web UI (http://localhost:<web-port>/<base64url(cwd)>/session/<id>), in a new tab.
  • Line 1 chips:
    • Calls: <userCalls>u | <apiCalls>a (assistant messages this session; backfilled from message history at startup).
    • Tokens (per request): <input> in (+ (<contextPct>%) when the context limit is known, color-coded red >50%, orange >20%) | <output> out | <reasoning> reasoning. out/reasoning count only the current request — they reset when a new user message starts (the request boundary is tracked by message id, so re-delivered events can't corrupt it). The in figure is the latest context size, not a sum.
    • Tokens (total): total <in>|<out>|<reasoning> — session-wide sums of input/output/reasoning tokens, abbreviated with integer rounding (k, M, G).
    • Cache: cache hit <cacheHit>% | avg <cacheAvg>%, color-coded by rate (green >90, blue >80, white >70, orange >60, red below).
    • Cost: $<total> | $<lastCost> (last message's cost to 4 decimals).
    • Finishes: N too long | N error | N aborted in orange — only when non-zero.
  • Line 2 chips: WEB/CLI (where the session was opened from: opencode web UI vs terminal; purple/gray, from process.argv at plugin load) · mode · agent (hidden when agent === mode, e.g. "build") · SUB (amber, only on sub-agent cards) · provider/model | variant · <last> tokens/s | avg <avg>/s (rates count output + reasoning tokens — reasoning is also generated).
  • Sub-agent breakdown chip (parent cards, after line 2): ▸ explore 3a $0.03 · +2 more — the direct sub-agents' calls and cost, active (running/waiting) ones first, up to 3 shown. Finished sub-agents are dimmed at 50% opacity; hovering shows the full per-agent breakdown (calls · cost · tokens, with a done/running marker per agent). Entries live as long as the child's card exists (60-min staleness window, session delete, or process exit).
  • Graphs (between line 2 and the toggles, only when the card has ≥80px of free space, i.e. few cards; cards ≥900px wide show them side by side, narrower cards stack them).
    • Token graph (left): one stacked bar per request, split at the vertical midline — upward: input cache-miss (white) + output (orange) + reasoning (purple), downward: prompt-cache hits (blue). Both halves share the same scale (max of up/down totals). When the graph is wide enough (≥420px), four thin cumulative lines are overlaid — one per token type in the same colors as the bars — scaled to a second Y axis on the right (labels: max / half / 0). Hovering shows req N · time · in · out · reas · cache plus the cumulative totals so far for that request.
    • Speed graph (right): a two-line chart over request time. The dim line is each request's speed (output+reasoning tokens ÷ duration), the bright line is the running average (cumulative tokens ÷ cumulative time — same definition as the avg rate chip, so it converges to it). Y scale auto-fits to the data (95th percentile ×1.2, spikes clipped at the top edge), X is categorical — each request gets an equal-width slot in chronological order. Hovering shows the exact values of the nearest request (number, time, speed, running average).
    • Both are drawn from per-request series the plugin persists (tokSeries: time + in/out/reas/cache tokens; rateSeries: time, tokens, duration) — no static legend or hint.
  • Sound + notify toggles: per-card checkboxes. Sound enables the Web Audio beeps (see below); notify enables push notifications for this session only — the ntfy topic/URL is global and set in the bottom-right field. Both save automatically. The first time you enable notify, a popup explains setup (with a persisted "don't show again" option).
  • Close button (×): appears in the card corner when a session is idle/errored/unknown. Clicking it hides the card (stored in localStorage as sp-hidden). The card stays hidden while the session is idle or errored and reappears automatically as soon as it becomes active again (running/waiting). A N hidden — restore pill in the top-left shows how many cards are hidden and restores them all. Hidden cards don't count toward the grid layout.
  • Path line: the working directory of the session, in the same style as the meta line (monospace, dim), at the top of each card, sharing a header div with the session title so there's no gap between them. When the directory is a git repo, the current branch is appended after a ·, refreshed every BRANCH_REFRESH_MS (15s) and on directory changes via a bounded git branch --show-current call (so branch switches mid-session show up within ~15s; detached HEAD clears the branch). The card's top padding is removed so the header takes the space the padding used to occupy — nothing else on the card is pushed down.
  • Meta line (small, monospace, dim): pid <pid> · v<version> · run <dur> · open <dur> (run <dur> · wait <dur> · idle <dur>) · started <HH:MM>.
    • The leading run <dur> (only while the session is running) is how long the current run has been going, reset to 0 each time a new run starts (a run = a running segment between waiting/idle breaks).
    • The leading wait <dur> (only while the session is waiting) is how long the current wait has been going, reset to 0 each time a new wait starts.
    • The leading idle <dur> (only when the session is idle) is time since the last message finished — anchored to the completion time of the last assistant response (or the creation of the last user message), so it counts from the true end of the last run, not its start.
    • open is how long the session has been open, and is the sum of the breakdown in parentheses: run (model generating), wait (awaiting your answer or a permission), and idle (no activity). Only parts with ≥1s are listed, so transient sub-second state flips never show as 0s. The plugin accumulates per-state time on every state transition and persists it per session ID in ~/.config/opencode/obvious-grid-session-times.json, so the totals accumulate across restarts of the same session — but time while the instance was closed still never counts (the in-memory clock restarts on each run, so overnight gaps are never accrued). Resuming yesterday's session shows today's work added on top of the historical totals, with started showing the session's original creation time (date-prefixed when not today).
  • Hover hints: every chip, the state watermark, the session title, the meta line, and the sound/notify toggles have mouseover tooltips explaining what they mean.

Sound Alerts

  • Beeps are generated with the Web Audio API (square + triangle octave, master gain 1.2) — no audio files, no subprocesses.
  • running → idle: ascending two-tone (659.3 Hz then 987.8 Hz).
  • running → waiting: a repeating low note (523.3 Hz) every 10s until the request is answered.
  • The AudioContext can only be started from a user gesture, so sound is armed on page load but actually unlocked the first time the sound checkbox is toggled (or any interaction). Each card remembers its own setting in localStorage (sp-sound-<pid>); the last toggled value is also stored globally (sp-sound-default) and used for new pids that appear after the page is already open.
  • Background-tab limitation (browser-imposed): while the page is in a non-foreground tab, the browser suspends the AudioContext (beeps scheduled while suspended are dropped, not queued) and throttles the 2s status poll — after ~5 min hidden, transitions may be detected up to a minute late. To compensate, a transition that happens while hidden is recorded as pending and replayed once when you refocus the tab (idle chime, or the waiting loop restarts); pending sounds are dropped if the session resumes or sound is turned off first. Beeps also can't be heard at all while the tab is hidden — for alerts that must reach you with the browser unfocused, use the notify checkbox instead: the alarm sound plays on the machine (server-side), independent of the browser (see Notifications below).

Notifications (ntfy push + alarm sound)

Events session.idle, session.error, and permission.asked can fire an ntfy.sh push notification (to your phone) and play the platform alarm sound on the machine (powershell.exe + C:\Windows\Media\Alarm01.wav on Windows, afplay on macOS, paplay on Linux; a no-op if the player or sound file is missing).

Setting it up (2 minutes, no account needed)

  1. Create a topic. ntfy topics need no registration and no payment — any name is instantly valid. Pick one; a long random string (e.g. opencode-9f3k2x7q) keeps it private, since anyone who knows the name can subscribe.
  2. Tell the page about it. Open the status page and type the topic in the ntfy field (bottom right) — either a bare name (my-topic → https://ntfy.sh/my-topic) or a full URL (https://ntfy.example.com/my-topic for a self-hosted server). It's global: one target for all sessions. Saved automatically to ~/.config/opencode/obvious-grid-config.json.
  3. Subscribe on your phone. Install the ntfy app, point it at the same server (ntfy.sh by default), and subscribe to the topic — subscribing is what creates it.
  4. Enable per session. Tick the notify checkbox on the card of the session you care about. You'll get a push (and the alarm sound) when that session goes idle (finished), errors, or asks for your permission. Off by default; deleting a session clears its flag. The first time you enable notify, a popup walks through these steps (dismissible permanently via "don't show again").

Test it

curl -d "test" https://ntfy.sh/my-topic

Your phone should ping (with the plugin's notify checkbox on, the alarm plays on your machine too).

Behavior

  • One notification per 10s max (NOTIFY_COOLDOWN_MS). Child sessions (created with a parentID) never notify, so spawning a sub-agent doesn't spam you.
  • The old hidden dotfile (.afk-notify flag) is still honored as fallback until the page saves its config file once.

How It Works

opencode ──events──▶ obvious-grid.js (entry) + lib/* ──write──▶ <os.tmpdir()>/obvious-grid-<pid>-<sessionID>.txt
                              │
                              └── serves ──▶ GET /            → fullscreen status page (page.html)
                                             GET /api/status   → JSON (polled every 2s)
                                             GET/POST /api/notify → notify flag
  1. Plugin (runs inside opencode). Listens to bus events and writes one JSON state file per session (obvious-grid-<pid>-<sessionID>.txt) to the platform temp dir (os.tmpdir() — /tmp in WSL/Linux, %TEMP% on Windows):
    • tool.execute.before / message.part.updated → running
    • permission.updated / question.asked → waiting
    • permission.replied / question.replied / question.rejected → idle
    • session.idle → idle; session.error → error
    • message.updated → model / variant / mode / agent / path, token counts, cache %, cost, finish counts, token rates, session start/last-activity
    • session.updated → session title; session.deleted → state file removed
    • Card granularity: one card per session, not per process. In CLI mode that's identical to before (one session per instance). In web mode, where one opencode server process runs several sessions, each session gets its own card. Events without a sessionID (e.g. tool.execute.before, permissions) are routed to the last-active session. Sub-agent sessions (created with a parentID) get their own cards — ↳-prefixed with a parent: line and a SUB chip, titled with the task description, carrying their own tokens/cost/calls — and their activity is rolled into the parent session's card: totals (apiCalls, userCalls, total tokens, cost, finish counts) include sub-agent work, rolled up recursively (a sub-agent's own sub-agents count into it first, so each card shows its whole subtree), and a ▸ chip on each card lists its direct sub-agents' calls and spend (up to 3 shown, active ones first; finished sub-agents are dimmed; the full per-agent breakdown is in the tooltip). Sub-agent contributions stay as long as the child's card exists (until the 60-min staleness window, session delete, or process exit). Current-request metrics (context %, cache hit, rates) stay per-session. Notifications stay per main session — child sessions never notify, and their notify toggle is hidden.
    • Startup placeholder: as soon as an instance starts, it writes an idle placeholder card (obvious-grid-<pid>.txt, no session yet) so the instance is visible immediately; the placeholder is replaced by the session's own card on the first session activity.
    • Writes are deduped — a file is only rewritten on an actual change.
    • While blocked on a pending permission/question, non-error transitions are ignored so the blue state isn't clobbered.
  2. State files. The plugin writes to the platform temp dir directly (os.tmpdir()); no Windows profile discovery, no /mnt/c translation — which is what makes the plugin platform-independent. (Sessions whose working dir is a Windows path in web mode still get their branch read via winToWsl under WSL.)
  3. Context limits. On startup it calls client.config.providers() to build a provider/model → context map used for the input-length percentage.
  4. Web port discovery. opencode web binds a random port, so for WEB processes the plugin finds the web UI port: on Linux/WSL it scans /proc/net/tcp{,6} for LISTEN sockets whose inode matches this process's fds (via /proc/<pid>/fd), excluding the status port 8765; elsewhere it falls back to netstat -ano (Windows) / lsof (macOS), bounded by the same 3s subprocess timeout. That port goes into every state file (webPort) so the page can deep-link into web sessions. Only for opencode web processes (detected from process.argv).
  5. Session backfill. On first message of a session, the plugin replays client.session.messages() so call counts, finish counts, cost, token totals and rates reflect the whole session, not just what changed since the page opened.
  6. Built-in HTTP server (Bun.serve). Binds 0.0.0.0:8765 and serves:
    • / — the fullscreen page (self-contained HTML, no external assets).
    • /api/status — JSON aggregate of all state files ({ state, running, errored, waiting, instances }, each instance carrying its notify flag), polled every 2s (and on tab focus).
    • /api/notify — GET returns { topic, sessions }; POST saves a partial update ({ topic } or { sessionID, enabled }) to obvious-grid-config.json.
    • If the port is already taken, another opencode instance is serving — this instance skips starting its own server. Instances that failed to bind keep retrying every SERVER_RETRY_MS (1s), so when the instance owning the server exits, one of the remaining instances takes over within ~1s and the page stays reachable (the page may briefly show "no connection" during the handoff).
  7. On opencode exit, the plugin deletes its own state files so its sessions drop out of the aggregate immediately. Sessions also drop out if stale for STALENESS_MS (60 min) — an idle-but-open session's card reappears on its next activity.
  8. Plugin export form. opencode ≥ the plugin refactor (June 2026) only loads modules whose default export is an object with id + server; the old V1 function export fails with a SchemaError (no shim — tracked upstream as anomalyco/opencode#39345). This plugin uses the V2 form (export default { id: "obvious-grid", server: async (input) => ({ event }) }).

Using It

On startup the plugin logs the URLs, e.g.:

[obvious-grid] v2.1.0 open http://localhost:8765 or http://192.168.1.50:8765
  • On the same machine: open http://localhost:8765.
  • On another device (phone/tablet) on the same Wi-Fi: open http://<lan-ip>:8765.

WSL2 note: the server binds 0.0.0.0 inside WSL. Reaching it from another device may require Windows-side port forwarding (netsh interface portproxy add v4tov4 listenport=8765 listenaddress=0.0.0.0 connectport=8765 connectaddress=<wsl-ip>), depending on your WSL2 networking mode. localhost always works from Windows itself.

The page is only up while opencode is running (the plugin owns the server). Close opencode → page goes unreachable.

Multiple Instances

Each opencode process writes its own state file obvious-grid-<pid>.txt (unique even across concurrent WSL processes). The page aggregates across all of them — orange if any is busy, etc.

Closed instances are dropped immediately — the aggregator checks each file's PID liveness with process.kill(pid, 0) (all instances share the same WSL PID namespace) and deletes the file if the process is gone, regardless of whether the exit handler ran. The process.on("exit"/"SIGINT"/"SIGTERM") cleanup is a best-effort backup, not the primary mechanism — opencode's client/server split means the TUI can close while the plugin's server process lingers (or dies without a clean signal).

The HTTP server has no single point of failure. Whichever instance first binds port 8765 owns the server, but every instance that failed to bind retries every 1s. Closing the first-opened instance therefore doesn't kill the page — a surviving instance takes over the port within ~1s.

Staleness Window (60 min)

Safety net for cases the PID-liveness check can't cover — most notably PID reuse: if a dead instance's PID gets recycled by a new WSL process, process.kill(pid, 0) would report it alive. The aggregator therefore also ignores (and deletes) any state file whose last write is older than 60 minutes.

60 minutes is deliberately long because the state file is only rewritten on state transitions — a long-running single tool call keeps the state at running without refreshing the timestamp, and a shorter window would wrongly show green mid-task. The PID check above handles the common close-instance case immediately; the mtime window is just insurance.

Files

Path Role
~/gitrepo/opencode-obvious-grid/obvious-grid.js Plugin entry: plugin export, event wiring, HTTP server, page serving (source of truth, git repo)
~/gitrepo/opencode-obvious-grid/lib/runtime.js PORT, PLUGIN_VERSION, client type, web-port discovery
~/gitrepo/opencode-obvious-grid/lib/win.js Bounded subprocess interop: runCmd/runWin/withTimeout, WSL detection + path translation, alarm sound
~/gitrepo/opencode-obvious-grid/lib/state.js Session tracking, state-file persistence, aggregation, git-branch refresh
~/gitrepo/opencode-obvious-grid/lib/notify.js Alarm sound + ntfy.sh push, .afk-notify flag handling
~/gitrepo/opencode-obvious-grid/page.html The static page (plain file — no template-literal escaping)
~/gitrepo/opencode-obvious-grid/scripts/check-page-script.mjs Dev check: extracts the <script> block from page.html and syntax-checks it (run via npm run check)
~/gitrepo/opencode-obvious-grid/docs/obvious-grid-view.png Screenshot of the page (used by this README)
~/gitrepo/opencode-obvious-grid/docs/grid.png Screenshot of the grid with four opencode instances (used by this README)
~/.config/opencode/plugins/obvious-grid.js Symlink → obvious-grid.js in the repo
<os.tmpdir()>/obvious-grid-<pid>-<sessionID>.txt Per-session state files, auto-created/removed (/tmp in WSL/Linux, %TEMP% on Windows)
%TEMP%\obvious-grid-<pid>.txt Idle placeholder while the instance runs with no session yet (same temp dir)
~/.config/opencode/obvious-grid-config.json Notify settings ({ notify: { topic }, sessions: { <sessionID>: true } }), edited from the page (the old status-page-config.json is honored as fallback until the page saves once)
~/.config/opencode/.afk-notify Legacy notify flag (honored as fallback until the page saves its config)
~/.config/opencode/obvious-grid-session-times.json Durable per-session open/run/wait/idle time accumulators (survive restarts)
/tmp/opencode/obvious-grid-debug.jsonl Debug log of every assistant message.updated (written unconditionally; os.tmpdir() on Windows)

Tuning

Edit the plugin modules (obvious-grid.js entry + lib/*):

Knob Location Default
Port OBVIOUS_GRID_PORT env var (fallback const PORT in lib/runtime.js) 8765
Plugin version const PLUGIN_VERSION in lib/runtime.js bump on every change
Staleness window const STALENESS_MS in lib/state.js 60 min
Server takeover retry const SERVER_RETRY_MS in obvious-grid.js 1 s
Notify cooldown const NOTIFY_COOLDOWN_MS in lib/notify.js 10 s
Page poll interval setInterval(refresh, 2000) in page.html 2000 ms
Colors colors map in page.html orange / red / blue / green
Marquee speeds updateCard tier thresholds + CSS var(--marq-dur, 90s) 90 / 60 / 30 s
State file dir os.tmpdir() in lib/state.js (configureStateDir) platform temp dir

Alternatives (and how this differs)

Project What it does How this plugin differs
opencode-observability Real-time web dashboard of tool calls, messages, and session lifecycle It ships a whole stack (plugin → Bun server → SQLite → WebSocket → Vue dashboard on separate ports) and is event-trace focused; this plugin is a single package that serves its own page and tracks per-request tokens/cost/rates, not tool traces
opencode-token-monitor Token/cost analytics as in-session tools + terminal charts, budgets Terminal-first; no web page, no multi-instance aggregate view
opencode-usage CLI usage tracker with a web dashboard Not an opencode plugin; usage accounting, not live status
opencode-notificator / opencode-notifier / opencode-notify Desktop/OS notifications for session events Notifications are a side feature here — this plugin is a status page first, with ntfy push + alarm sound merged in

The niche this fills: a self-contained, zero-infrastructure status page — one npm install, restart opencode, open a URL — that aggregates every instance (CLI and web) on the machine with live per-session metrics, on every platform opencode itself runs on.

Development

  • Dev install (from this repo): symlink obvious-grid.js (the entry) into ~/.config/opencode/plugins/ and restart opencode. No registration needed — opencode auto-loads *.js/*.ts plugins in that folder.
    ln -s ~/gitrepo/opencode-obvious-grid/obvious-grid.js ~/.config/opencode/plugins/obvious-grid.js
    
    The repo is the source of truth; the plugins-folder copy is a symlink so edits land in git immediately.
  • Why one symlink is enough (multi-module plugin): opencode loads file plugins with a real dynamic import() of the entry. The entry's relative imports (./lib/*.js, ./page.html) resolve against the entry's realpath, i.e. the repo — so the whole module graph lives in the repo and the install stays a single symlink. Verified against the upstream loader (packages/opencode/src/plugin/loader.ts load() + config/plugin.ts {plugin,plugins}/*.{ts,js} scan with symlink: true). The npm package works the same way: its main points at the entry, and the loader imports it like any npm package.
  • Do NOT drop module files directly into ~/.config/opencode/plugins/ — the auto-discovery scan matches any top-level *.js/*.ts in that folder, so stray files there would be auto-loaded as separate bogus plugins. All modules live in the repo, reached only through the import graph.
  • Versioning: bump PLUGIN_VERSION in lib/runtime.js and version in package.json (they must stay in sync) after each change. Each instance writes its own version into its state file, and the page shows it per instance — so with several instances running you can immediately see which ones are on the old plugin. Plugins are loaded once at startup and never hot-reloaded, so every instance must be restarted to pick up a new version.
  • Publishing: npm login, then npm run check && npm publish (prepublishOnly runs the check automatically). No runtime dependencies — the code doesn't import @opencode-ai/plugin; the client comes from the plugin input.

Uninstall

Remove the plugin entry from opencode.json (or delete the symlink) and restart opencode. (The one-time legacy status-border PowerShell cleanup was dropped in v2.2.0 along with the Windows profile discovery.)

About

Opencode plugin serving a status grid web page for all running opencode instances in a very obvious flavor.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages