@mkhitar99/context-pruner
v1.0.1
Published
AST-based codebase pruner for LLM context — strip implementation, keep architecture, reduce tokens by up to 80%
Maintainers
Readme
@mkhitar99/context-pruner
AST-based codebase pruner for LLM context — strip implementation details, keep architecture, reduce tokens by up to 80%.
Philosophy
Give the AI the Skeleton, not the Meat, to maximize reasoning efficiency.
Instead of pasting one file at a time into your LLM chat, prune your entire project into a structured, token-efficient snapshot — with every class, function, and type still visible by signature.
Table of Contents
- Installation
- Quick Start
- CLI Reference
- Configuration File
- Depth Levels
- Output Formats
- Programmatic API
- Ignore Files
- How It Works
- Examples
- Changelog
Features
- AST Parsing — Uses
ts-morphfor safe, syntax-aware code transformation - Function Body Stripping — Replaces implementations with
/* logic */ - Semantic Pointers — Every definition includes
filepath:linefor on-demand retrieval - Token Budget — Set a target token count; the pruner adjusts aggressively to meet it
- Depth Map — Configure
full,skeleton, ornoneper directory pattern - XML & Markdown — Output formats optimized for LLM consumption
- gitignore-aware — Respects
.gitignoreand.prunerignore
Installation
# Global install (recommended for CLI usage)
npm install -g @mkhitar99/context-pruner
# Local install (for programmatic API or npm scripts)
npm install --save-dev @mkhitar99/context-prunerQuick Start
# 1. Initialize a config file in your project
context-pruner init
# 2. Prune your project (outputs CONTEXT.xml by default)
context-pruner prune
# 3. Paste CONTEXT.xml into Claude, ChatGPT, or any LLMCLI Reference
init
Generates a pruner.config.json in the current directory with sensible defaults.
context-pruner initOptions:
| Flag | Description |
|------|-------------|
| --format <xml\|markdown> | Default output format to use in the generated config (default: xml) |
| --budget <number> | Default token budget to write into the config (default: 8000) |
Examples:
# Generate default config
context-pruner init
# Generate config pre-set for markdown and a tighter budget
context-pruner init --format markdown --budget 4000prune
Crawls a directory, parses TypeScript files, and emits a pruned context snapshot.
context-pruner prune [path] [options][path] defaults to . (current directory) if omitted.
Options:
| Flag | Alias | Description | Default |
|------|-------|-------------|---------|
| --format <xml\|markdown> | -f | Output format | xml |
| --output <file> | -o | Output file path. Use - for stdout | CONTEXT.xml |
| --budget <number> | -b | Target token count. Pruner aggressively strips to meet this | none |
| --config <file> | -c | Path to a pruner.config.json config file | none |
| --depth <full\|skeleton\|none> | -d | Override depth level for all files (ignores depthMap) | none |
| --preserve-types | | Keep all interface, type, and enum declarations even in skeleton mode | false |
| --no-pointers | | Omit the pointer index from output | false |
| --quiet | -q | Suppress progress output | false |
Examples:
# Prune current directory, default settings (XML to CONTEXT.xml)
context-pruner prune
# Prune a specific subdirectory
context-pruner prune ./src
# Output as Markdown
context-pruner prune ./src --format markdown --output CONTEXT.md
# Enforce a 5000-token budget
context-pruner prune ./src --budget 5000
# Skeleton-only pass on everything (ignore depthMap)
context-pruner prune ./src --depth skeleton
# Use a config file
context-pruner prune . --config pruner.config.json
# Combine: config file + override output path
context-pruner prune . --config pruner.config.json --output MY-CONTEXT.xml
# Print to stdout instead of a file (useful for piping)
context-pruner prune ./src --output -
# Pipe into pbcopy (macOS clipboard)
context-pruner prune ./src --output - | pbcopy
# Keep all types even in skeleton directories
context-pruner prune ./src --depth skeleton --preserve-types
# Quiet mode (no progress logs — good for CI)
context-pruner prune . --quiet --output CONTEXT.xmlConfiguration File
Create pruner.config.json in your project root (or generate it with context-pruner init):
{
"outputFormat": "xml",
"tokenBudget": 8000,
"depthMap": {
"src/core/**": "full",
"src/utils/**": "skeleton",
"src/models/**": "full",
"src/services/**": "skeleton",
"src/api/**": "skeleton",
"tests/**": "none",
"**/*.test.ts": "none",
"**/*.spec.ts": "none"
},
"preserveTypes": true,
"includeDevDependencies": false
}All Config Fields
| Field | Type | Description | Default |
|-------|------|-------------|---------|
| outputFormat | "xml" \| "markdown" | Output format | "xml" |
| tokenBudget | number | Target token count | none |
| depthMap | Record<string, "full" \| "skeleton" \| "none"> | Glob-pattern-to-depth mapping. Evaluated top-to-bottom; first match wins | {} |
| preserveTypes | boolean | Preserve all type declarations even in skeleton files | false |
| includeDevDependencies | boolean | Include devDependencies in output | false |
Glob matching uses standard glob patterns (
**,*,?). Patterns are matched against the file path relative to the pruned root.
Depth Levels
| Level | What is kept | What is stripped |
|-------|-------------|-----------------|
| full | Everything — complete source | Nothing |
| skeleton | Signatures, types, interfaces, constants, exports | Function bodies, method bodies, private variable initializers |
| none | Nothing | The entire file is excluded from output |
skeleton mode preserves:
- Function and method signatures (name, parameters, return type)
- Class declarations and their member signatures
interface,type, andenumdeclarations- Exported constants and their values
- Import statements
- JSDoc comments above declarations
skeleton mode strips:
- Function and method bodies → replaced with
/* logic */ - Private variable assignments
- Inline comments inside bodies
Output Formats
XML (default)
Structured and easy for LLMs to parse. Recommended for most use cases.
<?xml version="1.0" encoding="UTF-8"?>
<context tokenBudget="8000">
<pointers>
<pointer file="src/auth.ts" line="12" symbol="AuthService" />
<pointer file="src/auth.ts" line="20" symbol="AuthService.login" />
<pointer file="src/auth.ts" line="85" symbol="withAuth" />
</pointers>
<file path="src/auth.ts">
<![CDATA[
export class AuthService {
constructor(db: DatabaseClient, config: AuthConfig) { /* logic */ }
async login(email: string, password: string): Promise<AuthToken> { /* logic */ }
async register(email: string, password: string, name: string): Promise<User> { /* logic */ }
private generateToken(user: User): AuthToken { /* logic */ }
}
export function withAuth<T>(
handler: (user: User, ...args: any[]) => Promise<T>,
requiredPermission?: Permission
): (...args: any[]) => Promise<T> { /* logic */ }
]]>
</file>
</context>Markdown
Readable in any editor or Markdown viewer. Good for pasting into chat UIs.
## Pointer Index
- `AuthService` — src/auth.ts:12
- `AuthService.login` — src/auth.ts:20
- `withAuth` — src/auth.ts:85
## src/auth.ts
```typescript
export class AuthService {
constructor(db: DatabaseClient, config: AuthConfig) { /* logic */ }
async login(email: string, password: string): Promise<AuthToken> { /* logic */ }
}
---
## Programmatic API
```typescript
import { prune, PruneOptions } from '@mkhitar99/context-pruner';
const output = await prune('./my-project', {
outputFormat: 'markdown',
tokenBudget: 5000,
depthMap: {
'src/core/**': 'full',
'src/utils/**': 'skeleton',
'tests/**': 'none',
},
preserveTypes: true,
});
console.log(output);prune(rootPath, options?)
| Parameter | Type | Description |
|-----------|------|-------------|
| rootPath | string | Absolute or relative path to the directory to prune |
| options | PruneOptions | Optional. Same fields as pruner.config.json |
Returns: Promise<string> — the full pruned output as a string (XML or Markdown).
PruneOptions
interface PruneOptions {
outputFormat?: 'xml' | 'markdown';
tokenBudget?: number;
depthMap?: Record<string, 'full' | 'skeleton' | 'none'>;
preserveTypes?: boolean;
includeDevDependencies?: boolean;
noPointers?: boolean;
}Ignore Files
context-pruner respects two ignore files:
.gitignore
Standard .gitignore rules. Files ignored by git are automatically excluded.
.prunerignore
Same syntax as .gitignore, but pruner-specific. Use this to exclude files from the pruned output that you still want tracked by git.
# .prunerignore example
# Exclude generated output files from being pruned recursively
CONTEXT*.xml
CONTEXT*.md
# Exclude CI config — not useful for LLM context
.github/
# Exclude lock files
package-lock.json
yarn.lock
# Exclude compiled output
dist/
*.d.tsPrecedence:
.prunerignorerules are applied after.gitignore. A file excluded by either will not appear in output.
How It Works
Your project
│
▼
1. Discovery ──────── Crawls directory tree, applies .gitignore + .prunerignore
│
▼
2. AST Decomposition ─ Parses each .ts file into an Abstract Syntax Tree via ts-morph
│
▼
3. Depth Resolution ── Matches each file path against depthMap globs (first match wins)
│
▼
4. Pruning ─────────── Strips function bodies / excludes files based on resolved depth
│
▼
5. Token Accounting ── Counts tokens across all files; if over budget, re-prunes aggressively
│
▼
6. Serialization ───── Re-serializes AST nodes into XML or Markdown
│
▼
7. Pointer Injection ─ Prepends filepath:line index for every class, function, interface, enum
│
▼
Output file (CONTEXT.xml / CONTEXT.md)Examples
Minimal — just dump the architecture
context-pruner prune ./src --depth skeleton --output CONTEXT.mdFull project with per-directory control
context-pruner prune . --config pruner.config.json --output CONTEXT.xmlTight token budget for small context windows
context-pruner prune ./src --budget 3000 --format xml --output CONTEXT-small.xmlCopy directly to clipboard (macOS)
context-pruner prune ./src --format markdown --output - | pbcopyCopy directly to clipboard (Linux)
context-pruner prune ./src --format markdown --output - | xclip -selection clipboardUse in an npm script
{
"scripts": {
"context": "context-pruner prune . --config pruner.config.json --output CONTEXT.xml",
"context:md": "context-pruner prune . --format markdown --output CONTEXT.md",
"context:small": "context-pruner prune ./src --budget 4000 --output CONTEXT-small.xml"
}
}Use in GitHub Actions (generate context snapshot on push)
name: Generate Context
on:
push:
branches: [main]
jobs:
context:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install -g @mkhitar99/context-pruner
- run: context-pruner prune . --config pruner.config.json --output CONTEXT.xml
- uses: actions/upload-artifact@v4
with:
name: context-snapshot
path: CONTEXT.xmlProgrammatic usage in a build script
import { prune } from '@mkhitar99/context-pruner';
import { writeFileSync } from 'fs';
async function generateContext() {
const output = await prune('./src', {
outputFormat: 'xml',
tokenBudget: 8000,
depthMap: {
'src/core/**': 'full',
'src/utils/**': 'skeleton',
'tests/**': 'none',
},
preserveTypes: true,
});
writeFileSync('CONTEXT.xml', output, 'utf-8');
console.log('Context snapshot written to CONTEXT.xml');
}
generateContext();Changelog
1.0.0
- Initial release
- AST parsing with
ts-morph - Function body stripping with
/* logic */placeholders - Semantic pointer generation (
filepath:line) - Configurable depth map (
full/skeleton/none) - Token budget enforcement
- XML and Markdown output serialization
.gitignoreand.prunerignoresupport
License
MIT
