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

img-subir

v0.0.1

Published

Agent-friendly CLI that optimizes images (PNG/JPEG/WebP/AVIF/GIF/SVG) for websites, READMEs and Subir pages.

Readme

img-subir

Agent-friendly CLI that makes images small enough for websites, READMEs and Subir pages. Point it at a folder of screenshots and it does the right thing: downsizes Retina captures, strips metadata, tries WebP / AVIF / PNG / JPEG, keeps the smallest one that still looks the same, and never writes a file that is bigger than the original.

npx img-subir ./assets
img-subir v0.1.0 · preset: web

  ✔ [email protected]   2880×1800 PNG 3.40 MB  →  [email protected] 1920×1200 q82   148 KB  −95.7%
  ✔ settings.png       1440×900  PNG 1.10 MB  →  settings.webp     1440×900  q82    92 KB  −91.8%
  ✔ logo.svg           SVG 41 KB               →  logo.svg          (svgo)          12 KB  −70.7%
  · already.webp       320×200 WEBP 200 B     kept: no candidate smaller than original

Saved 4.54 MB → 252 KB (−94.6%) in 1.8s · 3 optimized, 1 kept
Originals kept in assets/.img-subir-originals — delete or ignore before upload/commit (or use --no-backup).

Why

Screenshots straight out of macOS are 3–5 MB each. Put a few in a plan page, a README or a marketing site and the page is slow, the repo bloats, and storage fills up. img-subir is the one command an agent (or you) runs right before uploading or committing.

Install

npx img-subir <paths>        # no install
npm i -g img-subir           # or global
pnpm add -D img-subir        # or per project

Node ≥ 20. Uses sharp (prebuilt libvips, no system deps) and svgo.

Usage

img-subir <paths...> [options]      files, directories (recursive) or globs

  -p, --preset <name>     web | github | screenshot | og | thumb | lossless   (default: web)
  -f, --format <fmt>      auto | keep | webp | avif | png | jpeg              (default: auto)
      --kind <kind>       auto | ui | photo | graphic                        (default: auto)
      --max-width <px>    cap width (overrides preset)
      --max-height <px>   cap height (overrides preset)
  -q, --quality <1-100>   starting quality for lossy encoders (overrides preset)
      --target-kb <n>     binary-search quality until each file is ≤ n KB
  -o, --out <dir>         write to dir (mirrors structure; kept files are copied too) instead of in place
      --suffix <str>      insert before the extension, e.g. ".min"
      --no-backup         don't keep originals in .img-subir-originals/
      --no-rename         keep the original filename even if the format changes
      --rewrite-refs      update src/href/url() in html/md/tsx/css that point at renamed files
  -n, --dry-run           report, write nothing
      --json              JSON report on stdout, human log on stderr
      --fast              skip SSIM checks (~3× faster)
  -c, --concurrency <n>   files in flight (default: cpus − 1)
      --quiet / --verbose

img-subir inspect <paths...>   metadata, detected kind, and what optimize would do
img-subir presets              list presets
img-subir skill                print the SKILL.md for agents
img-subir doctor               verify sharp and every encoder on this machine

Presets

| Preset | For | Max width | Tries | Notes | | ------------ | ------------------------------------- | --------- | ----------------------- | ----------------------------------------------------------- | | web | websites, Subir pages, docs | 1920 | webp, avif, png, jpeg | default. AVIF kept only when ≥15% smaller than WebP | | github | README images, PR screenshots | 1600 | webp, png, jpeg | never AVIF | | screenshot | crisp UI captures | 1600 | webp, webp-lossless, png | forces kind=ui, higher quality floor | | og | Open Graph / social cards | 1200×630 | jpeg, png | many crawlers dislike WebP | | thumb | thumbnails, avatars | 480 | webp, avif | aggressive; pair with --target-kb | | lossless | pixel-exact | none | png, webp-lossless | no resize, no lossy |

Unknown flags, missing values and malformed config files are errors (exit 2), never silent fallbacks, so an agent typo like --dryrun cannot cause real writes.

Flags beat config beat preset. Config lives in img-subir.config.json, .img-subirrc.json or a "img-subir" key in package.json, found by walking up from the current directory:

{ "preset": "github", "maxWidth": 1400, "rewriteRefs": true }

How it decides

  1. Probe — format, size, alpha, animation. A PNG whose alpha channel is entirely opaque is treated as opaque (this unlocks JPEG and smaller WebP for most macOS screenshots).
  2. Classify — ui / photo / graphic from cheap pixel statistics (flat runs, distinct colors) plus filename and device-size hints. Override with --kind.
  3. Resize — fit inside the preset's max width/height, never enlarge, Lanczos3. EXIF orientation is applied, colors converted to sRGB, metadata dropped.
  4. Encode candidates in parallel: lossy WebP, AVIF (skipped above 4 MP), palette PNG, mozjpeg JPEG, plus lossless PNG/WebP for graphics.
  5. Quality floor — each lossy candidate is compared to the resized source with SSIM (0.985 for UI, 0.96 for photos by default). Candidates below the floor get +6 quality and another try, up to three times. --fast skips this.
  6. Pick the smallest passing candidate. AVIF must beat the best non-AVIF by 15%. If nothing is smaller than the input (or the gain is under 5%, or under 10% when re-encoding an already-lossy file), the file is kept untouched.
  7. Write atomically — temp file, move the original to .img-subir-originals/, rename. A crash never leaves a half-written image.

SVGs go through svgo (viewBox, width/height and ids preserved). Animated GIF/APNG/WebP become animated WebP (lossless under the lossless preset).

Safety rules: an existing file at the output path is never overwritten (the input is kept with a reason), --out may not be an input directory, and when the extension changes the new file is published before the original is moved or deleted.

For agents

img-subir is built to be run blind by a coding agent:

  • Non-interactive, no prompts, no colors when stdout isn't a TTY.
  • --json emits a versioned report (schemaVersion: 1). renamed[] lists every a.png → a.webp so the agent can fix references, or pass --rewrite-refs and read rewrittenRefs[].
  • Every kept file has a reason; every written file has an actions[] trail.
  • Exit codes: 0 ok · 1 a file failed (others still written) · 2 bad invocation · 3 --target-kb could not be met within the quality floor (best effort written).
  • img-subir skill prints a ready-to-save SKILL.md for Claude Code, Codex and friends. Drop it in ~/.claude/skills/img-subir/.
// npx img-subir ./assets --json
{
  "schemaVersion": 1,
  "preset": "web",
  "files": [
    {
      "input":  { "path": "…/[email protected]", "format": "png", "width": 2880, "height": 1800, "bytes": 3565158, "hasAlpha": true, "animated": false },
      "output": { "path": "…/[email protected]", "format": "webp", "width": 1920, "height": 1200, "bytes": 151552, "quality": 82 },
      "kind": "ui", "status": "optimized", "savedBytes": 3413606, "savedPercent": 95.7,
      "actions": ["drop-unused-alpha", "resize:1920x1200", "strip-metadata", "encode:webp@82"],
      "candidates": [{ "format": "webp", "bytes": 151552, "quality": 82, "ssim": 0.9931, "passed": true }, …]
    }
  ],
  "totals": { "files": 4, "inputBytes": 4761600, "outputBytes": 258048, "savedPercent": 94.6, "optimized": 3, "kept": 1, "failed": 0, "budgetMissed": 0 },
  "renamed": [{ "from": "…/[email protected]", "to": "…/[email protected]" }],
  "rewrittenRefs": [],
  "backupDirs": ["…/assets/.img-subir-originals"]
}

With Subir

npx img-subir ./site --rewrite-refs      # shrink assets, fix <img src> in the HTML
rm -rf ./site/.img-subir-originals       # once you've eyeballed the page
subir upload ./site

Programmatic API

import { optimize, inspect, type Report } from "img-subir";

const report: Report = await optimize({ paths: ["./assets"], preset: "github", rewriteRefs: true });
console.log(report.totals.savedPercent);

License

MIT