docs · usage hub

Everything to put your files on a diet.

onadiet is a local, no-upload CLI and library that shrinks PDFs, images, SVGs, and whole folders under a size limit — on your machine, safe by default, with an honest measured receipt. This is the usage hub: install, every command and flag, the five plans, the four surfaces, and honest exit codes.

npm i -g onadiet
Install options ↓
Install

On npm now — pick how you want it.

The CLI ships as the unscoped onadiet package (binary diet, alias onadiet); the engine and adapters are scoped @onadiet/*. Published to npm at v0.1.0 — early (0.x), so the surface may still move. Requires Node >=22.

Global install — then just type diet anywhere.
npm i -g onadiet
No install — a one-off run with npx.
npx onadiet report.pdf --to 5mb
As a library — the pure engine for your own code (see the library surface).
npm i @onadiet/core

Build from source to hack on onadiet itself: clone the repo and run install → lint → typecheck → test → build with no manual setup — see the repository on GitHub.

Commands

One hero command, four sub-verbs.

The bare diet <path> is the hero — it slims a file or a folder. The sub-verbs cover everything around it. Every command speaks --json for scripts and agents (a stable object on stdout, logs on stderr).

Command What it does
diet <file> Slim a file (the default command) → <name>.diet.<ext>. Default plan balanced.
diet <dir> Slim a folder → <dir>.diet/, structure preserved; unknown files copied through.
diet weigh <path> Step on the scale — what it weighs and what's heavy. No writes.
diet plan <path> Dry-run — the same computation as slim, but writes nothing.
diet check <path> CI gate — pass/fail a --max / --max-total budget with honest exit codes.
diet checkup Is the kitchen stocked? Which local engines are available (a static report today).

Dry-run is the plan verb — there is no --dry-run flag. There is no --version flag either; diet --help (-h) prints usage.

Real examples — copy and run:
diet report.pdf --to 5mb
diet ./photos --to-each 500kb --out ./slim
diet weigh brochure.pdf
diet plan invoice.pdf --to 5mb --json
diet check ./public --max 2mb --max-total 25mb
diet checkup
Flags

The options, and exactly what they do.

Targets share one vocabulary — the slim verbs aim toward a size with the --to* family, and check fails a budget with --max*. Sizes accept decimal and binary units (5mb, 500kb, 2.5 MiB, bare bytes), case-insensitive.

Flag(s) Value Means
--to · --under · --goal size Whole-file byte target (single file). On a folder → usage error.
--to-each size Folder: cap every recognized file at this size (per-file).
--to-total size Folder: whole-tree budget — sweeps for the gentlest plan that fits (so drop --plan).
--plan cleanse | balanced | lowcarb | keto | crash Quality plan (default balanced).
--format keep | auto | jpeg | png | webp | avif Image output format (default keep; ignored for PDFs).
--fast Encode once at the plan's nominal quality; skip the size search. Cannot combine with a byte target.
--max-input size Skip / reject any input larger than this — a stat-based memory guard, before the file is read.
--timeout ms Abort a run longer than this (one deadline for the whole run); exits 2.
--concurrency · --jobs n | auto Folder parallelism (0/automin(cores−1, 8)).
--out dir Output directory (default: the sibling .diet path).
--force · --allow-signed Proceed on a signed PDF and drop the quality floor (floor = 0).
--include · --exclude globs Folder: only / skip files matching comma-separated globs (exclude wins).
--json Emit a stable JSON object on stdout (logs stay on stderr).

For the everyday flags in prose see the CLI guide; for the exhaustive table with defaults, the API reference.

The five diet plans

Pick how hard to cut — quality is a contract, not a guess.

--plan picks intent, not raw quality knobs. Every plan holds a measured perceptual floor (SSIM), so lowcarb stays visually lossless and only crash accepts visible loss. If a target can't be met above the floor, diet refuses honestly rather than returning garbage.

Honest caveat: cleanse is a no-op for PDFs and images today — lossless re-optimization isn't wired yet, so it keeps the original (only SVG cleanse does real, lossless work). The aggressive plans use only the in-house permissive sharp/mozjpeg path; copyleft engines are never bundled.

One engine, four surfaces

The same measured engine, wherever you need it.

onadiet is one honest engine behind four front doors — the CLI you type, an importable library for your hot path, a CI byte-budget gate, and (next) an agent Skill. Same rules everywhere: local, measured, keeps your original.

1 · CLI

One command.

Point diet at a file and it writes a sibling .diet file — your original untouched, never a bigger output, written atomically.

diet report.pdf --to 5mb
2 · Library

An engine for your code.

Import an adapter and slim bytes in / bytes out — no cross-call state, safe per request. The flagship onadiet also re-exports the whole @onadiet/core API, so no separate install is needed.

slim.ts
import { imageAdapter } from '@onadiet/image'

// no cross-call state — safe to call per request
const { output, outcome } = await imageAdapter.slim(bytes, { plan: 'balanced' })

output               // a smaller file (Uint8Array) — measured
  ? save(output)
  : keep(outcome)    // honest: kept original / infeasible

// helpers come straight off the flagship, too:
import { resolvePlan, parseSize } from 'onadiet'
3 · CI gate

Fail the build on bloat.

diet check weighs a path against a budget and writes nothing — exit 0 under budget, 1 over. Add --json so a step reads exactly what's over.

diet check ./assets --max-total 10mb
4 · Agent Skillplanned

The same binary, for agents.

A Claude Code Skill wrapping the CLI — no separate server. It's the next milestone (shipping with distribution: publish · Homebrew · Skill), not shipped yet. The intended loop, all local and deterministic:

agent-loop (planned)
# 1 · see the problem (size + causes)
diet weigh big-deck.pdf --json
# 2 · fix it
diet big-deck.pdf --to 10mb --json
# 3 · read the receipt → real saving, or honest "infeasible"
Exit & error codes

Honest codes so scripts and CI can branch.

The bin sets process.exitCode (it never calls process.exit()), so piped output is never truncated. In code, every engine failure is an OnadietError carrying a typed code — branch on the code, never on message strings.

Exit Meaning
0 Success — slim / weigh / plan OK, check PASS, folder budget fit.
1 Target or gate failed — infeasible target, check FAIL, folder --to-total overran.
2 Processing error — encrypted/unsupported input, aborted --timeout, read/write failure, --max-input exceeded.
3 Invalid usage — bad flags, unknown plan, invalid size, folder given a bare --to, check with no budget.
4 Unsafe operation blocked — signed PDF without --force, would overwrite the original, --out a symlink or inside the input tree.
Error code When
INVALID_SIZE Unparseable size / unknown unit; invalid byte count; bad floor/targetBytes.
UNKNOWN_PLAN Plan name not one of the five diet plans.
UNSUPPORTED_INPUT Not a supported image/PDF/SVG; undecodable bytes; animated/multi-frame image; no candidate format could encode.
SIGNED_PDF PDF is signed and allowSigned isn't set (use --force).
ENCRYPTED_PDF PDF is encrypted / password-protected.
TARGET_INFEASIBLE Can't reach the target above the floor (loosen the floor / use keto/crash) or it's structurally impossible.
ABORTED The signal aborted (cancellation / timeout) mid-slim — nothing written.
NOT_IMPLEMENTED Reserved — declared and handled, but not thrown by any current path.

Weigh in.

On npm now — built and proven against real files. Install with npm i -g onadiet.

npx onadiet report.pdf --to 5mb