opencode-see-image 0.2.0 → 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.
Files changed (3) hide show
  1. package/README.md +43 -17
  2. package/index.ts +111 -0
  3. 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 (e.g. GLM-5.2, DeepSeek, Kimi), opencode rejects it with an error. This plugin intercepts that flow: it registers a `see_image` tool that sends the image to a vision model (MiniMax M3 by default) and returns a textual description the primary model can reason about.
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
- That's it the plugin self-contains both the tool and the triggering instructions (injected into the system prompt). No separate skill file needed.
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.2.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",