# RTK (Rust Token Killer): o que é e como usar com Claude Code para enxugar a saída do terminal

> O RTK é um binário em Rust que intercepta comandos do agente e comprime a saída antes de ela entrar no contexto do LLM. Veja como funciona, como instalar e onde ele tropeça.

Por Lucas Roberto · Open source · publicado em 2026-10-12 · 11 min de leitura
Versão web: https://lucasroberto.com/blog/rtk-rust-token-killer-o-que-e-e-como-usar-com-claude-code-para-enxugar-a-saida-do-terminal

O RTK (Rust Token Killer) é um proxy de linha de comando que intercepta os comandos de shell que um agente de código executa (`git status`, `cargo test`, `ls`, `grep`) e devolve uma versão filtrada e compacta da saída antes que ela chegue ao contexto do LLM. Ele serve para quem usa Claude Code, Cursor, Codex, Gemini CLI e afins no dia a dia e percebe que boa parte da janela de contexto é gasta com barra de progresso, teste que passou e listagem de diretório. Funciona como um binário único em Rust, ativado por um hook que reescreve os comandos sem o agente perceber.

Este post se baseia na leitura do código e da documentação do repositório [rtk-ai/rtk](https://github.com/rtk-ai/rtk). Na data deste post, o projeto tem 83104 estrelas e licença Apache-2.0.

## O que é o RTK e que problema ele resolve?

Quem trabalha com agente de código já viu a cena: o agente roda a suíte de testes, recebe 300 linhas de `ok`, um traceback no meio e mais 50 linhas de resumo. Tudo isso entra no contexto como **token de entrada**, a unidade que o modelo lê e que você paga. Multiplique por dezenas de comandos por sessão e a conversa fica cara e lenta, além de empurrar o que importa para fora da janela.

O RTK ataca exatamente esse pedaço. Segundo o README, ele aplica quatro estratégias por tipo de comando:

1. **Filtragem**: remove ruído, como comentários, espaços e boilerplate.
2. **Agrupamento**: junta itens parecidos, como arquivos por diretório e erros por tipo.
3. **Truncamento**: mantém o contexto relevante e corta redundância.
4. **Deduplicação**: colapsa linhas de log repetidas com um contador.

Na prática, `git push` vira `ok main`, `cargo test` mostra só as falhas e `git log` mostra hash, autor e assunto. A documentação lista mais de 100 comandos suportados, de `pytest` e `go test` a `docker ps`, `kubectl logs`, `aws` e `pulumi`.

Para quem é do mundo PHP, existe uma pasta `src/cmds/php/` com módulos para `artisan`, `phpunit`, `pest`, `paratest`, `phpstan`, `pint` e `ecs`, além de um filtro TOML para `composer install`. O README só documenta explicitamente o `rtk phpt` (testes `.phpt`, com -99% de saída). Como fica a saída filtrada de um `php artisan test` ou de um `phpstan analyse` não está descrito na documentação principal; está apenas no código desses módulos.

## Como o RTK funciona por dentro?

A arquitetura é descrita no `CLAUDE.md` do próprio repositório como um **proxy de comandos**. O fluxo é este:

1. O hook do agente recebe o comando antes da execução. Um **hook PreToolUse** é um gancho que o Claude Code dispara antes de rodar uma ferramenta, e que pode alterar a entrada dela. O hook troca `git status` por `rtk git status`.
2. O `src/main.rs` roteia o subcomando por um enum `Commands` do Clap até o módulo especializado em `src/cmds/<ecossistema>/`, como `git_cmd.rs`, `cargo_cmd.rs` ou `pytest_cmd.rs`.
3. O módulo executa o comando real, captura stdout e stderr, aplica o filtro e imprime o resultado. O código de saída do processo filho é propagado com `std::process::exit(code)`.
4. A economia é registrada em SQLite (`src/core/tracking.rs`), e é dali que sai o painel do `rtk gain`.

Algumas peças merecem atenção:

- **Filtros TOML em `src/filters/`**: para saídas de texto previsíveis (`make`, `terraform plan`, `rsync`, `helm`), o filtro é declarativo, baseado em regex por linha. Filtros em Rust ficam para saídas estruturadas (JSON, NDJSON) ou que exigem uma máquina de estados, como as fases do pytest. O `CONTRIBUTING.md` traz uma tabela clara de quando usar cada um.
- **Regras de reescrita em `src/discover/rules.rs` e `src/discover/registry.rs`**: é ali que se decide qual comando vira `rtk <cmd>`.
- **Init por agente em `src/hooks/init/`**: há um arquivo por integração (`claude.rs`, `cursor.rs`, `codex.rs`, `gemini.rs` e outros). Desde a 0.24 o hook é um "delegador fino" que chama `rtk rewrite`, então a lógica de reescrita mora no binário e não no script.
- **`guard::never_worse(raw, filtered)`**: garante que o RTK nunca emite mais bytes que o comando cru.
- **Fallback**: se o filtro falha, a regra do projeto é devolver a saída original. Os hooks saem com código 0 em qualquer erro, para o comando do agente rodar sem modificação.
- **Recuperação**: quando um comando falha ou é truncado, o RTK guarda a saída completa e imprime algo como `[full output: rtk recall 3f9c2a81d4e7]`, para o LLM recuperar o conteúdo sem reexecutar o comando.

O projeto tem metas explícitas de menos de 10 ms de inicialização e menos de 5 MB de memória, sem runtime assíncrono e sem I/O de configuração no caminho crítico. O `Cargo.toml` proíbe `unsafe_code` e trata warnings como erro.

## Como instalar o RTK?

As opções documentadas no README:

```bash
# Homebrew (recomendado)
brew install rtk

# Linux/macOS via script (instala em ~/.local/bin)
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh

# Cargo, direto do repositório
cargo install --git https://github.com/rtk-ai/rtk
```

No Windows existe `winget install rtk-ai.rtk`, e também há binários pré-compilados nas releases.

Um detalhe que pega muita gente: existe outro projeto chamado `rtk` no crates.io (Rust Type Kit), que não tem nada a ver com este. Por isso a documentação recomenda `cargo install --git` em vez de `cargo install rtk` e pede para validar com:

```bash
rtk --version
rtk gain   # se falhar, você instalou o pacote errado
```

## Como ativar o hook no Claude Code?

O comando principal é o `rtk init`. Para o Claude Code, no modo global:

```bash
rtk init -g                 # hook + RTK.md (recomendado)
rtk init -g --auto-patch    # sem prompt, útil em automação
rtk init -g --hook-only     # só o hook, sem RTK.md
rtk init -g --no-patch      # só imprime as instruções para editar à mão
rtk init --show             # verifica a instalação
```

Segundo o `INSTALL.md`, o `rtk init -g` instala o hook em `~/.claude/hooks/rtk-rewrite.sh`, cria `~/.claude/RTK.md`, adiciona uma referência `@RTK.md` no `~/.claude/CLAUDE.md` e pergunta se pode alterar o `~/.claude/settings.json`, guardando um backup em `settings.json.bak`. Depois disso, é preciso reiniciar o Claude Code. Se você já organiza o agente por projeto, vale cruzar com o que escrevi sobre [configurar o Claude Code em projetos Laravel com CLAUDE.md, hooks e comandos](/blog/como-configurar-o-claude-code-em-projetos-laravel-claudemd-hooks-e-comandos): o RTK entra como mais um hook PreToolUse na mesma cadeia.

Para os outros agentes, a sintaxe muda pouco: `rtk init -g --agent cursor`, `rtk init -g --codex`, `rtk init -g --gemini`, `rtk init --agent cline`. A tabela do README mostra que nem todos têm hook de verdade. Windsurf, Cline, Kilo Code e Kimi recebem apenas um arquivo de regras pedindo que o modelo prefixe os comandos com `rtk`. Isso depende de o modelo obedecer, o que é bem menos confiável que uma reescrita transparente.

## Exemplo de uso no dia a dia

Com o hook ativo, você não muda nada no fluxo. O agente roda `git status` e recebe a versão compacta. Mas os comandos também funcionam direto no terminal:

```bash
rtk git status                  # status agrupado por estado
rtk git diff                    # diff com contexto reduzido
rtk grep "Idempotency-Key" app/ # resultados agrupados por arquivo
rtk read app/Services/Gateway.php -l aggressive  # só assinaturas
rtk test vendor/bin/phpunit     # wrapper genérico: só falhas
rtk err composer install        # só erros e warnings
```

O `rtk test <cmd>` e o `rtk err <cmd>` são os curingas para ferramentas sem filtro dedicado: executam o argv diretamente e mostram só falhas ou só erros. Eles não expandem globs, variáveis nem operadores de shell por padrão. Se precisar de sintaxe de shell, a documentação manda usar `rtk run -c '<script>'` ou `--shell <nome>`.

Para ver o efeito acumulado:

```bash
rtk gain                 # resumo
rtk gain --history       # histórico por comando
rtk discover             # comandos que poderiam ter sido filtrados
```

E quando um comando não deve ser mexido, há três saídas: `rtk proxy <cmd>` roda sem filtro mas registra no histórico, `RTK_DISABLED=1 <cmd>` pula o hook para aquele comando e o `config.toml` aceita exclusões permanentes:

```toml
[hooks]
exclude_commands = ["curl", "playwright"]
```

Uma limitação importante: o hook só pega chamadas da ferramenta Bash. As ferramentas nativas do Claude Code, como `Read`, `Grep` e `Glob`, não passam por ele. Se o agente lê arquivos pelo `Read`, o RTK não economiza nada ali.

## Quanto o RTK economiza de verdade?

Aqui o próprio repositório é honesto, e vale ler com atenção. O README insiste que os percentuais medem **redução de saída do bash**, não da fatura. A saída do shell é só uma parte dos tokens de entrada, que convivem com o system prompt, o histórico e o seu prompt. E os tokens de entrada são só parte do custo, porque os de saída também entram na conta. Além disso, o RTK não traz tokenizador: as contagens são estimadas como `bytes / 4`, então os percentuais são confiáveis e os números absolutos, aproximados.

O arquivo `FIX_PERF.md` vai além e documenta um benchmark interno com 10 pares de VMs com o RTK ligado e desligado. Nessa tarefa, a diferença de custo em dólar foi de −0,2%, praticamente empate. O documento atribui isso a comandos extras que o agente rodou para se recuperar de saídas quebradas. Também é um documento de investigação, não uma conclusão final, mas ele mostra que "90% menos saída" não se converte automaticamente em "90% mais barato".

Se a sua preocupação é custo de LLM, as alavancas maiores costumam ser outras, como as que descrevi em [prompt caching e Batch API do Claude](/blog/prompt-caching-e-batch-api-do-claude-no-laravel-como-cortar-o-custo-de-llm-em-producao). Sobre cache, o README afirma que o RTK não quebra o prompt cache do Claude, porque filtra a saída uma única vez e o resultado fica no histórico normalmente.

## Quando vale a pena e quando não?

Vale a pena quando:

1. Suas sessões são dominadas por comandos verbosos: suítes de teste grandes, builds, `docker logs`, `kubectl`, `git log` longo.
2. O problema principal é contexto estourando ou o agente se perdendo em ruído, mais do que o custo em si.
3. Você usa um agente com hook real (Claude Code, Cursor, Codex, Gemini CLI, Copilot no VS Code), em que a reescrita é transparente.

Provavelmente não vale quando:

1. O agente lê arquivos e busca código pelas ferramentas nativas, sem passar pelo Bash.
2. Seu fluxo depende de pipelines de shell para contar, ordenar e filtrar (mais sobre isso abaixo).
3. Você usa um agente que só aceita arquivo de regras, em que a ativação depende do modelo lembrar de prefixar `rtk`.

## Cuidados antes de instalar

Como toda ferramenta que se pluga no agente, o RTK roda com as suas permissões e executa comandos em seu nome. Pelo que dá para ver no código e na documentação, ele acessa:

- **Arquivos de configuração do agente**: o `rtk init -g` altera `~/.claude/settings.json` (com backup), cria `~/.claude/hooks/rtk-rewrite.sh`, `~/.claude/RTK.md` e mexe no `~/.claude/CLAUDE.md`. Para outros agentes, escreve em `~/.factory/hooks.json`, `.trae/hooks.json`, `~/.gemini/...` e equivalentes. Para desfazer: `rtk init -g --uninstall`.
- **Banco SQLite local**: o histórico de comandos e a saída completa de comandos que falharam ou foram truncados ficam guardados para o `rtk recall`. Se você roda `env`, `curl` contra APIs autenticadas ou testes que imprimem credenciais, isso pode parar em disco. O `[retriever] mode` aceita `disabled`. Em ambiente que mexe com dado de cliente, o raciocínio é o mesmo que uso para [proteger dados e ações em agentes de IA](/blog/prompt-injection-e-lgpd-em-agentes-de-ia-no-laravel-como-proteger-dados-e-acoes): saiba onde cada saída vai parar.
- **Rede**: o `Cargo.toml` inclui o cliente HTTP `ureq`, usado pela telemetria. Ela é desativada por padrão e exige opt-in no `rtk init` ou via `rtk telemetry enable`. Dá para travar de vez com `export RTK_TELEMETRY_DISABLED=1`. Um ponto de atenção: o `DISCLAIMER.md` ainda diz que a coleta é ativa por padrão, contradizendo o README. Na dúvida, rode `rtk telemetry status`.
- **Instalação via `curl | sh`**: execute só depois de ler o `install.sh`, ou prefira Homebrew ou `cargo install --git`.
- **Pipelines**: o `FIX_PERF.md` registra que `rtk find src -name "*.rs" | xargs wc -l` falhou em todas as VMs do benchmark, porque a saída compacta não é uma lista de caminhos. Registra também um caso pior: `cargo clippy ... | tee /tmp/clippy.log` gravou o resumo filtrado, e o agente contou zero warnings sem erro nenhum. É um bug de corretude, não de economia. Se você depende de saída parseável por outro programa, adicione esses comandos ao `exclude_commands` ou use `rtk proxy`.
- **Formato diferente do original**: o projeto diz buscar saídas que pareçam "uma versão mais curta do comando real", mas `git push` virar `ok main` é, objetivamente, outro formato. Scripts e CIs que fazem parse de saída não devem passar pelo RTK.

## Conclusão prática

Se eu fosse adotar o RTK num projeto Laravel com Claude Code, faria assim: instalaria via Homebrew ou `cargo install --git`, rodaria `rtk init -g --no-patch` para revisar o JSON antes de aplicar e começaria com `exclude_commands` para tudo que alimenta pipe ou parse (`find`, `wc` e qualquer coisa que vá para `tee`). Deixaria `[retriever] mode = "disabled"` em máquinas com acesso a dados sensíveis e conferiria `rtk telemetry status`. Depois de uma ou duas semanas, compararia o `rtk gain` com a fatura real do provedor, não com o painel do próprio RTK. Se o ganho aparecer em contexto mais limpo e menos compactação de conversa, mantenha. Se o agente começar a reexecutar comandos para entender a saída, a economia já foi embora, e o `exclude_commands` é o lugar de ajustar.

---

Lucas Roberto é desenvolvedor back-end e fundador da Granpag. Mais textos: https://lucasroberto.com/blog
