RA
RenovateAPIEngineering Hub
AI Agents & Context Engineering

Why Your Coding Agent Chokes on Big Repos (And How AST Compression Fixes It)

Raw file dumps blow up token costs and wreck reasoning in agentic coding tools. Here's how AST signature stripping and dependency-aware context loading cut repo context by 90% or more.

AAbhishek6 min read
AI Agents & Context Engineering6 min read

Why Your Coding Agent Chokes on Big Repos (And How AST Compression Fixes It)

RENOVATEAPI ARCHITECTURAL SPEC
CANONICAL GUIDE

Point a coding agent at a real enterprise repo and it falls over fast. Not because the model is dumb — because you just handed it 100k tokens of file dumps and asked it to find one bug in the middle of it.

That's the actual failure mode behind most "my agent went off the rails on a big codebase" complaints. It's not a reasoning problem. It's a context problem.

The three ways full-file context kills agentic coding

Load twenty or thirty complete TypeScript files into a Claude Code session (or Cursor, or any custom agentic CLI) and you get hit with three compounding issues at once.

First, cost and latency. A 100k+ token context isn't just slow — it's expensive on every single turn of an agentic loop, and agentic loops run a lot of turns.

Second, "lost in the middle" degradation. The bigger the context window fills up, the harder it gets for the model to hold onto the one architectural constraint or the one function signature that actually matters. Everything else is noise competing for attention.

Third, instruction drift. Your system prompt, your project rules, the constraint you carefully wrote about how error handling should work in this codebase — all of it gets pushed further from the model's effective attention span as file dumps pile up. I've watched agents quietly ignore a rule that was sitting right there in the prompt, simply because 80k tokens of source code sat between the rule and the current task.

None of this is solved by "just use a bigger context window." Bigger windows make the dilution problem worse, not better — more tokens for the signal to get lost in.

The fix: stop feeding the model source code it doesn't need yet

The answer isn't to shrink the codebase. It's to change what "context" means at each stage of the agent's work. Most of a file's tokens are implementation detail the model doesn't need until it's actually editing that exact function. Everything before that point only needs to know the shape of the code — its types, its exported functions, its public surface.

That's the whole idea behind a three-tier context pipeline:

Tier What it contains Rough size
Tier 1 — Symbol & File Topology Directory tree + exported symbols only ~1,500 tokens
Tier 2 — AST Interface Skeleton Types, interfaces, function headers; bodies stripped ~8,000 tokens
Tier 3 — Focused Implementation Full source of the 1–2 target functions only ~2,000 tokens

Instead of loading everything at full resolution, the agent starts at Tier 1, narrows down to the relevant files at Tier 2, and only pulls full implementation (Tier 3) for the exact function it's about to touch. Everything else in the codebase stays compressed the entire time.

Stripping function bodies with the TypeScript Compiler API

The mechanism that makes Tier 2 possible is straightforward AST walking. Using the TypeScript Compiler API, you can preserve interfaces and type aliases wholesale (they're already compact and the model needs the full contract), while collapsing classes and functions down to their signatures:

// src/agent/ast-compressor.ts
import * as ts from 'typescript';

export function compressTypeScriptFile(sourceCode: string): string {
  const sourceFile = ts.createSourceFile(
    'temp.ts',
    sourceCode,
    ts.ScriptTarget.Latest,
    true
  );

  const compressedLines: string[] = [];

  function visit(node: ts.Node) {
    // Interfaces and type aliases: keep as-is, they're the contract
    if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
      compressedLines.push(node.getText(sourceFile));
      return;
    }

    // Classes: keep property types and method signatures, drop bodies
    if (ts.isClassDeclaration(node)) {
      const className = node.name?.text ?? 'AnonymousClass';
      const members: string[] = [];

      for (const member of node.members) {
        if (ts.isPropertyDeclaration(member)) {
          members.push(`  ${member.getText(sourceFile)};`);
        } else if (ts.isMethodDeclaration(member)) {
          const methodName = member.name.getText(sourceFile);
          const params = member.parameters.map((p) => p.getText(sourceFile)).join(', ');
          const returnType = member.type ? member.type.getText(sourceFile) : 'any';
          members.push(`  ${methodName}(${params}): ${returnType}; // body stripped`);
        }
      }

      compressedLines.push(`export class ${className} {\n${members.join('\n')}\n}`);
      return;
    }

    // Standalone functions: collapse to signature only
    if (ts.isFunctionDeclaration(node) && node.name) {
      const fnName = node.name.text;
      const params = node.parameters.map((p) => p.getText(sourceFile)).join(', ');
      const returnType = node.type ? node.type.getText(sourceFile) : 'void';
      compressedLines.push(`export function ${fnName}(${params}): ${returnType};`);
      return;
    }

    ts.forEachChild(node, visit);
  }

  visit(sourceFile);
  return compressedLines.join('\n\n');
}

Run this against a 600-line service file and you're typically looking at a drop from around 4,500 tokens down to roughly 320 — a 92% reduction — and every type reference the model needs to reason about the file's public API is still intact. What's gone is exactly the stuff that doesn't matter until you're inside that function: loop bodies, internal error handling, logging calls.

Expanding back out, but only where it counts

Compression only helps if the agent can still get the real code when it needs it. The flow looks like this:

  1. Load Tier 2 (AST skeleton) for the target file and everything it imports directly.
  2. Let the agent identify the specific function it needs to modify — say, processPaymentOrder().
  3. Expand just that function to full Tier 3 implementation. Everything around it stays compressed.

The rest of the codebase never leaves its compressed form. You're not trading completeness for speed here — the agent can still see the full public surface of every related file, it just doesn't see implementation noise it has no immediate use for.

How this stacks up against naive RAG chunking

Keyword-based RAG chunking is the other common answer to "my context is too big," and it's worth being direct about why it's the weaker option for code specifically.

Strategy Token cost (50 files) "Lost in the middle" risk Reasoning precision
Raw whole-file ingestion 120,000+ tokens Extreme Low — diluted context
Naive keyword RAG chunking 15,000 tokens Moderate Moderate — broken syntax chunks
Hierarchical AST compression 6,000–12,000 tokens Minimal High — >95% syntax validity

Keyword chunking gets you real token savings, but it chunks on the wrong boundary. Splitting on keyword proximity instead of syntactic structure means you can easily cut a function in half, or hand the model a class body with no idea what class it belongs to. AST compression avoids this by construction — you're never chunking mid-syntax, because you're compressing along the tree, not slicing the text.

The tradeoff worth naming

This isn't free. Building and maintaining an AST compression layer is real engineering work — Tree-sitter or the TypeScript Compiler API, a caching layer so you're not re-parsing unchanged files on every turn, and some logic for deciding when a "related file" is related enough to include at Tier 2. For a small side project touching four files, none of this is worth building. For an agent working across a repo with hundreds of files, it's close to mandatory — the alternative is an agent that either can't fit the context it needs or reasons badly once it does.

If you're building or extending an agentic coding tool and you're not already doing something like this, the token cost of raw file ingestion is the first place to look before reaching for a bigger model or a longer context window.

RenovateAPI Engineering Suite

Accelerate your AI Agents & Context Engineering Modernization Roadmap

Need custom architecture auditing, automated OpenAPI contract generation, or zero-downtime microservice migration guidance for your engineering team?

Weekly Engineering Dispatch

Subscribe to RenovateAPI

Get weekly architectural guides, API refactoring strategies, and technical SEO updates delivered directly to your inbox.

Discussion (2)

A
Alex Rivera
2 hours ago

Extremely helpful breakdown of the Strangler Fig pattern! We're currently refactoring a legacy Java monolith at work and the OpenAPI gateway routing tips saved us weeks of experimentation.

S
Sophia Chen
1 day ago

The schema JSON-LD and FAQ block structure really helps with indexing. Great technical detail on entity mentions too.

Suggested Related Articles