A TypeScript CLI + library for building LLM-ready context bundles from a local repository.
Large codebases easily exceed model context limits. Manual copy/paste is noisy and often misses the most relevant files. context-trimmer scans your project, ranks files against a task query, estimates token usage, and emits the best-fit bundle under a strict budget — so the model sees what matters, not just what's largest.
Built for engineers working with AI pair programmers who want deterministic, reproducible context — not guesswork.
npx context-trimmer --query "add auth middleware" --budget 32000 --out context.mdStream to stdout:
npx context-trimmer --query "fix cache invalidation bug" --budget 16000Each file receives a composite relevance score based on three signals:
- Query match — TF-IDF style frequency of query terms within the file content, normalised by file length
- Recency — files modified more recently (via
git log) score higher, on the assumption that active files are more relevant - File type priority — configurable weighting by extension (e.g.
.ts>.json>.lock)
Files are then selected greedily by score until the token budget is exhausted.
- The CLI uses metadata-first scanning (
includeContent: false) to reduce peak memory during directory traversal. - Content is loaded lazily during ranking/bundling only when needed.
- Recency scoring uses batched git history parsing with filesystem timestamp fallback.
| Option | Type | Default | Description |
|---|---|---|---|
--query |
string |
required | Task description used for relevance ranking |
--budget |
number |
32000 |
Max estimated tokens for the output bundle |
--dir |
string |
current working dir | Root directory to scan |
--out |
string |
none (stdout) | Output markdown file path |
--extensions |
string[] |
ts tsx js jsx json md |
Extension whitelist |
--tokenizer |
"char4" | "tiktoken" |
char4 |
Token estimator strategy |
--model |
string |
gpt-4o-mini |
Tiktoken model encoding (only used with tiktoken) |
Ignore behaviour:
- Respects
.gitignore - Respects
.trimmerignore(same syntax as.gitignore) - Always ignores
node_modules,.git, anddist
# Context Bundle
- Files included: 3
- Tokens used: 18,542
- Files skipped due to budget: 9
- Files partially included: 1
---
## src/auth/middleware.ts
- Estimated tokens: 1,460
- Score: 0.812
\`\`\`ts
import { Request, Response, NextFunction } from "express";
import { verifyToken } from "./token";
export function authMiddleware(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.split(" ")[1];
if (!token || !verifyToken(token)) {
return res.status(401).json({ error: "Unauthorized" });
}
next();
}
\`\`\`
---
## src/auth/token.ts
- Estimated tokens: 890
- Score: 0.761
\`\`\`ts
// ... file content continues
\`\`\`import {
scanFiles,
rankFiles,
createTokenizer,
buildBundle,
formatBundleMarkdown,
} from "context-trimmer";
const files = await scanFiles({ rootDir: process.cwd() });
// For large repos, prefer metadata-first scanning:
// const files = await scanFiles({ rootDir: process.cwd(), includeContent: false });
const ranked = await rankFiles(files, {
query: "add auth middleware",
rootDir: process.cwd(),
});
const tokenizer = await createTokenizer({ mode: "char4" });
const bundle = await buildBundle(ranked, {
tokenBudget: 32000,
tokenizer,
});
console.log(formatBundleMarkdown(bundle, process.cwd()));When a file is too large for remaining budget, buildBundle() can include it in chunks (maxChunkTokens), using syntax-aware boundary heuristics:
- prefers breaks at blank lines
- prefers breaks near closing braces (
}/};) - prefers boundaries before declarations (
export,class,function,interface,type, etc.)
This improves readability vs pure fixed-size splitting while still honoring token limits.
npm install
npm test # Vitest
npm run build # tscOpen an issue before starting large changes — alignment on scope saves time for everyone.
Pull requests should:
- Be scoped to a single concern
- Include Vitest tests for any changed behaviour
- Pass
npm test && npm run buildbefore submission - Follow the existing ESLint config (
npm run lint)
Code style is enforced via ESLint + Prettier. The project targets Node.js 18+ and TypeScript strict mode throughout — no any, no suppressions without a comment explaining why.