opencode-see-image 0.5.10 → 0.6.1

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/bun.lock +3 -0
  2. package/index.ts +22 -137
  3. package/package.json +3 -2
package/bun.lock CHANGED
@@ -6,6 +6,7 @@
6
6
  "name": "opencode-see-image",
7
7
  "dependencies": {
8
8
  "@opencode-ai/plugin": "^1.15.0",
9
+ "opencode-plugin-update-kit": "^0.1.0",
9
10
  },
10
11
  },
11
12
  },
@@ -52,6 +53,8 @@
52
53
 
53
54
  "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
54
55
 
56
+ "opencode-plugin-update-kit": ["opencode-plugin-update-kit@0.1.0", "", {}, "sha512-2pZ8H6QnMGN2drK0g0vHSUY7jW4sP3XItUljdZlw1iVJIhcu1sQxzEmRD6oF+T7kWRXU6uJmN9Koq9IGuyibwg=="],
57
+
55
58
  "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
56
59
 
57
60
  "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="],
package/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { tool } from "@opencode-ai/plugin"
2
+ import { autoUpdate } from "opencode-plugin-update-kit"
2
3
  import path from "path"
3
4
  import os from "os"
4
5
  import fs from "fs"
@@ -324,158 +325,42 @@ async function seeImageViaHTTP(
324
325
 
325
326
  const SYSTEM_INSTRUCTIONS = `# See Image (vision bridge), opencode-see-image plugin
326
327
 
327
- You have access to a \`see_image\` tool. The current model may not support image input directly. When a user attaches a screenshot or image, opencode rejects it and you only receive an error string containing the **filename**, no path, no pixels. Use \`see_image\` to actually view it.
328
+ You have a \`see_image\` tool. When the user attaches an image, opencode rejects it and you receive an error string containing the **filename**. You MUST call \`see_image\` to actually view it — do NOT just tell the user about the error.
328
329
 
329
- ## When to use \`see_image\`
330
+ ## When you MUST call \`see_image\`
330
331
 
331
- Use ONLY when one of these is true:
332
- 1. You receive an error like: \`Cannot read "Screenshot ....png" (this model does not support image input)\`
333
- 2. The user references an image/screenshot they expect you to see ("see this", "look at this", "can you see this", ".png"/".jpg")
334
- 3. The user pastes an image path they want you to inspect
332
+ Call \`see_image\` immediately in ALL these cases — do not inform the user, do not ask for confirmation:
335
333
 
336
- Do NOT use \`see_image\` for reading text files, use the \`read\` tool for those.
334
+ 1. You receive an error containing \`Cannot read "Screenshot ..."\` or \`Cannot read "...png"\` or \`Cannot read "...jpg"\`
335
+ 2. The error message says \`this model does not support image input\`
336
+ 3. The error starts with \`ERROR:\` and mentions an image file
337
+ 4. The user references an image/screenshot ("see this", "look at this", ".png", ".jpg")
338
+ 5. The user pastes an image path
337
339
 
338
340
  ## How to use it
339
341
 
340
- 1. **Extract the filename** from the error string (the quoted name), or use the path the user gave.
341
- 2. **Call \`see_image\`** with \`filePath\` set to the bare filename (it auto-locates) or an absolute path. Pass an optional \`question\` if the user asked something specific.
342
- 3. **Answer using the returned description** as if you saw the image. Be natural, don't mention that you used another model unless asked.
342
+ 1. Extract the filename from the error (it's in quotes in the error message, e.g. \`Screenshot 2026-06-19 at 02.18.53.png\`)
343
+ 2. Call \`see_image\` with \`filePath\` set to that bare filename
344
+ 3. Optionally pass a \`question\` if the user asked something specific
345
+ 4. Answer using the returned description as if you saw the image. Be natural.
343
346
 
344
347
  ## Important
345
348
 
346
- - Never guess or confabulate image contents from the filename or surrounding text. If you have not called \`see_image\`, you have NOT seen the image.
347
- - 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.
348
- - To inspect a specific detail, pass a targeted \`question\` (e.g. "What error is shown in the terminal?").`
349
+ - NEVER just repeat the error to the user. Call the tool.
350
+ - If \`see_image\` cannot find the file, tell the user the filename and ask for an absolute path.
351
+ - Do NOT use \`see_image\` for text files (\`.ts\`, \`.md\`, \`.json\`, etc.) use \`read\` instead.
352
+ - Never guess image contents. If you haven't called \`see_image\`, you haven't seen the image.`
349
353
 
350
354
  const PKG_NAME = "opencode-see-image"
351
- const REGISTRY_LATEST = `https://registry.npmjs.org/${PKG_NAME}/latest`
352
-
353
- function currentVersion(): string | null {
354
- // Try import.meta.url first (works when not bundled)
355
- try {
356
- const here = new URL(".", import.meta.url)
357
- const pkgPath = new URL("package.json", here)
358
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
359
- if (pkg.version) return pkg.version
360
- } catch {}
361
-
362
- // Fallback: walk up from import.meta.url looking for package.json with our name
363
- try {
364
- let dir = new URL(".", import.meta.url)
365
- for (let i = 0; i < 10; i++) {
366
- const pkgPath = new URL("package.json", dir)
367
- try {
368
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
369
- if (pkg.name === PKG_NAME && pkg.version) return pkg.version
370
- } catch {}
371
- const parent = new URL("../", dir)
372
- if (parent.href === dir.href) break
373
- dir = parent
374
- }
375
- } catch {}
376
-
377
- // Last resort: check the known opencode cache paths
378
- try {
379
- const cacheDir = path.join(os.homedir(), ".cache/opencode/packages")
380
- if (fs.existsSync(cacheDir)) {
381
- for (const sub of fs.readdirSync(cacheDir)) {
382
- if (sub.startsWith(PKG_NAME)) {
383
- const pkgPath = path.join(cacheDir, sub, "node_modules", PKG_NAME, "package.json")
384
- try {
385
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
386
- if (pkg.version) return pkg.version
387
- } catch {}
388
- }
389
- }
390
- }
391
- } catch {}
392
-
393
- return null
394
- }
395
-
396
- function semverGt(a: string, b: string): boolean {
397
- const pa = a.split(".").map((n) => parseInt(n, 10) || 0)
398
- const pb = b.split(".").map((n) => parseInt(n, 10) || 0)
399
- for (let i = 0; i < 3; i++) {
400
- const x = pa[i] ?? 0
401
- const y = pb[i] ?? 0
402
- if (x > y) return true
403
- if (x < y) return false
404
- }
405
- return false
406
- }
407
-
408
- async function maybeAutoUpdate(
409
- client: any,
410
- $: any,
411
- log: (msg: string, level?: string) => void,
412
- ) {
413
- log(`starting update check`, "debug")
414
- const current = currentVersion()
415
- if (!current) {
416
- log(`could not determine current version`, "warn")
417
- return
418
- }
419
- log(`current version: ${current}`, "debug")
420
-
421
- let latest: string
422
- try {
423
- const res = await fetch(REGISTRY_LATEST, {
424
- headers: { accept: "application/json" },
425
- })
426
- if (!res.ok) return
427
- const data: any = await res.json()
428
- latest = data?.version
429
- if (!latest) return
430
- } catch {
431
- return
432
- }
433
-
434
- if (!semverGt(latest, current)) return
435
-
436
- log(`update available: ${current} -> ${latest}; updating`, "info")
437
-
438
- const specifier = `${PKG_NAME}@${latest}`
439
- const opencodeBin =
440
- process.env.OPENCODE_BIN ||
441
- path.join(os.homedir(), ".opencode/bin/opencode")
442
- try {
443
- await $`${opencodeBin} plugin ${specifier} --force --global`.quiet()
444
- } catch (e: any) {
445
- try {
446
- await $`opencode plugin ${specifier} --force --global`.quiet()
447
- } catch (e2: any) {
448
- log(`plugin update failed: ${e2?.message ?? e2}`, "warn")
449
- return
450
- }
451
- }
452
-
453
- log(`update applied: ${current} -> ${latest}; restart opencode to load`, "info")
454
- try {
455
- await client?.tui?.showToast?.({ body: { message: `${PKG_NAME} updated to ${latest}, restart opencode to apply`, variant: "success", duration: 86_400_000 } })
456
- } catch {
457
- // toast is non-critical, log already captured
458
- }
459
- }
460
355
 
461
356
  const SeeImagePlugin: Plugin = async (ctx) => {
462
357
  const { client, $ } = ctx
463
358
 
464
- const log = (message: string, level: string = "info") => {
465
- try {
466
- client?.app?.log?.({ body: { service: PKG_NAME, level, message } })
467
- } catch {
468
- // fallback for environments where client.app.log is unavailable
469
- if (process.env.NODE_ENV !== "production") {
470
- console.log(`[${PKG_NAME}] ${level}: ${message}`)
471
- }
472
- }
473
- }
474
-
475
- const version = currentVersion() || "unknown"
476
- log(`plugin initialized v${version}`, "info")
477
- maybeAutoUpdate(client, $, log).catch((e) => {
478
- log(`auto-update error: ${e?.message ?? e}`, "warn")
359
+ autoUpdate({
360
+ pkgName: PKG_NAME,
361
+ client,
362
+ $,
363
+ importMeta: import.meta,
479
364
  })
480
365
 
481
366
  const seeImageTool = tool({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-see-image",
3
- "version": "0.5.10",
3
+ "version": "0.6.1",
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",
@@ -22,6 +22,7 @@
22
22
  ],
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@opencode-ai/plugin": "^1.15.0"
25
+ "@opencode-ai/plugin": "^1.15.0",
26
+ "opencode-plugin-update-kit": "^0.1.0"
26
27
  }
27
28
  }