Code outlines instead of whole files.
When Claude Code reads a big file, the whole thing lands in context. cubtoken is one Rust binary that catches the read in a hook and hands the model an outline with exact line numbers instead. On the reads it compresses, I estimate 76–88% fewer tokens.
cubtoken is no longer actively maintained.
- Tool call
Read · Grep · GlobClaude Code asks for a file, a search, or a listing. - Runs locally
Read(file_path)The tool runs as usual. A local read costs no tokens. - Hook
PostToolUse → ctkcubtoken sees the result before the model does. - Outline
tree-sitterSignatures stay. Bodies fold into exact line ranges. - Context
updatedToolOutputThe model gets the outline and reads back only the lines it needs.
Where the
tokens go.
rtk and other shell compressors only see the Bash tool. Claude Code's built-in Read, Grep, and Glob go around them, and in most sessions those reads cost more tokens than anything else.
-
Tool call
The agent uses Claude Code's built-in tools the way it always has. You don't change how you work.
-
Runs locally
The tool runs normally and your files stay untouched. Only the copy on its way to the model changes.
-
Hook
ctkruns as aPostToolUsehook. Anything estimated under 2,000 tokens passes straight through, and so do targetedRead(offset, limit)calls, files the agent edited this session, and any outline that wouldn't come out at least 30% smaller. Errors fail open. -
Outline
Tree-sitter keeps signatures, doc comments, attributes, and decorators, and folds each body into its real line range. Rust, TypeScript, JavaScript, Python, and Go get full outlines, and every other file falls back to head and tail with line numbers, which is plain truncation rather than a summary of what the file does.
1pub struct Registry { 2 items: HashMap<String, Service>, 3 config: Arc<Config>, … 8 pub fn new(config: Arc<Config>) -> Self { // 780 lines of internal logic 789 pub fn shutdown(&mut self) -> Result<()> { // cleanup code 833}1pub struct Registry { … [L2-L6] 7impl Registry { 8 pub fn new(config: Arc<Config>) -> Self { … [L9-L788] 789 pub fn shutdown(&mut self) -> Result<()> { … [L790-L832] 833} -
Context
Claude Code swaps the outline in through
updatedToolOutput. Every folded range names the exact call that fetches it. So when the agent needsshutdown, it reads lines 790–832 and leaves the rest of the file alone.
What's proven,
and what isn't.
Only large reads have real evidence behind them. Run ctk stats inside your project to see what your own sessions saved.
- Large reads
- Proven76–88% estimated savings on the reads that trigger it.
- Grep and Glob folding
- UnprovenBuilt and tested, but it rarely fired when I used it day to day.
- Bash output
- Off by defaultDefers to rtk when it finds it on your PATH.
- Token counts
- EstimatesRoughly 3.5 characters per token, not real tokenizer output.
- Hosts
- Claude Code onlyCodex and Antigravity hooks can't replace a tool result, so it needs Claude Code's
updatedToolOutput.
Install.
Three steps. Claude Code reads hooks when a session starts, so restart it after you register one.
1. Get the binary
- macOS Apple Silicon
- ctk-aarch64-apple-darwin.tar.gz
- Linux x86_64
- ctk-x86_64-unknown-linux-gnu.tar.gz
- Windows x86_64
- ctk-x86_64-pc-windows-msvc.zip
Intel Macs, Linux ARM, and anything else build from source with stable Rust:
git clone https://github.com/thepragmaticbear/cubtoken.git
cd cubtoken
cargo install --locked --path crates/ctk-cli 2. Register the hook
ctk init
ctk doctor Or install the plugin from inside Claude Code: /plugin marketplace add thepragmaticbear/cubtoken, then /plugin install cubtoken@cubtoken. It doesn't ship the binary, so ctk still has to be on your PATH.
Restart Claude Code, work as usual, then run ctk stats. If ctk doctor passes but nothing ever compresses, update Claude Code first; I verified cubtoken against 2.1.258.
v0.1.3, MIT licensed. Every release ships SHA256SUMS and a build attestation.