DEV Community

ithiria894
ithiria894

Posted on • Originally published at dev.to

My Claude Code Sessions Hit 70MB. So I Built a Distiller.

I had a 4-hour coding session with Claude Code. Felt productive. Fixed a bunch of bugs, refactored a module, reviewed some screenshots Claude took of the UI along the way.

Then I tried to --resume it the next day.

The session file was 73MB. Claude loaded it, burned through half the context window on old tool outputs and base64-encoded screenshots from yesterday, and started forgetting things I'd said 20 minutes ago. The conversation was fine. The cargo it was dragging around was not.

I opened the JSONL. Here's what 73MB of "session" actually looks like:

Conversation text:          ~4MB  (what we actually said)
Tool results (Read):       ~28MB  (file contents Claude already read)
Tool results (Bash):        ~9MB  (build outputs, test runs, logs)
Base64 screenshots:        ~22MB  (UI screenshots, now stale)
Tool results (Edit/Write):  ~6MB  (diffs and file previews)
Everything else:            ~4MB  (metadata, tool_use blocks)
Enter fullscreen mode Exit fullscreen mode

93% of the file is stuff Claude doesn't need to resume the conversation. The Read results are files that still exist on disk. The screenshots are from yesterday's UI state. The Bash outputs are build logs from 6 hours ago.

So I built a distiller.

What Session Distiller Does

It reads a session JSONL, keeps every word of the actual conversation verbatim, and applies per-tool-type rules to strip results down to what's useful for context:

Tool type What's kept Why
Read Nothing (stripped entirely) The file is still on disk. Claude can re-read it.
Bash First 5 + last 5 lines You need the command and whether it succeeded. Not 800 lines of webpack output.
Edit File path + 200-char preview of old/new Enough to remember what changed.
Write File path + head/tail preview Same idea.
Agent Up to 2000 chars Research reports are worth keeping. Build logs aren't.

The key decision was extractive filtering, not summarization. I don't pass anything through an LLM. Every word of conversation text is preserved exactly as-is. Tool results are either kept (trimmed) or dropped based on deterministic rules. No tokens spent, no hallucination risk, no "the AI summarized away the one detail I needed."

Typical result: 70MB session → 7MB distilled. 90% reduction.

The original session is backed up before anything changes. You always have the full version if you need it.

The Tool-ID Matching Problem

This sounds simple until you hit parallel tool calls.

Claude Code often fires multiple tool calls in a single assistant message. A tool_result block references its parent by tool_use_id, not by position. My first implementation tracked a global lastToolName variable: "the most recent tool_use was a Read, so the next tool_result must be a Read result." That breaks immediately when an assistant message contains three parallel tool calls.

The fix: build a toolIdMap from every tool_use block (mapping id → tool name), then look up each tool_result.tool_use_id to find the correct tool type. Now parallel calls work correctly. A Read result and a Bash result in the same message get their own distillation rules applied independently.

// Build map: tool_use_id → tool name
if (block?.type === "tool_use" && block.id && block.name) {
  toolIdMap.set(block.id, block.name);
}

// Look up correct tool for each result
if (block?.type === "tool_result") {
  const toolName = toolIdMap.get(block.tool_use_id);
  // Now we know: this result came from "Read", "Bash", etc.
  return distillByToolType(toolName, block);
}
Enter fullscreen mode Exit fullscreen mode

Small detail. Would have caused silent data corruption without it.

Image Trimmer: The Targeted Fix

Sometimes you don't need full distillation. You just need to remove the screenshots.

I kept hitting Claude Code's "image exceeds dimension limit" warning after long sessions with a lot of UI review. The session file was fine except for 20-30MB of base64 image data that Claude couldn't even display anymore.

So I wrote a separate tool that does exactly one thing: find every image block in the JSONL, replace it with [image redacted], leave everything else untouched.

node src/trim-images.mjs ~/.claude/projects/.../session.jsonl
# → Redacted 47 image(s), saved 24832K
Enter fullscreen mode Exit fullscreen mode

It also handles images nested inside tool_result blocks (which is where most screenshots end up, since they come back as results of Bash commands that ran adb screencap or similar).

The whole script is 35 lines. It's also available as a Claude Code skill: type /trim-images when you see the dimension warning and it runs automatically.

How to Use It

From the dashboard:

If you're using Claude Code Organizer, every session row now has a Distill button. Click it, the session gets distilled in-place, and the result shows up as an expandable bundle in the tree view with the backup and index files grouped together.

From the command line:

# Full distillation (conversation + trimmed tool results + backup)
npx @mcpware/claude-code-organizer --distill ~/.claude/projects/.../session.jsonl

# Just strip images
node src/trim-images.mjs ~/.claude/projects/.../session.jsonl
Enter fullscreen mode Exit fullscreen mode

The distiller outputs stats showing before/after sizes, number of index entries, and where the backup landed.

What's Actually in the Backup

The distiller creates a folder named after the session ID:

{sessionId}/
  backup-{originalId}.jsonl    ← full original session, untouched
  index.md                     ← summary of what was kept/stripped
Enter fullscreen mode Exit fullscreen mode

The distilled session gets a context message injected at the top telling Claude where the backup lives and how to retrieve specific tool results if needed (Read with offset). So if Claude needs the full output of a Bash command from 3 hours ago, it knows exactly where to look.

Performance

Distillation runs in under 2 seconds on a 70MB file. It's pure JSON parsing and string manipulation. No LLM calls, no network, no dependencies.

The backup doubles your disk usage temporarily, but if your session was 70MB and the distilled version is 7MB, you're at 77MB total instead of 70MB. Not a meaningful difference on any modern machine.

The context window savings are the real win. A 70MB session dumps roughly 15-20M tokens of tool output into Claude's context when resumed. After distillation, that drops to 1-2M tokens of actual conversation. Claude remembers what you talked about instead of drowning in stale build logs.

Try It

GitHub logo mcpware / claude-code-organizer

Dashboard to manage Claude Code memories, configs, and MCP servers — security scanner for tool poisoning, context token budget tracker, duplicate cleanup, scope management. npx @mcpware/claude-code-organizer

Claude Code Organizer

AI agents: read AI_INDEX.md first. It is the navigation manifest for this codebase — where to find every module, how they connect, and where to look before making any claim about the code.

npm version npm downloads GitHub stars GitHub forks License: MIT Node.js Tests Zero Telemetry MCP Security Awesome MCP Verified Against CC Source English | 简体中文 | 繁體中文 | 廣東話 | 日本語 | 한국어 | Español | Bahasa Indonesia | Italiano | Português | Türkçe | Tiếng Việt | ไทย

Claude Code Organizer (CCO) is a free, open-source dashboard that lets you manage all Claude Code configuration — memories, skills, MCP servers, settings, agents, rules, and hooks — across global and project scopes. It includes a security scanner for MCP tool poisoning and prompt injection, a per-item context token budget tracker, per-project MCP enable/disable controls, and bulk cleanup for duplicate configs. All without leaving the window.

v0.17.0 — Session Distiller strips bloated sessions down to ~10% of their original size while keeping every word of conversation intact…

The distiller is part of CCO v0.17.0. Dashboard button, CLI flag, and API endpoint all included. Image trimmer works standalone or as a /trim-images skill.

If your sessions are small, you don't need this. If your sessions regularly push 50MB+, this is the difference between "--resume working" and "--resume followed by Claude forgetting your name."

About Me

CS dropout. Building tools for the Claude Code ecosystem. github.com/ithiria894

Star the repo if bloated sessions have ever ruined your day.

Top comments (0)