[Feature Request] Built-in tool output compression via PreToolUse hooks
Problem
Claude Code burns significant tokens on bloated tool output that contains mostly noise. A cargo test run with 30 passing tests and 1 failure produces ~500 tokens of output, but only ~100 tokens are useful (the failure, the summary). The other 400 tokens are passing test lines Claude doesn't need. Same pattern for build logs (repeated "Compiling..." lines), lint output (duplicate warnings), and stack traces (internal runtime frames).
This compounds across a session. In a typical test-debug-fix loop, Claude runs tests 5-10 times. That's 2,000-4,000 wasted tokens just on passing test output that's identical each time.
Proposed solution
Use PreToolUse hooks to intercept Bash commands that produce reducible output (test runners, build tools, linters) and pipe their output through type-aware compressors before Claude sees it. Exit codes are preserved so Claude still knows if tests passed or failed.
Working proof of concept
I built this as an open-source tool: context-os
It installs a PreToolUse hook that intercepts known commands and wraps them:
cargo test --lib
becomes:
_co_rc=0; _co_out=$(cargo test --lib 2>&1) || _co_rc=$?; printf '%s\n' "$_co_out" | context-os pipe; exit "$_co_rc"
The pipe command auto-detects content type (test log, build log, lint output, stack trace, JSON, config) and applies a typed reducer. Reducers are deterministic, use regex-based pattern matching (not LLM summarization), and operate in safe mode by default (100% protected string recall — error messages, file paths, versions are never dropped).
Measured results (benchmarked with CI gates)
| Content type | Reduction | Before | After | Protected string recall |
|---|---|---|---|---|
| Stack traces | 42% | 149 tokens | 86 tokens | 100% |
| Test logs | 36% | 163 tokens | 104 tokens | 100% |
| Build logs | 33% | 566 tokens | 379 tokens | 100% |
| Lint output | 26% | 599 tokens | 441 tokens | 100% |
| JSON blobs | 13% | 199 tokens | 173 tokens | 100% |
| Config files | 13% | 77 tokens | 67 tokens | 100% |
Average: 27.3% reduction across all content types, with 100% recall on protected strings (error messages, file paths, version numbers).
What gets compressed (examples)
Test logs: 29 passing test lines → [context-os] 29 tests passed (collapsed). All failure details, stack traces, and summaries preserved.
Build logs: 20 "Compiling..." lines → [context-os] 20 crates compiled (collapsed). All errors, warnings with file locations, and help messages preserved.
Lint output: 3 identical clippy::needless_return warnings across different files → 1 example + [context-os] 2 more occurrences. All errors preserved in full.
Stack traces: Internal runtime frames (rust_begin_unwind, core::panicking, std::rt, __libc_start_main) collapsed. User code frames and error messages preserved.
Why this might be worth building in
- It's automatic. Users run one setup command and every test/build/lint command is compressed from that point forward. No workflow change.
- It's safe. Fail-open by default — if the reducer isn't confident about the content type, or if reduction would increase token count, it returns the original output unchanged.
- The hooks API already supports it. PreToolUse with
updatedInputis the right integration point. The tool just wraps the Bash command.
- Research supports it. CompLLM (2025) showed that compressed context outperforms uncompressed at 50K+ tokens. NoLiMa (ICML 2025) showed 11 of 13 LLMs dropped below 50% accuracy at 32K tokens when they couldn't rely on surface patterns. Less noise = better output, not just fewer tokens.
- Users are asking for it. The rate limit threads (#37394, #16856, #5088) have hundreds of comments. Even a 27% reduction on tool output is meaningful when you're hitting limits.
Implementation notes
- Single Rust binary, no runtime dependencies, no network calls
- 32 tests, 7 benchmark gates in CI
- Also includes session continuity (auto-handoff between sessions with git state) and per-turn context injection via UserPromptSubmit
- Full source: https://github.com/sravan27/context-os
Happy to discuss the approach or contribute if there's interest in exploring this direction natively.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗