◉CLAUDE LABJP
●CODE 2.1.322 — Fast mode now works on Vertex in the global, us and eu regions (11/10), falling back to standard speed if refused●FABLE 5.2 — Claude Code 2.1.321 makes Fable 5.2 the default Fable model: 1M context at $8/$40 per Mtok●MANAGED — Self-hosted Claude Managed Agents is out (11/12): you run the whole service yourself●SONNET 4.5 — Retires on the Claude API on 11/30, 17 days left. Migrate to Sonnet 5.5●MCP APPS — Users report local MCP app views no longer showing in Chat after a desktop update●NEW — Cowork or Claude Code First, With No Terminal Experience●CODE 2.1.322 — Fast mode now works on Vertex in the global, us and eu regions (11/10), falling back to standard speed if refused●FABLE 5.2 — Claude Code 2.1.321 makes Fable 5.2 the default Fable model: 1M context at $8/$40 per Mtok●MANAGED — Self-hosted Claude Managed Agents is out (11/12): you run the whole service yourself●SONNET 4.5 — Retires on the Claude API on 11/30, 17 days left. Migrate to Sonnet 5.5●MCP APPS — Users report local MCP app views no longer showing in Chat after a desktop update●NEW — Cowork or Claude Code First, With No Terminal Experience
Articles/Claude Code
⟐ Claude Code/2026-08-14Advanced

One generated file outweighed all 70 hand-written source files, so I redrew what Claude Code may read

I profiled a repository I actually run to see which areas consume the most context. Here is what I found, the deny rules I settled on, how search selectivity changes the math, and the options I considered but rejected.

Claude Code284settings.json10context12cost design4repository operations

✦ Premium Article

"Which file decides where the preview gets cut off?" I asked Claude Code that about a site I run.

The answer came back correct — two files, precisely identified. What surprised me was the volume read along the way. It went well past what I had pictured when I said "just go find it."

As an indie developer working alone, there is nobody to point out that kind of friction. So before changing how I explore, I decided to measure the repository itself. You cannot decide what to exclude until you know what weighs what.

The finding that stopped me: a single generated file — one that gets rebuilt on every build — outweighed all seventy hand-written source files combined.

Measure the repository before the session starts

The subject is the claudelab.net repository: a Next.js site holding 806 Japanese and 806 English articles as MDX, with 1,796 files under version control.

I started with a small script that ranks areas by "how heavy is this to read." There is no need to replicate a tokenizer exactly. As long as areas can be compared against each other, the decision is the same. Japanese characters count as roughly one token each; everything else as roughly 3.6 characters per token.

#!/usr/bin/env python3
"""ctxweight.py - rank version-controlled files by how heavy they are to read.
 
Usage:
    python3 ctxweight.py            # whole repository
    python3 ctxweight.py src        # one directory
"""
import collections
import subprocess
import sys
 
PRICE_PER_MTOK = 2.0  # input price in USD per 1M tokens; substitute your own model's
 
 
def estimate_tokens(text: str) -> int:
    """Approximate CJK at ~1 char per token and everything else at ~3.6 chars.
 
    Exact counts depend on the model's tokenizer, so treat this as a relative
    measure -- "area A is N times area B" -- rather than an absolute figure.
    """
    cjk = sum(1 for ch in text if ' ' <= ch <= '鿿' or '＀' <= ch <= '￯')
    return int(cjk + (len(text) - cjk) / 3.6)
 
 
def tracked_files(roots):
    # git ls-files means .gitignore'd paths drop out without a hand-kept list
    out = subprocess.check_output(['git', 'ls-files', '-z'] + list(roots)).decode()
    return [f for f in out.split('\0') if f]
 
 
def group(path: str) -> str:
    parts = path.split('/')
    return '/'.join(parts[:2]) if len(parts) > 1 else '(root)'
 
 
def main():
    agg = collections.defaultdict(lambda: [0, 0])  # [tokens, files]
    per_file = []
 
    for path in tracked_files(sys.argv[1:]):
        try:
            text = open(path, encoding='utf-8').read()
        except (UnicodeDecodeError, OSError):
            continue  # binaries such as images are out of scope; skip without noise
        tokens = estimate_tokens(text)
        agg[group(path)][0] += tokens
        agg[group(path)][1] += 1
        per_file.append((tokens, path))
 
    total = sum(v[0] for v in agg.values()) or 1
    print(f"{'area':<28}{'est. tokens':>12}{'files':>7}{'share':>8}{'$/read':>9}")
    for key, (tokens, files) in sorted(agg.items(), key=lambda x: -x[1][0])[:15]:
        print(f"{key:<28}{tokens:>12,}{files:>7,}"
              f"{tokens / total * 100:>7.1f}%{tokens / 1e6 * PRICE_PER_MTOK:>9.3f}")
    print(f"{'total':<28}{total:>12,}"
          f"{sum(v[1] for v in agg.values()):>7,}{100.0:>7.1f}%"
          f"{total / 1e6 * PRICE_PER_MTOK:>9.3f}")
 
    print("\n-- heaviest individual files --")
    for tokens, path in sorted(per_file, reverse=True)[:5]:
        print(f"{tokens:>12,}  {path}")
 
 
if __name__ == '__main__':
    main()

Using git ls-files matters more than it looks. It drops node_modules and .next for free, and a hand-maintained exclusion list always drifts out of date.

Run against the whole repository:

AreaEst. tokensFilesShare
content/articles7,666,9271,61291.0%
src/generated289,60323.4%
repository root162,418151.9%
content/blog157,421701.9%
src/app80,278361.0%
src/components37,332220.4%
Total8,422,3231,796100%

Article bodies taking 91% is unremarkable for a content site. That line matched my expectations exactly.

The next line did not.

A generated file weighed 2.2 times all the code I wrote by hand

Narrow the scope to src and the distortion becomes obvious.

AreaEst. tokensFilesShare of src
src/generated289,603268.9%
src/app80,2783619.1%
src/components37,332228.9%
src/config8,11521.9%
src/lib and others5,029101.2%

All seventy hand-written source files together come to 130,754 tokens. src/generated reaches 289,603 across two files, with articles.json alone accounting for 284,072. A ratio of 2.21.

That file is metadata assembled mechanically from the MDX sources and rewritten on every build. Reading it adds no information whatsoever. Every fact inside it already lives under content/.

Yet in any exploration scoped to src, it sits right at the front of the queue. The filename says articles.json and the path says src, and both read as important. Files that look central by name and location are sometimes the derived ones.

Converted to input price, those two files cost about USD 0.58 per read. Reading every hand-written file costs USD 0.26. The side carrying zero new information was more than twice as expensive.

Prices move, of course, with contracts and model revisions. Replace PRICE_PER_MTOK with a figure you have checked yourself rather than trusting a number in an article.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦You will be able to pinpoint which parts of your own repository eat context, using a single command, and decide what to exclude with evidence behind it
✦You will be able to avoid the failure where generated artifacts quietly enter exploration, inflating input cost and muddying reasoning at the same time
✦You will be able to recognise the cases where a poorly scoped search costs more than simply reading the files
✦You will know when a per-area rollup hides a single heavy file, and keep the rollup and the single-file ranking side by side
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⟐ Claude Code2026-10-20
You Stepped Away and Came Back to a Summary — Spotting Idle Compaction and Keeping the Details You Can't Lose
Since v2.1.299, Claude Code compacts a long conversation after about 54 idle minutes. Here is how to tell it happened, how to tune or switch it off, and how to move the assumptions you can't afford to lose into CLAUDE.md.
⟐ Claude Code2026-10-19
If You Wrote MCP Allow Rules as Just `mcp__servername` — After v2.1.299, Only Servers With `__` in Their Names Need a Recheck
Claude Code v2.1.299 changed MCP allow rules to match only the server they name. A bare mcp__servername is affected only when the server name contains __ or ends in _. Includes a short audit script.
⟐ Claude Code2026-08-27
Curating the /model picker with modelPicker, and what replacing the lineup hides
Claude Code v2.1.242 added modelPicker, which lets you write the /model lineup yourself. Here is how appending differs from replacing, why project settings are ignored, and where it quietly narrows what availableModels allows.