Getting Started

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.

Claude Code → hook → chop → compressed output → Claude API
                 (850 tokens)           (250 tokens)

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.

Without chop: docker ps adds 850 tokens on turn 1. Those 850 tokens are carried forward on turns 2, 3, 4… until compaction is triggered early.

With chop: The same command adds 250 tokens. The session runs longer, the context budget stretches further, and compaction is deferred.

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 — more recapitulation, more hedging. Keeping the context lean produces tighter, faster output naturally.

Input token price:   $3.00 / MTok  (Claude Sonnet)
Output token price: $15.00 / MTok  (Claude Sonnet)
200K threshold: Once input exceeds 200K tokens, pricing jumps to $6.00 input / $22.50 output — applied to the entire request, not just the excess. Staying compressed avoids crossing that threshold.

Getting Started

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.

✗ Without chop — docker ps (850 tokens)
Turn 1850 tokens added
Turn 2+850 carried forward
Turn 3+850 carried forward
Turn N+850 every turn
⚠ Compaction triggered early
✓ With chop — docker ps (250 tokens)
Turn 1250 tokens added
Turn 2+250 carried forward
Turn 3+250 carried forward
Turn N+250 every turn
✓ Longer session, more budget

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 typePrice (Sonnet 4.6)Note
Input$3.00 / MTokcontext + tool results
Output$15.00 / MTokmodel responses
200K threshold: Once input exceeds 200K tokens, pricing jumps to $6.00 input / $22.50 output — applied to the entire request, not just the excess. Staying compressed avoids crossing that threshold.

Getting Started

Before & After

Real examples of chop in action across common commands.

Before 247 tokens
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
(use "git add <file>..." to update)
modified: src/app.ts
modified: src/auth/login.ts
modified: config.json

Untracked files:
src/utils/helpers.ts
After 12 tokens  95% saved
modified(3): src/app.ts, src/auth/login.ts, config.json
untracked(1): src/utils/helpers.ts
Before 850+ tokens
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx:1.25-alpine ... 2h ago Up 2h 0.0.0.0:80->80/tcp web
f6e5d4c3b2a1 postgres:16-alpine ... 2h ago Up 2h 0.0.0.0:5432->5432/tcp db
After compact table  70% saved
web nginx:1.25-alpine Up 2h :80->80
db postgres:16-alpine Up 2h :5432->5432
Before 1,200+ tokens
> @acme/ui@1.0.0 test
> jest

PASS src/lib/button/button.component.spec.ts
PASS src/lib/modal/modal.component.spec.ts
PASS src/lib/tooltip/tooltip.component.spec.ts
...58 more passing suites...

Test Suites: 59 passed, 59 total
Tests: 2 skipped, 1679 passed, 1681 total
Time: 12.296 s
After 5 tokens  99% saved
all 1681 tests passed

Getting Started

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

# Custom directory
curl -fsSL https://raw.githubusercontent.com/AgusRdz/chop/main/install.sh | CHOP_INSTALL_DIR=/usr/local/bin 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.

macOS quarantine: If downloaded manually (not via Homebrew), macOS may block the binary on first run. Remove the quarantine flag: 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

# Custom directory
$env:CHOP_INSTALL_DIR="C:\tools\chop"; 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

Getting Started

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

Agent Setup

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).

Agent Setup

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

Agent Setup

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

Agent Setup

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

Agent Setup

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

Reference

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).

CategoryCommandsSavings
Gitgit status/log/diff/branch/push, gh pr/issue/run50–90%
JavaScriptnpm install/list/test/view, pnpm, yarn, bun, npx, tsc, eslint, biome70–95%
Angular / Nxng build/test/serve, nx build/test, npx nx70–90%
.NETdotnet build/test70–90%
Rustcargo test/build/check/clippy70–90%
Gogo test/build/vet75–90%
Pythonpytest, pip, uv, mypy, ruff, flake8, pylint70–90%
Javamvn, gradle/gradlew70–85%
Rubybundle, rspec, rubocop70–90%
PHPcomposer install/update70–85%
Containersdocker ps/build/images/logs/inspect/stats/rmi, docker compose60–85%
Kuberneteskubectl get/describe/logs/top, helm60–85%
Infrastructureterraform plan/apply/init70–90%
Buildmake, cmake, gcc/g++/clang60–80%
Cloudaws, az, gcloud60–85%
HTTPcurl, http (HTTPie)50–80%
Searchgrep, rg50–70%
Systemping, ps, ss/netstat, df/du, systemctl50–80%
Files / Logscat, tail, less, more, ls, find60–95%
Atlassianacli jira list/get-issue60–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. Falls back to exact-match deduplication when no repeating patterns are found.

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)

Reference

Token Tracking

Every command run through chop is recorded in a local SQLite database. Use chop gain to query your savings.

chop gain
Chop — Token Savings
Commands: 3,417
Input tokens: 2.6M
Tokens saved: 715.7K
Efficiency:
27.2%
Period Commands Saved Avg%
Today 75 656 21.0%
This week 182 6,825 15.3%
This month 3,417 715,676 27.2%
This year 3,417 715,676 27.2%
All time 3,417 715,676 27.2%
chop gain --summary per-command breakdown
chop gain --history recent commands
Chop — Token Savings by Command
# Command Count Saved Avg% Impact
1 git 1859 296,812 66%
2 gh 246 215,774 94%
3 cat 158 84,431 16%
4 npx 71 44,476 89%
5 docker 494 34,432 66%
6 npm 90 19,818 59%
7 grep 280 9,398 53%
8 curl 17 5,930 0%
8 command(s) tracked
Chop — Recent Commands
10:16:55 git status 93.8% (48 → 3 tokens)
! 10:16:55 git log --oneline -10 0.0% (118 → 118 tokens)
! 10:14:03 git push --force-with-lease origin master 0.0% (9 → 9 tokens)
12:00:00 git show HEAD --stat 71.9% (57 → 16 tokens)
! 10:11:59 git log --oneline -5 0.0% (14 → 14 tokens)
! 10:10:58 git push --force origin main 0.0% (9 → 9 tokens)
09:48:21 docker compose up -d 75.6% (447 → 109 tokens)
09:30:12 npm test 98.7% (1,240 → 16 tokens)
09:22:05 gh pr list 82.4% (312 → 55 tokens)
! = 0% savings (filter may need improvement)

Overview

chop gain              # overall savings (today / week / month / year / total)
chop gain --since 7d   # stats for a specific time window

Time window formats: 30m, 24h, 7d, 2w, 1mo.

History

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 --history --since 7d            # history filtered to last 7 days
chop gain --history --since 7d --all      # all commands in the last 7 days
chop gain --history --verbose             # full command strings + project group headers
chop gain --history --project <path>      # history for a specific project root

Summaries

chop gain --summary    # per-command savings breakdown
chop gain --projects   # per-project savings breakdown (grouped by git root)

The ! marker

chop gain --history marks commands with ! when they produced 0% savings. This is expected in two cases:

  • Write commands (git commit, git push, git add, etc.) — near-zero output by design, nothing to compress.
  • Already-minimal output — a git log --oneline -5 or a find that returned one result is already compact.

To remove these entries and stop tracking them:

chop gain --no-track "git push"      # delete records for X and stop tracking it
chop gain --no-track "git commit"
chop gain --resume-track "git push"  # re-enable tracking

Suppressing tracking

chop gain --no-track "git push"      # delete records for X and stop tracking it
chop gain --resume-track "git push"  # re-enable tracking
chop gain --delete "git push"        # delete all records for X (tracking continues)

Unchopped report

Identify commands that pass through without compression — candidates for new filters:

chop gain --unchopped            # commands with no filter coverage
chop gain --unchopped --verbose  # untruncated command names + full detail
chop gain --unchopped --skip X   # mark X as intentionally unfiltered (hides it)
chop gain --unchopped --unskip X # restore X to the candidates list

The report has two sections:

  • no filter registered — output passes through raw; worth writing a filter if AVG tokens is high
  • filter registered, 0% runs — filter exists but output was already minimal; no action needed

Export

chop gain --export json           # export full history as JSON to stdout
chop gain --export csv            # export full history as CSV to stdout
chop gain --export json --since 7d  # scoped to a time window

Redirect to a file:

chop gain --export json > chop-history.json
chop gain --export csv  > chop-history.csv

Reference

Configuration

Global config

chop config               # show global config file path and contents
chop config init          # create ~/.config/chop/config.yml with examples
chop config export        # export config + filters to stdout (for syncing to another machine)
chop config import <file> # import a previously exported config

~/.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"
When a local .chop.yml exists, its disabled list replaces the global one entirely. This lets you narrow down or expand what’s disabled per project.

Reference

Custom Filters

Define your own output compression rules for any command — no Go code required. Filters live in ~/.config/chop/filters.yml (global) or .chop-filters.yml in your project directory (local).

Quick start

chop filter new "myctl deploy"   # scaffold a filter + guided workflow

Or create the config file first:

chop filter init                 # global ~/.config/chop/filters.yml
chop filter init --local         # local .chop-filters.yml in cwd

Managing filters

CommandDescription
chop filterList all active custom filters
chop filter pathShow the global filters file path
chop filter initCreate starter global filters.yml
chop filter init --localCreate starter .chop-filters.yml in cwd
chop filter new <cmd>Scaffold a filter + guided workflow
chop filter add <cmd> [flags]Add or update a filter
chop filter remove <cmd>Remove a filter
chop filter remove <cmd> --localRemove a project-level filter
chop filter test <cmd>Test a filter against stdin

Local filters are merged on top of global ones — local always wins on conflict.

Guided filter creation

chop filter new is the recommended starting point. It scaffolds a commented-out entry in filters.yml and prints the four-step workflow:

chop filter new "myctl deploy"
# scaffolded filter for "myctl deploy" in ~/.config/chop/filters.yml
#
# next steps:
#   1. chop capture myctl deploy          — capture real output as a fixture
#   2. edit filters.yml                   — uncomment and tune the rules
#   3. chop diff myctl deploy             — preview compression before enabling
#   4. chop filter test myctl deploy      — verify against stdin

Adding filters from the CLI

Use chop filter add with one or more rule 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
FlagDescription
--keep "p1,p2"Comma-separated regex patterns — only keep matching lines
--drop "p1,p2"Comma-separated regex patterns — remove matching lines
--head NKeep first N lines (after drop/keep)
--tail NKeep last N lines (after drop/keep)
--exec scriptPipe output through an external script or command
--localWrite to .chop-filters.yml in the current directory

Rules

Rules are applied in this order:

RuleDescription
dropRemove lines matching any pattern (applied first)
keepKeep only lines matching at least one pattern
head: NKeep first N lines (after drop/keep)
tail: NKeep last N lines (after drop/keep)
execPipe raw output through an external script (stdin → stdout)

If both head and tail are set and output exceeds head + tail lines, a ... (N lines hidden) separator is shown between them.

exec takes priority — when set, all other rules are ignored and the script receives the raw output on stdin. Supports any command in your shell (jq ., python3 filter.py, etc.).

Editing the YAML directly

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 ."
No manual escaping needed when using chop filter add — pass regex patterns as-is. Use \s for whitespace, \d for digits, etc.

Testing filters

Test a filter against sample input without running the actual command:

# 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

Security

The exec field executes an arbitrary external command. For this reason:

  • Filters in ~/.config/chop/filters.yml are trustedexec works.
  • Filters in .chop-filters.yml (local, per-project) are untrustedexec is silently skipped with a warning. This prevents a malicious repository from running arbitrary code on your machine.

To use exec for a project-specific command, define it in your global filters.yml.


Maintenance

Diagnostics

Health check

chop doctor runs a full health check and auto-fixes what it can:

chop doctor

Checks performed:

  • Hook is installed and points to the correct binary (auto-fixes path mismatches)
  • Binary is not in the legacy ~/bin location
  • chop is not globally disabled
  • Tracking database is accessible and healthy
  • Global config.yml has valid syntax
  • filters.yml has valid regex patterns and accessible exec scripts

Hook audit log

Every command rewrite performed by the hook is logged. Useful for debugging unexpected hook behavior:

chop hook-audit          # show last 20 hook rewrite log entries
chop hook-audit --clear  # clear the audit log

Config inspection

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)

Auto-updates

chop auto-update         # show current auto-update status
chop auto-update on      # enable background auto-updates (downloads silently, applies on next run)
chop auto-update off     # disable auto-updates (you'll get a notification instead)

Enable / Disable

Temporarily bypass chop without uninstalling:

chop disable   # hook passes all commands through uncompressed
chop enable    # resume compression

Maintenance

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

Maintenance

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.


Reference

Shell Completions

chop provides tab-completion scripts for bash, zsh, fish, and PowerShell. Completions cover all top-level commands, subcommands, and flags.

bash

Add to ~/.bashrc:

source <(chop completion bash)

Or install permanently (no subprocess on each shell start):

Linux:

chop completion bash > ~/.local/share/bash-completion/completions/chop

macOS (Homebrew):

chop completion bash > $(brew --prefix)/etc/bash_completion.d/chop
macOS ships with bash 3.2 which doesn’t support source <(...). Install a newer bash via Homebrew (brew install bash) and use the permanent install method above, or switch to zsh.

zsh

Add to ~/.zshrc:

source <(chop completion zsh)

Or install to a user completions directory:

mkdir -p ~/.zsh/completions
chop completion zsh > ~/.zsh/completions/_chop
# ensure this directory is in your fpath (add to ~/.zshrc):
fpath=(~/.zsh/completions $fpath)

fish

Add to ~/.config/fish/config.fish:

chop completion fish | source

Or install permanently:

chop completion fish > ~/.config/fish/completions/chop.fish

PowerShell

Add to your $PROFILE:

chop completion powershell | Invoke-Expression

To find your profile path: echo $PROFILE