Three modules decided what Claude reads, what you share, and what you believe about your spend — and none of them had a single test. Writing those tests found four defects. That is the release.
Exported HTML reports executed hostile file names
src/export.js opens by describing itself as exporting reports "for sharing or archiving", and interpolated file and project names straight into markup:
`<td><code>${basename(f.fullPath)}</code></td>`
labels: ${JSON.stringify(projects.map(([name]) => name))} // inside <script>JSON.stringify does not escape </script>. Both holes were confirmed with a proof-of-concept report before anything was changed:
--- injected filename lands raw in the DOM? ---
<img src=x onerror=alert('filename')>
--- script breakout? ---
</script><script>alert('project-name')</script>
Reaching this means opening a repo whose file or directory names are hostile, exporting a report, and sending it to someone — an ordinary sequence for an artifact whose stated purpose is being shared. Names now pass through escapeHtml, jsonForScript and escapeMarkdownCell, and every in-<script> payload is routed through the helper so a later edit cannot quietly reopen it.
/cco-pack could propose another project's source
findHistoricallyUseful folds in the _global patterns bucket unconditionally. That bucket spans every project on the machine and stores absolute paths — so a file from an unrelated repo exists on disk, clears every filter, and gets proposed as context for the project you are actually in. Measured against real data:
candidates before scope filter: 98
candidates after scope filter: 34
foreign files now excluded: 64
One of them ranked 8th by confidence, above most of the repo's own files. It stayed out of the pack only because the top-6 cut happened to be full. Candidates are now scoped to the tracked root that owns cwd, with boundary-aware containment — /a/bc is no longer "inside" /a/b.
/cco-roi credited tracked sessions for a number they did not produce
When sessions existed but none had recorded a file read — a fresh install, the same shape as #53 — the report projected from a hardcoded 35% waste while labelling itself N sessions (last 30 days), then built a full per-model dollar table on top of it. Now it says what it is:
Data source: industry estimate — 3 session(s) tracked, none with file reads yet
Average waste: 35.0% (assumed)
⚠ Everything below is a projection from those assumptions, not your
numbers. Re-run once CCO has tracked a few sessions with file reads.
Two claims were corrected while there: "What CCO Saves You / tokens blocked/deduplicated" became "What CCO Can Save You / tokens identified as waste", and "CCO makes your context budget 1.5x more effective" became "Cutting that waste makes...". CCO reports the waste. It does not remove it.
Smaller, but the same theme
/cco-packprintedNaN%wheneverpackBudgetPercentwas configured to0(#39). It now prints no percentage rather than a number it cannot defend.export.jsandroi.jsran their CLI at import time — importingexport.jswrote a report into the user's exports directory. Caught when the new tests did exactly that. Both guarded with theisMainModulehelper this repo already added for the purpose.- Markdown cell escaping handled
|before\, soa\|bsplit the cell anyway (CodeQLjs/incomplete-sanitization). Backslashes go first.
Tests
275 → 296. Path scoping and boundary containment, candidate source priority, relevance caps, budget arithmetic and the zero-cap path, escaping across all three output formats, ROI provenance and fallbacks, and no NaN/undefined in any generated table cell.
Full changelog: https://github.com/egorfedorov/claude-context-optimizer/blob/main/CHANGELOG.md