All posts

Give your coding agent an image compressor

PiPic team
Agents
pipic ./assets --replace --json
{"file":"assets/hero.png","status":"ok",…}
✓ exit 0

Ask Claude Code or Codex to "compress the images in this folder" and watch what happens. It will offer to install sharp, write a throwaway Node script, guess at quality settings, and hand you back a folder where some files got smaller, one got bigger, and nobody can tell you which.

The agent isn't the problem. Image compression is one of those capabilities that looks trivial and isn't: format-specific encoders, quality heuristics, and a result you can only judge by measuring. That's a tool's job, not a script the agent improvises each time.

The split that makes it work

There's one step an agent genuinely cannot do: sign in. pipic login opens a browser and waits for you to approve it. An agent running in a sandbox has a different HOME, no browser, and no way to click the button.

So don't ask it to. Split the work by who can actually do it:

You, once. In your own terminal:

npm i -g @pipic/cli
pipic login

Credentials land in ~/.config/pipic/config.json. Every tool running as you on that machine can now use them — including your agent.

Your agent, every time after that. Paste this into the conversation:

Use the `pipic` CLI to compress the images in ./assets.
It's already installed and signed in — do not install it or run
`pipic login` yourself.

  pipic ./assets --replace --json

It prints one JSON object per file:
  {"file":"a.png","status":"ok","before":102400,"after":41000,"saved":61400}
status is "ok", "skipped" or "error". "skipped" is not a failure — it
carries an `error` string explaining why (e.g. already small enough).

Exit 0 = all good, 1 = some files failed, 3 = not signed in,
4 = monthly quota exhausted.
Don't retry on exit 2 (usage error), 3 or 4 — on 3, stop and tell the
user to sign in. On exit 1, re-run only the paths whose rows had status
"error": a full re-run re-uploads every file and spends quota again.

Every line in there prevents a specific failure, and two of them prevent expensive ones.

Without the "do not install it or run pipic login yourself", the agent tries anyway — and pipic login waits up to ten minutes for a browser approval it can't give. Inside Codex or Claude Code that shows up as a tool call your framework eventually kills, which reads as the tool hanging.

Without the exit-1 line, an agent that sees two failures in a hundred-file run does the obvious thing and re-runs the whole directory. There's no local "already compressed" check — every file gets uploaded again, and the monthly allowance counts every request it accepts. On the free tier that's a hundred a month, so one blind retry can spend the lot.

The rest are cheaper mistakes: without a destination flag it gets a usage error, and without the note that skipped isn't a failure it reports trouble where there is none — skipped rows carry an error string too, explaining why nothing changed.

What the agent sees

An illustrative session — the shape is real, the numbers are made up and yours will differ:

> compress the images in ./assets

I'll use the pipic CLI.

  $ pipic ./assets --replace --json

  {"file":"assets/hero.png","status":"ok","before":2411233,"after":486201,"saved":1925032}
  {"file":"assets/team.jpg","status":"ok","before":880640,"after":712704,"saved":167936}
  {"file":"assets/icon.svg","status":"skipped","before":0,"after":0,"saved":0,"error":"unsupported file type"}
  {"file":"assets/logo.webp","status":"skipped","before":18944,"after":18944,"saved":0,"error":"not smaller — original kept"}

Done. Two files compressed, saving about 2.0 MB — hero.png did most of
the work, dropping from 2.4 MB to 486 KB. I left logo.webp alone because
compressing it came back larger, and skipped icon.svg since SVG isn't a
raster format.

That last paragraph is the part worth noticing. The agent isn't guessing — it's reading a contract. saved is a byte count, not a ratio. skipped is not error. not smaller — original kept says exactly why nothing changed.

Improvised scripts don't produce that. They print whatever console.log the agent felt like writing, and the summary you get back is the agent's impression of its own output.

Why "only if it's smaller" matters here

Already-optimised images sometimes come back larger. A naive script overwrites them anyway, and your repo quietly grows every time it runs.

pipic writes a file back only when the result is actually smaller. Under --replace the original stays untouched; under -o it's copied across so the output directory is still a complete set. Either way the file reports as skipped, not error — so an agent reading the output doesn't tell you something failed when nothing did.

The flag that isn't optional

--replace and -o <dir> are mutually exclusive, and one of them is required. Run pipic ./assets with neither and it exits with a usage error before sending a single request.

This is deliberate, and it matters more when an agent is driving. A tool that overwrites by default is a tool that eventually overwrites something you meant to keep, on a run you didn't fully read. Making the destination explicit means the agent has to state its intent, and you can see that intent in the command it shows you.

When the agent can't see your credentials

Sandboxed agents, containers, CI — anywhere ~/.config isn't yours. Create a token on your account page and expose it as PIPIC_TOKEN in that environment. It takes priority over the credentials file, so the same command works without pipic login.

Note that pipic token create deliberately sends you to the web page rather than minting a token locally. A stolen CLI credential shouldn't be able to mint fresh ones.

Exit codes are the interface

For anything automated, this is the part that matters most:

CodeMeaning
0Everything succeeded
1Some files failed
2Usage error
3Sign-in required
4Monthly quota exhausted

3 means run pipic login — the one thing you have to do yourself. 4 means you're out of monthly allowance for CLI and API use; nothing is charged automatically, and compressing on pipic.cc stays free and unlimited regardless.

An agent that branches on these does the right thing without asking you to interpret a wall of text. One that pattern-matches on message strings breaks the first time we reword something — which is why the codes, the status values and the field names are the stable contract, and the human-readable error text isn't.

Try it on your own images
Free, no sign-up, never stored.
Compress images