kly 0.0.1

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,3000 @@
1
+ import { createRequire } from "node:module";
2
+ import { createHash } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { parse, stringify } from "yaml";
6
+ import Database from "better-sqlite3";
7
+ import { globby } from "globby";
8
+ import { execSync } from "node:child_process";
9
+ import Parser from "tree-sitter";
10
+ import Swift from "tree-sitter-swift";
11
+ import TypeScript from "tree-sitter-typescript";
12
+ import { complete, getModel } from "@mariozechner/pi-ai";
13
+ import pLimit from "p-limit";
14
+ import { renderMermaidASCII, renderMermaidSVG } from "beautiful-mermaid";
15
+ //#region \0rolldown/runtime.js
16
+ var __create = Object.create;
17
+ var __defProp = Object.defineProperty;
18
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
19
+ var __getOwnPropNames = Object.getOwnPropertyNames;
20
+ var __getProtoOf = Object.getPrototypeOf;
21
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
22
+ var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
23
+ var __copyProps = (to, from, except, desc) => {
24
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
25
+ key = keys[i];
26
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
27
+ get: ((k) => from[k]).bind(null, key),
28
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
+ });
30
+ }
31
+ return to;
32
+ };
33
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
34
+ value: mod,
35
+ enumerable: true
36
+ }) : target, mod));
37
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
38
+ //#endregion
39
+ //#region src/config.ts
40
+ const KLY_DIR = ".kly";
41
+ const CONFIG_FILE = "config.yaml";
42
+ const DB_DIR = "db";
43
+ const STATE_FILE = "state.yaml";
44
+ const DEFAULT_CONFIG = {
45
+ llm: {
46
+ provider: "openrouter",
47
+ model: "anthropic/claude-haiku-4.5",
48
+ apiKey: ""
49
+ },
50
+ include: [
51
+ "**/*.ts",
52
+ "**/*.tsx",
53
+ "**/*.js",
54
+ "**/*.jsx",
55
+ "**/*.swift"
56
+ ],
57
+ exclude: [
58
+ "**/node_modules/**",
59
+ "**/dist/**",
60
+ "**/build/**",
61
+ "**/.git/**",
62
+ "**/.kly/**",
63
+ "**/vendor/**",
64
+ "**/*.d.ts",
65
+ "**/*.test.*",
66
+ "**/*.spec.*",
67
+ "**/__tests__/**"
68
+ ]
69
+ };
70
+ function getKlyDir(root) {
71
+ return path.join(root, KLY_DIR);
72
+ }
73
+ function getConfigPath(root) {
74
+ return path.join(getKlyDir(root), CONFIG_FILE);
75
+ }
76
+ function getDbDir(root) {
77
+ return path.join(getKlyDir(root), DB_DIR);
78
+ }
79
+ function getDbPath(root, dbName) {
80
+ return path.join(getDbDir(root), `${dbName}.db`);
81
+ }
82
+ function getStatePath(root) {
83
+ return path.join(getKlyDir(root), STATE_FILE);
84
+ }
85
+ function isInitialized(root) {
86
+ return fs.existsSync(getKlyDir(root));
87
+ }
88
+ function initKlyDir(root, config) {
89
+ const klyDir = getKlyDir(root);
90
+ if (!fs.existsSync(klyDir)) fs.mkdirSync(klyDir, { recursive: true });
91
+ const dbDir = getDbDir(root);
92
+ if (!fs.existsSync(dbDir)) fs.mkdirSync(dbDir, { recursive: true });
93
+ const configToWrite = config || DEFAULT_CONFIG;
94
+ fs.writeFileSync(getConfigPath(root), stringify(configToWrite), "utf-8");
95
+ }
96
+ function loadConfig(root) {
97
+ const configPath = getConfigPath(root);
98
+ if (!fs.existsSync(configPath)) return DEFAULT_CONFIG;
99
+ const parsed = parse(fs.readFileSync(configPath, "utf-8"));
100
+ return {
101
+ ...DEFAULT_CONFIG,
102
+ ...parsed,
103
+ llm: {
104
+ ...DEFAULT_CONFIG.llm,
105
+ ...parsed.llm
106
+ }
107
+ };
108
+ }
109
+ function hashConfig(config) {
110
+ const significant = JSON.stringify({
111
+ include: config.include,
112
+ exclude: config.exclude
113
+ });
114
+ return createHash("sha256").update(significant).digest("hex");
115
+ }
116
+ //#endregion
117
+ //#region src/database.ts
118
+ var IndexDatabase = class {
119
+ db;
120
+ constructor(dbPath) {
121
+ this.db = new Database(dbPath);
122
+ this.db.pragma("journal_mode = WAL");
123
+ this.db.pragma("foreign_keys = ON");
124
+ this.init();
125
+ }
126
+ init() {
127
+ this.db.exec(`
128
+ CREATE TABLE IF NOT EXISTS files (
129
+ path TEXT PRIMARY KEY,
130
+ name TEXT NOT NULL,
131
+ description TEXT NOT NULL DEFAULT '',
132
+ language TEXT NOT NULL,
133
+ imports TEXT NOT NULL DEFAULT '[]',
134
+ exports TEXT NOT NULL DEFAULT '[]',
135
+ symbols TEXT NOT NULL DEFAULT '[]',
136
+ summary TEXT NOT NULL DEFAULT '',
137
+ hash TEXT NOT NULL,
138
+ indexed_at INTEGER NOT NULL
139
+ );
140
+
141
+ CREATE TABLE IF NOT EXISTS metadata (
142
+ key TEXT PRIMARY KEY,
143
+ value TEXT NOT NULL
144
+ );
145
+
146
+ CREATE TABLE IF NOT EXISTS dependencies (
147
+ from_path TEXT NOT NULL,
148
+ to_path TEXT NOT NULL,
149
+ PRIMARY KEY (from_path, to_path)
150
+ );
151
+
152
+ CREATE INDEX IF NOT EXISTS idx_deps_to ON dependencies(to_path);
153
+ `);
154
+ try {
155
+ this.db.exec(`
156
+ CREATE VIRTUAL TABLE files_fts USING fts5(
157
+ path, name, description, summary, symbols_text,
158
+ content=files,
159
+ content_rowid=rowid
160
+ );
161
+ `);
162
+ } catch {}
163
+ this.db.exec(`
164
+ CREATE TRIGGER IF NOT EXISTS files_ai AFTER INSERT ON files BEGIN
165
+ INSERT INTO files_fts(rowid, path, name, description, summary, symbols_text)
166
+ VALUES (new.rowid, new.path, new.name, new.description, new.summary, '');
167
+ END;
168
+
169
+ CREATE TRIGGER IF NOT EXISTS files_ad AFTER DELETE ON files BEGIN
170
+ INSERT INTO files_fts(files_fts, rowid, path, name, description, summary, symbols_text)
171
+ VALUES ('delete', old.rowid, old.path, old.name, old.description, old.summary, '');
172
+ END;
173
+
174
+ CREATE TRIGGER IF NOT EXISTS files_au AFTER UPDATE ON files BEGIN
175
+ INSERT INTO files_fts(files_fts, rowid, path, name, description, summary, symbols_text)
176
+ VALUES ('delete', old.rowid, old.path, old.name, old.description, old.summary, '');
177
+ INSERT INTO files_fts(rowid, path, name, description, summary, symbols_text)
178
+ VALUES (new.rowid, new.path, new.name, new.description, new.summary, '');
179
+ END;
180
+ `);
181
+ }
182
+ getFile(filePath) {
183
+ const row = this.db.prepare("SELECT * FROM files WHERE path = ?").get(filePath);
184
+ return row ? rowToFileIndex(row) : void 0;
185
+ }
186
+ upsertFile(fileIndex) {
187
+ this.db.prepare(`
188
+ INSERT INTO files (path, name, description, language, imports, exports, symbols, summary, hash, indexed_at)
189
+ VALUES (@path, @name, @description, @language, @imports, @exports, @symbols, @summary, @hash, @indexed_at)
190
+ ON CONFLICT(path) DO UPDATE SET
191
+ name = @name,
192
+ description = @description,
193
+ language = @language,
194
+ imports = @imports,
195
+ exports = @exports,
196
+ symbols = @symbols,
197
+ summary = @summary,
198
+ hash = @hash,
199
+ indexed_at = @indexed_at
200
+ `).run(fileIndexToRow(fileIndex));
201
+ this.updateFtsSymbolsText(fileIndex);
202
+ }
203
+ upsertFiles(fileIndexes) {
204
+ const stmt = this.db.prepare(`
205
+ INSERT INTO files (path, name, description, language, imports, exports, symbols, summary, hash, indexed_at)
206
+ VALUES (@path, @name, @description, @language, @imports, @exports, @symbols, @summary, @hash, @indexed_at)
207
+ ON CONFLICT(path) DO UPDATE SET
208
+ name = @name,
209
+ description = @description,
210
+ language = @language,
211
+ imports = @imports,
212
+ exports = @exports,
213
+ symbols = @symbols,
214
+ summary = @summary,
215
+ hash = @hash,
216
+ indexed_at = @indexed_at
217
+ `);
218
+ this.db.transaction((files) => {
219
+ for (const file of files) {
220
+ stmt.run(fileIndexToRow(file));
221
+ this.updateFtsSymbolsText(file);
222
+ }
223
+ })(fileIndexes);
224
+ }
225
+ removeFile(filePath) {
226
+ this.db.prepare("DELETE FROM files WHERE path = ?").run(filePath);
227
+ }
228
+ removeFiles(paths) {
229
+ const stmt = this.db.prepare("DELETE FROM files WHERE path = ?");
230
+ this.db.transaction((pathList) => {
231
+ for (const p of pathList) stmt.run(p);
232
+ })(paths);
233
+ }
234
+ searchFiles(query, limit = 20) {
235
+ const ftsQuery = query.split(/\s+/).filter(Boolean).map((term) => `"${term.replace(/"/g, "\"\"")}"`).join(" OR ");
236
+ if (!ftsQuery) return [];
237
+ return this.db.prepare(`
238
+ SELECT f.*, bm25(files_fts) as rank
239
+ FROM files_fts fts
240
+ JOIN files f ON f.rowid = fts.rowid
241
+ WHERE files_fts MATCH ?
242
+ ORDER BY rank
243
+ LIMIT ?
244
+ `).all(ftsQuery, limit).map((row) => ({
245
+ file: rowToFileIndex(row),
246
+ score: -row.rank
247
+ }));
248
+ }
249
+ getAllFiles() {
250
+ return this.db.prepare("SELECT * FROM files ORDER BY path").all().map(rowToFileIndex);
251
+ }
252
+ getFileCount() {
253
+ return this.db.prepare("SELECT COUNT(*) as count FROM files").get().count;
254
+ }
255
+ getLanguageStats() {
256
+ const rows = this.db.prepare("SELECT language, COUNT(*) as count FROM files GROUP BY language").all();
257
+ const stats = {};
258
+ for (const row of rows) stats[row.language] = row.count;
259
+ return stats;
260
+ }
261
+ getMetadata(key) {
262
+ return this.db.prepare("SELECT value FROM metadata WHERE key = ?").get(key)?.value;
263
+ }
264
+ setMetadata(key, value) {
265
+ this.db.prepare(`INSERT INTO metadata (key, value) VALUES (?, ?)
266
+ ON CONFLICT(key) DO UPDATE SET value = ?`).run(key, value, value);
267
+ }
268
+ upsertDependencies(fromPath, toPaths) {
269
+ this.db.prepare("DELETE FROM dependencies WHERE from_path = ?").run(fromPath);
270
+ const stmt = this.db.prepare("INSERT OR IGNORE INTO dependencies (from_path, to_path) VALUES (?, ?)");
271
+ for (const to of toPaths) stmt.run(fromPath, to);
272
+ }
273
+ upsertBatchDependencies(entries) {
274
+ const del = this.db.prepare("DELETE FROM dependencies WHERE from_path = ?");
275
+ const ins = this.db.prepare("INSERT OR IGNORE INTO dependencies (from_path, to_path) VALUES (?, ?)");
276
+ this.db.transaction((items) => {
277
+ for (const { fromPath, toPaths } of items) {
278
+ del.run(fromPath);
279
+ for (const to of toPaths) ins.run(fromPath, to);
280
+ }
281
+ })(entries);
282
+ }
283
+ removeDependencies(fromPath) {
284
+ this.db.prepare("DELETE FROM dependencies WHERE from_path = ?").run(fromPath);
285
+ }
286
+ removeDependenciesBatch(fromPaths) {
287
+ const stmt = this.db.prepare("DELETE FROM dependencies WHERE from_path = ?");
288
+ this.db.transaction((paths) => {
289
+ for (const p of paths) stmt.run(p);
290
+ })(fromPaths);
291
+ }
292
+ getDependencies(filePath) {
293
+ return this.db.prepare("SELECT to_path FROM dependencies WHERE from_path = ? ORDER BY to_path").all(filePath).map((r) => r.to_path);
294
+ }
295
+ getDependents(filePath) {
296
+ return this.db.prepare("SELECT from_path FROM dependencies WHERE to_path = ? ORDER BY from_path").all(filePath).map((r) => r.from_path);
297
+ }
298
+ removeAllDependencies() {
299
+ this.db.exec("DELETE FROM dependencies");
300
+ }
301
+ close() {
302
+ this.db.close();
303
+ }
304
+ updateFtsSymbolsText(fileIndex) {
305
+ const symbolsText = fileIndex.symbols.map((s) => `${s.name} ${s.description}`).join(" ");
306
+ const row = this.db.prepare("SELECT rowid FROM files WHERE path = ?").get(fileIndex.path);
307
+ this.db.prepare(`INSERT INTO files_fts(files_fts, rowid, path, name, description, summary, symbols_text)
308
+ VALUES ('delete', ?, ?, ?, ?, ?, '')`).run(row.rowid, fileIndex.path, fileIndex.name, fileIndex.description, fileIndex.summary);
309
+ this.db.prepare(`INSERT INTO files_fts(rowid, path, name, description, summary, symbols_text)
310
+ VALUES (?, ?, ?, ?, ?, ?)`).run(row.rowid, fileIndex.path, fileIndex.name, fileIndex.description, fileIndex.summary, symbolsText);
311
+ }
312
+ };
313
+ function rowToFileIndex(row) {
314
+ return {
315
+ path: row.path,
316
+ name: row.name,
317
+ description: row.description,
318
+ language: row.language,
319
+ imports: JSON.parse(row.imports),
320
+ exports: JSON.parse(row.exports),
321
+ symbols: JSON.parse(row.symbols),
322
+ summary: row.summary,
323
+ hash: row.hash,
324
+ indexedAt: row.indexed_at
325
+ };
326
+ }
327
+ function fileIndexToRow(fileIndex) {
328
+ return {
329
+ path: fileIndex.path,
330
+ name: fileIndex.name,
331
+ description: fileIndex.description,
332
+ language: fileIndex.language,
333
+ imports: JSON.stringify(fileIndex.imports),
334
+ exports: JSON.stringify(fileIndex.exports),
335
+ symbols: JSON.stringify(fileIndex.symbols),
336
+ summary: fileIndex.summary,
337
+ hash: fileIndex.hash,
338
+ indexed_at: fileIndex.indexedAt
339
+ };
340
+ }
341
+ //#endregion
342
+ //#region src/scanner.ts
343
+ async function scanFiles(root, config) {
344
+ return (await globby(config.include, {
345
+ cwd: root,
346
+ ignore: config.exclude,
347
+ gitignore: true,
348
+ absolute: false
349
+ })).sort();
350
+ }
351
+ //#endregion
352
+ //#region src/hasher.ts
353
+ function hashFile(root, filePath) {
354
+ const fullPath = path.join(root, filePath);
355
+ const content = fs.readFileSync(fullPath, "utf-8");
356
+ return createHash("sha256").update(content).digest("hex");
357
+ }
358
+ function hasChanged(oldHash, newHash) {
359
+ return oldHash !== newHash;
360
+ }
361
+ //#endregion
362
+ //#region src/git.ts
363
+ function exec(root, cmd) {
364
+ return execSync(cmd, {
365
+ cwd: root,
366
+ encoding: "utf-8",
367
+ stdio: [
368
+ "pipe",
369
+ "pipe",
370
+ "pipe"
371
+ ]
372
+ }).trim();
373
+ }
374
+ function isGitRepo(root) {
375
+ try {
376
+ exec(root, "git rev-parse --is-inside-work-tree");
377
+ return true;
378
+ } catch {
379
+ return false;
380
+ }
381
+ }
382
+ function getCurrentBranch(root) {
383
+ try {
384
+ return exec(root, "git symbolic-ref --short HEAD");
385
+ } catch {
386
+ return null;
387
+ }
388
+ }
389
+ function getCurrentCommit(root) {
390
+ return exec(root, "git rev-parse HEAD");
391
+ }
392
+ function getChangedFiles(root, from, to) {
393
+ const output = exec(root, `git diff --name-status ${from} ${to || "HEAD"}`);
394
+ const diff = {
395
+ added: [],
396
+ modified: [],
397
+ deleted: [],
398
+ renamed: []
399
+ };
400
+ if (!output) return diff;
401
+ for (const line of output.split("\n")) {
402
+ const parts = line.split(" ");
403
+ const status = parts[0];
404
+ const filePath = parts[1];
405
+ switch (status[0]) {
406
+ case "A":
407
+ diff.added.push(filePath);
408
+ break;
409
+ case "M":
410
+ diff.modified.push(filePath);
411
+ break;
412
+ case "D":
413
+ diff.deleted.push(filePath);
414
+ break;
415
+ case "R":
416
+ diff.renamed.push({
417
+ from: filePath,
418
+ to: parts[2]
419
+ });
420
+ break;
421
+ }
422
+ }
423
+ return diff;
424
+ }
425
+ function isAncestor(root, ancestor, descendant) {
426
+ try {
427
+ execSync(`git merge-base --is-ancestor ${ancestor} ${descendant}`, {
428
+ cwd: root,
429
+ stdio: [
430
+ "pipe",
431
+ "pipe",
432
+ "pipe"
433
+ ]
434
+ });
435
+ return true;
436
+ } catch {
437
+ return false;
438
+ }
439
+ }
440
+ function getMergeBase(root, a, b) {
441
+ try {
442
+ return exec(root, `git merge-base ${a} ${b}`);
443
+ } catch {
444
+ return null;
445
+ }
446
+ }
447
+ function getFileHistory(root, filePath, limit = 5) {
448
+ let output;
449
+ try {
450
+ output = exec(root, `git log --follow -n ${limit} --format="%H|%an|%ae|%at|%s" -- "${filePath}"`);
451
+ } catch {
452
+ return [];
453
+ }
454
+ if (!output) return [];
455
+ return output.split("\n").filter(Boolean).map((line) => {
456
+ const [hash, author, email, date, ...msgParts] = line.split("|");
457
+ return {
458
+ hash,
459
+ author,
460
+ email,
461
+ date: parseInt(date, 10),
462
+ message: msgParts.join("|")
463
+ };
464
+ });
465
+ }
466
+ function branchToDbName(branch, commitHash) {
467
+ if (branch === null) return `_detached--${commitHash ? commitHash.slice(0, 8) : "unknown"}`;
468
+ return branch.replace(/\//g, "--");
469
+ }
470
+ //#endregion
471
+ //#region src/store.ts
472
+ const STATE_VERSION = 2;
473
+ function openDatabase(root, dbName) {
474
+ const name = dbName || resolveDbName(root);
475
+ const dbDir = getDbDir(root);
476
+ if (!fs.existsSync(dbDir)) fs.mkdirSync(dbDir, { recursive: true });
477
+ return new IndexDatabase(getDbPath(root, name));
478
+ }
479
+ function resolveDbName(root) {
480
+ if (!isGitRepo(root)) return "default";
481
+ const branch = getCurrentBranch(root);
482
+ return branchToDbName(branch, branch === null ? getCurrentCommit(root) : void 0);
483
+ }
484
+ function copyDatabase(root, fromName, toName) {
485
+ const fromPath = getDbPath(root, fromName);
486
+ const toPath = getDbPath(root, toName);
487
+ if (fs.existsSync(fromPath)) fs.copyFileSync(fromPath, toPath);
488
+ }
489
+ function loadState(root) {
490
+ const statePath = getStatePath(root);
491
+ if (!fs.existsSync(statePath)) return {
492
+ version: STATE_VERSION,
493
+ configHash: "",
494
+ branches: {}
495
+ };
496
+ return parse(fs.readFileSync(statePath, "utf-8"));
497
+ }
498
+ function saveState(root, state) {
499
+ const statePath = getStatePath(root);
500
+ fs.writeFileSync(statePath, stringify(state), "utf-8");
501
+ }
502
+ function getBranchState(state, dbName) {
503
+ return state.branches[dbName];
504
+ }
505
+ function setBranchState(state, dbName, branchState) {
506
+ state.branches[dbName] = branchState;
507
+ }
508
+ function getFileFromDb(root, filePath) {
509
+ const db = openDatabase(root);
510
+ try {
511
+ return db.getFile(filePath);
512
+ } finally {
513
+ db.close();
514
+ }
515
+ }
516
+ function getAllFilesFromDb(root) {
517
+ const db = openDatabase(root);
518
+ try {
519
+ return db.getAllFiles();
520
+ } finally {
521
+ db.close();
522
+ }
523
+ }
524
+ function listBranchDbs(root) {
525
+ const dbDir = getDbDir(root);
526
+ if (!fs.existsSync(dbDir)) return [];
527
+ return fs.readdirSync(dbDir).filter((f) => f.endsWith(".db")).map((f) => f.replace(/\.db$/, ""));
528
+ }
529
+ function removeBranchDb(root, dbName) {
530
+ const dbPath = getDbPath(root, dbName);
531
+ if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
532
+ for (const ext of ["-wal", "-shm"]) {
533
+ const auxPath = dbPath + ext;
534
+ if (fs.existsSync(auxPath)) fs.unlinkSync(auxPath);
535
+ }
536
+ }
537
+ //#endregion
538
+ //#region node_modules/picomatch/lib/constants.js
539
+ var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
540
+ const path$3 = __require("path");
541
+ const WIN_SLASH = "\\\\/";
542
+ const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
543
+ /**
544
+ * Posix glob regex
545
+ */
546
+ const DOT_LITERAL = "\\.";
547
+ const PLUS_LITERAL = "\\+";
548
+ const QMARK_LITERAL = "\\?";
549
+ const SLASH_LITERAL = "\\/";
550
+ const ONE_CHAR = "(?=.)";
551
+ const QMARK = "[^/]";
552
+ const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
553
+ const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
554
+ const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
555
+ const POSIX_CHARS = {
556
+ DOT_LITERAL,
557
+ PLUS_LITERAL,
558
+ QMARK_LITERAL,
559
+ SLASH_LITERAL,
560
+ ONE_CHAR,
561
+ QMARK,
562
+ END_ANCHOR,
563
+ DOTS_SLASH,
564
+ NO_DOT: `(?!${DOT_LITERAL})`,
565
+ NO_DOTS: `(?!${START_ANCHOR}${DOTS_SLASH})`,
566
+ NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`,
567
+ NO_DOTS_SLASH: `(?!${DOTS_SLASH})`,
568
+ QMARK_NO_DOT: `[^.${SLASH_LITERAL}]`,
569
+ STAR: `${QMARK}*?`,
570
+ START_ANCHOR
571
+ };
572
+ /**
573
+ * Windows glob regex
574
+ */
575
+ const WINDOWS_CHARS = {
576
+ ...POSIX_CHARS,
577
+ SLASH_LITERAL: `[${WIN_SLASH}]`,
578
+ QMARK: WIN_NO_SLASH,
579
+ STAR: `${WIN_NO_SLASH}*?`,
580
+ DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
581
+ NO_DOT: `(?!${DOT_LITERAL})`,
582
+ NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
583
+ NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
584
+ NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
585
+ QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
586
+ START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
587
+ END_ANCHOR: `(?:[${WIN_SLASH}]|$)`
588
+ };
589
+ module.exports = {
590
+ MAX_LENGTH: 1024 * 64,
591
+ POSIX_REGEX_SOURCE: {
592
+ alnum: "a-zA-Z0-9",
593
+ alpha: "a-zA-Z",
594
+ ascii: "\\x00-\\x7F",
595
+ blank: " \\t",
596
+ cntrl: "\\x00-\\x1F\\x7F",
597
+ digit: "0-9",
598
+ graph: "\\x21-\\x7E",
599
+ lower: "a-z",
600
+ print: "\\x20-\\x7E ",
601
+ punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",
602
+ space: " \\t\\r\\n\\v\\f",
603
+ upper: "A-Z",
604
+ word: "A-Za-z0-9_",
605
+ xdigit: "A-Fa-f0-9"
606
+ },
607
+ REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
608
+ REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
609
+ REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
610
+ REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
611
+ REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
612
+ REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
613
+ REPLACEMENTS: {
614
+ "***": "*",
615
+ "**/**": "**",
616
+ "**/**/**": "**"
617
+ },
618
+ CHAR_0: 48,
619
+ CHAR_9: 57,
620
+ CHAR_UPPERCASE_A: 65,
621
+ CHAR_LOWERCASE_A: 97,
622
+ CHAR_UPPERCASE_Z: 90,
623
+ CHAR_LOWERCASE_Z: 122,
624
+ CHAR_LEFT_PARENTHESES: 40,
625
+ CHAR_RIGHT_PARENTHESES: 41,
626
+ CHAR_ASTERISK: 42,
627
+ CHAR_AMPERSAND: 38,
628
+ CHAR_AT: 64,
629
+ CHAR_BACKWARD_SLASH: 92,
630
+ CHAR_CARRIAGE_RETURN: 13,
631
+ CHAR_CIRCUMFLEX_ACCENT: 94,
632
+ CHAR_COLON: 58,
633
+ CHAR_COMMA: 44,
634
+ CHAR_DOT: 46,
635
+ CHAR_DOUBLE_QUOTE: 34,
636
+ CHAR_EQUAL: 61,
637
+ CHAR_EXCLAMATION_MARK: 33,
638
+ CHAR_FORM_FEED: 12,
639
+ CHAR_FORWARD_SLASH: 47,
640
+ CHAR_GRAVE_ACCENT: 96,
641
+ CHAR_HASH: 35,
642
+ CHAR_HYPHEN_MINUS: 45,
643
+ CHAR_LEFT_ANGLE_BRACKET: 60,
644
+ CHAR_LEFT_CURLY_BRACE: 123,
645
+ CHAR_LEFT_SQUARE_BRACKET: 91,
646
+ CHAR_LINE_FEED: 10,
647
+ CHAR_NO_BREAK_SPACE: 160,
648
+ CHAR_PERCENT: 37,
649
+ CHAR_PLUS: 43,
650
+ CHAR_QUESTION_MARK: 63,
651
+ CHAR_RIGHT_ANGLE_BRACKET: 62,
652
+ CHAR_RIGHT_CURLY_BRACE: 125,
653
+ CHAR_RIGHT_SQUARE_BRACKET: 93,
654
+ CHAR_SEMICOLON: 59,
655
+ CHAR_SINGLE_QUOTE: 39,
656
+ CHAR_SPACE: 32,
657
+ CHAR_TAB: 9,
658
+ CHAR_UNDERSCORE: 95,
659
+ CHAR_VERTICAL_LINE: 124,
660
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
661
+ SEP: path$3.sep,
662
+ extglobChars(chars) {
663
+ return {
664
+ "!": {
665
+ type: "negate",
666
+ open: "(?:(?!(?:",
667
+ close: `))${chars.STAR})`
668
+ },
669
+ "?": {
670
+ type: "qmark",
671
+ open: "(?:",
672
+ close: ")?"
673
+ },
674
+ "+": {
675
+ type: "plus",
676
+ open: "(?:",
677
+ close: ")+"
678
+ },
679
+ "*": {
680
+ type: "star",
681
+ open: "(?:",
682
+ close: ")*"
683
+ },
684
+ "@": {
685
+ type: "at",
686
+ open: "(?:",
687
+ close: ")"
688
+ }
689
+ };
690
+ },
691
+ globChars(win32) {
692
+ return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
693
+ }
694
+ };
695
+ }));
696
+ //#endregion
697
+ //#region node_modules/picomatch/lib/utils.js
698
+ var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
699
+ const path$2 = __require("path");
700
+ const win32 = process.platform === "win32";
701
+ const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = require_constants();
702
+ exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
703
+ exports.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str);
704
+ exports.isRegexChar = (str) => str.length === 1 && exports.hasRegexChars(str);
705
+ exports.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
706
+ exports.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/");
707
+ exports.removeBackslashes = (str) => {
708
+ return str.replace(REGEX_REMOVE_BACKSLASH, (match) => {
709
+ return match === "\\" ? "" : match;
710
+ });
711
+ };
712
+ exports.supportsLookbehinds = () => {
713
+ const segs = process.version.slice(1).split(".").map(Number);
714
+ if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) return true;
715
+ return false;
716
+ };
717
+ exports.isWindows = (options) => {
718
+ if (options && typeof options.windows === "boolean") return options.windows;
719
+ return win32 === true || path$2.sep === "\\";
720
+ };
721
+ exports.escapeLast = (input, char, lastIdx) => {
722
+ const idx = input.lastIndexOf(char, lastIdx);
723
+ if (idx === -1) return input;
724
+ if (input[idx - 1] === "\\") return exports.escapeLast(input, char, idx - 1);
725
+ return `${input.slice(0, idx)}\\${input.slice(idx)}`;
726
+ };
727
+ exports.removePrefix = (input, state = {}) => {
728
+ let output = input;
729
+ if (output.startsWith("./")) {
730
+ output = output.slice(2);
731
+ state.prefix = "./";
732
+ }
733
+ return output;
734
+ };
735
+ exports.wrapOutput = (input, state = {}, options = {}) => {
736
+ let output = `${options.contains ? "" : "^"}(?:${input})${options.contains ? "" : "$"}`;
737
+ if (state.negated === true) output = `(?:^(?!${output}).*$)`;
738
+ return output;
739
+ };
740
+ }));
741
+ //#endregion
742
+ //#region node_modules/picomatch/lib/scan.js
743
+ var require_scan = /* @__PURE__ */ __commonJSMin(((exports, module) => {
744
+ const utils = require_utils();
745
+ const { CHAR_ASTERISK, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_COMMA, CHAR_DOT, CHAR_EXCLAMATION_MARK, CHAR_FORWARD_SLASH, CHAR_LEFT_CURLY_BRACE, CHAR_LEFT_PARENTHESES, CHAR_LEFT_SQUARE_BRACKET, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_CURLY_BRACE, CHAR_RIGHT_PARENTHESES, CHAR_RIGHT_SQUARE_BRACKET } = require_constants();
746
+ const isPathSeparator = (code) => {
747
+ return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
748
+ };
749
+ const depth = (token) => {
750
+ if (token.isPrefix !== true) token.depth = token.isGlobstar ? Infinity : 1;
751
+ };
752
+ /**
753
+ * Quickly scans a glob pattern and returns an object with a handful of
754
+ * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists),
755
+ * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not
756
+ * with `!(`) and `negatedExtglob` (true if the path starts with `!(`).
757
+ *
758
+ * ```js
759
+ * const pm = require('picomatch');
760
+ * console.log(pm.scan('foo/bar/*.js'));
761
+ * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
762
+ * ```
763
+ * @param {String} `str`
764
+ * @param {Object} `options`
765
+ * @return {Object} Returns an object with tokens and regex source string.
766
+ * @api public
767
+ */
768
+ const scan = (input, options) => {
769
+ const opts = options || {};
770
+ const length = input.length - 1;
771
+ const scanToEnd = opts.parts === true || opts.scanToEnd === true;
772
+ const slashes = [];
773
+ const tokens = [];
774
+ const parts = [];
775
+ let str = input;
776
+ let index = -1;
777
+ let start = 0;
778
+ let lastIndex = 0;
779
+ let isBrace = false;
780
+ let isBracket = false;
781
+ let isGlob = false;
782
+ let isExtglob = false;
783
+ let isGlobstar = false;
784
+ let braceEscaped = false;
785
+ let backslashes = false;
786
+ let negated = false;
787
+ let negatedExtglob = false;
788
+ let finished = false;
789
+ let braces = 0;
790
+ let prev;
791
+ let code;
792
+ let token = {
793
+ value: "",
794
+ depth: 0,
795
+ isGlob: false
796
+ };
797
+ const eos = () => index >= length;
798
+ const peek = () => str.charCodeAt(index + 1);
799
+ const advance = () => {
800
+ prev = code;
801
+ return str.charCodeAt(++index);
802
+ };
803
+ while (index < length) {
804
+ code = advance();
805
+ let next;
806
+ if (code === CHAR_BACKWARD_SLASH) {
807
+ backslashes = token.backslashes = true;
808
+ code = advance();
809
+ if (code === CHAR_LEFT_CURLY_BRACE) braceEscaped = true;
810
+ continue;
811
+ }
812
+ if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
813
+ braces++;
814
+ while (eos() !== true && (code = advance())) {
815
+ if (code === CHAR_BACKWARD_SLASH) {
816
+ backslashes = token.backslashes = true;
817
+ advance();
818
+ continue;
819
+ }
820
+ if (code === CHAR_LEFT_CURLY_BRACE) {
821
+ braces++;
822
+ continue;
823
+ }
824
+ if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
825
+ isBrace = token.isBrace = true;
826
+ isGlob = token.isGlob = true;
827
+ finished = true;
828
+ if (scanToEnd === true) continue;
829
+ break;
830
+ }
831
+ if (braceEscaped !== true && code === CHAR_COMMA) {
832
+ isBrace = token.isBrace = true;
833
+ isGlob = token.isGlob = true;
834
+ finished = true;
835
+ if (scanToEnd === true) continue;
836
+ break;
837
+ }
838
+ if (code === CHAR_RIGHT_CURLY_BRACE) {
839
+ braces--;
840
+ if (braces === 0) {
841
+ braceEscaped = false;
842
+ isBrace = token.isBrace = true;
843
+ finished = true;
844
+ break;
845
+ }
846
+ }
847
+ }
848
+ if (scanToEnd === true) continue;
849
+ break;
850
+ }
851
+ if (code === CHAR_FORWARD_SLASH) {
852
+ slashes.push(index);
853
+ tokens.push(token);
854
+ token = {
855
+ value: "",
856
+ depth: 0,
857
+ isGlob: false
858
+ };
859
+ if (finished === true) continue;
860
+ if (prev === CHAR_DOT && index === start + 1) {
861
+ start += 2;
862
+ continue;
863
+ }
864
+ lastIndex = index + 1;
865
+ continue;
866
+ }
867
+ if (opts.noext !== true) {
868
+ if ((code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK) === true && peek() === CHAR_LEFT_PARENTHESES) {
869
+ isGlob = token.isGlob = true;
870
+ isExtglob = token.isExtglob = true;
871
+ finished = true;
872
+ if (code === CHAR_EXCLAMATION_MARK && index === start) negatedExtglob = true;
873
+ if (scanToEnd === true) {
874
+ while (eos() !== true && (code = advance())) {
875
+ if (code === CHAR_BACKWARD_SLASH) {
876
+ backslashes = token.backslashes = true;
877
+ code = advance();
878
+ continue;
879
+ }
880
+ if (code === CHAR_RIGHT_PARENTHESES) {
881
+ isGlob = token.isGlob = true;
882
+ finished = true;
883
+ break;
884
+ }
885
+ }
886
+ continue;
887
+ }
888
+ break;
889
+ }
890
+ }
891
+ if (code === CHAR_ASTERISK) {
892
+ if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
893
+ isGlob = token.isGlob = true;
894
+ finished = true;
895
+ if (scanToEnd === true) continue;
896
+ break;
897
+ }
898
+ if (code === CHAR_QUESTION_MARK) {
899
+ isGlob = token.isGlob = true;
900
+ finished = true;
901
+ if (scanToEnd === true) continue;
902
+ break;
903
+ }
904
+ if (code === CHAR_LEFT_SQUARE_BRACKET) {
905
+ while (eos() !== true && (next = advance())) {
906
+ if (next === CHAR_BACKWARD_SLASH) {
907
+ backslashes = token.backslashes = true;
908
+ advance();
909
+ continue;
910
+ }
911
+ if (next === CHAR_RIGHT_SQUARE_BRACKET) {
912
+ isBracket = token.isBracket = true;
913
+ isGlob = token.isGlob = true;
914
+ finished = true;
915
+ break;
916
+ }
917
+ }
918
+ if (scanToEnd === true) continue;
919
+ break;
920
+ }
921
+ if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) {
922
+ negated = token.negated = true;
923
+ start++;
924
+ continue;
925
+ }
926
+ if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
927
+ isGlob = token.isGlob = true;
928
+ if (scanToEnd === true) {
929
+ while (eos() !== true && (code = advance())) {
930
+ if (code === CHAR_LEFT_PARENTHESES) {
931
+ backslashes = token.backslashes = true;
932
+ code = advance();
933
+ continue;
934
+ }
935
+ if (code === CHAR_RIGHT_PARENTHESES) {
936
+ finished = true;
937
+ break;
938
+ }
939
+ }
940
+ continue;
941
+ }
942
+ break;
943
+ }
944
+ if (isGlob === true) {
945
+ finished = true;
946
+ if (scanToEnd === true) continue;
947
+ break;
948
+ }
949
+ }
950
+ if (opts.noext === true) {
951
+ isExtglob = false;
952
+ isGlob = false;
953
+ }
954
+ let base = str;
955
+ let prefix = "";
956
+ let glob = "";
957
+ if (start > 0) {
958
+ prefix = str.slice(0, start);
959
+ str = str.slice(start);
960
+ lastIndex -= start;
961
+ }
962
+ if (base && isGlob === true && lastIndex > 0) {
963
+ base = str.slice(0, lastIndex);
964
+ glob = str.slice(lastIndex);
965
+ } else if (isGlob === true) {
966
+ base = "";
967
+ glob = str;
968
+ } else base = str;
969
+ if (base && base !== "" && base !== "/" && base !== str) {
970
+ if (isPathSeparator(base.charCodeAt(base.length - 1))) base = base.slice(0, -1);
971
+ }
972
+ if (opts.unescape === true) {
973
+ if (glob) glob = utils.removeBackslashes(glob);
974
+ if (base && backslashes === true) base = utils.removeBackslashes(base);
975
+ }
976
+ const state = {
977
+ prefix,
978
+ input,
979
+ start,
980
+ base,
981
+ glob,
982
+ isBrace,
983
+ isBracket,
984
+ isGlob,
985
+ isExtglob,
986
+ isGlobstar,
987
+ negated,
988
+ negatedExtglob
989
+ };
990
+ if (opts.tokens === true) {
991
+ state.maxDepth = 0;
992
+ if (!isPathSeparator(code)) tokens.push(token);
993
+ state.tokens = tokens;
994
+ }
995
+ if (opts.parts === true || opts.tokens === true) {
996
+ let prevIndex;
997
+ for (let idx = 0; idx < slashes.length; idx++) {
998
+ const n = prevIndex ? prevIndex + 1 : start;
999
+ const i = slashes[idx];
1000
+ const value = input.slice(n, i);
1001
+ if (opts.tokens) {
1002
+ if (idx === 0 && start !== 0) {
1003
+ tokens[idx].isPrefix = true;
1004
+ tokens[idx].value = prefix;
1005
+ } else tokens[idx].value = value;
1006
+ depth(tokens[idx]);
1007
+ state.maxDepth += tokens[idx].depth;
1008
+ }
1009
+ if (idx !== 0 || value !== "") parts.push(value);
1010
+ prevIndex = i;
1011
+ }
1012
+ if (prevIndex && prevIndex + 1 < input.length) {
1013
+ const value = input.slice(prevIndex + 1);
1014
+ parts.push(value);
1015
+ if (opts.tokens) {
1016
+ tokens[tokens.length - 1].value = value;
1017
+ depth(tokens[tokens.length - 1]);
1018
+ state.maxDepth += tokens[tokens.length - 1].depth;
1019
+ }
1020
+ }
1021
+ state.slashes = slashes;
1022
+ state.parts = parts;
1023
+ }
1024
+ return state;
1025
+ };
1026
+ module.exports = scan;
1027
+ }));
1028
+ //#endregion
1029
+ //#region node_modules/picomatch/lib/parse.js
1030
+ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1031
+ const constants = require_constants();
1032
+ const utils = require_utils();
1033
+ /**
1034
+ * Constants
1035
+ */
1036
+ const { MAX_LENGTH, POSIX_REGEX_SOURCE, REGEX_NON_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_BACKREF, REPLACEMENTS } = constants;
1037
+ /**
1038
+ * Helpers
1039
+ */
1040
+ const expandRange = (args, options) => {
1041
+ if (typeof options.expandRange === "function") return options.expandRange(...args, options);
1042
+ args.sort();
1043
+ const value = `[${args.join("-")}]`;
1044
+ try {
1045
+ new RegExp(value);
1046
+ } catch (ex) {
1047
+ return args.map((v) => utils.escapeRegex(v)).join("..");
1048
+ }
1049
+ return value;
1050
+ };
1051
+ /**
1052
+ * Create the message for a syntax error
1053
+ */
1054
+ const syntaxError = (type, char) => {
1055
+ return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
1056
+ };
1057
+ /**
1058
+ * Parse the given input string.
1059
+ * @param {String} input
1060
+ * @param {Object} options
1061
+ * @return {Object}
1062
+ */
1063
+ const parse = (input, options) => {
1064
+ if (typeof input !== "string") throw new TypeError("Expected a string");
1065
+ input = REPLACEMENTS[input] || input;
1066
+ const opts = { ...options };
1067
+ const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
1068
+ let len = input.length;
1069
+ if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
1070
+ const bos = {
1071
+ type: "bos",
1072
+ value: "",
1073
+ output: opts.prepend || ""
1074
+ };
1075
+ const tokens = [bos];
1076
+ const capture = opts.capture ? "" : "?:";
1077
+ const win32 = utils.isWindows(options);
1078
+ const PLATFORM_CHARS = constants.globChars(win32);
1079
+ const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
1080
+ const { DOT_LITERAL, PLUS_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOT_SLASH, NO_DOTS_SLASH, QMARK, QMARK_NO_DOT, STAR, START_ANCHOR } = PLATFORM_CHARS;
1081
+ const globstar = (opts) => {
1082
+ return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
1083
+ };
1084
+ const nodot = opts.dot ? "" : NO_DOT;
1085
+ const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
1086
+ let star = opts.bash === true ? globstar(opts) : STAR;
1087
+ if (opts.capture) star = `(${star})`;
1088
+ if (typeof opts.noext === "boolean") opts.noextglob = opts.noext;
1089
+ const state = {
1090
+ input,
1091
+ index: -1,
1092
+ start: 0,
1093
+ dot: opts.dot === true,
1094
+ consumed: "",
1095
+ output: "",
1096
+ prefix: "",
1097
+ backtrack: false,
1098
+ negated: false,
1099
+ brackets: 0,
1100
+ braces: 0,
1101
+ parens: 0,
1102
+ quotes: 0,
1103
+ globstar: false,
1104
+ tokens
1105
+ };
1106
+ input = utils.removePrefix(input, state);
1107
+ len = input.length;
1108
+ const extglobs = [];
1109
+ const braces = [];
1110
+ const stack = [];
1111
+ let prev = bos;
1112
+ let value;
1113
+ /**
1114
+ * Tokenizing helpers
1115
+ */
1116
+ const eos = () => state.index === len - 1;
1117
+ const peek = state.peek = (n = 1) => input[state.index + n];
1118
+ const advance = state.advance = () => input[++state.index] || "";
1119
+ const remaining = () => input.slice(state.index + 1);
1120
+ const consume = (value = "", num = 0) => {
1121
+ state.consumed += value;
1122
+ state.index += num;
1123
+ };
1124
+ const append = (token) => {
1125
+ state.output += token.output != null ? token.output : token.value;
1126
+ consume(token.value);
1127
+ };
1128
+ const negate = () => {
1129
+ let count = 1;
1130
+ while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) {
1131
+ advance();
1132
+ state.start++;
1133
+ count++;
1134
+ }
1135
+ if (count % 2 === 0) return false;
1136
+ state.negated = true;
1137
+ state.start++;
1138
+ return true;
1139
+ };
1140
+ const increment = (type) => {
1141
+ state[type]++;
1142
+ stack.push(type);
1143
+ };
1144
+ const decrement = (type) => {
1145
+ state[type]--;
1146
+ stack.pop();
1147
+ };
1148
+ /**
1149
+ * Push tokens onto the tokens array. This helper speeds up
1150
+ * tokenizing by 1) helping us avoid backtracking as much as possible,
1151
+ * and 2) helping us avoid creating extra tokens when consecutive
1152
+ * characters are plain text. This improves performance and simplifies
1153
+ * lookbehinds.
1154
+ */
1155
+ const push = (tok) => {
1156
+ if (prev.type === "globstar") {
1157
+ const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace");
1158
+ const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren");
1159
+ if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) {
1160
+ state.output = state.output.slice(0, -prev.output.length);
1161
+ prev.type = "star";
1162
+ prev.value = "*";
1163
+ prev.output = star;
1164
+ state.output += prev.output;
1165
+ }
1166
+ }
1167
+ if (extglobs.length && tok.type !== "paren") extglobs[extglobs.length - 1].inner += tok.value;
1168
+ if (tok.value || tok.output) append(tok);
1169
+ if (prev && prev.type === "text" && tok.type === "text") {
1170
+ prev.value += tok.value;
1171
+ prev.output = (prev.output || "") + tok.value;
1172
+ return;
1173
+ }
1174
+ tok.prev = prev;
1175
+ tokens.push(tok);
1176
+ prev = tok;
1177
+ };
1178
+ const extglobOpen = (type, value) => {
1179
+ const token = {
1180
+ ...EXTGLOB_CHARS[value],
1181
+ conditions: 1,
1182
+ inner: ""
1183
+ };
1184
+ token.prev = prev;
1185
+ token.parens = state.parens;
1186
+ token.output = state.output;
1187
+ const output = (opts.capture ? "(" : "") + token.open;
1188
+ increment("parens");
1189
+ push({
1190
+ type,
1191
+ value,
1192
+ output: state.output ? "" : ONE_CHAR
1193
+ });
1194
+ push({
1195
+ type: "paren",
1196
+ extglob: true,
1197
+ value: advance(),
1198
+ output
1199
+ });
1200
+ extglobs.push(token);
1201
+ };
1202
+ const extglobClose = (token) => {
1203
+ let output = token.close + (opts.capture ? ")" : "");
1204
+ let rest;
1205
+ if (token.type === "negate") {
1206
+ let extglobStar = star;
1207
+ if (token.inner && token.inner.length > 1 && token.inner.includes("/")) extglobStar = globstar(opts);
1208
+ if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) output = token.close = `)$))${extglobStar}`;
1209
+ if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) output = token.close = `)${parse(rest, {
1210
+ ...options,
1211
+ fastpaths: false
1212
+ }).output})${extglobStar})`;
1213
+ if (token.prev.type === "bos") state.negatedExtglob = true;
1214
+ }
1215
+ push({
1216
+ type: "paren",
1217
+ extglob: true,
1218
+ value,
1219
+ output
1220
+ });
1221
+ decrement("parens");
1222
+ };
1223
+ /**
1224
+ * Fast paths
1225
+ */
1226
+ if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) {
1227
+ let backslashes = false;
1228
+ let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => {
1229
+ if (first === "\\") {
1230
+ backslashes = true;
1231
+ return m;
1232
+ }
1233
+ if (first === "?") {
1234
+ if (esc) return esc + first + (rest ? QMARK.repeat(rest.length) : "");
1235
+ if (index === 0) return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : "");
1236
+ return QMARK.repeat(chars.length);
1237
+ }
1238
+ if (first === ".") return DOT_LITERAL.repeat(chars.length);
1239
+ if (first === "*") {
1240
+ if (esc) return esc + first + (rest ? star : "");
1241
+ return star;
1242
+ }
1243
+ return esc ? m : `\\${m}`;
1244
+ });
1245
+ if (backslashes === true) if (opts.unescape === true) output = output.replace(/\\/g, "");
1246
+ else output = output.replace(/\\+/g, (m) => {
1247
+ return m.length % 2 === 0 ? "\\\\" : m ? "\\" : "";
1248
+ });
1249
+ if (output === input && opts.contains === true) {
1250
+ state.output = input;
1251
+ return state;
1252
+ }
1253
+ state.output = utils.wrapOutput(output, state, options);
1254
+ return state;
1255
+ }
1256
+ /**
1257
+ * Tokenize input until we reach end-of-string
1258
+ */
1259
+ while (!eos()) {
1260
+ value = advance();
1261
+ if (value === "\0") continue;
1262
+ /**
1263
+ * Escaped characters
1264
+ */
1265
+ if (value === "\\") {
1266
+ const next = peek();
1267
+ if (next === "/" && opts.bash !== true) continue;
1268
+ if (next === "." || next === ";") continue;
1269
+ if (!next) {
1270
+ value += "\\";
1271
+ push({
1272
+ type: "text",
1273
+ value
1274
+ });
1275
+ continue;
1276
+ }
1277
+ const match = /^\\+/.exec(remaining());
1278
+ let slashes = 0;
1279
+ if (match && match[0].length > 2) {
1280
+ slashes = match[0].length;
1281
+ state.index += slashes;
1282
+ if (slashes % 2 !== 0) value += "\\";
1283
+ }
1284
+ if (opts.unescape === true) value = advance();
1285
+ else value += advance();
1286
+ if (state.brackets === 0) {
1287
+ push({
1288
+ type: "text",
1289
+ value
1290
+ });
1291
+ continue;
1292
+ }
1293
+ }
1294
+ /**
1295
+ * If we're inside a regex character class, continue
1296
+ * until we reach the closing bracket.
1297
+ */
1298
+ if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) {
1299
+ if (opts.posix !== false && value === ":") {
1300
+ const inner = prev.value.slice(1);
1301
+ if (inner.includes("[")) {
1302
+ prev.posix = true;
1303
+ if (inner.includes(":")) {
1304
+ const idx = prev.value.lastIndexOf("[");
1305
+ const pre = prev.value.slice(0, idx);
1306
+ const posix = POSIX_REGEX_SOURCE[prev.value.slice(idx + 2)];
1307
+ if (posix) {
1308
+ prev.value = pre + posix;
1309
+ state.backtrack = true;
1310
+ advance();
1311
+ if (!bos.output && tokens.indexOf(prev) === 1) bos.output = ONE_CHAR;
1312
+ continue;
1313
+ }
1314
+ }
1315
+ }
1316
+ }
1317
+ if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") value = `\\${value}`;
1318
+ if (value === "]" && (prev.value === "[" || prev.value === "[^")) value = `\\${value}`;
1319
+ if (opts.posix === true && value === "!" && prev.value === "[") value = "^";
1320
+ prev.value += value;
1321
+ append({ value });
1322
+ continue;
1323
+ }
1324
+ /**
1325
+ * If we're inside a quoted string, continue
1326
+ * until we reach the closing double quote.
1327
+ */
1328
+ if (state.quotes === 1 && value !== "\"") {
1329
+ value = utils.escapeRegex(value);
1330
+ prev.value += value;
1331
+ append({ value });
1332
+ continue;
1333
+ }
1334
+ /**
1335
+ * Double quotes
1336
+ */
1337
+ if (value === "\"") {
1338
+ state.quotes = state.quotes === 1 ? 0 : 1;
1339
+ if (opts.keepQuotes === true) push({
1340
+ type: "text",
1341
+ value
1342
+ });
1343
+ continue;
1344
+ }
1345
+ /**
1346
+ * Parentheses
1347
+ */
1348
+ if (value === "(") {
1349
+ increment("parens");
1350
+ push({
1351
+ type: "paren",
1352
+ value
1353
+ });
1354
+ continue;
1355
+ }
1356
+ if (value === ")") {
1357
+ if (state.parens === 0 && opts.strictBrackets === true) throw new SyntaxError(syntaxError("opening", "("));
1358
+ const extglob = extglobs[extglobs.length - 1];
1359
+ if (extglob && state.parens === extglob.parens + 1) {
1360
+ extglobClose(extglobs.pop());
1361
+ continue;
1362
+ }
1363
+ push({
1364
+ type: "paren",
1365
+ value,
1366
+ output: state.parens ? ")" : "\\)"
1367
+ });
1368
+ decrement("parens");
1369
+ continue;
1370
+ }
1371
+ /**
1372
+ * Square brackets
1373
+ */
1374
+ if (value === "[") {
1375
+ if (opts.nobracket === true || !remaining().includes("]")) {
1376
+ if (opts.nobracket !== true && opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]"));
1377
+ value = `\\${value}`;
1378
+ } else increment("brackets");
1379
+ push({
1380
+ type: "bracket",
1381
+ value
1382
+ });
1383
+ continue;
1384
+ }
1385
+ if (value === "]") {
1386
+ if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) {
1387
+ push({
1388
+ type: "text",
1389
+ value,
1390
+ output: `\\${value}`
1391
+ });
1392
+ continue;
1393
+ }
1394
+ if (state.brackets === 0) {
1395
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("opening", "["));
1396
+ push({
1397
+ type: "text",
1398
+ value,
1399
+ output: `\\${value}`
1400
+ });
1401
+ continue;
1402
+ }
1403
+ decrement("brackets");
1404
+ const prevValue = prev.value.slice(1);
1405
+ if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) value = `/${value}`;
1406
+ prev.value += value;
1407
+ append({ value });
1408
+ if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) continue;
1409
+ const escaped = utils.escapeRegex(prev.value);
1410
+ state.output = state.output.slice(0, -prev.value.length);
1411
+ if (opts.literalBrackets === true) {
1412
+ state.output += escaped;
1413
+ prev.value = escaped;
1414
+ continue;
1415
+ }
1416
+ prev.value = `(${capture}${escaped}|${prev.value})`;
1417
+ state.output += prev.value;
1418
+ continue;
1419
+ }
1420
+ /**
1421
+ * Braces
1422
+ */
1423
+ if (value === "{" && opts.nobrace !== true) {
1424
+ increment("braces");
1425
+ const open = {
1426
+ type: "brace",
1427
+ value,
1428
+ output: "(",
1429
+ outputIndex: state.output.length,
1430
+ tokensIndex: state.tokens.length
1431
+ };
1432
+ braces.push(open);
1433
+ push(open);
1434
+ continue;
1435
+ }
1436
+ if (value === "}") {
1437
+ const brace = braces[braces.length - 1];
1438
+ if (opts.nobrace === true || !brace) {
1439
+ push({
1440
+ type: "text",
1441
+ value,
1442
+ output: value
1443
+ });
1444
+ continue;
1445
+ }
1446
+ let output = ")";
1447
+ if (brace.dots === true) {
1448
+ const arr = tokens.slice();
1449
+ const range = [];
1450
+ for (let i = arr.length - 1; i >= 0; i--) {
1451
+ tokens.pop();
1452
+ if (arr[i].type === "brace") break;
1453
+ if (arr[i].type !== "dots") range.unshift(arr[i].value);
1454
+ }
1455
+ output = expandRange(range, opts);
1456
+ state.backtrack = true;
1457
+ }
1458
+ if (brace.comma !== true && brace.dots !== true) {
1459
+ const out = state.output.slice(0, brace.outputIndex);
1460
+ const toks = state.tokens.slice(brace.tokensIndex);
1461
+ brace.value = brace.output = "\\{";
1462
+ value = output = "\\}";
1463
+ state.output = out;
1464
+ for (const t of toks) state.output += t.output || t.value;
1465
+ }
1466
+ push({
1467
+ type: "brace",
1468
+ value,
1469
+ output
1470
+ });
1471
+ decrement("braces");
1472
+ braces.pop();
1473
+ continue;
1474
+ }
1475
+ /**
1476
+ * Pipes
1477
+ */
1478
+ if (value === "|") {
1479
+ if (extglobs.length > 0) extglobs[extglobs.length - 1].conditions++;
1480
+ push({
1481
+ type: "text",
1482
+ value
1483
+ });
1484
+ continue;
1485
+ }
1486
+ /**
1487
+ * Commas
1488
+ */
1489
+ if (value === ",") {
1490
+ let output = value;
1491
+ const brace = braces[braces.length - 1];
1492
+ if (brace && stack[stack.length - 1] === "braces") {
1493
+ brace.comma = true;
1494
+ output = "|";
1495
+ }
1496
+ push({
1497
+ type: "comma",
1498
+ value,
1499
+ output
1500
+ });
1501
+ continue;
1502
+ }
1503
+ /**
1504
+ * Slashes
1505
+ */
1506
+ if (value === "/") {
1507
+ if (prev.type === "dot" && state.index === state.start + 1) {
1508
+ state.start = state.index + 1;
1509
+ state.consumed = "";
1510
+ state.output = "";
1511
+ tokens.pop();
1512
+ prev = bos;
1513
+ continue;
1514
+ }
1515
+ push({
1516
+ type: "slash",
1517
+ value,
1518
+ output: SLASH_LITERAL
1519
+ });
1520
+ continue;
1521
+ }
1522
+ /**
1523
+ * Dots
1524
+ */
1525
+ if (value === ".") {
1526
+ if (state.braces > 0 && prev.type === "dot") {
1527
+ if (prev.value === ".") prev.output = DOT_LITERAL;
1528
+ const brace = braces[braces.length - 1];
1529
+ prev.type = "dots";
1530
+ prev.output += value;
1531
+ prev.value += value;
1532
+ brace.dots = true;
1533
+ continue;
1534
+ }
1535
+ if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") {
1536
+ push({
1537
+ type: "text",
1538
+ value,
1539
+ output: DOT_LITERAL
1540
+ });
1541
+ continue;
1542
+ }
1543
+ push({
1544
+ type: "dot",
1545
+ value,
1546
+ output: DOT_LITERAL
1547
+ });
1548
+ continue;
1549
+ }
1550
+ /**
1551
+ * Question marks
1552
+ */
1553
+ if (value === "?") {
1554
+ if (!(prev && prev.value === "(") && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") {
1555
+ extglobOpen("qmark", value);
1556
+ continue;
1557
+ }
1558
+ if (prev && prev.type === "paren") {
1559
+ const next = peek();
1560
+ let output = value;
1561
+ if (next === "<" && !utils.supportsLookbehinds()) throw new Error("Node.js v10 or higher is required for regex lookbehinds");
1562
+ if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) output = `\\${value}`;
1563
+ push({
1564
+ type: "text",
1565
+ value,
1566
+ output
1567
+ });
1568
+ continue;
1569
+ }
1570
+ if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) {
1571
+ push({
1572
+ type: "qmark",
1573
+ value,
1574
+ output: QMARK_NO_DOT
1575
+ });
1576
+ continue;
1577
+ }
1578
+ push({
1579
+ type: "qmark",
1580
+ value,
1581
+ output: QMARK
1582
+ });
1583
+ continue;
1584
+ }
1585
+ /**
1586
+ * Exclamation
1587
+ */
1588
+ if (value === "!") {
1589
+ if (opts.noextglob !== true && peek() === "(") {
1590
+ if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) {
1591
+ extglobOpen("negate", value);
1592
+ continue;
1593
+ }
1594
+ }
1595
+ if (opts.nonegate !== true && state.index === 0) {
1596
+ negate();
1597
+ continue;
1598
+ }
1599
+ }
1600
+ /**
1601
+ * Plus
1602
+ */
1603
+ if (value === "+") {
1604
+ if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") {
1605
+ extglobOpen("plus", value);
1606
+ continue;
1607
+ }
1608
+ if (prev && prev.value === "(" || opts.regex === false) {
1609
+ push({
1610
+ type: "plus",
1611
+ value,
1612
+ output: PLUS_LITERAL
1613
+ });
1614
+ continue;
1615
+ }
1616
+ if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) {
1617
+ push({
1618
+ type: "plus",
1619
+ value
1620
+ });
1621
+ continue;
1622
+ }
1623
+ push({
1624
+ type: "plus",
1625
+ value: PLUS_LITERAL
1626
+ });
1627
+ continue;
1628
+ }
1629
+ /**
1630
+ * Plain text
1631
+ */
1632
+ if (value === "@") {
1633
+ if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") {
1634
+ push({
1635
+ type: "at",
1636
+ extglob: true,
1637
+ value,
1638
+ output: ""
1639
+ });
1640
+ continue;
1641
+ }
1642
+ push({
1643
+ type: "text",
1644
+ value
1645
+ });
1646
+ continue;
1647
+ }
1648
+ /**
1649
+ * Plain text
1650
+ */
1651
+ if (value !== "*") {
1652
+ if (value === "$" || value === "^") value = `\\${value}`;
1653
+ const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
1654
+ if (match) {
1655
+ value += match[0];
1656
+ state.index += match[0].length;
1657
+ }
1658
+ push({
1659
+ type: "text",
1660
+ value
1661
+ });
1662
+ continue;
1663
+ }
1664
+ /**
1665
+ * Stars
1666
+ */
1667
+ if (prev && (prev.type === "globstar" || prev.star === true)) {
1668
+ prev.type = "star";
1669
+ prev.star = true;
1670
+ prev.value += value;
1671
+ prev.output = star;
1672
+ state.backtrack = true;
1673
+ state.globstar = true;
1674
+ consume(value);
1675
+ continue;
1676
+ }
1677
+ let rest = remaining();
1678
+ if (opts.noextglob !== true && /^\([^?]/.test(rest)) {
1679
+ extglobOpen("star", value);
1680
+ continue;
1681
+ }
1682
+ if (prev.type === "star") {
1683
+ if (opts.noglobstar === true) {
1684
+ consume(value);
1685
+ continue;
1686
+ }
1687
+ const prior = prev.prev;
1688
+ const before = prior.prev;
1689
+ const isStart = prior.type === "slash" || prior.type === "bos";
1690
+ const afterStar = before && (before.type === "star" || before.type === "globstar");
1691
+ if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) {
1692
+ push({
1693
+ type: "star",
1694
+ value,
1695
+ output: ""
1696
+ });
1697
+ continue;
1698
+ }
1699
+ const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace");
1700
+ const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren");
1701
+ if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) {
1702
+ push({
1703
+ type: "star",
1704
+ value,
1705
+ output: ""
1706
+ });
1707
+ continue;
1708
+ }
1709
+ while (rest.slice(0, 3) === "/**") {
1710
+ const after = input[state.index + 4];
1711
+ if (after && after !== "/") break;
1712
+ rest = rest.slice(3);
1713
+ consume("/**", 3);
1714
+ }
1715
+ if (prior.type === "bos" && eos()) {
1716
+ prev.type = "globstar";
1717
+ prev.value += value;
1718
+ prev.output = globstar(opts);
1719
+ state.output = prev.output;
1720
+ state.globstar = true;
1721
+ consume(value);
1722
+ continue;
1723
+ }
1724
+ if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) {
1725
+ state.output = state.output.slice(0, -(prior.output + prev.output).length);
1726
+ prior.output = `(?:${prior.output}`;
1727
+ prev.type = "globstar";
1728
+ prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)");
1729
+ prev.value += value;
1730
+ state.globstar = true;
1731
+ state.output += prior.output + prev.output;
1732
+ consume(value);
1733
+ continue;
1734
+ }
1735
+ if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") {
1736
+ const end = rest[1] !== void 0 ? "|$" : "";
1737
+ state.output = state.output.slice(0, -(prior.output + prev.output).length);
1738
+ prior.output = `(?:${prior.output}`;
1739
+ prev.type = "globstar";
1740
+ prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
1741
+ prev.value += value;
1742
+ state.output += prior.output + prev.output;
1743
+ state.globstar = true;
1744
+ consume(value + advance());
1745
+ push({
1746
+ type: "slash",
1747
+ value: "/",
1748
+ output: ""
1749
+ });
1750
+ continue;
1751
+ }
1752
+ if (prior.type === "bos" && rest[0] === "/") {
1753
+ prev.type = "globstar";
1754
+ prev.value += value;
1755
+ prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
1756
+ state.output = prev.output;
1757
+ state.globstar = true;
1758
+ consume(value + advance());
1759
+ push({
1760
+ type: "slash",
1761
+ value: "/",
1762
+ output: ""
1763
+ });
1764
+ continue;
1765
+ }
1766
+ state.output = state.output.slice(0, -prev.output.length);
1767
+ prev.type = "globstar";
1768
+ prev.output = globstar(opts);
1769
+ prev.value += value;
1770
+ state.output += prev.output;
1771
+ state.globstar = true;
1772
+ consume(value);
1773
+ continue;
1774
+ }
1775
+ const token = {
1776
+ type: "star",
1777
+ value,
1778
+ output: star
1779
+ };
1780
+ if (opts.bash === true) {
1781
+ token.output = ".*?";
1782
+ if (prev.type === "bos" || prev.type === "slash") token.output = nodot + token.output;
1783
+ push(token);
1784
+ continue;
1785
+ }
1786
+ if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) {
1787
+ token.output = value;
1788
+ push(token);
1789
+ continue;
1790
+ }
1791
+ if (state.index === state.start || prev.type === "slash" || prev.type === "dot") {
1792
+ if (prev.type === "dot") {
1793
+ state.output += NO_DOT_SLASH;
1794
+ prev.output += NO_DOT_SLASH;
1795
+ } else if (opts.dot === true) {
1796
+ state.output += NO_DOTS_SLASH;
1797
+ prev.output += NO_DOTS_SLASH;
1798
+ } else {
1799
+ state.output += nodot;
1800
+ prev.output += nodot;
1801
+ }
1802
+ if (peek() !== "*") {
1803
+ state.output += ONE_CHAR;
1804
+ prev.output += ONE_CHAR;
1805
+ }
1806
+ }
1807
+ push(token);
1808
+ }
1809
+ while (state.brackets > 0) {
1810
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]"));
1811
+ state.output = utils.escapeLast(state.output, "[");
1812
+ decrement("brackets");
1813
+ }
1814
+ while (state.parens > 0) {
1815
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", ")"));
1816
+ state.output = utils.escapeLast(state.output, "(");
1817
+ decrement("parens");
1818
+ }
1819
+ while (state.braces > 0) {
1820
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "}"));
1821
+ state.output = utils.escapeLast(state.output, "{");
1822
+ decrement("braces");
1823
+ }
1824
+ if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) push({
1825
+ type: "maybe_slash",
1826
+ value: "",
1827
+ output: `${SLASH_LITERAL}?`
1828
+ });
1829
+ if (state.backtrack === true) {
1830
+ state.output = "";
1831
+ for (const token of state.tokens) {
1832
+ state.output += token.output != null ? token.output : token.value;
1833
+ if (token.suffix) state.output += token.suffix;
1834
+ }
1835
+ }
1836
+ return state;
1837
+ };
1838
+ /**
1839
+ * Fast paths for creating regular expressions for common glob patterns.
1840
+ * This can significantly speed up processing and has very little downside
1841
+ * impact when none of the fast paths match.
1842
+ */
1843
+ parse.fastpaths = (input, options) => {
1844
+ const opts = { ...options };
1845
+ const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
1846
+ const len = input.length;
1847
+ if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
1848
+ input = REPLACEMENTS[input] || input;
1849
+ const win32 = utils.isWindows(options);
1850
+ const { DOT_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOTS, NO_DOTS_SLASH, STAR, START_ANCHOR } = constants.globChars(win32);
1851
+ const nodot = opts.dot ? NO_DOTS : NO_DOT;
1852
+ const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
1853
+ const capture = opts.capture ? "" : "?:";
1854
+ const state = {
1855
+ negated: false,
1856
+ prefix: ""
1857
+ };
1858
+ let star = opts.bash === true ? ".*?" : STAR;
1859
+ if (opts.capture) star = `(${star})`;
1860
+ const globstar = (opts) => {
1861
+ if (opts.noglobstar === true) return star;
1862
+ return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
1863
+ };
1864
+ const create = (str) => {
1865
+ switch (str) {
1866
+ case "*": return `${nodot}${ONE_CHAR}${star}`;
1867
+ case ".*": return `${DOT_LITERAL}${ONE_CHAR}${star}`;
1868
+ case "*.*": return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
1869
+ case "*/*": return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
1870
+ case "**": return nodot + globstar(opts);
1871
+ case "**/*": return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
1872
+ case "**/*.*": return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
1873
+ case "**/.*": return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
1874
+ default: {
1875
+ const match = /^(.*?)\.(\w+)$/.exec(str);
1876
+ if (!match) return;
1877
+ const source = create(match[1]);
1878
+ if (!source) return;
1879
+ return source + DOT_LITERAL + match[2];
1880
+ }
1881
+ }
1882
+ };
1883
+ let source = create(utils.removePrefix(input, state));
1884
+ if (source && opts.strictSlashes !== true) source += `${SLASH_LITERAL}?`;
1885
+ return source;
1886
+ };
1887
+ module.exports = parse;
1888
+ }));
1889
+ //#endregion
1890
+ //#region node_modules/picomatch/lib/picomatch.js
1891
+ var require_picomatch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1892
+ const path$1 = __require("path");
1893
+ const scan = require_scan();
1894
+ const parse = require_parse();
1895
+ const utils = require_utils();
1896
+ const constants = require_constants();
1897
+ const isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
1898
+ /**
1899
+ * Creates a matcher function from one or more glob patterns. The
1900
+ * returned function takes a string to match as its first argument,
1901
+ * and returns true if the string is a match. The returned matcher
1902
+ * function also takes a boolean as the second argument that, when true,
1903
+ * returns an object with additional information.
1904
+ *
1905
+ * ```js
1906
+ * const picomatch = require('picomatch');
1907
+ * // picomatch(glob[, options]);
1908
+ *
1909
+ * const isMatch = picomatch('*.!(*a)');
1910
+ * console.log(isMatch('a.a')); //=> false
1911
+ * console.log(isMatch('a.b')); //=> true
1912
+ * ```
1913
+ * @name picomatch
1914
+ * @param {String|Array} `globs` One or more glob patterns.
1915
+ * @param {Object=} `options`
1916
+ * @return {Function=} Returns a matcher function.
1917
+ * @api public
1918
+ */
1919
+ const picomatch = (glob, options, returnState = false) => {
1920
+ if (Array.isArray(glob)) {
1921
+ const fns = glob.map((input) => picomatch(input, options, returnState));
1922
+ const arrayMatcher = (str) => {
1923
+ for (const isMatch of fns) {
1924
+ const state = isMatch(str);
1925
+ if (state) return state;
1926
+ }
1927
+ return false;
1928
+ };
1929
+ return arrayMatcher;
1930
+ }
1931
+ const isState = isObject(glob) && glob.tokens && glob.input;
1932
+ if (glob === "" || typeof glob !== "string" && !isState) throw new TypeError("Expected pattern to be a non-empty string");
1933
+ const opts = options || {};
1934
+ const posix = utils.isWindows(options);
1935
+ const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true);
1936
+ const state = regex.state;
1937
+ delete regex.state;
1938
+ let isIgnored = () => false;
1939
+ if (opts.ignore) {
1940
+ const ignoreOpts = {
1941
+ ...options,
1942
+ ignore: null,
1943
+ onMatch: null,
1944
+ onResult: null
1945
+ };
1946
+ isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
1947
+ }
1948
+ const matcher = (input, returnObject = false) => {
1949
+ const { isMatch, match, output } = picomatch.test(input, regex, options, {
1950
+ glob,
1951
+ posix
1952
+ });
1953
+ const result = {
1954
+ glob,
1955
+ state,
1956
+ regex,
1957
+ posix,
1958
+ input,
1959
+ output,
1960
+ match,
1961
+ isMatch
1962
+ };
1963
+ if (typeof opts.onResult === "function") opts.onResult(result);
1964
+ if (isMatch === false) {
1965
+ result.isMatch = false;
1966
+ return returnObject ? result : false;
1967
+ }
1968
+ if (isIgnored(input)) {
1969
+ if (typeof opts.onIgnore === "function") opts.onIgnore(result);
1970
+ result.isMatch = false;
1971
+ return returnObject ? result : false;
1972
+ }
1973
+ if (typeof opts.onMatch === "function") opts.onMatch(result);
1974
+ return returnObject ? result : true;
1975
+ };
1976
+ if (returnState) matcher.state = state;
1977
+ return matcher;
1978
+ };
1979
+ /**
1980
+ * Test `input` with the given `regex`. This is used by the main
1981
+ * `picomatch()` function to test the input string.
1982
+ *
1983
+ * ```js
1984
+ * const picomatch = require('picomatch');
1985
+ * // picomatch.test(input, regex[, options]);
1986
+ *
1987
+ * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
1988
+ * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
1989
+ * ```
1990
+ * @param {String} `input` String to test.
1991
+ * @param {RegExp} `regex`
1992
+ * @return {Object} Returns an object with matching info.
1993
+ * @api public
1994
+ */
1995
+ picomatch.test = (input, regex, options, { glob, posix } = {}) => {
1996
+ if (typeof input !== "string") throw new TypeError("Expected input to be a string");
1997
+ if (input === "") return {
1998
+ isMatch: false,
1999
+ output: ""
2000
+ };
2001
+ const opts = options || {};
2002
+ const format = opts.format || (posix ? utils.toPosixSlashes : null);
2003
+ let match = input === glob;
2004
+ let output = match && format ? format(input) : input;
2005
+ if (match === false) {
2006
+ output = format ? format(input) : input;
2007
+ match = output === glob;
2008
+ }
2009
+ if (match === false || opts.capture === true) if (opts.matchBase === true || opts.basename === true) match = picomatch.matchBase(input, regex, options, posix);
2010
+ else match = regex.exec(output);
2011
+ return {
2012
+ isMatch: Boolean(match),
2013
+ match,
2014
+ output
2015
+ };
2016
+ };
2017
+ /**
2018
+ * Match the basename of a filepath.
2019
+ *
2020
+ * ```js
2021
+ * const picomatch = require('picomatch');
2022
+ * // picomatch.matchBase(input, glob[, options]);
2023
+ * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
2024
+ * ```
2025
+ * @param {String} `input` String to test.
2026
+ * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
2027
+ * @return {Boolean}
2028
+ * @api public
2029
+ */
2030
+ picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
2031
+ return (glob instanceof RegExp ? glob : picomatch.makeRe(glob, options)).test(path$1.basename(input));
2032
+ };
2033
+ /**
2034
+ * Returns true if **any** of the given glob `patterns` match the specified `string`.
2035
+ *
2036
+ * ```js
2037
+ * const picomatch = require('picomatch');
2038
+ * // picomatch.isMatch(string, patterns[, options]);
2039
+ *
2040
+ * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
2041
+ * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
2042
+ * ```
2043
+ * @param {String|Array} str The string to test.
2044
+ * @param {String|Array} patterns One or more glob patterns to use for matching.
2045
+ * @param {Object} [options] See available [options](#options).
2046
+ * @return {Boolean} Returns true if any patterns match `str`
2047
+ * @api public
2048
+ */
2049
+ picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
2050
+ /**
2051
+ * Parse a glob pattern to create the source string for a regular
2052
+ * expression.
2053
+ *
2054
+ * ```js
2055
+ * const picomatch = require('picomatch');
2056
+ * const result = picomatch.parse(pattern[, options]);
2057
+ * ```
2058
+ * @param {String} `pattern`
2059
+ * @param {Object} `options`
2060
+ * @return {Object} Returns an object with useful properties and output to be used as a regex source string.
2061
+ * @api public
2062
+ */
2063
+ picomatch.parse = (pattern, options) => {
2064
+ if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options));
2065
+ return parse(pattern, {
2066
+ ...options,
2067
+ fastpaths: false
2068
+ });
2069
+ };
2070
+ /**
2071
+ * Scan a glob pattern to separate the pattern into segments.
2072
+ *
2073
+ * ```js
2074
+ * const picomatch = require('picomatch');
2075
+ * // picomatch.scan(input[, options]);
2076
+ *
2077
+ * const result = picomatch.scan('!./foo/*.js');
2078
+ * console.log(result);
2079
+ * { prefix: '!./',
2080
+ * input: '!./foo/*.js',
2081
+ * start: 3,
2082
+ * base: 'foo',
2083
+ * glob: '*.js',
2084
+ * isBrace: false,
2085
+ * isBracket: false,
2086
+ * isGlob: true,
2087
+ * isExtglob: false,
2088
+ * isGlobstar: false,
2089
+ * negated: true }
2090
+ * ```
2091
+ * @param {String} `input` Glob pattern to scan.
2092
+ * @param {Object} `options`
2093
+ * @return {Object} Returns an object with
2094
+ * @api public
2095
+ */
2096
+ picomatch.scan = (input, options) => scan(input, options);
2097
+ /**
2098
+ * Compile a regular expression from the `state` object returned by the
2099
+ * [parse()](#parse) method.
2100
+ *
2101
+ * @param {Object} `state`
2102
+ * @param {Object} `options`
2103
+ * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser.
2104
+ * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
2105
+ * @return {RegExp}
2106
+ * @api public
2107
+ */
2108
+ picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
2109
+ if (returnOutput === true) return state.output;
2110
+ const opts = options || {};
2111
+ const prepend = opts.contains ? "" : "^";
2112
+ const append = opts.contains ? "" : "$";
2113
+ let source = `${prepend}(?:${state.output})${append}`;
2114
+ if (state && state.negated === true) source = `^(?!${source}).*$`;
2115
+ const regex = picomatch.toRegex(source, options);
2116
+ if (returnState === true) regex.state = state;
2117
+ return regex;
2118
+ };
2119
+ /**
2120
+ * Create a regular expression from a parsed glob pattern.
2121
+ *
2122
+ * ```js
2123
+ * const picomatch = require('picomatch');
2124
+ * const state = picomatch.parse('*.js');
2125
+ * // picomatch.compileRe(state[, options]);
2126
+ *
2127
+ * console.log(picomatch.compileRe(state));
2128
+ * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
2129
+ * ```
2130
+ * @param {String} `state` The object returned from the `.parse` method.
2131
+ * @param {Object} `options`
2132
+ * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
2133
+ * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
2134
+ * @return {RegExp} Returns a regex created from the given pattern.
2135
+ * @api public
2136
+ */
2137
+ picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
2138
+ if (!input || typeof input !== "string") throw new TypeError("Expected a non-empty string");
2139
+ let parsed = {
2140
+ negated: false,
2141
+ fastpaths: true
2142
+ };
2143
+ if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) parsed.output = parse.fastpaths(input, options);
2144
+ if (!parsed.output) parsed = parse(input, options);
2145
+ return picomatch.compileRe(parsed, options, returnOutput, returnState);
2146
+ };
2147
+ /**
2148
+ * Create a regular expression from the given regex source string.
2149
+ *
2150
+ * ```js
2151
+ * const picomatch = require('picomatch');
2152
+ * // picomatch.toRegex(source[, options]);
2153
+ *
2154
+ * const { output } = picomatch.parse('*.js');
2155
+ * console.log(picomatch.toRegex(output));
2156
+ * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
2157
+ * ```
2158
+ * @param {String} `source` Regular expression source string.
2159
+ * @param {Object} `options`
2160
+ * @return {RegExp}
2161
+ * @api public
2162
+ */
2163
+ picomatch.toRegex = (source, options) => {
2164
+ try {
2165
+ const opts = options || {};
2166
+ return new RegExp(source, opts.flags || (opts.nocase ? "i" : ""));
2167
+ } catch (err) {
2168
+ if (options && options.debug === true) throw err;
2169
+ return /$^/;
2170
+ }
2171
+ };
2172
+ /**
2173
+ * Picomatch constants.
2174
+ * @return {Object}
2175
+ */
2176
+ picomatch.constants = constants;
2177
+ /**
2178
+ * Expose "picomatch"
2179
+ */
2180
+ module.exports = picomatch;
2181
+ }));
2182
+ //#endregion
2183
+ //#region src/diff-filter.ts
2184
+ var import_picomatch = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
2185
+ module.exports = require_picomatch$1();
2186
+ })))(), 1);
2187
+ function filterGitDiff(diff, config) {
2188
+ const isIncluded = (0, import_picomatch.default)(config.include);
2189
+ const isExcluded = (0, import_picomatch.default)(config.exclude);
2190
+ const matches = (filePath) => isIncluded(filePath) && !isExcluded(filePath);
2191
+ const toIndex = [];
2192
+ const toDelete = [];
2193
+ const renamed = [];
2194
+ for (const filePath of [...diff.added, ...diff.modified]) if (matches(filePath)) toIndex.push(filePath);
2195
+ for (const filePath of diff.deleted) if (matches(filePath)) toDelete.push(filePath);
2196
+ for (const r of diff.renamed) {
2197
+ const fromMatches = matches(r.from);
2198
+ const toMatches = matches(r.to);
2199
+ if (toMatches) renamed.push(r);
2200
+ if (fromMatches && !toMatches) toDelete.push(r.from);
2201
+ }
2202
+ return {
2203
+ toIndex,
2204
+ toDelete,
2205
+ renamed
2206
+ };
2207
+ }
2208
+ //#endregion
2209
+ //#region src/parser/base.ts
2210
+ var BaseParser = class {
2211
+ supports(filePath) {
2212
+ return this.extensions.some((ext) => filePath.endsWith(ext));
2213
+ }
2214
+ };
2215
+ //#endregion
2216
+ //#region src/parser/swift.ts
2217
+ const swiftParser = new Parser();
2218
+ swiftParser.setLanguage(Swift);
2219
+ const SYMBOL_NODE_TYPES$1 = {
2220
+ class_declaration: "class",
2221
+ protocol_declaration: "protocol",
2222
+ function_declaration: "function"
2223
+ };
2224
+ const KEYWORD_TO_KIND = {
2225
+ struct: "struct",
2226
+ enum: "enum",
2227
+ class: "class"
2228
+ };
2229
+ function extractName$1(node) {
2230
+ /* v8 ignore next */
2231
+ return node.childForFieldName("name")?.text ?? null;
2232
+ }
2233
+ var SwiftParser = class extends BaseParser {
2234
+ extensions = [".swift"];
2235
+ parse(content, _filePath) {
2236
+ const rootNode = swiftParser.parse(content).rootNode;
2237
+ const imports = [];
2238
+ const exports = [];
2239
+ const symbols = [];
2240
+ function walk(node) {
2241
+ if (node.type === "import_declaration") {
2242
+ const children = node.children.filter((c) => c.type !== "import");
2243
+ /* v8 ignore next */
2244
+ if (children.length > 0) imports.push(children.map((c) => c.text).join("."));
2245
+ }
2246
+ let kind = SYMBOL_NODE_TYPES$1[node.type];
2247
+ if (node.type === "class_declaration") {
2248
+ const keyword = node.children.find((c) => c.type in KEYWORD_TO_KIND);
2249
+ /* v8 ignore next */
2250
+ kind = keyword ? KEYWORD_TO_KIND[keyword.type] : "class";
2251
+ }
2252
+ if (kind) {
2253
+ const name = extractName$1(node);
2254
+ /* v8 ignore next */
2255
+ if (name) {
2256
+ symbols.push({
2257
+ name,
2258
+ kind,
2259
+ description: ""
2260
+ });
2261
+ exports.push(name);
2262
+ }
2263
+ }
2264
+ for (const child of node.children) walk(child);
2265
+ }
2266
+ walk(rootNode);
2267
+ return {
2268
+ imports,
2269
+ exports,
2270
+ symbols
2271
+ };
2272
+ }
2273
+ };
2274
+ //#endregion
2275
+ //#region src/parser/typescript.ts
2276
+ const tsParser = new Parser();
2277
+ tsParser.setLanguage(TypeScript.typescript);
2278
+ const tsxParser = new Parser();
2279
+ tsxParser.setLanguage(TypeScript.tsx);
2280
+ const IMPORT_TYPES = new Set(["import_statement"]);
2281
+ const EXPORT_NODE_TYPES = new Set(["export_statement"]);
2282
+ const SYMBOL_NODE_TYPES = {
2283
+ class_declaration: "class",
2284
+ function_declaration: "function",
2285
+ method_definition: "method",
2286
+ interface_declaration: "interface",
2287
+ type_alias_declaration: "type",
2288
+ enum_declaration: "enum",
2289
+ lexical_declaration: "variable",
2290
+ variable_declaration: "variable"
2291
+ };
2292
+ function extractName(node) {
2293
+ const nameNode = node.childForFieldName("name");
2294
+ if (nameNode) return nameNode.text;
2295
+ for (const child of node.children) if (child.type === "variable_declarator") {
2296
+ const n = child.childForFieldName("name");
2297
+ /* v8 ignore next */
2298
+ if (n) return n.text;
2299
+ }
2300
+ /* v8 ignore next */
2301
+ return null;
2302
+ }
2303
+ function extractImports(rootNode) {
2304
+ const imports = [];
2305
+ for (const child of rootNode.children) if (IMPORT_TYPES.has(child.type)) {
2306
+ const source = child.childForFieldName("source");
2307
+ /* v8 ignore next */
2308
+ if (source) imports.push(source.text.replace(/['"]/g, ""));
2309
+ }
2310
+ return imports;
2311
+ }
2312
+ function extractExports(rootNode) {
2313
+ const exports = [];
2314
+ for (const child of rootNode.children) if (EXPORT_NODE_TYPES.has(child.type)) {
2315
+ const exportClause = child.children.find((c) => c.type === "export_clause");
2316
+ if (exportClause) {
2317
+ for (const specifier of exportClause.children) if (specifier.type === "export_specifier") {
2318
+ const name = specifier.childForFieldName("name");
2319
+ /* v8 ignore next */
2320
+ if (name) exports.push(name.text);
2321
+ }
2322
+ continue;
2323
+ }
2324
+ const declaration = child.childForFieldName("declaration");
2325
+ if (declaration) {
2326
+ const name = extractName(declaration);
2327
+ /* v8 ignore next */
2328
+ if (name) exports.push(name);
2329
+ }
2330
+ }
2331
+ return exports;
2332
+ }
2333
+ function extractSymbols(rootNode) {
2334
+ const symbols = [];
2335
+ function walk(node) {
2336
+ if (EXPORT_NODE_TYPES.has(node.type)) {
2337
+ const declaration = node.childForFieldName("declaration");
2338
+ if (declaration) processSymbolNode(declaration);
2339
+ return;
2340
+ }
2341
+ processSymbolNode(node);
2342
+ for (const child of node.children) if (child.type === "class_body") for (const member of child.children) processSymbolNode(member);
2343
+ }
2344
+ function processSymbolNode(node) {
2345
+ const kind = SYMBOL_NODE_TYPES[node.type];
2346
+ if (!kind) return;
2347
+ const name = extractName(node);
2348
+ /* v8 ignore next */
2349
+ if (!name) return;
2350
+ symbols.push({
2351
+ name,
2352
+ kind,
2353
+ description: ""
2354
+ });
2355
+ }
2356
+ for (const child of rootNode.children) walk(child);
2357
+ return symbols;
2358
+ }
2359
+ var TypeScriptParser = class extends BaseParser {
2360
+ extensions = [
2361
+ ".ts",
2362
+ ".tsx",
2363
+ ".js",
2364
+ ".jsx"
2365
+ ];
2366
+ parse(content, filePath) {
2367
+ const rootNode = (filePath.endsWith(".tsx") || filePath.endsWith(".jsx") ? tsxParser : tsParser).parse(content).rootNode;
2368
+ return {
2369
+ imports: extractImports(rootNode),
2370
+ exports: extractExports(rootNode),
2371
+ symbols: extractSymbols(rootNode)
2372
+ };
2373
+ }
2374
+ };
2375
+ //#endregion
2376
+ //#region src/parser/index.ts
2377
+ const EXTENSION_TO_LANGUAGE = {
2378
+ ".ts": "typescript",
2379
+ ".tsx": "typescript",
2380
+ ".js": "javascript",
2381
+ ".jsx": "javascript",
2382
+ ".swift": "swift"
2383
+ };
2384
+ var ParserManager = class {
2385
+ parsers;
2386
+ constructor() {
2387
+ this.parsers = [new TypeScriptParser(), new SwiftParser()];
2388
+ }
2389
+ getParser(filePath) {
2390
+ return this.parsers.find((p) => p.supports(filePath));
2391
+ }
2392
+ parse(content, filePath) {
2393
+ const parser = this.getParser(filePath);
2394
+ if (!parser) return null;
2395
+ return parser.parse(content, filePath);
2396
+ }
2397
+ getLanguage(filePath) {
2398
+ return EXTENSION_TO_LANGUAGE[filePath.slice(filePath.lastIndexOf("."))];
2399
+ }
2400
+ };
2401
+ //#endregion
2402
+ //#region src/llm/batcher.ts
2403
+ var Batcher = class {
2404
+ limit;
2405
+ constructor(concurrency = 5) {
2406
+ this.limit = pLimit(concurrency);
2407
+ }
2408
+ async run(tasks) {
2409
+ return Promise.all(tasks.map((task) => this.limit(() => task.execute())));
2410
+ }
2411
+ };
2412
+ //#endregion
2413
+ //#region src/llm/prompts.ts
2414
+ const INDEXING_SYSTEM_PROMPT = `You are a code indexing assistant. Your job is to analyze source code files and generate structured metadata.
2415
+
2416
+ For each file, you must return a JSON object with exactly these fields:
2417
+ - "name": A short, descriptive name for the file (e.g., "User Authentication Service")
2418
+ - "description": A one-line description of what this file does
2419
+ - "summary": A 2-3 sentence summary of the file's purpose and key functionality
2420
+ - "symbols": An array of symbol descriptions, each with "name" and "description"
2421
+
2422
+ Rules:
2423
+ - Be concise and precise
2424
+ - Focus on what the code DOES, not how it's structured
2425
+ - Symbol descriptions should explain the purpose, not restate the signature
2426
+ - Return ONLY valid JSON, no markdown or explanation`;
2427
+ const RERANK_SYSTEM_PROMPT = `You are a search result reranking assistant. Given a user query and a list of candidate files, reorder them by relevance.
2428
+
2429
+ Return ONLY a JSON array of file paths, ordered from most relevant to least relevant.
2430
+ Do not include any explanation, markdown, or extra text — just the JSON array.`;
2431
+ function buildRerankPrompt(query, candidates) {
2432
+ return `User query: "${query}"
2433
+
2434
+ Candidate files:
2435
+ ${candidates.map((c, i) => `${i + 1}. ${c.path}\n Name: ${c.name}\n Description: ${c.description}\n Summary: ${c.summary}`).join("\n\n")}
2436
+
2437
+ Reorder these files by relevance to the query. Return a JSON array of file paths, most relevant first.`;
2438
+ }
2439
+ function buildIndexingPrompt(filePath, content, symbols) {
2440
+ return `Analyze this source file and generate indexing metadata.
2441
+
2442
+ File: ${filePath}
2443
+ Detected symbols:
2444
+ ${symbols.map((s) => `- ${s.kind}: ${s.name}`).join("\n")}
2445
+
2446
+ Source code:
2447
+ \`\`\`
2448
+ ${content}
2449
+ \`\`\`
2450
+
2451
+ Return a JSON object with: name, description, summary, and symbols (array with name + description for each detected symbol).`;
2452
+ }
2453
+ //#endregion
2454
+ //#region src/llm/index.ts
2455
+ /**
2456
+ * Resolve a model by provider and model ID from user config.
2457
+ * pi-ai's getModel is generic over compile-time known providers/models,
2458
+ * but user config is dynamic. This wrapper bridges the gap.
2459
+ */
2460
+ function resolveModel(provider, modelId) {
2461
+ const model = getModel(provider, modelId);
2462
+ if (!model) throw new Error(`Unknown model "${modelId}" for provider "${provider}". Check your .kly/config.yaml.`);
2463
+ return model;
2464
+ }
2465
+ var LLMService = class {
2466
+ model;
2467
+ batcher;
2468
+ constructor(config) {
2469
+ const envKey = this.getEnvKeyName(config.llm.provider);
2470
+ if (config.llm.apiKey && !process.env[envKey]) process.env[envKey] = config.llm.apiKey;
2471
+ this.model = resolveModel(config.llm.provider, config.llm.model);
2472
+ this.batcher = new Batcher(5);
2473
+ }
2474
+ getEnvKeyName(provider) {
2475
+ return {
2476
+ openrouter: "OPENROUTER_API_KEY",
2477
+ anthropic: "ANTHROPIC_API_KEY",
2478
+ openai: "OPENAI_API_KEY",
2479
+ google: "GOOGLE_API_KEY",
2480
+ mistral: "MISTRAL_API_KEY",
2481
+ groq: "GROQ_API_KEY",
2482
+ xai: "XAI_API_KEY"
2483
+ }[provider] || `${provider.toUpperCase()}_API_KEY`;
2484
+ }
2485
+ async indexFile(filePath, content, symbols) {
2486
+ const prompt = buildIndexingPrompt(filePath, content, symbols.map((s) => ({
2487
+ name: s.name,
2488
+ kind: s.kind
2489
+ })));
2490
+ const text = (await complete(this.model, {
2491
+ systemPrompt: INDEXING_SYSTEM_PROMPT,
2492
+ messages: [{
2493
+ role: "user",
2494
+ content: prompt,
2495
+ timestamp: Date.now()
2496
+ }]
2497
+ })).content.filter((block) => block.type === "text").map((block) => block.text).join("");
2498
+ return this.parseResponse(text);
2499
+ }
2500
+ async indexFiles(files) {
2501
+ const results = /* @__PURE__ */ new Map();
2502
+ const tasks = files.map((file) => ({ execute: async () => {
2503
+ const result = await this.indexFile(file.path, file.content, file.symbols);
2504
+ results.set(file.path, result);
2505
+ return result;
2506
+ } }));
2507
+ await this.batcher.run(tasks);
2508
+ return results;
2509
+ }
2510
+ parseResponse(text) {
2511
+ const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
2512
+ const jsonStr = jsonMatch ? jsonMatch[1].trim() : text.trim();
2513
+ const parsed = JSON.parse(jsonStr);
2514
+ return {
2515
+ name: parsed.name || "",
2516
+ description: parsed.description || "",
2517
+ summary: parsed.summary || "",
2518
+ symbols: Array.isArray(parsed.symbols) ? parsed.symbols.map((s) => ({
2519
+ name: s.name || "",
2520
+ description: s.description || ""
2521
+ })) : []
2522
+ };
2523
+ }
2524
+ };
2525
+ //#endregion
2526
+ //#region src/graph.ts
2527
+ const RESOLVE_EXTENSIONS = [
2528
+ ".ts",
2529
+ ".tsx",
2530
+ ".js",
2531
+ ".jsx",
2532
+ "/index.ts",
2533
+ "/index.tsx",
2534
+ "/index.js",
2535
+ "/index.jsx"
2536
+ ];
2537
+ function isRelativeImport(importPath) {
2538
+ return importPath.startsWith(".") || importPath.startsWith("/");
2539
+ }
2540
+ /**
2541
+ * Resolve a relative import path to a file path that exists in the index.
2542
+ */
2543
+ function resolveImport(fromFile, importPath, indexedPaths) {
2544
+ if (!isRelativeImport(importPath)) return void 0;
2545
+ const dir = path.dirname(fromFile);
2546
+ const resolved = path.normalize(path.join(dir, importPath));
2547
+ if (indexedPaths.has(resolved)) return resolved;
2548
+ for (const ext of RESOLVE_EXTENSIONS) {
2549
+ const candidate = resolved + ext;
2550
+ if (indexedPaths.has(candidate)) return candidate;
2551
+ }
2552
+ }
2553
+ /**
2554
+ * Build a dependency graph from indexed files.
2555
+ */
2556
+ function buildDependencyGraph(db, options = {}) {
2557
+ const { focus, depth = 2 } = options;
2558
+ const allFiles = db.getAllFiles();
2559
+ const indexedPaths = new Set(allFiles.map((f) => f.path));
2560
+ const fileMap = /* @__PURE__ */ new Map();
2561
+ for (const file of allFiles) fileMap.set(file.path, file);
2562
+ const adjacency = /* @__PURE__ */ new Map();
2563
+ for (const file of allFiles) {
2564
+ const deps = [];
2565
+ for (const imp of file.imports) {
2566
+ const resolved = resolveImport(file.path, imp, indexedPaths);
2567
+ if (resolved) deps.push(resolved);
2568
+ }
2569
+ adjacency.set(file.path, deps);
2570
+ }
2571
+ let includedPaths;
2572
+ if (focus) {
2573
+ includedPaths = /* @__PURE__ */ new Set();
2574
+ const queue = [{
2575
+ path: focus,
2576
+ currentDepth: 0
2577
+ }];
2578
+ includedPaths.add(focus);
2579
+ while (queue.length > 0) {
2580
+ const { path: current, currentDepth } = queue.shift();
2581
+ if (currentDepth >= depth) continue;
2582
+ const deps = adjacency.get(current) || [];
2583
+ for (const dep of deps) if (!includedPaths.has(dep)) {
2584
+ includedPaths.add(dep);
2585
+ queue.push({
2586
+ path: dep,
2587
+ currentDepth: currentDepth + 1
2588
+ });
2589
+ }
2590
+ for (const [source, targets] of adjacency) if (targets.includes(current) && !includedPaths.has(source)) {
2591
+ includedPaths.add(source);
2592
+ queue.push({
2593
+ path: source,
2594
+ currentDepth: currentDepth + 1
2595
+ });
2596
+ }
2597
+ }
2598
+ } else includedPaths = indexedPaths;
2599
+ const nodes = /* @__PURE__ */ new Map();
2600
+ const edges = [];
2601
+ for (const filePath of includedPaths) {
2602
+ const file = fileMap.get(filePath);
2603
+ if (!file) continue;
2604
+ nodes.set(filePath, {
2605
+ path: file.path,
2606
+ name: file.name,
2607
+ language: file.language
2608
+ });
2609
+ }
2610
+ for (const filePath of includedPaths) {
2611
+ const deps = adjacency.get(filePath) || [];
2612
+ for (const dep of deps) if (includedPaths.has(dep)) edges.push({
2613
+ from: filePath,
2614
+ to: dep
2615
+ });
2616
+ }
2617
+ return {
2618
+ nodes,
2619
+ edges
2620
+ };
2621
+ }
2622
+ /**
2623
+ * Generate Mermaid syntax from a dependency graph.
2624
+ */
2625
+ function generateMermaid(graph) {
2626
+ const lines = ["graph LR"];
2627
+ const nodeIds = /* @__PURE__ */ new Map();
2628
+ let idCounter = 0;
2629
+ for (const filePath of graph.nodes.keys()) nodeIds.set(filePath, `N${idCounter++}`);
2630
+ for (const [filePath, node] of graph.nodes) {
2631
+ const id = nodeIds.get(filePath);
2632
+ const label = node.path.replace(/"/g, "'");
2633
+ lines.push(` ${id}["${label}"]`);
2634
+ }
2635
+ for (const edge of graph.edges) {
2636
+ const fromId = nodeIds.get(edge.from);
2637
+ const toId = nodeIds.get(edge.to);
2638
+ if (fromId && toId) lines.push(` ${fromId} --> ${toId}`);
2639
+ }
2640
+ return lines.join("\n");
2641
+ }
2642
+ /**
2643
+ * Render a Mermaid diagram string to ASCII/Unicode art.
2644
+ */
2645
+ function renderGraphAscii(mermaid) {
2646
+ return renderMermaidASCII(mermaid, { colorMode: "none" });
2647
+ }
2648
+ /**
2649
+ * Render a Mermaid diagram string to SVG.
2650
+ */
2651
+ function renderGraphSvg(mermaid) {
2652
+ return renderMermaidSVG(mermaid, { transparent: true });
2653
+ }
2654
+ //#endregion
2655
+ //#region src/indexer.ts
2656
+ function emptyBuildResult(root) {
2657
+ const gitRepo = isGitRepo(root);
2658
+ return {
2659
+ totalFiles: 0,
2660
+ newFiles: 0,
2661
+ updatedFiles: 0,
2662
+ deletedFiles: 0,
2663
+ unchangedFiles: 0,
2664
+ branch: gitRepo ? getCurrentBranch(root) || "detached" : "default",
2665
+ commit: gitRepo ? getCurrentCommit(root) : "",
2666
+ durationMs: 0
2667
+ };
2668
+ }
2669
+ async function buildIndex(root, options = {}) {
2670
+ const start = Date.now();
2671
+ const config = loadConfig(root);
2672
+ const parserManager = new ParserManager();
2673
+ const llmService = new LLMService(config);
2674
+ const gitRepo = isGitRepo(root);
2675
+ let result;
2676
+ if (gitRepo && !options.full) result = await buildGitAware(root, config, parserManager, llmService, options);
2677
+ else result = await buildClassic(root, config, parserManager, llmService, options);
2678
+ result.durationMs = Date.now() - start;
2679
+ return result;
2680
+ }
2681
+ async function buildGitAware(root, config, parserManager, llmService, options) {
2682
+ const branch = getCurrentBranch(root);
2683
+ const commit = getCurrentCommit(root);
2684
+ const dbName = branchToDbName(branch, commit);
2685
+ const state = loadState(root);
2686
+ const currentConfigHash = hashConfig(config);
2687
+ let result;
2688
+ if (state.configHash && state.configHash !== currentConfigHash) {
2689
+ result = await buildClassic(root, config, parserManager, llmService, options, dbName);
2690
+ state.configHash = currentConfigHash;
2691
+ setBranchState(state, dbName, {
2692
+ lastCommit: commit,
2693
+ lastBuilt: Date.now()
2694
+ });
2695
+ saveState(root, state);
2696
+ return result;
2697
+ }
2698
+ const branchState = getBranchState(state, dbName);
2699
+ if (branchState?.lastCommit) if (isAncestor(root, branchState.lastCommit, commit)) result = await buildFromGitDiff(root, config, parserManager, llmService, options, dbName, branchState.lastCommit, commit);
2700
+ else result = await buildClassic(root, config, parserManager, llmService, options, dbName);
2701
+ else {
2702
+ const forkedFromDb = tryForkFromParent(root, dbName, state);
2703
+ if (forkedFromDb) {
2704
+ setBranchState(state, dbName, {
2705
+ lastCommit: "",
2706
+ lastBuilt: 0,
2707
+ forkedFrom: forkedFromDb
2708
+ });
2709
+ const mergeBase = getMergeBase(root, getBranchState(state, forkedFromDb).lastCommit, commit);
2710
+ if (mergeBase) result = await buildFromGitDiff(root, config, parserManager, llmService, options, dbName, mergeBase, commit);
2711
+ else result = await buildClassic(root, config, parserManager, llmService, options, dbName);
2712
+ } else result = await buildClassic(root, config, parserManager, llmService, options, dbName);
2713
+ }
2714
+ state.configHash = currentConfigHash;
2715
+ const newBranchState = {
2716
+ lastCommit: commit,
2717
+ lastBuilt: Date.now()
2718
+ };
2719
+ const currentBranchState = getBranchState(state, dbName);
2720
+ if (currentBranchState?.forkedFrom) newBranchState.forkedFrom = currentBranchState.forkedFrom;
2721
+ setBranchState(state, dbName, newBranchState);
2722
+ saveState(root, state);
2723
+ return result;
2724
+ }
2725
+ function tryForkFromParent(root, dbName, state) {
2726
+ for (const candidate of ["main", "master"]) if (state.branches[candidate] && candidate !== dbName) {
2727
+ copyDatabase(root, candidate, dbName);
2728
+ return candidate;
2729
+ }
2730
+ return null;
2731
+ }
2732
+ async function buildFromGitDiff(root, config, parserManager, llmService, options, dbName, fromCommit, toCommit) {
2733
+ const filtered = filterGitDiff(getChangedFiles(root, fromCommit, toCommit), config);
2734
+ const toIndex = [...filtered.toIndex];
2735
+ const toDelete = [...filtered.toDelete];
2736
+ for (const r of filtered.renamed) {
2737
+ toIndex.push(r.to);
2738
+ toDelete.push(r.from);
2739
+ }
2740
+ const db = openDatabase(root, dbName);
2741
+ const result = emptyBuildResult(root);
2742
+ try {
2743
+ if (toDelete.length > 0) {
2744
+ db.removeFiles(toDelete);
2745
+ db.removeDependenciesBatch(toDelete);
2746
+ result.deletedFiles = toDelete.length;
2747
+ }
2748
+ if (toIndex.length === 0) {
2749
+ options.onProgress?.({
2750
+ total: 0,
2751
+ completed: 0,
2752
+ current: "",
2753
+ skipped: 0
2754
+ });
2755
+ result.totalFiles = db.getFileCount();
2756
+ result.unchangedFiles = result.totalFiles;
2757
+ return result;
2758
+ }
2759
+ const indexResult = await indexFiles(root, toIndex, db, parserManager, llmService, options);
2760
+ result.newFiles = indexResult.newFiles;
2761
+ result.updatedFiles = indexResult.updatedFiles;
2762
+ result.totalFiles = db.getFileCount();
2763
+ result.unchangedFiles = result.totalFiles - result.newFiles - result.updatedFiles;
2764
+ return result;
2765
+ } finally {
2766
+ db.close();
2767
+ }
2768
+ }
2769
+ async function buildClassic(root, config, parserManager, llmService, options, dbName) {
2770
+ const db = openDatabase(root, dbName || (isGitRepo(root) ? branchToDbName(getCurrentBranch(root), getCurrentCommit(root)) : "default"));
2771
+ const result = emptyBuildResult(root);
2772
+ try {
2773
+ const files = await scanFiles(root, config);
2774
+ const progress = {
2775
+ total: files.length,
2776
+ completed: 0,
2777
+ current: "",
2778
+ skipped: 0
2779
+ };
2780
+ const toIndex = [];
2781
+ const existingPaths = /* @__PURE__ */ new Set();
2782
+ for (const filePath of files) {
2783
+ const hash = hashFile(root, filePath);
2784
+ const existing = db.getFile(filePath);
2785
+ if (existing) existingPaths.add(filePath);
2786
+ if (existing && !hasChanged(existing.hash, hash) && !options.full) {
2787
+ progress.skipped++;
2788
+ progress.completed++;
2789
+ result.unchangedFiles++;
2790
+ options.onProgress?.(progress);
2791
+ continue;
2792
+ }
2793
+ toIndex.push(filePath);
2794
+ }
2795
+ if (toIndex.length > 0) {
2796
+ await indexFiles(root, toIndex, db, parserManager, llmService, options, progress);
2797
+ for (const filePath of toIndex) if (existingPaths.has(filePath)) result.updatedFiles++;
2798
+ else result.newFiles++;
2799
+ }
2800
+ const fileSet = new Set(files);
2801
+ const toRemove = db.getAllFiles().filter((f) => !fileSet.has(f.path)).map((f) => f.path);
2802
+ if (toRemove.length > 0) {
2803
+ db.removeFiles(toRemove);
2804
+ db.removeDependenciesBatch(toRemove);
2805
+ result.deletedFiles = toRemove.length;
2806
+ }
2807
+ result.totalFiles = db.getFileCount();
2808
+ return result;
2809
+ } finally {
2810
+ db.close();
2811
+ }
2812
+ }
2813
+ async function indexFiles(root, filePaths, db, parserManager, llmService, options, progress) {
2814
+ const prog = progress || {
2815
+ total: filePaths.length,
2816
+ completed: 0,
2817
+ current: "",
2818
+ skipped: 0
2819
+ };
2820
+ if (!progress) prog.total = filePaths.length;
2821
+ const parsedFiles = filePaths.map((filePath) => {
2822
+ const fullPath = path.join(root, filePath);
2823
+ const content = fs.readFileSync(fullPath, "utf-8");
2824
+ const hash = hashFile(root, filePath);
2825
+ const language = parserManager.getLanguage(filePath);
2826
+ const parseResult = parserManager.parse(content, filePath);
2827
+ return {
2828
+ path: filePath,
2829
+ content,
2830
+ hash,
2831
+ language: language || "typescript",
2832
+ imports: parseResult?.imports || [],
2833
+ exports: parseResult?.exports || [],
2834
+ symbols: parseResult?.symbols || []
2835
+ };
2836
+ });
2837
+ const llmResults = await llmService.indexFiles(parsedFiles.map((f) => ({
2838
+ path: f.path,
2839
+ content: f.content,
2840
+ symbols: f.symbols
2841
+ })));
2842
+ const fileIndexes = parsedFiles.map((file) => {
2843
+ const llmResult = llmResults.get(file.path);
2844
+ const fileIndex = {
2845
+ path: file.path,
2846
+ name: llmResult?.name || path.basename(file.path),
2847
+ description: llmResult?.description || "",
2848
+ language: file.language,
2849
+ imports: file.imports,
2850
+ exports: file.exports,
2851
+ symbols: file.symbols.map((s) => {
2852
+ const llmSymbol = llmResult?.symbols.find((ls) => ls.name === s.name);
2853
+ return {
2854
+ ...s,
2855
+ description: llmSymbol?.description || s.description
2856
+ };
2857
+ }),
2858
+ summary: llmResult?.summary || "",
2859
+ hash: file.hash,
2860
+ indexedAt: Date.now()
2861
+ };
2862
+ prog.completed++;
2863
+ prog.current = file.path;
2864
+ options.onProgress?.(prog);
2865
+ return fileIndex;
2866
+ });
2867
+ db.upsertFiles(fileIndexes);
2868
+ const allFiles = db.getAllFiles();
2869
+ const indexedPaths = new Set(allFiles.map((f) => f.path));
2870
+ const depEntries = [];
2871
+ for (const file of parsedFiles) {
2872
+ const resolved = [];
2873
+ for (const imp of file.imports) {
2874
+ const target = resolveImport(file.path, imp, indexedPaths);
2875
+ if (target) resolved.push(target);
2876
+ }
2877
+ depEntries.push({
2878
+ fromPath: file.path,
2879
+ toPaths: resolved
2880
+ });
2881
+ }
2882
+ if (depEntries.length > 0) db.upsertBatchDependencies(depEntries);
2883
+ return {
2884
+ indexed: fileIndexes.length,
2885
+ newFiles: 0,
2886
+ updatedFiles: 0
2887
+ };
2888
+ }
2889
+ //#endregion
2890
+ //#region src/llm/reranker.ts
2891
+ /**
2892
+ * Rerank search results using LLM.
2893
+ * Takes FTS5 candidates and returns them reordered by semantic relevance.
2894
+ */
2895
+ async function rerankResults(model, query, candidates, topK = 10) {
2896
+ if (candidates.length === 0) return [];
2897
+ if (candidates.length <= 1) return candidates.slice(0, topK);
2898
+ const rankedPaths = parseRerankResponse((await complete(model, {
2899
+ systemPrompt: RERANK_SYSTEM_PROMPT,
2900
+ messages: [{
2901
+ role: "user",
2902
+ content: buildRerankPrompt(query, candidates.map((c) => ({
2903
+ path: c.file.path,
2904
+ name: c.file.name,
2905
+ description: c.file.description,
2906
+ summary: c.file.summary
2907
+ }))),
2908
+ timestamp: Date.now()
2909
+ }]
2910
+ })).content.filter((block) => block.type === "text").map((block) => block.text).join(""));
2911
+ const resultMap = /* @__PURE__ */ new Map();
2912
+ for (const candidate of candidates) resultMap.set(candidate.file.path, candidate);
2913
+ const reranked = [];
2914
+ const seen = /* @__PURE__ */ new Set();
2915
+ for (const filePath of rankedPaths) {
2916
+ const result = resultMap.get(filePath);
2917
+ if (result && !seen.has(filePath)) {
2918
+ reranked.push(result);
2919
+ seen.add(filePath);
2920
+ }
2921
+ }
2922
+ for (const candidate of candidates) if (!seen.has(candidate.file.path)) {
2923
+ reranked.push(candidate);
2924
+ seen.add(candidate.file.path);
2925
+ }
2926
+ return reranked.slice(0, topK);
2927
+ }
2928
+ /**
2929
+ * Parse the LLM rerank response into an array of file paths.
2930
+ */
2931
+ function parseRerankResponse(text) {
2932
+ const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
2933
+ const jsonStr = jsonMatch ? jsonMatch[1].trim() : text.trim();
2934
+ try {
2935
+ const parsed = JSON.parse(jsonStr);
2936
+ if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) return parsed;
2937
+ } catch {}
2938
+ return jsonStr.split("\n").map((line) => line.replace(/^[\d.)\-\s"]+/, "").replace(/[",\s]+$/, "").trim()).filter((line) => line.length > 0 && (line.includes("/") || line.includes(".")));
2939
+ }
2940
+ //#endregion
2941
+ //#region src/query.ts
2942
+ function searchFiles(db, query, limit = 20) {
2943
+ return db.searchFiles(query, limit);
2944
+ }
2945
+ async function searchFilesWithRerank(db, model, query, topK = 10) {
2946
+ const candidates = db.searchFiles(query, 50);
2947
+ if (candidates.length === 0) return [];
2948
+ return rerankResults(model, query, candidates, topK);
2949
+ }
2950
+ function filterByLanguage(db, language) {
2951
+ return db.getAllFiles().filter((f) => f.language === language);
2952
+ }
2953
+ function filterByPath(db, pathPattern) {
2954
+ return db.getAllFiles().filter((f) => f.path.includes(pathPattern));
2955
+ }
2956
+ //#endregion
2957
+ //#region src/enrich.ts
2958
+ /**
2959
+ * Enrich error stack frames with file descriptions, dependencies, and git history.
2960
+ *
2961
+ * For each frame, looks up the file in the kly index and augments it with:
2962
+ * - File description, summary, symbols, language (from index)
2963
+ * - importedBy / importsFrom (from dependencies table)
2964
+ * - Recent git commits (from git log)
2965
+ *
2966
+ * Frames whose files are not in the index are included with empty/default values.
2967
+ */
2968
+ function enrichErrorStack(db, root, frames) {
2969
+ const allImportedBy = /* @__PURE__ */ new Set();
2970
+ const enrichedFrames = [];
2971
+ for (const frame of frames) {
2972
+ const fileIndex = db.getFile(frame.file);
2973
+ const importedBy = db.getDependents(frame.file);
2974
+ const importsFrom = db.getDependencies(frame.file);
2975
+ const commits = getFileHistory(root, frame.file, 5);
2976
+ for (const dep of importedBy) allImportedBy.add(dep);
2977
+ enrichedFrames.push({
2978
+ file: frame.file,
2979
+ line: frame.line,
2980
+ column: frame.column,
2981
+ function: frame.function,
2982
+ fileDescription: fileIndex?.description ?? "",
2983
+ fileSummary: fileIndex?.summary ?? "",
2984
+ symbols: fileIndex?.symbols ?? [],
2985
+ language: fileIndex?.language ?? "typescript",
2986
+ importedBy,
2987
+ importsFrom,
2988
+ lastModified: commits[0] ?? null,
2989
+ recentCommits: commits
2990
+ });
2991
+ }
2992
+ return {
2993
+ frames: enrichedFrames,
2994
+ affectedFiles: allImportedBy.size
2995
+ };
2996
+ }
2997
+ //#endregion
2998
+ export { getCurrentBranch as A, getConfigPath as B, openDatabase as C, setBranchState as D, saveState as E, isGitRepo as F, hashConfig as G, getDbPath as H, hasChanged as I, loadConfig as J, initKlyDir as K, hashFile as L, getFileHistory as M, getMergeBase as N, branchToDbName as O, isAncestor as P, scanFiles as R, loadState as S, resolveDbName as T, getKlyDir as U, getDbDir as V, getStatePath as W, copyDatabase as _, searchFilesWithRerank as a, getFileFromDb as b, buildDependencyGraph as c, renderGraphAscii as d, renderGraphSvg as f, filterGitDiff as g, ParserManager as h, searchFiles as i, getCurrentCommit as j, getChangedFiles as k, generateMermaid as l, LLMService as m, filterByLanguage as n, rerankResults as o, resolveImport as p, isInitialized as q, filterByPath as r, buildIndex as s, enrichErrorStack as t, isRelativeImport as u, getAllFilesFromDb as v, removeBranchDb as w, listBranchDbs as x, getBranchState as y, IndexDatabase as z };
2999
+
3000
+ //# sourceMappingURL=enrich-BHaZ2daX.mjs.map