If you're using Claude Code, Cursor, Copilot, or any AI coding agent, you're burning tokens on noise — verbose git output, full test logs, directory listings padded with permissions and timestamps. RTK (Rust Token Killer) is a single Rust binary that sits between your AI agent and the shell, filtering and compressing command output before it ever hits the LLM context window.
Why This Matters
A typical 30-minute Claude Code session consumes ~118,000 tokens on shell command output alone. With RTK, that drops to ~24,000 — an 80% reduction. That's real money saved on API calls, and more importantly, it means your agent has more context budget for actual reasoning instead of parsing git push boilerplate.
| Command | Without RTK | With RTK | Savings |
|---|---|---|---|
ls / tree (x10) | 2,000 | 400 | -80% |
cat / read (x20) | 40,000 | 12,000 | -70% |
grep / rg (x8) | 16,000 | 3,200 | -80% |
git status/log/diff (x20) | 15,500 | 3,600 | -77% |
| Test runners (x12) | 39,000 | 3,900 | -90% |
| Total | ~118,000 | ~23,900 | -80% |
How It Works
Without RTK:
Claude --git status--> shell --> git
^ |
| ~2,000 tokens (raw) |
+-----------------------------------+
With RTK:
Claude --git status--> RTK --> git
^ | |
| ~200 tokens | filter |
+---- (filtered) -------+----------+
RTK applies four strategies to compress output:
-
Smart Filtering — Strips noise: comments, whitespace, boilerplate. A
git pushthat normally spits out 15 lines of progress bars becomes justok main. -
Grouping — Aggregates similar items. Test results get collapsed to "FAILED: 2/15 tests" with only the failing test details. Lint errors get grouped by rule or file.
-
Truncation — Keeps the relevant head and tail, cuts the middle. Long file reads preserve signatures and key sections.
-
Deduplication — Collapses repeated log lines with counts. Docker logs that repeat the same health check 500 times become one line with a count.
The magic is in the auto-rewrite hook: once installed, RTK transparently intercepts every Bash tool call your AI agent makes. git status gets rewritten to rtk git status before execution. The AI agent never knows — it just gets cleaner, shorter output.
Installation & Setup
Install
# Homebrew (recommended)
brew install rtk
# Or quick install script
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh
# Or via Cargo
cargo install --git https://github.com/rtk-ai/rtk
# Verify
rtk --version
Configure for Your AI Tool
# Claude Code (default) — one command, done
rtk init -g
# Other tools:
rtk init -g --gemini # Gemini CLI
rtk init -g --codex # OpenAI Codex
rtk init -g --agent cursor # Cursor
rtk init --agent windsurf # Windsurf
rtk init --agent cline # Cline / Roo Code
Then restart your AI tool. That's it. Every shell command your agent runs now goes through RTK automatically.
Daily Usage
You Don't Need to Change Anything
After rtk init -g, the hook handles everything transparently. Your AI agent calls git status, it actually runs rtk git status, and gets compressed output. Zero workflow changes.
Direct Commands (when you want explicit control)
# File operations
rtk ls . # Compact directory tree
rtk read file.rs # Smart file reading
rtk read file.rs -l aggressive # Signatures only (strips function bodies)
rtk grep "pattern" . # Grouped search results
# Git — verbose output becomes one-liners
rtk git status # Compact status
rtk git log -n 10 # One-line per commit
rtk git push # -> "ok main"
rtk git commit -m "msg" # -> "ok abc1234"
# Tests — only failures shown
rtk cargo test # -90% tokens
rtk pytest # -90% tokens
rtk vitest run # Failures only
# Build & lint
rtk tsc # TypeScript errors grouped by file
rtk cargo clippy # -80%
rtk ruff check # -80%
Important Caveat
The hook only intercepts Bash tool calls. Claude Code's built-in tools (Read, Grep, Glob) bypass the hook entirely. If you want RTK filtering for those workflows, use the shell equivalents:
- Instead of
Read-> usertk read file.rs - Instead of
Grep-> usertk grep "pattern" . - Instead of
Glob-> usertk find "*.rs" .
Track Your Savings
rtk gain # Summary stats
rtk gain --graph # ASCII graph (last 30 days)
rtk gain --daily # Day-by-day breakdown
rtk discover # Find missed savings opportunities
rtk session # Show RTK adoption across recent sessions
Configuration
Config file: ~/Library/Application Support/rtk/config.toml (macOS)
[hooks]
exclude_commands = ["curl", "playwright"] # Skip rewrite for these
[tee]
enabled = true # Save raw output on failure
mode = "failures" # "failures", "always", or "never"
The tee feature is clever: when a command fails, RTK saves the full unfiltered output to disk so the LLM can read it without re-executing. You get the compressed version first, and the full version is available if needed.
Supported AI Tools (12 and counting)
| Tool | Command | Method |
|---|---|---|
| Claude Code | rtk init -g | PreToolUse hook |
| GitHub Copilot | rtk init -g --copilot | PreToolUse hook |
| Cursor | rtk init -g --agent cursor | hooks.json |
| Gemini CLI | rtk init -g --gemini | BeforeTool hook |
| OpenAI Codex | rtk init -g --codex | AGENTS.md injection |
| Windsurf | rtk init --agent windsurf | .windsurfrules |
| Cline / Roo Code | rtk init --agent cline | .clinerules |
| Kilo Code | rtk init --agent kilocode | rules file |
| Google Antigravity | rtk init --agent antigravity | rules file |
Deep Dive: How RTK Works Internally
Architecture
RTK is a single Rust binary with a modular design:
src/
├── main.rs # CLI entry point, Clap argument parsing
├── hooks/ # Hook system (Claude Code, Cursor, Gemini, etc.)
│ ├── init.rs # rtk init — installs hooks for each AI tool
│ ├── rewrite_cmd.rs # Command rewriting logic
│ ├── integrity.rs # SHA-256 hook pinning (tamper detection)
│ └── trust.rs # Trust model for project-local filters
├── filters/ # 60+ TOML-defined filter rules
│ ├── cargo.toml, pytest.toml, git.toml, docker.toml, ...
│ └── (each defines match patterns + output transformations)
├── cmds/ # Built-in command handlers (ls, read, grep, git, etc.)
├── core/ # Core filtering engine
├── parser/ # Output parsing (NDJSON, TAP, JUnit, etc.)
├── analytics/ # Token tracking + savings calculations
└── discover/ # "Missed savings" detection
The Hook System
The key innovation is the PreToolUse hook for Claude Code. When you run rtk init -g, it adds a hook to ~/.claude/settings.json that intercepts every Bash tool call:
- Claude decides to run
git diff HEAD~3 - Before execution, the hook script (
rtk-rewrite.sh) intercepts the command - It rewrites it to
rtk git diff HEAD~3 - The rewritten command executes, RTK filters the output
- Claude receives the compressed output — it never knows the rewrite happened
The hook also outputs permissionDecision: "allow", auto-approving the rewritten command. This is convenient but expands Claude Code's auto-approval surface (more on this in Security).
Filter System
RTK ships 60+ TOML-based filter definitions. Each filter specifies:
- Match patterns: Which commands to intercept (e.g.,
cargo test,git status) - Output transformations: Regex-based rules to strip, group, or collapse output
- Format-aware parsing: NDJSON, TAP, JUnit XML for test runners
Users can also define project-local filters in .rtk/filters.toml (requires SHA-256 trust verification) or global filters in ~/.config/rtk/filters.toml.
Tee: Failure Recovery
When a command fails, RTK saves the full unfiltered output to ~/.local/share/rtk/tee/. The compressed output includes a pointer to the full log:
FAILED: 2/15 tests
[full output: ~/.local/share/rtk/tee/1707753600_cargo_test.log]
This means the AI agent gets the compressed summary first, and can read the full output only if needed — a smart lazy-loading pattern.
Competitors & Alternatives
| Tool | Approach | Scope | Token Savings |
|---|---|---|---|
| RTK | CLI proxy, output filtering | Shell commands only | 60-90% |
| Headroom | Context optimization layer | Everything (JSON, code, logs, RAG, conversation) | Varies |
| Morph Compact | Conversation compression SDK | Agent conversation history | 50-70% |
| Claude Prompt Caching | Built-in to Claude | Repeated content (system prompts, files) | Up to 90% |
| Model Routing | Route by task difficulty | All LLM calls | Cost reduction via cheaper models |
| Subagent Delegation | Built-in to Claude Code | Verbose tasks | Context isolation |
Headroom — The Closest Competitor
Headroom is a "context optimization layer" that compresses everything before it reaches the LLM: JSON arrays, code (AST-aware for Python/JS/Go/Rust), logs, RAG results, and conversation history. It uses ModernBERT-based compression.
RTK vs Headroom: They're actually complementary. RTK handles CLI command output specifically with hand-crafted filters. Headroom handles everything else with ML-based compression. You could run both.
Morph Compact
A hosted/SDK service that compresses agent conversation context 50-70% via verbatim deletion (no summarization). Focused on conversation-history bloat, not CLI output. Different problem space.
Built-in Alternatives (Free)
Before reaching for RTK, consider what's already available:
- Prompt Caching: Claude automatically caches repeated content. Zero config, up to 90% cost reduction on cache hits.
- Auto-compaction: Claude Code automatically compresses conversation history when approaching context limits.
- Subagents: Delegate verbose tasks to subagents — only the summary returns to the main context.
.claudeignore: Exclude files and directories from being indexed.
Security Analysis
A community-initiated security audit (issue #640) found several concerns. Here's the honest breakdown:
Critical: Shell Injection
rtk err, rtk test, and rtk summary pass user-provided command strings directly to sh -c without sanitization. Since RTK's hook auto-rewrites commands from LLM output, this creates an LLM prompt injection -> shell injection chain.
// The problematic pattern:
Command::new("sh")
.args(["-c", command]) // command is user-controlled, unescaped
Severity is elevated because the hook auto-approves commands, bypassing Claude Code's permission prompt.
High: Telemetry Concerns
- Default telemetry (in Homebrew builds) sends a persistent device hash (SHA-256 of hostname + username) — linkable across sessions
top_commandsreveals workflow patterns- No install-time consent dialog (opt-out requires editing config)
Mitigation:
# Disable telemetry
rtk telemetry disable
# Or via environment variable
export RTK_TELEMETRY_DISABLED=1
High: Token/Secret Leakage in Tracking DB
RTK stores full command strings (including arguments) in a SQLite database for 90 days. Commands like curl -H "Authorization: Bearer tok_abc" persist verbatim. The rtk gain --history command can surface these in LLM context.
Medium: Global Filters Not Integrity-Checked
Project-local .rtk/filters.toml requires SHA-256 verification, but ~/.config/rtk/filters.toml is trusted unconditionally. If malware modifies the global filter, it could suppress security scanner output or rewrite arbitrary command output — particularly dangerous since RTK sits in the LLM's output pipeline.
Trade-off Analysis
Pros
| Aspect | Detail |
|---|---|
| Token savings | 60-90% reduction is real and significant for heavy users |
| Zero friction | Install once, forget about it. Transparent hook system. |
| Performance | <10ms overhead per command. Written in Rust. Single binary, zero dependencies. |
| Breadth | 100+ supported commands, 12+ AI tools, 60+ filter rules |
| Tee recovery | Full output saved on failure — no information loss when it matters |
| Open source | MIT license, active community, 27k+ stars |
Cons
| Aspect | Detail |
|---|---|
| Security surface | Shell injection risk via sh -c, auto-approval widens attack surface |
| Information loss | Aggressive filtering can hide relevant context. If RTK strips something the LLM needed, you get worse results silently. |
| Hook bypass | Built-in Claude Code tools (Read, Grep, Glob) bypass RTK entirely — inconsistent coverage |
| Telemetry defaults | Opt-out rather than opt-in for Homebrew builds |
| Secret retention | Full command strings stored for 90 days in SQLite |
| Debugging difficulty | When something goes wrong, RTK adds another layer to debug through |
| Filter maintenance | Filters are hand-crafted per command — new tools need new filters |
Who Should NOT Use RTK
- Security-sensitive environments: The shell injection chain (LLM -> hook ->
sh -c) is a real concern if you're running Claude Code on sensitive infrastructure. - Debugging-heavy workflows: If you frequently need full command output to debug issues, RTK's filtering adds friction.
- Light AI coding usage: If you only use Claude Code occasionally, the token savings aren't worth the added complexity.
The Core Trade-off
RTK trades completeness for efficiency. You're betting that 80% of shell output is noise the LLM doesn't need. Most of the time, that's correct — the LLM doesn't need 15 lines of git push progress bars. But occasionally, the filtered-out information was exactly what the LLM needed to diagnose an issue. The tee system mitigates this for failures, but for successful commands that produce "too much" output, the information is simply gone.
The security trade-off is more nuanced: RTK's hook auto-approves rewritten commands, which is convenient but means you're trusting RTK's rewrite logic to never produce something dangerous. The sh -c shell injection finding shows this trust isn't fully warranted yet.
My Recommendation
For most AI coding power users: worth installing, with these precautions:
# Install
brew install rtk
rtk init -g
# Immediately disable telemetry
rtk telemetry disable
# Or set in your shell profile
echo 'export RTK_TELEMETRY_DISABLED=1' >> ~/.zshrc
Monitor your savings with rtk gain periodically. If you notice Claude struggling with filtered output, you can always exclude specific commands:
# ~/.config/rtk/config.toml
[hooks]
exclude_commands = ["curl", "playwright", "your-custom-tool"]
Links:
- GitHub: https://github.com/rtk-ai/rtk
- Security audit: https://github.com/rtk-ai/rtk/issues/640
- Discord: https://discord.gg/RySmvNF5kF
- License: MIT


