token-goat 2.6.35 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +35 -7
  2. package/SECURITY.md +47 -16
  3. package/dist/{token-goat-chunk-I2KCEGIP.mjs → token-goat-chunk-2F6TFBZE.mjs} +1355 -408
  4. package/dist/token-goat-chunk-324QOJYZ.mjs +91 -0
  5. package/dist/{token-goat-chunk-K7W3P2TJ.mjs → token-goat-chunk-44Y77VHR.mjs} +425 -39
  6. package/dist/{token-goat-chunk-UIZVX3HN.mjs → token-goat-chunk-4KZILRZN.mjs} +2 -2
  7. package/dist/token-goat-chunk-5CVKO3DA.mjs +185 -0
  8. package/dist/token-goat-chunk-A62K4XW2.mjs +23 -0
  9. package/dist/{token-goat-chunk-SIHYQCTM.mjs → token-goat-chunk-AO6MFFTW.mjs} +331 -13
  10. package/dist/{token-goat-chunk-DRAIYVUI.mjs → token-goat-chunk-FBGBTICM.mjs} +4 -4
  11. package/dist/{token-goat-chunk-YF67EWRH.mjs → token-goat-chunk-J35AKWEQ.mjs} +5 -5
  12. package/dist/{token-goat-hook-chunk-4POU6OHK.mjs → token-goat-chunk-LVCBDJVE.mjs} +318 -51
  13. package/dist/token-goat-chunk-R4SR7MQY.mjs +486 -0
  14. package/dist/{token-goat-chunk-D5IZEGN2.mjs → token-goat-chunk-SRAR6DOK.mjs} +144 -147
  15. package/dist/{token-goat-hook-chunk-NIPXIAPA.mjs → token-goat-chunk-TUPJRK7R.mjs} +1 -1
  16. package/dist/{token-goat-chunk-HW4VUKJ5.mjs → token-goat-chunk-VXSYZGBA.mjs} +589 -141
  17. package/dist/{token-goat-chunk-DG53MVNJ.mjs → token-goat-chunk-WN5T5EW5.mjs} +212 -212
  18. package/dist/token-goat-hook.mjs +7 -7
  19. package/dist/token-goat.core.mjs +5 -5
  20. package/package.json +18 -15
  21. package/scripts/install-git-hooks.mjs +55 -0
  22. package/dist/token-goat-chunk-DQIT5JIF.mjs +0 -13659
  23. package/dist/token-goat-chunk-Y2NH6DGZ.mjs +0 -10048
  24. package/dist/token-goat-hook-chunk-2O7P4Z6Q.mjs +0 -6419
  25. package/dist/token-goat-hook-chunk-3AAH72SO.mjs +0 -15970
  26. package/dist/token-goat-hook-chunk-3QYSN4QV.mjs +0 -14764
  27. package/dist/token-goat-hook-chunk-BUOCULAM.mjs +0 -29
  28. package/dist/token-goat-hook-chunk-CRBMPT74.mjs +0 -900
  29. package/dist/token-goat-hook-chunk-D6UJEFHX.mjs +0 -153
  30. package/dist/token-goat-hook-chunk-J4HKWUWZ.mjs +0 -23
  31. package/dist/token-goat-hook-chunk-NXVT4F3S.mjs +0 -10406
  32. package/dist/token-goat-hook-chunk-RFRLWOQH.mjs +0 -11
  33. package/dist/token-goat-hook-chunk-VU6VGZBM.mjs +0 -706
  34. package/dist/token-goat-hook-chunk-XM4W3WCO.mjs +0 -18144
  35. package/dist/token-goat-hook-chunk-XUMIVYEN.mjs +0 -109
@@ -21,6 +21,7 @@ import {
21
21
  formatCsvTable,
22
22
  getFileEntry,
23
23
  indexFileSync,
24
+ isAvailable,
24
25
  isBlobStale,
25
26
  isImagePath,
26
27
  isIndexEmptyForProject,
@@ -50,8 +51,9 @@ import {
50
51
  walkProject,
51
52
  yamlLineClosesQuote,
52
53
  yamlOpenQuoteAfter
53
- } from "./token-goat-chunk-I2KCEGIP.mjs";
54
+ } from "./token-goat-chunk-2F6TFBZE.mjs";
54
55
  import {
56
+ Database,
55
57
  PER_FILE_COUNTERFACTUAL_CEILING,
56
58
  _detectOpenQuote,
57
59
  _lineClosesQuote,
@@ -83,6 +85,7 @@ import {
83
85
  loadConfig,
84
86
  normalizePath,
85
87
  offsetToLine,
88
+ pushAll,
86
89
  recordStat,
87
90
  redactIfDotenv,
88
91
  redactSecrets,
@@ -100,7 +103,7 @@ import {
100
103
  unsupportedLanguageName,
101
104
  windowsCmdQuoteArg,
102
105
  withExtension
103
- } from "./token-goat-chunk-K7W3P2TJ.mjs";
106
+ } from "./token-goat-chunk-44Y77VHR.mjs";
104
107
  import {
105
108
  registerReset
106
109
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -319,7 +322,15 @@ function resolveHeaderPos(headers, base, ordinal) {
319
322
  const pick2 = ordinal === null ? 0 : ordinal - 1;
320
323
  const headerPos = matches[pick2];
321
324
  if (headerPos === void 0) return null;
322
- return { headerPos, redirectedFrom: null };
325
+ let occurrences = null;
326
+ if (ordinal === null && matches.length > 1) {
327
+ occurrences = [];
328
+ for (const i of matches) {
329
+ const h = headers[i];
330
+ if (h !== void 0) occurrences.push(h.index + 1);
331
+ }
332
+ }
333
+ return { headerPos, redirectedFrom: null, occurrences };
323
334
  }
324
335
  if (ordinal !== null || normalizedTarget.length === 0) return null;
325
336
  let prefixPos = -1;
@@ -336,7 +347,7 @@ function resolveHeaderPos(headers, base, ordinal) {
336
347
  if (distinct.size === 1 && prefixPos !== -1) {
337
348
  const chosen = headers[prefixPos];
338
349
  if (chosen === void 0) return null;
339
- return { headerPos: prefixPos, redirectedFrom: base };
350
+ return { headerPos: prefixPos, redirectedFrom: base, occurrences: null };
340
351
  }
341
352
  const queryWords = normalizedTarget.split(/\s+/).filter((w) => w.length >= MIN_WIDEN_WORD_LEN);
342
353
  if (queryWords.length === 0) return null;
@@ -351,7 +362,7 @@ function resolveHeaderPos(headers, base, ordinal) {
351
362
  if (widenedMatches.length !== 1) return null;
352
363
  const widenedPos = widenedMatches[0];
353
364
  if (widenedPos === void 0) return null;
354
- return { headerPos: widenedPos, redirectedFrom: base };
365
+ return { headerPos: widenedPos, redirectedFrom: base, occurrences: null };
355
366
  }
356
367
  function buildSectionResult(headers, kind, lines, headerPos, redirectedFrom) {
357
368
  const header = headers[headerPos];
@@ -377,7 +388,9 @@ function resolveSectionFromText(text, headingSpec, language) {
377
388
  const lines = text.split("\n");
378
389
  const resolved = resolveHeaderPos(headers, base, ordinal);
379
390
  if (resolved === null) return null;
380
- return buildSectionResult(headers, kind, lines, resolved.headerPos, resolved.redirectedFrom);
391
+ const built = buildSectionResult(headers, kind, lines, resolved.headerPos, resolved.redirectedFrom);
392
+ if (built === null || resolved.occurrences === null) return built;
393
+ return { ...built, occurrences: resolved.occurrences };
381
394
  }
382
395
  function extractSection(text, headingSpec) {
383
396
  return resolveSectionFromText(text, headingSpec, "unknown");
@@ -563,9 +576,9 @@ function evalJsonPath(data, ops) {
563
576
  } else if (op.kind === "wildcard") {
564
577
  fanned = true;
565
578
  if (Array.isArray(item)) {
566
- next.push(...item);
579
+ pushAll(next, item);
567
580
  } else if (typeof item === "object" && item !== null) {
568
- next.push(...Object.values(item));
581
+ pushAll(next, Object.values(item));
569
582
  }
570
583
  } else {
571
584
  fanned = true;
@@ -997,7 +1010,7 @@ function queryXml(xmlText, pathStr) {
997
1010
  const attrName = step.attributeSelect;
998
1011
  for (const cand of currentCandidates) {
999
1012
  if (attrName === "*") {
1000
- attrVals.push(...Object.values(cand.attributes));
1013
+ pushAll(attrVals, Object.values(cand.attributes));
1001
1014
  } else if (cand.attributes[attrName] !== void 0) {
1002
1015
  attrVals.push(cand.attributes[attrName]);
1003
1016
  }
@@ -1046,7 +1059,7 @@ function queryXml(xmlText, pathStr) {
1046
1059
  if (step.allIndices || matching.length > 1 || step.attributeFilter !== void 0) {
1047
1060
  hasFanned = true;
1048
1061
  }
1049
- nextCandidates.push(...matching);
1062
+ pushAll(nextCandidates, matching);
1050
1063
  }
1051
1064
  }
1052
1065
  currentCandidates = nextCandidates;
@@ -3744,7 +3757,6 @@ ${c.body}`).join("\n\n");
3744
3757
 
3745
3758
  // src/sqlite_query.ts
3746
3759
  import * as fs2 from "node:fs";
3747
- import Database from "better-sqlite3";
3748
3760
  var SQLITE_QUERY_ROW_CAP = 5e3;
3749
3761
  var SQLITE_MAGIC = Buffer.from([83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0]);
3750
3762
  function readMagicBytes(filePath) {
@@ -4929,7 +4941,7 @@ function loadTs() {
4929
4941
  }
4930
4942
  return _ts;
4931
4943
  }
4932
- function isAvailable() {
4944
+ function isAvailable2() {
4933
4945
  return loadTs() !== null;
4934
4946
  }
4935
4947
  function loadError() {
@@ -5093,6 +5105,7 @@ function findIdentifierNearPosition(ts, sourceFile, line0, position, name) {
5093
5105
 
5094
5106
  // src/read_commands.ts
5095
5107
  var DIDYOUMEAN_LIMIT = 5;
5108
+ var AMBIGUOUS_HEADING_LIMIT = 10;
5096
5109
  var TYPO_TWO_EDIT_MIN_LEN = 8;
5097
5110
  var TYPO_MAX_QUERY_LEN = 64;
5098
5111
  var MIN_REVERSE_MATCH_LEN = 3;
@@ -5381,13 +5394,14 @@ function filterSimilarHeadings(available, query) {
5381
5394
  return sortByLengthCloseness(matched, query);
5382
5395
  }
5383
5396
  function didYouMean(candidates) {
5384
- if (candidates.length === 0) return "";
5397
+ const unique = [...new Set(candidates)];
5398
+ if (unique.length === 0) return "";
5385
5399
  const lines = ["Did you mean:"];
5386
- for (const c of candidates.slice(0, DIDYOUMEAN_LIMIT)) {
5400
+ for (const c of unique.slice(0, DIDYOUMEAN_LIMIT)) {
5387
5401
  lines.push(` - ${c}`);
5388
5402
  }
5389
- if (candidates.length > DIDYOUMEAN_LIMIT) {
5390
- lines.push(` (${candidates.length - DIDYOUMEAN_LIMIT} more not shown)`);
5403
+ if (unique.length > DIDYOUMEAN_LIMIT) {
5404
+ lines.push(` (${unique.length - DIDYOUMEAN_LIMIT} more not shown)`);
5391
5405
  }
5392
5406
  return lines.join("\n");
5393
5407
  }
@@ -6017,6 +6031,18 @@ function runSection(opts) {
6017
6031
  if (!fs5.existsSync(filePath)) {
6018
6032
  return { text: `File not found: '${filePath}'`, code: 1 };
6019
6033
  }
6034
+ const ordSpec = /^(.*?)#(\d+)$/.exec(heading);
6035
+ const ordBase = ordSpec?.[1]?.trim();
6036
+ if (ordBase !== void 0 && ordBase.length > 0) {
6037
+ const baseResult = readSection(filePath, ordBase, readFileText);
6038
+ if (baseResult !== null) {
6039
+ const total = baseResult.occurrences?.length ?? 1;
6040
+ return {
6041
+ text: `Heading '${ordBase}' has ${countNoun(total, "occurrence")} in '${specFilePath}'; valid ordinals are #1 to #${total}`,
6042
+ code: 1
6043
+ };
6044
+ }
6045
+ }
6020
6046
  const messages = [`Section '${heading}' not found in '${filePath}'`];
6021
6047
  const allHeadings = listSections(filePath, readFileText);
6022
6048
  const available = filterSimilarHeadings(allHeadings, heading);
@@ -6025,6 +6051,18 @@ function runSection(opts) {
6025
6051
  else messages.push(`Try: token-goat outline ${specFilePath}`);
6026
6052
  return { text: messages.join("\n"), code: 1 };
6027
6053
  }
6054
+ if (result.occurrences !== void 0) {
6055
+ const lines = [
6056
+ `Ambiguous heading '${heading}' in '${specFilePath}': ${countNoun(result.occurrences.length, "heading")} match. Retry with one of the qualified commands below to pick one:`
6057
+ ];
6058
+ for (const [i, line] of result.occurrences.slice(0, AMBIGUOUS_HEADING_LIMIT).entries()) {
6059
+ lines.push(` - line ${line} -> token-goat section "${specFilePath}::${heading}#${i + 1}"`);
6060
+ }
6061
+ if (result.occurrences.length > AMBIGUOUS_HEADING_LIMIT) {
6062
+ lines.push(` (${result.occurrences.length - AMBIGUOUS_HEADING_LIMIT} more not shown)`);
6063
+ }
6064
+ return { text: lines.join("\n"), code: 1 };
6065
+ }
6028
6066
  const kind = result.redirectedFrom !== void 0 ? "section_replacement" : "section_read";
6029
6067
  const fullSourceBytes = sumFileSizes([filePath]);
6030
6068
  if (opts.json === true) {
@@ -6144,40 +6182,15 @@ function refsSpecFiles(spec) {
6144
6182
  const { file } = parseMultiRefsSpec(spec);
6145
6183
  return file === void 0 ? [] : [file];
6146
6184
  }
6147
- function runRefs(opts) {
6148
- if (opts.limit !== void 0 && opts.limit <= 0) {
6149
- emitErr(`--limit must be a positive number, got: ${opts.limit}`);
6150
- return 1;
6151
- }
6152
- if (opts.top !== void 0 && opts.top <= 0) {
6153
- emitErr(`--top must be a positive number, got: ${opts.top}`);
6154
- return 1;
6155
- }
6156
- const confinedRoot = confinedProjectRoot();
6157
- if (confinedRoot !== null) {
6158
- const projectDenial = opts.projectRoot === void 0 ? null : confinementRefusal("--project", opts.projectRoot, confinedRoot);
6159
- if (projectDenial !== null) {
6160
- emitErr(projectDenial);
6161
- return 1;
6162
- }
6163
- for (const file2 of refsSpecFiles(opts.spec)) {
6164
- const denial = confinementRefusal("This file", resolveIndexPath(file2, opts.projectRoot ?? process.cwd()), confinedRoot);
6165
- if (denial !== null) {
6166
- emitErr(denial);
6167
- return 1;
6168
- }
6169
- }
6170
- }
6171
- const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
6172
- if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
6173
- const { file, symbols } = parseMultiRefsSpec(opts.spec);
6174
- if (symbols.length <= 1) return runRefsSingle(opts);
6185
+ function renderRefsTargets(targets, opts, {
6186
+ annotateHiddenByGrep
6187
+ }) {
6175
6188
  const jsonOut = {};
6176
6189
  let anyFound = false;
6177
6190
  const lines = [];
6178
6191
  const refFilePaths = [];
6179
- for (const sym of symbols) {
6180
- const queryOpts = { name: sym };
6192
+ for (const { file, symbol, key } of targets) {
6193
+ const queryOpts = { name: symbol };
6181
6194
  if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
6182
6195
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
6183
6196
  else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
@@ -6185,7 +6198,7 @@ function runRefs(opts) {
6185
6198
  if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6186
6199
  const scanned = queryRefs(queryOpts);
6187
6200
  const preScanCount = scanned.length;
6188
- let results = applyTypedRefsTier(sym, file, scanned);
6201
+ let results = applyTypedRefsTier(symbol, file, scanned);
6189
6202
  let suppressed = 0;
6190
6203
  if (opts.excludeTests === true) {
6191
6204
  const f = applyExcludeTestsFilter(results);
@@ -6204,25 +6217,25 @@ function runRefs(opts) {
6204
6217
  refFilePaths.push(...results.map((r) => r.filePath));
6205
6218
  if (opts.json === true) {
6206
6219
  const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - (filteredTotal ?? results.length) : 0;
6207
- const withHidden = (payload) => ({ ...payload, ...hiddenByGrep > 0 ? { hiddenByGrep } : {} });
6220
+ const withHidden = (payload) => ({ ...payload, ...annotateHiddenByGrep && hiddenByGrep > 0 ? { hiddenByGrep } : {} });
6208
6221
  if (opts.top !== void 0) {
6209
- jsonOut[sym] = withHidden(topFilesJsonPayload(results, opts.top));
6222
+ jsonOut[key] = withHidden(topFilesJsonPayload(results, opts.top));
6210
6223
  } else {
6211
6224
  const capped = guardJsonRows(results);
6212
6225
  const trueTotal = opts.excludeTests === true || matchesGrep !== void 0 ? filteredTotal ?? results.length : countRefs(queryOpts);
6213
- jsonOut[sym] = withHidden({ items: refsJsonItems(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal });
6226
+ jsonOut[key] = withHidden({ items: refsJsonItems(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal });
6214
6227
  }
6215
6228
  continue;
6216
6229
  }
6217
6230
  if (results.length === 0) {
6218
6231
  if (matchesGrep !== void 0 && preGrepCount > 0) {
6219
- lines.push(`${sym}: ${grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references").trim()}`);
6232
+ lines.push(`${key}: ${grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references").trim()}`);
6220
6233
  continue;
6221
6234
  }
6222
- lines.push(opts.excludeTests === true && suppressed > 0 ? `${sym}: (no non-test references found; ${excludeTestsHiddenNote(suppressed)})` : `${sym}: (no references found)`);
6235
+ lines.push(opts.excludeTests === true && suppressed > 0 ? `${key}: (no non-test references found; ${excludeTestsHiddenNote(suppressed)})` : `${key}: (no references found)`);
6223
6236
  continue;
6224
6237
  }
6225
- lines.push(`${sym}:`);
6238
+ lines.push(`${key}:`);
6226
6239
  if (opts.top !== void 0) {
6227
6240
  lines.push(...renderTopFilesSummary(results, opts.top, suppressed));
6228
6241
  } else if (opts.callers === true) {
@@ -6255,90 +6268,50 @@ function runRefs(opts) {
6255
6268
  if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
6256
6269
  return anyFound ? 0 : 1;
6257
6270
  }
6258
- function runRefsCrossFile(pairs, opts) {
6259
- const distinctFiles = new Set(pairs.map((p) => p.file));
6260
- const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
6261
- const jsonOut = {};
6262
- let anyFound = false;
6263
- const lines = [];
6264
- const refFilePaths = [];
6265
- for (const { file, symbol } of pairs) {
6266
- const key = keyFor({ file, symbol });
6267
- const queryOpts = { name: symbol };
6268
- if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
6269
- else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
6270
- else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
6271
- const rootDir = refsRootDir(opts);
6272
- if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6273
- const scanned = queryRefs(queryOpts);
6274
- const preScanCount = scanned.length;
6275
- let results = applyTypedRefsTier(symbol, file, scanned);
6276
- let suppressed = 0;
6277
- if (opts.excludeTests === true) {
6278
- const f = applyExcludeTestsFilter(results);
6279
- suppressed = f.suppressed;
6280
- results = f.refs;
6281
- }
6282
- const preGrepCount = results.length;
6283
- const matchesGrep = refGrepFilter(opts.grep);
6284
- if (matchesGrep !== void 0) results = results.filter(matchesGrep);
6285
- let filteredTotal;
6286
- if (opts.excludeTests === true || matchesGrep !== void 0) filteredTotal = results.length;
6287
- if ((opts.excludeTests === true || matchesGrep !== void 0) && opts.top === void 0) {
6288
- results = results.slice(0, opts.limit ?? 100);
6289
- }
6290
- if (results.length > 0) anyFound = true;
6291
- refFilePaths.push(...results.map((r) => r.filePath));
6292
- if (opts.json === true) {
6293
- if (opts.top !== void 0) {
6294
- jsonOut[key] = topFilesJsonPayload(results, opts.top);
6295
- } else {
6296
- const capped = guardJsonRows(results);
6297
- const trueTotal = opts.excludeTests === true || matchesGrep !== void 0 ? filteredTotal ?? results.length : countRefs(queryOpts);
6298
- jsonOut[key] = { items: refsJsonItems(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
6299
- }
6300
- continue;
6271
+ function runRefs(opts) {
6272
+ if (opts.limit !== void 0 && opts.limit <= 0) {
6273
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
6274
+ return 1;
6275
+ }
6276
+ if (opts.top !== void 0 && opts.top <= 0) {
6277
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
6278
+ return 1;
6279
+ }
6280
+ const confinedRoot = confinedProjectRoot();
6281
+ if (confinedRoot !== null) {
6282
+ const projectDenial = opts.projectRoot === void 0 ? null : confinementRefusal("--project", opts.projectRoot, confinedRoot);
6283
+ if (projectDenial !== null) {
6284
+ emitErr(projectDenial);
6285
+ return 1;
6301
6286
  }
6302
- if (results.length === 0) {
6303
- if (matchesGrep !== void 0 && preGrepCount > 0) {
6304
- lines.push(`${key}: ${grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references").trim()}`);
6305
- continue;
6287
+ for (const file2 of refsSpecFiles(opts.spec)) {
6288
+ const denial = confinementRefusal("This file", resolveIndexPath(file2, opts.projectRoot ?? process.cwd()), confinedRoot);
6289
+ if (denial !== null) {
6290
+ emitErr(denial);
6291
+ return 1;
6306
6292
  }
6307
- lines.push(opts.excludeTests === true && suppressed > 0 ? `${key}: (no non-test references found; ${excludeTestsHiddenNote(suppressed)})` : `${key}: (no references found)`);
6308
- continue;
6309
- }
6310
- lines.push(`${key}:`);
6311
- if (opts.top !== void 0) {
6312
- lines.push(...renderTopFilesSummary(results, opts.top, suppressed));
6313
- } else if (opts.callers === true) {
6314
- if (opts.excludeTests === true && suppressed > 0) lines.push(` ${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`);
6315
- lines.push(...renderCallerGroups(results, opts.context ?? 0));
6316
- } else {
6317
- if (opts.excludeTests === true && suppressed > 0) lines.push(` ${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`);
6318
- for (const ref of results) lines.push(...renderRefLines(ref, opts.context ?? 0));
6319
- }
6320
- if (opts.top === void 0) {
6321
- const notice = truncationNotice(
6322
- results.length,
6323
- opts.limit ?? 100,
6324
- () => refsTotal(opts.excludeTests === true || matchesGrep !== void 0, filteredTotal, results.length, () => countRefs(queryOpts), preScanCount),
6325
- "references",
6326
- "--limit"
6327
- );
6328
- if (notice !== null) lines.push(` token-goat: ${notice}`);
6329
6293
  }
6330
6294
  }
6331
- const fullSourceBytes = sumFileSizes(refFilePaths);
6332
- if (opts.json === true) {
6333
- const text2 = JSON.stringify(jsonOut, null, 2);
6334
- emit(text2);
6335
- if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text2, opts.spec);
6336
- return anyFound ? 0 : 1;
6337
- }
6338
- const text = lines.join("\n");
6339
- emitGuarded(text, "symbol");
6340
- if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
6341
- return anyFound ? 0 : 1;
6295
+ const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
6296
+ if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
6297
+ const { file, symbols } = parseMultiRefsSpec(opts.spec);
6298
+ if (symbols.length <= 1) return runRefsSingle(opts);
6299
+ return renderRefsTargets(
6300
+ symbols.map((symbol) => ({ file, symbol, key: symbol })),
6301
+ opts,
6302
+ { annotateHiddenByGrep: true }
6303
+ );
6304
+ }
6305
+ function runRefsCrossFile(pairs, opts) {
6306
+ const distinctFiles = new Set(pairs.map((p) => p.file));
6307
+ const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
6308
+ return renderRefsTargets(
6309
+ pairs.map((p) => ({ file: p.file, symbol: p.symbol, key: keyFor(p) })),
6310
+ opts,
6311
+ // Not annotated today; renderRefsTargets's own option doc explains why that is a preserved
6312
+ // divergence rather than a decision.
6313
+ { annotateHiddenByGrep: false }
6314
+ );
6342
6315
  }
6343
6316
  function runRefsSingle(opts) {
6344
6317
  const { file, symbol } = parseReadSpec(opts.spec);
@@ -6552,6 +6525,10 @@ function mergeListingJson(files, blocks, anyOk) {
6552
6525
  const payload = { items, truncated, totalCount, ...errors.length > 0 ? { errors } : {} };
6553
6526
  return { text: JSON.stringify(payload, null, 2), code: anyOk ? 0 : 1 };
6554
6527
  }
6528
+ function symbolCountLabel(shown, truncated, trueCount) {
6529
+ if (!truncated || trueCount === void 0 || trueCount <= shown) return countNoun(shown, "symbol");
6530
+ return `${shown} of ${countNoun(trueCount, "symbol")}`;
6531
+ }
6555
6532
  function runSkeleton(opts) {
6556
6533
  const multiFiles = parseMultiFileSpec(opts.file);
6557
6534
  if (multiFiles !== null) return runPerFileListing(multiFiles, (file) => runSkeleton({ ...opts, file, includeFilePath: true }), opts.json === true);
@@ -6584,7 +6561,7 @@ function runSkeleton(opts) {
6584
6561
  return { text: text2, code: 0 };
6585
6562
  }
6586
6563
  const totalLines = filtered.length > 0 ? Math.max(...filtered.map((s) => s.lineEnd)) : 0;
6587
- const lines = [`# Skeleton: ${opts.file} (${countNoun(filtered.length, "symbol")}, ${countNoun(totalLines, "line")})`];
6564
+ const lines = [`# Skeleton: ${opts.file} (${symbolCountLabel(filtered.length, symbolsTruncated, trueSymbolCount)}, ${countNoun(totalLines, "line")})`];
6588
6565
  if (filtered.length === 0 && preFilterCount > 0) lines.push(filteredToEmptyNotice(preFilterCount, opts.minLines, opts.grep));
6589
6566
  for (const sym of filtered) {
6590
6567
  const lineStr = sym.lineStart.toString().padStart(6);
@@ -6626,7 +6603,7 @@ function runOutline(opts) {
6626
6603
  recordReadStat("outline", fullSourceBytes, text2, opts.file);
6627
6604
  return { text: text2, code: 0 };
6628
6605
  }
6629
- const lines = [`# Outline: ${opts.file} (${countNoun(filtered.length, "symbol")})`];
6606
+ const lines = [`# Outline: ${opts.file} (${symbolCountLabel(filtered.length, symbolsTruncated, trueSymbolCount)})`];
6630
6607
  if (filtered.length === 0 && preFilterCount > 0) lines.push(filteredToEmptyNotice(preFilterCount, opts.minLines, opts.grep));
6631
6608
  for (const sym of filtered) {
6632
6609
  const rangeStr = `${sym.lineStart.toString().padStart(4)}-${sym.lineEnd.toString().padEnd(6)}`;
@@ -7564,7 +7541,15 @@ function runListSections(opts) {
7564
7541
  }
7565
7542
  const preFilterCount = sections.length;
7566
7543
  const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
7567
- const filtered = matchesGrep !== void 0 ? sections.filter((s) => matchesGrep(s)) : sections;
7544
+ const totals = /* @__PURE__ */ new Map();
7545
+ for (const heading of sections) totals.set(heading, (totals.get(heading) ?? 0) + 1);
7546
+ const seen = /* @__PURE__ */ new Map();
7547
+ const labelled = sections.map((heading) => {
7548
+ const nth = (seen.get(heading) ?? 0) + 1;
7549
+ seen.set(heading, nth);
7550
+ return (totals.get(heading) ?? 1) > 1 ? `${heading}#${nth}` : heading;
7551
+ });
7552
+ const filtered = matchesGrep !== void 0 ? labelled.filter((_, i) => matchesGrep(sections[i] ?? "")) : labelled;
7568
7553
  if (filtered.length === 0) {
7569
7554
  if (opts.json === true) {
7570
7555
  emit(JSON.stringify({ items: [], truncated: false, totalCount: 0 }, null, 2));
@@ -7578,8 +7563,8 @@ function runListSections(opts) {
7578
7563
  emit(JSON.stringify({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }, null, 2));
7579
7564
  return 0;
7580
7565
  }
7581
- for (const s of filtered) {
7582
- emit(s);
7566
+ for (const heading of filtered) {
7567
+ emit(heading);
7583
7568
  }
7584
7569
  return 0;
7585
7570
  }
@@ -8776,15 +8761,27 @@ async function runSemantic(query, opts) {
8776
8761
  }
8777
8762
  }
8778
8763
  const rootDir = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
8764
+ if (!isAvailable()) {
8765
+ console.warn(
8766
+ "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)"
8767
+ );
8768
+ }
8779
8769
  const overFetchForMerge = Math.min(MAX_OVER_FETCH, n * OVER_FETCH_FACTOR);
8780
- const rawHits = await searchSemantic(
8781
- getDb(globalDbPath()),
8782
- query,
8783
- overFetchForMerge,
8784
- void 0,
8785
- void 0,
8786
- rootDir
8787
- );
8770
+ let rawHits = [];
8771
+ try {
8772
+ rawHits = await searchSemantic(
8773
+ getDb(globalDbPath()),
8774
+ query,
8775
+ overFetchForMerge,
8776
+ void 0,
8777
+ void 0,
8778
+ rootDir
8779
+ );
8780
+ } catch (e) {
8781
+ console.warn(
8782
+ `Matching on meaning is off (${extractErrorMessage(e)}); these results come from keyword search alone.`
8783
+ );
8784
+ }
8788
8785
  const mergedHits = mergeNearbyHits(rawHits);
8789
8786
  const overFetchFts = Math.min(MAX_OVER_FETCH, n * OVER_FETCH_FACTOR);
8790
8787
  const ftsRows = searchSymbolsFts(query, overFetchFts, void 0, rootDir);
@@ -10335,7 +10332,7 @@ export {
10335
10332
  symbolNamesInFile,
10336
10333
  computeFileFingerprint,
10337
10334
  upsertNote,
10338
- isAvailable,
10335
+ isAvailable2 as isAvailable,
10339
10336
  loadError,
10340
10337
  ConfinementIdentityError,
10341
10338
  ABSENT_PIN,
@@ -4,7 +4,7 @@ import {
4
4
  loadConfig,
5
5
  redactSecrets,
6
6
  stripAnsiCodes
7
- } from "./token-goat-hook-chunk-2O7P4Z6Q.mjs";
7
+ } from "./token-goat-chunk-44Y77VHR.mjs";
8
8
 
9
9
  // src/tool_filters/helpers.ts
10
10
  import * as fs from "node:fs";