Overview
chop is a CLI output compressor for AI coding agents. Claude Code, Gemini CLI, Codex CLI, and other agents waste 50–90% of their context window on verbose CLI output — build logs, test results, container listings, git diffs. chop compresses that output before the agent sees it, saving tokens and keeping conversations focused.
The name comes from chop chop: the sound of something eating through all that verbosity before it ever reaches the context window.
How It Works
When Claude Code runs a Bash command, the raw output is fed back into the conversation as a tool_result — part of the input of the next API call. chop intercepts that result and compresses it before it enters the context.
The Cascade Effect
The savings aren’t one-time. Every tool_result that enters the context stays there for the rest of the session — included in the input of every subsequent API call. A bloated command result compounds across turns.
Why Output Tokens Also Benefit
API output tokens cost 5× more than input tokens and are slower to generate. A larger, noisier context causes the model to produce longer, more verbose responses. Keeping the context lean produces tighter, faster output naturally.
| Token type | Price (Sonnet 4.6) | Note |
|---|---|---|
| Input | $3.00 / MTok | context + tool results |
| Output | $15.00 / MTok | model responses |
Before & After
Real examples of chop in action across common commands.
Installation
macOS / Linux
# Install to ~/.local/bin (default)
curl -fsSL https://raw.githubusercontent.com/AgusRdz/chop/main/install.sh | sh
# Specific version
curl -fsSL https://raw.githubusercontent.com/AgusRdz/chop/main/install.sh | CHOP_VERSION=v1.0.0 sh
# Reload shell after install
source ~/.zshrc # or ~/.bashrc
The installer places the binary in ~/.local/bin by default. If it is not in your PATH, it is added automatically to ~/.zshrc or ~/.bashrc.
xattr -d com.apple.quarantine ./chop. Installing via Homebrew avoids this entirely.Windows (PowerShell)
irm https://raw.githubusercontent.com/AgusRdz/chop/main/install.ps1 | iex
# Specific version
$env:CHOP_VERSION="v1.0.0"; irm https://raw.githubusercontent.com/AgusRdz/chop/main/install.ps1 | iex
The installer adds the binary to your user PATH automatically. Restart your terminal after installing.
Homebrew (macOS / Linux)
brew install AgusRdz/tap/chop
Go
go install github.com/AgusRdz/chop@latest
Build from Source
Requires Docker.
git clone https://github.com/AgusRdz/chop.git
cd chop
make install # builds + copies to ~/.local/bin/
Update
chop update
Verification
All release binaries are signed with GitHub Artifact Attestations. Requires the GitHub CLI.
gh attestation verify chop-darwin-arm64 --repo AgusRdz/chop
Quick Start
Use chop directly by prefixing any command. chop auto-detects the command and applies the best compression strategy.
chop git status # compressed git status
chop docker ps # compact container list
chop npm test # just failures and summary
chop kubectl get pods # essential columns only
chop terraform plan # resource changes, no attribute noise
chop curl https://api.io # JSON compressed to structure + types
chop anything # auto-detects and compresses any output
Claude Code
Register a PreToolUse hook that automatically wraps every Bash command. After this, every command Claude Code runs gets compressed transparently. You’ll see chop git status in the tool calls — that’s the hook working.
chop init --global # install hook
chop init --uninstall # remove hook
chop init --status # check if installed
CLAUDE.md snippet
Add this to your CLAUDE.md for best results:
## Chop (Token Optimizer)
`chop` is installed globally. It compresses CLI output to reduce token consumption.
When running CLI commands via Bash, prefix with `chop` for read-only commands:
- `chop git status`, `chop git log -10`, `chop git diff`
- `chop docker ps`, `chop npm test`, `chop dotnet build`
- `chop curl <url>` (auto-compresses JSON responses)
Do NOT use chop for: interactive commands, pipes, redirects, or write commands
(git commit, git push, npm init, docker run).
Gemini CLI
Registers a BeforeTool hook in ~/.gemini/settings.json that wraps run_shell_command.
chop init --gemini # install hook
chop init --gemini --uninstall # remove hook
chop init --gemini --status # check if installed
Codex CLI
Registers a PreToolUse hook in ~/.codex/settings.json that wraps the bash tool.
chop init --codex # install hook
chop init --codex --uninstall # remove hook
chop init --codex --status # check if installed
Antigravity IDE
Registers a PreToolUse hook in ~/.antigravity/settings.json that wraps the bash tool.
chop init --antigravity # install hook
chop init --antigravity --uninstall # remove hook
chop init --antigravity --status # check if installed
AI Agent Discovery
chop writes a discovery file at ~/.chop/path.json on every install, update, or hook registration, so AI agents can always locate the binary without searching:
{
"version": "v1.15.0",
"path": "/home/user/.local/bin/chop"
}
Two commands support agent-first workflows:
chop agent-info # output JSON metadata (path, version, installed hooks)
chop init --agent-handshake # print discovery message agents recognize
setup is also available as an alias for init, useful when working with Gemini CLI where /init conflicts with a built-in command:
chop setup --global # same as chop init --global
Supported Commands
chop has built-in filters for 60+ commands across all major categories. Any command not listed still gets compressed via auto-detection (JSON, CSV, tables, log lines).
| Category | Commands | Savings |
|---|---|---|
| Git | git status/log/diff/branch/push, gh pr/issue/run | 50–90% |
| JavaScript | npm install/list/test/view, pnpm, yarn, bun, npx, tsc, eslint, biome | 70–95% |
| Angular / Nx | ng build/test/serve, nx build/test, npx nx | 70–90% |
| .NET | dotnet build/test | 70–90% |
| Rust | cargo test/build/check/clippy | 70–90% |
| Go | go test/build/vet | 75–90% |
| Python | pytest, pip, uv, mypy, ruff, flake8, pylint | 70–90% |
| Java | mvn, gradle/gradlew | 70–85% |
| Ruby | bundle, rspec, rubocop | 70–90% |
| PHP | composer install/update | 70–85% |
| Containers | docker ps/build/images/logs/inspect/stats/rmi, docker compose | 60–85% |
| Kubernetes | kubectl get/describe/logs/top, helm | 60–85% |
| Infrastructure | terraform plan/apply/init | 70–90% |
| Build | make, cmake, gcc/g++/clang | 60–80% |
| Cloud | aws, az, gcloud | 60–85% |
| HTTP | curl, http (HTTPie) | 50–80% |
| Search | grep, rg | 50–70% |
| System | ping, ps, ss/netstat, df/du, systemctl | 50–80% |
| Files / Logs | cat, tail, less, more, ls, find | 60–95% |
| Atlassian | acli jira list/get-issue | 60–80% |
Log Pattern Compression
When reading log files with cat, tail, or any log-producing command, chop groups structurally similar lines by replacing variable parts (UUIDs, IPs, timestamps, numbers, key=value pairs) with a fingerprint, then shows a representative line with a repeat count. Errors and warnings are always shown in full and floated to the top.
Before (51 lines):
2024-03-11 10:00:00 INFO Processing request id=req0001 duration=31ms status=200
2024-03-11 10:00:01 INFO Processing request id=req0002 duration=32ms status=200
... (48 more identical-structure lines)
2024-03-11 11:00:00 ERROR Connection timeout to 10.0.0.5:3306
After (2 lines):
2024-03-11 11:00:00 ERROR Connection timeout to 10.0.0.5:3306
2024-03-11 10:00:49 INFO Processing request id=req0049 duration=79ms status=200 (x50)
Token Tracking
Every command is tracked in a local SQLite database. Use chop gain to see exactly how much context you’re saving.
chop gain # overall stats
chop gain --history # last 20 commands with per-command savings
chop gain --history --limit 100 # last 100 commands
chop gain --history --all # all recorded commands
chop gain --since 7d # stats for the last 7 days
chop gain --history --since 7d # history filtered to last 7 days
chop gain --history --since 7d --all # all commands in the last 7 days
chop gain --summary # per-command breakdown
Sample output:
chop - token savings report
today: 42 commands, 12,847 tokens saved
week: 187 commands, 52,340 tokens saved
month: 318 commands, 89,234 tokens saved
total: 1,203 commands, 456,789 tokens saved (73.2% avg)
The ! marker in history
chop gain --history marks any command with ! when it produced 0% savings. This happens in two legitimate cases:
- Write commands (
git commit,git push,git add,git tag) — near-zero output by design. 0% is expected and correct. - Already-minimal output — a
git log --oneline -5that returned one result is already compact.
chop gain --no-track "git push" # delete records + suppress future tracking
chop gain --no-track "git commit"
chop gain --resume-track "git push" # re-enable tracking
Unchopped Report
Identify commands that pass through without compression — candidates for new filters.
chop gain --unchopped # show commands with no filter coverage
chop gain --unchopped --verbose # untruncated command names + full detail
chop gain --unchopped --skip X # mark X as intentionally unfiltered
chop gain --unchopped --unskip X # restore X to the candidates list
chop gain --delete X # permanently delete all tracking records for X
Configuration
Global config
chop config # show global config file path and contents
chop config init # create ~/.config/chop/config.yml with examples
~/.config/chop/config.yml:
# Skip filtering - return full uncompressed output
disabled:
- curl # disables all curl commands
- "git diff" # disables only git diff (git status still compressed)
- "git show" # disables only git show
Entries can be a base command (disables all subcommands) or "command subcommand" for granular control.
Local config (per-project)
chop local # show current local config
chop local add "git diff" # disable git diff in this project
chop local add "docker ps" # add another entry
chop local remove "git diff" # re-enable git diff
chop local clear # remove local config entirely
The first chop local add creates a .chop.yml file and adds it to .gitignore automatically.
.chop.yml:
# .chop.yml — overrides global config for this project
disabled:
- "git diff"
.chop.yml exists, its disabled list replaces the global one entirely. This lets you narrow down or expand what’s disabled per project.Custom Filters
Define your own output compression rules for any command — no Go code required. Global filters live at ~/.config/chop/filters.yml. Local filters at .chop-filters.yml in the current directory. Local always wins on conflict.
Managing filters
# Global filters (~/.config/chop/filters.yml)
chop filter init # create starter global filters file
chop filter add <cmd> [flags] # add or update a filter
chop filter remove <cmd> # remove a filter
chop filter # list all active filters
chop filter path # show config file location
# Project filters (.chop-filters.yml in current directory)
chop filter init --local # create starter local filters file
chop filter add <cmd> [flags] --local # add or update a project-level filter
chop filter remove <cmd> --local # remove a project-level filter
filter add flags
chop filter add "myctl deploy" --keep "ERROR,WARN,deployed,^=" --drop "DEBUG,^\s*$"
chop filter add "ansible-playbook" --keep "^PLAY,^TASK,fatal,changed,^\s+ok=" --tail 20
chop filter add "custom-tool" --exec "~/.config/chop/scripts/custom-tool.sh"
chop filter add "make build" --keep "error:,warning:,^make\[" --tail 10 --local
| Flag | Description |
|---|---|
--keep "p1,p2" | Comma-separated regex patterns — only keep matching lines |
--drop "p1,p2" | Comma-separated regex patterns — remove matching lines |
--head N | Keep first N lines (after drop/keep) |
--tail N | Keep last N lines (after drop/keep) |
--exec script | Pipe output through an external script or command |
--local | Write to .chop-filters.yml in the current directory |
Rule order
| Rule | Description |
|---|---|
drop | Remove lines matching any pattern (applied first) |
keep | Keep only lines matching at least one pattern |
head: N | Keep first N lines (after drop/keep) |
tail: N | Keep last N lines (after drop/keep) |
exec | Pipe raw output through external script. Takes priority — ignores all other rules. |
Editing the YAML manually
Backslashes must be escaped in YAML double-quoted strings (\\s in the file = \s in regex). chop filter add handles this automatically.
filters:
"myctl deploy":
keep: ["ERROR", "WARN", "deployed", "^="]
drop: ["DEBUG", "^\\s*$"] # \\s in YAML = \s in the regex
"ansible-playbook":
keep: ["^PLAY", "^TASK", "fatal", "changed", "^\\s+ok="]
tail: 20
"custom-tool":
exec: "jq ."
chop filter add — pass regex patterns as-is. Use \s for whitespace, \d for digits, etc.Testing filters
# Linux / macOS
echo -e "DEBUG init\nINFO started\nERROR failed" | chop filter test myctl deploy
# Windows (PowerShell)
"DEBUG init`nINFO started`nERROR failed" | chop filter test myctl deploy
Diagnostics
Tools for inspecting chop’s state and debugging hook issues.
chop doctor # check and fix common issues
chop hook-audit # show last 20 hook rewrite log entries
chop hook-audit --clear # clear the hook audit log
chop config # show global config file path and contents
chop config init # create a starter global config.yml
chop local # show local project config
chop agent-info # JSON metadata (path, version, installed hooks)
chop --post-update-check # verify install location after update
Uninstall & Reset
chop uninstall # remove hook, data, config, and binary
chop uninstall --keep-data # uninstall but preserve tracking history
chop reset # clear tracking data and audit log, keep installation
Migration
Versions before v0.14.4 (pre v1.0.0) installed the binary to ~/bin. Run the migration script to move it to the standard location and update your shell config automatically.
macOS / Linux
curl -fsSL https://raw.githubusercontent.com/AgusRdz/chop/main/migrate.sh | sh
# Reload shell after migrating
source ~/.zshrc # or ~/.bashrc
Or manually:
mkdir -p ~/.local/bin
mv ~/bin/chop ~/.local/bin/chop
# Remove ~/bin from ~/.zshrc or ~/.bashrc, then add:
export PATH="$HOME/.local/bin:$PATH"
Windows (PowerShell)
irm https://raw.githubusercontent.com/AgusRdz/chop/main/migrate.ps1 | iex
Or manually:
New-Item -ItemType Directory -Force "$env:LOCALAPPDATA\Programs\chop"
Move-Item "$env:USERPROFILE\bin\chop.exe" "$env:LOCALAPPDATA\Programs\chop\chop.exe"
[Environment]::SetEnvironmentVariable("PATH", "$env:LOCALAPPDATA\Programs\chop;" + [Environment]::GetEnvironmentVariable("PATH","User"), "User")
Restart your terminal after migrating.