@mnemonik/cli 7.154.3 → 7.155.2

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.
Files changed (49) hide show
  1. package/dist/auth/credentials.js +35 -22
  2. package/dist/auth/index.js +35 -22
  3. package/dist/auth/installSession.js +21 -10
  4. package/dist/auth/status.js +21 -10
  5. package/dist/authorizationWords.js +21 -10
  6. package/dist/diagnostics.js +35 -22
  7. package/dist/digests.json +47 -43
  8. package/dist/hooksLog.js +3 -1
  9. package/dist/identity/migrate.js +35 -22
  10. package/dist/install/hosts.js +21 -10
  11. package/dist/install/journal.js +35 -22
  12. package/dist/install/journey.d.ts.map +1 -1
  13. package/dist/install/journey.js +42 -22
  14. package/dist/install/journey.js.map +1 -1
  15. package/dist/install/ownership.js +35 -22
  16. package/dist/install/transaction.js +35 -22
  17. package/dist/install/ui.js +35 -22
  18. package/dist/installSession.js +21 -10
  19. package/dist/installation.js +35 -22
  20. package/dist/launcher.js +35 -22
  21. package/dist/preflight.js +23 -10
  22. package/dist/project/eligibility.js +35 -22
  23. package/dist/project/records.js +35 -22
  24. package/dist/project.js +35 -22
  25. package/dist/router.js +35 -22
  26. package/dist/runtime/releaseSource.js +35 -22
  27. package/dist/runtime/selfUpdate.js +35 -22
  28. package/dist/runtime/signers.js +97 -1
  29. package/dist/runtime/store.js +3 -1
  30. package/dist/runtime/updateCheck.js +35 -22
  31. package/dist/sbom.json +21 -21
  32. package/dist/scanner/control.js +35 -22
  33. package/dist/scanner/data.js +21 -10
  34. package/dist/scanner/discover.js +23 -10
  35. package/dist/scanner/enable.d.ts +5 -0
  36. package/dist/scanner/enable.d.ts.map +1 -1
  37. package/dist/scanner/enable.js +52 -22
  38. package/dist/scanner/enable.js.map +1 -1
  39. package/dist/scanner/estimate.d.ts +41 -0
  40. package/dist/scanner/estimate.d.ts.map +1 -0
  41. package/dist/scanner/estimate.js +2461 -0
  42. package/dist/scanner/estimate.js.map +1 -0
  43. package/dist/scanner/picker.js +23 -10
  44. package/dist/scanner/service.js +35 -22
  45. package/dist/scanner/update.js +35 -22
  46. package/dist/scanner-release.json +24 -24
  47. package/dist/status.js +35 -22
  48. package/dist/vendor/local-setup.js +35 -22
  49. package/package.json +31 -31
@@ -0,0 +1,2461 @@
1
+ import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res, err) => function __init() {
9
+ if (err) throw err[0];
10
+ try {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ } catch (e) {
13
+ throw err = [e], e;
14
+ }
15
+ };
16
+ var __commonJS = (cb, mod) => function __require() {
17
+ try {
18
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
19
+ } catch (e) {
20
+ throw mod = 0, e;
21
+ }
22
+ };
23
+ var __export = (target, all) => {
24
+ for (var name in all)
25
+ __defProp(target, name, { get: all[name], enumerable: true });
26
+ };
27
+ var __copyProps = (to, from, except, desc) => {
28
+ if (from && typeof from === "object" || typeof from === "function") {
29
+ for (let key of __getOwnPropNames(from))
30
+ if (!__hasOwnProp.call(to, key) && key !== except)
31
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
32
+ }
33
+ return to;
34
+ };
35
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
36
+ // If the importer is in node compatibility mode or this is not an ESM
37
+ // file that has been converted to a CommonJS file using a Babel-
38
+ // compatible transform (i.e. "__esModule" has not been set), then set
39
+ // "default" to the CommonJS "module.exports" for node compatibility.
40
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
41
+ mod
42
+ ));
43
+
44
+ // node_modules/ignore/index.js
45
+ var require_ignore = __commonJS({
46
+ "node_modules/ignore/index.js"(exports, module) {
47
+ function makeArray(subject) {
48
+ return Array.isArray(subject) ? subject : [subject];
49
+ }
50
+ var UNDEFINED = void 0;
51
+ var EMPTY = "";
52
+ var SPACE = " ";
53
+ var ESCAPE = "\\";
54
+ var REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/;
55
+ var BOM = "\uFEFF";
56
+ var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
57
+ var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
58
+ var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
59
+ var REGEX_SPLITALL_CRLF = /\r?\n/g;
60
+ var DOUBLE_SLASH = "//";
61
+ var SLASH_CODE = 47;
62
+ var DOT_CODE = 46;
63
+ var SLASH = "/";
64
+ var TMP_KEY_IGNORE = "node-ignore";
65
+ if (typeof Symbol !== "undefined") {
66
+ TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
67
+ }
68
+ var KEY_IGNORE = TMP_KEY_IGNORE;
69
+ var define = (object, key, value) => {
70
+ Object.defineProperty(object, key, { value });
71
+ return value;
72
+ };
73
+ var RETURN_FALSE = () => false;
74
+ var cleanRangeBackSlash = (slashes) => {
75
+ const { length } = slashes;
76
+ return slashes.slice(0, length - length % 2);
77
+ };
78
+ var POSIX_CLASSES = {
79
+ alnum: "0-9A-Za-z",
80
+ alpha: "A-Za-z",
81
+ blank: " \\t",
82
+ cntrl: "\\x00-\\x1f\\x7f",
83
+ digit: "0-9",
84
+ graph: "!-.0-~",
85
+ lower: "a-z",
86
+ print: " -.0-~",
87
+ punct: "!-.:-@\\[-`{-~",
88
+ // git's `sane-ctype.h` classifies \v and \f as control, not space,
89
+ // unlike C's `isspace`
90
+ space: " \\t\\n\\r",
91
+ upper: "A-Z",
92
+ xdigit: "0-9A-Fa-f"
93
+ };
94
+ var CLASS_MEMBERS_TO_ESCAPE = "\\]^-[";
95
+ var escapeMember = (char) => CLASS_MEMBERS_TO_ESCAPE.indexOf(char) < 0 ? char : ESCAPE + char;
96
+ var NON_SLASH = "(?!\\/)";
97
+ var classSource = (negated, body) => {
98
+ if (negated) {
99
+ return `[^\\/${body}]`;
100
+ }
101
+ const source = `[${body}]`;
102
+ return new RegExp(source).test("/") ? NON_SLASH + source : source;
103
+ };
104
+ var scanBracket = (pattern, start) => {
105
+ const { length } = pattern;
106
+ let index = start + 1;
107
+ let negated = EMPTY;
108
+ const lead = pattern[index];
109
+ if (lead === "!" || lead === "^") {
110
+ negated = "^";
111
+ index++;
112
+ }
113
+ let body = EMPTY;
114
+ let prev = EMPTY;
115
+ for (; ; ) {
116
+ const char = pattern[index];
117
+ if (char === UNDEFINED) {
118
+ return null;
119
+ }
120
+ if (char === ESCAPE) {
121
+ const escaped = pattern[index + 1];
122
+ if (escaped === UNDEFINED) {
123
+ return null;
124
+ }
125
+ body += escapeMember(escaped);
126
+ prev = escaped;
127
+ index++;
128
+ } else if (char === "-" && prev && index + 1 < length && pattern[index + 1] !== "]") {
129
+ index++;
130
+ let to = pattern[index];
131
+ if (to === ESCAPE) {
132
+ to = pattern[index += 1];
133
+ }
134
+ if (prev <= to) {
135
+ body += `-${escapeMember(to)}`;
136
+ }
137
+ prev = EMPTY;
138
+ } else if (char === "[" && pattern[index + 1] === ":") {
139
+ const nameStart = index + 2;
140
+ let end = nameStart;
141
+ while (end < length && pattern[end] !== "]") {
142
+ end++;
143
+ }
144
+ if (end === length) {
145
+ return null;
146
+ }
147
+ if (end > nameStart && pattern[end - 1] === ":") {
148
+ const expanded = POSIX_CLASSES[pattern.slice(nameStart, end - 1)];
149
+ if (expanded === UNDEFINED) {
150
+ return null;
151
+ }
152
+ body += expanded;
153
+ prev = EMPTY;
154
+ index = end;
155
+ } else {
156
+ body += escapeMember("[");
157
+ prev = "[";
158
+ index = nameStart - 2;
159
+ }
160
+ } else {
161
+ body += escapeMember(char);
162
+ prev = char;
163
+ }
164
+ index++;
165
+ if (pattern[index] === "]") {
166
+ return {
167
+ end: index,
168
+ source: classSource(negated, body)
169
+ };
170
+ }
171
+ }
172
+ };
173
+ var NEVER_MATCH = "[]";
174
+ var PLACEHOLDER = "\0";
175
+ var REGEX_RESTORE_PLACEHOLDER = new RegExp(
176
+ `${PLACEHOLDER}(\\d+)${PLACEHOLDER}`,
177
+ "g"
178
+ );
179
+ var TRAILING_WILDCARD = "\uE000";
180
+ var TRAILING_DOUBLESTAR = "\uE001";
181
+ var extractBrackets = (pattern) => {
182
+ const sources = [];
183
+ const hold = (source) => `${PLACEHOLDER}${sources.push(source) - 1}${PLACEHOLDER}`;
184
+ const { length } = pattern;
185
+ let out = EMPTY;
186
+ let index = 0;
187
+ while (index < length) {
188
+ const char = pattern[index];
189
+ if (char === ESCAPE) {
190
+ const escaped = pattern[index + 1];
191
+ if (escaped === UNDEFINED || escaped === SLASH && index + 2 === length) {
192
+ out += hold(NEVER_MATCH);
193
+ } else if (escaped === "*" || escaped === "[" || escaped === SPACE || escaped === ESCAPE) {
194
+ out += pattern.slice(index, index + 2);
195
+ } else {
196
+ out += hold(
197
+ REGEX_LITERAL_SPECIAL.test(escaped) ? ESCAPE + escaped : escaped
198
+ );
199
+ }
200
+ index += 2;
201
+ } else if (char === PLACEHOLDER) {
202
+ out += hold(`[${PLACEHOLDER}]`);
203
+ index++;
204
+ } else if (char === "[") {
205
+ const scanned = scanBracket(pattern, index);
206
+ if (scanned === null) {
207
+ out += hold(NEVER_MATCH);
208
+ index = length;
209
+ } else {
210
+ out += hold(scanned.source);
211
+ index = scanned.end + 1;
212
+ }
213
+ } else {
214
+ out += char;
215
+ index++;
216
+ }
217
+ }
218
+ return {
219
+ source: out,
220
+ sources
221
+ };
222
+ };
223
+ var DIRECT = null;
224
+ var REGEX_INNER_SLASH = /\/(?!$)/;
225
+ var REPLACERS = [
226
+ // Replace (\ ) with ' '
227
+ // Only a space: an escaped tab or other whitespace is already a literal by
228
+ // the time it reaches here, and a bare tab must be left as one, not turned
229
+ // into a space.
230
+ // (\ ) -> ' '
231
+ // (\\ ) -> '\\ '
232
+ // (\\\ ) -> '\\ '
233
+ [
234
+ // A run of backslashes is taken whole, from its first one, so no match
235
+ // ever starts again inside it -- `(\\+?) ` did, which made a long run
236
+ // quadratic.
237
+ /(\\+)( ?)/g,
238
+ (_, run, space) => space ? run.slice(0, run.length - run.length % 2) + SPACE : run,
239
+ ESCAPE + SPACE
240
+ ],
241
+ // Escape metacharacters
242
+ // which is written down by users but means special for regular expressions.
243
+ // > There are 12 characters with special meanings:
244
+ // > - the backslash \,
245
+ // > - the caret ^,
246
+ // > - the dollar sign $,
247
+ // > - the period or dot .,
248
+ // > - the vertical bar or pipe symbol |,
249
+ // > - the question mark ?,
250
+ // > - the asterisk or star *,
251
+ // > - the plus sign +,
252
+ // > - the opening parenthesis (,
253
+ // > - the closing parenthesis ),
254
+ // > - and the opening square bracket [,
255
+ // > - the opening curly brace {,
256
+ // > These special characters are often called "metacharacters".
257
+ [
258
+ /[\\$.|*+(){^]/g,
259
+ (match) => `\\${match}`
260
+ ],
261
+ [
262
+ // > a question mark (?) matches a single character
263
+ /(?!\\)\?/g,
264
+ () => "[^/]",
265
+ "?"
266
+ ],
267
+ // leading slash
268
+ [
269
+ // > A leading slash matches the beginning of the pathname.
270
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
271
+ // A leading slash matches the beginning of the pathname
272
+ /^\//,
273
+ () => "^",
274
+ SLASH
275
+ ],
276
+ // replace special metacharacter slash after the leading slash
277
+ [
278
+ /\//g,
279
+ () => "\\/",
280
+ SLASH
281
+ ],
282
+ [
283
+ // > A leading "**" followed by a slash means match in all directories.
284
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
285
+ // > the same as pattern "foo".
286
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
287
+ // > under directory "foo".
288
+ // Notice that the '*'s have been replaced as '\\*'
289
+ /^\^*(?:\\\*\\\*\\\/)+/,
290
+ // '**/foo' <-> 'foo'
291
+ () => "^(?:.*\\/)?",
292
+ "*"
293
+ ],
294
+ // starting
295
+ [
296
+ // there will be no leading '/'
297
+ // (which has been replaced by section "leading slash")
298
+ // If starts with '**', adding a '^' to the regular expression also works
299
+ DIRECT,
300
+ (source, pattern) => {
301
+ if (!source || source[0] === "^") {
302
+ return source;
303
+ }
304
+ const anchor = !REGEX_INNER_SLASH.test(pattern) ? "(?:^|\\/)" : "^";
305
+ return anchor + source;
306
+ }
307
+ ],
308
+ // two globstars
309
+ [
310
+ // Use lookahead assertions so that we could match more than one `'/**'`
311
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
312
+ // Zero, one or several directories
313
+ // should not use '*', or it will be replaced by the next replacer
314
+ // Check if it is not the last `'/**'`
315
+ (_, index, str) => index + 6 < str.length ? str.slice(index + 6) === "\\/" ? "(?:\\/[^\\/]+)+" : "(?:\\/[^\\/]+)*" : `\\/${TRAILING_DOUBLESTAR}`,
316
+ "*"
317
+ ],
318
+ // normal intermediate wildcards
319
+ [
320
+ // Never replace escaped '*'
321
+ // ignore rule '\*' will match the path '*'
322
+ // 'abc.*/' -> go
323
+ // 'abc.*' -> skip this rule,
324
+ // coz trailing single wildcard will be handed by [trailing wildcard]
325
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
326
+ // '*.js' matches '.js'
327
+ // '*.js' doesn't match 'abc'
328
+ (_, p1, p2) => {
329
+ const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
330
+ return p1 + unescaped;
331
+ },
332
+ "*"
333
+ ],
334
+ // trailing wildcard, held apart from a literal star
335
+ [
336
+ // The step above leaves a trailing `*` alone, so a single `\*` is all that
337
+ // can be left at the end here. Whether it is a wildcard or a literal
338
+ // turns on the backslashes the user put in front of it: the escaper has
339
+ // since doubled every one, so what stands here is those `2N` doubled
340
+ // backslashes and then the star's own escape. An even number of the
341
+ // original `N` leaves the star unescaped -- a wildcard -- and an odd
342
+ // number escapes it -- a literal. This runs while the two are still
343
+ // distinct, before the unescape steps below collapse the literal onto
344
+ // the very `\*` a wildcard leaves behind.
345
+ /(^|[^\\])((?:\\\\)*)\\\*$/,
346
+ (match, p1, p2) => (
347
+ // `p2` holds the doubled user backslashes; half of them is `N`.
348
+ p2.length / 2 % 2 === 0 ? p1 + p2 + TRAILING_WILDCARD : match
349
+ ),
350
+ "*"
351
+ ],
352
+ [
353
+ // unescape, revert step 3 except for back slash
354
+ // For example, if a user escape a '\\*',
355
+ // after step 3, the result will be '\\\\\\*'
356
+ /\\\\\\(?=[$.|*+(){^])/g,
357
+ () => ESCAPE,
358
+ ESCAPE + ESCAPE
359
+ ],
360
+ [
361
+ // '\\\\' -> '\\'
362
+ /\\\\/g,
363
+ () => ESCAPE,
364
+ ESCAPE + ESCAPE
365
+ ],
366
+ [
367
+ // Every real bracket expression -- POSIX classes included -- has already
368
+ // been held aside by `extractBrackets`, so the only `[` left in the
369
+ // pattern is an escaped, literal one.
370
+ // `\` is escaped by step 3
371
+ /\\\[([^\]/]*?)(\\*)($|\])/g,
372
+ // '\\[bar]' -> '\\\\[bar\\]'
373
+ (match, range, endEscape, close) => `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`,
374
+ "["
375
+ ],
376
+ // ending
377
+ [
378
+ // 'js' will not match 'js.'
379
+ // 'ab' will not match 'abc'
380
+ DIRECT,
381
+ // WTF!
382
+ // https://git-scm.com/docs/gitignore
383
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
384
+ // which re-fixes #24, #38
385
+ // > If there is a separator at the end of the pattern then the pattern
386
+ // > will only match directories, otherwise the pattern can match both
387
+ // > files and directories.
388
+ // 'js*' will not match 'a.js'
389
+ // 'js/' will not match 'a.js'
390
+ // 'js' will match 'a.js' and 'a.js/'
391
+ (source) => {
392
+ const last = source[source.length - 1];
393
+ if (!last || last === TRAILING_WILDCARD || last === TRAILING_DOUBLESTAR) {
394
+ return source;
395
+ }
396
+ return last === SLASH ? `${source}$` : `${source}(?=$|\\/$)`;
397
+ }
398
+ ]
399
+ ];
400
+ var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\uE000$/;
401
+ var MODE_IGNORE = "regex";
402
+ var MODE_CHECK_IGNORE = "checkRegex";
403
+ var UNDERSCORE = "_";
404
+ var replaceTrailingWildcard = (_, p1) => {
405
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
406
+ return `${prefix}(?=$|\\/$)`;
407
+ };
408
+ var REGEX_REPLACE_TRAILING_DOUBLESTAR = /\uE001$/;
409
+ var replaceTrailingDoublestar = () => ".+(?=$|\\/$)";
410
+ var WILDCARD = "[^\\/]*";
411
+ var separatorAfter = (run, at) => {
412
+ let separator = EMPTY;
413
+ for (let index = at + 1; index < run.length && !run[index].wildcard; index++) {
414
+ separator += run[index].single;
415
+ }
416
+ return separator;
417
+ };
418
+ var pinWildcards = (source) => {
419
+ if (source.indexOf(WILDCARD) < 0) {
420
+ return source;
421
+ }
422
+ const tokens = [];
423
+ const { length } = source;
424
+ let index = 0;
425
+ while (index < length) {
426
+ const char = source[index];
427
+ if (source.startsWith(WILDCARD, index)) {
428
+ tokens.push({ wildcard: true });
429
+ index += WILDCARD.length;
430
+ } else if (char === "[") {
431
+ let end = index + 1;
432
+ if (source[end] === "^") {
433
+ end++;
434
+ }
435
+ if (source[end] === "]") {
436
+ end++;
437
+ }
438
+ while (end < length && source[end] !== "]") {
439
+ end += source[end] === ESCAPE ? 2 : 1;
440
+ }
441
+ end++;
442
+ tokens.push({ single: source.slice(index, end) });
443
+ index = end;
444
+ } else if (char === ESCAPE) {
445
+ tokens.push({ single: source.slice(index, index + 2) });
446
+ index += 2;
447
+ } else if (char === "(") {
448
+ let depth = 0;
449
+ let end = index;
450
+ do {
451
+ if (source[end] === ESCAPE) {
452
+ end++;
453
+ } else if (source[end] === "(") {
454
+ depth++;
455
+ } else if (source[end] === ")") {
456
+ depth--;
457
+ }
458
+ end++;
459
+ } while (end < length && depth > 0);
460
+ if ("*+?".indexOf(source[end]) >= 0) {
461
+ end++;
462
+ }
463
+ tokens.push({ boundary: source.slice(index, end) });
464
+ index = end;
465
+ } else if (char === "^" || char === "$") {
466
+ tokens.push({ boundary: char });
467
+ index++;
468
+ } else {
469
+ tokens.push({ single: char });
470
+ index++;
471
+ }
472
+ }
473
+ let out = EMPTY;
474
+ let run = [];
475
+ const flush = () => {
476
+ let lastWildcard;
477
+ run.forEach((token, at) => {
478
+ if (token.wildcard) {
479
+ lastWildcard = at;
480
+ }
481
+ });
482
+ run.forEach((token, at) => {
483
+ if (!token.wildcard) {
484
+ out += token.single;
485
+ return;
486
+ }
487
+ out += at === lastWildcard ? WILDCARD : `(?:(?!${separatorAfter(run, at)})[^\\/])*`;
488
+ });
489
+ run = [];
490
+ };
491
+ tokens.forEach((token) => {
492
+ if (token.boundary === void 0) {
493
+ run.push(token);
494
+ return;
495
+ }
496
+ flush();
497
+ out += token.boundary;
498
+ });
499
+ flush();
500
+ return out;
501
+ };
502
+ var makeRegexPrefix = (pattern) => {
503
+ const { source, sources } = extractBrackets(pattern);
504
+ const replaced = REPLACERS.reduce(
505
+ // A pass whose matcher finds nothing hands back the very string it was
506
+ // given, so asking first costs a search and saves a rewrite. Ten of the
507
+ // fifteen passes never fire for a typical .gitignore line, and between
508
+ // them they were 45% of this chain.
509
+ (prev, [matcher, replacer, required]) => {
510
+ if (matcher === DIRECT) {
511
+ return replacer(prev, pattern);
512
+ }
513
+ if (required !== UNDEFINED && prev.indexOf(required) < 0) {
514
+ return prev;
515
+ }
516
+ return matcher.test(prev) ? prev.replace(matcher, replacer.bind(pattern)) : prev;
517
+ },
518
+ source
519
+ );
520
+ return sources.length ? replaced.replace(
521
+ REGEX_RESTORE_PLACEHOLDER,
522
+ (match, index) => sources[index]
523
+ ) : replaced;
524
+ };
525
+ var checkSourceOf = (body, prefix) => {
526
+ if (body[body.length - 1] === SLASH) {
527
+ prefix = makeRegexPrefix(body.slice(0, -1));
528
+ }
529
+ const head = prefix.slice(0, -1);
530
+ const last = prefix[prefix.length - 1];
531
+ return last === TRAILING_WILDCARD ? `${head}$` : last === TRAILING_DOUBLESTAR ? `${head}.*$` : NEVER_MATCH;
532
+ };
533
+ var matchesBasename = (body) => {
534
+ const index = body.indexOf(SLASH);
535
+ return index < 0 || index === body.length - 1;
536
+ };
537
+ var basenameOf = (path) => {
538
+ const end = path.length - 1;
539
+ const index = path.lastIndexOf(
540
+ SLASH,
541
+ path[end] === SLASH ? end - 1 : end
542
+ );
543
+ return index < 0 ? path : path.slice(index + 1);
544
+ };
545
+ var parentOf = (path) => {
546
+ if (path.charCodeAt(0) === SLASH_CODE || path.indexOf(DOUBLE_SLASH) >= 0) {
547
+ const slices = path.split(SLASH).filter(Boolean);
548
+ slices.pop();
549
+ return slices.length ? slices.join(SLASH) + SLASH : EMPTY;
550
+ }
551
+ const end = path.length - 1;
552
+ const cut = path.lastIndexOf(
553
+ SLASH,
554
+ path.charCodeAt(end) === SLASH_CODE ? end - 1 : end
555
+ );
556
+ return cut < 0 ? EMPTY : path.slice(0, cut + 1);
557
+ };
558
+ var isString = (subject) => typeof subject === "string";
559
+ var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern);
560
+ var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
561
+ var IgnoreRule = class {
562
+ constructor(pattern, mark, body, ignoreCase, negative, prefix) {
563
+ this.pattern = pattern;
564
+ this.mark = mark;
565
+ this.negative = negative;
566
+ define(this, "body", body);
567
+ define(this, "ignoreCase", ignoreCase);
568
+ define(this, "regexPrefix", prefix);
569
+ }
570
+ // Worked out on first use and kept behind an own property, the way `regex`
571
+ // caches itself in `_regex`. Deciding it in the constructor instead would
572
+ // add a fourth `defineProperty` to every rule ever built, which cost 4% of
573
+ // every compile -- including the compiles of rules that are never matched
574
+ // against anything.
575
+ get _basenameOnly() {
576
+ return define(this, "_basenameOnly", matchesBasename(this.body));
577
+ }
578
+ get regex() {
579
+ const key = UNDERSCORE + MODE_IGNORE;
580
+ if (this[key]) {
581
+ return this[key];
582
+ }
583
+ return this._make(MODE_IGNORE, key);
584
+ }
585
+ get checkRegex() {
586
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
587
+ if (this[key]) {
588
+ return this[key];
589
+ }
590
+ return this._make(MODE_CHECK_IGNORE, key);
591
+ }
592
+ _make(mode, key) {
593
+ const str = pinWildcards(
594
+ mode === MODE_IGNORE ? this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD, replaceTrailingWildcard).replace(REGEX_REPLACE_TRAILING_DOUBLESTAR, replaceTrailingDoublestar) : checkSourceOf(this.body, this.regexPrefix)
595
+ );
596
+ const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
597
+ return define(this, key, regex);
598
+ }
599
+ };
600
+ var isLineEnd = (char) => char === "\r" || char === "\n";
601
+ var trimEnd = (body) => {
602
+ let end = body.length;
603
+ while (end && isLineEnd(body[end - 1])) {
604
+ end--;
605
+ }
606
+ const lineEnd = end;
607
+ while (end && body[end - 1] === SPACE) {
608
+ end--;
609
+ }
610
+ if (end === lineEnd) {
611
+ return body.slice(0, end);
612
+ }
613
+ let backslashes = 0;
614
+ while (backslashes < end && body[end - backslashes - 1] === ESCAPE) {
615
+ backslashes++;
616
+ }
617
+ return backslashes % 2 ? body.slice(0, end - 1) + SPACE : body.slice(0, end);
618
+ };
619
+ var createRule = ({
620
+ pattern,
621
+ mark
622
+ }, ignoreCase) => {
623
+ let body = pattern[0] === BOM ? pattern.slice(1) : pattern;
624
+ if (body[0] === "#") {
625
+ return;
626
+ }
627
+ let negative = false;
628
+ if (body[0] === "!") {
629
+ negative = true;
630
+ body = body.slice(1);
631
+ }
632
+ const last = body[body.length - 1];
633
+ if (last === SPACE || last === "\r" || last === "\n") {
634
+ body = trimEnd(body);
635
+ }
636
+ if (!body) {
637
+ return;
638
+ }
639
+ body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
640
+ const regexPrefix = makeRegexPrefix(body);
641
+ return new IgnoreRule(
642
+ pattern,
643
+ mark,
644
+ body,
645
+ ignoreCase,
646
+ negative,
647
+ regexPrefix
648
+ );
649
+ };
650
+ var RuleManager = class {
651
+ constructor(ignoreCase) {
652
+ this._ignoreCase = ignoreCase;
653
+ this._rules = [];
654
+ this._basenameCount = 0;
655
+ }
656
+ _add(pattern) {
657
+ if (pattern && pattern[KEY_IGNORE]) {
658
+ this._rules = this._rules.concat(pattern._rules._rules);
659
+ this._basenameCount += pattern._rules._basenameCount;
660
+ this._added = true;
661
+ return;
662
+ }
663
+ if (isString(pattern)) {
664
+ pattern = {
665
+ pattern
666
+ };
667
+ }
668
+ const rule = checkPattern(pattern.pattern) && createRule(pattern, this._ignoreCase);
669
+ if (rule) {
670
+ this._added = true;
671
+ this._rules.push(rule);
672
+ if (matchesBasename(rule.body)) {
673
+ this._basenameCount++;
674
+ }
675
+ }
676
+ }
677
+ // @param {Array<string> | string | Ignore} pattern
678
+ add(pattern) {
679
+ this._added = false;
680
+ makeArray(
681
+ isString(pattern) ? splitPattern(pattern) : pattern
682
+ ).forEach(this._add, this);
683
+ if (this._added) {
684
+ this._literalRules = UNDEFINED;
685
+ }
686
+ return this._added;
687
+ }
688
+ // Match the literal 'abc/' for `checkIgnore`, last rule wins. Only a rule
689
+ // ending in a wildcard can match it (see `checkSourceOf`), so the rest
690
+ // are left out once, rather than tested or compiled for every path.
691
+ testLiteral(path) {
692
+ const rules = this._literalRules || (this._literalRules = this._rules.filter(
693
+ ({ body }) => body[body.length - (body[body.length - 1] === SLASH ? 2 : 1)] === "*"
694
+ ));
695
+ let ignored = false;
696
+ let unignored = false;
697
+ let matchedRule;
698
+ for (let index = rules.length - 1; index >= 0; index--) {
699
+ const rule = rules[index];
700
+ if (rule.checkRegex.test(path)) {
701
+ ignored = !rule.negative;
702
+ unignored = rule.negative;
703
+ matchedRule = rule.negative ? UNDEFINED : rule;
704
+ break;
705
+ }
706
+ }
707
+ const ret = {
708
+ ignored,
709
+ unignored
710
+ };
711
+ if (matchedRule) {
712
+ ret.rule = matchedRule;
713
+ }
714
+ return ret;
715
+ }
716
+ // Test one single path without recursively checking parent directories
717
+ //
718
+ // - checkUnignored `boolean` whether should check if the path is unignored,
719
+ // setting `checkUnignored` to `false` could reduce additional
720
+ // path matching.
721
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
722
+ // @returns {TestResult} true if a file is ignored
723
+ test(path, checkUnignored, mode) {
724
+ let ignored = false;
725
+ let unignored = false;
726
+ let matchedRule;
727
+ const rules = this._rules;
728
+ const { length } = rules;
729
+ const shortcut = this._basenameCount * 2 >= length;
730
+ const basename = shortcut ? basenameOf(path) : path;
731
+ for (let index = 0; index < length; index++) {
732
+ const rule = rules[index];
733
+ const { negative } = rule;
734
+ const skip = unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored;
735
+ if (!skip && rule[mode].test(
736
+ shortcut && rule._basenameOnly ? basename : path
737
+ )) {
738
+ ignored = !negative;
739
+ unignored = negative;
740
+ matchedRule = negative ? UNDEFINED : rule;
741
+ }
742
+ }
743
+ const ret = {
744
+ ignored,
745
+ unignored
746
+ };
747
+ if (matchedRule) {
748
+ ret.rule = matchedRule;
749
+ }
750
+ return ret;
751
+ }
752
+ };
753
+ var throwError = (message, Ctor) => {
754
+ throw new Ctor(message);
755
+ };
756
+ var checkPath = (path, originalPath, doThrow) => {
757
+ if (!isString(path)) {
758
+ return doThrow(
759
+ `path must be a string, but got \`${originalPath}\``,
760
+ TypeError
761
+ );
762
+ }
763
+ if (!path) {
764
+ return doThrow(`path must not be empty`, TypeError);
765
+ }
766
+ if (checkPath.isNotRelative(path)) {
767
+ const r = "`path.relative()`d";
768
+ return doThrow(
769
+ `path should be a ${r} string, but got "${originalPath}"`,
770
+ RangeError
771
+ );
772
+ }
773
+ return true;
774
+ };
775
+ var isNotRelative = (path) => {
776
+ const first = path.charCodeAt(0);
777
+ if (first === SLASH_CODE) {
778
+ return true;
779
+ }
780
+ if (first !== DOT_CODE) {
781
+ return false;
782
+ }
783
+ if (path.length === 1) {
784
+ return true;
785
+ }
786
+ const second = path.charCodeAt(1);
787
+ if (second === SLASH_CODE) {
788
+ return true;
789
+ }
790
+ if (second !== DOT_CODE) {
791
+ return false;
792
+ }
793
+ return path.length === 2 || path.charCodeAt(2) === SLASH_CODE;
794
+ };
795
+ checkPath.isNotRelative = isNotRelative;
796
+ checkPath.convert = (p) => p;
797
+ var Ignore = class {
798
+ constructor({
799
+ ignorecase = true,
800
+ ignoreCase = ignorecase,
801
+ allowRelativePaths = false
802
+ } = {}) {
803
+ define(this, KEY_IGNORE, true);
804
+ this._rules = new RuleManager(ignoreCase);
805
+ this._strictPathCheck = !allowRelativePaths;
806
+ this._initCache();
807
+ }
808
+ _initCache() {
809
+ this._ignoreCache = /* @__PURE__ */ Object.create(null);
810
+ this._testCache = /* @__PURE__ */ Object.create(null);
811
+ }
812
+ add(pattern) {
813
+ if (this._rules.add(pattern)) {
814
+ this._initCache();
815
+ }
816
+ return this;
817
+ }
818
+ // legacy
819
+ addPattern(pattern) {
820
+ return this.add(pattern);
821
+ }
822
+ // @returns {TestResult}
823
+ _test(originalPath, cache, checkUnignored) {
824
+ const path = originalPath && checkPath.convert(originalPath);
825
+ checkPath(
826
+ path,
827
+ originalPath,
828
+ this._strictPathCheck ? throwError : RETURN_FALSE
829
+ );
830
+ return this._t(path, cache, checkUnignored);
831
+ }
832
+ checkIgnore(path) {
833
+ if (path.charCodeAt(path.length - 1) !== SLASH_CODE) {
834
+ return this.test(path);
835
+ }
836
+ const dir = this._t(path, this._testCache, true);
837
+ if (dir.ignored) {
838
+ return dir;
839
+ }
840
+ const literal = this._rules.testLiteral(path);
841
+ return literal.ignored || literal.unignored ? literal : dir;
842
+ }
843
+ _t(path, cache, checkUnignored) {
844
+ if (path in cache) {
845
+ return cache[path];
846
+ }
847
+ const parentPath = parentOf(path);
848
+ const parent = parentPath ? this._t(parentPath, cache, checkUnignored) : UNDEFINED;
849
+ return cache[path] = parent && parent.ignored ? parent : this._rules.test(path, checkUnignored, MODE_IGNORE);
850
+ }
851
+ ignores(path) {
852
+ return this._test(path, this._ignoreCache, false).ignored;
853
+ }
854
+ createFilter() {
855
+ return (path) => !this.ignores(path);
856
+ }
857
+ filter(paths) {
858
+ return makeArray(paths).filter(this.createFilter());
859
+ }
860
+ // @returns {TestResult}
861
+ test(path) {
862
+ return this._test(path, this._testCache, true);
863
+ }
864
+ };
865
+ var factory = (options) => new Ignore(options);
866
+ var isPathValid = (path) => checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
867
+ var setupWindows = () => {
868
+ const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
869
+ checkPath.convert = makePosix;
870
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
871
+ checkPath.isNotRelative = (path) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path);
872
+ };
873
+ if (
874
+ // Detect `process` so that it can run in browsers.
875
+ typeof process !== "undefined" && process.platform === "win32"
876
+ ) {
877
+ setupWindows();
878
+ }
879
+ module.exports = factory;
880
+ factory.default = factory;
881
+ module.exports.isPathValid = isPathValid;
882
+ define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
883
+ }
884
+ });
885
+
886
+ // packages/shared/dist/logger.js
887
+ var debug;
888
+ var init_logger = __esm({
889
+ "packages/shared/dist/logger.js"() {
890
+ "use strict";
891
+ debug = (_msg, _ctx) => {
892
+ };
893
+ }
894
+ });
895
+
896
+ // packages/shared/dist/protectedPaths.js
897
+ import { homedir } from "node:os";
898
+ import { posix, win32 } from "node:path";
899
+ function pathsFor(platform) {
900
+ return platform === "win32" ? win32 : posix;
901
+ }
902
+ function mnemonikStateDirectory(platform = process.platform, env = process.env, home = homedir()) {
903
+ if (env.MNEMONIK_STATE_DIR)
904
+ return env.MNEMONIK_STATE_DIR;
905
+ if (platform === "win32")
906
+ return win32.join(env.LOCALAPPDATA || win32.join(home, "AppData", "Local"), "Mnemonik");
907
+ if (platform === "darwin")
908
+ return posix.join(home, "Library", "Application Support", "Mnemonik");
909
+ return posix.join(env.XDG_STATE_HOME || posix.join(home, ".local", "state"), "mnemonik");
910
+ }
911
+ function protectedLocalPaths(platform = process.platform, env = process.env, home = homedir()) {
912
+ const path = pathsFor(platform);
913
+ const localAppData = env.LOCALAPPDATA || win32.join(home, "AppData", "Local");
914
+ const appData = env.APPDATA || win32.join(home, "AppData", "Roaming");
915
+ const state = mnemonikStateDirectory(platform, env, home);
916
+ const common = [
917
+ state,
918
+ path.join(home, ".mnemonik"),
919
+ path.join(home, ".claude", ".credentials.json"),
920
+ path.join(home, ".codex", "auth.json"),
921
+ path.join(home, ".cursor", "mcp.json"),
922
+ path.join(home, ".grok", "mcp_credentials.json")
923
+ ];
924
+ const osPaths = platform === "darwin" ? [path.join(home, "Library", "Keychains")] : platform === "win32" ? [
925
+ win32.join(appData, "Microsoft", "Credentials"),
926
+ win32.join(localAppData, "Microsoft", "Credentials"),
927
+ win32.join(appData, "Microsoft", "Protect")
928
+ ] : [
929
+ path.join(home, ".local", "share", "keyrings"),
930
+ path.join(home, ".gnupg"),
931
+ path.join(home, ".ssh"),
932
+ path.join(home, ".aws"),
933
+ path.join(home, ".config", "gcloud"),
934
+ path.join(home, ".docker", "config.json"),
935
+ path.join(home, ".npmrc"),
936
+ path.join(home, ".netrc"),
937
+ path.join(home, ".pypirc")
938
+ ];
939
+ return [.../* @__PURE__ */ new Set([...common, ...osPaths])];
940
+ }
941
+ function comparable(value, platform) {
942
+ const path = pathsFor(platform);
943
+ const normalized = path.resolve(value);
944
+ return platform === "win32" || platform === "darwin" ? normalized.toLowerCase() : normalized;
945
+ }
946
+ function isProtectedLocalPath(candidate, protectedPaths = protectedLocalPaths(), platform = process.platform) {
947
+ const path = pathsFor(platform);
948
+ const target = comparable(candidate, platform);
949
+ return protectedPaths.some((protectedPath) => {
950
+ const parent = comparable(protectedPath, platform);
951
+ const relative2 = path.relative(parent, target);
952
+ return relative2 === "" || !relative2.startsWith(`..${path.sep}`) && relative2 !== ".." && !path.isAbsolute(relative2);
953
+ });
954
+ }
955
+ var init_protectedPaths = __esm({
956
+ "packages/shared/dist/protectedPaths.js"() {
957
+ "use strict";
958
+ }
959
+ });
960
+
961
+ // packages/shared/dist/projectIdentityFile.js
962
+ import { lstat, readFile } from "node:fs/promises";
963
+ import { join } from "node:path";
964
+ function parseIdentityFile(text) {
965
+ let value;
966
+ try {
967
+ value = JSON.parse(text);
968
+ } catch (error) {
969
+ return { kind: "malformed", detail: `unparseable JSON: ${error.message}` };
970
+ }
971
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
972
+ return { kind: "malformed", detail: "identity file must contain a JSON object" };
973
+ }
974
+ const record = value;
975
+ if (record.schemaVersion !== 1) {
976
+ return { kind: "unknown_version", version: record.schemaVersion };
977
+ }
978
+ const unknownKey = Object.keys(record).find((key) => !IDENTITY_KEYS.has(key));
979
+ if (unknownKey)
980
+ return { kind: "malformed", detail: `unknown key "${unknownKey}"` };
981
+ if (!isCanonicalUuid(record.projectId)) {
982
+ return { kind: "malformed", detail: '"projectId" must be a canonical RFC 4122 UUID string' };
983
+ }
984
+ if ("projectName" in record && typeof record.projectName !== "string") {
985
+ return { kind: "malformed", detail: '"projectName" must be a string when present' };
986
+ }
987
+ if ("repositoryFingerprint" in record && (!record.repositoryFingerprint || typeof record.repositoryFingerprint !== "object" || Array.isArray(record.repositoryFingerprint))) {
988
+ return { kind: "malformed", detail: '"repositoryFingerprint" must be an object when present' };
989
+ }
990
+ const fingerprint = record.repositoryFingerprint;
991
+ const unknownFingerprintKey = fingerprint ? Object.keys(fingerprint).find((key) => !FINGERPRINT_KEYS.has(key)) : void 0;
992
+ if (unknownFingerprintKey) {
993
+ return {
994
+ kind: "malformed",
995
+ detail: `unknown repositoryFingerprint key "${unknownFingerprintKey}"`
996
+ };
997
+ }
998
+ if (fingerprint && (fingerprint.algorithmVersion !== 1 || typeof fingerprint.hash !== "string")) {
999
+ return {
1000
+ kind: "malformed",
1001
+ detail: '"repositoryFingerprint" must contain algorithmVersion 1 and a string hash'
1002
+ };
1003
+ }
1004
+ const repositoryFingerprint = fingerprint ? { algorithmVersion: 1, hash: fingerprint.hash } : void 0;
1005
+ return {
1006
+ kind: "ok",
1007
+ identity: {
1008
+ schemaVersion: 1,
1009
+ projectId: record.projectId,
1010
+ ...record.projectName === void 0 ? {} : { projectName: record.projectName },
1011
+ ...repositoryFingerprint ? { repositoryFingerprint } : {}
1012
+ }
1013
+ };
1014
+ }
1015
+ function isConfigurationOnly(text) {
1016
+ try {
1017
+ const value = JSON.parse(text);
1018
+ return !!value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1);
1019
+ } catch {
1020
+ return false;
1021
+ }
1022
+ }
1023
+ async function readIdentityFile(dir, options = {}) {
1024
+ const path = join(dir, ".mnemonik.json");
1025
+ try {
1026
+ const stat = await lstat(path);
1027
+ if (stat.isSymbolicLink())
1028
+ return { kind: "malformed", detail: "symlink" };
1029
+ if (!stat.isFile())
1030
+ return { kind: "malformed", detail: "identity path is not a regular file" };
1031
+ const text = await readFile(path, "utf8");
1032
+ if ((options.selectedRoot || options.configurationOnlyAbsent) && isConfigurationOnly(text))
1033
+ return { kind: "absent" };
1034
+ return parseIdentityFile(text);
1035
+ } catch (error) {
1036
+ if (error.code === "ENOENT")
1037
+ return { kind: "absent" };
1038
+ return { kind: "malformed", detail: `cannot read identity file: ${error.message}` };
1039
+ }
1040
+ }
1041
+ var CANONICAL_UUID, isCanonicalUuid, IDENTITY_KEYS, FINGERPRINT_KEYS;
1042
+ var init_projectIdentityFile = __esm({
1043
+ "packages/shared/dist/projectIdentityFile.js"() {
1044
+ "use strict";
1045
+ CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
1046
+ isCanonicalUuid = (value) => typeof value === "string" && CANONICAL_UUID.test(value);
1047
+ IDENTITY_KEYS = /* @__PURE__ */ new Set([
1048
+ "schemaVersion",
1049
+ "projectId",
1050
+ "projectName",
1051
+ "repositoryFingerprint"
1052
+ ]);
1053
+ FINGERPRINT_KEYS = /* @__PURE__ */ new Set(["algorithmVersion", "hash"]);
1054
+ }
1055
+ });
1056
+
1057
+ // packages/shared/dist/indexClassifier.js
1058
+ import { posix as posix2 } from "path";
1059
+ function isTestOrSpecName(base) {
1060
+ return base.includes(".test.") || base.includes(".spec.");
1061
+ }
1062
+ function hasGeneratedStemToken(base) {
1063
+ const tokens = base.split(".");
1064
+ if (tokens.length < 2)
1065
+ return false;
1066
+ return tokens.slice(0, -1).some((token) => token.toLowerCase() === "generated");
1067
+ }
1068
+ function posixPath(relPath) {
1069
+ return relPath.replace(/\\/g, "/").replace(/^\.\//, "");
1070
+ }
1071
+ function softDefaultForDirectory(dirRel) {
1072
+ const path = posixPath(dirRel).replace(/\/+$/, "");
1073
+ if (!path)
1074
+ return null;
1075
+ const withSlash = `${path}/`;
1076
+ for (const { reason, rule, re } of DIRECTORY_RULES) {
1077
+ if (re.test(withSlash))
1078
+ return { reason, rule };
1079
+ }
1080
+ return null;
1081
+ }
1082
+ function softDefaultForPath(relPath) {
1083
+ const path = posixPath(relPath);
1084
+ if (!path)
1085
+ return null;
1086
+ for (const { reason, rule, re } of DIRECTORY_RULES) {
1087
+ if (re.test(path))
1088
+ return { reason, rule };
1089
+ }
1090
+ const base = posix2.basename(path);
1091
+ if (base.startsWith("._"))
1092
+ return { reason: "binary", rule: "AppleDouble resource fork" };
1093
+ if (!isTestOrSpecName(base)) {
1094
+ if (hasGeneratedStemToken(base))
1095
+ return { reason: "generated", rule: "generated file name" };
1096
+ for (const { rule, re } of GENERATED_BASENAME_RULES) {
1097
+ if (re.test(base))
1098
+ return { reason: "generated", rule };
1099
+ }
1100
+ }
1101
+ if (CHANGELOG_UPPER.test(base) || CHANGELOG_DOC.test(base)) {
1102
+ return { reason: "changelog", rule: "changelog file" };
1103
+ }
1104
+ return null;
1105
+ }
1106
+ function generatedByHeader(content) {
1107
+ const lines = content.split("\n", HEADER_LINES);
1108
+ let generation = false;
1109
+ let doNotEdit = false;
1110
+ for (const raw of lines) {
1111
+ const line = raw.replace(/\r$/, "");
1112
+ if (GO_GENERATED_HEADER.test(line))
1113
+ return "Go generated-code header";
1114
+ if (!COMMENT_LINE.test(line))
1115
+ continue;
1116
+ if (AT_GENERATED.test(line))
1117
+ return "@generated marker";
1118
+ if (GENERATION_PHRASE.test(line))
1119
+ generation = true;
1120
+ if (DO_NOT_EDIT_PHRASE.test(line))
1121
+ doNotEdit = true;
1122
+ }
1123
+ return generation && doNotEdit ? "generated + do-not-edit header" : null;
1124
+ }
1125
+ function utf8Length(line) {
1126
+ if (line.length * 3 <= MINIFIED_LONG_LINE_BYTES)
1127
+ return line.length;
1128
+ return Buffer.byteLength(line, "utf8");
1129
+ }
1130
+ function minified(relPath, content) {
1131
+ if (PROSE_EXTENSION.test(relPath))
1132
+ return null;
1133
+ const lines = content.split("\n");
1134
+ let longLines = 0;
1135
+ let total = 0;
1136
+ for (const line of lines) {
1137
+ total += line.length;
1138
+ if (line.length > MINIFIED_LONG_LINE_BYTES / 3 && utf8Length(line) > MINIFIED_LONG_LINE_BYTES) {
1139
+ longLines++;
1140
+ if (longLines > 1)
1141
+ return "two or more lines over 4,096 bytes";
1142
+ }
1143
+ }
1144
+ if (MINIFIABLE_EXTENSION.test(relPath) && lines.length > 0) {
1145
+ const average = total / lines.length;
1146
+ if (average > MINIFIED_AVERAGE_LINE_LENGTH)
1147
+ return "average line length over 110";
1148
+ }
1149
+ return null;
1150
+ }
1151
+ function dataDump(relPath, content) {
1152
+ if (!/\.sql$/i.test(relPath))
1153
+ return null;
1154
+ if (/(^|\/)migrations\//i.test(posixPath(relPath)))
1155
+ return null;
1156
+ if (COPY_FROM_STDIN.test(content))
1157
+ return "SQL COPY ... FROM stdin";
1158
+ let inserts = 0;
1159
+ INSERT_INTO.lastIndex = 0;
1160
+ while (INSERT_INTO.exec(content) !== null) {
1161
+ if (++inserts >= DATA_DUMP_INSERT_THRESHOLD)
1162
+ return "SQL with 50+ INSERT INTO statements";
1163
+ }
1164
+ return null;
1165
+ }
1166
+ function softDefaultForContent(relPath, content) {
1167
+ if (content.slice(0, BINARY_SNIFF_BYTES).includes("\0")) {
1168
+ return { reason: "binary", rule: "NUL byte in the first 8,000 bytes" };
1169
+ }
1170
+ const header = generatedByHeader(content);
1171
+ if (header)
1172
+ return { reason: "generated", rule: header };
1173
+ const minifiedRule = minified(relPath, content);
1174
+ if (minifiedRule)
1175
+ return { reason: "minified", rule: minifiedRule };
1176
+ const dump = dataDump(relPath, content);
1177
+ if (dump)
1178
+ return { reason: "data_dump", rule: dump };
1179
+ return null;
1180
+ }
1181
+ function escapeRegex(char) {
1182
+ return /[.+^${}()|[\]\\*?/]/.test(char) ? `\\${char}` : char;
1183
+ }
1184
+ function globToRegex(glob) {
1185
+ let re = "";
1186
+ let i = 0;
1187
+ while (i < glob.length) {
1188
+ if (glob.startsWith("**", i)) {
1189
+ const atSegmentStart = i === 0 || glob[i - 1] === "/";
1190
+ const next = glob[i + 2];
1191
+ if (atSegmentStart && next === "/") {
1192
+ re += "(?:.*/)?";
1193
+ i += 3;
1194
+ continue;
1195
+ }
1196
+ if (atSegmentStart && next === void 0) {
1197
+ re += ".*";
1198
+ i += 2;
1199
+ continue;
1200
+ }
1201
+ re += "[^/]*";
1202
+ i += 2;
1203
+ continue;
1204
+ }
1205
+ const char = glob[i];
1206
+ if (char === "*") {
1207
+ re += "[^/]*";
1208
+ } else if (char === "?") {
1209
+ re += "[^/]";
1210
+ } else if (char === "[") {
1211
+ const close = glob.indexOf("]", i + 2);
1212
+ if (close === -1) {
1213
+ re += "\\[";
1214
+ } else {
1215
+ let body = glob.slice(i + 1, close);
1216
+ if (body.startsWith("!"))
1217
+ body = `^${body.slice(1)}`;
1218
+ re += `[${body.replace(/\\/g, "\\\\")}]`;
1219
+ i = close;
1220
+ }
1221
+ } else if (char === "\\" && i + 1 < glob.length) {
1222
+ re += escapeRegex(glob[i + 1]);
1223
+ i++;
1224
+ } else {
1225
+ re += escapeRegex(char);
1226
+ }
1227
+ i++;
1228
+ }
1229
+ return new RegExp(`^${re}$`);
1230
+ }
1231
+ function splitAttributesLine(line) {
1232
+ let pattern;
1233
+ let rest;
1234
+ if (line.startsWith('"')) {
1235
+ let i = 1;
1236
+ let out = "";
1237
+ for (; i < line.length && line[i] !== '"'; i++) {
1238
+ if (line[i] === "\\" && i + 1 < line.length) {
1239
+ i++;
1240
+ out += line[i] === "t" ? " " : line[i] === "n" ? "\n" : line[i];
1241
+ } else {
1242
+ out += line[i];
1243
+ }
1244
+ }
1245
+ if (i >= line.length)
1246
+ return null;
1247
+ pattern = out;
1248
+ rest = line.slice(i + 1);
1249
+ } else {
1250
+ const match = /^(\S+)(.*)$/.exec(line);
1251
+ if (!match)
1252
+ return null;
1253
+ pattern = match[1];
1254
+ rest = match[2];
1255
+ }
1256
+ return { pattern, attrs: rest.split(/\s+/).filter(Boolean) };
1257
+ }
1258
+ function parseGitattributes(text) {
1259
+ const rules = [];
1260
+ for (const rawLine of text.split(/\r?\n/)) {
1261
+ const line = rawLine.trim();
1262
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
1263
+ continue;
1264
+ const split = splitAttributesLine(line);
1265
+ if (!split || split.pattern.startsWith("!"))
1266
+ continue;
1267
+ let { pattern } = split;
1268
+ if (pattern.endsWith("/"))
1269
+ continue;
1270
+ const attrs = /* @__PURE__ */ new Map();
1271
+ for (const token of split.attrs) {
1272
+ if (token.startsWith("-"))
1273
+ attrs.set(token.slice(1), false);
1274
+ else if (token.startsWith("!"))
1275
+ attrs.set(token.slice(1), void 0);
1276
+ else if (token.includes("=")) {
1277
+ const eq = token.indexOf("=");
1278
+ attrs.set(token.slice(0, eq), token.slice(eq + 1));
1279
+ } else
1280
+ attrs.set(token, true);
1281
+ }
1282
+ if (attrs.get("binary") === true)
1283
+ attrs.set("text", false);
1284
+ if (attrs.size === 0)
1285
+ continue;
1286
+ const basenameOnly = !pattern.includes("/");
1287
+ if (pattern.startsWith("/"))
1288
+ pattern = pattern.slice(1);
1289
+ rules.push({ re: globToRegex(pattern), basenameOnly, attrs });
1290
+ }
1291
+ return rules;
1292
+ }
1293
+ function booleanAttribute(value) {
1294
+ if (value === true || value === "true")
1295
+ return true;
1296
+ if (value === false || value === "false")
1297
+ return false;
1298
+ return void 0;
1299
+ }
1300
+ function linguistAttributesFor(relPath, layers) {
1301
+ const path = posixPath(relPath);
1302
+ const state = /* @__PURE__ */ new Map();
1303
+ const ordered = [...layers].sort((a, b) => depthOf(a.baseRel) - depthOf(b.baseRel));
1304
+ for (const layer of ordered) {
1305
+ let sub;
1306
+ if (layer.baseRel === "")
1307
+ sub = path;
1308
+ else if (path.startsWith(`${layer.baseRel}/`))
1309
+ sub = path.slice(layer.baseRel.length + 1);
1310
+ else
1311
+ continue;
1312
+ const base = posix2.basename(sub);
1313
+ for (const rule of layer.rules) {
1314
+ if (!(rule.basenameOnly ? rule.re.test(base) : rule.re.test(sub)))
1315
+ continue;
1316
+ for (const [name, value] of rule.attrs)
1317
+ state.set(name, value);
1318
+ }
1319
+ }
1320
+ return {
1321
+ vendored: booleanAttribute(state.get("linguist-vendored")),
1322
+ generated: booleanAttribute(state.get("linguist-generated")),
1323
+ binary: state.get("text") === false
1324
+ };
1325
+ }
1326
+ function depthOf(baseRel) {
1327
+ return baseRel === "" ? 0 : baseRel.split("/").length;
1328
+ }
1329
+ function gitattributesCanReinclude(layers) {
1330
+ return layers.some((layer) => layer.rules.some((rule) => ["linguist-vendored", "linguist-generated"].some((name) => booleanAttribute(rule.attrs.get(name)) === false)));
1331
+ }
1332
+ function classifyForIndex(relPath, options = {}) {
1333
+ if (options.ignoreFileVerdict === "unignored")
1334
+ return { include: true, override: "ignore_file" };
1335
+ if (options.attributes && options.attributes.length > 0) {
1336
+ const attrs = linguistAttributesFor(relPath, options.attributes);
1337
+ if (attrs.vendored === true) {
1338
+ return { include: false, reason: "vendored", rule: ".gitattributes linguist-vendored" };
1339
+ }
1340
+ if (attrs.generated === true) {
1341
+ return { include: false, reason: "generated", rule: ".gitattributes linguist-generated" };
1342
+ }
1343
+ if (attrs.binary)
1344
+ return { include: false, reason: "binary", rule: ".gitattributes binary" };
1345
+ if (attrs.vendored === false || attrs.generated === false) {
1346
+ return { include: true, override: "gitattributes" };
1347
+ }
1348
+ }
1349
+ const byPath = softDefaultForPath(relPath);
1350
+ if (byPath)
1351
+ return { include: false, ...byPath };
1352
+ if (options.content !== void 0) {
1353
+ const byContent = softDefaultForContent(relPath, options.content);
1354
+ if (byContent)
1355
+ return { include: false, ...byContent };
1356
+ }
1357
+ return { include: true };
1358
+ }
1359
+ var DIRECTORY_RULES, GENERATED_BASENAME_RULES, CHANGELOG_UPPER, CHANGELOG_DOC, BINARY_SNIFF_BYTES, HEADER_LINES, MINIFIED_AVERAGE_LINE_LENGTH, MINIFIED_LONG_LINE_BYTES, DATA_DUMP_INSERT_THRESHOLD, MINIFIABLE_EXTENSION, PROSE_EXTENSION, COMMENT_LINE, GO_GENERATED_HEADER, AT_GENERATED, GENERATION_PHRASE, DO_NOT_EDIT_PHRASE, INSERT_INTO, COPY_FROM_STDIN;
1360
+ var init_indexClassifier = __esm({
1361
+ "packages/shared/dist/indexClassifier.js"() {
1362
+ "use strict";
1363
+ DIRECTORY_RULES = [
1364
+ // R2 - vendored dependencies and archive debris.
1365
+ {
1366
+ reason: "vendored",
1367
+ rule: "vendored directory",
1368
+ re: /(^|\/)(vendors?|Vendor|third[-_]?party|3rd[-_]?party|Godeps|Carthage|jspm_packages|site-packages|dist-packages|__MACOSX)\//
1369
+ },
1370
+ {
1371
+ reason: "vendored",
1372
+ rule: "Yarn release/plugin/cache directory",
1373
+ re: /(^|\/)\.yarn\/(releases|plugins|sdks|versions|unplugged|cache)\//
1374
+ },
1375
+ { reason: "vendored", rule: "documentation build output", re: /(^|\/)docs?\/_?build\// },
1376
+ // Test fixtures are copies of other code: byte-identical snapshot trees
1377
+ // stole the content-hash dedup slot from the real file and outranked it in
1378
+ // code_search. This folds in the old built-in `/tests/fixtures/*` pattern.
1379
+ { reason: "vendored", rule: "test fixtures", re: /(^|\/)([Tt]ests?|[Ss]pecs?)\/fixtures\// },
1380
+ { reason: "vendored", rule: "test fixtures", re: /(^|\/)(testdata|__fixtures__)\// },
1381
+ { reason: "vendored", rule: "test snapshots", re: /(^|\/)__snapshots__\// },
1382
+ // R3 - generated output directories.
1383
+ {
1384
+ reason: "generated",
1385
+ rule: "generated directory",
1386
+ re: /(^|\/)(__generated__|_generated|generated|autogen)\//
1387
+ }
1388
+ ];
1389
+ GENERATED_BASENAME_RULES = [
1390
+ // Infixes: foo.gen.ts, foo.g.dart, foo.pb.go, foo.pb.gw.go, foo.freezed.dart,
1391
+ // Form1.Designer.cs. Case-insensitive: Visual Studio writes `.Designer.cs`.
1392
+ { rule: "generated infix", re: /\.(gen|g|pb|pb\.gw|freezed|designer)\.[^.]+$/i },
1393
+ // Protocol Buffers / gRPC outputs.
1394
+ { rule: "protobuf/gRPC output", re: /_pb2(_grpc)?\.pyi?$/ },
1395
+ { rule: "protobuf/gRPC output", re: /_pb\.(js|ts)$/ },
1396
+ { rule: "protobuf/gRPC output", re: /_grpc_pb\..+$/ },
1397
+ { rule: "protobuf/gRPC output", re: /_grpc\.pb\.go$/ },
1398
+ { rule: "protobuf/gRPC output", re: /\.pb\.(go|cc|h|swift|dart|ts|js)$/ },
1399
+ // SpecFlow, Yarn Plug'n'Play, Delphi type libraries.
1400
+ { rule: "SpecFlow feature binding", re: /\.feature\.cs$/ },
1401
+ { rule: "Yarn Plug'n'Play loader", re: /^\.pnp\./ },
1402
+ { rule: "Delphi type library", re: /_tlb\.pas$/ }
1403
+ ];
1404
+ CHANGELOG_UPPER = /^(CHANGELOG|CHANGES|HISTORY|NEWS)(\.[^/]*)?$/;
1405
+ CHANGELOG_DOC = /^(changelog|changes|history|news)(\.(md|markdown|rst|txt|adoc))?$/i;
1406
+ BINARY_SNIFF_BYTES = 8e3;
1407
+ HEADER_LINES = 10;
1408
+ MINIFIED_AVERAGE_LINE_LENGTH = 110;
1409
+ MINIFIED_LONG_LINE_BYTES = 4096;
1410
+ DATA_DUMP_INSERT_THRESHOLD = 50;
1411
+ MINIFIABLE_EXTENSION = /\.(js|mjs|cjs|css)$/i;
1412
+ PROSE_EXTENSION = /\.(md|mdc|mdx|markdown)$/i;
1413
+ COMMENT_LINE = /^\s*(\/\/|\/\*|\*|#|--|;|<!--|%|\(\*|\{-|"""|''')/;
1414
+ GO_GENERATED_HEADER = /^\/\/ Code generated .* DO NOT EDIT\.$/;
1415
+ AT_GENERATED = /@generated\b/;
1416
+ GENERATION_PHRASE = /\b(generated by|auto-generated|autogenerated|this file was generated|code generated)\b/i;
1417
+ DO_NOT_EDIT_PHRASE = /\b(do not edit|do not modify|manual changes will be overwritten)\b/i;
1418
+ INSERT_INTO = /\binsert\s+into\b/gi;
1419
+ COPY_FROM_STDIN = /^\s*COPY\s+[^\n]*\bFROM\s+stdin\b/im;
1420
+ }
1421
+ });
1422
+
1423
+ // packages/shared/dist/fileSelection.js
1424
+ var fileSelection_exports = {};
1425
+ __export(fileSelection_exports, {
1426
+ AUTHORITY_FILE_MATCHERS: () => AUTHORITY_FILE_MATCHERS,
1427
+ BUILT_IN_IGNORE_DIRS: () => BUILT_IN_IGNORE_DIRS,
1428
+ BUILT_IN_IGNORE_FILE_PATTERNS: () => BUILT_IN_IGNORE_FILE_PATTERNS,
1429
+ CMAKE_LISTS_PATH: () => CMAKE_LISTS_PATH,
1430
+ DEFAULT_IGNORE_PATTERNS: () => DEFAULT_IGNORE_PATTERNS,
1431
+ DEFAULT_INCLUDE_EXTENSIONS: () => DEFAULT_INCLUDE_EXTENSIONS,
1432
+ FIXTURE_PATH_RE: () => FIXTURE_PATH_RE,
1433
+ FileSelector: () => FileSelector,
1434
+ XCODE_AUTHORITY_PATH: () => XCODE_AUTHORITY_PATH,
1435
+ enterDirectory: () => enterDirectory,
1436
+ fileOp: () => fileOp,
1437
+ isAuthorityOnlyPath: () => isAuthorityOnlyPath,
1438
+ isCmakeListsPath: () => isCmakeListsPath,
1439
+ isCursorRulePath: () => isCursorRulePath,
1440
+ isFixturePath: () => isFixturePath,
1441
+ isGitBoundary: () => isGitBoundary,
1442
+ isLinkedWorktreeCheckout: () => isLinkedWorktreeCheckout,
1443
+ isProjectBoundary: () => isProjectBoundary,
1444
+ isSecretFile: () => isSecretFile,
1445
+ isXcodeAuthorityPath: () => isXcodeAuthorityPath,
1446
+ languageForExtension: () => languageForExtension,
1447
+ makeIgnoreMatcher: () => makeIgnoreMatcher,
1448
+ markUnreadable: () => markUnreadable,
1449
+ rootProjectId: () => rootProjectId,
1450
+ statTuple: () => statTuple
1451
+ });
1452
+ import { readdir, readFile as readFile2, open, lstat as lstat2, realpath } from "fs/promises";
1453
+ import { constants } from "fs";
1454
+ import { join as join2, relative, extname, sep, resolve, dirname } from "path";
1455
+ function timedOp(op, timeoutMs, label) {
1456
+ let timer;
1457
+ return Promise.race([
1458
+ op,
1459
+ new Promise((_resolve, reject) => {
1460
+ timer = setTimeout(() => reject(new FileOpTimeoutError(label, timeoutMs)), timeoutMs);
1461
+ })
1462
+ ]).finally(() => clearTimeout(timer));
1463
+ }
1464
+ async function fileOp(op, label, budgets = {
1465
+ first: FILE_OP_TIMEOUT_MS,
1466
+ retry: FILE_OP_RETRY_TIMEOUT_MS
1467
+ }) {
1468
+ try {
1469
+ return await timedOp(op(), budgets.first, label);
1470
+ } catch (error) {
1471
+ if (!(error instanceof FileOpTimeoutError))
1472
+ throw error;
1473
+ return timedOp(op(), budgets.retry, label);
1474
+ }
1475
+ }
1476
+ function statTuple(stats) {
1477
+ return { size: stats.size, mtimeMs: stats.mtimeMs, ctimeMs: stats.ctimeMs, ino: stats.ino };
1478
+ }
1479
+ function markUnreadable(walk, path, error) {
1480
+ walk.complete = false;
1481
+ walk.unreadable = (walk.unreadable ?? 0) + 1;
1482
+ walk.unreadableSamples ??= [];
1483
+ if (walk.unreadableSamples.length < UNREADABLE_SAMPLES)
1484
+ walk.unreadableSamples.push({
1485
+ path,
1486
+ reason: error?.code ?? (error instanceof Error ? error.message : "unreadable")
1487
+ });
1488
+ }
1489
+ async function directoryKey(path) {
1490
+ const stats = await lstat2(path, { bigint: true });
1491
+ return stats.ino ? `${stats.dev}:${stats.ino}` : null;
1492
+ }
1493
+ async function enterDirectory(walk, path) {
1494
+ let key;
1495
+ try {
1496
+ key = await directoryKey(path);
1497
+ } catch {
1498
+ key = null;
1499
+ }
1500
+ if (key === null)
1501
+ return () => void 0;
1502
+ walk.ancestors ??= /* @__PURE__ */ new Set();
1503
+ if (walk.ancestors.has(key)) {
1504
+ markUnreadable(walk, path, Object.assign(new Error("directory cycle"), { code: "ELOOP" }));
1505
+ return null;
1506
+ }
1507
+ const entered = key;
1508
+ walk.ancestors.add(entered);
1509
+ return () => {
1510
+ walk.ancestors?.delete(entered);
1511
+ };
1512
+ }
1513
+ function isSecretFile(relPath) {
1514
+ if (!relPath)
1515
+ return false;
1516
+ const segments = relPath.split(sep).join("/").split("/");
1517
+ const base = segments[segments.length - 1] ?? "";
1518
+ if (!base || SECRET_FILE_PLACEHOLDER_BASENAMES.has(base))
1519
+ return false;
1520
+ if (SECRET_FILE_BASENAME_PATTERNS.some((re) => re.test(base)))
1521
+ return true;
1522
+ const parent = segments[segments.length - 2];
1523
+ return base === "credentials" && parent !== void 0 && parent.startsWith(".");
1524
+ }
1525
+ function isCursorRulePath(relPath) {
1526
+ if (!relPath)
1527
+ return false;
1528
+ const posix3 = relPath.split(sep).join("/");
1529
+ return /(^|\/)\.cursorrules$/.test(posix3) || /(^|\/)\.cursor\/rules\/.+\.mdc$/.test(posix3);
1530
+ }
1531
+ function languageForExtension(filePathOrExt) {
1532
+ if (isCmakeListsPath(filePathOrExt))
1533
+ return "cmake";
1534
+ const fromPath = EXTENSION_LANGUAGES[extname(filePathOrExt).toLowerCase()];
1535
+ if (fromPath !== void 0)
1536
+ return fromPath;
1537
+ return EXTENSION_LANGUAGES[filePathOrExt.toLowerCase()] ?? "unknown";
1538
+ }
1539
+ function isAuthorityOnlyPath(relPath) {
1540
+ if (!relPath)
1541
+ return false;
1542
+ const posix3 = relPath.split(sep).join("/");
1543
+ return AUTHORITY_ONLY_PATH_PREDICATES.some((matches) => matches(posix3));
1544
+ }
1545
+ function isFixturePath(path) {
1546
+ return FIXTURE_PATH_RE.test(path);
1547
+ }
1548
+ function makeIgnoreMatcher(extraPatterns = []) {
1549
+ const cleaned = extraPatterns.filter((p) => typeof p === "string" && p.trim().length > 0);
1550
+ const ig = cleaned.length > 0 ? (0, import_ignore.default)().add([...cleaned]) : null;
1551
+ return (relPath) => {
1552
+ if (!relPath)
1553
+ return false;
1554
+ const norm = relPath.split(sep).join("/");
1555
+ if (norm.split("/").some((s) => BUILT_IN_IGNORE_DIR_SET.has(s)))
1556
+ return true;
1557
+ if (isSecretFile(norm))
1558
+ return true;
1559
+ if (ig) {
1560
+ try {
1561
+ return ig.ignores(norm);
1562
+ } catch {
1563
+ return false;
1564
+ }
1565
+ }
1566
+ return false;
1567
+ };
1568
+ }
1569
+ async function isGitBoundary(dirPath) {
1570
+ try {
1571
+ await fileOp(() => lstat2(join2(dirPath, ".git")), `lstat: ${join2(dirPath, ".git")}`);
1572
+ return true;
1573
+ } catch {
1574
+ return false;
1575
+ }
1576
+ }
1577
+ async function isProjectBoundary(dirPath, enclosingProjectId) {
1578
+ if (await lstatWithin(join2(dirPath, ".mnemonik.json"))) {
1579
+ const identity = await fileOp(() => readIdentityFile(dirPath, { configurationOnlyAbsent: true }), `identity read: ${dirPath}`).catch(() => void 0);
1580
+ if (!identity)
1581
+ return true;
1582
+ if (identity.kind === "ok") {
1583
+ if (identity.identity.projectId !== enclosingProjectId)
1584
+ return true;
1585
+ if ((await lstatWithin(join2(dirPath, ".git")))?.isDirectory())
1586
+ return true;
1587
+ } else if (identity.kind !== "absent")
1588
+ return true;
1589
+ }
1590
+ return isLinkedWorktreeCheckout(dirPath);
1591
+ }
1592
+ async function rootProjectId(rootPath) {
1593
+ const identity = await fileOp(() => readIdentityFile(rootPath), `identity read: ${rootPath}`).catch(() => void 0);
1594
+ return identity?.kind === "ok" ? identity.identity.projectId : void 0;
1595
+ }
1596
+ async function isLinkedWorktreeCheckout(dirPath) {
1597
+ const marker = await lstatWithin(join2(dirPath, ".git"));
1598
+ if (!marker?.isFile())
1599
+ return false;
1600
+ try {
1601
+ const pointer = /^gitdir:\s*(.+?)\s*$/im.exec(await fileOp(() => readFile2(join2(dirPath, ".git"), "utf8"), `read: ${join2(dirPath, ".git")}`))?.[1];
1602
+ if (!pointer)
1603
+ return false;
1604
+ const gitDirectory = resolve(dirPath, pointer);
1605
+ if (!await lstatWithin(gitDirectory))
1606
+ return true;
1607
+ return !!await lstatWithin(join2(gitDirectory, "commondir"));
1608
+ } catch {
1609
+ return false;
1610
+ }
1611
+ }
1612
+ var import_ignore, FILE_OP_TIMEOUT_MS, FILE_OP_RETRY_TIMEOUT_MS, FileOpTimeoutError, UNREADABLE_SAMPLES, BUILT_IN_IGNORE_DIRS, BUILT_IN_IGNORE_FILE_PATTERNS, SECRET_FILE_PLACEHOLDER_BASENAMES, SECRET_FILE_BASENAME_PATTERNS, IGNORE_FILE_NAMES, ATTRIBUTES_FILE_NAME, DEFAULT_INCLUDE_EXTENSIONS, EXTENSION_LANGUAGES, isMigrationSqlAuthorityPath, XCODE_AUTHORITY_PATH, isXcodeAuthorityPath, CMAKE_LISTS_PATH, isCmakeListsPath, AUTHORITY_ONLY_PATH_PREDICATES, AUTHORITY_FILE_MATCHERS, FIXTURE_PATH_RE, BUILT_IN_IGNORE_DIR_SET, lstatWithin, DEFAULT_IGNORE_PATTERNS, FileSelector;
1613
+ var init_fileSelection = __esm({
1614
+ "packages/shared/dist/fileSelection.js"() {
1615
+ "use strict";
1616
+ import_ignore = __toESM(require_ignore(), 1);
1617
+ init_logger();
1618
+ init_protectedPaths();
1619
+ init_projectIdentityFile();
1620
+ init_indexClassifier();
1621
+ FILE_OP_TIMEOUT_MS = 5e3;
1622
+ FILE_OP_RETRY_TIMEOUT_MS = 3e4;
1623
+ FileOpTimeoutError = class extends Error {
1624
+ constructor(label, timeoutMs) {
1625
+ super(`${label} timed out after ${timeoutMs} ms`);
1626
+ this.name = "FileOpTimeoutError";
1627
+ }
1628
+ };
1629
+ UNREADABLE_SAMPLES = 5;
1630
+ BUILT_IN_IGNORE_DIRS = [
1631
+ // JS / web
1632
+ "node_modules",
1633
+ ".next",
1634
+ ".nuxt",
1635
+ ".output",
1636
+ "dist",
1637
+ "build",
1638
+ "coverage",
1639
+ ".turbo",
1640
+ ".vercel",
1641
+ ".svelte-kit",
1642
+ ".angular",
1643
+ ".astro",
1644
+ ".parcel-cache",
1645
+ "bower_components",
1646
+ // Python
1647
+ "venv",
1648
+ ".venv",
1649
+ "env",
1650
+ "__pycache__",
1651
+ ".tox",
1652
+ ".mypy_cache",
1653
+ ".pytest_cache",
1654
+ ".ruff_cache",
1655
+ // Native mobile - iOS / Android / React Native / Flutter (2026-07-11 Pods incident)
1656
+ "Pods",
1657
+ "DerivedData",
1658
+ ".gradle",
1659
+ ".expo",
1660
+ ".dart_tool",
1661
+ // Infra / cloud
1662
+ ".terraform",
1663
+ "cdk.out",
1664
+ ".serverless",
1665
+ // Build systems, caches, VCS
1666
+ "target",
1667
+ "bin",
1668
+ "obj",
1669
+ ".cache",
1670
+ ".git",
1671
+ ".svn",
1672
+ ".hg",
1673
+ // The coding agent's captured shell environment, written as .sh files.
1674
+ // Harmless while only source extensions were collected; the moment shell
1675
+ // scripts became collectable these scanned as project code, which would put
1676
+ // an agent's own shell state into the user's searchable memory.
1677
+ //
1678
+ // Scoped to this one directory name deliberately. `.claude/` at large holds
1679
+ // real project content - `.claude/rules.md` is indexed on purpose
1680
+ // (tests/CodeScannerIgnorePatterns.test.ts), and only `.claude/worktrees/`
1681
+ // is suppressed, by the generic nested-git-boundary rule.
1682
+ "shell-snapshots"
1683
+ ];
1684
+ BUILT_IN_IGNORE_FILE_PATTERNS = [
1685
+ // NOTE: the `.env` family and every other secret-bearing FILE KIND lives in
1686
+ // `isSecretFile` below, not here. This glob engine cannot express negation
1687
+ // (`.env.example` must stay collectable) and its unanchored substring
1688
+ // matching would make `.env.*` also swallow `.environment.ts`.
1689
+ //
1690
+ // `/tests/fixtures/*` used to live here. It is now a soft default in
1691
+ // indexClassifier.ts (test fixtures, R2), which a project can re-include.
1692
+ ".DS_Store",
1693
+ "*.log",
1694
+ "*.lock",
1695
+ "package-lock.json",
1696
+ "yarn.lock",
1697
+ "*.min.js",
1698
+ "*.min.css",
1699
+ "*.bundle.js",
1700
+ "*.legacy.js",
1701
+ "*.map"
1702
+ ];
1703
+ SECRET_FILE_PLACEHOLDER_BASENAMES = /* @__PURE__ */ new Set([".env.example"]);
1704
+ SECRET_FILE_BASENAME_PATTERNS = [
1705
+ /^\.env(\..+)?$/i,
1706
+ // .env, .env.local, .env.production.local
1707
+ /^id_(?:rsa|dsa|ecdsa|ed25519)/i,
1708
+ // ssh private keys (and .pub siblings)
1709
+ /^\.(?:npmrc|netrc|pgpass)$/i,
1710
+ // registry / ftp / postgres password files
1711
+ /^kubeconfig$/i,
1712
+ /\.(?:pem|key|p12|pfx|keystore|jks|kubeconfig)$/i,
1713
+ // Terraform variable files: `terraform.tfvars` is the conventional home for
1714
+ // provider credentials, and unlike the entries above nothing else was
1715
+ // keeping it out - it is excluded here rather than merely left off
1716
+ // `DEFAULT_INCLUDE_EXTENSIONS`, so a future widening of the allowlist cannot
1717
+ // quietly opt a project into embedding its own cloud keys.
1718
+ /\.tfvars(\.json)?$/i
1719
+ ];
1720
+ IGNORE_FILE_NAMES = [".gitignore", ".mnemonikignore"];
1721
+ ATTRIBUTES_FILE_NAME = ".gitattributes";
1722
+ DEFAULT_INCLUDE_EXTENSIONS = [
1723
+ ".ts",
1724
+ ".tsx",
1725
+ ".js",
1726
+ ".jsx",
1727
+ ".py",
1728
+ ".rs",
1729
+ ".go",
1730
+ ".java",
1731
+ ".c",
1732
+ ".cpp",
1733
+ ".h",
1734
+ ".cs",
1735
+ ".rb",
1736
+ ".php",
1737
+ ".swift",
1738
+ ".kt",
1739
+ // Shell and Lua are the working languages of whole real projects -
1740
+ // deployment tooling, container entrypoints, imapfilter/nginx/redis
1741
+ // configuration. Omitting them meant such a project indexed ZERO code and
1742
+ // code_search could not answer anything about it, while reporting that as
1743
+ // "no indexed matches" rather than as missing coverage.
1744
+ ".sh",
1745
+ ".bash",
1746
+ ".zsh",
1747
+ ".lua",
1748
+ ".md",
1749
+ // -- Variants of languages already on the list. `.mjs`/`.cjs`/`.mts`/`.cts`/
1750
+ // `.pyi` inherit their language string from a listed extension, so their
1751
+ // absence was oversight rather than policy. The C++ spellings are a choice,
1752
+ // not an inheritance: `.h` stays 'c' (C headers dominate, and reading a C++
1753
+ // header as C is the safer default), while the unambiguously-C++ spellings
1754
+ // resolve to 'cpp'.
1755
+ ".mjs",
1756
+ ".cjs",
1757
+ ".mts",
1758
+ ".cts",
1759
+ ".pyi",
1760
+ ".hpp",
1761
+ ".hh",
1762
+ ".hxx",
1763
+ ".cc",
1764
+ ".cxx",
1765
+ ".kts",
1766
+ // -- Languages the scanner could not read at all.
1767
+ ".dart",
1768
+ ".m",
1769
+ ".mm",
1770
+ ".scala",
1771
+ ".sc",
1772
+ ".ex",
1773
+ ".exs",
1774
+ ".erl",
1775
+ ".hrl",
1776
+ ".hs",
1777
+ ".jl",
1778
+ ".ml",
1779
+ ".mli",
1780
+ ".clj",
1781
+ ".cljs",
1782
+ ".cljc",
1783
+ ".groovy",
1784
+ ".gradle",
1785
+ ".ps1",
1786
+ ".psm1",
1787
+ ".pl",
1788
+ ".pm",
1789
+ ".r",
1790
+ ".sol",
1791
+ ".zig",
1792
+ ".vue",
1793
+ ".svelte",
1794
+ // -- Infrastructure and schema DSLs, where real logic lives and where "how
1795
+ // is this deployed?" and "what does this table hold?" go unanswered today.
1796
+ ".tf",
1797
+ ".sql",
1798
+ ".proto",
1799
+ ".graphql",
1800
+ ".gql",
1801
+ ".cmake",
1802
+ ".nix",
1803
+ ".bzl"
1804
+ ];
1805
+ EXTENSION_LANGUAGES = {
1806
+ ".ts": "typescript",
1807
+ ".tsx": "typescript",
1808
+ ".mts": "typescript",
1809
+ ".cts": "typescript",
1810
+ ".js": "javascript",
1811
+ ".jsx": "javascript",
1812
+ ".mjs": "javascript",
1813
+ ".cjs": "javascript",
1814
+ ".py": "python",
1815
+ ".pyi": "python",
1816
+ ".rs": "rust",
1817
+ ".go": "go",
1818
+ ".java": "java",
1819
+ ".c": "c",
1820
+ ".h": "c",
1821
+ ".cpp": "cpp",
1822
+ ".cc": "cpp",
1823
+ ".cxx": "cpp",
1824
+ ".hpp": "cpp",
1825
+ ".hh": "cpp",
1826
+ ".hxx": "cpp",
1827
+ ".cs": "csharp",
1828
+ ".rb": "ruby",
1829
+ ".php": "php",
1830
+ ".swift": "swift",
1831
+ ".kt": "kotlin",
1832
+ ".kts": "kotlin",
1833
+ ".sh": "shell",
1834
+ ".bash": "shell",
1835
+ ".zsh": "shell",
1836
+ ".lua": "lua",
1837
+ ".md": "markdown",
1838
+ ".dart": "dart",
1839
+ ".m": "objc",
1840
+ ".mm": "objc",
1841
+ ".scala": "scala",
1842
+ ".sc": "scala",
1843
+ ".ex": "elixir",
1844
+ ".exs": "elixir",
1845
+ ".erl": "erlang",
1846
+ ".hrl": "erlang",
1847
+ ".hs": "haskell",
1848
+ ".jl": "julia",
1849
+ ".ml": "ocaml",
1850
+ ".mli": "ocaml",
1851
+ ".clj": "clojure",
1852
+ ".cljs": "clojure",
1853
+ ".cljc": "clojure",
1854
+ ".groovy": "groovy",
1855
+ ".gradle": "groovy",
1856
+ ".ps1": "powershell",
1857
+ ".psm1": "powershell",
1858
+ ".pl": "perl",
1859
+ ".pm": "perl",
1860
+ ".r": "r",
1861
+ ".sol": "solidity",
1862
+ ".zig": "zig",
1863
+ ".vue": "vue",
1864
+ ".svelte": "svelte",
1865
+ ".tf": "terraform",
1866
+ ".sql": "sql",
1867
+ ".proto": "protobuf",
1868
+ ".graphql": "graphql",
1869
+ ".gql": "graphql",
1870
+ ".cmake": "cmake",
1871
+ ".nix": "nix",
1872
+ ".bzl": "starlark"
1873
+ };
1874
+ isMigrationSqlAuthorityPath = (posixRelPath) => /^migrations\/.*\.sql$/.test(posixRelPath);
1875
+ XCODE_AUTHORITY_PATH = /(?:^|\/)[^/]+\.xcodeproj\/project\.pbxproj$|\.xcconfig$/;
1876
+ isXcodeAuthorityPath = (path) => XCODE_AUTHORITY_PATH.test(path);
1877
+ CMAKE_LISTS_PATH = /(?:^|\/)CMakeLists\.txt$/;
1878
+ isCmakeListsPath = (path) => CMAKE_LISTS_PATH.test(path);
1879
+ AUTHORITY_ONLY_PATH_PREDICATES = [
1880
+ isMigrationSqlAuthorityPath,
1881
+ isXcodeAuthorityPath,
1882
+ isCmakeListsPath
1883
+ ];
1884
+ AUTHORITY_FILE_MATCHERS = [
1885
+ (p) => p === "package.json",
1886
+ (p) => /^(packages|apps|services|tools)\/[^/]+\/package\.json$/.test(p),
1887
+ (p) => p === "tsconfig.json" || /^tsconfig\.[^/]+\.json$/.test(p),
1888
+ (p) => p === "pyproject.toml",
1889
+ (p) => p === "requirements.txt",
1890
+ (p) => p === "setup.py",
1891
+ (p) => p === "Cargo.toml",
1892
+ (p) => p === "Gemfile",
1893
+ (p) => p === "Makefile",
1894
+ (p) => p === ".env.example",
1895
+ (p) => /^\.github\/workflows\/[^/]+\.(yml|yaml)$/.test(p),
1896
+ // SQL migrations: the schema_columns authority extractor reads every .sql
1897
+ // under `migrations/` (listFiles('migrations/') -> LIKE 'migrations/%' then
1898
+ // .endsWith('.sql')). Without collecting these, that authority is empty and
1899
+ // every schema_table_enumeration claim falls to unverifiable. Shared with
1900
+ // `AUTHORITY_ONLY_PATH_PREDICATES` by reference, not by a copied regex, so
1901
+ // collection and the chunking exclusion cannot disagree.
1902
+ isMigrationSqlAuthorityPath,
1903
+ isXcodeAuthorityPath,
1904
+ isCmakeListsPath
1905
+ ];
1906
+ FIXTURE_PATH_RE = /(^|\/)tests\/fixtures\//;
1907
+ BUILT_IN_IGNORE_DIR_SET = new Set(BUILT_IN_IGNORE_DIRS);
1908
+ lstatWithin = (path) => fileOp(() => lstat2(path), `lstat: ${path}`).catch(() => void 0);
1909
+ DEFAULT_IGNORE_PATTERNS = [
1910
+ ...BUILT_IN_IGNORE_DIRS,
1911
+ ...BUILT_IN_IGNORE_FILE_PATTERNS
1912
+ ];
1913
+ FileSelector = class _FileSelector {
1914
+ selection;
1915
+ protectedPaths;
1916
+ /** Cumulative observations since this scanner started, including repeated walks. */
1917
+ skipReasons = { symlink_skipped: 0 };
1918
+ skipSymlink(stats) {
1919
+ if (!stats.isSymbolicLink())
1920
+ return false;
1921
+ this.skipReasons.symlink_skipped++;
1922
+ return true;
1923
+ }
1924
+ /**
1925
+ * `includeExtensions` folded to lowercase for matching. The gate used to
1926
+ * compare `extname()` verbatim while `detectLanguage` lowercased, so a
1927
+ * project spelling its files the canonical way - `.R` for R, `.SQL`/`.PS1`
1928
+ * on Windows, `.C`/`.H` in older C trees - indexed zero of them and nothing
1929
+ * said why.
1930
+ */
1931
+ includeExtensionSet;
1932
+ constructor(options = {}) {
1933
+ this.selection = {
1934
+ ignorePatterns: [...DEFAULT_IGNORE_PATTERNS],
1935
+ includeExtensions: [...DEFAULT_INCLUDE_EXTENSIONS],
1936
+ protectedPaths: [],
1937
+ ...options
1938
+ };
1939
+ this.protectedPaths = [
1940
+ .../* @__PURE__ */ new Set([...protectedLocalPaths(), ...options.protectedPaths ?? []])
1941
+ ];
1942
+ this.includeExtensionSet = new Set(this.selection.includeExtensions.map((ext) => ext.toLowerCase()));
1943
+ }
1944
+ isProtected(path) {
1945
+ return isProtectedLocalPath(path, this.protectedPaths);
1946
+ }
1947
+ async isProtectedRead(path) {
1948
+ if (this.isProtected(path))
1949
+ return true;
1950
+ return isProtectedLocalPath(await realpath(path), this.protectedPaths);
1951
+ }
1952
+ /**
1953
+ * The chunkable-file gate, shared by every walker and by the explicit
1954
+ * file-list path so all three agree on what exists. `relPath` is the path
1955
+ * relative to the scan root (OS-native separators accepted).
1956
+ */
1957
+ isChunkable(absOrRelPath, relPath) {
1958
+ if (!this.includeExtensionSet.has(extname(absOrRelPath).toLowerCase()) && !isCursorRulePath(relPath)) {
1959
+ return false;
1960
+ }
1961
+ if (isAuthorityOnlyPath(relPath)) {
1962
+ debug("Skipping chunking for authority-collected path", { relPath });
1963
+ return false;
1964
+ }
1965
+ return true;
1966
+ }
1967
+ /**
1968
+ * Read `.gitignore` + `.mnemonikignore` + `.gitattributes` in `absDir` and
1969
+ * compile them into one layer (the two ignore files are unioned -
1970
+ * `.mnemonikignore` is just an additional gitignore-syntax exclusion list).
1971
+ * Returns null when none of the three exists. FAIL-CLOSED: a non-ENOENT read
1972
+ * error (EISDIR, EACCES, timeout) latches `walk.complete = false` so callers
1973
+ * deriving *removals* from the walk treat it as untrustworthy rather than
1974
+ * silently ignoring nothing.
1975
+ */
1976
+ async loadIgnoreLayer(absDir, baseRel, walk, rootPath) {
1977
+ if (this.isProtected(absDir))
1978
+ return null;
1979
+ const read = async (name) => {
1980
+ const filePath = join2(absDir, name);
1981
+ try {
1982
+ const content = await fileOp(() => this.readConfined(filePath, rootPath), `readFile: ${filePath}`);
1983
+ return content.split(/\r?\n/);
1984
+ } catch (err) {
1985
+ if (err.message === "symlink_skipped")
1986
+ return null;
1987
+ if (err?.code !== "ENOENT") {
1988
+ markUnreadable(walk, filePath, err);
1989
+ debug("Rule-file read failed (fail-closed)", { path: filePath, err });
1990
+ }
1991
+ return null;
1992
+ }
1993
+ };
1994
+ const patterns = [];
1995
+ for (const name of IGNORE_FILE_NAMES) {
1996
+ const lines = await read(name);
1997
+ if (lines)
1998
+ patterns.push(...lines);
1999
+ }
2000
+ const attributeLines = await read(ATTRIBUTES_FILE_NAME) ?? [];
2001
+ if (patterns.length > 0 || attributeLines.length > 0) {
2002
+ walk.rules?.set(baseRel, { ignore: patterns, attributes: attributeLines });
2003
+ }
2004
+ const attributes = attributeLines.length > 0 ? parseGitattributes(attributeLines.join("\n")) : [];
2005
+ if (patterns.length === 0 && attributes.length === 0)
2006
+ return null;
2007
+ return {
2008
+ baseRel,
2009
+ ig: patterns.length > 0 ? (0, import_ignore.default)().add(patterns) : null,
2010
+ hasNegation: patterns.some((line) => line.trimStart().startsWith("!")),
2011
+ attributes: attributes.length > 0 ? attributes : null
2012
+ };
2013
+ }
2014
+ /**
2015
+ * What the .gitignore / .mnemonikignore layers in `stack` say about
2016
+ * `entryRel` (relative to the scan root). Tested deepest-first: the first
2017
+ * layer that explicitly ignores OR re-includes (via a `!negation`) wins,
2018
+ * matching git's rule that a deeper `.gitignore` overrides a shallower one.
2019
+ * Directories are tested with a trailing slash so `foo/` dir-only rules
2020
+ * match the directory itself. `'unignored'` means a negation matched this
2021
+ * path itself; a re-included parent directory leaves its files `'unmatched'`.
2022
+ */
2023
+ ignoreVerdict(entryRel, isDir, stack) {
2024
+ for (let i = stack.length - 1; i >= 0; i--) {
2025
+ const layer = stack[i];
2026
+ if (!layer?.ig)
2027
+ continue;
2028
+ const { baseRel, ig } = layer;
2029
+ let sub;
2030
+ if (baseRel === "") {
2031
+ sub = entryRel;
2032
+ } else if (entryRel === baseRel || !entryRel.startsWith(baseRel + "/")) {
2033
+ continue;
2034
+ } else {
2035
+ sub = entryRel.slice(baseRel.length + 1);
2036
+ }
2037
+ if (!sub)
2038
+ continue;
2039
+ const res = ig.test(isDir ? sub + "/" : sub);
2040
+ if (res.ignored)
2041
+ return "ignored";
2042
+ if (res.unignored)
2043
+ return "unignored";
2044
+ }
2045
+ return "unmatched";
2046
+ }
2047
+ ignoredByStack(entryRel, isDir, stack) {
2048
+ return this.ignoreVerdict(entryRel, isDir, stack) === "ignored";
2049
+ }
2050
+ static attributeLayers(stack) {
2051
+ return stack.flatMap((layer) => layer.attributes ? [{ baseRel: layer.baseRel, rules: layer.attributes }] : []);
2052
+ }
2053
+ /**
2054
+ * Skip this directory? User-ignored directories are skipped as before. A
2055
+ * directory a soft default excludes (vendor/, generated/, ...) is pruned too -
2056
+ * nothing under it can be indexed - UNLESS a rule above it could re-include
2057
+ * something inside it: a `!pattern` or a `-linguist-*` attribute. Then the
2058
+ * walk descends and judges each file, so the override works. Rule files
2059
+ * INSIDE a pruned directory are never read: a vendored library's own
2060
+ * .gitignore negations are not the project's intent.
2061
+ */
2062
+ skipDirectory(relPath, stack) {
2063
+ const posixRel = relPath.split(sep).join("/");
2064
+ const verdict = this.ignoreVerdict(posixRel, true, stack);
2065
+ if (verdict === "ignored")
2066
+ return true;
2067
+ if (verdict === "unignored")
2068
+ return false;
2069
+ if (!softDefaultForDirectory(posixRel))
2070
+ return false;
2071
+ const canReinclude = stack.some((layer) => layer.hasNegation) || gitattributesCanReinclude(_FileSelector.attributeLayers(stack));
2072
+ return !canReinclude;
2073
+ }
2074
+ /**
2075
+ * The index decision for a file from its path and the rule stack: `null`
2076
+ * when a user ignore rule excludes it, otherwise the classifier's answer
2077
+ * (precedence in indexClassifier.ts). Content rules are the caller's next
2078
+ * step for an included path without an `override`.
2079
+ */
2080
+ classifyEntry(relPath, stack) {
2081
+ const posixRel = relPath.split(sep).join("/");
2082
+ const verdict = this.ignoreVerdict(posixRel, false, stack);
2083
+ if (verdict === "ignored")
2084
+ return null;
2085
+ return classifyForIndex(posixRel, {
2086
+ ignoreFileVerdict: verdict,
2087
+ attributes: _FileSelector.attributeLayers(stack)
2088
+ });
2089
+ }
2090
+ /**
2091
+ * Reconstruct the root->parent ignore-layer stack for a single file, for the
2092
+ * incremental `scanFiles` path (which has a flat file list, not a recursive
2093
+ * walk). Layers are memoized per directory in `cache` across a batch.
2094
+ */
2095
+ async buildIgnoreStackForFile(fileRel, rootPath, cache, walk) {
2096
+ const dirs = [""];
2097
+ let acc = "";
2098
+ for (const seg of fileRel.split("/").slice(0, -1)) {
2099
+ acc = acc === "" ? seg : `${acc}/${seg}`;
2100
+ dirs.push(acc);
2101
+ }
2102
+ const stack = [];
2103
+ for (const dir of dirs) {
2104
+ let layer = cache.get(dir);
2105
+ if (layer === void 0) {
2106
+ layer = await this.loadIgnoreLayer(dir === "" ? rootPath : join2(rootPath, dir), dir, walk, rootPath);
2107
+ cache.set(dir, layer);
2108
+ }
2109
+ if (layer)
2110
+ stack.push(layer);
2111
+ }
2112
+ return stack;
2113
+ }
2114
+ /**
2115
+ * A predicate for walkers outside this class (the daemon's file watcher):
2116
+ * true when the walk would descend into `absDir`, by the same rules
2117
+ * `listFiles` applies - user ignore files and soft-default directories,
2118
+ * with their overrides. Rule files are read once per directory for the life
2119
+ * of the returned predicate; take a fresh one after a rule file changes.
2120
+ * Directories outside `rootPath`, and the root itself, always pass.
2121
+ */
2122
+ directoryFilter(rootPath) {
2123
+ const cache = /* @__PURE__ */ new Map();
2124
+ return async (absDir) => {
2125
+ try {
2126
+ const root = await this.canonicalRoot(rootPath);
2127
+ const rel = relative(root, absDir).split(sep).join("/");
2128
+ if (!rel || rel.startsWith(".."))
2129
+ return true;
2130
+ const stack = await this.buildIgnoreStackForFile(rel, root, cache, { complete: true });
2131
+ return !this.skipDirectory(rel, stack);
2132
+ } catch {
2133
+ return true;
2134
+ }
2135
+ };
2136
+ }
2137
+ /**
2138
+ * True when any directory strictly between `rootPath` and `filePath`
2139
+ * starts a subtree this project does not index (see isProjectBoundary). Paths that don't
2140
+ * resolve under `rootPath` are not judged here (return false) - the
2141
+ * server's path validation handles escapes.
2142
+ */
2143
+ async insideNestedGitBoundary(filePath, rootPath) {
2144
+ const rel = relative(rootPath, filePath);
2145
+ if (!rel || rel.startsWith(".."))
2146
+ return false;
2147
+ const segments = rel.split(sep);
2148
+ let current = rootPath;
2149
+ for (let i = 0; i < segments.length - 1; i++) {
2150
+ const segment = segments[i];
2151
+ if (segment === void 0)
2152
+ continue;
2153
+ current = join2(current, segment);
2154
+ if (await this.isBoundary(current, rootPath))
2155
+ return true;
2156
+ }
2157
+ return false;
2158
+ }
2159
+ rootProjectIds = /* @__PURE__ */ new Map();
2160
+ /**
2161
+ * isProjectBoundary, with the scan root's own project as the enclosing one.
2162
+ * The root's file is read at most once a minute: a walk tests many folders,
2163
+ * and a project relinked since is seen on the next scan.
2164
+ */
2165
+ async isBoundary(dirPath, rootPath) {
2166
+ let cached = this.rootProjectIds.get(rootPath);
2167
+ if (!cached || Date.now() - cached.at > 6e4) {
2168
+ cached = { at: Date.now(), projectId: rootProjectId(rootPath) };
2169
+ this.rootProjectIds.set(rootPath, cached);
2170
+ }
2171
+ return isProjectBoundary(dirPath, await cached.projectId);
2172
+ }
2173
+ canonicalRoots = /* @__PURE__ */ new Map();
2174
+ async canonicalRoot(root) {
2175
+ let canonical = this.canonicalRoots.get(root);
2176
+ if (!canonical) {
2177
+ canonical = await realpath(root);
2178
+ this.canonicalRoots.set(root, canonical);
2179
+ }
2180
+ return canonical;
2181
+ }
2182
+ static containsCanonical(candidate, root, paths = { sep, resolve }, caseInsensitive = process.platform === "win32") {
2183
+ candidate = paths.resolve(candidate);
2184
+ root = paths.resolve(root);
2185
+ if (caseInsensitive) {
2186
+ candidate = candidate.toLowerCase();
2187
+ root = root.toLowerCase();
2188
+ }
2189
+ return candidate === root || candidate.startsWith(root.endsWith(paths.sep) ? root : root + paths.sep);
2190
+ }
2191
+ async confineSymlink(fullPath, canonicalRoot) {
2192
+ let path = resolve(fullPath);
2193
+ if (!_FileSelector.containsCanonical(path, canonicalRoot))
2194
+ return false;
2195
+ for (; ; ) {
2196
+ if (this.skipSymlink(await lstat2(path)))
2197
+ throw new Error("symlink_skipped");
2198
+ if (_FileSelector.containsCanonical(canonicalRoot, path))
2199
+ break;
2200
+ path = dirname(path);
2201
+ }
2202
+ return _FileSelector.containsCanonical(await realpath(fullPath), canonicalRoot);
2203
+ }
2204
+ async readConfined(fullPath, canonicalRoot, enumerated) {
2205
+ const expected = enumerated ?? await lstat2(fullPath);
2206
+ if (this.skipSymlink(expected))
2207
+ throw new Error("symlink_skipped");
2208
+ if (!expected.isFile())
2209
+ throw new Error("scanner_not_regular");
2210
+ if (!await this.confineSymlink(fullPath, canonicalRoot))
2211
+ throw new Error("scanner_containment");
2212
+ const canonical = await realpath(fullPath);
2213
+ if (!_FileSelector.containsCanonical(canonical, canonicalRoot) || await this.isProtectedRead(canonical))
2214
+ throw new Error("scanner_containment");
2215
+ const flags = constants.O_RDONLY | (constants.O_NONBLOCK ?? 0) | (process.platform === "win32" ? 0 : constants.O_NOFOLLOW ?? 0);
2216
+ const handle = await open(fullPath, flags).catch((error) => {
2217
+ if (error.code === "ELOOP") {
2218
+ this.skipReasons.symlink_skipped++;
2219
+ throw new Error("symlink_skipped");
2220
+ }
2221
+ throw error;
2222
+ });
2223
+ try {
2224
+ const actual = await handle.stat();
2225
+ if (!actual.isFile())
2226
+ throw new Error("scanner_not_regular");
2227
+ if (actual.dev !== expected.dev || actual.ino !== expected.ino)
2228
+ throw new Error("scanner_identity_changed");
2229
+ if (process.platform === "win32" && this.skipSymlink(await lstat2(fullPath)))
2230
+ throw new Error("symlink_skipped");
2231
+ if (process.platform === "linux" && await realpath(`/proc/self/fd/${handle.fd}`) !== canonical)
2232
+ throw new Error("scanner_containment");
2233
+ return await handle.readFile("utf-8");
2234
+ } finally {
2235
+ await handle.close();
2236
+ }
2237
+ }
2238
+ /**
2239
+ * Enumerate scan-eligible relative file paths under `rootPath` without
2240
+ * reading or chunking content. Same walk + ignorePatterns + extension
2241
+ * filter as `scanDirectory`, but bounded to O(file count) directory ops
2242
+ * - cheap enough to call on every periodic reconcile tick.
2243
+ *
2244
+ * Returns relative paths normalized against `rootPath`, matching the
2245
+ * shape the server stores in `memories.metadata->>'filePath'`. Use this
2246
+ * for the scanner reconciliation channel (`POST /api/v1/scan/reconcile`).
2247
+ *
2248
+ * Defensive: skips any path whose `relative()` result contains a `..`
2249
+ * traversal segment (can happen when a symlink resolves under root but
2250
+ * the readdir entry path doesn't normalize cleanly). The server's
2251
+ * `safeScanPath` rejects such paths; filtering here keeps a single
2252
+ * malformed entry from failing the entire reconcile push.
2253
+ */
2254
+ async listFiles(rootPath) {
2255
+ const { paths } = await this.listFilesWithStatus(rootPath);
2256
+ return paths;
2257
+ }
2258
+ /**
2259
+ * Completeness-aware variant of `listFiles`. `complete === false` means
2260
+ * an fs error truncated part of the walk - the returned paths may be
2261
+ * missing files that still exist on disk. The daemon's reconcile channel
2262
+ * MUST NOT derive removedFiles from an incomplete inventory (a truncated
2263
+ * walk otherwise reads as mass deletion and deprecates live memories).
2264
+ */
2265
+ async listFilesWithStatus(rootPath) {
2266
+ rootPath = await this.canonicalRoot(rootPath);
2267
+ const paths = [];
2268
+ const overridden = /* @__PURE__ */ new Set();
2269
+ const stats = /* @__PURE__ */ new Map();
2270
+ const walk = { complete: true, rules: /* @__PURE__ */ new Map() };
2271
+ await this.traversePaths(rootPath, rootPath, paths, 0, walk, [], overridden, stats);
2272
+ if (walk.complete && walk.rules)
2273
+ this.ruleFilesByRoot.set(rootPath, walk.rules);
2274
+ return {
2275
+ paths: paths.filter((p) => !/(^|[/\\])\.\.([/\\]|$)/.test(p)),
2276
+ complete: walk.complete,
2277
+ overridden,
2278
+ unreadable: walk.unreadable ?? 0,
2279
+ unreadableSamples: walk.unreadableSamples ?? [],
2280
+ stats
2281
+ };
2282
+ }
2283
+ /**
2284
+ * The rule files the last complete `listFilesWithStatus` walk of `rootPath`
2285
+ * applied, by directory ('' = root). Null before the first complete walk.
2286
+ * The daemon uploads these lines so the server judges paths as it did.
2287
+ */
2288
+ async indexRuleFiles(rootPath) {
2289
+ return this.ruleFilesByRoot.get(await this.canonicalRoot(rootPath)) ?? null;
2290
+ }
2291
+ ruleFilesByRoot = /* @__PURE__ */ new Map();
2292
+ async traversePaths(currentPath, rootPath, out, depth, walk, stack = [], overridden, statsOut) {
2293
+ if (this.isProtected(currentPath))
2294
+ return;
2295
+ let entries;
2296
+ try {
2297
+ if (!await this.confineSymlink(currentPath, rootPath))
2298
+ throw new Error("scanner_containment");
2299
+ if (depth === 0 && !await enterDirectory(walk, currentPath))
2300
+ return;
2301
+ entries = await fileOp(() => readdir(currentPath), `readdir: ${currentPath}`);
2302
+ } catch (error) {
2303
+ markUnreadable(walk, currentPath, error);
2304
+ debug("Error traversing directory (listFiles)", { path: currentPath, error });
2305
+ return;
2306
+ }
2307
+ const dirRel = currentPath === rootPath ? "" : relative(rootPath, currentPath).split(sep).join("/");
2308
+ const localLayer = await this.loadIgnoreLayer(currentPath, dirRel, walk, rootPath);
2309
+ const localStack = localLayer ? [...stack, localLayer] : stack;
2310
+ for (const entry of entries) {
2311
+ const fullPath = join2(currentPath, entry);
2312
+ const relativePath = relative(rootPath, fullPath);
2313
+ if (this.isProtected(fullPath))
2314
+ continue;
2315
+ if (this.shouldIgnore(relativePath))
2316
+ continue;
2317
+ try {
2318
+ const lstats = await fileOp(() => lstat2(fullPath), `lstat: ${fullPath}`);
2319
+ if (this.skipSymlink(lstats))
2320
+ continue;
2321
+ const stats = lstats;
2322
+ if (stats.isDirectory()) {
2323
+ if (this.skipDirectory(relativePath, localStack))
2324
+ continue;
2325
+ if (await this.isBoundary(fullPath, rootPath))
2326
+ continue;
2327
+ const leave = await enterDirectory(walk, fullPath);
2328
+ if (!leave)
2329
+ continue;
2330
+ try {
2331
+ await this.traversePaths(fullPath, rootPath, out, depth + 1, walk, localStack, overridden, statsOut);
2332
+ } finally {
2333
+ leave();
2334
+ }
2335
+ } else if (stats.isFile()) {
2336
+ if (!this.isChunkable(fullPath, relativePath))
2337
+ continue;
2338
+ const decision = this.classifyEntry(relativePath, localStack);
2339
+ if (!decision?.include)
2340
+ continue;
2341
+ out.push(relativePath);
2342
+ statsOut?.set(relativePath, statTuple(stats));
2343
+ if (decision.override)
2344
+ overridden?.add(relativePath);
2345
+ }
2346
+ } catch (error) {
2347
+ markUnreadable(walk, fullPath, error);
2348
+ debug("Error traversing directory (listFiles)", { path: fullPath, error });
2349
+ }
2350
+ }
2351
+ }
2352
+ /**
2353
+ * Check if path should be ignored
2354
+ * Fixed glob-to-regex conversion and substring matching.
2355
+ * - Escape regex special chars before replacing * with .*
2356
+ * - Replace ALL * occurrences (not just the first)
2357
+ * - For non-glob patterns, match on path segments to avoid false positives
2358
+ * (e.g., '.env' should not match '.environment.ts')
2359
+ */
2360
+ shouldIgnore(path) {
2361
+ if (isSecretFile(path))
2362
+ return true;
2363
+ const segments = path.split("/");
2364
+ return this.selection.ignorePatterns.some((pattern) => {
2365
+ if (pattern.includes("*")) {
2366
+ const anchored = pattern.startsWith("/");
2367
+ const body = anchored ? pattern.slice(1) : pattern;
2368
+ const escaped = body.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
2369
+ const source = escaped.replace(/\*/g, ".*");
2370
+ const regex = new RegExp(anchored ? `(^|/)${source}` : source);
2371
+ return regex.test(path);
2372
+ }
2373
+ return segments.some((segment) => segment === pattern) || path.endsWith("/" + pattern);
2374
+ });
2375
+ }
2376
+ };
2377
+ }
2378
+ });
2379
+
2380
+ // packages/cli/src/scanner/estimate.ts
2381
+ import { lstat as lstat3 } from "node:fs/promises";
2382
+ import { join as join3 } from "node:path";
2383
+ var COUNT_TIME_BOUND_MS = 3e4;
2384
+ async function defaultLister() {
2385
+ const { FileSelector: FileSelector2 } = await Promise.resolve().then(() => (init_fileSelection(), fileSelection_exports));
2386
+ const selector = new FileSelector2();
2387
+ return (root) => selector.listFilesWithStatus(root);
2388
+ }
2389
+ async function countIndexableFiles(roots, options = {}) {
2390
+ const now = options.now ?? Date.now;
2391
+ const deadline = now() + (options.timeBoundMs ?? COUNT_TIME_BOUND_MS);
2392
+ const list = options.list ?? await defaultLister();
2393
+ let files = 0;
2394
+ let bytes = 0;
2395
+ for (const root of roots) {
2396
+ const remaining = deadline - now();
2397
+ if (remaining <= 0) return { files, bytes, partial: true };
2398
+ const listed = await Promise.race([
2399
+ list(root),
2400
+ new Promise((resolve2) => setTimeout(() => resolve2(null), remaining).unref())
2401
+ ]);
2402
+ if (!listed) return { files, bytes, partial: true };
2403
+ for (let i = 0; i < listed.paths.length; i += 64) {
2404
+ if (now() > deadline) return { files, bytes, partial: true };
2405
+ const sizes = await Promise.all(
2406
+ listed.paths.slice(i, i + 64).map(
2407
+ (path) => lstat3(join3(root, path)).then(
2408
+ (stat) => stat.size,
2409
+ () => null
2410
+ )
2411
+ )
2412
+ );
2413
+ for (const size of sizes) {
2414
+ if (size === null) continue;
2415
+ files++;
2416
+ bytes += size;
2417
+ }
2418
+ }
2419
+ }
2420
+ return { files, bytes, partial: false };
2421
+ }
2422
+ var number = (value) => value.toLocaleString("en-US");
2423
+ function formatBytes(bytes) {
2424
+ if (bytes < 1024 * 1024) return `${Math.max(1, Math.round(bytes / 1024))} KB`;
2425
+ if (bytes < 1024 * 1024 * 1024) return `${number(Math.round(bytes / (1024 * 1024)))} MB`;
2426
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
2427
+ }
2428
+ function unit(seconds) {
2429
+ if (seconds < 90 * 60) return { value: Math.max(1, Math.round(seconds / 60)), name: "minute" };
2430
+ if (seconds < 48 * 3600) return { value: Math.round(seconds / 3600), name: "hour" };
2431
+ return { value: Math.round(seconds / 86400), name: "day" };
2432
+ }
2433
+ function formatDurationRange(lowSeconds, highSeconds) {
2434
+ if (highSeconds < 60) return "under a minute";
2435
+ const low = unit(lowSeconds);
2436
+ const high = unit(highSeconds);
2437
+ const plural = (n, name) => `${n} ${name}${n === 1 ? "" : "s"}`;
2438
+ if (low.name !== high.name)
2439
+ return `about ${plural(low.value, low.name)} to ${plural(high.value, high.name)}`;
2440
+ if (low.value === high.value) return `about ${plural(high.value, high.name)}`;
2441
+ return `about ${low.value} to ${plural(high.value, high.name)}`;
2442
+ }
2443
+ function onboardingEstimateLines(count, estimate) {
2444
+ if (count.files === 0) return [];
2445
+ const files = `${count.partial ? "at least " : ""}${number(count.files)} file${count.files === 1 ? "" : "s"}`;
2446
+ const lines = [`Mnemonik will index ${files} (${formatBytes(count.bytes)}).`];
2447
+ if (estimate) {
2448
+ const busy = estimate.accountsAhead > 0 ? " at the current load" : "";
2449
+ lines.push(
2450
+ `Indexing will take ${formatDurationRange(estimate.lowSeconds, estimate.highSeconds)}${busy}. You can keep working while it runs; files become searchable as they are indexed.`
2451
+ );
2452
+ }
2453
+ return lines;
2454
+ }
2455
+ export {
2456
+ COUNT_TIME_BOUND_MS,
2457
+ countIndexableFiles,
2458
+ formatBytes,
2459
+ formatDurationRange,
2460
+ onboardingEstimateLines
2461
+ };