contextdiet-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/contextdiet.js +4 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +104 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/bundler/index.d.ts +49 -0
- package/dist/core/bundler/index.d.ts.map +1 -0
- package/dist/core/bundler/index.js +57 -0
- package/dist/core/bundler/index.js.map +1 -0
- package/dist/core/graph/index.d.ts +29 -0
- package/dist/core/graph/index.d.ts.map +1 -0
- package/dist/core/graph/index.js +195 -0
- package/dist/core/graph/index.js.map +1 -0
- package/dist/core/graph/selector.d.ts +29 -0
- package/dist/core/graph/selector.d.ts.map +1 -0
- package/dist/core/graph/selector.js +49 -0
- package/dist/core/graph/selector.js.map +1 -0
- package/dist/core/graph/symbol-selector.d.ts +31 -0
- package/dist/core/graph/symbol-selector.d.ts.map +1 -0
- package/dist/core/graph/symbol-selector.js +0 -0
- package/dist/core/graph/symbol-selector.js.map +1 -0
- package/dist/core/graph/types.d.ts +58 -0
- package/dist/core/graph/types.d.ts.map +1 -0
- package/dist/core/graph/types.js +11 -0
- package/dist/core/graph/types.js.map +1 -0
- package/dist/core/metrics/index.d.ts +51 -0
- package/dist/core/metrics/index.d.ts.map +1 -0
- package/dist/core/metrics/index.js +91 -0
- package/dist/core/metrics/index.js.map +1 -0
- package/dist/core/parser/index.d.ts +32 -0
- package/dist/core/parser/index.d.ts.map +1 -0
- package/dist/core/parser/index.js +274 -0
- package/dist/core/parser/index.js.map +1 -0
- package/dist/core/parser/types.d.ts +127 -0
- package/dist/core/parser/types.d.ts.map +1 -0
- package/dist/core/parser/types.js +24 -0
- package/dist/core/parser/types.js.map +1 -0
- package/dist/core/pipeline.d.ts +38 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +69 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/pruner/index.d.ts +25 -0
- package/dist/core/pruner/index.d.ts.map +1 -0
- package/dist/core/pruner/index.js +56 -0
- package/dist/core/pruner/index.js.map +1 -0
- package/dist/core/pruner/types.d.ts +18 -0
- package/dist/core/pruner/types.d.ts.map +1 -0
- package/dist/core/pruner/types.js +2 -0
- package/dist/core/pruner/types.js.map +1 -0
- package/dist/core/ranker/index.d.ts +26 -0
- package/dist/core/ranker/index.d.ts.map +1 -0
- package/dist/core/ranker/index.js +149 -0
- package/dist/core/ranker/index.js.map +1 -0
- package/dist/core/ranker/types.d.ts +61 -0
- package/dist/core/ranker/types.d.ts.map +1 -0
- package/dist/core/ranker/types.js +12 -0
- package/dist/core/ranker/types.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rizzhab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# ContextDiet
|
|
4
|
+
|
|
5
|
+
### An AST-based token optimizer that slashes AI agent context bloat — up to **99% measured token reduction**, with **$0 network overhead**.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/risshabs22-quantified/contextDiet/actions/workflows/ci.yml)
|
|
8
|
+
[](./LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](#-contributing)
|
|
12
|
+
|
|
13
|
+
**Stop paying to send dead code to your LLM.**
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## The 30-Second Pitch
|
|
20
|
+
|
|
21
|
+
Your AI agent doesn't need your entire repo to fix a JWT bug. It needs the token-handling middleware, `verifyToken` underneath it, and the crypto util they both depend on — and **nothing else**.
|
|
22
|
+
|
|
23
|
+
Naive context packers dump every byte they can find. ContextDiet parses your code into an **Abstract Syntax Tree**, builds a real **dependency graph**, ranks the symbols that match your task, and keeps *only the reachable code paths*. The result is a dense, surgical Markdown bundle that fits your intent — not your file count.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx contextdiet-cli trim ./src --focus "verify the token signature"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The **Markdown bundle streams to stdout** (pipe it straight into a file, your clipboard, or an LLM), while a human-readable summary dashboard is printed to **stderr** — so redirecting stdout gives you a pristine bundle:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx contextdiet-cli trim ./src --focus "verify the token signature" > context.md
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Here's a **real, reproducible run** — ContextDiet trimming its own source (`npm run build && node bin/contextdiet.js trim ./src --focus "malformed syntax ParseError"`):
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
ContextDiet · trim
|
|
39
|
+
────────────────────────────────────────────────────
|
|
40
|
+
focus malformed syntax ParseError
|
|
41
|
+
root ./src (ContextDiet's own engine)
|
|
42
|
+
seeds 2 matched · kept 2/14 files
|
|
43
|
+
────────────────────────────────────────────────────
|
|
44
|
+
original 62.6 KB ~30,472 tok
|
|
45
|
+
pruned 616 B ~302 tok
|
|
46
|
+
────────────────────────────────────────────────────
|
|
47
|
+
reduction 99.0% tokens ✓ >80% target met
|
|
48
|
+
99.0% bytes
|
|
49
|
+
output (stdout)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> Compression scales with how precisely your task maps to code: narrow, single-symbol tasks measure up to **99.0%**; broader tasks that legitimately touch more code measure less (e.g. **68.2%** on our auth-monolith fixture — see the example below). All numbers are measured runs, logged in `PROJECT_STATUS.md`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Why ContextDiet?
|
|
57
|
+
|
|
58
|
+
Most "repo-to-prompt" tools (think `repomix`, `gitingest`, and friends) are **text dumpers**. They walk your filesystem, concatenate files, and maybe strip comments. That's it. They have no idea what your code *means*, so they send everything and let you (and your wallet) sort it out.
|
|
59
|
+
|
|
60
|
+
That's expensive in three ways:
|
|
61
|
+
|
|
62
|
+
| | Naive text packers | **ContextDiet** |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| **Unit of understanding** | Files & bytes | AST symbols & call graph |
|
|
65
|
+
| **What gets included** | Everything it can glob | Only code reachable from your task |
|
|
66
|
+
| **Dead code** | Shipped to the model | Sliced away |
|
|
67
|
+
| **Cost model** | You pay for the whole repo | You pay for the relevant slice |
|
|
68
|
+
| **Circular imports** | Duplicated / mangled | Cycle-safe graph traversal |
|
|
69
|
+
| **Re-exports & aliases** | Often broken by regex | Resolved via real AST |
|
|
70
|
+
| **Network / API calls** | Sometimes (embeddings) | **Zero. Fully local.** |
|
|
71
|
+
| **Determinism** | Order-dependent | Byte-identical every run |
|
|
72
|
+
|
|
73
|
+
> **The core insight:** LLM cost is billed per token, and most tokens in a naive dump are *irrelevant*. A billing module, a PDF generator, and an analytics service have **zero structural connection** to your auth bug — so why pay to send them? ContextDiet answers "what does the model actually need to see?" with graph theory, not guesswork.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Zero install — run it directly
|
|
81
|
+
npx contextdiet-cli trim ./src --focus "verify the token signature"
|
|
82
|
+
|
|
83
|
+
# Or install globally (the installed command keeps the short name)
|
|
84
|
+
npm install -g contextdiet-cli
|
|
85
|
+
contextdiet trim ./src --focus "add rate limiting to the login route"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**`contextdiet trim <path> [options]`**
|
|
89
|
+
|
|
90
|
+
| Flag | Description | Default |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `<path>` | Root directory to analyze. | *(required)* |
|
|
93
|
+
| `-f, --focus <query>` | Natural-language description of your task. Drives seed selection. | *(required)* |
|
|
94
|
+
| `--hops <n>` | Dependency-graph traversal depth from the seed nodes. | `2` |
|
|
95
|
+
| `-o, --output <file>` | Write the bundle to a file instead of stdout. | *(stdout)* |
|
|
96
|
+
|
|
97
|
+
> The bundle is written to **stdout** and the summary dashboard to **stderr**, so `contextdiet trim … > bundle.md` yields a clean file with no dashboard noise mixed in.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## How It Works
|
|
102
|
+
|
|
103
|
+
ContextDiet is a five-stage pipeline. Each stage is a pure, independently testable module — source in, data out, no hidden state.
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
┌───────────────────────────────────────────────┐
|
|
107
|
+
./src ──────────────▶│ ContextDiet │
|
|
108
|
+
--focus "verify…" └───────────────────────────────────────────────┘
|
|
109
|
+
│
|
|
110
|
+
┌─────────────────────────────────────┼─────────────────────────────────────┐
|
|
111
|
+
▼ ▼ ▼
|
|
112
|
+
┌───────────────┐ symbols + ┌───────────────────┐ graph ┌──────────────────────┐
|
|
113
|
+
│ 1. PARSER │ imports │ 2. DEPENDENCY │ nodes │ 3. LEXICAL SEED │
|
|
114
|
+
│ │──────────────▶│ GRAPH │───────────▶│ RANKER │
|
|
115
|
+
│ AST via │ │ │ │ │
|
|
116
|
+
│ ast-grep │ │ resolves imports, │ │ tokenizes the focus │
|
|
117
|
+
│ → functions, │ │ re-exports, │ │ string, scores it │
|
|
118
|
+
│ classes, │ │ aliases; handles │ │ against symbol names │
|
|
119
|
+
│ vars, types │ │ circular deps │ │ → seed nodes │
|
|
120
|
+
└───────────────┘ └───────────────────┘ └──────────────────────┘
|
|
121
|
+
│
|
|
122
|
+
┌────────────────────────────────────────────────────────────────────┘
|
|
123
|
+
▼ ▼
|
|
124
|
+
┌───────────────────────┐ kept code ┌──────────────────────────┐
|
|
125
|
+
│ 4. SELECT + PRUNE │───────────────▶│ 5. MARKDOWN BUNDLER │─────▶ stdout (→ pipe to file)
|
|
126
|
+
│ │ │ │ + metrics → stderr
|
|
127
|
+
│ walks the graph from │ │ fences each kept file │
|
|
128
|
+
│ the seeds (reference │ │ with START/END delimiters│ Raw: 30,472 tok
|
|
129
|
+
│ closure), keeps only │ │ optimized for LLM │ Compressed: 302 tok
|
|
130
|
+
│ reachable declarations│ │ context parsing │ Reduction: 99.0%
|
|
131
|
+
└───────────────────────┘ └──────────────────────────┘
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Stage | Module | Responsibility |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| **1. Parser** | `src/core/parser` | Extracts imports & top-level symbols from each file via a real AST (ast-grep). Never returns a silently-wrong partial tree — surfaces a catchable `ParseError`. |
|
|
137
|
+
| **2. Dependency Graph** | `src/core/graph` | Resolves the import/export edges between symbols. Cycle-safe (`a → b → a` won't loop). |
|
|
138
|
+
| **3. Lexical Seed Ranker** | `src/core/ranker` | Turns your `--focus` string into weighted seed nodes. Strips stop words, splits camelCase, scores overlap. **$0, deterministic, local.** |
|
|
139
|
+
| **4. Selector + Pruner** | `src/core/graph` · `src/core/pruner` | Walks the dependency graph from the seed symbols (symbol-level reference closure — same-file helpers always follow, so nothing dangles), then re-emits only the surviving declarations, verbatim. |
|
|
140
|
+
| **5. Markdown Bundler** | `src/core/bundler` | Serializes the survivors into a dense, delimiter-fenced stream — plus a `metrics` report proving the reduction. |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Example: The Monolith Auth App
|
|
145
|
+
|
|
146
|
+
Given a backend where `index.ts` boots Express and mounts **auth**, **billing**, and **analytics** routes:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
src/
|
|
150
|
+
├── index.ts ← 🔴 sliced (entry point — a dependent, not a dependency)
|
|
151
|
+
├── routes/auth.ts ← 🔴 sliced (calls the token code; not needed to understand it)
|
|
152
|
+
├── middleware/authMiddleware ← 🟢 kept (its token handlers match the focus)
|
|
153
|
+
├── utils/jwtUtils.ts ← 🟢 kept (verifyToken and friends)
|
|
154
|
+
├── utils/crypto.ts ← 🟢 kept (jwtUtils depends on it)
|
|
155
|
+
├── routes/billing.ts ← 🔴 sliced
|
|
156
|
+
├── utils/pdfGenerator.ts ← 🔴 sliced
|
|
157
|
+
└── services/analytics.ts ← 🔴 sliced
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Running `--focus "verify the token signature"` seeds the symbols that match the task (the middleware's token handlers, `verifyToken`) and keeps their **dependency closure**: `authMiddleware → jwtUtils → crypto` — 3 of 8 files, measured at **9,386 → 2,985 estimated tokens (68.2%)**.
|
|
161
|
+
|
|
162
|
+
Note the direction: ContextDiet keeps what your focused code **depends on**, never what merely *calls into it*. The entry point and the auth route import the JWT code, but the model doesn't need them to reason about token verification — and billing, the PDF generator, and analytics have zero structural connection to the token path, so they never stand a chance.
|
|
163
|
+
|
|
164
|
+
Narrower focus, deeper cuts: on ContextDiet's own repo, `--focus "malformed syntax ParseError"` maps to two leaf symbols and measures **99.0%** (30,472 → 302 tokens).
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Local Development
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git clone https://github.com/risshabs22-quantified/contextDiet.git
|
|
172
|
+
cd contextDiet
|
|
173
|
+
npm install
|
|
174
|
+
|
|
175
|
+
npm run typecheck # tsc --noEmit, strict mode
|
|
176
|
+
npm test # full Vitest suite
|
|
177
|
+
npm run build # compile to dist/
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Requirements:** Node.js `>= 20`.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Contributing
|
|
185
|
+
|
|
186
|
+
Contributions are welcome and appreciated — ContextDiet is built to be extended.
|
|
187
|
+
|
|
188
|
+
1. **Fork** the repo and create a feature branch (`git checkout -b feat/my-improvement`).
|
|
189
|
+
2. **Keep it strict.** All code must pass `npm run typecheck` (strict TypeScript, no implicit `any`) and `npm test`.
|
|
190
|
+
3. **Test first.** Every new capability ships with Vitest coverage — see `tests/unit` for the style.
|
|
191
|
+
4. **Stay modular.** Each pipeline stage is a pure module; new backends (tree-sitter, SWC) should slot in behind the existing interfaces without touching callers.
|
|
192
|
+
5. **Open a PR.** CI runs typecheck + the full test suite on every push and pull request. Green check = ready for review.
|
|
193
|
+
|
|
194
|
+
Good first issues: additional language parsers, a smarter ranker (embeddings-optional), and richer bundle formats (JSON, XML).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
Released under the **[MIT License](./LICENSE)**. Use it, fork it, ship it, sell it — no strings attached. Copyright © 2026 the ContextDiet contributors.
|
|
201
|
+
|
|
202
|
+
<div align="center">
|
|
203
|
+
|
|
204
|
+
**If ContextDiet saves you tokens, [give it a ⭐](https://github.com/risshabs22-quantified/contextDiet) — it genuinely helps.**
|
|
205
|
+
|
|
206
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextDiet CLI (Task 5.0).
|
|
3
|
+
*
|
|
4
|
+
* contextdiet trim <path> --focus "<query>" [--hops <n>] [-o <file>]
|
|
5
|
+
*
|
|
6
|
+
* Contract: the Markdown bundle is the ONLY thing written to stdout (so it pipes
|
|
7
|
+
* cleanly into an LLM / clipboard / file). The human-facing summary dashboard is
|
|
8
|
+
* written to stderr, so `contextdiet trim … > bundle.md` yields a pristine file.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextDiet CLI (Task 5.0).
|
|
3
|
+
*
|
|
4
|
+
* contextdiet trim <path> --focus "<query>" [--hops <n>] [-o <file>]
|
|
5
|
+
*
|
|
6
|
+
* Contract: the Markdown bundle is the ONLY thing written to stdout (so it pipes
|
|
7
|
+
* cleanly into an LLM / clipboard / file). The human-facing summary dashboard is
|
|
8
|
+
* written to stderr, so `contextdiet trim … > bundle.md` yields a pristine file.
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
import { promises as fs } from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import { trim } from '../core/pipeline.js';
|
|
14
|
+
const program = new Command();
|
|
15
|
+
program
|
|
16
|
+
.name('contextdiet')
|
|
17
|
+
.description('AST-based token optimizer — trim a codebase to just what an AI agent needs.')
|
|
18
|
+
.version('0.1.0');
|
|
19
|
+
program
|
|
20
|
+
.command('trim')
|
|
21
|
+
.description('Trim a codebase to the code relevant to a focus task, as one dense Markdown stream.')
|
|
22
|
+
.argument('<path>', 'root directory to analyze')
|
|
23
|
+
.requiredOption('-f, --focus <query>', 'natural-language task to focus on')
|
|
24
|
+
.option('--hops <n>', 'dependency-graph traversal depth from seeds', '2')
|
|
25
|
+
.option('-o, --output <file>', 'write the bundle to a file instead of stdout')
|
|
26
|
+
.action(async (targetPath, options) => {
|
|
27
|
+
try {
|
|
28
|
+
const hops = Number.parseInt(options.hops, 10);
|
|
29
|
+
if (!Number.isFinite(hops) || hops < 0) {
|
|
30
|
+
throw new Error(`--hops must be a non-negative integer (got "${options.hops}")`);
|
|
31
|
+
}
|
|
32
|
+
const root = path.resolve(targetPath);
|
|
33
|
+
const result = await trim(root, options.focus, { hops });
|
|
34
|
+
if (result.totalFiles === 0) {
|
|
35
|
+
process.stderr.write(`\n contextdiet: no source files found under ${root}\n\n`);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const outputPath = options.output === undefined ? null : path.resolve(options.output);
|
|
40
|
+
if (outputPath !== null) {
|
|
41
|
+
await fs.writeFile(outputPath, ensureTrailingNewline(result.bundle), 'utf8');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
process.stdout.write(ensureTrailingNewline(result.bundle));
|
|
45
|
+
}
|
|
46
|
+
printDashboard(result, outputPath);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
process.stderr.write(`\n contextdiet: ${error.message}\n\n`);
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
54
|
+
process.stderr.write(`\n contextdiet: ${error.message}\n\n`);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
});
|
|
57
|
+
// --- presentation ---------------------------------------------------------
|
|
58
|
+
const useColor = Boolean(process.stderr.isTTY) && process.env.NO_COLOR === undefined;
|
|
59
|
+
const paint = (code, text) => useColor ? `\x1b[${code}m${text}\x1b[0m` : text;
|
|
60
|
+
const bold = (t) => paint('1', t);
|
|
61
|
+
const dim = (t) => paint('2', t);
|
|
62
|
+
const cyan = (t) => paint('36', t);
|
|
63
|
+
const green = (t) => paint('32', t);
|
|
64
|
+
const yellow = (t) => paint('33', t);
|
|
65
|
+
function printDashboard(result, outputPath) {
|
|
66
|
+
const m = result.metrics;
|
|
67
|
+
const hitTarget = m.tokenReductionPercentage >= 80;
|
|
68
|
+
const pct = `${m.tokenReductionPercentage.toFixed(1)}%`;
|
|
69
|
+
const pctText = hitTarget ? green(bold(pct)) : yellow(bold(pct));
|
|
70
|
+
const targetText = hitTarget ? green('✓ >80% target met') : yellow('⚠ below >80% target');
|
|
71
|
+
const rule = dim('─'.repeat(52));
|
|
72
|
+
const label = (t) => dim(t.padEnd(11));
|
|
73
|
+
const lines = [
|
|
74
|
+
'',
|
|
75
|
+
` ${bold(cyan('ContextDiet'))} ${dim('· trim')}`,
|
|
76
|
+
` ${rule}`,
|
|
77
|
+
` ${label('focus')}${result.focus}`,
|
|
78
|
+
` ${label('root')}${result.rootDir}`,
|
|
79
|
+
` ${label('seeds')}${bold(String(result.seedCount))} matched ${dim('·')} kept ${bold(`${result.keptFiles.length}/${result.totalFiles}`)} files`,
|
|
80
|
+
` ${rule}`,
|
|
81
|
+
` ${label('original')}${humanBytes(m.rawBytes).padStart(9)} ${dim(`~${grouped(m.rawTokens)} tok`)}`,
|
|
82
|
+
` ${label('pruned')}${humanBytes(m.compressedBytes).padStart(9)} ${dim(`~${grouped(m.compressedTokens)} tok`)}`,
|
|
83
|
+
` ${rule}`,
|
|
84
|
+
` ${label('reduction')}${pctText} ${dim('tokens')} ${targetText}`,
|
|
85
|
+
` ${dim(''.padEnd(11))}${dim(`${m.byteReductionPercentage.toFixed(1)}% bytes`)}`,
|
|
86
|
+
` ${label('output')}${outputPath === null ? dim('(stdout)') : outputPath}`,
|
|
87
|
+
'',
|
|
88
|
+
];
|
|
89
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
90
|
+
}
|
|
91
|
+
function ensureTrailingNewline(text) {
|
|
92
|
+
return text.endsWith('\n') ? text : `${text}\n`;
|
|
93
|
+
}
|
|
94
|
+
function humanBytes(bytes) {
|
|
95
|
+
if (bytes < 1024)
|
|
96
|
+
return `${bytes} B`;
|
|
97
|
+
if (bytes < 1024 * 1024)
|
|
98
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
99
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
100
|
+
}
|
|
101
|
+
function grouped(n) {
|
|
102
|
+
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarkdownBundler — Task 5.0 (output delivery).
|
|
3
|
+
*
|
|
4
|
+
* The final stage of the pipeline: take the pruned files and serialize them
|
|
5
|
+
* into one hyper-dense, LLM-friendly stream. The format is deliberately plain
|
|
6
|
+
* (explicit START/END delimiters rather than triple-backtick fences) because:
|
|
7
|
+
* - it survives nested code that itself contains ``` fences without escaping,
|
|
8
|
+
* - the path appears on both boundaries so a model can attribute every line,
|
|
9
|
+
* - it costs almost no extra tokens per file.
|
|
10
|
+
*
|
|
11
|
+
* Pure and deterministic: same input files always produce byte-identical output.
|
|
12
|
+
* No I/O, no network.
|
|
13
|
+
*/
|
|
14
|
+
/** A single file to include in the bundle. */
|
|
15
|
+
export interface BundleFile {
|
|
16
|
+
/** Path shown in the delimiters — should be repo-relative for readability. */
|
|
17
|
+
readonly path: string;
|
|
18
|
+
/** The (already pruned) file contents. */
|
|
19
|
+
readonly content: string;
|
|
20
|
+
}
|
|
21
|
+
/** Tunables for how the stream is assembled. */
|
|
22
|
+
export interface BundlerOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Blank line(s) inserted between consecutive file blocks. Defaults to `1`.
|
|
25
|
+
* Purely cosmetic; does not affect a block's own delimiters.
|
|
26
|
+
*/
|
|
27
|
+
readonly blockSeparation?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Sort files by path before emitting, for stable output regardless of input
|
|
30
|
+
* order. Defaults to `false` (preserve caller order, typically graph order).
|
|
31
|
+
*/
|
|
32
|
+
readonly sortByPath?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Wrap a single file's content in the START/END delimiters. Exported so callers
|
|
36
|
+
* (and tests) can format one block in isolation.
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatFileBlock(file: BundleFile): string;
|
|
39
|
+
export declare class MarkdownBundler {
|
|
40
|
+
private readonly blockSeparation;
|
|
41
|
+
private readonly sortByPath;
|
|
42
|
+
constructor(options?: BundlerOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Combine all files into a single Markdown stream. Returns an empty string for
|
|
45
|
+
* an empty file list.
|
|
46
|
+
*/
|
|
47
|
+
bundle(files: readonly BundleFile[]): string;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/bundler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8CAA8C;AAC9C,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAOxD;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IAErC,YAAY,OAAO,GAAE,cAAmB,EAGvC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CAS3C;CACF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarkdownBundler — Task 5.0 (output delivery).
|
|
3
|
+
*
|
|
4
|
+
* The final stage of the pipeline: take the pruned files and serialize them
|
|
5
|
+
* into one hyper-dense, LLM-friendly stream. The format is deliberately plain
|
|
6
|
+
* (explicit START/END delimiters rather than triple-backtick fences) because:
|
|
7
|
+
* - it survives nested code that itself contains ``` fences without escaping,
|
|
8
|
+
* - the path appears on both boundaries so a model can attribute every line,
|
|
9
|
+
* - it costs almost no extra tokens per file.
|
|
10
|
+
*
|
|
11
|
+
* Pure and deterministic: same input files always produce byte-identical output.
|
|
12
|
+
* No I/O, no network.
|
|
13
|
+
*/
|
|
14
|
+
const START_PREFIX = '--- START FILE: ';
|
|
15
|
+
const END_PREFIX = '--- END FILE: ';
|
|
16
|
+
const DELIM_SUFFIX = ' ---';
|
|
17
|
+
/**
|
|
18
|
+
* Wrap a single file's content in the START/END delimiters. Exported so callers
|
|
19
|
+
* (and tests) can format one block in isolation.
|
|
20
|
+
*/
|
|
21
|
+
export function formatFileBlock(file) {
|
|
22
|
+
const header = `${START_PREFIX}${file.path}${DELIM_SUFFIX}`;
|
|
23
|
+
const footer = `${END_PREFIX}${file.path}${DELIM_SUFFIX}`;
|
|
24
|
+
// Preserve content verbatim; only guarantee the delimiters sit on their own
|
|
25
|
+
// lines by trimming a single trailing newline so the footer never doubles up.
|
|
26
|
+
const body = stripSingleTrailingNewline(file.content);
|
|
27
|
+
return `${header}\n${body}\n${footer}`;
|
|
28
|
+
}
|
|
29
|
+
export class MarkdownBundler {
|
|
30
|
+
blockSeparation;
|
|
31
|
+
sortByPath;
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.blockSeparation = Math.max(0, options.blockSeparation ?? 1);
|
|
34
|
+
this.sortByPath = options.sortByPath ?? false;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Combine all files into a single Markdown stream. Returns an empty string for
|
|
38
|
+
* an empty file list.
|
|
39
|
+
*/
|
|
40
|
+
bundle(files) {
|
|
41
|
+
if (files.length === 0)
|
|
42
|
+
return '';
|
|
43
|
+
const ordered = this.sortByPath
|
|
44
|
+
? [...files].sort((a, b) => a.path.localeCompare(b.path))
|
|
45
|
+
: files;
|
|
46
|
+
const separator = '\n'.repeat(this.blockSeparation + 1);
|
|
47
|
+
return ordered.map(formatFileBlock).join(separator);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function stripSingleTrailingNewline(content) {
|
|
51
|
+
if (content.endsWith('\r\n'))
|
|
52
|
+
return content.slice(0, -2);
|
|
53
|
+
if (content.endsWith('\n'))
|
|
54
|
+
return content.slice(0, -1);
|
|
55
|
+
return content;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/bundler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAwBH,MAAM,YAAY,GAAG,kBAAkB,CAAC;AACxC,MAAM,UAAU,GAAG,gBAAgB,CAAC;AACpC,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,MAAM,MAAM,GAAG,GAAG,YAAY,GAAG,IAAI,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5D,MAAM,MAAM,GAAG,GAAG,UAAU,GAAG,IAAI,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;IAC1D,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,IAAI,GAAG,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,OAAO,GAAG,MAAM,KAAK,IAAI,KAAK,MAAM,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,OAAO,eAAe;IACT,eAAe,CAAS;IACxB,UAAU,CAAU;IAErC,YAAY,OAAO,GAAmB,EAAE;QACtC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC;IAChD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAA4B;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAElC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU;YAC7B,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzD,CAAC,CAAC,KAAK,CAAC;QAEV,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;QACxD,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;CACF;AAED,SAAS,0BAA0B,CAAC,OAAe;IACjD,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DependencyGraphResolver (Task 2.0).
|
|
3
|
+
*
|
|
4
|
+
* Builds a {@link DependencyGraph} for a codebase by:
|
|
5
|
+
* 1. enumerating source files under the root (skipping `node_modules`/dotdirs),
|
|
6
|
+
* 2. parsing each with the {@link Parser} to get symbols + dependency specifiers,
|
|
7
|
+
* 3. resolving each specifier to an absolute file path (or marking it external).
|
|
8
|
+
*
|
|
9
|
+
* Cycle safety: the graph is built by filesystem enumeration + per-file edge
|
|
10
|
+
* resolution — NOT by recursively following imports. Import cycles therefore
|
|
11
|
+
* cannot cause infinite loops or stack overflow at build time; they simply appear
|
|
12
|
+
* as cycles in the adjacency data. Consumers that walk the graph must use a
|
|
13
|
+
* visited set — see {@link collectReachable}.
|
|
14
|
+
*/
|
|
15
|
+
import type { Parser } from '../parser/types.js';
|
|
16
|
+
import type { DependencyGraph, GraphResolver } from './types.js';
|
|
17
|
+
export declare class DependencyGraphResolver implements GraphResolver {
|
|
18
|
+
private readonly parser;
|
|
19
|
+
constructor(parser?: Parser);
|
|
20
|
+
buildGraph(rootDir: string): Promise<DependencyGraph>;
|
|
21
|
+
private buildNode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Iteratively collect every node reachable from `startPath` (inclusive), up to
|
|
25
|
+
* `maxHops` edges away. Uses a visited set + explicit queue, so cycles terminate
|
|
26
|
+
* and deep graphs never overflow the call stack.
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectReachable(graph: DependencyGraph, startPath: string, maxHops?: number): Set<string>;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/graph/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAEV,eAAe,EAEf,aAAa,EACd,MAAM,YAAY,CAAC;AAUpB,qBAAa,uBAAwB,YAAW,aAAa;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,YAAY,MAAM,GAAE,MAA+B,EAElD;IAEK,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA2B1D;YAEa,SAAS;CAgCxB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAiC,GACzC,GAAG,CAAC,MAAM,CAAC,CAwBb"}
|