mozex/iminify-plugin/optimize-images

Claude Code and agent skill by mozex/iminify-plugin

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Optimize the images in a project with Iminify and write the smaller files back in place. Compresses by default; converts to WebP, AVIF, JPG or PNG, resizes, fits each file under a size in KB, or adds alt text along the way, when asked. Use when the user asks to compress, optimize, shrink, convert or resize the images in a project, a folder or a set of files, or to get them under a file size. Not for alt text or file names alone (the image-seo skill) or for how a page loads its images (the fix-c…

Install

npx vskill@latest install mozex/iminify-plugin/optimize-images

SKILL.md

Optimize images

Compress a project's images with Iminify and put the smaller files back where they were. Iminify does the encoding on the user's own account, through the iminify MCP server this plugin connects. This skill is the procedure around it: which files, which settings, and what gets replaced.

It changes files in the user's project, so it confirms before it starts and never overwrites anything that can't be restored.

0. Check the connection and the allowance

The Iminify tools are get_usage, create_upload_link, compress_images and get_image, plus write_alt_text for step 7. If they're missing, or a call says the server needs authentication, stop and tell the user how to sign in:

  • Claude Code: run /mcp, pick plugin:iminify:iminify, and press Approve on the Iminify page that opens.
  • Cursor: open Cursor Settings, then MCP, and press Connect next to iminify.

Signing in needs an Iminify account with a verified email address. The free plan works.

Then call get_usage and note:

  • usage.images.remaining: images left today, null on a plan with unlimited images;
  • limits.max_file_size: the largest file the plan takes, in bytes;
  • limits.compressions_per_minute: how many compressions a minute the plan allows;
  • usage.alt_texts.remaining: AI alt texts left today, when the user wants alt text.

1. Find the images

Work on exactly what the user named: a file, a folder, a pattern. When they said "the project" or "the site", look through the project. In a git repository, list tracked files with git ls-files, so ignored and generated files are left out. Otherwise, search the folder and skip dependency, build and cache folders: node_modules, vendor, .git, dist, build, public/build, .next, .nuxt, .output, coverage, storage, tmp.

Iminify reads .jpg, .jpeg, .png, .webp, .gif, .heic, .heif, .tif and .tiff. Leave everything else alone, SVG, ICO and AVIF included. Leave out files larger than max_file_size too, and name them in the report.

Show the user what you found before uploading anything: the number of files, their total size, the largest few, and the settings you'll use (step 3). Wait for a yes, unless the user already named the exact files and settings. When remaining is a number and there are more files than it allows today, say so and offer to start with the largest ones.

2. Make sure the originals can come back

  • In a git repository, a file can be restored after it's replaced only when git tracks it and it has no uncommitted changes: git ls-files --error-unmatch <file> succeeds and git status --porcelain <file> prints nothing. An ignored file prints nothing too, so the first check is what tells them apart. Name every file that fails either check (untracked, ignored, or changed) and ask before touching it.
  • Outside git, say that the originals stay in the user's Iminify account (Iminify keeps every upload until the user deletes it, and get_image gives a fresh original.download_url at any time), and ask whether to go on.

3. Choose the settings

Send no settings unless the user asked for something. The defaults suit a website: level smart (the lowest quality that still looks like the original), the same format, and the metadata stripped (a photo's orientation is applied to its pixels first, so nothing comes back sideways).

The user asks forSend
no visible loss at all, "lossless"level: "lossless"
"as small as possible", "aggressive"level: "ultra"
WebP, AVIF, JPG or PNGformat: "webp" (or "avif", "jpg", "png"), and read step 6 first
"whatever format is smallest"format: "auto", and read step 6 first
no wider than N pixelswidth: N, only for the images wider than N (below)
under N KB each, for a form or a page with a file size limitsize_limit: N, and read the size limit paragraph below
keep camera data, dates or locationkeep_metadata: true
alt text, image descriptions, accessibilitynothing extra (alt text is on by default), alt_text_language for another language, and read step 7
only alt text or file names, nothing elsestop here and use the image-seo skill instead

Unless the user asked for alt text, send alt_text: false. Iminify writes an AI alt text for every image by default, from a small copy it sends to Anthropic, and each one counts against the day's AI alt texts. This procedure writes files back and uses no alt text, so there is no reason to send the copies or spend the allowance.

A width or height is exact: Iminify enlarges an image that is smaller than it. For "no wider than 1600", read each image's width first and send width only in a call for the wider images. The rest go in a call without it. To read a width: file prints it for PNG and JPEG, as do ImageMagick's identify and, on macOS, sips -g pixelWidth. In Windows PowerShell, for JPG, PNG, GIF and TIFF (not WebP or HEIC): Add-Type -AssemblyName System.Drawing; ([System.Drawing.Image]::FromFile((Resolve-Path 'path\to\hero.png'))).Width. When a file's width can't be read, don't resize it: leave width out for it and say so in the report. Settings apply to every image in a call, so images that need different settings go in separate calls.

A size_limit is the most each optimized file may weigh, in KB of 1,000 bytes. Iminify lowers the quality until the file fits, and when even the lowest quality is over, makes the image smaller in pixels until it fits. The limit comes before the level: with level: "lossless", a file over the limit is encoded again with loss, so tell the user when they asked for both. Two things can change beyond the bytes, so say both when you show the list in step 1: a PNG or GIF over the limit comes back as JPG, WebP or AVIF, which is a conversion (ask whether to convert those and follow step 6, or leave them out of the call), and a photo can come back smaller in pixels. format: "png" with a limit is refused, and an animation is never fitted. Each finished image's size_limit_status says how it came out: under, resized (made smaller in pixels to fit) or over (it couldn't get under).

An animated WebP or PNG can't be resized: Iminify refuses the resize with animation_not_resizable, because only its first frame would come back (step 4 says what to do then). An animated GIF can be resized, frame by frame. Sent without a resize and without a format change, an animated WebP or PNG comes back with every frame, only its metadata taken off (kept with keep_metadata: true).

HEIC and TIFF always come back in another format, because Iminify doesn't write either. Treat them as conversions (step 6).

4. Upload and compress, one batch at a time

One compress_images call takes a limited number of images (its own description gives the number, and a call over it is refused whole). Split the files into batches of at most that many, and finish each batch before you start the next:

  1. For each file in the batch, one at a time:
  2. Call create_upload_link with the file's name. The answer has an upload_id, an upload_url and a ready curl command.
  3. Send the file straight away, because the link expires within minutes and works once: run the answer's curl command with the file's real path in place of its name. In Windows PowerShell, type curl.exe: plain curl there is an alias for Invoke-WebRequest, which takes different arguments.
  4. Keep the upload_id with the file's path.
  5. Call compress_images with the batch's upload_ids and the settings.

Iminify counts every file sent but not yet compressed, and stops handing out upload links once that count reaches the images the plan can still compress today, and never more than a fixed number. Compressing each batch before uploading the next keeps the count low. When create_upload_link is refused, handle it by its code:

  • uploads_waiting: uploads not yet compressed are holding every place. When the message names upload ids, those files were already sent: send them to compress_images in calls of at most the number its description gives, then ask for the link again. An id you didn't keep, left over from an earlier run that stopped, still gets compressed so it stops counting, but don't write its result over any file. When the message names no ids, the places are held by open links whose file never arrived, and they lapse within the minutes it gives: wait that long and ask again, or stop and report.
  • daily_limit_reached: stop. Report what's done, what's left, and the message as it came.

The answer from compress_images lists its images in the order of the upload_ids you sent, less the ones turned down, and each has an id: keep a map from that id to the local path. Anything turned down is in refused, with its source (the upload id), code and message. When nothing at all could be queued, the call comes back as an error that carries the same refused list. Handle each refusal by its code:

  • rate_limited: the minute's allowance is spent. Wait retry_after seconds and send those upload ids again. An upload refused this way can still be used.
  • daily_limit_reached: stop. Report what's done, what's left, and the message as it came.
  • animation_not_resizable: the file is an animated WebP or PNG and the call asked for a resize. The upload is kept: send the same upload id again in a call with no width, height, scale or format, and every frame stays. Say in the report that it wasn't resized.
  • anything else: leave that file alone and give the reason in the report.

An uploaded file keeps for about a day, so a batch interrupted between its uploads and its compress_images call can still be finished with the ids you kept.

5. Wait, then write the results back

Poll get_image for each id, a few seconds apart, until its status is completed, already-optimized, failed or cancelled. Deal with each image as soon as it finishes, because download addresses expire after an hour. Ask get_image again for a fresh one if an address has expired.

  • completed, the same format as the file, and optimized.size smaller than the file on disk: save optimized.download_url to a temporary file in the same folder, such as .hero.png.iminify beside hero.png, following redirects and failing on an HTTP error rather than saving the error page. Check that its size in bytes equals optimized.size, then move it over the original. If it doesn't, delete the temporary file, ask get_image for a fresh address and download again.
  • completed in another format: it's a conversion. Follow step 6.
  • With a size_limit, read size_limit_status too. resized: optimized.width and optimized.height are smaller than the file's own; write it back the way its format's bullet says (the first bullet, or step 6 for a conversion), and name it in the report with its new size. over: it couldn't get under the limit; write it back only when it is still smaller than the file on disk, and say in the report that it missed, with its size.
  • content_credentials.status is removed: the file carries Content Credentials, a signed record of where it came from (every Gemini image has one), and the optimized copy doesn't, because any change to a file breaks their signature. Write it back as its format's bullet says, and name it in the report: the project's file loses the record once it's written over (git still has the old one, if the project uses it), while the upload Iminify keeps at original.download_url still carries it.
  • already-optimized: Iminify couldn't make the image smaller with these settings. Leave the file as it is and say so plainly, with no saving reported.
  • completed but not smaller: leave the file as it is.
  • failed or cancelled: leave the file as it is, and pass on the hint if there is one.

6. Conversions change file names

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
1
7-day trend score
3

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/mozex/iminify-plugin/optimize-images/badge)](https://verified-skill.com/skills/mozex/iminify-plugin/optimize-images)

Something wrong with this skill or its page? Tell us.