Prefix cartoon onto any command and its output becomes compact, structured, agent-ready — same exit codes, same behavior, a fraction of the input tokens. The full raw log is always archived, so compression is reversible by construction.
cartoon exists to save your agent input tokens — so install it first. Pick your harness; each option makes noisy commands wrap automatically.
Skill + auto-wrap hook in one install. Claude wraps noisy commands without being asked.
/plugin marketplace add abhijitbansal/cartoon /plugin install cartoon@cartoon
skills.sh auto-detects the agents you have and installs the cartoon skill into each.
npx skills add abhijitbansal/cartoon
No plugin? Install the Claude Code hook directly, or paste the snippet from docs/agents.md into your AGENTS.md.
cartoon hook install # auto-wrap in ~/.claude/settings.json cartoon hook status # check · uninstall to remove
Pasting rules into copilot-instructions.md or AGENTS.md only asks the model to prefix cartoon — and it sometimes forgets. A PreToolUse hook intercepts the command and wraps it every time. One cartoon hook rewrite serves every agent; install it where each one looks.
Writes a Copilot hook that rewrites the command in place — same as Claude Code.
cartoon hook install --copilot # repo-shared (also the coding agent): cartoon hook install --copilot --project
Chat exposes no rewrite field, so the hook blocks the raw command and tells the agent to re-run it wrapped. It reads the same Claude-format file, so one entry covers both.
cartoon hook install --vscode # → ~/.claude/settings.json # (also covers Claude Code)
Functions shadow the bare tool names and re-invoke through cartoon — they beat PATH and venv binaries.
cartoon shim install export BASH_ENV=~/.config/cartoon/shims.sh
cartoon hook install --copilot --deny — it blocks-and-suggests instead of rewriting, no modal.cartoon hook rewrite --deny-mode forces deny anywhere; handy on Copilot builds before updatedInput landed.cartoon hook status shows every surface at once; cartoon shim status shows the shim file..github/hooks/cartoon.json so the whole team (and the Copilot coding agent) gets auto-wrap for free.CARTOON_NO_WRAP=1 (hook) or CARTOON_NO_SHIM=1 (shims) in the agent's environment — any non-empty value turns it off for the session. cartoon --raw <cmd> runs one command untouched..github/hooks format), so install it with --vscode.pytest | grep x. Keep them to tools the agent runs bare; disable per-shell with CARTOON_NO_SHIM=1../gradlew, ./node_modules/.bin/jest) bypass shell functions entirely. Tools with non-identifier names (pre-commit) and python -m/xcodebuild aren't shimmed — the hook covers them.~/.copilot/hooks or .github/hooks, not a plugin dir — plugin-defined Copilot hooks currently don't fire.cartoon --raw for live streaming.Agents read CLI output formatted for humans: banners, progress noise, hundreds of pass lines — and you pay for every token, on every subsequent turn of the conversation. cartoon keeps what the agent needs and drops the rest.
==================== test session starts ==================== platform darwin -- Python 3.12.4, pytest-9.0.3 collected 48 items tests/test_auth.py::test_login PASSED [ 2%] tests/test_auth.py::test_logout PASSED [ 4%] …44 more lines of PASSED… tests/test_auth.py::test_expiry FAILED [ 87%] …full session banner, warnings, summary…
runner: pytest summary: total: 48 passed: 45 failed: 2 skipped: 1 duration_s: 3.2 failures[2]{id,loc,msg}: "tests/test_auth.py::test_expiry","tests/test_auth.py:42",assert exp < now "tests/test_user.py::test_create","tests/test_user.py:88","KeyError: 'email'" traces: "tests/test_auth.py::test_expiry"[2]: "tests/test_auth.py:42 in test_expiry",assert token.exp < now() raw_log: ~/.local/state/cartoon/runs/20260611-051415-342d
pytest, unittest, jest, vitest, ruff, eslint, tsc — plus first-class Apple support: swift test, swift build, xcodebuild test, xcodebuild build. cartoon injects each tool's machine-readable flag (junit-xml, --json, --xunit-output, -resultBundlePath) and renders a compact report. Clean lint/typecheck/build runs collapse to a summary line. Runs through uv are first-class too — uv run pytest (uv auto-picks the project .venv), uv run -m pytest, and uvx ruff check are detected and auto-wrapped just like the bare tools.
No adapter? The safe tier still fires: ANSI stripping, progress-bar collapse, duplicate and blank collapse — deterministic, non-lossy in practice. Opt into --compress=aggressive for log-level filtering, near-duplicate templating, compiler-diagnostic tables, and error-anchored windowing.
Every run stores complete stdout/stderr on disk and prints a raw_log: pointer. Need something the summary dropped? cartoon logs grep ERROR --last fetches just the matching lines — never re-run, never re-read 100k tokens.
cartoon ingestAlready have the log? cartoon ingest build.log — or pipe it: some-cmd | cartoon -. Existing files go through the exact same flow as a wrapped command: detection, ladder, guard, archive, stats.
cartoon learnMines your own usage ledger — all local, no telemetry. Commands wasting tokens uncompressed get ready-to-paste config pins; repeated identical failures get a "read the archive instead of re-running" nudge.
The plugin ships a PreToolUse hook that rewrites noisy dev commands (test, lint, typecheck, build) to run under cartoon automatically. Conservative allowlist, fail-open on anything unrecognized — savings stop depending on the model remembering a skill.
cartoon statsA local ledger records tokens in, tokens out, and savings per call, per adapter. cartoon stats --since 7d tells you exactly what cartoon earned — self-measuring, not self-reported.
--fast modeOpt-in acceleration: pytest gains -n auto via pytest-xdist, disclosed right in the report. Strictly off by default — parallel execution is not "same behavior", so cartoon never enables it on its own.
Every wrapped command and ingested log flows through the same stages. Each compression rule is a pure function that no-ops when its pattern is absent — plain prose is never mangled.
Filter tools must be trusted blindly — dropped lines are gone. cartoon is built so you never have to trust it.
Every run archives full raw stdout/stderr with a one-command escape hatch. Compression you can always undo.
The net-savings guard: output gets smaller or passes through byte-identical. Trying cartoon is zero-risk.
Exit codes mirrored exactly — cartoon pytest && deploy behaves like pytest && deploy. Parse failures pass the original through.
TOON re-encoding keeps every test and diagnostic as queryable data, and the stats ledger makes savings self-measuring.
Eleven runners get native, machine-readable handling. Everything else still rides the compression ladder. The four Apple-platform adapters are new.
| Adapter | Trigger | Source |
|---|---|---|
pytest | pytest, python -m pytest, uv run [-m] pytest, uvx pytest | injected junit-xml |
unittest | python -m unittest, uv run [python] -m unittest | stderr text parse |
jest | jest, npx jest | injected --json |
vitest | vitest run (watch passes through) | injected --reporter=json |
swift-testNew · Apple | swift test | injected --xunit-output + --parallel; merges XCTest + Swift Testing xml |
xcodebuild-testNew · Apple | xcodebuild test | injected -resultBundlePath, parsed via xcrun xcresulttool get test-results summary --format json (Xcode 16+) |
ruff | ruff check | injected --output-format json |
eslint | eslint, npx eslint | injected --format json |
tsc | tsc, npx tsc (not --watch) | injected --pretty false |
swift-buildNew · Apple | swift build | stdout/stderr diagnostics parse |
xcodebuild-buildNew · Apple | xcodebuild build (no test action) | stdout/stderr diagnostics parse |
"Your agent reads 12 lines instead of 800 — and the receipt is always on disk."