opencode-see-image 0.2.1 → 0.3.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.
- package/README.md +43 -17
- package/index.ts +111 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Give non-vision opencode models the ability to **see images and screenshots** by routing them to a vision-capable model.
|
|
4
4
|
|
|
5
|
-
When a user attaches a screenshot to a text-only model
|
|
5
|
+
When a user attaches a screenshot to a text-only model, opencode rejects it with an error. This plugin intercepts that flow by registering a `see_image` tool that sends the image to a vision model and returns a textual description the primary model can reason about.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -15,10 +15,24 @@ Add the plugin to your opencode config:
|
|
|
15
15
|
"plugin": ["opencode-see-image"]
|
|
16
16
|
}
|
|
17
17
|
```
|
|
18
|
+
Then restart opencode.
|
|
18
19
|
|
|
19
|
-
Then **restart opencode**.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
## Install via your agent (copy-paste this)
|
|
22
|
+
|
|
23
|
+
Paste this prompt to your opencode agent, and it'll install the plugin for you.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Install the opencode-see-image plugin so I can send you screenshots. Do this:
|
|
27
|
+
|
|
28
|
+
1. Edit ~/.config/opencode/opencode.jsonc (create it if missing). Preserve any existing fields and the $schema. Add "opencode-see-image" to the "plugin" array. If "plugin" doesn't exist, add it as ["opencode-see-image"].
|
|
29
|
+
2. Check that a vision-capable provider is connected by looking for ~/.local/share/opencode/auth.json with either an "opencode-go" entry (paid, fast) OR an "opencode" entry (free). If neither is present, tell me to run /connect and select either opencode-go or opencode (key from opencode.ai/auth).
|
|
30
|
+
3. Tell me to quit and restart opencode for the plugin to load.
|
|
31
|
+
|
|
32
|
+
After I restart and attach a screenshot, you should call the see_image tool to view it.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then restart opencode.
|
|
22
36
|
|
|
23
37
|
## Prerequisites
|
|
24
38
|
|
|
@@ -62,6 +76,17 @@ see_image tool:
|
|
|
62
76
|
primary model answers using the description
|
|
63
77
|
```
|
|
64
78
|
|
|
79
|
+
## The `see_image` tool
|
|
80
|
+
|
|
81
|
+
The plugin registers a `see_image` tool with two arguments:
|
|
82
|
+
|
|
83
|
+
| Arg | Type | Required | Description |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| `filePath` | string | yes | Path to the image. Absolute path, or a bare filename like `"Screenshot 2026-06-18 at 17.32.24.png"` to auto-locate. |
|
|
86
|
+
| `question` | string | no | A specific question about the image. Defaults to a general detailed description. Use this to focus on a particular detail (e.g. `"What error is shown in the terminal?"`). |
|
|
87
|
+
|
|
88
|
+
Your model calls this tool automatically when you attach a screenshot — you don't need to do anything special. The `question` arg is optional; the model uses it when you ask something specific about the image.
|
|
89
|
+
|
|
65
90
|
## Configuration
|
|
66
91
|
|
|
67
92
|
All settings are env-var overrides. Defaults work out-of-the-box for opencode-go + MiniMax M3.
|
|
@@ -108,6 +133,21 @@ export SEE_IMAGE_MODEL="kimi-k2.7-code"
|
|
|
108
133
|
| `kimi-k2.6` | ~20s | Accurate but slow. |
|
|
109
134
|
| `qwen3.7-plus` | ~20s | Emits thinking blocks (handled). |
|
|
110
135
|
|
|
136
|
+
## Updating
|
|
137
|
+
|
|
138
|
+
**Auto-update (built in):** the plugin checks npm for a newer version on every opencode startup. If one exists, it runs `bun update` automatically and shows a toast: *"opencode-see-image updated to X.Y.Z — restart opencode to apply"*. You just need to restart opencode to load the new version. Nothing to configure.
|
|
139
|
+
|
|
140
|
+
**Manual update** (if you want to force it now):
|
|
141
|
+
```bash
|
|
142
|
+
cd ~/.cache/opencode && bun update opencode-see-image
|
|
143
|
+
```
|
|
144
|
+
Then restart opencode.
|
|
145
|
+
|
|
146
|
+
**Pin a version** in your config to opt out of auto-updates:
|
|
147
|
+
```jsonc
|
|
148
|
+
"plugin": ["opencode-see-image@0.3.0"]
|
|
149
|
+
```
|
|
150
|
+
|
|
111
151
|
## File search locations
|
|
112
152
|
|
|
113
153
|
When opencode rejects an image attachment, the model only receives a bare filename. `see_image` searches these locations in order:
|
|
@@ -120,20 +160,6 @@ When opencode rejects an image attachment, the model only receives a bare filena
|
|
|
120
160
|
|
|
121
161
|
Pass an absolute `filePath` to skip the search.
|
|
122
162
|
|
|
123
|
-
## Install via your agent (copy-paste this)
|
|
124
|
-
|
|
125
|
-
Paste this prompt to your opencode agent — it'll install the plugin for you:
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
Install the opencode-see-image plugin so I can send you screenshots. Do this:
|
|
129
|
-
|
|
130
|
-
1. Edit ~/.config/opencode/opencode.jsonc (create it if missing). Preserve any existing fields and the $schema. Add "opencode-see-image" to the "plugin" array. If "plugin" doesn't exist, add it as ["opencode-see-image"].
|
|
131
|
-
2. Check that a vision-capable provider is connected by looking for ~/.local/share/opencode/auth.json with either an "opencode-go" entry (paid, fast) OR an "opencode" entry (free). If neither is present, tell me to run /connect and select either opencode-go or opencode (key from opencode.ai/auth).
|
|
132
|
-
3. Tell me to quit and restart opencode for the plugin to load.
|
|
133
|
-
|
|
134
|
-
After I restart and attach a screenshot, you should call the see_image tool to view it.
|
|
135
|
-
```
|
|
136
|
-
|
|
137
163
|
## License
|
|
138
164
|
|
|
139
165
|
MIT
|
package/index.ts
CHANGED
|
@@ -252,8 +252,119 @@ Do NOT use \`see_image\` for reading text files — use the \`read\` tool for th
|
|
|
252
252
|
- If the tool cannot find the file, tell the user the filename and ask for a full path or to drag the file into the project directory.
|
|
253
253
|
- To inspect a specific detail, pass a targeted \`question\` (e.g. "What error is shown in the terminal?").`
|
|
254
254
|
|
|
255
|
+
// ─── Auto-update ────────────────────────────────────────────────────────────
|
|
256
|
+
// Runs once at plugin init (async, non-blocking). Checks npm for a newer
|
|
257
|
+
// version, runs `bun update` in the opencode plugin cache if available, and
|
|
258
|
+
// toasts the user to restart opencode to apply. Never throws — failures are
|
|
259
|
+
// logged and swallowed so the plugin always loads.
|
|
260
|
+
|
|
261
|
+
const PKG_NAME = "opencode-see-image"
|
|
262
|
+
const REGISTRY_LATEST = `https://registry.npmjs.org/${PKG_NAME}/latest`
|
|
263
|
+
|
|
264
|
+
function currentVersion(): string | null {
|
|
265
|
+
try {
|
|
266
|
+
// import.meta.url points at this module inside the bun cache.
|
|
267
|
+
const here = new URL(".", import.meta.url)
|
|
268
|
+
const pkgPath = new URL("package.json", here)
|
|
269
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
270
|
+
return pkg.version ?? null
|
|
271
|
+
} catch {
|
|
272
|
+
return null
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function semverGt(a: string, b: string): boolean {
|
|
277
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0)
|
|
278
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0)
|
|
279
|
+
for (let i = 0; i < 3; i++) {
|
|
280
|
+
const x = pa[i] ?? 0
|
|
281
|
+
const y = pb[i] ?? 0
|
|
282
|
+
if (x > y) return true
|
|
283
|
+
if (x < y) return false
|
|
284
|
+
}
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function maybeAutoUpdate(
|
|
289
|
+
client: any,
|
|
290
|
+
$: any,
|
|
291
|
+
log: (msg: string, level?: string) => void,
|
|
292
|
+
) {
|
|
293
|
+
const current = currentVersion()
|
|
294
|
+
if (!current) {
|
|
295
|
+
log("could not determine current version; skipping update check", "debug")
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let latest: string
|
|
300
|
+
try {
|
|
301
|
+
const res = await fetch(REGISTRY_LATEST, {
|
|
302
|
+
headers: { accept: "application/json" },
|
|
303
|
+
})
|
|
304
|
+
if (!res.ok) {
|
|
305
|
+
log(`registry fetch returned HTTP ${res.status}`, "debug")
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
const data: any = await res.json()
|
|
309
|
+
latest = data?.version
|
|
310
|
+
if (!latest) {
|
|
311
|
+
log("registry response had no version field", "debug")
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
} catch (e: any) {
|
|
315
|
+
log(`registry fetch failed: ${e?.message ?? e}`, "debug")
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!semverGt(latest, current)) {
|
|
320
|
+
log(`up to date (${current})`, "debug")
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
log(`update available: ${current} → ${latest}; running bun update`, "info")
|
|
325
|
+
|
|
326
|
+
// Update the plugin in opencode's cache. --no-save keeps the lockfile
|
|
327
|
+
// resolution intact while still pulling the new tarball. We cd into the
|
|
328
|
+
// cache dir because bun operates on the nearest package.json/lockfile.
|
|
329
|
+
const cacheDir = path.join(os.homedir(), ".cache/opencode")
|
|
330
|
+
try {
|
|
331
|
+
await $`cd ${cacheDir} && bun update ${PKG_NAME} --no-save`.quiet()
|
|
332
|
+
} catch (e: any) {
|
|
333
|
+
log(`bun update failed: ${e?.message ?? e}`, "warn")
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Tell the user to restart. Toast is non-blocking; if it fails, we log.
|
|
338
|
+
try {
|
|
339
|
+
await client?.tui?.showToast?.({
|
|
340
|
+
body: {
|
|
341
|
+
message: `${PKG_NAME} updated to ${latest} — restart opencode to apply`,
|
|
342
|
+
variant: "success",
|
|
343
|
+
},
|
|
344
|
+
})
|
|
345
|
+
} catch {
|
|
346
|
+
log(`update applied: ${current} → ${latest}; restart opencode to load`, "info")
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
255
350
|
// ─── Plugin export ──────────────────────────────────────────────────────────
|
|
256
351
|
const SeeImagePlugin: Plugin = async (ctx) => {
|
|
352
|
+
const { client, $ } = ctx
|
|
353
|
+
|
|
354
|
+
const log = (message: string, level: string = "info") => {
|
|
355
|
+
try {
|
|
356
|
+
client?.app?.log?.({
|
|
357
|
+
body: { service: PKG_NAME, level, message },
|
|
358
|
+
})
|
|
359
|
+
} catch {
|
|
360
|
+
// logging is best-effort
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Fire-and-forget the update check. Never awaited so plugin init is not
|
|
365
|
+
// delayed by network. Errors are swallowed inside.
|
|
366
|
+
maybeAutoUpdate(client, $, log).catch(() => {})
|
|
367
|
+
|
|
257
368
|
return {
|
|
258
369
|
tool: {
|
|
259
370
|
see_image: seeImageTool,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-see-image",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Give non-vision opencode models the ability to see images/screenshots by routing them to a vision-capable model (MiniMax M3 via opencode-go by default).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|