npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-image-compact

v0.2.2

Published

OpenCode plugin that automatically compresses images exceeding Anthropic's 5MB base64 API limit

Readme

opencode-image-compact

OpenCode plugin that compresses oversized image attachments before they hit Anthropic's 5 MB base64 limit.

License: MIT

The Problem

Anthropic rejects image payloads once the base64 body crosses 5,242,880 bytes. Because base64 adds about 33% overhead, a raw image can fail well before it looks unusually large on disk.

In OpenCode, that failure is especially annoying because the oversized image can stay in the session history. After that, later prompts may keep failing with the same image-too-large error until the bad message is no longer part of the conversation.

This plugin exists to intercept that case before the request reaches the model. It targets the same recurring problem described in OpenCode issues #2104, #20021, and #33152.

How It Works

Image attached
  -> check base64 size
  -> if under limit, pass through unchanged
  -> if over limit, resize and recompress
  -> send the updated image to the LLM

The plugin uses two hooks:

  • tool.execute.after catches oversized images returned by the read tool as early as possible.
  • experimental.chat.messages.transform acts as the backup path before the request goes to Anthropic.

The plugin is zero-config today. Import it, list it in opencode.json, and it uses the built-in defaults from src/config.ts.

Installation

  1. Install the package in your OpenCode project.
bun add opencode-image-compact
  1. Add it to your opencode.json plugin array.
{
  "plugin": ["opencode-image-compact"]
}

That JSON block is the whole setup. No extra plugin options are required for the current release.

Configuration

This version is zero-config at runtime. It does not read custom plugin options yet.

The current built-in behavior is:

| Setting | Default | Meaning | | --- | --- | --- | | maxWidth | 1568 | Longest edge target used during resize | | maxHeight | 1568 | Matching height bound used during resize | | jpegQuality | 80 | JPEG output quality | | pngCompressionLevel | 9 | PNG compression level | | webpQuality | 80 | WebP output quality | | maxRawBytes | 3500000 | Raw-byte safety threshold before base64 inflation | | formatConversion | "never" | Preserve original format by default |

These defaults matter because the plugin is intentionally conservative. It only changes images when it needs to, and it keeps the original format unless the implementation is changed in code.

Examples

Typical oversized screenshot flow:

8.4 MB screenshot
  -> resized to 1568 px longest edge
  -> recompressed as JPEG quality 80
  -> 1.2 MB output

Example log output:

[image-compact] Compressed 8.4 MB -> 1.2 MB (jpeg, 1568x882)

Small image that does not need work:

[image-compact] Image passed through: under size limit

How It Handles Edge Cases

  • Animated GIFs pass through unchanged.
  • Transparent PNGs stay PNG by default because formatConversion is "never".
  • Small images under the safety threshold pass through unchanged.
  • Images that still cannot be reduced below the limit fall back to the original buffer, so the plugin does not make the request worse.
  • Corrupted or malformed image data is handled defensively. The plugin falls back to the original payload and logs an error when compression fails.

Troubleshooting

I still see the Anthropic 5 MB error

This plugin only helps when OpenCode is sending an inline image payload that the hooks can intercept. If the original image still cannot be reduced enough without breaking the fallback rules, the plugin returns the original image and the API error will still surface.

The plugin looks installed, but nothing changes

Check that opencode-image-compact is listed in the plugin array in opencode.json. The current release does not require options, so the plugin entry should just be the package name string.

If you use oh-my-opencode, place opencode-image-compact before it in the array. There is a known issue where the OpenCode plugin loader can stop processing subsequent plugins after loading oh-my-opencode:

{
  "plugin": [
    "opencode-image-compact",
    "oh-my-opencode@latest"
  ]
}

My PNG was not converted to JPEG

That is expected in this release. The built-in default is formatConversion: "never", which preserves the original format.

Why do I see pass-through logs?

Pass-through is normal for images that are already under the threshold, for animated GIFs, for malformed data URIs, or for cases where compression would not safely improve the payload.

Contributing

Local development:

bun install
bun run typecheck
bun run build

Useful checks:

npm pack --dry-run

If you change the compression behavior, keep the docs aligned with the actual defaults in src/config.ts and the real hook behavior in src/hooks/tool-hook.ts and src/hooks/message-hook.ts.

License

MIT