DEV Community

bayuh85
bayuh85

Posted on

ctxtrim: stop feeding your coding agent 300-line logs

Repo: ๐Ÿ‘‰ github.com/bayuh85/ctxtrim (MIT, Node.js 20+)

The problem

Coding agents like Claude Code, Cursor, and Cline are great until the context window fills up with junk. A test run dumps hundreds of lines. A server log repeats the same startup message over and over. The agent cats a large JSON file to check one field. All of that lands in context, gets re-read on every turn, and pushes out the stuff that actually matters โ€” like what you decided twenty minutes ago.

The usual fixes are manual: | tail -50, grep ERROR, or telling the agent "please don't read the whole file." That works until it doesn't.

What ctxtrim does

ctxtrim shrinks what a coding agent reads:

  • Press: large logs, test runs, and file dumps are trimmed mechanically โ€” no network, no key needed.
  • Gloss (optional): if the trimmed result is still big, a cheap OpenAI-compatible model summarizes it (default: zai/glm-5.3).
  • Session notes: decisions and findings go into a compact session ledger instead of a growing transcript.

It ships as both a command-line tool and an MCP server, and both expose the same operations.

Quickstart

Requires Node.js 20 or newer.

git clone https://github.com/bayuh85/ctxtrim.git
cd ctxtrim
npm install
npm run build
Enter fullscreen mode Exit fullscreen mode

The mechanical press works with no key at all. If you want model summaries, set a key:

cp .env.example .env
# CTXTRIM_API_KEY=your-key
Enter fullscreen mode Exit fullscreen mode

Now pipe something noisy through it:

export CTXTRIM_API_KEY=your-key
# optional; these are already the defaults
export CTXTRIM_BASE_URL=https://atmorouter.dev/v1
export CTXTRIM_MODEL=zai/glm-5.3

npm test 2>&1 | node dist/cli.js shrink --kind test --session demo
node dist/cli.js note add --session demo --tag decision "Retry refresh with jitter."
node dist/cli.js recall --session demo
Enter fullscreen mode Exit fullscreen mode

The brief goes to stdout (so you can keep piping), and a one-line receipt goes to stderr. The README's example: pressing a 300-line log containing three connection errors and one warning reports:

ctxtrim ยท log ยท press ยท 12578 โ†’ 1299 bytes ยท kept 10%
Enter fullscreen mode Exit fullscreen mode

The brief keeps the repeated startup line collapsed (ready ร—3), the errors, the warning, and the head and tail. The quiet middle becomes a single omission line.

The receipt tells you which path was taken:

  • passthrough โ€” input was already small
  • press โ€” mechanical trim
  • gloss โ€” model summary
  • cache โ€” a gloss reused for the same text and model

Setting it up with your agent (MCP)

Build first (npm run build), then point your client at node /absolute/path/to/ctxtrim/dist/cli.js serve.

Claude Code

Project .mcp.json (or your user config):

{
  "mcpServers": {
    "ctxtrim": {
      "command": "node",
      "args": ["/absolute/path/to/ctxtrim/dist/cli.js", "serve"],
      "env": {
        "CTXTRIM_API_KEY": "your-key",
        "CTXTRIM_BASE_URL": "https://atmorouter.dev/v1",
        "CTXTRIM_MODEL": "zai/glm-5.3"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Or from a shell:

claude mcp add ctxtrim --env CTXTRIM_API_KEY=your-key -- node /absolute/path/to/ctxtrim/dist/cli.js serve
Enter fullscreen mode Exit fullscreen mode

Cursor

Put the same mcpServers.ctxtrim block in .cursor/mcp.json in your project, or add it via Cursor Settings โ†’ MCP.

Cline

Add the same entry to Cline's MCP settings file:

  • Linux: ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
  • macOS: ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
  • Windows: %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json

(VS Code Insiders and other forks use their own User/globalStorage directory.) Cline picks up the file on save.

The tools your agent gets

Tool Use it when
shrink A tool result is too large to keep. mode is auto (default), gloss, or offline.
note_add You decided something, found a cause, or have a next step.
note_list You only want the current notes.
note_clear The session is finished.
recall You need the stats, the rolled-up digest, and the notes.

After restarting the MCP client, the practical habit is: ask the agent to call shrink on bulky tool output, and recall at the start of a long task. You can bake that into your CLAUDE.md, Cursor rules, or Cline custom instructions.

How a shrink works

  1. Detect. auto classifies the input as a log, a test run, a file dump (source or JSON), or generic text. Override with --kind.
  2. Press. Duplicate lines collapse. Logs keep errors, warnings, and the head and tail. Test output keeps failures, nearby frames, and the summary. Source code keeps an outline of imports and declarations. JSON becomes a short description of its shape. Anything still huge is cut to a character cap.
  3. Gloss. If the press is still large and CTXTRIM_API_KEY is set, ctxtrim sends the press โ€” not the original blob โ€” to POST {CTXTRIM_BASE_URL}/chat/completions and asks for a plain-text brief under CTXTRIM_TARGET_CHARS. A gloss that comes back empty or longer than the press is thrown away. Results are cached under CTXTRIM_HOME.

A few behaviors worth knowing:

  • --offline never calls a model.
  • --gloss requires a key and exits with status 2 if it's missing.
  • Provider errors fall back to the press, unless you explicitly asked for --gloss.
  • Binary input isn't expanded; you get the original size and a one-line stub.

The idea behind the split is simple: the mechanical press is free and deterministic, and the cheap model only ever sees already-trimmed text, so the summary step stays much cheaper than having your main coding model read the raw output.

The session ledger

Notes are stored per session as JSON under $CTXTRIM_HOME/sessions/ (default ~/.ctxtrim), one compact line per note, with optional tags like decision, blocker, or a filename stem. When active notes exceed CTXTRIM_NOTE_BUDGET, the oldest lines roll up into a digest so the session stays small enough to paste back into a prompt.

recall prints shrink totals (bytes in, bytes out, how much was kept), the digest, and current notes. Note that the shrunk brief itself isn't copied into the ledger โ€” if you want the agent to remember a conclusion, write a note.

Tuning

Everything is an environment variable. The main knobs:

Variable Default Role
CTXTRIM_PASSTHROUGH_CHARS 2000 Return text as-is at or below this size
CTXTRIM_GLOSS_THRESHOLD 3500 Gloss when the press is still at least this long
CTXTRIM_TARGET_CHARS 1800 Budget given to the model
CTXTRIM_PRESS_CHARS 12000 Cap on the press (what the model sees)
CTXTRIM_NOTE_BUDGET 4000 Active note budget before rollup

Use it as a library

import { loadConfig, shrink, Ledger } from "ctxtrim";

const result = await shrink(logText, { env: process.env, kind: "log" });
const ledger = new Ledger(loadConfig().home);
await ledger.add("feature-auth", "Refresh flake is a clock skew, not a bad signature.");
Enter fullscreen mode Exit fullscreen mode

Credit

The concept is inspired by context-mode by mksglu. ctxtrim is a separate project and is not affiliated with it. If you're exploring this space, check out context-mode too.

About AtmoRouter: a cheap AI API gateway and OpenRouter alternative

Out of the box, ctxtrim sends glosses to AtmoRouter using zai/glm-5.3, which fits the "keep the summary step cheaper than the coding model" idea. Any OpenAI-compatible endpoint works too. AtmoRouter is a cheap AI API gateway that speaks both the OpenAI Chat Completions API and the Anthropic Messages API, so one key reaches 100+ models from Cursor, Cline, Claude Code, or any compatible client. It is pay-per-token with no subscription, accepts QRIS and USDC, and prices sit well below official rates: AtmoRouter advertises an average of 83.7% below official prices, which makes it an affordable LLM API and a practical OpenRouter alternative for agent workloads.

Try it

If your agent keeps drowning in test output and logs, give it a try:

๐Ÿ‘‰ github.com/bayuh85/ctxtrim

Feedback on the press heuristics is especially welcome โ€” if there's a log or test format it handles badly, an issue with a sample is the most useful thing you can send.

Top comments (0)