Token Goat is a developer tool that reduces AI token consumption by 40–90% through intelligent file interception, image compression, and output filtering. It works invisibly with Claude Code, Gemini CLI, and other AI coding assistants, while also defending against prompt injection attacks.
Token burn reducer and focus keeper for Claude Code, Codex, Copilot, Gemini CLI, and more: surgical read hints, PDF/Office/CSV/markdown file interception, 160+ filter & interception rules, compact manifest injection, image shrinking, cache and compact skills, cache MCP calls, prompt injection pro...
README
title: "AI Token Optimizer — Cuts Costs, Sharpens Focus, Blocks Prompt Injection" description: "Cuts AI tool costs 40–80% and guards against prompt injection. Stops re-reads, extracts one function vs. whole file, shrinks screenshots 97%." image: /token-goat/assets/goat-social.png permalink: /
Token-Goat

85% smaller reads · 97.4% image compression · 180+ filter & interception rules · 94–99% skill overhead cut · compaction memory · prompt injection guard · 3.7 GB never reached the model · 1.1 Gt tokens saved
Reduces AI token use/costs by 40–90%, and improves its focus. Fully automated, always online.
Also defends against prompt injection. Every fetched page is scanned for attack patterns and wrapped in an untrusted-content fence before hitting the model. One config line to disable.
Your AI re-reads the same file three times. Every compaction causes amnesia. Every build log buries the one line that matters. You pay for all of it. Token-Goat fixes all of it — automatically.
Token-Goat sits silently between your AI and your tools. Re-read a file? It gets a one-line hint and a narrow-slice suggestion instead of the full file again. Grab a screenshot? A 100 KB copy reaches the model instead of 10 MB. Run pytest, npm install, docker build, or cargo? The thousands of progress bars and passing-test names are stripped to the failures before the output even reaches the context window. Open a PDF, a large Markdown doc, or a CSV? The hook intercepts it — heading tree, page count, or column preview — so the model never pays for the full file. Run gh run watch or next dev a second time? Prior output is recalled rather than re-run. Compact a long session? It gets a clean structured manifest of edited files and key symbols so nothing important is forgotten. Sessions drop 40–90%+ in cost. You change nothing about how you work.
Works with Claude Code, Gemini CLI, Qwen Code, Codex CLI, Aider, Cursor, Cline, Windsurf, Copilot CLI, Grok CLI (xAI Grok Build), and OpenCode, plus pi (pi-coding-agent).
Ask your AI to install it fully (give it this GitHub link), or install in one command:
npm install -g token-goat && token-goat install
Restart your AI sessions. Run token-goat stats a couple of minutes after your next session to see the massive savings. It also doubles as a great tracker of your work. Welcome to token efficiency.
Built and continually improved, free, by one person. If it saves you tokens, drop a ⭐️ at the top of this page. One click. Makes my day. Also, if you'd like anything added, drop me a line.
Install · CLI · What gets installed? · Stats · Security & uninstall
The problem
AIs read auth.py. Then reads it again. And again. Then a third time after compaction wipes the session. Then it can't find what it wanted and searches other lines and files. You pay for every token and most of it is waste.
Long sessions accumulate waste five ways. Screenshots cross the model at full resolution. A single PNG can land at 10+ MB. The agent re-reads files it already parsed earlier in the same conversation. When a session compacts, the summary LLM doesn't know which files were edited or which symbols mattered, so it preserves the wrong things. And every pytest, npm install, docker build, or git log dumps thousands of lines of progress bars, deprecation warnings, and passing-test names that bury the one line that actually matters.
The fifth waste is skills. A single large skill injects 10k–65k tokens every time. Run a five-iteration /improve loop and you've paid for five full copies of the same rules. Token-Goat now blocks repeat skill loads before they happen: a PreToolUse hook intercepts the second invocation, serves the cached compact (~400 tokens) instead, and only allows a reload when compaction may have evicted the skill from context. It also intercepts direct reads of skill files and ensures the compaction manifest carries the full skill index — so nothing is forgotten and the full body never re-enters context unnecessarily.
The fastest way to reduce AI token costs is fixing these five, not writing shorter prompts. Each one is preventable. Token-Goat intercepts all five, automatically.
What changes
| Without Token-Goat | With Token-Goat |
|---|---|
| 3.3 MB screenshot lands in model context | 84 KB compressed copy, 97.4% smaller |
| Agent re-reads files from earlier in the session | "Already read this" reminder with narrow slice suggestion |
| Agent re-reads a file edited mid-session | Unified diff injected as a hint — full Read avoided when the diff covers the change. Docs (.md/.rst/.txt) by default; source/style/data files (.ts/.css/.json/…) when serve_diff_on_reread is enabled |
| Compaction forgets which files were edited | Structured session manifest injected before compact |
Same files re-read from scratch after /compact |
Recovery hint at SessionStart lists cached snapshot + bash + WebFetch IDs |
| Loaded skill body summarised away by compaction | ### Active Skills manifest section + **Skills**: recovery block list every loaded skill; full body recoverable via token-goat skill-body <name> without re-invoking |
| Large skill bodies re-injected each turn (6 active skills = 65k+ tokens) | <!-- COMPACT_END --> marker: everything above the marker is the compact form; token-goat detects it on load, caches the compact slice, and injects only that — typically ~400 tokens vs. 10k+ |
| Model reads a skill SKILL.md file directly mid-session (burning the full 10k–65k tokens again) | Pre-Read hook intercepts */.claude/skills/<name>/SKILL.md paths; if the skill is already cached this session it emits a token-goat skill-body <name> hint instead |
| Same large skill invoked twice in a session | PreToolUse hook blocks the reload; serves cached compact (~400 tokens) via additionalContext instead of the full 40–65k body. Allows the reload if compaction fired since the last load |
Skill invoked with first_load_compact=true and <!-- COMPACT_END --> present |
First load also blocked; only the curated compact section is served. Full body available via token-goat skill-body <name> on demand |
| Same docs URL fetched twice in a session | Re-fetch blocked at warm+ context pressure; cached body available via token-goat web-output <id> |
cat src/auth.py or Get-Content module.py run via Bash |
Pre-Bash hook detects whole-file reads of indexed source files and suggests token-goat read "file::Symbol", skeleton, or section — covers cat, bat, type, PowerShell Get-Content/gc |
rg pattern src/ or grep -rn run via Bash (first time) |
Pre-Bash hook suggests token-goat symbol <name> and token-goat semantic "<query>" as indexed alternatives to a full directory walk |
rg "^def" src/file.py or grep "class " module.ts — structural search on a single source file |
Pre-Bash hook redirects to token-goat skeleton "file" or outline "file" — all symbols with line numbers, no full-file read |
rg or grep run twice with the same pattern |
Pre-Bash dedup hint fires on repeated rg/grep/ag calls the same way it fires on the native Grep tool; repeat searches return a cached match-count hint instead of re-running |
Read tool targets tool-results/<id>.txt or tasks/<id>.output |
Pre-Read hook suggests token-goat bash-output <id> --tail N / --grep PATTERN / --section H; the filename stem is the output ID |
Repeated monitoring command run again (gh run watch, next dev, vitest, docker logs) |
Pre-bash recall hint: when a prior run is cached and its output exceeds 2 KB, a pointer to token-goat bash-output <id> --grep PATTERN is injected instead of re-running the command. Cache is keyed on the base command, so re-running with a different trailing pipe (e.g., ` |
pnpm/yarn/bun install or build dumps full output |
pnpm, yarn, and bun compress filters now strip install noise and build logs the same way npm does; pnpm run/yarn run route through their own filter |
| Surgical-read command returns a 10k-line symbol or a full section dump | Capped at ~25k tokens; marker names the truncation ratio and narrowing command (symbol → file::Class.method; section → sub-heading; cached → --grep/--tail) |
| Full file read for one function or section | token-goat read file::symbol, about 85% smaller |
pytest dumps 150 PASSED lines + dots + tracebacks |
Failures-first view, 80 to 97% smaller |
npm install floods deprecation warnings + spinner |
Errors kept; warnings collapsed by package, ~90% smaller |
docker build emits sha256 digests + transfer progress |
Step headers + errors kept; noise dropped, ~75% smaller |
ruff / eslint / mypy repeat the same rule 50 times |
Grouped by rule with first 3 examples, ~80% smaller |
Same pytest / cargo / git log re-run mid-session |
Small prior outputs (≤8 KB) served inline on first repeat; larger outputs get a hint pointing at token-goat bash-output <id> |
Same Grep pattern re-run with hundreds of matches |
Pre-Grep dedup hint quotes the prior match count |
| Same docs URL fetched twice | Re-fetch denied at warm+ context pressure (redirects to token-goat web-output <id>); advisory hint at cool |
token-goat section pyproject.toml::tool.ruff |
One TOML table extracted instead of the whole config; same for .yaml/.yml/.json/.ini/.cfg/.env/Dockerfile |
Typoed token-goat symbol getUserr |
symbol matches on exact name; a miss returns No matches for 'getUserr' (no fuzzy/auto-redirect) — use token-goat find for a fuzzy + semantic lookup instead |
grep/rg returns 50+ match lines |
File-level summary: top 20 files by match count; full result cached, ~80% smaller |
| Same "already read" hint fires on every re-read | Suppressed after first injection; SHA-256 fingerprinting prevents the same nag twice per session |
| Same bash command runs 3+ times in one session | Escalating warning: "ran 2×" on repeat, "WARNING: ran N×" by the third; output always cached |
| Agent starts cold with no git context in a dirty repo | Branch, change counts, and 5 recent commits injected at startup (~50 tokens) |
| Re-read hint shows only the line range | Hint includes previously-accessed symbol names: [symbols: login, refresh, …] |
| Manifest too large or unstructured after compaction | Manifest gains ### MUST_PRESERVE sealed block, ### What Worked (last 2 green test runs), inline git diffs, and ### TODOs from TaskList |
| CSV/JSON/JSONL/log file re-read when only structure changed | Pre-Read hint for structured files (CSV headers, JSON keys, log format), ~70% smaller than full read |
| Index-only files (lockfiles, source maps, bundles) read on every session | Pre-Read suppression for read-only files (package-lock.json, *.map, dist/), skipped unless explicitly edited |
| Large markdown file read in full (README.md, CHANGELOG.md, CLAUDE.md ≥8 KB) | Heading tree intercepted instead — H1–H3 with #2/#3 disambiguation; token-goat section shortcuts listed for well-known files; post-edit injects a re-read suggestion rather than the full file |
| PDF opened via Read | Full read denied; PDF shows page count and outline (token-goat pdf-extract pulls the actual text, optionally paged/sliced, when the outline isn't enough) |
| Excel/PowerPoint/Word file (.xlsx/.pptx/.docx) opened via Read | Full read denied; redirects to the matching narrow-slice command family (xlsx-sheets/xlsx-head/xlsx-range/xlsx-query, pptx-outline/pptx-slide/pptx-notes/pptx-text, docx-outline/docx-text) instead of extracting the whole document as text |
| Other Office binary (.odt, .ods, .ott, .odp) opened via Read | Full read denied; redirects to pandoc for text extraction (no dedicated reader for these formats yet) |
| Large CSV or TSV file (≥10 KB) read in full | Column headers, row count, and 3 sample rows shown; token-goat csv-query projects columns and/or filters rows instead of a full read; duckdb query suggestion for very large tabular data |
| WebFetch returns a page's full raw HTML | HTML-to-text extraction strips markup/scripts/styles before the model ever sees it — readable prose instead of a wall of tags |
| Large WebVTT/SRT transcript (≥10 KB) read in full | Duration, cue count, and detected speakers shown; token-goat transcript-outline gives a skimmable speaker/time overview and token-goat transcript slices by speaker/time range/pattern instead of a full read |
| Large TXT or log file (≥20 KB) read in full | Line count + first/last 5 lines shown; .log/.out files bias toward --tail 100 --grep; general catch-all for any file ≥100 KB |
| Subagent reads a 47–86 KB recon dump (or greps a 73 KB transcript) and overflows its window | pre_read denies a full Read at or above large_read_redirect_bytes (512 KB base, tightened by context pressure to as low as ~92 KB once the session is nearly full — the case that matters most for an already-strained subagent), and a content-mode Grep over one oversized file, redirecting both to surgical reads or a windowed offset/limit |
| Subagent overflows at "hello" with no idea why | token-goat baseline (--subagent for the terser variant a fresh subagent gets) prints a project map — file count, languages, top symbols, recent files — as quick orientation instead of an ls -R/full-repo read |
| MCP screenshot call lands 10 MB image in context because no file path was passed | pre_screenshot denies chrome-devtools and playwright screenshot calls without a filePath/file_path argument; redirects the model to re-issue with one, so the saved file flows through image-shrink (~39K tokens raw → ~8K compressed) |
claude-in-chrome's computer/browser_batch return a raw, full-resolution base64 screenshot in-band, with no destination-file option to redirect through image-shrink |
Inline screenshot blocks are shrunk via the same image-shrink pipeline in place, and a repeated Tab Context: listing (appended to nearly every call, often unchanged) collapses to a placeholder once seen unchanged this session |
| Agent tool spawns a subagent with no orientation and no reuse hints | A PreToolUse handler appends a compact briefing pack to the prompt: a one-line project-map summary, 2-3 recent cached-output IDs, and a surgical-read reminder (~300 tokens) |
| Subagent's own final report runs long and gets discarded once the parent moves on | Agent tool results ≥8000 characters get a recall pointer appended (token-goat recall); the original report always reaches the parent untouched |
| Large MCP tool result (≥2 KB) is a homogeneous array of objects, e.g. a list/search result | Deterministic structural compression: table-ified into one header row + tab-delimited rows, with columns constant across every row hoisted into a single constant: line instead of repeated per row; only applied when it saves ≥15%. Full original always recoverable via token-goat bash-output <id> (labeled [token-goat: compressed, full via mcp-output <id>]). Disable with TOKEN_GOAT_MCP_COMPRESS=0 |
GitHub MCP tool result (list_pull_requests, list_issues, search_code, get_file_contents, pull_request_read, …) carries dozens of boilerplate fields per object |
GitHub compression pack strips _links, node_id, gravatar_id, site_admin, and every *_url field (avatar_url, html_url, events_url, gists_url, followers_url, …) except download_url/git_url/clone_url/ssh_url, before handing the shrunk JSON to the same table-ifying pass — same TOKEN_GOAT_MCP_COMPRESS=0 opt-out and full-recovery-by-id guarantee |
Browser-automation MCP tool result (claude-in-chrome's read_console_messages/read_network_requests, chrome-devtools-mcp's list_console_messages/list_network_requests) carries verbose CDP plumbing per entry |
Browser compression pack strips console stackTrace frames and network requestHeaders/responseHeaders/timing/initiator/securityDetails/cookie fields, keeping url/method/status/resourceType/mimeType/reqid and the actual log text, before the same table-ifying pass runs — same opt-out and full-recovery-by-id guarantee |
curl -v dumps TLS handshake + all request/response headers |
Verbose lines stripped; request line, HTTP status, content-type, and body kept — typically 70–90% smaller |
jest --verbose / vitest --verbose emits one ✓ line per passing test |
Consecutive passing-test lines collapsed to a count per file; failures kept verbatim, ~95% smaller on passing suites |
go test -v emits --- PASS: TestName (Ns) for every passing test |
PASS lines collapsed to a count per package; FAIL lines and panic output kept, ~90% smaller on clean runs |
| Python script raises and dumps a 30-frame traceback | Intermediate frame pairs collapsed to a count; outermost frame, exception type, and message kept |
tsc --noEmit emits hundreds of type errors across many files |
Errors grouped by file, up to 3 examples per file shown, rest counted; ~70–90% smaller |
make/cmake/ninja emits hundreds of [N%] Building … progress lines |
Progress lines collapsed to a count; warnings, errors, and Built target lines kept, ~85% smaller on clean builds |
| Command writes JUnit XML and prints the path | XML parsed directly; compact summary (totals + failed test names/messages) injected — raw XML never enters context |
grep/rg matches a line in a .min.js or .min.css file |
Matching line truncated to 200 chars; filename and line number preserved |
| Claude Code writes async-task output to a temp file | pre_read intercepts the path and redirects to token-goat bash-output <id> with --head/--tail/--grep support |
| Re-read hints fire immediately after conversation compaction | Grace period suppresses deny hints for the first few reads after a compact so the model can re-orient |
| Large reference doc (CLAUDE.arch.md, API spec) re-read in full every new session | token-goat compact-doc <path> builds a deterministic extractive sidecar (headings + first N lines per section); pre_read serves it in place of the full file — 80–95% smaller. Sidecar is automatically marked stale when the source is edited. |
| Re-read denial fires as an advisory hint the model can ignore | When deny_reread is on (default), pre_read actively denies re-reads of files confirmed in the current context window, not just nudges; the advisory still fires for older reads that may have scrolled out |
| Unchanged files produce duplicate hints across sessions | Hint fingerprint includes file path; unchanged-file short-circuit skips re-read pre-check entirely |
| Bash dedup hints conflict with other compression | token-goat compress can be called as dedup-vs-hint filter; one-call access to cached output |
| Large manifest sections with no useful signal | Drop empty sections, strip project name from paths (cleaner relative paths in manifest) |
| Manifest git-history section loses signal on clean main | Inline git diffs + skip git log when on clean main branch; session-awareness improves manifest hygiene |
| Skill body lost after compaction but recovery too verbose | Recovery hint deduped skills by content_sha (same skill loaded twice = one entry); inline skill checklist |
| Recovery hints omit critical paths when space is tight | Skip bash snippet when recall available |
| AVIF format not supported despite better compression | AVIF image-shrink via sharp (when libvips is built with libaom); WebP fallback; codec auto-detection in docker |
Token-savings invisible until you run stats |
Token-savings benchmark (slow-marked test suite) locks in measured wins; token-goat stats reports net-positive impact |
| Hook crash leaves agent waiting for response | Fail-soft barrier catches BaseException/MemoryError/SystemExit; hook always returns {"continue": true} |
| Concurrent edits lose update counts mid-session | Session CAS + mtime-based retry prevent lost edits in manifest |
| Dirty queue appends corrupt on concurrent writes | OS file lock (fcntl/msvcrt) prevents torn JSON lines |
| Worker claim file blocks all re-spawns on crash | Mtime staleness check (>60s) auto-recovers zombie claim files |
| DRY consolidation — 600+ lines duplicated | Tool-response extractor unified; cache helpers (_safe_join, OutputStatDict) consolidated; dedup-hint template collapsed; CLI output/history commands unified; humanize_bytes centralized in render/ansi |
| Compaction hook subprocess ~190 ms cold | Lazy imports of heavy modules in hooks_session and compact; compaction path ~110 ms cold (~42% faster) |
| Pre-compact subprocess runs on every session | Compact-skip sentinel on disk: if session file is <5 min old and no edits logged, subprocess exits in <1 ms |
| Git ops slow manifest build in non-repo dirs | git diff / git log calls skipped when cwd is not inside a git repo (saves 60–100 ms per hook fire) |
terraform init downloads 30+ provider plugins |
Provider install lines collapsed to a count note; generic progress lines head/tail compressed (5+5 kept); Init complete! preserved |
terraform show dumps a full resource block |
Noise attributes (id, arn, timeouts, tags) stripped per resource block; high-signal fields kept with a suppression note |
kubectl events lists raw repetitive events |
Events grouped by REASON with a per-group count; field-selector hint added to narrow scope |
kubectl describe floods labels and annotations |
Labels/annotations blocks collapsed to line counts; Conditions table kept in full; container resource fields preserved |
npm install verbose output with sill/http/verb/spinner lines |
Verbose timing, sill, http, verb lines suppressed; warn lines beyond first 3 collapsed; braille spinner reify lines dropped |
| Fetched web content lands raw in model context | Scanned for attack patterns, wrapped in an untrusted-content fence; matched pattern name written to the log |
| Chatty log repeats the same error or event thousands of times | token-goat logfold collapses consecutive duplicates to [Nx] counts; same event logged with different timestamps or request IDs folds correctly — ~90–95% smaller on repetitive logs |
| Reading poetry.lock or package-lock.json to find a pinned version | token-goat lockdeps returns a name/version table of direct dependencies; optional packages and transitive entries excluded |
On a per-token API plan, 100K wasted tokens per session runs about $0.30. Five sessions a week is ~$450/year. AI coding cost reduction at that scale comes from fixing the waste, not from using the product less. Token-goat is free. And on subscription plans, it can result in limits feeling 10x higher.
Token savings, measured
Numbers below come from synthetic-fixture benchmarks in the test suite. Each row points at the source file where the measurement is reproduced.
| Source | Improvement | Measured impact | Where |
|---|---|---|---|
| Image shrink | WebP encoder beats JPEG on screenshot-shaped images | ~39% smaller than the same image at JPEG quality 85 | src/image_shrink.ts (codec selection) |
| Repomap output | --compact trims the top-symbols list to 10 (vs 30) and drops the recent-files section and per-symbol locations |
Denser overview for the same byte budget | src/baseline.ts (buildProjectMap, token-goat map --compact) |
| DB reindex | Batched single transaction + composite indexes on (file_id, kind) |
100 files / 10K rows: 84 s → 1 s (~80× faster) | src/parser.ts, src/db.ts (index migration) |
| Hook cold-start | Lazy import of heavy modules; unknown events short-circuit | 86 ms → 30 ms (~65% faster); unknown-event dispatch <1 ms | src/hooks_cli.ts |
| Symbol start_line | TypeScript decorators captured in symbol span | One token-goat read returns the decorator + signature + body; no re-read |
src/parser.ts (TypeScript adapter) |
| Section extraction | Setext headings, h5/h6, anchor IDs, and __frontmatter__ |
token-goat section resolves more headings without falling back to a full file read |
src/parser.ts (Markdown adapter) |
| Image cache | Real LRU eviction (was FIFO; old hot entries got dropped) | Higher hit rate on repeat screenshots in long sessions | src/image_shrink.ts |
| Monorepo defaults | Reindex batch 500 → 2000; compact min_events 5 → 3 |
Fewer worker wakeups; compact manifests fire on shorter sessions | src/config.ts defaults |
| Miss suggestions | read / section print "Did you mean…?" on a miss; section also auto-redirects on an unambiguous heading-prefix match |
Keeps agents on the surgical-read path instead of falling back to full-file Read |
src/read_commands.ts |
Token-savings examples
Concrete before/after for the four interception points. Token counts use the ~4-chars-per-token rule of thumb.
1. Image — screenshot interception
$ ls -lh screenshot.png
-rw-r--r-- 1 user user 1.2M screenshot.png
# Without token-goat: Claude reads the 1.2 MB PNG.
# With token-goat: hook re-encodes as WebP and substitutes the cached copy.
$ token-goat image-shrink screenshot.png
out: ~74 KB WebP (94% smaller)
The same image at JPEG quality 85 lands around 120 KB. WebP wins by another ~39% on screenshot-shaped content (large flat regions, sharp text edges).
2. Surgical read — one function, not the whole file
# Without token-goat: full file read.
$ wc -l src/auth.py
512 src/auth.py # ~12,000 tokens
# With token-goat: pull just the function.
$ token-goat read "src/auth.py::login"
out: 38 lines # ~300 tokens (97% smaller)
Same applies to token-goat section "README.md::Install" — one heading instead of the whole document. Anchor IDs and setext headings resolve too, so section "doc.md::Quick-start" works when the file uses Quick start as an <h2> with an explicit {#quick-start} anchor.
3. Compact manifest — preserve what mattered
# Without token-goat: PreCompact fires with no extra context.
# The summarizer LLM picks what to keep, often loses the edit set.
# With token-goat: PreCompact hook injects a structured manifest.
$ token-goat compact-hint --session-id <id>
out: ~280 tokens covering 8 edited files + 12 symbols accessed + 4 key reads
The 280-token manifest is one-shot during compaction. The win is downstream: post-compaction, the agent doesn't re-read files it had already edited, saving a full-file Read pass on each one.
4. Repomap — orientation without an ls -R dump
# Without token-goat: recursive ls + a handful of Read calls to figure out the repo.
$ ls -R . | wc -c
51234 # ~50 KB of raw paths, no signal about importance
# With token-goat: a ranked orientation summary instead of raw paths.
$ token-goat map --compact
out: ~1 KB # top-ranked classes/functions, no locations (98% smaller)
token-goat map ranks headline symbols by kind (classes/interfaces first) and body size. --compact trims that list to the top 10 symbols (name + kind only, no file/line) and drops the recent-files section, for a denser orientation than the full form.
5. Bash output compression
# Without token-goat: pytest dumps every PASSED line + dots + tracebacks.
$ pytest -v tests/
... (3 KB of output, 150 PASSED lines, 1 FAILED at the bottom)
# With token-goat: the PreToolUse hook rewrites the command to
# `token-goat compress --filter pytest`. The wrapper runs pytest, captures
# stdout+stderr, applies the per-tool filter, and prints failures first.
$ token-goat compress --filter pytest --cmd "pytest -v tests/"
= test session starts =
collected 150 items
FAILED tests/test_x.py::test_one
= 1 failed, 149 passed in 2.3s =
[token-goat: collapsed 149 PASSED lines]
[token-goat: pytest filter compressed 4.8 KiB to 0.1 KiB (97% saved)]
Built-in output compression covers 130+ dev tool CLIs: pytest, jest / vitest, cargo, npm / pnpm / yarn / bun, docker, kubectl / helm, aws, ruff / eslint / mypy / pylint / oxlint, git, make / gradle / mvn / ant / bazel, go test / golangci-lint, terraform / pulumi / cdk, pip / uv / conda, python, gh, ansible, pre-commit, grep, eza / ls, fd, bat, jq, yq, curl / wget, rsync, dotnet, cmake / ctest, swift / xcodebuild, ruby / bundler, elixir / mix, php / composer, flutter / dart, rust / cargo, kotlin / ktlint, zig, crystal, haskell / cabal, nix, R, c++ (conan / vcpkg / cppcheck / clang-tidy), wrangler / hardhat / serverless, erlang, fly.io, forge, elm, julia, tox, vault, packer, nx / lerna / turbo, prettier / biome, sass, wasm-pack, deno, and AI tool CLIs: aider, gemini, claude, gh copilot, copilot, cursor, windsurf (incl. Cascade), opencode, continue, cline. Each filter strips ANSI escapes, collapses \r progress bars, dedupes repeated lines, groups linter issues by rule, keeps every error block verbatim, and caps total output at 1000 lines / 64 KiB. Compound commands (cmd1 && cmd2) are wrapped per segment, so git diff && git log compresses both halves. Disable globally with TOKEN_GOAT_BASH_COMPRESS=0, per-filter via [bash_compress] disabled_filters = ["docker"] in config.toml, or preview the output of any command with token-goat compress --cmd '<your command>'. To exclude project-specific directories from indexing (temporary venvs, build sandboxes), add [indexing] skip_dirs = ["my-tmpdir"] to config.toml.
gh api responses get an extra pass: boilerplate *_url fields (followers_url, gists_url, starred_url, and around a dozen others) are stripped from JSON objects; html_url, avatar_url, clone_url, and ssh_url are kept. User and repo objects typically shrink 60–80%. When token-goat sees a GitHub permission error in the output or a non-zero exit on a security endpoint, it injects a system message suggesting gh auth refresh -s security_events.
6. Context pressure
Token-goat tracks how close a session is to the autocompact trigger and tightens its hints as the window fills. Surgical-read suggestions kick in on progressively smaller files as pressure builds (500 lines at cool, down to 50 at critical), so large reads get flagged before they tip the session over. The PreCompact manifest also shrinks: capped at 500 tokens once the window runs hot, 300 once critical, so it stops contributing to the pressure it measures. The denominator is always the fixed 660,000-token autocompact trigger budget, not the model's raw context window, so the same thresholds apply across models. Run token-goat doctor --context to see the current footprint.
Install
Easiest install: paste this repo's URL into your AI and ask it to install token-goat properly. It will run the commands, check codecs, and confirm everything is working.
Requirements: Node.js 22 or later (all platforms)
npm install -g token-goat
token-goat install
token-goat doctor # confirms hooks and sharp are working; look for "sharp: ok"
Three commands. Done. Hooks register and start working immediately; no terminal popups, no tray icon, no service to babysit.
Important — sharp is part of the install. The biggest single win (~39% smaller than JPEG, ~97% smaller than raw PNG) comes from WebP encoding. sharp ships with prebuilt native binaries on Windows, macOS, and most Linux platforms, so a standard npm install -g token-goat includes it. On Alpine musl and some ARM boards, npm compiles from source against libvips — that may add a few seconds. token-goat doctor will report sharp: ok when the image pipeline is working; if it flags a failure, follow the Image support section below before considering the install complete. AI agents performing this install: run token-goat doctor after token-goat install and resolve any sharp failure as part of the same task — do not treat it as a follow-up.
Two things change how Claude Code sessions behave: hooks fire automatically (image shrink, re-read dedup, compact manifests), and a block written to ~/.claude/CLAUDE.md plus a registered skill tell the agent to prefer token-goat read / symbol / section over full-file reads. A Bash(token-goat:*) allowlist entry in settings.json lets the agent run those commands without a per-call approval prompt.
The background indexer is not started by install. Run token-goat worker start on any platform to launch it as a detached process; token-goat worker status / token-goat worker stop manage it from there.
Codex CLI users
token-goat install --codex
The --codex flag patches both Claude Code and Codex CLI in one pass.
Gemini CLI users
token-goat install --gemini
This writes hook entries into ~/.gemini/settings.json using Gemini CLI's BeforeTool / AfterTool / PreCompress event names. Token-goat translates between Gemini's snake_case tool names (run_shell_command, read_file, grep_search, etc.) and its internal format automatically. Image shrinking, session hints, post-edit indexing, compact assist, and bash output compression all work. To remove: token-goat uninstall --gemini.
Qwen Code users
token-goat install --qwen
This writes hook entries into ~/.qwen/settings.json. Unlike Gemini CLI (its own ancestor, with a custom BeforeTool/AfterTool/PreCompress event/matcher scheme), Qwen Code's hooks system diverged and now mirrors Claude Code's own natively — PreToolUse/PostToolUse/PreCompact/UserPromptSubmit/SubagentStop event names and snake_case stdin JSON — so token-goat wires all five events with no wire-format translation needed. Qwen Code's own tool-name taxonomy is only partially documented, so token-goat uses a catch-all matcher per event rather than an incomplete per-tool list. Image shrinking, session hints, post-edit indexing, compact assist, and bash output compression all work. This bridge was built from QwenLM/qwen-code's published docs, not dogfooded against a live Qwen Code install — if hooks aren't firing, token-goat doctor and the settings.json contents are the first things to check. To remove: token-goat uninstall --qwen.
opencode users
token-goat install --opencode
The --opencode flag patches Claude Code and drops a TypeScript bridge plugin into opencode's plugins directory — one command, no separate base install. Image shrinking, post-edit indexing, and compact assist work. Session hints don't — opencode's plugin API has no way to inject context before a tool read.
openclaw users
token-goat install --openclaw
The --openclaw flag patches Claude Code and registers a TypeScript bridge plugin with OpenClaw's gateway: it drops ~/.openclaw/plugins/token-goat.ts and adds it to ~/.openclaw/openclaw.json's plugins.load.paths / plugins.entries (existing config is merged, never overwritten). OpenClaw's plugin SDK does support before_tool_call/after_tool_call hooks with the block/rewrite shape token-goat needs; unlike the other bridges, no argument-key remapping is needed at all, since OpenClaw's tool-call params are already snake_case (file_path, command, etc.) — the same keys token-goat's own tool_input uses.
What works: bash output compression, re-read denial and surgical-read redirects for oversized first reads, image shrinking, and post-edit indexing (all via before_tool_call/after_tool_call). What doesn't: session hints — OpenClaw's tool-call hooks have no context-injection channel, only param rewriting — and the compaction manifest — OpenClaw's before_compaction/after_compaction are observation-only, with no return-value mechanism to inject a manifest into the next turn the way pi's compaction hooks do.
This bridge has not been validated against a live OpenClaw instance — it's built from OpenClaw's documented plugin SDK and hook event types, not dogfooded against a real running gateway. If tool calls aren't being intercepted, the built-in tool name list in openclaw.ts's TOOL_TO_TG map is the first thing to check. To remove: token-goat uninstall --openclaw.
pi users
token-goat install --pi
The --pi flag patches Claude Code and drops a TypeScript extension into pi's global extensions directory (~/.pi/agent/extensions/token-goat.ts). pi auto-discovers it on the next launch (approve the project-trust prompt the first time). The extension is a normal pi extension — a default-exported factory that subscribes to session_start, tool_call, tool_result, session_before_compact, and session_compact — and bridges those events into token-goat's token-goat hook <event> subprocess protocol.
What works: bash output compression (the bash command is rewritten in tool_call), re-read denial and surgical-read redirects for oversized first reads (both return { block, reason } from tool_call — a confirmed re-read, or a first read at/above the pressure-scaled large_read_redirect_bytes gate, pointing at token-goat skeleton/section/symbol instead), image shrinking (tool_call rewrites the read path in place to a materialized shrunk copy), post-edit indexing and output caching (tool_result), and the compaction manifest (captured at session_before_compact, re-injected after session_compact since pi's compaction replaces rather than appends). Skill-overhead preservation does not apply — pi has no Skill tool; skills are template expansions. To remove: token-goat uninstall --pi.
Project-local install (single project only). pi also loads extensions from a project's .pi/extensions/ directory (after the project is trusted). To install for one project without touching the global directory, drop the extension there:
npx token-goat install --pi --local
This writes .pi/extensions/token-goat.ts in the current project only. Remove it by deleting that file.
Copilot CLI users
token-goat install --copilot
The --copilot flag patches Claude Code and registers a Copilot CLI hook config: ~/.copilot/hooks/token-goat.json (a { version, hooks } file registering preToolUse, postToolUse, preCompact, agentStop, subagentStop, and userPromptSubmitted, per Copilot's own [hooks reference](https://docs.github.com/en/copilot/referenc
Comments (0)
Sign in to join the discussion.
No comments yet
Be the first to share your take.