@ajdev0/token-shrink 2.0.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.
@@ -0,0 +1,518 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ languageForFile
4
+ } from "./chunk-7SQ6HMWM.js";
5
+
6
+ // src/server/assembler.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ function assemble(activeFilePath, cache, opts = {}) {
10
+ const { includeStats = false, maxSkeletons = 50, pruneActiveFile = false } = opts;
11
+ const abs = path.resolve(activeFilePath);
12
+ const activeSource = fs.existsSync(abs) ? fs.readFileSync(abs, "utf8") : "";
13
+ const activeEntry = cache.get(abs);
14
+ const included = [];
15
+ const unresolved = [];
16
+ const seen = /* @__PURE__ */ new Set();
17
+ if (activeEntry) {
18
+ for (const imp of activeEntry.imports) {
19
+ const entry = cache.get(imp);
20
+ if (!entry) {
21
+ unresolved.push(imp);
22
+ continue;
23
+ }
24
+ if (seen.has(imp)) continue;
25
+ seen.add(imp);
26
+ included.push({ filePath: imp, language: entry.language });
27
+ if (included.length >= maxSkeletons) break;
28
+ }
29
+ }
30
+ if (!activeEntry && activeSource) {
31
+ const specifiers = extractSpecifiers(activeSource);
32
+ for (const spec of specifiers) {
33
+ const resolved = resolveLocal(abs, spec);
34
+ if (!resolved) {
35
+ unresolved.push(spec);
36
+ continue;
37
+ }
38
+ if (seen.has(resolved)) continue;
39
+ seen.add(resolved);
40
+ const cached = cache.get(resolved);
41
+ included.push({ filePath: resolved, language: cached?.language ?? null });
42
+ if (included.length >= maxSkeletons) break;
43
+ }
44
+ }
45
+ const lines = [];
46
+ lines.push("# Compressed Code Context", "");
47
+ lines.push(`Active file: \`${rel(abs)}\``, "");
48
+ lines.push("## Ring 0 \u2014 Active file (full text)", "");
49
+ const ring0 = pruneActiveFile && activeEntry ? activeEntry.skeleton : activeSource;
50
+ lines.push(`\`\`\`${ext(activeFilePath)}`);
51
+ lines.push(ring0.trim() || "(empty or unreadable file)");
52
+ lines.push("```", "");
53
+ lines.push(
54
+ `## Ring 1 \u2014 Pruned dependencies (${included.length})`,
55
+ "",
56
+ "Implementation bodies removed; type signatures, interfaces and exports retained.",
57
+ ""
58
+ );
59
+ if (included.length === 0) {
60
+ lines.push("_No local dependency skeletons available._", "");
61
+ }
62
+ for (const inc of included) {
63
+ const entry = cache.get(inc.filePath);
64
+ const label = rel(inc.filePath);
65
+ lines.push(`### \`${label}\``, "");
66
+ if (entry) {
67
+ lines.push(`\`\`\`${ext(inc.filePath)}`);
68
+ lines.push(entry.skeleton.trim());
69
+ lines.push("```", "");
70
+ } else {
71
+ lines.push("_Unindexed file._", "");
72
+ }
73
+ }
74
+ if (unresolved.length > 0) {
75
+ lines.push("## Unresolved imports", "");
76
+ for (const u of unresolved) lines.push(`- \`${u}\``);
77
+ lines.push("");
78
+ }
79
+ if (includeStats) {
80
+ const depTokens = included.reduce(
81
+ (acc, inc) => {
82
+ const e = cache.get(inc.filePath);
83
+ return e ? acc + approximateTokens(e.skeleton) : acc;
84
+ },
85
+ 0
86
+ );
87
+ lines.push("---", "");
88
+ lines.push(
89
+ `_Token estimate \u2014 active: ${approximateTokens(activeSource)} \xB7 pruned deps: ${depTokens}._`,
90
+ ""
91
+ );
92
+ }
93
+ return {
94
+ markdown: lines.join("\n"),
95
+ activeFilePath: abs,
96
+ activeSource,
97
+ included,
98
+ unresolved
99
+ };
100
+ }
101
+ function extractSpecifiers(source) {
102
+ const found = [];
103
+ const re = /(?:from\s+['"`]|import\s+['"`]|require\s*\(\s*['"`])([^'"`]+)['"`]/g;
104
+ let m;
105
+ while ((m = re.exec(source)) !== null) {
106
+ if (m[1]) found.push(m[1]);
107
+ }
108
+ return [...new Set(found)];
109
+ }
110
+ function resolveLocal(importer, specifier) {
111
+ if (!/^[.~@]/.test(specifier)) return null;
112
+ if (specifier.startsWith("@/")) specifier = specifier.slice(2);
113
+ else if (specifier.startsWith("~")) specifier = specifier.slice(1);
114
+ const base = path.dirname(path.resolve(importer));
115
+ const p = path.resolve(base, specifier);
116
+ const candidates = [
117
+ p,
118
+ `${p}.ts`,
119
+ `${p}.tsx`,
120
+ `${p}.js`,
121
+ `${p}.jsx`,
122
+ `${p}.py`,
123
+ `${p}.go`,
124
+ `${p}.rs`,
125
+ `${p}.dart`,
126
+ `${p}.swift`,
127
+ `${p}.java`,
128
+ `${p}.kt`,
129
+ `${p}.c`,
130
+ `${p}.cpp`,
131
+ path.join(p, "index.ts"),
132
+ path.join(p, "index.js")
133
+ ];
134
+ for (const c of candidates) {
135
+ if (fs.existsSync(c) && fs.statSync(c).isFile()) return path.resolve(c);
136
+ }
137
+ return null;
138
+ }
139
+ function rel(p) {
140
+ try {
141
+ const cwd = process.cwd();
142
+ if (p.startsWith(cwd)) return "." + p.slice(cwd.length);
143
+ } catch {
144
+ }
145
+ return p;
146
+ }
147
+ function ext(p) {
148
+ return path.extname(p).replace(/^\./, "") || "text";
149
+ }
150
+ function approximateTokens(text) {
151
+ if (!text) return 0;
152
+ const tokens = text.match(/[A-Za-z0-9_$]+|[^A-Za-z0-9_\s$]/g);
153
+ return tokens ? tokens.length : 0;
154
+ }
155
+
156
+ // src/parser/wasm.ts
157
+ import fs2 from "fs";
158
+ import path2 from "path";
159
+ function resolveWasmDir() {
160
+ const candidates = [
161
+ path2.resolve(__dirname, "../../wasm"),
162
+ path2.resolve(__dirname, "../wasm"),
163
+ path2.resolve(process.cwd(), "wasm")
164
+ ];
165
+ for (const dir2 of candidates) {
166
+ try {
167
+ fs2.mkdirSync(dir2, { recursive: true });
168
+ return dir2;
169
+ } catch {
170
+ }
171
+ }
172
+ const dir = path2.resolve(process.cwd(), "wasm");
173
+ fs2.mkdirSync(dir, { recursive: true });
174
+ return dir;
175
+ }
176
+ var wasmDir = null;
177
+ function cacheDir() {
178
+ if (!wasmDir) wasmDir = resolveWasmDir();
179
+ return wasmDir;
180
+ }
181
+ function cachePath(spec) {
182
+ return path2.join(cacheDir(), spec.wasm);
183
+ }
184
+ function looksLikeWasm(bytes) {
185
+ return bytes.length >= 4 && bytes[0] === 0 && bytes[1] === 97 && bytes[2] === 115 && bytes[3] === 109;
186
+ }
187
+ var inflight = /* @__PURE__ */ new Map();
188
+ function download(spec) {
189
+ const pending = inflight.get(spec.wasm);
190
+ if (pending) return pending;
191
+ const job = (async () => {
192
+ const res = await fetch(spec.url, { redirect: "follow" });
193
+ if (!res.ok) {
194
+ throw new Error(
195
+ `Failed to download grammar "${spec.wasm}" from ${spec.url} (HTTP ${res.status})`
196
+ );
197
+ }
198
+ const bytes = new Uint8Array(await res.arrayBuffer());
199
+ if (!looksLikeWasm(bytes)) {
200
+ throw new Error(
201
+ `Downloaded grammar "${spec.wasm}" is not valid WebAssembly (got HTML/redirect).`
202
+ );
203
+ }
204
+ const target = cachePath(spec);
205
+ const tmp = `${target}.${process.pid}.tmp`;
206
+ fs2.writeFileSync(tmp, bytes);
207
+ fs2.renameSync(tmp, target);
208
+ inflight.delete(spec.wasm);
209
+ return bytes;
210
+ })().catch((err) => {
211
+ inflight.delete(spec.wasm);
212
+ throw err;
213
+ });
214
+ inflight.set(spec.wasm, job);
215
+ return job;
216
+ }
217
+ async function getGrammar(spec, force = false) {
218
+ if (!force && fs2.existsSync(cachePath(spec))) {
219
+ const cached = fs2.readFileSync(cachePath(spec));
220
+ if (looksLikeWasm(cached)) return cached;
221
+ }
222
+ return download(spec);
223
+ }
224
+ async function warmGrammars(specs = []) {
225
+ const registryModule = await import("./registry-JLP6X4QB.js");
226
+ const list = specs.length > 0 ? specs : Object.values(registryModule.registry);
227
+ const unique = /* @__PURE__ */ new Map();
228
+ for (const spec of list) unique.set(spec.wasm, spec);
229
+ const results = await Promise.allSettled(
230
+ [...unique.values()].map((spec) => getGrammar(spec))
231
+ );
232
+ return results.filter((r) => r.status === "fulfilled").length;
233
+ }
234
+
235
+ // src/parser/pruner.ts
236
+ import Parser from "web-tree-sitter";
237
+ import { createRequire } from "module";
238
+ import path3 from "path";
239
+ var languageCache = /* @__PURE__ */ new Map();
240
+ var initPromise = null;
241
+ function ensureParserInit() {
242
+ if (!initPromise) {
243
+ initPromise = Parser.init({
244
+ locateFile: (file) => {
245
+ const require2 = createRequire(__filename);
246
+ try {
247
+ const pkgRoot = path3.dirname(require2.resolve("web-tree-sitter/package.json"));
248
+ return path3.join(pkgRoot, file);
249
+ } catch {
250
+ return file;
251
+ }
252
+ }
253
+ });
254
+ }
255
+ return initPromise;
256
+ }
257
+ function loadLanguage(spec, force = false) {
258
+ if (!force) {
259
+ const cached = languageCache.get(spec.wasm);
260
+ if (cached) return Promise.resolve(cached);
261
+ }
262
+ if (force) languageCache.delete(spec.wasm);
263
+ return (async () => {
264
+ await ensureParserInit();
265
+ const grammar = await getGrammar(spec, force);
266
+ const language = await Parser.Language.load(grammar);
267
+ languageCache.set(spec.wasm, language);
268
+ return language;
269
+ })();
270
+ }
271
+ async function prune(filePath, source, opts = {}) {
272
+ const spec = languageForFile(filePath);
273
+ if (!spec || spec.rules.length === 0) {
274
+ return { code: source, language: null, removed: 0 };
275
+ }
276
+ const language = await loadLanguage(spec, opts.forceDownload);
277
+ const parser = new Parser();
278
+ parser.setLanguage(language);
279
+ const tree = parser.parse(source);
280
+ const ranges = [];
281
+ for (const rule of spec.rules) {
282
+ const query = language.query(rule.query);
283
+ const captures = query.captures(tree.rootNode);
284
+ for (const cap of captures) {
285
+ if (cap.node.startIndex === cap.node.endIndex) continue;
286
+ if (rule.replacement.keepIf?.test(cap.node.text)) continue;
287
+ ranges.push({
288
+ start: cap.node.startIndex,
289
+ end: cap.node.endIndex,
290
+ token: rule.replacement.token
291
+ });
292
+ }
293
+ query.delete();
294
+ }
295
+ tree.delete();
296
+ parser.delete();
297
+ const { code, removed } = spliceRanges(source, ranges);
298
+ return { code, language: spec.name, removed };
299
+ }
300
+ function spliceRanges(source, ranges) {
301
+ let removed = 0;
302
+ const sorted = [...ranges].sort((a, b) => b.start - a.start);
303
+ let out = source;
304
+ for (const r of sorted) {
305
+ if (r.start < 0 || r.end > out.length || r.end < r.start) continue;
306
+ removed += r.end - r.start;
307
+ out = out.slice(0, r.start) + r.token + out.slice(r.end);
308
+ }
309
+ return { code: out, removed };
310
+ }
311
+
312
+ // src/watcher/sync.ts
313
+ import fs3 from "fs";
314
+ import path4 from "path";
315
+ import { createHash } from "crypto";
316
+ import chokidar from "chokidar";
317
+ var DEFAULT_IGNORED = [
318
+ /(^|[/\\])\.[^/\\]+/,
319
+ // dotfiles / dot-dirs (.git, .cursor, .env, ...)
320
+ /node_modules/,
321
+ /[/\\](build|dist|out|coverage|\.next|\.turbo|\.cache)[/\\]/
322
+ ];
323
+ var ContextCache = class {
324
+ entries = /* @__PURE__ */ new Map();
325
+ initPromise = null;
326
+ ensureInit() {
327
+ if (!this.initPromise) {
328
+ this.initPromise = ensureParserInit();
329
+ }
330
+ return this.initPromise;
331
+ }
332
+ /** Returns the cached skeleton for a file, if present. */
333
+ getSkeleton(filePath) {
334
+ return this.entries.get(path4.resolve(filePath)) ?? null;
335
+ }
336
+ get imports() {
337
+ return this.entries;
338
+ }
339
+ };
340
+ function hashOf(text) {
341
+ return createHash("sha1").update(text).digest("hex");
342
+ }
343
+ function extractImports(_filePath, source) {
344
+ const out = [];
345
+ const re = /(?:from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g;
346
+ let m;
347
+ while ((m = re.exec(source)) !== null) {
348
+ const spec = m[1] ?? m[2] ?? m[3];
349
+ if (spec) out.push(spec);
350
+ }
351
+ return [...new Set(out)].filter(Boolean);
352
+ }
353
+ function resolveImport(importer, specifier, root) {
354
+ if (!/^[.~@]/.test(specifier)) {
355
+ if (specifier.startsWith("@/")) {
356
+ return resolveCandidate(path4.join(root, specifier.slice(2)));
357
+ }
358
+ if (specifier.startsWith("@")) {
359
+ return null;
360
+ }
361
+ if (specifier.startsWith("~")) {
362
+ return resolveCandidate(path4.join(root, specifier.slice(1)));
363
+ }
364
+ return null;
365
+ }
366
+ const base = path4.dirname(path4.resolve(importer));
367
+ return resolveCandidate(path4.resolve(base, specifier));
368
+ }
369
+ function resolveCandidate(p) {
370
+ const candidates = [
371
+ p,
372
+ `${p}.ts`,
373
+ `${p}.tsx`,
374
+ `${p}.js`,
375
+ `${p}.jsx`,
376
+ `${p}.mjs`,
377
+ `${p}.cjs`,
378
+ `${p}.py`,
379
+ `${p}.go`,
380
+ `${p}.rs`,
381
+ `${p}.dart`,
382
+ `${p}.swift`,
383
+ `${p}.java`,
384
+ `${p}.kt`,
385
+ `${p}.c`,
386
+ `${p}.cpp`,
387
+ `${p}.h`,
388
+ `${p}.hpp`,
389
+ `${p}.php`,
390
+ path4.join(p, "index.ts"),
391
+ path4.join(p, "index.js"),
392
+ path4.join(p, "index.tsx"),
393
+ path4.join(p, "index.jsx"),
394
+ path4.join(p, "index.py")
395
+ ];
396
+ for (const c of candidates) {
397
+ if (fs3.existsSync(c) && fs3.statSync(c).isFile()) return path4.resolve(c);
398
+ }
399
+ return null;
400
+ }
401
+ function createWatcher(opts) {
402
+ const cache = new ContextCache();
403
+ const { root, ignored = DEFAULT_IGNORED, onIndexed } = opts;
404
+ const pending = /* @__PURE__ */ new Map();
405
+ const DEBOUNCE_MS = 100;
406
+ async function handle(filePath) {
407
+ const abs = path4.resolve(filePath);
408
+ let source;
409
+ try {
410
+ const st = fs3.statSync(abs);
411
+ if (!st.isFile()) return;
412
+ source = fs3.readFileSync(abs, "utf8");
413
+ } catch {
414
+ return;
415
+ }
416
+ const hash = hashOf(source);
417
+ const cached = cache.entries.get(abs);
418
+ if (cached && cached.hash === hash) return;
419
+ await cache.ensureInit();
420
+ const { code, language } = await prune(abs, source);
421
+ const imports = await extractImports(abs, source);
422
+ const entry = {
423
+ hash,
424
+ skeleton: code,
425
+ language,
426
+ imports: imports.map((spec) => resolveImport(abs, spec, root)).filter((p) => p !== null)
427
+ };
428
+ cache.entries.set(abs, entry);
429
+ onIndexed?.(abs, entry);
430
+ }
431
+ const watcher = chokidar.watch(root, {
432
+ // Combine path matchers with a stat check so non-regular files (e.g. unix
433
+ // sockets) are never opened with fs.watch (which raises UVException).
434
+ ignored(_p, stats) {
435
+ if (stats && !stats.isFile() && !stats.isDirectory()) return true;
436
+ return isIgnored(path4.resolve(String(_p)), ignored);
437
+ },
438
+ alwaysStat: true,
439
+ ignoreInitial: true,
440
+ persistent: true,
441
+ awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 10 }
442
+ });
443
+ function debounce(filePath) {
444
+ const abs = path4.resolve(filePath);
445
+ const existing = pending.get(abs);
446
+ if (existing) clearTimeout(existing);
447
+ if (!existing) void handle(abs);
448
+ pending.set(
449
+ abs,
450
+ setTimeout(() => pending.delete(abs), DEBOUNCE_MS)
451
+ );
452
+ }
453
+ watcher.on("add", debounce);
454
+ watcher.on("change", debounce);
455
+ watcher.on("unlink", (filePath) => {
456
+ const abs = path4.resolve(filePath);
457
+ cache.entries.delete(abs);
458
+ });
459
+ return {
460
+ cache,
461
+ watcher,
462
+ /** Index an existing file right now (bypasses the watcher). */
463
+ index: handle,
464
+ /**
465
+ * Index the whole tree once (cold start). Returns the number of files
466
+ * successfully indexed.
467
+ */
468
+ async indexAll() {
469
+ const files = [];
470
+ await walk(root, (f) => files.push(f), ignored);
471
+ let ok = 0;
472
+ for (const f of files) {
473
+ try {
474
+ await handle(f);
475
+ ok++;
476
+ } catch {
477
+ }
478
+ }
479
+ return ok;
480
+ },
481
+ close: () => watcher.close()
482
+ };
483
+ }
484
+ async function walk(root, push, ignored) {
485
+ const entries = await fs3.promises.readdir(root, { withFileTypes: true });
486
+ for (const e of entries) {
487
+ const abs = path4.join(root, e.name);
488
+ if (isIgnored(abs, ignored)) continue;
489
+ if (e.isDirectory()) {
490
+ await walk(abs, push, ignored);
491
+ } else if (e.isFile()) {
492
+ push(abs);
493
+ }
494
+ }
495
+ }
496
+ function isIgnored(abs, ignored) {
497
+ for (const m of ignored) {
498
+ if (typeof m === "string" && abs.includes(m)) return true;
499
+ if (m instanceof RegExp && m.test(abs)) return true;
500
+ }
501
+ return false;
502
+ }
503
+
504
+ export {
505
+ assemble,
506
+ extractSpecifiers,
507
+ approximateTokens,
508
+ getGrammar,
509
+ warmGrammars,
510
+ prune,
511
+ spliceRanges,
512
+ DEFAULT_IGNORED,
513
+ hashOf,
514
+ extractImports,
515
+ resolveImport,
516
+ createWatcher
517
+ };
518
+ //# sourceMappingURL=chunk-HRF3BIOV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/assembler.ts","../src/parser/wasm.ts","../src/parser/pruner.ts","../src/watcher/sync.ts"],"sourcesContent":["/**\n * Context Assembler (Phase 4). Produces a Markdown context payload from the\n * graph cache:\n *\n * Ring 0 — the active file's raw, full text.\n * Ring 1 — pruned skeletons of the files it directly imports.\n *\n * The payload keeps full type signatures (imports, interfaces, exports) while\n * dropping implementation bodies, delivering the 80–90% reduction target.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { GraphCache } from '../watcher/sync.js';\n\nexport interface AssembleOptions {\n /** Number of PRD-style stats to append (disabled by default). */\n includeStats?: boolean;\n /** Maximum number of ring-1 files to include. */\n maxSkeletons?: number;\n /** Whether the active file itself should also be pruned (Ring 0 raw by default). */\n pruneActiveFile?: boolean;\n}\n\nexport interface AssembleResult {\n /** Human-readable Markdown payload for an agent. */\n markdown: string;\n activeFilePath: string;\n /** Ring-0 full text of the active file. */\n activeSource: string;\n /** Ring-1 entries actually included. */\n included: { filePath: string; language: string | null }[];\n /** Paths referenced by imports that could not be resolved to a file. */\n unresolved: string[];\n}\n\n/**\n * Build the compressed context for an open file.\n * `activeFilePath` may be absolute or project-relative.\n */\nexport function assemble(\n activeFilePath: string,\n cache: GraphCache,\n opts: AssembleOptions = {},\n): AssembleResult {\n const { includeStats = false, maxSkeletons = 50, pruneActiveFile = false } = opts;\n\n const abs = path.resolve(activeFilePath);\n const activeSource = fs.existsSync(abs) ? fs.readFileSync(abs, 'utf8') : '';\n const activeEntry = cache.get(abs);\n\n const included: AssembleResult['included'] = [];\n const unresolved: string[] = [];\n\n // Ring 1: walk the active file's direct imports.\n const seen = new Set<string>();\n if (activeEntry) {\n for (const imp of activeEntry.imports) {\n const entry = cache.get(imp);\n if (!entry) {\n unresolved.push(imp);\n continue;\n }\n if (seen.has(imp)) continue;\n seen.add(imp);\n included.push({ filePath: imp, language: entry.language });\n if (included.length >= maxSkeletons) break;\n }\n }\n\n // If the active file is unsupported (no cache entry), try to resolve its\n // imports on the fly from disk so the payload is still useful.\n if (!activeEntry && activeSource) {\n // Fallback: derive imports from a lightweight regex and resolve from disk.\n const specifiers = extractSpecifiers(activeSource);\n for (const spec of specifiers) {\n const resolved = resolveLocal(abs, spec);\n if (!resolved) {\n unresolved.push(spec);\n continue;\n }\n if (seen.has(resolved)) continue;\n seen.add(resolved);\n const cached = cache.get(resolved);\n included.push({ filePath: resolved, language: cached?.language ?? null });\n if (included.length >= maxSkeletons) break;\n }\n }\n\n const lines: string[] = [];\n lines.push('# Compressed Code Context', '');\n lines.push(`Active file: \\`${rel(abs)}\\``, '');\n\n // Ring 0.\n lines.push('## Ring 0 — Active file (full text)', '');\n const ring0 = pruneActiveFile && activeEntry ? activeEntry.skeleton : activeSource;\n lines.push(`\\`\\`\\`${ext(activeFilePath)}`);\n lines.push(ring0.trim() || '(empty or unreadable file)');\n lines.push('```', '');\n\n // Ring 1.\n lines.push(\n `## Ring 1 — Pruned dependencies (${included.length})`,\n '',\n 'Implementation bodies removed; type signatures, interfaces and exports retained.',\n '',\n );\n if (included.length === 0) {\n lines.push('_No local dependency skeletons available._', '');\n }\n for (const inc of included) {\n const entry = cache.get(inc.filePath);\n const label = rel(inc.filePath);\n lines.push(`### \\`${label}\\``, '');\n if (entry) {\n lines.push(`\\`\\`\\`${ext(inc.filePath)}`);\n lines.push(entry.skeleton.trim());\n lines.push('```', '');\n } else {\n lines.push('_Unindexed file._', '');\n }\n }\n\n if (unresolved.length > 0) {\n lines.push('## Unresolved imports', '');\n for (const u of unresolved) lines.push(`- \\`${u}\\``);\n lines.push('');\n }\n\n if (includeStats) {\n const depTokens = included.reduce(\n (acc, inc) => {\n const e = cache.get(inc.filePath);\n return e ? acc + approximateTokens(e.skeleton) : acc;\n },\n 0,\n );\n lines.push('---', '');\n lines.push(\n `_Token estimate — active: ${approximateTokens(activeSource)} · pruned deps: ${depTokens}._`,\n '',\n );\n }\n\n return {\n markdown: lines.join('\\n'),\n activeFilePath: abs,\n activeSource,\n included,\n unresolved,\n };\n}\n\n/** Fallback import specifier extraction when no AST index exists yet. */\nexport function extractSpecifiers(source: string): string[] {\n const found: string[] = [];\n // import ... from 'x'; / import 'x'; / require('x')\n const re = /(?:from\\s+['\"`]|import\\s+['\"`]|require\\s*\\(\\s*['\"`])([^'\"`]+)['\"`]/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(source)) !== null) {\n if (m[1]) found.push(m[1]);\n }\n return [...new Set(found)];\n}\n\n/** Resolve a fallback specifier to an existing file, mirroring sync.resolveImport. */\nfunction resolveLocal(importer: string, specifier: string): string | null {\n if (!/^[.~@]/.test(specifier)) return null;\n if (specifier.startsWith('@/')) specifier = specifier.slice(2);\n else if (specifier.startsWith('~')) specifier = specifier.slice(1);\n const base = path.dirname(path.resolve(importer));\n const p = path.resolve(base, specifier);\n const candidates = [\n p,\n `${p}.ts`,\n `${p}.tsx`,\n `${p}.js`,\n `${p}.jsx`,\n `${p}.py`,\n `${p}.go`,\n `${p}.rs`,\n `${p}.dart`,\n `${p}.swift`,\n `${p}.java`,\n `${p}.kt`,\n `${p}.c`,\n `${p}.cpp`,\n path.join(p, 'index.ts'),\n path.join(p, 'index.js'),\n ];\n for (const c of candidates) {\n if (fs.existsSync(c) && fs.statSync(c).isFile()) return path.resolve(c);\n }\n return null;\n}\n\n/** Relative path for display (fall back to basename). */\nfunction rel(p: string): string {\n try {\n const cwd = process.cwd();\n if (p.startsWith(cwd)) return '.' + p.slice(cwd.length);\n } catch {\n /* noop */\n }\n return p;\n}\n\n/** Return a code-block language label for a file path. */\nfunction ext(p: string): string {\n return path.extname(p).replace(/^\\./, '') || 'text';\n}\n\n/** Rough, dependency-free token approximation (words + punctuation runs). */\nexport function approximateTokens(text: string): number {\n if (!text) return 0;\n const tokens = text.match(/[A-Za-z0-9_$]+|[^A-Za-z0-9_\\s$]/g);\n return tokens ? tokens.length : 0;\n}\n\nexport function assembleFromCache(\n activeFilePath: string,\n cache: GraphCache,\n): AssembleResult {\n return assemble(activeFilePath, cache);\n}\n","/**\n * WASM grammar acquisition. Lazily downloads a language's `.wasm` grammar\n * from its upstream GitHub release on first use and caches it on disk in a\n * `wasm/` directory at the project root (mirrored to `dist/wasm` by tsup).\n *\n * `Language.load` on Node expects either a Uint8Array of the real parser\n * bytes or a filesystem path; we hand it the raw bytes via `getGrammar`,\n * which returns a `Uint8Array` for the pruner to load directly.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { LanguageSpec } from './registry.js';\n\n// `__dirname` is shimmed automatically by tsup so it works in both ESM and\n// CJS output: in ESM it derives from import.meta.url, in CJS it is native.\n// This avoids calling fileURLToPath(import.meta.url), which is empty in the\n// CJS bundle and would crash the packaged CLI (`dist/cli.cjs`, `dist/mcp.cjs`).\n/**\n * Resolve the `wasm/` cache directory, preferring the packaged copy.\n * Candidates are searched in order: project `wasm/`, `dist/wasm/`.\n */\nfunction resolveWasmDir(): string {\n const candidates = [\n path.resolve(__dirname, '../../wasm'),\n path.resolve(__dirname, '../wasm'),\n path.resolve(process.cwd(), 'wasm'),\n ];\n for (const dir of candidates) {\n try {\n fs.mkdirSync(dir, { recursive: true });\n return dir;\n } catch {\n /* try next */\n }\n }\n const dir = path.resolve(process.cwd(), 'wasm');\n fs.mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nlet wasmDir: string | null = null;\nfunction cacheDir(): string {\n if (!wasmDir) wasmDir = resolveWasmDir();\n return wasmDir;\n}\n\nexport function cachePath(spec: LanguageSpec): string {\n return path.join(cacheDir(), spec.wasm);\n}\n\n/** True if the head bytes are the wasm magic `\\0asm`. */\nfunction looksLikeWasm(bytes: Uint8Array): boolean {\n return (\n bytes.length >= 4 &&\n bytes[0] === 0 &&\n bytes[1] === 97 &&\n bytes[2] === 115 &&\n bytes[3] === 109\n );\n}\n\n/** In-flight downloads keyed by wasm filename, so concurrent callers share one. */\nconst inflight = new Map<string, Promise<Uint8Array>>();\n\nfunction download(spec: LanguageSpec): Promise<Uint8Array> {\n const pending = inflight.get(spec.wasm);\n if (pending) return pending;\n const job = (async () => {\n const res = await fetch(spec.url, { redirect: 'follow' });\n if (!res.ok) {\n throw new Error(\n `Failed to download grammar \"${spec.wasm}\" from ${spec.url} (HTTP ${res.status})`,\n );\n }\n const bytes = new Uint8Array(await res.arrayBuffer());\n if (!looksLikeWasm(bytes)) {\n throw new Error(\n `Downloaded grammar \"${spec.wasm}\" is not valid WebAssembly (got HTML/redirect).`,\n );\n }\n // Write atomically (tmp file + rename) so a concurrent reader never\n // observes a partially-written grammar.\n const target = cachePath(spec);\n const tmp = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, bytes);\n fs.renameSync(tmp, target);\n inflight.delete(spec.wasm);\n return bytes;\n })().catch((err) => {\n inflight.delete(spec.wasm);\n throw err;\n });\n inflight.set(spec.wasm, job);\n return job;\n}\n\n/**\n * Return the raw grammar bytes for a language, downloading them if needed.\n * `force` bypasses the local disk cache (used by tests / `--warm`).\n */\nexport async function getGrammar(\n spec: LanguageSpec,\n force = false,\n): Promise<Uint8Array> {\n if (!force && fs.existsSync(cachePath(spec))) {\n const cached = fs.readFileSync(cachePath(spec));\n if (looksLikeWasm(cached)) return cached;\n }\n return download(spec);\n}\n\n/**\n * Pre-download every supported grammar (used on cold boot / `prune --warm`).\n * Returns the number of grammars that are ready on disk afterward.\n */\nexport async function warmGrammars(specs: LanguageSpec[] = []): Promise<number> {\n const registryModule = await import('./registry.js');\n const list =\n specs.length > 0 ? specs : (Object.values(registryModule.registry) as LanguageSpec[]);\n const unique = new Map<string, LanguageSpec>();\n for (const spec of list) unique.set(spec.wasm, spec);\n const results = await Promise.allSettled(\n [...unique.values()].map((spec) => getGrammar(spec)),\n );\n return results.filter((r) => r.status === 'fulfilled').length;\n}\n\n","/**\n * Polyglot AST pruning engine. Parses a source file with its language grammar\n * and replaces matched implementation blocks with a compact skeleton token,\n * preserving type signatures, interfaces, and module exports.\n *\n * Range splicing is done bottom-up (descending start offset) so that earlier\n * replacements never shift the byte offsets of later ones.\n */\n\nimport Parser from 'web-tree-sitter';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport { languageForFile, type LanguageSpec } from './registry.js';\nimport { getGrammar } from './wasm.js';\n\n/** A byte-range that will be replaced by `token`. */\ninterface PruneRange {\n start: number;\n end: number;\n token: string;\n}\n\n/**\n * Cache of decoded web-tree-sitter Language objects, keyed by grammar file\n * name. Grammar bytes only change when the on-disk binary changes, so we can\n * decode once per process and reuse it across all parses for that language.\n */\nconst languageCache = new Map<string, Parser.Language>();\n\nlet initPromise: Promise<void> | null = null;\n/** `Parser.init()` only needs to run once; make concurrent calls share it. */\nexport function ensureParserInit(): Promise<void> {\n if (!initPromise) {\n initPromise = Parser.init({\n locateFile: (file: string) => {\n // Point the wasm runtime at the bundled tree-sitter.wasm so it works\n // from tsup's dist build as well as from node_modules. `__filename` is\n // shimmed by tsup for ESM and native in CJS, avoiding the empty\n // `import.meta.url` that crashes the CJS CLI bundle.\n const require = createRequire(__filename);\n try {\n const pkgRoot = path.dirname(require.resolve('web-tree-sitter/package.json'));\n return path.join(pkgRoot, file);\n } catch {\n return file;\n }\n },\n });\n }\n return initPromise;\n}\n\n/** Load (and cache) a decoded Language object for a language spec. */\nexport function loadLanguage(spec: LanguageSpec, force = false): Promise<Parser.Language> {\n if (!force) {\n const cached = languageCache.get(spec.wasm);\n if (cached) return Promise.resolve(cached);\n }\n // Force re-download/re-decode and refresh the cache.\n if (force) languageCache.delete(spec.wasm);\n return (async () => {\n await ensureParserInit();\n const grammar = await getGrammar(spec, force);\n const language = await Parser.Language.load(grammar);\n languageCache.set(spec.wasm, language);\n return language;\n })();\n}\n\n/**\n * Prune the implementation bodies out of `source` (a file at `filePath`).\n * If the language has no registered rules, returns the source unchanged.\n */\nexport async function prune(\n filePath: string,\n source: string,\n opts: { forceDownload?: boolean } = {},\n): Promise<{ code: string; language: string | null; removed: number }> {\n const spec = languageForFile(filePath);\n if (!spec || spec.rules.length === 0) {\n return { code: source, language: null, removed: 0 };\n }\n\n const language = await loadLanguage(spec, opts.forceDownload);\n\n const parser = new Parser();\n parser.setLanguage(language);\n const tree = parser.parse(source);\n\n const ranges: PruneRange[] = [];\n for (const rule of spec.rules) {\n const query = language.query(rule.query);\n const captures = query.captures(tree.rootNode);\n for (const cap of captures) {\n // Skip captures whose source text is empty (there's nothing to shrink).\n if (cap.node.startIndex === cap.node.endIndex) continue;\n // Allow rules to keep certain blocks (e.g. 'use client/server').\n if (rule.replacement.keepIf?.test(cap.node.text)) continue;\n ranges.push({\n start: cap.node.startIndex,\n end: cap.node.endIndex,\n token: rule.replacement.token,\n });\n }\n query.delete();\n }\n tree.delete();\n parser.delete();\n\n const { code, removed } = spliceRanges(source, ranges);\n return { code, language: spec.name, removed };\n}\n\n/**\n * Replace all ranges with their tokens. Ranges are pre-sorted descending by\n * start offset so each splice happens at the tail of the string first,\n * keeping earlier offsets stable.\n */\nexport function spliceRanges(source: string, ranges: PruneRange[]): {\n code: string;\n removed: number;\n} {\n let removed = 0;\n const sorted = [...ranges].sort((a, b) => b.start - a.start);\n let out = source;\n for (const r of sorted) {\n if (r.start < 0 || r.end > out.length || r.end < r.start) continue;\n removed += r.end - r.start;\n out = out.slice(0, r.start) + r.token + out.slice(r.end);\n }\n return { code: out, removed };\n}\n\n","/**\n * Incremental synchronization layer (Phase 3).\n *\n * - Watches the repository with chokidar (ignoring vendor/build dirs).\n * - On add/change, hashes the file and, if the hash changed, re-prunes it and\n * updates the in-memory graph cache.\n * - Extracts import/require specifiers from each file and normalizes relative\n * and aliased paths to absolute file paths on disk.\n *\n * The graph cache is shared with the Context Assembler, which reads ring-1\n * skeletons out of it.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { createHash } from 'node:crypto';\n\nimport chokidar, { type FSWatcher, type Matcher } from 'chokidar';\n\nimport { ensureParserInit, prune } from '../parser/pruner.js';\n\nexport type { Matcher };\n\n/** Default paths that will never be indexed or watched. */\nexport const DEFAULT_IGNORED = [\n /(^|[/\\\\])\\.[^/\\\\]+/, // dotfiles / dot-dirs (.git, .cursor, .env, ...)\n /node_modules/,\n /[/\\\\](build|dist|out|coverage|\\.next|\\.turbo|\\.cache)[/\\\\]/,\n];\n\nexport interface CacheEntry {\n /** sha1 of the last-indexed file content. */\n hash: string;\n /** Pruned skeleton for this file. */\n skeleton: string;\n /** The language name used to prune it (or null if unsupported). */\n language: string | null;\n /** Absolute paths of the files this file imports. */\n imports: string[];\n}\n\nexport type GraphCache = Map<string, CacheEntry>;\n\nexport class ContextCache {\n readonly entries: GraphCache = new Map();\n private initPromise: Promise<void> | null = null;\n\n ensureInit(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = ensureParserInit();\n }\n return this.initPromise;\n }\n\n /** Returns the cached skeleton for a file, if present. */\n getSkeleton(filePath: string): CacheEntry | null {\n return this.entries.get(path.resolve(filePath)) ?? null;\n }\n\n get imports() {\n return this.entries;\n }\n}\n\n/** Compute a sha1 of a string. */\nexport function hashOf(text: string): string {\n return createHash('sha1').update(text).digest('hex');\n}\n\n/**\n * Extract import/require specifiers from source text using a robust,\n * language-agnostic regex (AST-based import queries are grammar-fragile and\n * occasionally malformed; regex covers the common import forms across JS/TS,\n * Python, Go, Rust, Dart, Swift, Java, Kotlin, PHP, C/C++).\n * Returns the matched specifier strings (may be relative or absolute).\n */\nexport function extractImports(_filePath: string, source: string): string[] {\n const out: string[] = [];\n\n // import ... from 'x' | import 'x' | require('x')\n const re =\n /(?:from\\s+['\"`]([^'\"`]+)['\"`]|import\\s+['\"`]([^'\"`]+)['\"`]|require\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\))/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(source)) !== null) {\n const spec = m[1] ?? m[2] ?? m[3];\n if (spec) out.push(spec);\n }\n\n // Python / Go / Rust / Dart / Swift / Java / Kotlin / C/C++ single-quote not\n // covered by the JS-style regex is out of scope for the first pass.\n\n return [...new Set(out)].filter(Boolean);\n}\n\n/** Normalize a specifier to an absolute file path when it resolves locally. */\nexport function resolveImport(\n importer: string,\n specifier: string,\n root: string,\n): string | null {\n // Skip bare, non-relative imports (npm packages) and obviously external.\n if (!/^[.~@]/.test(specifier)) {\n // Alias `@/...` and `~` map to the project root.\n if (specifier.startsWith('@/')) {\n return resolveCandidate(path.join(root, specifier.slice(2)));\n }\n if (specifier.startsWith('@')) {\n return null; // scoped package, not local\n }\n if (specifier.startsWith('~')) {\n return resolveCandidate(path.join(root, specifier.slice(1)));\n }\n return null;\n }\n\n const base = path.dirname(path.resolve(importer));\n return resolveCandidate(path.resolve(base, specifier));\n}\n\n/** Try a specifier with common extension/index additions. */\nfunction resolveCandidate(p: string): string | null {\n const candidates = [\n p,\n `${p}.ts`,\n `${p}.tsx`,\n `${p}.js`,\n `${p}.jsx`,\n `${p}.mjs`,\n `${p}.cjs`,\n `${p}.py`,\n `${p}.go`,\n `${p}.rs`,\n `${p}.dart`,\n `${p}.swift`,\n `${p}.java`,\n `${p}.kt`,\n `${p}.c`,\n `${p}.cpp`,\n `${p}.h`,\n `${p}.hpp`,\n `${p}.php`,\n path.join(p, 'index.ts'),\n path.join(p, 'index.js'),\n path.join(p, 'index.tsx'),\n path.join(p, 'index.jsx'),\n path.join(p, 'index.py'),\n ];\n for (const c of candidates) {\n if (fs.existsSync(c) && fs.statSync(c).isFile()) return path.resolve(c);\n }\n return null;\n}\n\nexport interface SyncOptions {\n root: string;\n ignored?: Matcher[];\n onIndexed?: (filePath: string, entry: CacheEntry) => void;\n}\n\n/**\n * Watch a project root and keep the graph cache fresh. Returns the cache and\n * a close() handle. `prune` is awaited per change to keep hot-reload latency\n * predictable.\n */\nexport function createWatcher(opts: SyncOptions) {\n const cache = new ContextCache();\n const { root, ignored = DEFAULT_IGNORED, onIndexed } = opts;\n\n // Debounce a burst of events into a single re-prune per file.\n const pending = new Map<string, NodeJS.Timeout>();\n const DEBOUNCE_MS = 100;\n\n async function handle(filePath: string) {\n const abs = path.resolve(filePath);\n let source: string;\n try {\n const st = fs.statSync(abs);\n if (!st.isFile()) return; // sockets/symlinks etc.\n source = fs.readFileSync(abs, 'utf8');\n } catch {\n return; // file vanished between event and read\n }\n const hash = hashOf(source);\n const cached = cache.entries.get(abs);\n if (cached && cached.hash === hash) return; // unchanged\n\n await cache.ensureInit();\n const { code, language } = await prune(abs, source);\n const imports = await extractImports(abs, source);\n const entry: CacheEntry = {\n hash,\n skeleton: code,\n language,\n imports: imports\n .map((spec) => resolveImport(abs, spec, root))\n .filter((p): p is string => p !== null),\n };\n cache.entries.set(abs, entry);\n onIndexed?.(abs, entry);\n }\n\n const watcher: FSWatcher = chokidar.watch(root, {\n // Combine path matchers with a stat check so non-regular files (e.g. unix\n // sockets) are never opened with fs.watch (which raises UVException).\n ignored(_p, stats) {\n if (stats && !stats.isFile() && !stats.isDirectory()) return true;\n return isIgnored(path.resolve(String(_p)), ignored);\n },\n alwaysStat: true,\n ignoreInitial: true,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 10 },\n });\n\n function debounce(filePath: string) {\n const abs = path.resolve(filePath);\n const existing = pending.get(abs);\n if (existing) clearTimeout(existing);\n // Fire immediately on the first event, then coalesce noise.\n if (!existing) void handle(abs);\n pending.set(\n abs,\n setTimeout(() => pending.delete(abs), DEBOUNCE_MS),\n );\n }\n\n watcher.on('add', debounce);\n watcher.on('change', debounce);\n watcher.on('unlink', (filePath: string) => {\n const abs = path.resolve(filePath);\n cache.entries.delete(abs);\n });\n\n return {\n cache,\n watcher,\n /** Index an existing file right now (bypasses the watcher). */\n index: handle,\n /**\n * Index the whole tree once (cold start). Returns the number of files\n * successfully indexed.\n */\n async indexAll(): Promise<number> {\n const files: string[] = [];\n await walk(root, (f) => files.push(f), ignored);\n let ok = 0;\n for (const f of files) {\n try {\n await handle(f);\n ok++;\n } catch {\n /* skip unparseable files */\n }\n }\n return ok;\n },\n close: () => watcher.close(),\n };\n}\n\n/** Recursively list supported source files, honoring ignore patterns. */\nasync function walk(\n root: string,\n push: (f: string) => void,\n ignored: Matcher[],\n): Promise<void> {\n const entries = await fs.promises.readdir(root, { withFileTypes: true });\n for (const e of entries) {\n const abs = path.join(root, e.name);\n if (isIgnored(abs, ignored)) continue;\n if (e.isDirectory()) {\n await walk(abs, push, ignored);\n } else if (e.isFile()) {\n push(abs);\n }\n // Skip sockets, symlinks to non-files, devices, etc. to avoid chokidar/fs\n // throwing UVException while trying to watch them.\n }\n}\n\nfunction isIgnored(abs: string, ignored: Matcher[]): boolean {\n for (const m of ignored) {\n if (typeof m === 'string' && abs.includes(m)) return true;\n if (m instanceof RegExp && m.test(abs)) return true;\n }\n return false;\n}\n"],"mappings":";;;;;;AAWA,OAAO,QAAQ;AACf,OAAO,UAAU;AA6BV,SAAS,SACd,gBACA,OACA,OAAwB,CAAC,GACT;AAChB,QAAM,EAAE,eAAe,OAAO,eAAe,IAAI,kBAAkB,MAAM,IAAI;AAE7E,QAAM,MAAM,KAAK,QAAQ,cAAc;AACvC,QAAM,eAAe,GAAG,WAAW,GAAG,IAAI,GAAG,aAAa,KAAK,MAAM,IAAI;AACzE,QAAM,cAAc,MAAM,IAAI,GAAG;AAEjC,QAAM,WAAuC,CAAC;AAC9C,QAAM,aAAuB,CAAC;AAG9B,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,aAAa;AACf,eAAW,OAAO,YAAY,SAAS;AACrC,YAAM,QAAQ,MAAM,IAAI,GAAG;AAC3B,UAAI,CAAC,OAAO;AACV,mBAAW,KAAK,GAAG;AACnB;AAAA,MACF;AACA,UAAI,KAAK,IAAI,GAAG,EAAG;AACnB,WAAK,IAAI,GAAG;AACZ,eAAS,KAAK,EAAE,UAAU,KAAK,UAAU,MAAM,SAAS,CAAC;AACzD,UAAI,SAAS,UAAU,aAAc;AAAA,IACvC;AAAA,EACF;AAIA,MAAI,CAAC,eAAe,cAAc;AAEhC,UAAM,aAAa,kBAAkB,YAAY;AACjD,eAAW,QAAQ,YAAY;AAC7B,YAAM,WAAW,aAAa,KAAK,IAAI;AACvC,UAAI,CAAC,UAAU;AACb,mBAAW,KAAK,IAAI;AACpB;AAAA,MACF;AACA,UAAI,KAAK,IAAI,QAAQ,EAAG;AACxB,WAAK,IAAI,QAAQ;AACjB,YAAM,SAAS,MAAM,IAAI,QAAQ;AACjC,eAAS,KAAK,EAAE,UAAU,UAAU,UAAU,QAAQ,YAAY,KAAK,CAAC;AACxE,UAAI,SAAS,UAAU,aAAc;AAAA,IACvC;AAAA,EACF;AAEA,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,6BAA6B,EAAE;AAC1C,QAAM,KAAK,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE;AAG7C,QAAM,KAAK,4CAAuC,EAAE;AACpD,QAAM,QAAQ,mBAAmB,cAAc,YAAY,WAAW;AACtE,QAAM,KAAK,SAAS,IAAI,cAAc,CAAC,EAAE;AACzC,QAAM,KAAK,MAAM,KAAK,KAAK,4BAA4B;AACvD,QAAM,KAAK,OAAO,EAAE;AAGpB,QAAM;AAAA,IACJ,yCAAoC,SAAS,MAAM;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,KAAK,8CAA8C,EAAE;AAAA,EAC7D;AACA,aAAW,OAAO,UAAU;AAC1B,UAAM,QAAQ,MAAM,IAAI,IAAI,QAAQ;AACpC,UAAM,QAAQ,IAAI,IAAI,QAAQ;AAC9B,UAAM,KAAK,SAAS,KAAK,MAAM,EAAE;AACjC,QAAI,OAAO;AACT,YAAM,KAAK,SAAS,IAAI,IAAI,QAAQ,CAAC,EAAE;AACvC,YAAM,KAAK,MAAM,SAAS,KAAK,CAAC;AAChC,YAAM,KAAK,OAAO,EAAE;AAAA,IACtB,OAAO;AACL,YAAM,KAAK,qBAAqB,EAAE;AAAA,IACpC;AAAA,EACF;AAEA,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,KAAK,yBAAyB,EAAE;AACtC,eAAW,KAAK,WAAY,OAAM,KAAK,OAAO,CAAC,IAAI;AACnD,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,cAAc;AAChB,UAAM,YAAY,SAAS;AAAA,MACzB,CAAC,KAAK,QAAQ;AACZ,cAAM,IAAI,MAAM,IAAI,IAAI,QAAQ;AAChC,eAAO,IAAI,MAAM,kBAAkB,EAAE,QAAQ,IAAI;AAAA,MACnD;AAAA,MACA;AAAA,IACF;AACA,UAAM,KAAK,OAAO,EAAE;AACpB,UAAM;AAAA,MACJ,kCAA6B,kBAAkB,YAAY,CAAC,sBAAmB,SAAS;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,MAAM,KAAK,IAAI;AAAA,IACzB,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGO,SAAS,kBAAkB,QAA0B;AAC1D,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK;AACX,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM;AACrC,QAAI,EAAE,CAAC,EAAG,OAAM,KAAK,EAAE,CAAC,CAAC;AAAA,EAC3B;AACA,SAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;AAGA,SAAS,aAAa,UAAkB,WAAkC;AACxE,MAAI,CAAC,SAAS,KAAK,SAAS,EAAG,QAAO;AACtC,MAAI,UAAU,WAAW,IAAI,EAAG,aAAY,UAAU,MAAM,CAAC;AAAA,WACpD,UAAU,WAAW,GAAG,EAAG,aAAY,UAAU,MAAM,CAAC;AACjE,QAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,QAAQ,CAAC;AAChD,QAAM,IAAI,KAAK,QAAQ,MAAM,SAAS;AACtC,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,KAAK,KAAK,GAAG,UAAU;AAAA,IACvB,KAAK,KAAK,GAAG,UAAU;AAAA,EACzB;AACA,aAAW,KAAK,YAAY;AAC1B,QAAI,GAAG,WAAW,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,OAAO,EAAG,QAAO,KAAK,QAAQ,CAAC;AAAA,EACxE;AACA,SAAO;AACT;AAGA,SAAS,IAAI,GAAmB;AAC9B,MAAI;AACF,UAAM,MAAM,QAAQ,IAAI;AACxB,QAAI,EAAE,WAAW,GAAG,EAAG,QAAO,MAAM,EAAE,MAAM,IAAI,MAAM;AAAA,EACxD,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAGA,SAAS,IAAI,GAAmB;AAC9B,SAAO,KAAK,QAAQ,CAAC,EAAE,QAAQ,OAAO,EAAE,KAAK;AAC/C;AAGO,SAAS,kBAAkB,MAAsB;AACtD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,KAAK,MAAM,kCAAkC;AAC5D,SAAO,SAAS,OAAO,SAAS;AAClC;;;AChNA,OAAOA,SAAQ;AACf,OAAOC,WAAU;AAWjB,SAAS,iBAAyB;AAChC,QAAM,aAAa;AAAA,IACjBA,MAAK,QAAQ,WAAW,YAAY;AAAA,IACpCA,MAAK,QAAQ,WAAW,SAAS;AAAA,IACjCA,MAAK,QAAQ,QAAQ,IAAI,GAAG,MAAM;AAAA,EACpC;AACA,aAAWC,QAAO,YAAY;AAC5B,QAAI;AACF,MAAAF,IAAG,UAAUE,MAAK,EAAE,WAAW,KAAK,CAAC;AACrC,aAAOA;AAAA,IACT,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,MAAMD,MAAK,QAAQ,QAAQ,IAAI,GAAG,MAAM;AAC9C,EAAAD,IAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,SAAO;AACT;AAEA,IAAI,UAAyB;AAC7B,SAAS,WAAmB;AAC1B,MAAI,CAAC,QAAS,WAAU,eAAe;AACvC,SAAO;AACT;AAEO,SAAS,UAAU,MAA4B;AACpD,SAAOC,MAAK,KAAK,SAAS,GAAG,KAAK,IAAI;AACxC;AAGA,SAAS,cAAc,OAA4B;AACjD,SACE,MAAM,UAAU,KAChB,MAAM,CAAC,MAAM,KACb,MAAM,CAAC,MAAM,MACb,MAAM,CAAC,MAAM,OACb,MAAM,CAAC,MAAM;AAEjB;AAGA,IAAM,WAAW,oBAAI,IAAiC;AAEtD,SAAS,SAAS,MAAyC;AACzD,QAAM,UAAU,SAAS,IAAI,KAAK,IAAI;AACtC,MAAI,QAAS,QAAO;AACpB,QAAM,OAAO,YAAY;AACvB,UAAM,MAAM,MAAM,MAAM,KAAK,KAAK,EAAE,UAAU,SAAS,CAAC;AACxD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,+BAA+B,KAAK,IAAI,UAAU,KAAK,GAAG,UAAU,IAAI,MAAM;AAAA,MAChF;AAAA,IACF;AACA,UAAM,QAAQ,IAAI,WAAW,MAAM,IAAI,YAAY,CAAC;AACpD,QAAI,CAAC,cAAc,KAAK,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,uBAAuB,KAAK,IAAI;AAAA,MAClC;AAAA,IACF;AAGA,UAAM,SAAS,UAAU,IAAI;AAC7B,UAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,GAAG;AACpC,IAAAD,IAAG,cAAc,KAAK,KAAK;AAC3B,IAAAA,IAAG,WAAW,KAAK,MAAM;AACzB,aAAS,OAAO,KAAK,IAAI;AACzB,WAAO;AAAA,EACT,GAAG,EAAE,MAAM,CAAC,QAAQ;AAClB,aAAS,OAAO,KAAK,IAAI;AACzB,UAAM;AAAA,EACR,CAAC;AACD,WAAS,IAAI,KAAK,MAAM,GAAG;AAC3B,SAAO;AACT;AAMA,eAAsB,WACpB,MACA,QAAQ,OACa;AACrB,MAAI,CAAC,SAASA,IAAG,WAAW,UAAU,IAAI,CAAC,GAAG;AAC5C,UAAM,SAASA,IAAG,aAAa,UAAU,IAAI,CAAC;AAC9C,QAAI,cAAc,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,SAAO,SAAS,IAAI;AACtB;AAMA,eAAsB,aAAa,QAAwB,CAAC,GAAoB;AAC9E,QAAM,iBAAiB,MAAM,OAAO,wBAAe;AACnD,QAAM,OACJ,MAAM,SAAS,IAAI,QAAS,OAAO,OAAO,eAAe,QAAQ;AACnE,QAAM,SAAS,oBAAI,IAA0B;AAC7C,aAAW,QAAQ,KAAM,QAAO,IAAI,KAAK,MAAM,IAAI;AACnD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,WAAW,IAAI,CAAC;AAAA,EACrD;AACA,SAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,WAAW,EAAE;AACzD;;;ACrHA,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAC9B,OAAOG,WAAU;AAiBjB,IAAM,gBAAgB,oBAAI,IAA6B;AAEvD,IAAI,cAAoC;AAEjC,SAAS,mBAAkC;AAChD,MAAI,CAAC,aAAa;AAChB,kBAAc,OAAO,KAAK;AAAA,MACxB,YAAY,CAAC,SAAiB;AAK5B,cAAMC,WAAU,cAAc,UAAU;AACxC,YAAI;AACF,gBAAM,UAAUC,MAAK,QAAQD,SAAQ,QAAQ,8BAA8B,CAAC;AAC5E,iBAAOC,MAAK,KAAK,SAAS,IAAI;AAAA,QAChC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGO,SAAS,aAAa,MAAoB,QAAQ,OAAiC;AACxF,MAAI,CAAC,OAAO;AACV,UAAM,SAAS,cAAc,IAAI,KAAK,IAAI;AAC1C,QAAI,OAAQ,QAAO,QAAQ,QAAQ,MAAM;AAAA,EAC3C;AAEA,MAAI,MAAO,eAAc,OAAO,KAAK,IAAI;AACzC,UAAQ,YAAY;AAClB,UAAM,iBAAiB;AACvB,UAAM,UAAU,MAAM,WAAW,MAAM,KAAK;AAC5C,UAAM,WAAW,MAAM,OAAO,SAAS,KAAK,OAAO;AACnD,kBAAc,IAAI,KAAK,MAAM,QAAQ;AACrC,WAAO;AAAA,EACT,GAAG;AACL;AAMA,eAAsB,MACpB,UACA,QACA,OAAoC,CAAC,GACgC;AACrE,QAAM,OAAO,gBAAgB,QAAQ;AACrC,MAAI,CAAC,QAAQ,KAAK,MAAM,WAAW,GAAG;AACpC,WAAO,EAAE,MAAM,QAAQ,UAAU,MAAM,SAAS,EAAE;AAAA,EACpD;AAEA,QAAM,WAAW,MAAM,aAAa,MAAM,KAAK,aAAa;AAE5D,QAAM,SAAS,IAAI,OAAO;AAC1B,SAAO,YAAY,QAAQ;AAC3B,QAAM,OAAO,OAAO,MAAM,MAAM;AAEhC,QAAM,SAAuB,CAAC;AAC9B,aAAW,QAAQ,KAAK,OAAO;AAC7B,UAAM,QAAQ,SAAS,MAAM,KAAK,KAAK;AACvC,UAAM,WAAW,MAAM,SAAS,KAAK,QAAQ;AAC7C,eAAW,OAAO,UAAU;AAE1B,UAAI,IAAI,KAAK,eAAe,IAAI,KAAK,SAAU;AAE/C,UAAI,KAAK,YAAY,QAAQ,KAAK,IAAI,KAAK,IAAI,EAAG;AAClD,aAAO,KAAK;AAAA,QACV,OAAO,IAAI,KAAK;AAAA,QAChB,KAAK,IAAI,KAAK;AAAA,QACd,OAAO,KAAK,YAAY;AAAA,MAC1B,CAAC;AAAA,IACH;AACA,UAAM,OAAO;AAAA,EACf;AACA,OAAK,OAAO;AACZ,SAAO,OAAO;AAEd,QAAM,EAAE,MAAM,QAAQ,IAAI,aAAa,QAAQ,MAAM;AACrD,SAAO,EAAE,MAAM,UAAU,KAAK,MAAM,QAAQ;AAC9C;AAOO,SAAS,aAAa,QAAgB,QAG3C;AACA,MAAI,UAAU;AACd,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC3D,MAAI,MAAM;AACV,aAAW,KAAK,QAAQ;AACtB,QAAI,EAAE,QAAQ,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,EAAE,MAAO;AAC1D,eAAW,EAAE,MAAM,EAAE;AACrB,UAAM,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI,EAAE,QAAQ,IAAI,MAAM,EAAE,GAAG;AAAA,EACzD;AACA,SAAO,EAAE,MAAM,KAAK,QAAQ;AAC9B;;;ACvHA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,kBAAkB;AAE3B,OAAO,cAAgD;AAOhD,IAAM,kBAAkB;AAAA,EAC7B;AAAA;AAAA,EACA;AAAA,EACA;AACF;AAeO,IAAM,eAAN,MAAmB;AAAA,EACf,UAAsB,oBAAI,IAAI;AAAA,EAC/B,cAAoC;AAAA,EAE5C,aAA4B;AAC1B,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAc,iBAAiB;AAAA,IACtC;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,YAAY,UAAqC;AAC/C,WAAO,KAAK,QAAQ,IAAIC,MAAK,QAAQ,QAAQ,CAAC,KAAK;AAAA,EACrD;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AACF;AAGO,SAAS,OAAO,MAAsB;AAC3C,SAAO,WAAW,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACrD;AASO,SAAS,eAAe,WAAmB,QAA0B;AAC1E,QAAM,MAAgB,CAAC;AAGvB,QAAM,KACJ;AACF,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM;AACrC,UAAM,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;AAChC,QAAI,KAAM,KAAI,KAAK,IAAI;AAAA,EACzB;AAKA,SAAO,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,EAAE,OAAO,OAAO;AACzC;AAGO,SAAS,cACd,UACA,WACA,MACe;AAEf,MAAI,CAAC,SAAS,KAAK,SAAS,GAAG;AAE7B,QAAI,UAAU,WAAW,IAAI,GAAG;AAC9B,aAAO,iBAAiBA,MAAK,KAAK,MAAM,UAAU,MAAM,CAAC,CAAC,CAAC;AAAA,IAC7D;AACA,QAAI,UAAU,WAAW,GAAG,GAAG;AAC7B,aAAO;AAAA,IACT;AACA,QAAI,UAAU,WAAW,GAAG,GAAG;AAC7B,aAAO,iBAAiBA,MAAK,KAAK,MAAM,UAAU,MAAM,CAAC,CAAC,CAAC;AAAA,IAC7D;AACA,WAAO;AAAA,EACT;AAEA,QAAM,OAAOA,MAAK,QAAQA,MAAK,QAAQ,QAAQ,CAAC;AAChD,SAAO,iBAAiBA,MAAK,QAAQ,MAAM,SAAS,CAAC;AACvD;AAGA,SAAS,iBAAiB,GAA0B;AAClD,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJA,MAAK,KAAK,GAAG,UAAU;AAAA,IACvBA,MAAK,KAAK,GAAG,UAAU;AAAA,IACvBA,MAAK,KAAK,GAAG,WAAW;AAAA,IACxBA,MAAK,KAAK,GAAG,WAAW;AAAA,IACxBA,MAAK,KAAK,GAAG,UAAU;AAAA,EACzB;AACA,aAAW,KAAK,YAAY;AAC1B,QAAIC,IAAG,WAAW,CAAC,KAAKA,IAAG,SAAS,CAAC,EAAE,OAAO,EAAG,QAAOD,MAAK,QAAQ,CAAC;AAAA,EACxE;AACA,SAAO;AACT;AAaO,SAAS,cAAc,MAAmB;AAC/C,QAAM,QAAQ,IAAI,aAAa;AAC/B,QAAM,EAAE,MAAM,UAAU,iBAAiB,UAAU,IAAI;AAGvD,QAAM,UAAU,oBAAI,IAA4B;AAChD,QAAM,cAAc;AAEpB,iBAAe,OAAO,UAAkB;AACtC,UAAM,MAAMA,MAAK,QAAQ,QAAQ;AACjC,QAAI;AACJ,QAAI;AACF,YAAM,KAAKC,IAAG,SAAS,GAAG;AAC1B,UAAI,CAAC,GAAG,OAAO,EAAG;AAClB,eAASA,IAAG,aAAa,KAAK,MAAM;AAAA,IACtC,QAAQ;AACN;AAAA,IACF;AACA,UAAM,OAAO,OAAO,MAAM;AAC1B,UAAM,SAAS,MAAM,QAAQ,IAAI,GAAG;AACpC,QAAI,UAAU,OAAO,SAAS,KAAM;AAEpC,UAAM,MAAM,WAAW;AACvB,UAAM,EAAE,MAAM,SAAS,IAAI,MAAM,MAAM,KAAK,MAAM;AAClD,UAAM,UAAU,MAAM,eAAe,KAAK,MAAM;AAChD,UAAM,QAAoB;AAAA,MACxB;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA,SAAS,QACN,IAAI,CAAC,SAAS,cAAc,KAAK,MAAM,IAAI,CAAC,EAC5C,OAAO,CAAC,MAAmB,MAAM,IAAI;AAAA,IAC1C;AACA,UAAM,QAAQ,IAAI,KAAK,KAAK;AAC5B,gBAAY,KAAK,KAAK;AAAA,EACxB;AAEA,QAAM,UAAqB,SAAS,MAAM,MAAM;AAAA;AAAA;AAAA,IAG9C,QAAQ,IAAI,OAAO;AACjB,UAAI,SAAS,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,YAAY,EAAG,QAAO;AAC7D,aAAO,UAAUD,MAAK,QAAQ,OAAO,EAAE,CAAC,GAAG,OAAO;AAAA,IACpD;AAAA,IACA,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,kBAAkB,EAAE,oBAAoB,IAAI,cAAc,GAAG;AAAA,EAC/D,CAAC;AAED,WAAS,SAAS,UAAkB;AAClC,UAAM,MAAMA,MAAK,QAAQ,QAAQ;AACjC,UAAM,WAAW,QAAQ,IAAI,GAAG;AAChC,QAAI,SAAU,cAAa,QAAQ;AAEnC,QAAI,CAAC,SAAU,MAAK,OAAO,GAAG;AAC9B,YAAQ;AAAA,MACN;AAAA,MACA,WAAW,MAAM,QAAQ,OAAO,GAAG,GAAG,WAAW;AAAA,IACnD;AAAA,EACF;AAEA,UAAQ,GAAG,OAAO,QAAQ;AAC1B,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,UAAU,CAAC,aAAqB;AACzC,UAAM,MAAMA,MAAK,QAAQ,QAAQ;AACjC,UAAM,QAAQ,OAAO,GAAG;AAAA,EAC1B,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA;AAAA,IAEA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKP,MAAM,WAA4B;AAChC,YAAM,QAAkB,CAAC;AACzB,YAAM,KAAK,MAAM,CAAC,MAAM,MAAM,KAAK,CAAC,GAAG,OAAO;AAC9C,UAAI,KAAK;AACT,iBAAW,KAAK,OAAO;AACrB,YAAI;AACF,gBAAM,OAAO,CAAC;AACd;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IACA,OAAO,MAAM,QAAQ,MAAM;AAAA,EAC7B;AACF;AAGA,eAAe,KACb,MACA,MACA,SACe;AACf,QAAM,UAAU,MAAMC,IAAG,SAAS,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AACvE,aAAW,KAAK,SAAS;AACvB,UAAM,MAAMD,MAAK,KAAK,MAAM,EAAE,IAAI;AAClC,QAAI,UAAU,KAAK,OAAO,EAAG;AAC7B,QAAI,EAAE,YAAY,GAAG;AACnB,YAAM,KAAK,KAAK,MAAM,OAAO;AAAA,IAC/B,WAAW,EAAE,OAAO,GAAG;AACrB,WAAK,GAAG;AAAA,IACV;AAAA,EAGF;AACF;AAEA,SAAS,UAAU,KAAa,SAA6B;AAC3D,aAAW,KAAK,SAAS;AACvB,QAAI,OAAO,MAAM,YAAY,IAAI,SAAS,CAAC,EAAG,QAAO;AACrD,QAAI,aAAa,UAAU,EAAE,KAAK,GAAG,EAAG,QAAO;AAAA,EACjD;AACA,SAAO;AACT;","names":["fs","path","dir","path","require","path","fs","path","path","fs"]}