token-goat 2.8.6 → 2.9.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.
@@ -29,6 +29,7 @@ import {
29
29
  formatCsvTable,
30
30
  getEmbeddingCoverage,
31
31
  getFileEntry,
32
+ getTrackedFiles,
32
33
  indexFileSync,
33
34
  isAvailable,
34
35
  isBlobStale,
@@ -42,6 +43,7 @@ import {
42
43
  mergeNearbyHits,
43
44
  ocrImage,
44
45
  ocrIntegrityFailed,
46
+ parseIpv6Groups,
45
47
  parseWhereSpecs,
46
48
  pathEqClause,
47
49
  probeImageMeta,
@@ -66,7 +68,7 @@ import {
66
68
  walkProject,
67
69
  yamlLineClosesQuote,
68
70
  yamlOpenQuoteAfter
69
- } from "./token-goat-chunk-DK4VLLYB.mjs";
71
+ } from "./token-goat-chunk-LJ3CHCTT.mjs";
70
72
  import {
71
73
  Database,
72
74
  PER_FILE_COUNTERFACTUAL_CEILING,
@@ -120,36 +122,11 @@ import {
120
122
  unsupportedLanguageName,
121
123
  windowsCmdQuoteArg,
122
124
  withExtension
123
- } from "./token-goat-chunk-2JZ66BBE.mjs";
125
+ } from "./token-goat-chunk-UZ2NFOOZ.mjs";
124
126
  import {
125
127
  registerReset
126
128
  } from "./token-goat-chunk-AO2QD2AG.mjs";
127
129
 
128
- // src/repomap.ts
129
- import * as fs from "fs";
130
- import * as path from "path";
131
- function getTrackedFiles(cwd = process.cwd()) {
132
- try {
133
- let dir = cwd;
134
- let onlyFile = "";
135
- try {
136
- if (fs.statSync(cwd).isFile()) {
137
- dir = path.dirname(cwd);
138
- onlyFile = path.basename(cwd);
139
- }
140
- } catch {
141
- }
142
- const args = onlyFile === "" ? ["ls-files"] : ["ls-files", "--error-unmatch", "--", onlyFile];
143
- const result = runGit(args, { cwd: dir });
144
- if (result.exitCode !== 0 || !result.stdout) {
145
- return [];
146
- }
147
- return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((rel) => path.join(dir, rel));
148
- } catch {
149
- return [];
150
- }
151
- }
152
-
153
130
  // src/section_reader.ts
154
131
  import { readFileSync } from "node:fs";
155
132
  function parseHeadingSpec(spec, headers) {
@@ -477,8 +454,8 @@ import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
477
454
  import { randomUUID } from "node:crypto";
478
455
 
479
456
  // src/read_commands.ts
480
- import * as fs5 from "node:fs";
481
- import * as path5 from "node:path";
457
+ import * as fs4 from "node:fs";
458
+ import * as path4 from "node:path";
482
459
 
483
460
  // src/json_query.ts
484
461
  function jsonType(value) {
@@ -2371,24 +2348,32 @@ function collectionTagName(state, event, defaultTagName) {
2371
2348
  function isMappingTag(tag) {
2372
2349
  return tag.nodeKind === "mapping";
2373
2350
  }
2351
+ function chargeMergeWork(state) {
2352
+ state.totalMergeKeys++;
2353
+ if (state.maxTotalMergeKeys !== -1 && state.totalMergeKeys > state.maxTotalMergeKeys) throwError$1(state, `merge keys exceeded maxTotalMergeKeys (${state.maxTotalMergeKeys})`);
2354
+ }
2374
2355
  function mergeKeys(state, frame, source, sourceTag) {
2356
+ chargeMergeWork(state);
2375
2357
  for (const sourceKey of sourceTag.keys(source)) {
2376
- if (state.maxTotalMergeKeys !== -1 && ++state.totalMergeKeys > state.maxTotalMergeKeys) throwError$1(state, `merge keys exceeded maxTotalMergeKeys (${state.maxTotalMergeKeys})`);
2358
+ chargeMergeWork(state);
2377
2359
  if (frame.tag.has(frame.value, sourceKey)) continue;
2378
2360
  const err = frame.tag.addPair(frame.value, sourceKey, sourceTag.get(source, sourceKey));
2379
2361
  if (err) throwError$1(state, err);
2380
- (frame.overridable ??= /* @__PURE__ */ new Set()).add(sourceKey);
2362
+ frame.overridable ??= /* @__PURE__ */ new Set();
2363
+ frame.overridable.add(sourceKey);
2381
2364
  }
2382
2365
  }
2383
2366
  function mergeSource(state, frame, source, sourceTag) {
2384
2367
  state.position = frame.keyPosition;
2385
2368
  if (isMappingTag(sourceTag)) mergeKeys(state, frame, source, sourceTag);
2386
- else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) for (const element of source) {
2387
- const elementTag = state.nodeTags.get(element);
2388
- if (!elementTag) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
2389
- mergeKeys(state, frame, element, elementTag);
2390
- }
2391
- else throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
2369
+ else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) {
2370
+ if (source.length > 100) throwError$1(state, "abnormal merge sequence size");
2371
+ for (const element of source) {
2372
+ const elementTag = state.nodeTags.get(element);
2373
+ if (!elementTag) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
2374
+ mergeKeys(state, frame, element, elementTag);
2375
+ }
2376
+ } else throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
2392
2377
  }
2393
2378
  function addMappingValue(state, frame, key, value, tag) {
2394
2379
  state.position = frame.keyPosition;
@@ -2731,6 +2716,24 @@ function testDocumentSeparator(state, position = state.position) {
2731
2716
  }
2732
2717
  return false;
2733
2718
  }
2719
+ function skipByteOrderMark(state) {
2720
+ if (state.position === state.lineStart && state.input.charCodeAt(state.position) === 65279) {
2721
+ state.position++;
2722
+ state.lineStart = state.position;
2723
+ }
2724
+ }
2725
+ function testDocumentBoundary(state) {
2726
+ if (state.position !== state.lineStart) return false;
2727
+ if (testDocumentSeparator(state)) return true;
2728
+ if (state.input.charCodeAt(state.position) !== 65279) return false;
2729
+ const snapshot = snapshotState(state);
2730
+ skipByteOrderMark(state);
2731
+ skipSeparationSpace(state, true);
2732
+ const ch = state.input.charCodeAt(state.position);
2733
+ const result = state.position === state.lineStart && (ch === 37 || ch === 45 && testDocumentSeparator(state));
2734
+ restoreState(state, snapshot);
2735
+ return result;
2736
+ }
2734
2737
  function skipUntilLineEnd(state) {
2735
2738
  let ch = state.input.charCodeAt(state.position);
2736
2739
  while (ch !== 0 && !isEol(ch)) ch = state.input.charCodeAt(++state.position);
@@ -2914,7 +2917,7 @@ function readBlockScalar(state, parentIndent, props) {
2914
2917
  } else if (column > 0) valueEnd = linePosition + column;
2915
2918
  break;
2916
2919
  }
2917
- if (linePosition === state.lineStart && testDocumentSeparator(state, linePosition)) break;
2920
+ if (testDocumentBoundary(state)) break;
2918
2921
  if (!detectedIndent && contentIndent === -1 && isEol(first)) maxLeadingIndent = Math.max(maxLeadingIndent, column);
2919
2922
  if (!detectedIndent && contentIndent === -1 && !isEol(first)) {
2920
2923
  if (first === 9 && column < parentIndent) {
@@ -2967,7 +2970,7 @@ function readPlainScalar(state, nodeIndent, nodeContext, props) {
2967
2970
  const inFlow = nodeContext === CONTEXT_FLOW_IN;
2968
2971
  let multiline = false;
2969
2972
  while (ch !== 0) {
2970
- if (state.position === state.lineStart && testDocumentSeparator(state)) break;
2973
+ if (testDocumentBoundary(state)) break;
2971
2974
  if (ch === 58) {
2972
2975
  const following = state.input.charCodeAt(state.position + 1);
2973
2976
  if (isWsOrEolOrEnd(following) || inFlow && isFlowIndicator(following)) break;
@@ -3324,7 +3327,7 @@ function readDocument(state) {
3324
3327
  const documentEvent = state.events[documentEventIndex];
3325
3328
  if (documentEvent?.type === EVENT_ID.DOCUMENT) documentEvent.explicitEnd = explicitEnd;
3326
3329
  addPopEvent(state);
3327
- if (!explicitEnd && state.position < state.length && !(state.position === state.lineStart && testDocumentSeparator(state))) throwError(state, "end of the stream or a document separator is expected");
3330
+ if (!explicitEnd && state.position < state.length && !testDocumentBoundary(state)) throwError(state, "end of the stream or a document separator is expected");
3328
3331
  }
3329
3332
  function parseEvents(input, options) {
3330
3333
  const length = input.length;
@@ -3345,8 +3348,8 @@ function parseEvents(input, options) {
3345
3348
  };
3346
3349
  const nullpos = input.indexOf("\0");
3347
3350
  if (nullpos !== -1) YAMLException.throwAt(input, nullpos, "null byte is not allowed in input", state.filename);
3348
- if (state.input.charCodeAt(state.position) === 65279) state.position++;
3349
3351
  while (state.position < state.length) {
3352
+ skipByteOrderMark(state);
3350
3353
  skipSeparationSpace(state, true);
3351
3354
  if (state.position >= state.length) break;
3352
3355
  const documentStart = state.position;
@@ -3387,27 +3390,108 @@ function load(input, options) {
3387
3390
  if (documents.length === 1) return documents[0];
3388
3391
  throw new YAMLException("expected a single document in the stream, but found more");
3389
3392
  }
3390
- var ESCAPE_SEQUENCES = {};
3391
- ESCAPE_SEQUENCES[0] = "\\0";
3392
- ESCAPE_SEQUENCES[7] = "\\a";
3393
- ESCAPE_SEQUENCES[8] = "\\b";
3394
- ESCAPE_SEQUENCES[9] = "\\t";
3395
- ESCAPE_SEQUENCES[10] = "\\n";
3396
- ESCAPE_SEQUENCES[11] = "\\v";
3397
- ESCAPE_SEQUENCES[12] = "\\f";
3398
- ESCAPE_SEQUENCES[13] = "\\r";
3399
- ESCAPE_SEQUENCES[27] = "\\e";
3400
- ESCAPE_SEQUENCES[34] = '\\"';
3401
- ESCAPE_SEQUENCES[92] = "\\\\";
3402
- ESCAPE_SEQUENCES[133] = "\\N";
3403
- ESCAPE_SEQUENCES[160] = "\\_";
3404
- ESCAPE_SEQUENCES[8232] = "\\L";
3405
- ESCAPE_SEQUENCES[8233] = "\\P";
3393
+ function hasBit(mask, bit) {
3394
+ return (mask & 1 << bit) !== 0;
3395
+ }
3396
+ var DEFAULT_SCALAR_STYLE_RULES = {
3397
+ applyQuoteFlowKeysOption,
3398
+ doubleQuoteForInvisibles,
3399
+ doubleQuoteWhitespaceOnly,
3400
+ applyForceQuotesOption,
3401
+ tryLongOrMultilineAsBlock,
3402
+ quoteInvalidPlain,
3403
+ fallbackToDoubleQuoted
3404
+ };
3405
+ function _preferredQuotedStyle(layout) {
3406
+ if (layout.presenterOptions.quoteStyle === "single" && hasBit(layout.allowedStylesMask, SCALAR_STYLE.SINGLE_QUOTED)) return SCALAR_STYLE.SINGLE_QUOTED;
3407
+ return SCALAR_STYLE.DOUBLE_QUOTED;
3408
+ }
3409
+ function applyQuoteFlowKeysOption(layout) {
3410
+ if (!layout.presenterOptions.quoteFlowKeys) return;
3411
+ if (!layout.isKey || !layout.flowOnly || layout.style !== SCALAR_STYLE.PLAIN) return;
3412
+ layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
3413
+ }
3414
+ function doubleQuoteForInvisibles(layout) {
3415
+ if (layout.style === SCALAR_STYLE.PLAIN && /[\t\x7F-\xA0\u2028\u2029\uFEFF\uFFFE\uFFFF]/.test(layout.node.value)) layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
3416
+ }
3417
+ function doubleQuoteWhitespaceOnly(layout) {
3418
+ if (layout.style === SCALAR_STYLE.PLAIN && /^\s+$/.test(layout.node.value)) layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
3419
+ }
3420
+ function applyForceQuotesOption(layout) {
3421
+ if (!layout.presenterOptions.forceQuotes) return;
3422
+ if (layout.isKey || layout.style !== SCALAR_STYLE.PLAIN) return;
3423
+ layout.style = layout.node.value.includes("\n") ? SCALAR_STYLE.DOUBLE_QUOTED : _preferredQuotedStyle(layout);
3424
+ }
3425
+ function tryLongOrMultilineAsBlock(layout) {
3426
+ if (layout.style !== SCALAR_STYLE.PLAIN || layout.isKey) return;
3427
+ const value = layout.node.value;
3428
+ const multiline = value.indexOf("\n") !== -1;
3429
+ if (!hasBit(layout.allowedStylesMask, SCALAR_STYLE.LITERAL_BLOCK)) {
3430
+ if (multiline) layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
3431
+ return;
3432
+ }
3433
+ const w = layout.presenterOptions.lineWidth;
3434
+ if (w === -1) {
3435
+ if (multiline) layout.style = SCALAR_STYLE.LITERAL_BLOCK;
3436
+ return;
3437
+ }
3438
+ const availableWidth = Math.max(Math.min(w, 40), w - layout.shiftOfContent);
3439
+ let position = 0;
3440
+ let shouldFold = false;
3441
+ while (position <= value.length) {
3442
+ let lineEnd = value.length;
3443
+ const nextLineBreak = value.indexOf("\n", position);
3444
+ if (nextLineBreak !== -1) lineEnd = nextLineBreak;
3445
+ const line = value.slice(position, lineEnd);
3446
+ if (line.length > availableWidth && line[0] !== " " && / [^ \t]/.test(line)) shouldFold = true;
3447
+ if (nextLineBreak === -1) break;
3448
+ position = nextLineBreak + 1;
3449
+ }
3450
+ if (shouldFold) layout.style = SCALAR_STYLE.FOLDED_BLOCK;
3451
+ else if (multiline) layout.style = SCALAR_STYLE.LITERAL_BLOCK;
3452
+ }
3453
+ function quoteInvalidPlain(layout) {
3454
+ if (layout.style === SCALAR_STYLE.PLAIN && !hasBit(layout.allowedStylesMask, SCALAR_STYLE.PLAIN)) layout.style = _preferredQuotedStyle(layout);
3455
+ }
3456
+ function fallbackToDoubleQuoted(layout) {
3457
+ if (!hasBit(layout.allowedStylesMask, layout.style)) layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
3458
+ }
3459
+ var SRC_C_PRINTABLE = "[\\x09\\x0A\\x0D\\x20-\\x7E\\x85\\xA0-\\uD7FF\\uE000-\\uFFFD\\u{10000}-\\u{10FFFF}]";
3460
+ var SRC_B_CHAR = "[\\n\\r]";
3461
+ var SRC_C_BYTE_ORDER_MARK = "\\uFEFF";
3462
+ var SRC_S_WHITE = "[ \\t]";
3463
+ var SRC_NB_CHAR = `(?:(?!(?:${SRC_B_CHAR}|${SRC_C_BYTE_ORDER_MARK}))${SRC_C_PRINTABLE})`;
3464
+ var SRC_NS_CHAR = `(?:(?!${SRC_S_WHITE})${SRC_NB_CHAR})`;
3465
+ var SRC_NB_JSON = "[\\x09\\x20-\\uD7FF\\uE000-\\uFFFF\\u{10000}-\\u{10FFFF}]";
3466
+ var SRC_C_INDICATOR = "[-?:,\\[\\]{}#&*!|>'\"%@`]";
3467
+ var SRC_C_FLOW_INDICATOR = "[,\\[\\]{}]";
3468
+ var SRC_NS_PLAIN_SAFE_FLOW_OUT = SRC_NS_CHAR;
3469
+ var SRC_NS_PLAIN_SAFE_FLOW_IN = `(?:(?!${SRC_C_FLOW_INDICATOR})${SRC_NS_CHAR})`;
3470
+ var SRC_NS_PLAIN_FIRST_FLOW_OUT = `(?:(?:(?!${SRC_C_INDICATOR})${SRC_NS_CHAR})|[?:-](?=${SRC_NS_PLAIN_SAFE_FLOW_OUT}))`;
3471
+ var SRC_NS_PLAIN_FIRST_FLOW_IN = `(?:(?:(?!${SRC_C_INDICATOR})${SRC_NS_CHAR})|[?:-](?=${SRC_NS_PLAIN_SAFE_FLOW_IN}))`;
3472
+ var SRC_NS_PLAIN_CHAR_FLOW_OUT = `(?:(?:(?![:#])${SRC_NS_PLAIN_SAFE_FLOW_OUT})|:(?=${SRC_NS_PLAIN_SAFE_FLOW_OUT}))#*`;
3473
+ var SRC_NS_PLAIN_CHAR_FLOW_IN = `(?:(?:(?![:#])${SRC_NS_PLAIN_SAFE_FLOW_IN})|:(?=${SRC_NS_PLAIN_SAFE_FLOW_IN}))#*`;
3474
+ var SRC_NB_NS_PLAIN_IN_LINE_FLOW_OUT = `(?:${SRC_S_WHITE}*${SRC_NS_PLAIN_CHAR_FLOW_OUT})*`;
3475
+ var SRC_NB_NS_PLAIN_IN_LINE_FLOW_IN = `(?:${SRC_S_WHITE}*${SRC_NS_PLAIN_CHAR_FLOW_IN})*`;
3476
+ var SRC_NS_PLAIN_ONE_LINE_FLOW_OUT = `${SRC_NS_PLAIN_FIRST_FLOW_OUT}#*${SRC_NB_NS_PLAIN_IN_LINE_FLOW_OUT}`;
3477
+ var SRC_NS_PLAIN_ONE_LINE_FLOW_IN = `${SRC_NS_PLAIN_FIRST_FLOW_IN}#*${SRC_NB_NS_PLAIN_IN_LINE_FLOW_IN}`;
3478
+ var SRC_NS_PLAIN_ONE_LINE_BLOCK_KEY = SRC_NS_PLAIN_ONE_LINE_FLOW_OUT;
3479
+ var SRC_NS_PLAIN_ONE_LINE_FLOW_KEY = SRC_NS_PLAIN_ONE_LINE_FLOW_IN;
3480
+ var SRC_S_NS_PLAIN_NEXT_LINE_FLOW_OUT = `\\n+${SRC_NS_PLAIN_CHAR_FLOW_OUT}${SRC_NB_NS_PLAIN_IN_LINE_FLOW_OUT}`;
3481
+ var SRC_S_NS_PLAIN_NEXT_LINE_FLOW_IN = `\\n+${SRC_NS_PLAIN_CHAR_FLOW_IN}${SRC_NB_NS_PLAIN_IN_LINE_FLOW_IN}`;
3482
+ var SRC_NS_PLAIN_MULTI_LINE_FLOW_OUT = `${SRC_NS_PLAIN_ONE_LINE_FLOW_OUT}(?:${SRC_S_NS_PLAIN_NEXT_LINE_FLOW_OUT})*`;
3483
+ var SRC_NS_PLAIN_MULTI_LINE_FLOW_IN = `${SRC_NS_PLAIN_ONE_LINE_FLOW_IN}(?:${SRC_S_NS_PLAIN_NEXT_LINE_FLOW_IN})*`;
3484
+ var NS_PLAIN_FLOW_OUT = new RegExp(`^(?:${SRC_NS_PLAIN_MULTI_LINE_FLOW_OUT})$`, "u");
3485
+ var NS_PLAIN_FLOW_IN = new RegExp(`^(?:${SRC_NS_PLAIN_MULTI_LINE_FLOW_IN})$`, "u");
3486
+ var NS_PLAIN_BLOCK_KEY = new RegExp(`^(?:${SRC_NS_PLAIN_ONE_LINE_BLOCK_KEY})$`, "u");
3487
+ var NS_PLAIN_FLOW_KEY = new RegExp(`^(?:${SRC_NS_PLAIN_ONE_LINE_FLOW_KEY})$`, "u");
3488
+ var NB_SINGLE_ONE_LINE = new RegExp(`^(?:${SRC_NB_JSON})*$`, "u");
3489
+ var NB_SINGLE_MULTI_LINE = new RegExp(`^(?:${SRC_NB_JSON}|\\n)*$`, "u");
3490
+ var BLOCK_SCALAR_CONTENT = new RegExp(`^(?:${SRC_NB_CHAR}|\\n)*$`, "u");
3406
3491
  var DEFAULT_PRESENTER_OPTIONS = {
3407
3492
  indent: 2,
3408
3493
  seqNoIndent: false,
3409
3494
  seqInlineFirst: true,
3410
- sortKeys: false,
3411
3495
  lineWidth: 80,
3412
3496
  flowBracketPadding: false,
3413
3497
  flowSkipCommaSpace: false,
@@ -3415,6 +3499,7 @@ var DEFAULT_PRESENTER_OPTIONS = {
3415
3499
  quoteFlowKeys: false,
3416
3500
  quoteStyle: "single",
3417
3501
  forceQuotes: false,
3502
+ scalarStyleRules: Object.keys(DEFAULT_SCALAR_STYLE_RULES).map((name) => Reflect.get(DEFAULT_SCALAR_STYLE_RULES, name)),
3418
3503
  tagBeforeAnchor: false
3419
3504
  };
3420
3505
  var DEFAULT_DUMP_OPTIONS = {
@@ -3423,6 +3508,7 @@ var DEFAULT_DUMP_OPTIONS = {
3423
3508
  skipInvalid: false,
3424
3509
  noRefs: false,
3425
3510
  flowLevel: -1,
3511
+ sortKeys: false,
3426
3512
  transform: () => {
3427
3513
  }
3428
3514
  };
@@ -3444,11 +3530,11 @@ var CHOMPING_STRIP = CHOMPING_MODE.STRIP;
3444
3530
  var CHOMPING_KEEP = CHOMPING_MODE.KEEP;
3445
3531
 
3446
3532
  // src/openapi_query.ts
3447
- import * as path2 from "node:path";
3533
+ import * as path from "node:path";
3448
3534
  var HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
3449
3535
  function parseOpenApiSpec(text, filePath) {
3450
3536
  text = stripBom(text);
3451
- const ext = path2.extname(filePath).toLowerCase();
3537
+ const ext = path.extname(filePath).toLowerCase();
3452
3538
  if (ext === ".yaml" || ext === ".yml") return load(text);
3453
3539
  if (ext === ".json") return JSON.parse(text);
3454
3540
  try {
@@ -3802,24 +3888,24 @@ ${c.body}`).join("\n\n");
3802
3888
  }
3803
3889
 
3804
3890
  // src/sqlite_query.ts
3805
- import * as fs2 from "node:fs";
3891
+ import * as fs from "node:fs";
3806
3892
  var SQLITE_QUERY_ROW_CAP = 5e3;
3807
3893
  var SQLITE_MAGIC = Buffer.from([83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0]);
3808
3894
  function readMagicBytes(filePath) {
3809
3895
  let fd;
3810
3896
  try {
3811
- fd = fs2.openSync(filePath, "r");
3897
+ fd = fs.openSync(filePath, "r");
3812
3898
  } catch {
3813
3899
  return null;
3814
3900
  }
3815
3901
  try {
3816
3902
  const buf = Buffer.alloc(SQLITE_MAGIC.length);
3817
- const bytesRead = fs2.readSync(fd, buf, 0, buf.length, 0);
3903
+ const bytesRead = fs.readSync(fd, buf, 0, buf.length, 0);
3818
3904
  return bytesRead === buf.length ? buf : null;
3819
3905
  } catch {
3820
3906
  return null;
3821
3907
  } finally {
3822
- fs2.closeSync(fd);
3908
+ fs.closeSync(fd);
3823
3909
  }
3824
3910
  }
3825
3911
  function isSqliteFile(filePath) {
@@ -3827,7 +3913,7 @@ function isSqliteFile(filePath) {
3827
3913
  return magic !== null && magic.equals(SQLITE_MAGIC);
3828
3914
  }
3829
3915
  function openReadonlySqlite(filePath) {
3830
- if (!fs2.existsSync(filePath)) {
3916
+ if (!fs.existsSync(filePath)) {
3831
3917
  throw new Error(`file not found: ${filePath}`);
3832
3918
  }
3833
3919
  if (!isSqliteFile(filePath)) {
@@ -4530,8 +4616,8 @@ function formatConflictSummaries(summaries) {
4530
4616
 
4531
4617
  // src/screenshot.ts
4532
4618
  import dns from "node:dns/promises";
4533
- import fs3 from "node:fs";
4534
- import path3 from "node:path";
4619
+ import fs2 from "node:fs";
4620
+ import path2 from "node:path";
4535
4621
  var loadPuppeteer = createLazyModuleLoader(
4536
4622
  async () => await import("puppeteer-core"),
4537
4623
  "screenshot disabled (puppeteer-core unavailable)"
@@ -4540,7 +4626,7 @@ var PLAYWRIGHT_CHROME_SUBDIRS = ["chrome-win64", "chrome-win"];
4540
4626
  function findPlaywrightChromium(msPlaywrightDir) {
4541
4627
  let entries;
4542
4628
  try {
4543
- entries = fs3.readdirSync(msPlaywrightDir);
4629
+ entries = fs2.readdirSync(msPlaywrightDir);
4544
4630
  } catch {
4545
4631
  return [];
4546
4632
  }
@@ -4548,7 +4634,7 @@ function findPlaywrightChromium(msPlaywrightDir) {
4548
4634
  const candidates = [];
4549
4635
  for (const m of versioned) {
4550
4636
  for (const sub of PLAYWRIGHT_CHROME_SUBDIRS) {
4551
- candidates.push(path3.join(msPlaywrightDir, m[0], sub, "chrome.exe"));
4637
+ candidates.push(path2.join(msPlaywrightDir, m[0], sub, "chrome.exe"));
4552
4638
  }
4553
4639
  }
4554
4640
  return candidates;
@@ -4559,33 +4645,33 @@ function platformCandidatePaths() {
4559
4645
  const programFiles = process.env["PROGRAMFILES"];
4560
4646
  const programFilesX86 = process.env["PROGRAMFILES(X86)"];
4561
4647
  const localAppData = process.env["LOCALAPPDATA"];
4562
- if (programFiles) candidates2.push(path3.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"));
4563
- if (programFilesX86) candidates2.push(path3.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"));
4648
+ if (programFiles) candidates2.push(path2.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"));
4649
+ if (programFilesX86) candidates2.push(path2.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"));
4564
4650
  if (localAppData) {
4565
- candidates2.push(path3.join(localAppData, "Google", "Chrome", "Application", "chrome.exe"));
4566
- candidates2.push(...findPlaywrightChromium(path3.join(localAppData, "ms-playwright")));
4651
+ candidates2.push(path2.join(localAppData, "Google", "Chrome", "Application", "chrome.exe"));
4652
+ candidates2.push(...findPlaywrightChromium(path2.join(localAppData, "ms-playwright")));
4567
4653
  }
4568
4654
  return candidates2;
4569
4655
  }
4570
4656
  if (process.platform === "darwin") {
4571
4657
  const home2 = process.env["HOME"];
4572
4658
  const candidates2 = ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
4573
- if (home2) candidates2.push(...findPlaywrightChromium(path3.join(home2, "Library", "Caches", "ms-playwright")));
4659
+ if (home2) candidates2.push(...findPlaywrightChromium(path2.join(home2, "Library", "Caches", "ms-playwright")));
4574
4660
  return candidates2;
4575
4661
  }
4576
4662
  const home = process.env["HOME"];
4577
4663
  const candidates = ["/usr/bin/google-chrome-stable", "/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium"];
4578
- if (home) candidates.push(...findPlaywrightChromium(path3.join(home, ".cache", "ms-playwright")));
4664
+ if (home) candidates.push(...findPlaywrightChromium(path2.join(home, ".cache", "ms-playwright")));
4579
4665
  return candidates;
4580
4666
  }
4581
4667
  function resolveBrowserExecutablePath(explicit) {
4582
- if (explicit && fs3.existsSync(explicit)) return explicit;
4668
+ if (explicit && fs2.existsSync(explicit)) return explicit;
4583
4669
  const cfgPath = loadConfig().screenshot.chrome_path;
4584
- if (cfgPath && fs3.existsSync(cfgPath)) return cfgPath;
4670
+ if (cfgPath && fs2.existsSync(cfgPath)) return cfgPath;
4585
4671
  const envPath = process.env["TOKEN_GOAT_CHROME_PATH"];
4586
- if (envPath && fs3.existsSync(envPath)) return envPath;
4672
+ if (envPath && fs2.existsSync(envPath)) return envPath;
4587
4673
  for (const candidate of platformCandidatePaths()) {
4588
- if (fs3.existsSync(candidate)) return candidate;
4674
+ if (fs2.existsSync(candidate)) return candidate;
4589
4675
  }
4590
4676
  return null;
4591
4677
  }
@@ -4598,43 +4684,6 @@ function isBlockedIpv4Octets(a, b) {
4598
4684
  if (a === 0) return true;
4599
4685
  return false;
4600
4686
  }
4601
- function parseIpv6Groups(text) {
4602
- let rest = text;
4603
- const zoneAt = rest.indexOf("%");
4604
- if (zoneAt !== -1) rest = rest.slice(0, zoneAt);
4605
- if (!/^[0-9a-f:.]+$/i.test(rest) || !rest.includes(":")) return null;
4606
- const lastColon = rest.lastIndexOf(":");
4607
- const tail = rest.slice(lastColon + 1);
4608
- if (tail.includes(".")) {
4609
- const quad = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(tail);
4610
- if (!quad) return null;
4611
- const octets = [Number(quad[1]), Number(quad[2]), Number(quad[3]), Number(quad[4])];
4612
- if (octets.some((n) => n > 255)) return null;
4613
- const hi = octets[0] << 8 | octets[1];
4614
- const lo = octets[2] << 8 | octets[3];
4615
- rest = `${rest.slice(0, lastColon + 1)}${hi.toString(16)}:${lo.toString(16)}`;
4616
- }
4617
- const parseGroup = (g) => /^[0-9a-f]{1,4}$/i.test(g) ? parseInt(g, 16) : null;
4618
- const halves = rest.split("::");
4619
- if (halves.length > 2) return null;
4620
- let parts;
4621
- if (halves.length === 2) {
4622
- const left = halves[0].length > 0 ? halves[0].split(":") : [];
4623
- const right = halves[1].length > 0 ? halves[1].split(":") : [];
4624
- if (left.length + right.length > 7) return null;
4625
- parts = [...left, ...Array(8 - left.length - right.length).fill("0"), ...right];
4626
- } else {
4627
- parts = rest.split(":");
4628
- if (parts.length !== 8) return null;
4629
- }
4630
- const groups = [];
4631
- for (const part of parts) {
4632
- const value = parseGroup(part);
4633
- if (value === null) return null;
4634
- groups.push(value);
4635
- }
4636
- return groups;
4637
- }
4638
4687
  function isBlockedIpv6(groups) {
4639
4688
  if (groups.every((g) => g === 0)) return true;
4640
4689
  if (groups.slice(0, 7).every((g) => g === 0) && groups[7] === 1) return true;
@@ -4892,7 +4941,7 @@ async function takeScreenshot(url, destPath, opts) {
4892
4941
  }
4893
4942
 
4894
4943
  // src/notes.ts
4895
- import * as fs4 from "node:fs";
4944
+ import * as fs3 from "node:fs";
4896
4945
  var WHOLE_FILE_NOTE_SYMBOL = "";
4897
4946
  function toNoteRow(row) {
4898
4947
  return {
@@ -4917,7 +4966,7 @@ function resolveSymbolMatch(filePath, symbolName, dbPath = globalDbPath()) {
4917
4966
  }
4918
4967
  function bodyFromSource(entry) {
4919
4968
  try {
4920
- return fs4.readFileSync(entry.filePath, "utf8").split(/\r?\n/).slice(Math.max(0, entry.lineStart - 1), entry.lineEnd).join("\n");
4969
+ return fs3.readFileSync(entry.filePath, "utf8").split(/\r?\n/).slice(Math.max(0, entry.lineStart - 1), entry.lineEnd).join("\n");
4921
4970
  } catch {
4922
4971
  return "";
4923
4972
  }
@@ -4968,7 +5017,7 @@ function isNoteStale(note, dbPath = globalDbPath()) {
4968
5017
 
4969
5018
  // src/ts_refs.ts
4970
5019
  import { createRequire } from "node:module";
4971
- import * as path4 from "node:path";
5020
+ import * as path3 from "node:path";
4972
5021
  var _require = createRequire(import.meta.url);
4973
5022
  var MAX_CANDIDATE_FILES = 50;
4974
5023
  var _ts = null;
@@ -4999,7 +5048,7 @@ registerReset(() => {
4999
5048
  var TS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
5000
5049
  var TS_JS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
5001
5050
  function isTsPath(filePath) {
5002
- return TS_EXTENSIONS.has(path4.extname(filePath).toLowerCase());
5051
+ return TS_EXTENSIONS.has(path3.extname(filePath).toLowerCase());
5003
5052
  }
5004
5053
  function resolveTypedRefs(input) {
5005
5054
  const ts = loadTs();
@@ -5029,7 +5078,7 @@ function resolveTypedRefs(input) {
5029
5078
  if (defSymbol === null) return null;
5030
5079
  const out = [];
5031
5080
  for (const ref of input.candidates) {
5032
- if (!TS_JS_EXTENSIONS.has(path4.extname(ref.filePath).toLowerCase())) {
5081
+ if (!TS_JS_EXTENSIONS.has(path3.extname(ref.filePath).toLowerCase())) {
5033
5082
  out.push(ref);
5034
5083
  continue;
5035
5084
  }
@@ -5055,12 +5104,12 @@ function buildScopedProgram(ts, rootNames, searchFrom) {
5055
5104
  resolveJsonModule: true,
5056
5105
  esModuleInterop: true
5057
5106
  };
5058
- const configPath = ts.findConfigFile(path4.dirname(searchFrom), ts.sys.fileExists, "tsconfig.json");
5107
+ const configPath = ts.findConfigFile(path3.dirname(searchFrom), ts.sys.fileExists, "tsconfig.json");
5059
5108
  if (configPath !== void 0) {
5060
5109
  try {
5061
5110
  const raw = ts.readConfigFile(configPath, ts.sys.readFile);
5062
5111
  if (raw.error === void 0) {
5063
- const parsed = ts.parseJsonConfigFileContent(raw.config, ts.sys, path4.dirname(configPath));
5112
+ const parsed = ts.parseJsonConfigFileContent(raw.config, ts.sys, path3.dirname(configPath));
5064
5113
  Object.assign(options, parsed.options);
5065
5114
  }
5066
5115
  } catch {
@@ -5164,7 +5213,7 @@ var STRUCTURED_MISS_MAX_NODES = 2e4;
5164
5213
  var REFS_TOP_SCAN_LIMIT = 2e4;
5165
5214
  function fileExists(p) {
5166
5215
  try {
5167
- fs5.statSync(p);
5216
+ fs4.statSync(p);
5168
5217
  return true;
5169
5218
  } catch {
5170
5219
  return false;
@@ -5195,30 +5244,30 @@ function withPinnedReads(pins, fn) {
5195
5244
  }
5196
5245
  }
5197
5246
  function readPinnedBytes(p, pinned) {
5198
- const fd = fs5.openSync(p, fs5.constants.O_RDONLY);
5247
+ const fd = fs4.openSync(p, fs4.constants.O_RDONLY);
5199
5248
  try {
5200
- const actual = fileIdentity(fs5.fstatSync(fd, { bigint: true }));
5249
+ const actual = fileIdentity(fs4.fstatSync(fd, { bigint: true }));
5201
5250
  if (actual !== pinned) {
5202
5251
  throw new ConfinementIdentityError(
5203
5252
  `refused: "${p}" changed identity between validation and read (validated ${pinned}, opened ${actual}). The file was replaced or redirected after the confinement check, so the read was not performed.`
5204
5253
  );
5205
5254
  }
5206
- return fs5.readFileSync(fd);
5255
+ return fs4.readFileSync(fd);
5207
5256
  } finally {
5208
- fs5.closeSync(fd);
5257
+ fs4.closeSync(fd);
5209
5258
  }
5210
5259
  }
5211
5260
  function verifyPinnedIdentity(p, pinned) {
5212
- const fd = fs5.openSync(p, fs5.constants.O_RDONLY);
5261
+ const fd = fs4.openSync(p, fs4.constants.O_RDONLY);
5213
5262
  try {
5214
- const actual = fileIdentity(fs5.fstatSync(fd, { bigint: true }));
5263
+ const actual = fileIdentity(fs4.fstatSync(fd, { bigint: true }));
5215
5264
  if (actual !== pinned) {
5216
5265
  throw new ConfinementIdentityError(
5217
5266
  `refused: "${p}" changed identity between validation and read (validated ${pinned}, opened ${actual}). The file was replaced or redirected after the confinement check, so the read was not performed.`
5218
5267
  );
5219
5268
  }
5220
5269
  } finally {
5221
- fs5.closeSync(fd);
5270
+ fs4.closeSync(fd);
5222
5271
  }
5223
5272
  }
5224
5273
  function verifyStillAbsent(p) {
@@ -5236,7 +5285,7 @@ function verifyPin(p, pinned) {
5236
5285
  verifyPinnedIdentity(p, pinned);
5237
5286
  }
5238
5287
  function indexFileSyncPinned(resolvedPath, dbPath) {
5239
- const pinned = activePins?.get(pinKey(path5.resolve(resolvedPath)));
5288
+ const pinned = activePins?.get(pinKey(path4.resolve(resolvedPath)));
5240
5289
  if (pinned === void 0) {
5241
5290
  indexFileSync(resolvedPath, dbPath);
5242
5291
  return;
@@ -5258,21 +5307,21 @@ function indexFileSyncPinned(resolvedPath, dbPath) {
5258
5307
  indexFileSync(resolvedPath, dbPath, bytes);
5259
5308
  }
5260
5309
  function readFileText(p) {
5261
- const pinned = activePins?.get(pinKey(path5.resolve(p)));
5310
+ const pinned = activePins?.get(pinKey(path4.resolve(p)));
5262
5311
  try {
5263
5312
  if (pinned === ABSENT_PIN) {
5264
5313
  verifyStillAbsent(p);
5265
5314
  return null;
5266
5315
  }
5267
5316
  if (pinned !== void 0) return redactIfDotenv(p, decodeSource(readPinnedBytes(p, pinned)));
5268
- return redactIfDotenv(p, decodeSource(fs5.readFileSync(p)));
5317
+ return redactIfDotenv(p, decodeSource(fs4.readFileSync(p)));
5269
5318
  } catch (err) {
5270
5319
  if (err instanceof ConfinementIdentityError) throw err;
5271
5320
  return null;
5272
5321
  }
5273
5322
  }
5274
5323
  function readFileBytes(p) {
5275
- const pinned = activePins?.get(pinKey(path5.resolve(p)));
5324
+ const pinned = activePins?.get(pinKey(path4.resolve(p)));
5276
5325
  try {
5277
5326
  if (pinned === ABSENT_PIN) {
5278
5327
  verifyStillAbsent(p);
@@ -5283,9 +5332,9 @@ function readFileBytes(p) {
5283
5332
  if (bytes.length > MAX_ZIP_INPUT_BYTES) throw new ZipInputTooLargeError(p, bytes.length, MAX_ZIP_INPUT_BYTES);
5284
5333
  return bytes;
5285
5334
  }
5286
- const stat = fs5.statSync(p);
5335
+ const stat = fs4.statSync(p);
5287
5336
  if (stat.size > MAX_ZIP_INPUT_BYTES) throw new ZipInputTooLargeError(p, stat.size, MAX_ZIP_INPUT_BYTES);
5288
- return fs5.readFileSync(p);
5337
+ return fs4.readFileSync(p);
5289
5338
  } catch (err) {
5290
5339
  if (err instanceof ConfinementIdentityError || err instanceof ZipInputTooLargeError) throw err;
5291
5340
  return null;
@@ -5304,7 +5353,7 @@ var STALE_WARNING = "\u26A0 STALE: index is older than the file on disk (worker
5304
5353
  var DELETED_WARNING = "\u26A0 DELETED: this file is no longer on disk \u2014 what follows is what the index last saw of it";
5305
5354
  var DELETED_TAG = "\u26A0 DELETED: file no longer on disk";
5306
5355
  function fileIsGone(absPath) {
5307
- if (!path5.isAbsolute(absPath)) return false;
5356
+ if (!path4.isAbsolute(absPath)) return false;
5308
5357
  return fileIsAbsent(absPath);
5309
5358
  }
5310
5359
  function staleWarning(resolvedPath) {
@@ -5372,12 +5421,18 @@ function sumFileSizes(filePaths) {
5372
5421
  let total = 0;
5373
5422
  for (const fp of new Set(filePaths)) {
5374
5423
  try {
5375
- total += Math.min(fs5.statSync(fp).size, PER_FILE_COUNTERFACTUAL_CEILING);
5424
+ total += Math.min(fs4.statSync(fp).size, PER_FILE_COUNTERFACTUAL_CEILING);
5376
5425
  } catch {
5377
5426
  }
5378
5427
  }
5379
5428
  return total;
5380
5429
  }
5430
+ function refsSearchBaselineBytes(rows) {
5431
+ let total = 0;
5432
+ for (const ref of rows) total += Buffer.byteLength(`${refsDisplayPath(ref.filePath)}:${ref.line}: ${ref.context}
5433
+ `, "utf8");
5434
+ return total;
5435
+ }
5381
5436
  function recordReadStat(kind, fullSourceBytes, emittedText, detail) {
5382
5437
  const emittedBytes = Buffer.byteLength(emittedText, "utf8");
5383
5438
  const bytesSaved = Math.max(1, fullSourceBytes - emittedBytes);
@@ -5550,7 +5605,7 @@ function findStructuredKeyPath(name, filePaths) {
5550
5605
  if (!isYaml && !lower.endsWith(".json")) continue;
5551
5606
  let size;
5552
5607
  try {
5553
- size = fs5.statSync(filePath).size;
5608
+ size = fs4.statSync(filePath).size;
5554
5609
  } catch {
5555
5610
  continue;
5556
5611
  }
@@ -6088,7 +6143,7 @@ ${sub.text}`);
6088
6143
  return { text, code: 1 };
6089
6144
  }
6090
6145
  function resolveAgainstProjectRoot(file, projectRoot) {
6091
- return projectRoot !== void 0 && !path5.isAbsolute(file) ? path5.resolve(projectRoot, file) : file;
6146
+ return projectRoot !== void 0 && !path4.isAbsolute(file) ? path4.resolve(projectRoot, file) : file;
6092
6147
  }
6093
6148
  function literalHeadingExists(filePath, heading) {
6094
6149
  const ordinalMatch = /^([^#\r\n]+)#(\d+)$/.exec(heading);
@@ -6112,7 +6167,7 @@ function runSection(opts) {
6112
6167
  }
6113
6168
  const result = readSection(filePath, heading, readFileText);
6114
6169
  if (result === null) {
6115
- if (!fs5.existsSync(filePath)) {
6170
+ if (!fs4.existsSync(filePath)) {
6116
6171
  return { text: `File not found: '${filePath}'`, code: 1 };
6117
6172
  }
6118
6173
  const ordSpec = /^(.*?)#(\d+)$/.exec(heading);
@@ -6199,7 +6254,7 @@ function runSectionCrossFile(pairs, opts) {
6199
6254
  textBlocks.push(`${key}:
6200
6255
  ${sub.text}`);
6201
6256
  }
6202
- const resolvePath = (f) => opts.projectRoot !== void 0 && !path5.isAbsolute(f) ? path5.resolve(opts.projectRoot, f) : f;
6257
+ const resolvePath = (f) => opts.projectRoot !== void 0 && !path4.isAbsolute(f) ? path4.resolve(opts.projectRoot, f) : f;
6203
6258
  const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
6204
6259
  if (anyFound) {
6205
6260
  const fullSourceBytes = sumFileSizes(Array.from(distinctFiles, resolvePath));
@@ -6272,7 +6327,7 @@ function renderRefsTargets(targets, opts, {
6272
6327
  const jsonOut = {};
6273
6328
  let anyFound = false;
6274
6329
  const lines = [];
6275
- const refFilePaths = [];
6330
+ const refRows = [];
6276
6331
  for (const { file, symbol, key } of targets) {
6277
6332
  const queryOpts = { name: symbol };
6278
6333
  if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
@@ -6298,7 +6353,7 @@ function renderRefsTargets(targets, opts, {
6298
6353
  results = results.slice(0, opts.limit ?? 100);
6299
6354
  }
6300
6355
  if (results.length > 0) anyFound = true;
6301
- refFilePaths.push(...results.map((r) => r.filePath));
6356
+ refRows.push(...results);
6302
6357
  if (opts.json === true) {
6303
6358
  const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - (filteredTotal ?? results.length) : 0;
6304
6359
  const withHidden = (payload) => ({ ...payload, ...annotateHiddenByGrep && hiddenByGrep > 0 ? { hiddenByGrep } : {} });
@@ -6340,7 +6395,7 @@ function renderRefsTargets(targets, opts, {
6340
6395
  if (notice !== null) lines.push(` token-goat: ${notice}`);
6341
6396
  }
6342
6397
  }
6343
- const fullSourceBytes = sumFileSizes(refFilePaths);
6398
+ const fullSourceBytes = refsSearchBaselineBytes(refRows);
6344
6399
  if (opts.json === true) {
6345
6400
  const text2 = JSON.stringify(jsonOut, null, 2);
6346
6401
  emit(text2);
@@ -6449,7 +6504,7 @@ function runRefsSingle(opts) {
6449
6504
  }
6450
6505
  return 1;
6451
6506
  }
6452
- const fullSourceBytes = sumFileSizes(results.map((r) => r.filePath));
6507
+ const fullSourceBytes = refsSearchBaselineBytes(results);
6453
6508
  if (opts.json === true) {
6454
6509
  let payload;
6455
6510
  if (opts.top !== void 0) {
@@ -6522,7 +6577,7 @@ function renderCallerGroups(refs, contextLines = 0) {
6522
6577
  return lines;
6523
6578
  }
6524
6579
  function noSymbolsMessage(displayPath, resolvedPath) {
6525
- if (!fs5.existsSync(resolvedPath)) {
6580
+ if (!fs4.existsSync(resolvedPath)) {
6526
6581
  return `Could not read: ${displayPath}`;
6527
6582
  }
6528
6583
  const lang = unsupportedLanguageName(resolvedPath);
@@ -7361,10 +7416,10 @@ function runConflicts(opts) {
7361
7416
  if (opts.path === void 0) {
7362
7417
  files = walkProject(process.cwd()).files;
7363
7418
  } else {
7364
- const abs = path5.resolve(opts.path);
7419
+ const abs = path4.resolve(opts.path);
7365
7420
  let stat;
7366
7421
  try {
7367
- stat = fs5.statSync(abs);
7422
+ stat = fs4.statSync(abs);
7368
7423
  } catch {
7369
7424
  emitErr(`Could not read: ${opts.path}`);
7370
7425
  return 1;
@@ -7399,7 +7454,7 @@ async function runPdfExtractText(file, pagesSpec, layout = false) {
7399
7454
  if (!fileExists(file)) {
7400
7455
  throw new Error(`Could not read: ${file}`);
7401
7456
  }
7402
- const data = fs5.readFileSync(file);
7457
+ const data = fs4.readFileSync(file);
7403
7458
  const result = await extractPdfText(new Uint8Array(data), pagesSpec, layout);
7404
7459
  return result.text;
7405
7460
  }
@@ -7407,21 +7462,21 @@ async function runPdfOutline(file) {
7407
7462
  if (!fileExists(file)) {
7408
7463
  throw new Error(`Could not read: ${file}`);
7409
7464
  }
7410
- const data = fs5.readFileSync(file);
7465
+ const data = fs4.readFileSync(file);
7411
7466
  return extractPdfOutline(new Uint8Array(data));
7412
7467
  }
7413
7468
  async function runPdfMeta(file) {
7414
7469
  if (!fileExists(file)) {
7415
7470
  throw new Error(`Could not read: ${file}`);
7416
7471
  }
7417
- const data = fs5.readFileSync(file);
7472
+ const data = fs4.readFileSync(file);
7418
7473
  return extractPdfMeta(new Uint8Array(data));
7419
7474
  }
7420
7475
  async function runPdfLocate(file, pattern, opts) {
7421
7476
  if (!fileExists(file)) {
7422
7477
  throw new Error(`Could not read: ${file}`);
7423
7478
  }
7424
- const data = fs5.readFileSync(file);
7479
+ const data = fs4.readFileSync(file);
7425
7480
  return locatePdfPages(new Uint8Array(data), pattern, opts);
7426
7481
  }
7427
7482
  async function runImageMeta(file) {
@@ -7431,7 +7486,7 @@ async function runImageMeta(file) {
7431
7486
  if (!isImagePath(file)) {
7432
7487
  throw new Error(`Not an image file: ${file}`);
7433
7488
  }
7434
- const data = fs5.readFileSync(file);
7489
+ const data = fs4.readFileSync(file);
7435
7490
  const bytes = data.length;
7436
7491
  let probe;
7437
7492
  try {
@@ -7463,7 +7518,7 @@ async function runImageText(file) {
7463
7518
  if (!isImagePath(file)) {
7464
7519
  throw new Error(`Not an image file: ${file}`);
7465
7520
  }
7466
- const data = fs5.readFileSync(file);
7521
+ const data = fs4.readFileSync(file);
7467
7522
  const ocr = await ocrImage(data);
7468
7523
  if (ocr === null) {
7469
7524
  if (ocrIntegrityFailed()) {
@@ -8167,7 +8222,7 @@ function runGrep(opts) {
8167
8222
  if (activePins !== null) {
8168
8223
  let realDir;
8169
8224
  try {
8170
- realDir = foldRealpath(fs5.realpathSync(dir));
8225
+ realDir = foldRealpath(fs4.realpathSync(dir));
8171
8226
  } catch {
8172
8227
  return;
8173
8228
  }
@@ -8176,12 +8231,12 @@ function runGrep(opts) {
8176
8231
  visitedRealDirs.add(realDir);
8177
8232
  }
8178
8233
  try {
8179
- for (const entry of fs5.readdirSync(dir)) {
8234
+ for (const entry of fs4.readdirSync(dir)) {
8180
8235
  if (entry.startsWith(".")) continue;
8181
- const full = path5.join(dir, entry);
8236
+ const full = path4.join(dir, entry);
8182
8237
  let lst;
8183
8238
  try {
8184
- lst = fs5.lstatSync(full);
8239
+ lst = fs4.lstatSync(full);
8185
8240
  } catch {
8186
8241
  continue;
8187
8242
  }
@@ -8189,7 +8244,7 @@ function runGrep(opts) {
8189
8244
  if (lst.isSymbolicLink()) {
8190
8245
  let real;
8191
8246
  try {
8192
- real = fs5.realpathSync(full);
8247
+ real = fs4.realpathSync(full);
8193
8248
  } catch {
8194
8249
  continue;
8195
8250
  }
@@ -8198,7 +8253,7 @@ function runGrep(opts) {
8198
8253
  }
8199
8254
  let stat;
8200
8255
  try {
8201
- stat = fs5.statSync(target);
8256
+ stat = fs4.statSync(target);
8202
8257
  } catch {
8203
8258
  continue;
8204
8259
  }
@@ -8218,16 +8273,16 @@ function runGrep(opts) {
8218
8273
  emitErr(`Path not found: ${searchPath}`);
8219
8274
  return 1;
8220
8275
  }
8221
- const stat = fs5.statSync(searchPath);
8276
+ const stat = fs4.statSync(searchPath);
8222
8277
  if (stat.isDirectory()) {
8223
- const pinned = activePins?.get(pinKey(path5.resolve(searchPath)));
8278
+ const pinned = activePins?.get(pinKey(path4.resolve(searchPath)));
8224
8279
  if (pinned !== void 0) verifyPin(searchPath, pinned);
8225
8280
  let boundaryReal;
8226
8281
  try {
8227
- boundaryReal = fs5.realpathSync(searchPath);
8282
+ boundaryReal = fs4.realpathSync(searchPath);
8228
8283
  } catch (err) {
8229
8284
  if (pinned === void 0) {
8230
- boundaryReal = path5.resolve(searchPath);
8285
+ boundaryReal = path4.resolve(searchPath);
8231
8286
  } else {
8232
8287
  throw new ConfinementIdentityError(
8233
8288
  `refused: "${searchPath}" could not be resolved after validation (${String(err)}). The path may have been replaced or redirected after the confinement check, so the search was not performed.`
@@ -8358,7 +8413,7 @@ function runConfigGet(opts) {
8358
8413
  emit(value);
8359
8414
  return 0;
8360
8415
  }
8361
- const ext = path5.extname(opts.file).toLowerCase();
8416
+ const ext = path4.extname(opts.file).toLowerCase();
8362
8417
  if (ext === ".json") {
8363
8418
  try {
8364
8419
  let obj = JSON.parse(text);
@@ -8481,7 +8536,7 @@ function runExports(opts) {
8481
8536
  names.push(s.name);
8482
8537
  }
8483
8538
  }
8484
- const ext = path5.extname(opts.file).toLowerCase();
8539
+ const ext = path4.extname(opts.file).toLowerCase();
8485
8540
  const text = readFileText(diskPath);
8486
8541
  if (text === null && symbols.length === 0) {
8487
8542
  emitErr(`Could not read: ${opts.file}`);
@@ -8818,9 +8873,9 @@ function extractImports(text, ext) {
8818
8873
  return found;
8819
8874
  }
8820
8875
  function importsExtensionFor(filePath) {
8821
- const base = path5.basename(filePath).toLowerCase();
8876
+ const base = path4.basename(filePath).toLowerCase();
8822
8877
  if (base === "makefile" || base === "gnumakefile" || base === "bsdmakefile") return ".mk";
8823
- return path5.extname(filePath);
8878
+ return path4.extname(filePath);
8824
8879
  }
8825
8880
  function runImports(opts) {
8826
8881
  const multiFiles = parseMultiFileSpec(opts.file);
@@ -8912,7 +8967,7 @@ async function runSemantic(query, opts) {
8912
8967
  }
8913
8968
  const n = opts.limit !== void 0 && Number.isFinite(opts.limit) ? opts.limit : 20;
8914
8969
  if (opts.projectRoot !== void 0) {
8915
- if (!path5.isAbsolute(opts.projectRoot) || !fs5.existsSync(opts.projectRoot) || !fs5.statSync(opts.projectRoot).isDirectory()) {
8970
+ if (!path4.isAbsolute(opts.projectRoot) || !fs4.existsSync(opts.projectRoot) || !fs4.statSync(opts.projectRoot).isDirectory()) {
8916
8971
  const message = `token-goat: projectRoot must be an absolute, existing directory, got '${opts.projectRoot}'`;
8917
8972
  if (opts.json === true) {
8918
8973
  return { text: JSON.stringify({ error: message }, null, 2), code: 1 };
@@ -9150,6 +9205,318 @@ function runNoteList(opts = {}) {
9150
9205
  return { text: lines.join("\n"), code: 0 };
9151
9206
  }
9152
9207
 
9208
+ // src/import_graph.ts
9209
+ import * as fs5 from "node:fs";
9210
+ import * as path5 from "node:path";
9211
+ var DIRECT_PROBE_EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cjs", ".cts", ".py"];
9212
+ var INDEX_PROBE_EXTENSIONS = [".ts", ".js", ".tsx", ".jsx", ".mts", ".cts"];
9213
+ function buildImportGraph(cwd) {
9214
+ const files = getTrackedFiles(cwd);
9215
+ const filesByFoldedPath = /* @__PURE__ */ new Map();
9216
+ for (const f of files) filesByFoldedPath.set(foldPath(f), f);
9217
+ const resolve5 = (fromFile, spec) => {
9218
+ if (!spec.startsWith(".")) return null;
9219
+ const dir = path5.dirname(fromFile);
9220
+ const strippedSpec = spec.replace(/\.(m?js|cjs)$/, "");
9221
+ const base = path5.resolve(dir, strippedSpec);
9222
+ for (const ext of DIRECT_PROBE_EXTENSIONS) {
9223
+ const match = filesByFoldedPath.get(foldPath(base + ext));
9224
+ if (match !== void 0) return match;
9225
+ }
9226
+ const idx = path5.join(base, "index");
9227
+ for (const ext of INDEX_PROBE_EXTENSIONS) {
9228
+ const match = filesByFoldedPath.get(foldPath(idx + ext));
9229
+ if (match !== void 0) return match;
9230
+ }
9231
+ return null;
9232
+ };
9233
+ const graph = /* @__PURE__ */ new Map();
9234
+ const importedBy = /* @__PURE__ */ new Map();
9235
+ for (const file of files) {
9236
+ let text;
9237
+ try {
9238
+ text = fs5.readFileSync(file, "utf8");
9239
+ } catch {
9240
+ continue;
9241
+ }
9242
+ const internal = [];
9243
+ for (const spec of extractImports(text, importsExtensionFor(file))) {
9244
+ const resolved = resolve5(file, spec);
9245
+ if (resolved === null) continue;
9246
+ internal.push(resolved);
9247
+ let importers = importedBy.get(resolved);
9248
+ if (importers === void 0) {
9249
+ importers = /* @__PURE__ */ new Set();
9250
+ importedBy.set(resolved, importers);
9251
+ }
9252
+ importers.add(file);
9253
+ }
9254
+ graph.set(file, internal);
9255
+ }
9256
+ return { files, graph, importedBy, resolve: resolve5 };
9257
+ }
9258
+
9259
+ // src/modules.ts
9260
+ var WEAK_MODULARITY = 0.3;
9261
+ var MAX_PASSES = 32;
9262
+ var GAIN_EPSILON = 1e-12;
9263
+ function emptyGraph(n) {
9264
+ return { n, adj: Array.from({ length: n }, () => /* @__PURE__ */ new Map()), degree: new Array(n).fill(0), m: 0 };
9265
+ }
9266
+ function addEdge(g, a, b, w) {
9267
+ if (a === b) {
9268
+ g.adj[a].set(a, (g.adj[a].get(a) ?? 0) + w);
9269
+ g.degree[a] += 2 * w;
9270
+ } else {
9271
+ g.adj[a].set(b, (g.adj[a].get(b) ?? 0) + w);
9272
+ g.adj[b].set(a, (g.adj[b].get(a) ?? 0) + w);
9273
+ g.degree[a] += w;
9274
+ g.degree[b] += w;
9275
+ }
9276
+ g.m += w;
9277
+ }
9278
+ function localMoving(g) {
9279
+ const community = Array.from({ length: g.n }, (_, i) => i);
9280
+ const tot = g.degree.slice();
9281
+ const twoM = 2 * g.m;
9282
+ let movedEver = false;
9283
+ if (twoM === 0) return { community, moved: false };
9284
+ for (let pass = 0; pass < MAX_PASSES; pass++) {
9285
+ let improved = false;
9286
+ for (let i = 0; i < g.n; i++) {
9287
+ const ki = g.degree[i];
9288
+ const ci = community[i];
9289
+ const links = /* @__PURE__ */ new Map();
9290
+ for (const [j, w] of g.adj[i]) {
9291
+ if (j === i) continue;
9292
+ const cj = community[j];
9293
+ links.set(cj, (links.get(cj) ?? 0) + w);
9294
+ }
9295
+ tot[ci] = tot[ci] - ki;
9296
+ let bestC = ci;
9297
+ let bestGain = (links.get(ci) ?? 0) - tot[ci] * ki / twoM;
9298
+ for (const c of [...links.keys()].sort((a, b) => a - b)) {
9299
+ if (c === ci) continue;
9300
+ const gain = links.get(c) - tot[c] * ki / twoM;
9301
+ if (gain > bestGain + GAIN_EPSILON) {
9302
+ bestGain = gain;
9303
+ bestC = c;
9304
+ }
9305
+ }
9306
+ tot[bestC] = tot[bestC] + ki;
9307
+ community[i] = bestC;
9308
+ if (bestC !== ci) {
9309
+ improved = true;
9310
+ movedEver = true;
9311
+ }
9312
+ }
9313
+ if (!improved) break;
9314
+ }
9315
+ return { community, moved: movedEver };
9316
+ }
9317
+ function densify(community) {
9318
+ const firstMember = /* @__PURE__ */ new Map();
9319
+ for (let i = 0; i < community.length; i++) {
9320
+ const c = community[i];
9321
+ if (!firstMember.has(c)) firstMember.set(c, i);
9322
+ }
9323
+ const ordered = [...firstMember.entries()].sort((a, b) => a[1] - b[1]).map(([c]) => c);
9324
+ const remap = /* @__PURE__ */ new Map();
9325
+ ordered.forEach((c, idx) => remap.set(c, idx));
9326
+ return { dense: community.map((c) => remap.get(c)), count: ordered.length };
9327
+ }
9328
+ function modularityOf(g, partition) {
9329
+ const twoM = 2 * g.m;
9330
+ if (twoM === 0) return null;
9331
+ const internal = /* @__PURE__ */ new Map();
9332
+ const tot = /* @__PURE__ */ new Map();
9333
+ for (let i = 0; i < g.n; i++) {
9334
+ const ci = partition[i];
9335
+ tot.set(ci, (tot.get(ci) ?? 0) + g.degree[i]);
9336
+ for (const [j, w] of g.adj[i]) {
9337
+ if (j < i) continue;
9338
+ if (partition[j] === ci) internal.set(ci, (internal.get(ci) ?? 0) + w);
9339
+ }
9340
+ }
9341
+ let q = 0;
9342
+ for (const [c, t] of tot) {
9343
+ q += 2 * (internal.get(c) ?? 0) / twoM - (t / twoM) ** 2;
9344
+ }
9345
+ return q;
9346
+ }
9347
+ function louvain(g) {
9348
+ let current = g;
9349
+ let mapping = Array.from({ length: g.n }, (_, i) => i);
9350
+ for (let level = 0; level < MAX_PASSES; level++) {
9351
+ const { community, moved } = localMoving(current);
9352
+ const { dense, count } = densify(community);
9353
+ mapping = mapping.map((c) => dense[c]);
9354
+ if (!moved || count === current.n) break;
9355
+ const next = emptyGraph(count);
9356
+ for (let i = 0; i < current.n; i++) {
9357
+ for (const [j, w] of current.adj[i]) {
9358
+ if (j < i) continue;
9359
+ addEdge(next, dense[i], dense[j], w);
9360
+ }
9361
+ }
9362
+ current = next;
9363
+ }
9364
+ return mapping;
9365
+ }
9366
+ function commonDirLabel(files) {
9367
+ const dirs = new Set(files.map(dirOf));
9368
+ if (dirs.size === 1) {
9369
+ const only = [...dirs][0];
9370
+ return only === "." ? "." : `${only}/`;
9371
+ }
9372
+ const segmentLists = files.map((f) => {
9373
+ const parts = f.split("/");
9374
+ parts.pop();
9375
+ return parts;
9376
+ });
9377
+ const first = segmentLists[0];
9378
+ if (first === void 0) return null;
9379
+ const prefix = [];
9380
+ for (let i = 0; i < first.length; i++) {
9381
+ const seg = first[i];
9382
+ if (!segmentLists.every((s) => s[i] === seg)) break;
9383
+ prefix.push(seg);
9384
+ }
9385
+ return prefix.length === 0 ? null : `${prefix.join("/")}/`;
9386
+ }
9387
+ function dirOf(file) {
9388
+ const idx = file.lastIndexOf("/");
9389
+ return idx === -1 ? "." : file.slice(0, idx);
9390
+ }
9391
+ function detectModules(imports, cwd, top) {
9392
+ const { files, graph } = imports;
9393
+ const root = getDisplayRoot(cwd);
9394
+ const degreeOf = /* @__PURE__ */ new Map();
9395
+ const bump = (f) => {
9396
+ degreeOf.set(f, (degreeOf.get(f) ?? 0) + 1);
9397
+ };
9398
+ for (const [from, targets] of graph) {
9399
+ for (const to of targets) {
9400
+ if (from === to) continue;
9401
+ bump(from);
9402
+ bump(to);
9403
+ }
9404
+ }
9405
+ const connected = files.filter((f) => (degreeOf.get(f) ?? 0) > 0);
9406
+ const isolatedCount = files.length - connected.length;
9407
+ if (connected.length === 0) {
9408
+ return { modules: [], modulesTotal: 0, modularity: null, isolatedCount, crossImports: [], crossImportsTotal: 0, noEdges: true };
9409
+ }
9410
+ const ordered = [...connected].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
9411
+ const idOf = /* @__PURE__ */ new Map();
9412
+ ordered.forEach((f, i) => idOf.set(f, i));
9413
+ const g = emptyGraph(ordered.length);
9414
+ const pairWeight = /* @__PURE__ */ new Map();
9415
+ for (const [from, targets] of graph) {
9416
+ const a = idOf.get(from);
9417
+ if (a === void 0) continue;
9418
+ for (const to of targets) {
9419
+ const b = idOf.get(to);
9420
+ if (b === void 0 || a === b) continue;
9421
+ const key = a < b ? `${a}:${b}` : `${b}:${a}`;
9422
+ pairWeight.set(key, (pairWeight.get(key) ?? 0) + 1);
9423
+ }
9424
+ }
9425
+ for (const key of [...pairWeight.keys()].sort()) {
9426
+ const [a, b] = key.split(":").map(Number);
9427
+ addEdge(g, a, b, pairWeight.get(key));
9428
+ }
9429
+ const partition = louvain(g);
9430
+ const modularity = modularityOf(g, partition);
9431
+ const membersOf = /* @__PURE__ */ new Map();
9432
+ for (let i = 0; i < ordered.length; i++) {
9433
+ const c = partition[i];
9434
+ const list = membersOf.get(c);
9435
+ if (list === void 0) membersOf.set(c, [ordered[i]]);
9436
+ else list.push(ordered[i]);
9437
+ }
9438
+ const inModuleDegree = /* @__PURE__ */ new Map();
9439
+ for (const [from, targets] of graph) {
9440
+ const a = idOf.get(from);
9441
+ if (a === void 0) continue;
9442
+ for (const to of targets) {
9443
+ const b = idOf.get(to);
9444
+ if (b === void 0 || a === b || partition[a] !== partition[b]) continue;
9445
+ inModuleDegree.set(from, (inModuleDegree.get(from) ?? 0) + 1);
9446
+ inModuleDegree.set(to, (inModuleDegree.get(to) ?? 0) + 1);
9447
+ }
9448
+ }
9449
+ const built = [...membersOf.entries()].map(([c, absFiles]) => {
9450
+ const display = absFiles.map((f) => toDisplayPath(root, f)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
9451
+ const coreAbs = [...absFiles].sort((a, b) => (inModuleDegree.get(b) ?? 0) - (inModuleDegree.get(a) ?? 0) || (a < b ? -1 : a > b ? 1 : 0))[0];
9452
+ return { community: c, core: toDisplayPath(root, coreAbs), commonDir: commonDirLabel(display), files: display, directories: new Set(display.map(dirOf)).size };
9453
+ }).sort((a, b) => b.files.length - a.files.length || (a.core < b.core ? -1 : a.core > b.core ? 1 : 0) || (a.files[0] < b.files[0] ? -1 : 1));
9454
+ const indexOfCommunity = /* @__PURE__ */ new Map();
9455
+ built.forEach((m, i) => indexOfCommunity.set(m.community, i + 1));
9456
+ const coreOfIndex = /* @__PURE__ */ new Map();
9457
+ built.forEach((m, i) => coreOfIndex.set(i + 1, m.core));
9458
+ const crossCount = /* @__PURE__ */ new Map();
9459
+ for (const [from, targets] of graph) {
9460
+ const a = idOf.get(from);
9461
+ if (a === void 0) continue;
9462
+ const ca = indexOfCommunity.get(partition[a]);
9463
+ if (ca === void 0) continue;
9464
+ for (const to of targets) {
9465
+ const b = idOf.get(to);
9466
+ if (b === void 0) continue;
9467
+ const cb = indexOfCommunity.get(partition[b]);
9468
+ if (cb === void 0 || ca === cb) continue;
9469
+ const key = `${ca}:${cb}`;
9470
+ crossCount.set(key, (crossCount.get(key) ?? 0) + 1);
9471
+ }
9472
+ }
9473
+ const allCross = [...crossCount.entries()].map(([key, imports2]) => {
9474
+ const [fromIndex, toIndex] = key.split(":").map(Number);
9475
+ return { fromIndex, toIndex, fromCore: coreOfIndex.get(fromIndex), toCore: coreOfIndex.get(toIndex), imports: imports2 };
9476
+ }).sort((a, b) => b.imports - a.imports || a.fromIndex - b.fromIndex || a.toIndex - b.toIndex);
9477
+ const modules = built.slice(0, top).map((m, i) => ({ index: i + 1, core: m.core, commonDir: m.commonDir, files: m.files, size: m.files.length, directories: m.directories }));
9478
+ return {
9479
+ modules,
9480
+ modulesTotal: built.length,
9481
+ modularity,
9482
+ isolatedCount,
9483
+ crossImports: allCross.slice(0, top),
9484
+ crossImportsTotal: allCross.length,
9485
+ noEdges: false
9486
+ };
9487
+ }
9488
+ function renderModules(result, top) {
9489
+ const lines = [];
9490
+ if (result.noEdges) {
9491
+ lines.push(`modules: none. No file in this project imports another by a relative path, so there is nothing to group (${countNoun(result.isolatedCount, "file")} checked).`);
9492
+ return lines;
9493
+ }
9494
+ const q = result.modularity;
9495
+ const strength = q === null ? "" : q < WEAK_MODULARITY ? `, modularity ${q.toFixed(2)} -- weak, so treat this grouping as a hint rather than a finding` : `, modularity ${q.toFixed(2)}`;
9496
+ lines.push(result.modules.length < result.modulesTotal ? `modules (top ${result.modules.length} of ${result.modulesTotal}${strength}):` : `modules (${result.modulesTotal} found${strength}):`);
9497
+ for (const m of result.modules) {
9498
+ const where = m.commonDir === null ? `across ${m.directories} directories` : m.commonDir === "." ? "all in the repository root" : `all in ${m.commonDir}`;
9499
+ lines.push(` #${m.index} ${m.size} files ${m.core} (${where})`);
9500
+ }
9501
+ if (result.isolatedCount > 0) {
9502
+ lines.push(` ${result.isolatedCount} files are in no module (they neither import nor are imported within the project).`);
9503
+ }
9504
+ if (result.crossImportsTotal === 0) {
9505
+ lines.push("cross-module imports: none. Every import stays inside its own module.");
9506
+ } else {
9507
+ lines.push(
9508
+ result.crossImports.length < result.crossImportsTotal ? `cross-module imports (top ${result.crossImports.length} of ${result.crossImportsTotal} pairs):` : `cross-module imports (${result.crossImportsTotal} pairs):`
9509
+ );
9510
+ for (const c of result.crossImports) {
9511
+ lines.push(` ${c.imports} imports #${c.fromIndex} ${c.fromCore} -> #${c.toIndex} ${c.toCore}`);
9512
+ }
9513
+ }
9514
+ if (result.modules.length < result.modulesTotal && result.crossImports.some((c) => c.fromIndex > top || c.toIndex > top)) {
9515
+ lines.push(` (a #N above ${top} refers to a module the --top cap left out; raise --top to see it.)`);
9516
+ }
9517
+ return lines;
9518
+ }
9519
+
9153
9520
  // src/graph_commands.ts
9154
9521
  var DEFAULT_REF_QUERY_LIMIT = 500;
9155
9522
  var ALL_SYMBOLS_IN_FILE_LIMIT = 1e4;
@@ -10237,53 +10604,11 @@ function runArch(opts) {
10237
10604
  }
10238
10605
  const cwd = opts.cwd ?? process.cwd();
10239
10606
  const top = opts.top ?? 10;
10240
- const files = getTrackedFiles(cwd);
10607
+ const { files, graph, importedBy, resolve: resolve5 } = buildImportGraph(cwd);
10241
10608
  if (files.length === 0 && opts.json !== true) {
10242
10609
  emit2(`no tracked files found under '${toDisplayPath(getDisplayRoot(cwd), cwd)}' (is it a git repo?). Nothing to analyse.`);
10243
10610
  return 0;
10244
10611
  }
10245
- const filesByFoldedPath = /* @__PURE__ */ new Map();
10246
- for (const f of files) filesByFoldedPath.set(foldPath(f), f);
10247
- const graph = /* @__PURE__ */ new Map();
10248
- const importedBy = /* @__PURE__ */ new Map();
10249
- const resolveRelImport = (fromFile, spec) => {
10250
- if (!spec.startsWith(".")) return null;
10251
- const dir = path6.dirname(fromFile);
10252
- const strippedSpec = spec.replace(/\.(m?js|cjs)$/, "");
10253
- const base = path6.resolve(dir, strippedSpec);
10254
- for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cjs", ".cts", ".py"]) {
10255
- const candidate = base + ext;
10256
- const match = filesByFoldedPath.get(foldPath(candidate));
10257
- if (match !== void 0) return match;
10258
- }
10259
- const idx = path6.join(base, "index");
10260
- for (const ext of [".ts", ".js", ".tsx", ".jsx", ".mts", ".cts"]) {
10261
- const candidate = idx + ext;
10262
- const match = filesByFoldedPath.get(foldPath(candidate));
10263
- if (match !== void 0) return match;
10264
- }
10265
- return null;
10266
- };
10267
- for (const file of files) {
10268
- let text;
10269
- try {
10270
- text = fs6.readFileSync(file, "utf8");
10271
- } catch {
10272
- continue;
10273
- }
10274
- const ext = importsExtensionFor(file);
10275
- const rawImports = extractImports(text, ext);
10276
- const internal = [];
10277
- for (const spec of rawImports) {
10278
- const resolved = resolveRelImport(file, spec);
10279
- if (resolved !== null) {
10280
- internal.push(resolved);
10281
- if (!importedBy.has(resolved)) importedBy.set(resolved, /* @__PURE__ */ new Set());
10282
- importedBy.get(resolved).add(file);
10283
- }
10284
- }
10285
- graph.set(file, internal);
10286
- }
10287
10612
  const allHubs = [...importedBy.entries()].sort((a, b) => b[1].size - a[1].size).map(([f, importers]) => ({ file: f, importedBy: importers.size }));
10288
10613
  const hubsTotal = allHubs.length;
10289
10614
  const hubs = allHubs.slice(0, top);
@@ -10291,8 +10616,10 @@ function runArch(opts) {
10291
10616
  const entryPointsTotal = allEntryPoints.length;
10292
10617
  const entryPoints = allEntryPoints.slice(0, top);
10293
10618
  const { cycles, truncated: cyclesTruncated } = findCyclesCapped(graph);
10619
+ const moduleResult = opts.modules === true ? detectModules({ files, graph, importedBy, resolve: resolve5 }, cwd, top) : null;
10294
10620
  if (opts.json === true) {
10295
- emit2(JSON.stringify({ hubs, hubsTotal, hubsTruncated: hubs.length < hubsTotal, entryPoints, entryPointsTotal, entryPointsTruncated: entryPoints.length < entryPointsTotal, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {} }, null, 2));
10621
+ const modulePayload = moduleResult === null ? {} : { modules: moduleResult.modules, modulesTotal: moduleResult.modulesTotal, modulesTruncated: moduleResult.modules.length < moduleResult.modulesTotal, modularity: moduleResult.modularity, isolatedCount: moduleResult.isolatedCount, crossImports: moduleResult.crossImports, crossImportsTotal: moduleResult.crossImportsTotal, crossImportsTruncated: moduleResult.crossImports.length < moduleResult.crossImportsTotal, noImportEdges: moduleResult.noEdges };
10622
+ emit2(JSON.stringify({ hubs, hubsTotal, hubsTruncated: hubs.length < hubsTotal, entryPoints, entryPointsTotal, entryPointsTruncated: entryPoints.length < entryPointsTotal, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {}, ...modulePayload }, null, 2));
10296
10623
  return 0;
10297
10624
  }
10298
10625
  emit2(hubs.length < hubsTotal ? `hubs (top ${hubs.length} of ${hubsTotal} most-imported):` : `hubs (${hubsTotal} most-imported):`);
@@ -10301,6 +10628,7 @@ function runArch(opts) {
10301
10628
  for (const e of entryPoints) emit2(` ${toDisplayPath(getDisplayRoot(opts.cwd), e.file)}`);
10302
10629
  emit2(cyclesTruncated ? `cycles (first ${cycles.length}, truncated at the ${MAX_CYCLES}-cycle enumeration limit: more cycles exist):` : `cycles (${cycles.length} found):`);
10303
10630
  for (const c of cycles) emit2(` ${c.map((f) => toDisplayPath(getDisplayRoot(opts.cwd), f)).join(" -> ")}`);
10631
+ if (moduleResult !== null) for (const line of renderModules(moduleResult, top)) emit2(line);
10304
10632
  return 0;
10305
10633
  }
10306
10634
  function runBlame(opts) {
@@ -10556,10 +10884,10 @@ function resolveHandoff(name, opts = {}) {
10556
10884
  }
10557
10885
 
10558
10886
  export {
10559
- getTrackedFiles,
10560
10887
  extractSection,
10561
10888
  readSection,
10562
10889
  listSections,
10890
+ buildImportGraph,
10563
10891
  ALL_SYMBOLS_IN_FILE_LIMIT,
10564
10892
  enclosingSymbol,
10565
10893
  runCallers,
@@ -10651,5 +10979,5 @@ export {
10651
10979
  /*! Bundled license information:
10652
10980
 
10653
10981
  js-yaml/dist/js-yaml.mjs:
10654
- (*! js-yaml 5.3.0 https://github.com/nodeca/js-yaml @license MIT *)
10982
+ (*! js-yaml 5.4.1 https://github.com/nodeca/js-yaml @license MIT *)
10655
10983
  */