token-goat 2.6.36 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +36 -8
  2. package/SECURITY.md +46 -17
  3. package/dist/{token-goat-chunk-UFOVM7ZN.mjs → token-goat-chunk-2G6RAB4G.mjs} +1391 -378
  4. package/dist/token-goat-chunk-324QOJYZ.mjs +91 -0
  5. package/dist/token-goat-chunk-5CVKO3DA.mjs +185 -0
  6. package/dist/{token-goat-chunk-65BKISIS.mjs → token-goat-chunk-73MF6YWW.mjs} +4 -4
  7. package/dist/{token-goat-chunk-MGOUYAA2.mjs → token-goat-chunk-7JDXDERZ.mjs} +1 -1
  8. package/dist/{token-goat-hook-chunk-BDR6C6IE.mjs → token-goat-chunk-AOBZUFNJ.mjs} +318 -51
  9. package/dist/{token-goat-chunk-V465YKOR.mjs → token-goat-chunk-ELDJRLHZ.mjs} +492 -28
  10. package/dist/{token-goat-chunk-CNDOJ3ZP.mjs → token-goat-chunk-I6TOUPLP.mjs} +2 -2
  11. package/dist/token-goat-chunk-R4SR7MQY.mjs +486 -0
  12. package/dist/{token-goat-chunk-IYTVE6KN.mjs → token-goat-chunk-RMDQFTQD.mjs} +226 -150
  13. package/dist/{token-goat-chunk-KYFJC37X.mjs → token-goat-chunk-VBXBLGTO.mjs} +792 -144
  14. package/dist/{token-goat-chunk-DG53MVNJ.mjs → token-goat-chunk-WN5T5EW5.mjs} +212 -212
  15. package/dist/{token-goat-chunk-VYMGEVZS.mjs → token-goat-chunk-XEDQH5DA.mjs} +317 -11
  16. package/dist/{token-goat-chunk-LN6OUHTV.mjs → token-goat-chunk-Y2SYNH3S.mjs} +5 -5
  17. package/dist/token-goat-chunk-YUGNM3KL.mjs +23 -0
  18. package/dist/token-goat-hook.mjs +7 -7
  19. package/dist/token-goat.core.mjs +5 -5
  20. package/package.json +12 -8
  21. package/dist/token-goat-chunk-FRTBMRP7.mjs +0 -10048
  22. package/dist/token-goat-hook-chunk-3QYSN4QV.mjs +0 -14764
  23. package/dist/token-goat-hook-chunk-3ZDBWJDF.mjs +0 -13659
  24. package/dist/token-goat-hook-chunk-5UH54CW6.mjs +0 -912
  25. package/dist/token-goat-hook-chunk-6ODM3MP7.mjs +0 -706
  26. package/dist/token-goat-hook-chunk-A77A26A7.mjs +0 -18184
  27. package/dist/token-goat-hook-chunk-BUOCULAM.mjs +0 -29
  28. package/dist/token-goat-hook-chunk-C6GIABOX.mjs +0 -15971
  29. package/dist/token-goat-hook-chunk-E257IGSN.mjs +0 -153
  30. package/dist/token-goat-hook-chunk-MW5HPEGD.mjs +0 -10411
  31. package/dist/token-goat-hook-chunk-QSCYNJ2B.mjs +0 -23
  32. package/dist/token-goat-hook-chunk-RFRLWOQH.mjs +0 -11
  33. package/dist/token-goat-hook-chunk-XUMIVYEN.mjs +0 -109
  34. package/dist/token-goat-hook-chunk-Y2WHX2P3.mjs +0 -6463
@@ -2,6 +2,7 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  IMPORT_RE,
5
+ ImageDecodeError,
5
6
  MAX_OVER_FETCH,
6
7
  OVER_FETCH_FACTOR,
7
8
  SKIP_DIRS,
@@ -21,12 +22,15 @@ import {
21
22
  formatCsvTable,
22
23
  getFileEntry,
23
24
  indexFileSync,
25
+ isAvailable,
24
26
  isBlobStale,
25
27
  isImagePath,
26
28
  isIndexEmptyForProject,
29
+ isOcrEngineAvailable,
27
30
  isTextHeavy,
28
31
  lineOpenDelimiterAfter,
29
32
  loadBlob,
33
+ locatePdfPages,
30
34
  mergeNearbyHits,
31
35
  ocrImage,
32
36
  parseWhereSpecs,
@@ -50,8 +54,9 @@ import {
50
54
  walkProject,
51
55
  yamlLineClosesQuote,
52
56
  yamlOpenQuoteAfter
53
- } from "./token-goat-chunk-UFOVM7ZN.mjs";
57
+ } from "./token-goat-chunk-2G6RAB4G.mjs";
54
58
  import {
59
+ Database,
55
60
  PER_FILE_COUNTERFACTUAL_CEILING,
56
61
  _detectOpenQuote,
57
62
  _lineClosesQuote,
@@ -101,7 +106,7 @@ import {
101
106
  unsupportedLanguageName,
102
107
  windowsCmdQuoteArg,
103
108
  withExtension
104
- } from "./token-goat-chunk-V465YKOR.mjs";
109
+ } from "./token-goat-chunk-ELDJRLHZ.mjs";
105
110
  import {
106
111
  registerReset
107
112
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -320,7 +325,15 @@ function resolveHeaderPos(headers, base, ordinal) {
320
325
  const pick2 = ordinal === null ? 0 : ordinal - 1;
321
326
  const headerPos = matches[pick2];
322
327
  if (headerPos === void 0) return null;
323
- return { headerPos, redirectedFrom: null };
328
+ let occurrences = null;
329
+ if (ordinal === null && matches.length > 1) {
330
+ occurrences = [];
331
+ for (const i of matches) {
332
+ const h = headers[i];
333
+ if (h !== void 0) occurrences.push(h.index + 1);
334
+ }
335
+ }
336
+ return { headerPos, redirectedFrom: null, occurrences };
324
337
  }
325
338
  if (ordinal !== null || normalizedTarget.length === 0) return null;
326
339
  let prefixPos = -1;
@@ -337,7 +350,7 @@ function resolveHeaderPos(headers, base, ordinal) {
337
350
  if (distinct.size === 1 && prefixPos !== -1) {
338
351
  const chosen = headers[prefixPos];
339
352
  if (chosen === void 0) return null;
340
- return { headerPos: prefixPos, redirectedFrom: base };
353
+ return { headerPos: prefixPos, redirectedFrom: base, occurrences: null };
341
354
  }
342
355
  const queryWords = normalizedTarget.split(/\s+/).filter((w) => w.length >= MIN_WIDEN_WORD_LEN);
343
356
  if (queryWords.length === 0) return null;
@@ -352,7 +365,7 @@ function resolveHeaderPos(headers, base, ordinal) {
352
365
  if (widenedMatches.length !== 1) return null;
353
366
  const widenedPos = widenedMatches[0];
354
367
  if (widenedPos === void 0) return null;
355
- return { headerPos: widenedPos, redirectedFrom: base };
368
+ return { headerPos: widenedPos, redirectedFrom: base, occurrences: null };
356
369
  }
357
370
  function buildSectionResult(headers, kind, lines, headerPos, redirectedFrom) {
358
371
  const header = headers[headerPos];
@@ -378,7 +391,9 @@ function resolveSectionFromText(text, headingSpec, language) {
378
391
  const lines = text.split("\n");
379
392
  const resolved = resolveHeaderPos(headers, base, ordinal);
380
393
  if (resolved === null) return null;
381
- return buildSectionResult(headers, kind, lines, resolved.headerPos, resolved.redirectedFrom);
394
+ const built = buildSectionResult(headers, kind, lines, resolved.headerPos, resolved.redirectedFrom);
395
+ if (built === null || resolved.occurrences === null) return built;
396
+ return { ...built, occurrences: resolved.occurrences };
382
397
  }
383
398
  function extractSection(text, headingSpec) {
384
399
  return resolveSectionFromText(text, headingSpec, "unknown");
@@ -3439,6 +3454,33 @@ function mergeParameters(pathLevelParams, ownParams) {
3439
3454
  });
3440
3455
  return [...inheritedParams, ...ownParams];
3441
3456
  }
3457
+ function resolveJsonPointer(root, ref) {
3458
+ const parts = ref.slice(2).split("/").map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
3459
+ let cur = root;
3460
+ for (const part of parts) {
3461
+ if (typeof cur !== "object" || cur === null) return null;
3462
+ cur = cur[part];
3463
+ if (cur === void 0) return null;
3464
+ }
3465
+ return cur;
3466
+ }
3467
+ function dereferenceLocalRefs(node, root, seen) {
3468
+ if (Array.isArray(node)) return node.map((item) => dereferenceLocalRefs(item, root, seen));
3469
+ if (typeof node !== "object" || node === null) return node;
3470
+ const rec = node;
3471
+ const ref = rec["$ref"];
3472
+ if (typeof ref === "string" && ref.startsWith("#/")) {
3473
+ if (seen.has(ref)) return node;
3474
+ const target = resolveJsonPointer(root, ref);
3475
+ if (target === void 0 || target === null) return node;
3476
+ return dereferenceLocalRefs(target, root, /* @__PURE__ */ new Set([...seen, ref]));
3477
+ }
3478
+ const out = {};
3479
+ for (const [key, value] of Object.entries(rec)) {
3480
+ out[key] = dereferenceLocalRefs(value, root, seen);
3481
+ }
3482
+ return out;
3483
+ }
3442
3484
  function extractOperations(spec) {
3443
3485
  if (typeof spec !== "object" || spec === null) return [];
3444
3486
  const paths = spec["paths"];
@@ -3461,9 +3503,9 @@ function extractOperations(spec) {
3461
3503
  ...typeof op["summary"] === "string" ? { summary: op["summary"] } : {},
3462
3504
  ...typeof op["description"] === "string" ? { description: op["description"] } : {},
3463
3505
  ...tags !== void 0 && tags.length > 0 ? { tags } : {},
3464
- parameters: mergeParameters(pathLevelParams, ownParams),
3465
- ...op["requestBody"] !== void 0 ? { requestBody: op["requestBody"] } : {},
3466
- responses: typeof op["responses"] === "object" && op["responses"] !== null ? op["responses"] : {}
3506
+ parameters: dereferenceLocalRefs(mergeParameters(pathLevelParams, ownParams), spec, /* @__PURE__ */ new Set()),
3507
+ ...op["requestBody"] !== void 0 ? { requestBody: dereferenceLocalRefs(op["requestBody"], spec, /* @__PURE__ */ new Set()) } : {},
3508
+ responses: typeof op["responses"] === "object" && op["responses"] !== null ? dereferenceLocalRefs(op["responses"], spec, /* @__PURE__ */ new Set()) : {}
3467
3509
  });
3468
3510
  }
3469
3511
  }
@@ -3745,7 +3787,6 @@ ${c.body}`).join("\n\n");
3745
3787
 
3746
3788
  // src/sqlite_query.ts
3747
3789
  import * as fs2 from "node:fs";
3748
- import Database from "better-sqlite3";
3749
3790
  var SQLITE_QUERY_ROW_CAP = 5e3;
3750
3791
  var SQLITE_MAGIC = Buffer.from([83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0]);
3751
3792
  function readMagicBytes(filePath) {
@@ -4930,7 +4971,7 @@ function loadTs() {
4930
4971
  }
4931
4972
  return _ts;
4932
4973
  }
4933
- function isAvailable() {
4974
+ function isAvailable2() {
4934
4975
  return loadTs() !== null;
4935
4976
  }
4936
4977
  function loadError() {
@@ -5094,6 +5135,8 @@ function findIdentifierNearPosition(ts, sourceFile, line0, position, name) {
5094
5135
 
5095
5136
  // src/read_commands.ts
5096
5137
  var DIDYOUMEAN_LIMIT = 5;
5138
+ var SYMBOL_PREVIEW_LINES = 5;
5139
+ var AMBIGUOUS_HEADING_LIMIT = 10;
5097
5140
  var TYPO_TWO_EDIT_MIN_LEN = 8;
5098
5141
  var TYPO_MAX_QUERY_LEN = 64;
5099
5142
  var MIN_REVERSE_MATCH_LEN = 3;
@@ -5382,13 +5425,14 @@ function filterSimilarHeadings(available, query) {
5382
5425
  return sortByLengthCloseness(matched, query);
5383
5426
  }
5384
5427
  function didYouMean(candidates) {
5385
- if (candidates.length === 0) return "";
5428
+ const unique = [...new Set(candidates)];
5429
+ if (unique.length === 0) return "";
5386
5430
  const lines = ["Did you mean:"];
5387
- for (const c of candidates.slice(0, DIDYOUMEAN_LIMIT)) {
5431
+ for (const c of unique.slice(0, DIDYOUMEAN_LIMIT)) {
5388
5432
  lines.push(` - ${c}`);
5389
5433
  }
5390
- if (candidates.length > DIDYOUMEAN_LIMIT) {
5391
- lines.push(` (${candidates.length - DIDYOUMEAN_LIMIT} more not shown)`);
5434
+ if (unique.length > DIDYOUMEAN_LIMIT) {
5435
+ lines.push(` (${unique.length - DIDYOUMEAN_LIMIT} more not shown)`);
5392
5436
  }
5393
5437
  return lines.join("\n");
5394
5438
  }
@@ -5644,9 +5688,13 @@ ${emptyIndexMessage(emptyIndexRoot)}`;
5644
5688
  const goneTag = fileIsGone(sym.filePath) ? ` ${DELETED_TAG}` : "";
5645
5689
  const header = `# ${sym.name} (${sym.kind}) \u2014 ${toDisplayPath(symbolDisplayRoot, sym.filePath)}:${sym.lineStart}-${sym.lineEnd}${statsStr}${goneTag}`;
5646
5690
  const body = resolveBody(sym);
5647
- const preview = body.split(/\r?\n/).slice(0, 5).join("\n");
5691
+ const bodyLines = body.split(/\r?\n/);
5692
+ const preview = bodyLines.slice(0, SYMBOL_PREVIEW_LINES).join("\n");
5693
+ const dropped = bodyLines.length - SYMBOL_PREVIEW_LINES;
5694
+ const elided = dropped > 0 ? `
5695
+ ...(${countNoun(dropped, "more line")}; full body: token-goat read "${toDisplayPath(symbolDisplayRoot, sym.filePath)}::${sym.name}")` : "";
5648
5696
  return preview.trim() !== "" ? `${header}
5649
- ${preview}` : header;
5697
+ ${preview}${elided}` : header;
5650
5698
  });
5651
5699
  const warning = opts.file !== void 0 ? staleWarning(resolveIndexPath(opts.file, opts.projectRoot ?? process.cwd())) : "";
5652
5700
  const text = guardText(warning + blocks.join("\n\n"), "symbol");
@@ -6018,6 +6066,18 @@ function runSection(opts) {
6018
6066
  if (!fs5.existsSync(filePath)) {
6019
6067
  return { text: `File not found: '${filePath}'`, code: 1 };
6020
6068
  }
6069
+ const ordSpec = /^(.*?)#(\d+)$/.exec(heading);
6070
+ const ordBase = ordSpec?.[1]?.trim();
6071
+ if (ordBase !== void 0 && ordBase.length > 0) {
6072
+ const baseResult = readSection(filePath, ordBase, readFileText);
6073
+ if (baseResult !== null) {
6074
+ const total = baseResult.occurrences?.length ?? 1;
6075
+ return {
6076
+ text: `Heading '${ordBase}' has ${countNoun(total, "occurrence")} in '${specFilePath}'; valid ordinals are #1 to #${total}`,
6077
+ code: 1
6078
+ };
6079
+ }
6080
+ }
6021
6081
  const messages = [`Section '${heading}' not found in '${filePath}'`];
6022
6082
  const allHeadings = listSections(filePath, readFileText);
6023
6083
  const available = filterSimilarHeadings(allHeadings, heading);
@@ -6026,6 +6086,18 @@ function runSection(opts) {
6026
6086
  else messages.push(`Try: token-goat outline ${specFilePath}`);
6027
6087
  return { text: messages.join("\n"), code: 1 };
6028
6088
  }
6089
+ if (result.occurrences !== void 0) {
6090
+ const lines = [
6091
+ `Ambiguous heading '${heading}' in '${specFilePath}': ${countNoun(result.occurrences.length, "heading")} match. Retry with one of the qualified commands below to pick one:`
6092
+ ];
6093
+ for (const [i, line] of result.occurrences.slice(0, AMBIGUOUS_HEADING_LIMIT).entries()) {
6094
+ lines.push(` - line ${line} -> token-goat section "${specFilePath}::${heading}#${i + 1}"`);
6095
+ }
6096
+ if (result.occurrences.length > AMBIGUOUS_HEADING_LIMIT) {
6097
+ lines.push(` (${result.occurrences.length - AMBIGUOUS_HEADING_LIMIT} more not shown)`);
6098
+ }
6099
+ return { text: lines.join("\n"), code: 1 };
6100
+ }
6029
6101
  const kind = result.redirectedFrom !== void 0 ? "section_replacement" : "section_read";
6030
6102
  const fullSourceBytes = sumFileSizes([filePath]);
6031
6103
  if (opts.json === true) {
@@ -6145,40 +6217,15 @@ function refsSpecFiles(spec) {
6145
6217
  const { file } = parseMultiRefsSpec(spec);
6146
6218
  return file === void 0 ? [] : [file];
6147
6219
  }
6148
- function runRefs(opts) {
6149
- if (opts.limit !== void 0 && opts.limit <= 0) {
6150
- emitErr(`--limit must be a positive number, got: ${opts.limit}`);
6151
- return 1;
6152
- }
6153
- if (opts.top !== void 0 && opts.top <= 0) {
6154
- emitErr(`--top must be a positive number, got: ${opts.top}`);
6155
- return 1;
6156
- }
6157
- const confinedRoot = confinedProjectRoot();
6158
- if (confinedRoot !== null) {
6159
- const projectDenial = opts.projectRoot === void 0 ? null : confinementRefusal("--project", opts.projectRoot, confinedRoot);
6160
- if (projectDenial !== null) {
6161
- emitErr(projectDenial);
6162
- return 1;
6163
- }
6164
- for (const file2 of refsSpecFiles(opts.spec)) {
6165
- const denial = confinementRefusal("This file", resolveIndexPath(file2, opts.projectRoot ?? process.cwd()), confinedRoot);
6166
- if (denial !== null) {
6167
- emitErr(denial);
6168
- return 1;
6169
- }
6170
- }
6171
- }
6172
- const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
6173
- if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
6174
- const { file, symbols } = parseMultiRefsSpec(opts.spec);
6175
- if (symbols.length <= 1) return runRefsSingle(opts);
6220
+ function renderRefsTargets(targets, opts, {
6221
+ annotateHiddenByGrep
6222
+ }) {
6176
6223
  const jsonOut = {};
6177
6224
  let anyFound = false;
6178
6225
  const lines = [];
6179
6226
  const refFilePaths = [];
6180
- for (const sym of symbols) {
6181
- const queryOpts = { name: sym };
6227
+ for (const { file, symbol, key } of targets) {
6228
+ const queryOpts = { name: symbol };
6182
6229
  if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
6183
6230
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
6184
6231
  else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
@@ -6186,7 +6233,7 @@ function runRefs(opts) {
6186
6233
  if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6187
6234
  const scanned = queryRefs(queryOpts);
6188
6235
  const preScanCount = scanned.length;
6189
- let results = applyTypedRefsTier(sym, file, scanned);
6236
+ let results = applyTypedRefsTier(symbol, file, scanned);
6190
6237
  let suppressed = 0;
6191
6238
  if (opts.excludeTests === true) {
6192
6239
  const f = applyExcludeTestsFilter(results);
@@ -6205,25 +6252,25 @@ function runRefs(opts) {
6205
6252
  refFilePaths.push(...results.map((r) => r.filePath));
6206
6253
  if (opts.json === true) {
6207
6254
  const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - (filteredTotal ?? results.length) : 0;
6208
- const withHidden = (payload) => ({ ...payload, ...hiddenByGrep > 0 ? { hiddenByGrep } : {} });
6255
+ const withHidden = (payload) => ({ ...payload, ...annotateHiddenByGrep && hiddenByGrep > 0 ? { hiddenByGrep } : {} });
6209
6256
  if (opts.top !== void 0) {
6210
- jsonOut[sym] = withHidden(topFilesJsonPayload(results, opts.top));
6257
+ jsonOut[key] = withHidden(topFilesJsonPayload(results, opts.top));
6211
6258
  } else {
6212
6259
  const capped = guardJsonRows(results);
6213
6260
  const trueTotal = opts.excludeTests === true || matchesGrep !== void 0 ? filteredTotal ?? results.length : countRefs(queryOpts);
6214
- jsonOut[sym] = withHidden({ items: refsJsonItems(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal });
6261
+ jsonOut[key] = withHidden({ items: refsJsonItems(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal });
6215
6262
  }
6216
6263
  continue;
6217
6264
  }
6218
6265
  if (results.length === 0) {
6219
6266
  if (matchesGrep !== void 0 && preGrepCount > 0) {
6220
- lines.push(`${sym}: ${grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references").trim()}`);
6267
+ lines.push(`${key}: ${grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references").trim()}`);
6221
6268
  continue;
6222
6269
  }
6223
- lines.push(opts.excludeTests === true && suppressed > 0 ? `${sym}: (no non-test references found; ${excludeTestsHiddenNote(suppressed)})` : `${sym}: (no references found)`);
6270
+ lines.push(opts.excludeTests === true && suppressed > 0 ? `${key}: (no non-test references found; ${excludeTestsHiddenNote(suppressed)})` : `${key}: (no references found)`);
6224
6271
  continue;
6225
6272
  }
6226
- lines.push(`${sym}:`);
6273
+ lines.push(`${key}:`);
6227
6274
  if (opts.top !== void 0) {
6228
6275
  lines.push(...renderTopFilesSummary(results, opts.top, suppressed));
6229
6276
  } else if (opts.callers === true) {
@@ -6256,90 +6303,50 @@ function runRefs(opts) {
6256
6303
  if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
6257
6304
  return anyFound ? 0 : 1;
6258
6305
  }
6259
- function runRefsCrossFile(pairs, opts) {
6260
- const distinctFiles = new Set(pairs.map((p) => p.file));
6261
- const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
6262
- const jsonOut = {};
6263
- let anyFound = false;
6264
- const lines = [];
6265
- const refFilePaths = [];
6266
- for (const { file, symbol } of pairs) {
6267
- const key = keyFor({ file, symbol });
6268
- const queryOpts = { name: symbol };
6269
- if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
6270
- else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
6271
- else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
6272
- const rootDir = refsRootDir(opts);
6273
- if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6274
- const scanned = queryRefs(queryOpts);
6275
- const preScanCount = scanned.length;
6276
- let results = applyTypedRefsTier(symbol, file, scanned);
6277
- let suppressed = 0;
6278
- if (opts.excludeTests === true) {
6279
- const f = applyExcludeTestsFilter(results);
6280
- suppressed = f.suppressed;
6281
- results = f.refs;
6282
- }
6283
- const preGrepCount = results.length;
6284
- const matchesGrep = refGrepFilter(opts.grep);
6285
- if (matchesGrep !== void 0) results = results.filter(matchesGrep);
6286
- let filteredTotal;
6287
- if (opts.excludeTests === true || matchesGrep !== void 0) filteredTotal = results.length;
6288
- if ((opts.excludeTests === true || matchesGrep !== void 0) && opts.top === void 0) {
6289
- results = results.slice(0, opts.limit ?? 100);
6290
- }
6291
- if (results.length > 0) anyFound = true;
6292
- refFilePaths.push(...results.map((r) => r.filePath));
6293
- if (opts.json === true) {
6294
- if (opts.top !== void 0) {
6295
- jsonOut[key] = topFilesJsonPayload(results, opts.top);
6296
- } else {
6297
- const capped = guardJsonRows(results);
6298
- const trueTotal = opts.excludeTests === true || matchesGrep !== void 0 ? filteredTotal ?? results.length : countRefs(queryOpts);
6299
- jsonOut[key] = { items: refsJsonItems(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
6300
- }
6301
- continue;
6306
+ function runRefs(opts) {
6307
+ if (opts.limit !== void 0 && opts.limit <= 0) {
6308
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
6309
+ return 1;
6310
+ }
6311
+ if (opts.top !== void 0 && opts.top <= 0) {
6312
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
6313
+ return 1;
6314
+ }
6315
+ const confinedRoot = confinedProjectRoot();
6316
+ if (confinedRoot !== null) {
6317
+ const projectDenial = opts.projectRoot === void 0 ? null : confinementRefusal("--project", opts.projectRoot, confinedRoot);
6318
+ if (projectDenial !== null) {
6319
+ emitErr(projectDenial);
6320
+ return 1;
6302
6321
  }
6303
- if (results.length === 0) {
6304
- if (matchesGrep !== void 0 && preGrepCount > 0) {
6305
- lines.push(`${key}: ${grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references").trim()}`);
6306
- continue;
6322
+ for (const file2 of refsSpecFiles(opts.spec)) {
6323
+ const denial = confinementRefusal("This file", resolveIndexPath(file2, opts.projectRoot ?? process.cwd()), confinedRoot);
6324
+ if (denial !== null) {
6325
+ emitErr(denial);
6326
+ return 1;
6307
6327
  }
6308
- lines.push(opts.excludeTests === true && suppressed > 0 ? `${key}: (no non-test references found; ${excludeTestsHiddenNote(suppressed)})` : `${key}: (no references found)`);
6309
- continue;
6310
- }
6311
- lines.push(`${key}:`);
6312
- if (opts.top !== void 0) {
6313
- lines.push(...renderTopFilesSummary(results, opts.top, suppressed));
6314
- } else if (opts.callers === true) {
6315
- if (opts.excludeTests === true && suppressed > 0) lines.push(` ${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`);
6316
- lines.push(...renderCallerGroups(results, opts.context ?? 0));
6317
- } else {
6318
- if (opts.excludeTests === true && suppressed > 0) lines.push(` ${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`);
6319
- for (const ref of results) lines.push(...renderRefLines(ref, opts.context ?? 0));
6320
6328
  }
6321
- if (opts.top === void 0) {
6322
- const notice = truncationNotice(
6323
- results.length,
6324
- opts.limit ?? 100,
6325
- () => refsTotal(opts.excludeTests === true || matchesGrep !== void 0, filteredTotal, results.length, () => countRefs(queryOpts), preScanCount),
6326
- "references",
6327
- "--limit"
6328
- );
6329
- if (notice !== null) lines.push(` token-goat: ${notice}`);
6330
- }
6331
- }
6332
- const fullSourceBytes = sumFileSizes(refFilePaths);
6333
- if (opts.json === true) {
6334
- const text2 = JSON.stringify(jsonOut, null, 2);
6335
- emit(text2);
6336
- if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text2, opts.spec);
6337
- return anyFound ? 0 : 1;
6338
6329
  }
6339
- const text = lines.join("\n");
6340
- emitGuarded(text, "symbol");
6341
- if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
6342
- return anyFound ? 0 : 1;
6330
+ const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
6331
+ if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
6332
+ const { file, symbols } = parseMultiRefsSpec(opts.spec);
6333
+ if (symbols.length <= 1) return runRefsSingle(opts);
6334
+ return renderRefsTargets(
6335
+ symbols.map((symbol) => ({ file, symbol, key: symbol })),
6336
+ opts,
6337
+ { annotateHiddenByGrep: true }
6338
+ );
6339
+ }
6340
+ function runRefsCrossFile(pairs, opts) {
6341
+ const distinctFiles = new Set(pairs.map((p) => p.file));
6342
+ const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
6343
+ return renderRefsTargets(
6344
+ pairs.map((p) => ({ file: p.file, symbol: p.symbol, key: keyFor(p) })),
6345
+ opts,
6346
+ // Not annotated today; renderRefsTargets's own option doc explains why that is a preserved
6347
+ // divergence rather than a decision.
6348
+ { annotateHiddenByGrep: false }
6349
+ );
6343
6350
  }
6344
6351
  function runRefsSingle(opts) {
6345
6352
  const { file, symbol } = parseReadSpec(opts.spec);
@@ -6801,8 +6808,29 @@ function runQueryCommand(opts, parse, formatLabel, guardTag, kind) {
6801
6808
  function runJsonQuery(opts) {
6802
6809
  return runQueryCommand(opts, JSON.parse, "JSON", "json-query", "json_query");
6803
6810
  }
6811
+ function isPlainObject2(v) {
6812
+ return typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
6813
+ }
6814
+ function resolveYamlMergeKeys(node) {
6815
+ if (Array.isArray(node)) return node.map(resolveYamlMergeKeys);
6816
+ if (!isPlainObject2(node)) return node;
6817
+ const merged = {};
6818
+ const mergeVal = node["<<"];
6819
+ if (mergeVal !== void 0) {
6820
+ const sources = Array.isArray(mergeVal) ? mergeVal : [mergeVal];
6821
+ for (const src of [...sources].reverse()) {
6822
+ const resolved = resolveYamlMergeKeys(src);
6823
+ if (isPlainObject2(resolved)) Object.assign(merged, resolved);
6824
+ }
6825
+ }
6826
+ for (const [key, value] of Object.entries(node)) {
6827
+ if (key === "<<") continue;
6828
+ merged[key] = resolveYamlMergeKeys(value);
6829
+ }
6830
+ return merged;
6831
+ }
6804
6832
  function parseYamlDocument(text) {
6805
- const docs = loadAll(text);
6833
+ const docs = loadAll(text).map(resolveYamlMergeKeys);
6806
6834
  return docs.length === 1 ? docs[0] : docs;
6807
6835
  }
6808
6836
  function runYamlOutline(opts) {
@@ -7305,6 +7333,13 @@ async function runPdfMeta(file) {
7305
7333
  const data = fs5.readFileSync(file);
7306
7334
  return extractPdfMeta(new Uint8Array(data));
7307
7335
  }
7336
+ async function runPdfLocate(file, pattern, opts) {
7337
+ if (!fileExists(file)) {
7338
+ throw new Error(`Could not read: ${file}`);
7339
+ }
7340
+ const data = fs5.readFileSync(file);
7341
+ return locatePdfPages(new Uint8Array(data), pattern, opts);
7342
+ }
7308
7343
  async function runImageMeta(file) {
7309
7344
  if (!fileExists(file)) {
7310
7345
  throw new Error(`Could not read: ${file}`);
@@ -7314,7 +7349,15 @@ async function runImageMeta(file) {
7314
7349
  }
7315
7350
  const data = fs5.readFileSync(file);
7316
7351
  const bytes = data.length;
7317
- const probe = await probeImageMeta(data);
7352
+ let probe;
7353
+ try {
7354
+ probe = await probeImageMeta(data);
7355
+ } catch (e) {
7356
+ if (e instanceof ImageDecodeError) {
7357
+ throw new Error(`${file} is not a readable image: ${e.message}`, { cause: e });
7358
+ }
7359
+ throw e;
7360
+ }
7318
7361
  if (probe === null) {
7319
7362
  return { width: 0, height: 0, format: null, bytes, sharpAvailable: false, wouldShrink: false, shrunkBytes: null };
7320
7363
  }
@@ -7339,6 +7382,9 @@ async function runImageText(file) {
7339
7382
  const data = fs5.readFileSync(file);
7340
7383
  const ocr = await ocrImage(data);
7341
7384
  if (ocr === null) {
7385
+ if (isOcrEngineAvailable()) {
7386
+ throw new Error(`${file} could not be processed by OCR (unreadable image, timeout, or offline model fetch)`);
7387
+ }
7342
7388
  return { ocrAvailable: false, confidence: 0, chars: 0, textHeavy: false, text: null };
7343
7389
  }
7344
7390
  const minConfidence = loadConfig().image_shrink.ocr_min_confidence;
@@ -7569,7 +7615,15 @@ function runListSections(opts) {
7569
7615
  }
7570
7616
  const preFilterCount = sections.length;
7571
7617
  const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
7572
- const filtered = matchesGrep !== void 0 ? sections.filter((s) => matchesGrep(s)) : sections;
7618
+ const totals = /* @__PURE__ */ new Map();
7619
+ for (const heading of sections) totals.set(heading, (totals.get(heading) ?? 0) + 1);
7620
+ const seen = /* @__PURE__ */ new Map();
7621
+ const labelled = sections.map((heading) => {
7622
+ const nth = (seen.get(heading) ?? 0) + 1;
7623
+ seen.set(heading, nth);
7624
+ return (totals.get(heading) ?? 1) > 1 ? `${heading}#${nth}` : heading;
7625
+ });
7626
+ const filtered = matchesGrep !== void 0 ? labelled.filter((_, i) => matchesGrep(sections[i] ?? "")) : labelled;
7573
7627
  if (filtered.length === 0) {
7574
7628
  if (opts.json === true) {
7575
7629
  emit(JSON.stringify({ items: [], truncated: false, totalCount: 0 }, null, 2));
@@ -7583,8 +7637,8 @@ function runListSections(opts) {
7583
7637
  emit(JSON.stringify({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }, null, 2));
7584
7638
  return 0;
7585
7639
  }
7586
- for (const s of filtered) {
7587
- emit(s);
7640
+ for (const heading of filtered) {
7641
+ emit(heading);
7588
7642
  }
7589
7643
  return 0;
7590
7644
  }
@@ -8781,15 +8835,27 @@ async function runSemantic(query, opts) {
8781
8835
  }
8782
8836
  }
8783
8837
  const rootDir = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
8838
+ if (!isAvailable()) {
8839
+ console.warn(
8840
+ "Matching on meaning is off (onnxruntime-node is not installed); these results come from keyword search alone. Install it with: npm install -g onnxruntime-node (drop -g if token-goat is a project dependency)"
8841
+ );
8842
+ }
8784
8843
  const overFetchForMerge = Math.min(MAX_OVER_FETCH, n * OVER_FETCH_FACTOR);
8785
- const rawHits = await searchSemantic(
8786
- getDb(globalDbPath()),
8787
- query,
8788
- overFetchForMerge,
8789
- void 0,
8790
- void 0,
8791
- rootDir
8792
- );
8844
+ let rawHits = [];
8845
+ try {
8846
+ rawHits = await searchSemantic(
8847
+ getDb(globalDbPath()),
8848
+ query,
8849
+ overFetchForMerge,
8850
+ void 0,
8851
+ void 0,
8852
+ rootDir
8853
+ );
8854
+ } catch (e) {
8855
+ console.warn(
8856
+ `Matching on meaning is off (${extractErrorMessage(e)}); these results come from keyword search alone.`
8857
+ );
8858
+ }
8793
8859
  const mergedHits = mergeNearbyHits(rawHits);
8794
8860
  const overFetchFts = Math.min(MAX_OVER_FETCH, n * OVER_FETCH_FACTOR);
8795
8861
  const ftsRows = searchSymbolsFts(query, overFetchFts, void 0, rootDir);
@@ -9761,6 +9827,14 @@ function runScope(opts) {
9761
9827
  const filePath = resolveIndexPath(file);
9762
9828
  const syms = querySymbols({ filePath, limit: ALL_SYMBOLS_IN_FILE_LIMIT });
9763
9829
  const enclosing = syms.filter((s) => s.lineStart <= line && line <= s.lineEnd).sort((a, b) => b.lineStart - a.lineStart);
9830
+ if (syms.length === 0) {
9831
+ if (!fs6.existsSync(filePath)) {
9832
+ emitErr2(`Could not read: ${file}`);
9833
+ return 1;
9834
+ }
9835
+ emitErr2(`No indexed symbols in '${file}' \u2014 the file exists but nothing is indexed for it, so every line looks empty`);
9836
+ return 1;
9837
+ }
9764
9838
  if (enclosing.length === 0) {
9765
9839
  emitErr2(`No symbols enclosing line ${line} in '${file}'`);
9766
9840
  return 1;
@@ -10096,9 +10170,9 @@ function runBlame(opts) {
10096
10170
  }
10097
10171
  if (opts.json === true) {
10098
10172
  const lines = raw.split("\n").filter((l) => l.length > 0).map((l) => {
10099
- const m = /^([0-9a-f]+)\s+\((.+?)\s+(\d{4}-\d{2}-\d{2}[^)]*)\s+(\d+)\)(.*)/.exec(l);
10173
+ const m = /^(\^?)([0-9a-f]+)\s+\((.+?)\s+(\d{4}-\d{2}-\d{2}[^)]*)\s+(\d+)\)(.*)/.exec(l);
10100
10174
  if (!m) return { raw: l };
10101
- return { commit: m[1], author: (m[2] ?? "").trim(), date: (m[3] ?? "").trim(), line: Number.parseInt(m[4] ?? "0", 10), content: m[5] };
10175
+ return { commit: m[2], boundary: m[1] === "^", author: (m[3] ?? "").trim(), date: (m[4] ?? "").trim(), line: Number.parseInt(m[5] ?? "0", 10), content: m[6] };
10102
10176
  });
10103
10177
  emit2(JSON.stringify({ symbol: sym.name, file: filePath, lines }, null, 2));
10104
10178
  return 0;
@@ -10340,8 +10414,9 @@ export {
10340
10414
  symbolNamesInFile,
10341
10415
  computeFileFingerprint,
10342
10416
  upsertNote,
10343
- isAvailable,
10417
+ isAvailable2 as isAvailable,
10344
10418
  loadError,
10419
+ AMBIGUOUS_HEADING_LIMIT,
10345
10420
  ConfinementIdentityError,
10346
10421
  ABSENT_PIN,
10347
10422
  pinKey,
@@ -10381,6 +10456,7 @@ export {
10381
10456
  runPdfExtractText,
10382
10457
  runPdfOutline,
10383
10458
  runPdfMeta,
10459
+ runPdfLocate,
10384
10460
  runImageMeta,
10385
10461
  runImageText,
10386
10462
  runScreenshot,