Capability-aware vision + paste plugin for opencode.
opencode-vision makes image files referenced in your messages usable by
the active model — and never wastes a call:
- Multimodal primary (e.g. MiniMax-M3, GPT-5 vision) → image paths in your
message are rewritten to
[Image-#N]markers and the images are attached natively. Zero delegation, zero extra tokens. - Text-only primary → the message gets markers + a hint (or an automatic
description) and the model delegates to a configured vision-capable model
through a hidden opencode subagent (the
visionagent), not a hand-rolled HTTP call.
Ported from the pi extension @gitawego/pi-vision.
- Paste UX. Paths (absolute
/…, home~/…, relative./…, WindowsC:\…, drag-and-drop escaped spaces) →[Image-#N]markers.- Multimodal primary → native attachment.
- Text-only primary →
hint(zero-token nudge),auto(auto-describe, timeout protected, concurrency-bounded), oroff(markers only).
describe_imagetool. Single (image_path) or batch (image_paths, up to 50) analysis with one subagent turn. Returns structured text.- Subagent delegation. A hidden
visionsubagent runs the configured vision model in a child session; the images travel as data URLs so the model sees them natively. Retry (5xx/429/network, backoff), configurable fallback model, abort-aware. - Settings menu (TUI).
/vision, the command palette (Vision settings), orctrl+shift+mopens a dialog-based panel — model picker (auto-lists vision-capable models), paste mode, marker style, local-only, cache, concurrency, and more.ctrl+shift+iis the quick model picker. - Capability-aware by default. Auto-detects a vision-capable model when
none is configured. On a multimodal primary,
describe_imagerefuses with a redirect instead of delegating. - Cache. Content-addressed (
sha256(bytes)+ prompt + model), memory + optional disk LRU. A repeat call costs zero subagent spawns. - Audit log. Every delegation is recorded as one JSONL line in
~/.config/opencode/vision-audit.log(routing only — image bytes are never logged, only asource_hashfingerprint). Opt-out. - Local-only mode. Image bytes never leave the machine; cache hits still work, a miss refuses with a clear error.
Requires opencode ≥ 1.18 (TUI plugin support). Loads the
./serverand./tuipackage entries automatically. Plugins load at opencode startup — restart opencode after installing.
One command installs both entries (server + TUI settings page):
opencode plugin /path/to/opencode-vision -gThis is fully transparent: opencode surgically inserts the plugin into the
plugin array of both opencode.json and tui.json (global config),
preserving every other setting and comment, and skips files where the plugin
is already present. The in-TUI plugin manager (/plugins → install) does the
same. Re-running is a no-op.
Why both files? opencode loads the two entries from different config files:
- The server entry (image detection,
describe_image, subagent delegation) loads from thepluginarray inopencode.json. - The TUI entry (the
/visionsettings page,ctrl+shift+mhotkeys) loads from thepluginarray intui.json(global~/.config/opencode/tui.json, or a project-localtui.json/.opencode/tui.json).
If the plugin only appears in opencode.json, opencode starts and image
handling works, but there is no settings page (/vision won't exist).
From git (the pi install git:... equivalent):
opencode plugin github:gitawego/opencode-vision -g # install + write to global opencode.json
opencode plug github:gitawego/opencode-vision # alias; -f to force-replace a pinned versionor list the git spec in opencode.json directly (a #tag/#commit pin works):
The loader passes non-path specs to
@npmcli/arborist(npm-package-arg), sogithub:user/repo,git+https://github.com/user/repo.git, bareuser/repo, and#committishpins all work. This is undocumented upstream — the officially documented methods are npm packages and local files — so treat git install as experimental.
From a local checkout (dev):
// opencode.json
{ "plugin": ["/path/to/opencode-vision"] }From npm (once published):
{ "plugin": ["@gitawego/opencode-vision"] }Everything is configurable from the settings menu (no manual file editing needed):
| Action | How |
|---|---|
| Open settings | /vision or command palette → "Vision settings" or ctrl+shift+m |
| Pick vision model | Settings → "Vision model", or ctrl+shift+i |
| Paste behavior on text-only primaries | Settings → "Paste mode" (hint/auto/off) |
| Local-only / audit / cache / concurrency | Settings menu |
| Reset | Settings → "Clear config" |
Config is stored at ~/.config/opencode/vision.json. OPENCODE_VISION_CONFIG
overrides the path (dev convenience).
On a text-only primary the model can analyze images directly:
describe_image(image_path: "/tmp/screenshot.png", prompt: "What's in this image?")
describe_image(image_paths: ["/tmp/a.png", "/tmp/b.png"], prompt: "Compare these.")
On a multimodal primary you don't need the tool — just reference the path and the image is attached natively.
- Server (
confighook) registers the hiddenvisionsubagent and auto-detects a vision-capable model. - Paste hook (
chat.message) rewrites image paths into[Image-#N]markers and, for multimodal primaries, attaches them as native file parts. - Delegation (
describe_imageor auto mode) spawns a child opencode session running thevisionagent with the vision model; images are attached as data-URL file parts. The description is returned as the tool result. - TUI provides the settings menu + commands.
describe_image is always registered (opencode plugins can't hide a tool per
model). For multimodal primaries its execute detects the active model's
capability at runtime and returns a "respond directly" redirect instead of
delegating.
opencodestarts with no plugin errors:grep -iE "opencode-vision|vision" ~/.local/share/opencode/log/opencode.log | grep -iE "error|fail"→ empty./visionopens the settings menu; pick a vision model from the list.- Reference an image path in a message (e.g.
/tmp/screenshot.png) on a text-only primary → the message gains markers + a hint, and the model can calldescribe_image. - Check
~/.config/opencode/vision-audit.logfor delegation entries.
src/server/index.ts server plugin: hooks + describe_image tool
src/tui/index.tsx TUI plugin: settings menu, commands, hotkey
src/… pure modules (config, paste, marker, cache, delegate, …)
test/ node:test suites (30 tests, fake-client delegate tests)
SPEC.md design + porting matrix + validation items
npm install
npm run typecheck # tsc --noEmit
npm test # node --import tsx --testForked design from getpipher/vision
→ @gitawego/pi-vision (pi) — the code, config shape and behavior originate
there. This is the opencode plugin API port with subagent-based delegation.
MIT
{ "plugin": ["github:gitawego/opencode-vision#v0.1.0"] }