token-goat 2.9.18 → 2.9.20

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 (55) hide show
  1. package/README.md +5 -1
  2. package/dist/token-goat-chunk-2CLWWRFR.mjs +26 -0
  3. package/dist/{token-goat-chunk-LTYWLKAG.mjs → token-goat-chunk-2Q3N5KUQ.mjs} +1283 -1012
  4. package/dist/{token-goat-chunk-6LGVCCMY.mjs → token-goat-chunk-3OGZOFLS.mjs} +26 -16
  5. package/dist/{token-goat-chunk-QML7ITHJ.mjs → token-goat-chunk-3VTJC7KU.mjs} +693 -438
  6. package/dist/{token-goat-chunk-QIDLJGXZ.mjs → token-goat-chunk-6LJNM4ZZ.mjs} +29 -5
  7. package/dist/{token-goat-chunk-CA6V36KW.mjs → token-goat-chunk-74ADIZZX.mjs} +68 -50
  8. package/dist/{token-goat-chunk-52IMXJ2L.mjs → token-goat-chunk-ASTBLE2R.mjs} +4 -4
  9. package/dist/token-goat-chunk-AU2K2QHR.mjs +20 -0
  10. package/dist/token-goat-chunk-BQIC3AVL.mjs +292 -0
  11. package/dist/token-goat-chunk-CBXLE54Q.mjs +30 -0
  12. package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
  13. package/dist/{token-goat-chunk-ARNRFR6M.mjs → token-goat-chunk-DNAF26L4.mjs} +1 -1
  14. package/dist/{token-goat-chunk-BFEWOMA3.mjs → token-goat-chunk-DPHJU3P7.mjs} +1186 -4573
  15. package/dist/{token-goat-chunk-OWSJJY5R.mjs → token-goat-chunk-DW4VKU7D.mjs} +5 -5
  16. package/dist/token-goat-chunk-DYUBZZNP.mjs +198 -0
  17. package/dist/{token-goat-chunk-W3E65QOW.mjs → token-goat-chunk-ERGHDBC6.mjs} +310 -47
  18. package/dist/token-goat-chunk-EUZKG7ZO.mjs +304 -0
  19. package/dist/{token-goat-chunk-5MMJAJF3.mjs → token-goat-chunk-FWYZVS3E.mjs} +324 -31
  20. package/dist/token-goat-chunk-G6SYQQO7.mjs +41 -0
  21. package/dist/{token-goat-chunk-YQZE5NS6.mjs → token-goat-chunk-GIPMOGOI.mjs} +6 -6
  22. package/dist/{token-goat-chunk-BWCRC23L.mjs → token-goat-chunk-H4VC5TR6.mjs} +29 -20
  23. package/dist/{token-goat-chunk-V3ZWBEHM.mjs → token-goat-chunk-I5PQRHFE.mjs} +103 -24
  24. package/dist/token-goat-chunk-ITWYDOKP.mjs +341 -0
  25. package/dist/{token-goat-chunk-I67CPTDD.mjs → token-goat-chunk-K3ZNBN4U.mjs} +19 -14
  26. package/dist/{token-goat-chunk-HPOFR6QX.mjs → token-goat-chunk-K5XPIUVB.mjs} +222 -61
  27. package/dist/token-goat-chunk-KT75DZK4.mjs +1989 -0
  28. package/dist/{token-goat-chunk-H3FONFBO.mjs → token-goat-chunk-MNJWG2ZO.mjs} +3 -3
  29. package/dist/{token-goat-chunk-GRI6HWX3.mjs → token-goat-chunk-MUJV7BR3.mjs} +1 -1
  30. package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
  31. package/dist/{token-goat-chunk-LA3O4JGD.mjs → token-goat-chunk-ODF7H7WS.mjs} +3 -3
  32. package/dist/{token-goat-chunk-K4DG6EEF.mjs → token-goat-chunk-ONMU7BON.mjs} +7 -8
  33. package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
  34. package/dist/{token-goat-chunk-EPXNOKIV.mjs → token-goat-chunk-P4XBHZXU.mjs} +2 -2
  35. package/dist/{token-goat-chunk-CTOYJ232.mjs → token-goat-chunk-PAVWB4L4.mjs} +173 -1840
  36. package/dist/{token-goat-chunk-UENEL6KX.mjs → token-goat-chunk-PTW22MRC.mjs} +2 -2
  37. package/dist/token-goat-chunk-SL454EN5.mjs +1816 -0
  38. package/dist/token-goat-chunk-SP5QR2HN.mjs +1379 -0
  39. package/dist/{token-goat-chunk-7VLWNQDQ.mjs → token-goat-chunk-T46UKRFM.mjs} +1 -1
  40. package/dist/{token-goat-chunk-L7VCX33I.mjs → token-goat-chunk-T63VHOL3.mjs} +40 -10
  41. package/dist/token-goat-chunk-VSJGPEBB.mjs +248 -0
  42. package/dist/{token-goat-chunk-HIZXQVNU.mjs → token-goat-chunk-VYXGHGUY.mjs} +4 -4
  43. package/dist/token-goat-chunk-W6QYCWAT.mjs +128 -0
  44. package/dist/{token-goat-chunk-WFX2V3OF.mjs → token-goat-chunk-Z4ALQADF.mjs} +579 -422
  45. package/dist/token-goat-chunk-ZHFM4DHJ.mjs +324 -0
  46. package/dist/token-goat-hook.mjs +18 -12
  47. package/dist/token-goat.core.mjs +29 -20
  48. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  49. package/docs/cli.md +15 -13
  50. package/docs/install.md +1 -1
  51. package/docs/security.md +2 -2
  52. package/package.json +3 -2
  53. package/dist/token-goat-chunk-2KTZ6J7T.mjs +0 -173
  54. package/dist/token-goat-chunk-34OUJ2IE.mjs +0 -35
  55. package/dist/token-goat-chunk-RHHFEXKF.mjs +0 -93
@@ -5,7 +5,7 @@ import {
5
5
  isTextHeavy,
6
6
  ocrImage,
7
7
  ocrIntegrityFailed
8
- } from "./token-goat-chunk-H3FONFBO.mjs";
8
+ } from "./token-goat-chunk-MNJWG2ZO.mjs";
9
9
  import {
10
10
  formatSqliteQueryTable,
11
11
  formatSqliteSchema,
@@ -13,37 +13,43 @@ import {
13
13
  getSqliteSchema,
14
14
  getSqliteTables,
15
15
  runReadOnlySqliteQuery
16
- } from "./token-goat-chunk-LMFO66YD.mjs";
16
+ } from "./token-goat-chunk-DA43OE7L.mjs";
17
17
  import {
18
18
  ImageDecodeError,
19
+ LARGE_SYMBOL_LINE_THRESHOLD,
19
20
  NOTEBOOK_CELL_LINES_SUFFIX,
20
21
  SKIP_DIRS,
21
22
  canShrinkFormat,
22
- capJsonRows,
23
23
  emptyIndexMessage,
24
24
  enqueueDirtyPathSafe,
25
- estimateTokens,
26
25
  fenceUntrusted,
27
26
  formatSymbolLocation,
28
27
  getEmbeddingCoverage,
29
28
  getTrackedFiles,
30
29
  indexedSourceText,
31
- isImagePath,
30
+ isIgnoredIndexPath,
32
31
  isIndexEmptyForProject,
33
32
  isPrivateIpv4Octets,
34
33
  isPrivateIpv6Groups,
35
34
  isVirtualIndexedPath,
36
35
  parseIpv6Groups,
37
36
  probeImageMeta,
37
+ recordKnownRootThrottled,
38
+ resolveLineRegions,
38
39
  scanAndRecord,
39
40
  searchEvidenceSemantically,
40
41
  shrinkImage,
41
- trimToBudget,
42
42
  urlPolicyDenialReason,
43
43
  virtualIndexedScopeNote,
44
44
  walkProject
45
- } from "./token-goat-chunk-BFEWOMA3.mjs";
45
+ } from "./token-goat-chunk-DPHJU3P7.mjs";
46
+ import {
47
+ capJsonRows,
48
+ estimateTokens,
49
+ trimToBudget
50
+ } from "./token-goat-chunk-ZHFM4DHJ.mjs";
46
51
  import {
52
+ BYTES_PER_TOKEN,
47
53
  colorStdout,
48
54
  isBlobStale,
49
55
  loadBlob,
@@ -51,8 +57,8 @@ import {
51
57
  redactSecrets,
52
58
  savedTokensFromBytes,
53
59
  storeBlob,
54
- stripAnsi
55
- } from "./token-goat-chunk-HPOFR6QX.mjs";
60
+ stripAnsiEscapes
61
+ } from "./token-goat-chunk-K5XPIUVB.mjs";
56
62
  import {
57
63
  extractPdfMeta,
58
64
  extractPdfOutline,
@@ -66,7 +72,7 @@ import {
66
72
  parseWhereSpecs,
67
73
  profileCsv,
68
74
  queryCsv
69
- } from "./token-goat-chunk-EPXNOKIV.mjs";
75
+ } from "./token-goat-chunk-P4XBHZXU.mjs";
70
76
  import {
71
77
  MAX_ZIP_INPUT_BYTES,
72
78
  MAX_ZIP_OUTPUT_BYTES,
@@ -101,7 +107,7 @@ import {
101
107
  extractSolidity,
102
108
  extractThrift,
103
109
  stripLeadingAttributes
104
- } from "./token-goat-chunk-GRI6HWX3.mjs";
110
+ } from "./token-goat-chunk-MUJV7BR3.mjs";
105
111
  import {
106
112
  MAX_OVER_FETCH,
107
113
  OVER_FETCH_FACTOR,
@@ -109,12 +115,13 @@ import {
109
115
  findContainingSection,
110
116
  indexFileSync,
111
117
  isAvailable,
118
+ isImagePath,
112
119
  isTreeSitterAvailable,
113
120
  listSections,
114
121
  mergeNearbyHits,
115
122
  readSection,
116
123
  searchSemantic
117
- } from "./token-goat-chunk-W3E65QOW.mjs";
124
+ } from "./token-goat-chunk-ERGHDBC6.mjs";
118
125
  import {
119
126
  DEFAULT_QUERY_LIMIT,
120
127
  countRefs,
@@ -127,7 +134,7 @@ import {
127
134
  queryRefsByContext,
128
135
  querySymbols,
129
136
  searchSymbolsFts
130
- } from "./token-goat-chunk-I67CPTDD.mjs";
137
+ } from "./token-goat-chunk-K3ZNBN4U.mjs";
131
138
  import {
132
139
  FILENAME_LANGUAGE,
133
140
  TREE_SITTER_LANGUAGES,
@@ -146,7 +153,7 @@ import {
146
153
  partialRefsReason,
147
154
  redactIfDotenv,
148
155
  unsupportedLanguageName
149
- } from "./token-goat-chunk-L7VCX33I.mjs";
156
+ } from "./token-goat-chunk-T63VHOL3.mjs";
150
157
  import {
151
158
  fileIsAbsent,
152
159
  findProject,
@@ -156,7 +163,7 @@ import {
156
163
  loadConfig,
157
164
  resolveProjectRoot,
158
165
  shortFingerprint
159
- } from "./token-goat-chunk-CA6V36KW.mjs";
166
+ } from "./token-goat-chunk-74ADIZZX.mjs";
160
167
  import {
161
168
  registerReset
162
169
  } from "./token-goat-chunk-EEIDFMEM.mjs";
@@ -166,6 +173,7 @@ import {
166
173
  atomicWriteBytes,
167
174
  compileGrepMatcher,
168
175
  countNoun,
176
+ dataDir,
169
177
  decodeSource,
170
178
  ensureNewline,
171
179
  escapeRegExp,
@@ -188,7 +196,7 @@ import {
188
196
  supportRequestLine,
189
197
  windowsCmdQuoteArg,
190
198
  withExtension
191
- } from "./token-goat-chunk-QIDLJGXZ.mjs";
199
+ } from "./token-goat-chunk-6LJNM4ZZ.mjs";
192
200
  import {
193
201
  UNTRUSTED_FILE_TAG,
194
202
  UNTRUSTED_GITHUB_TAG,
@@ -200,7 +208,7 @@ import {
200
208
  normalizePath,
201
209
  resolveIndexPath,
202
210
  toDisplayPath
203
- } from "./token-goat-chunk-ERTXEKB6.mjs";
211
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
204
212
  import {
205
213
  compileGuardedRegex
206
214
  } from "./token-goat-chunk-GMOUBOX4.mjs";
@@ -878,6 +886,20 @@ function renderContextWindow(displayFile, matchLine, window, matchSuffix = "", i
878
886
  );
879
887
  }
880
888
 
889
+ // src/emit.ts
890
+ init_define_import_meta_env();
891
+ function emit(text) {
892
+ const payload = colorStdout() ? text : stripAnsiEscapes(text);
893
+ process.stdout.write(ensureNewline(payload));
894
+ }
895
+ function emitErr(text) {
896
+ process.stderr.write(ensureNewline(text));
897
+ }
898
+
899
+ // src/query_limits.ts
900
+ init_define_import_meta_env();
901
+ var UNBOUNDED_QUERY_LIMIT = -1;
902
+
881
903
  // src/ref_blindness.ts
882
904
  init_define_import_meta_env();
883
905
  var REF_INDEXED_LANGUAGES = /* @__PURE__ */ new Set([
@@ -932,8 +954,8 @@ import { randomUUID } from "node:crypto";
932
954
  // src/graph_traversal.ts
933
955
  init_define_import_meta_env();
934
956
  var DEFAULT_REF_QUERY_LIMIT = 500;
935
- var ALL_SYMBOLS_IN_FILE_LIMIT = -1;
936
- var UNBOUNDED_REF_LIMIT = -1;
957
+ var ALL_SYMBOLS_IN_FILE_LIMIT = UNBOUNDED_QUERY_LIMIT;
958
+ var UNBOUNDED_REF_LIMIT = UNBOUNDED_QUERY_LIMIT;
937
959
  function enclosingSymbol(symbols, line) {
938
960
  let best = null;
939
961
  for (const s of symbols) {
@@ -1273,13 +1295,6 @@ function findCyclesCapped(graph) {
1273
1295
  init_define_import_meta_env();
1274
1296
  import * as fs from "node:fs";
1275
1297
  import * as path2 from "node:path";
1276
- function emit(text) {
1277
- const payload = colorStdout() ? text : stripAnsi(text);
1278
- process.stdout.write(ensureNewline(payload));
1279
- }
1280
- function emitErr(text) {
1281
- process.stderr.write(ensureNewline(text));
1282
- }
1283
1298
  function runDead(opts) {
1284
1299
  if (opts.top !== void 0 && opts.top <= 0) {
1285
1300
  emitErr(`--top must be a positive number, got: ${opts.top}`);
@@ -1947,21 +1962,14 @@ function renderModules(result, top) {
1947
1962
  }
1948
1963
 
1949
1964
  // src/graph_analysis.ts
1950
- function emit2(text) {
1951
- const payload = colorStdout() ? text : stripAnsi(text);
1952
- process.stdout.write(ensureNewline(payload));
1953
- }
1954
- function emitErr2(text) {
1955
- process.stderr.write(ensureNewline(text));
1956
- }
1957
1965
  function runSimilar(opts) {
1958
1966
  if (opts.top !== void 0 && opts.top <= 0) {
1959
- emitErr2(`--top must be a positive number, got: ${opts.top}`);
1967
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
1960
1968
  return 1;
1961
1969
  }
1962
1970
  const sepIdx = opts.spec.lastIndexOf("::");
1963
1971
  if (sepIdx < 0) {
1964
- emitErr2(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
1972
+ emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
1965
1973
  return 1;
1966
1974
  }
1967
1975
  const top = opts.top ?? 10;
@@ -1974,18 +1982,18 @@ function runSimilar(opts) {
1974
1982
  const matches = hits.filter((h) => !(h.filePath === anchor.filePath && h.name === anchor.name));
1975
1983
  const results = matches.slice(0, top);
1976
1984
  if (matches.length > results.length) {
1977
- emitErr2(`Showing top ${results.length} of ${matches.length} similar symbols (raise --top to see the rest).`);
1985
+ emitErr(`Showing top ${results.length} of ${matches.length} similar symbols (raise --top to see the rest).`);
1978
1986
  }
1979
1987
  if (opts.json === true) {
1980
- emit2(displaySafeJson(results.map((h) => ({ name: h.name, kind: h.kind, file: h.filePath, line: h.lineStart }))));
1988
+ emit(displaySafeJson(results.map((h) => ({ name: h.name, kind: h.kind, file: h.filePath, line: h.lineStart }))));
1981
1989
  return 0;
1982
1990
  }
1983
- for (const h of results) emit2(`${displaySafeText(h.name)} ${displaySafeText(h.kind)} ${formatSymbolLocation(displaySafeText(toDisplayPath(rootDir, h.filePath)), h.lineStart)}`);
1991
+ for (const h of results) emit(`${displaySafeText(h.name)} ${displaySafeText(h.kind)} ${formatSymbolLocation(displaySafeText(toDisplayPath(rootDir, h.filePath)), h.lineStart)}`);
1984
1992
  return 0;
1985
1993
  }
1986
1994
  function runContextFor(opts) {
1987
1995
  if (opts.top !== void 0 && opts.top <= 0) {
1988
- emitErr2(`--top must be a positive number, got: ${opts.top}`);
1996
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
1989
1997
  return 1;
1990
1998
  }
1991
1999
  const top = opts.top ?? 12;
@@ -1995,7 +2003,7 @@ function runContextFor(opts) {
1995
2003
  const moreBeyondTop = fetched.length > top;
1996
2004
  const hits = fetched.slice(0, top);
1997
2005
  if (hits.length === 0) {
1998
- emitErr2(`No matches found for '${opts.task}'`);
2006
+ emitErr(`No matches found for '${opts.task}'`);
1999
2007
  return 1;
2000
2008
  }
2001
2009
  const entries = [];
@@ -2014,19 +2022,19 @@ function runContextFor(opts) {
2014
2022
  );
2015
2023
  }
2016
2024
  if (moreBeyondTop) notices.push(`More matches exist beyond --top ${top} (raise it to see them).`);
2017
- for (const n of notices) emitErr2(n);
2025
+ for (const n of notices) emitErr(n);
2018
2026
  if (opts.json === true) {
2019
- emit2(displaySafeJson(entries));
2027
+ emit(displaySafeJson(entries));
2020
2028
  return 0;
2021
2029
  }
2022
- for (const e of entries) emit2(`token-goat read "${displaySafeText(toDisplayPath(rootDir, e.file))}::${displaySafeText(e.symbol)}@${e.line}"`);
2030
+ for (const e of entries) emit(`token-goat read "${displaySafeText(toDisplayPath(rootDir, e.file))}::${displaySafeText(e.symbol)}@${e.line}"`);
2023
2031
  return 0;
2024
2032
  }
2025
2033
  function runTestFor(opts) {
2026
2034
  const filePath = resolveIndexPath(opts.file);
2027
2035
  const symbols = querySymbols({ filePath, limit: ALL_SYMBOLS_IN_FILE_LIMIT });
2028
2036
  if (!fs3.existsSync(opts.file) && symbols.length === 0) {
2029
- emitErr2(`Could not read: ${opts.file}`);
2037
+ emitErr(`Could not read: ${opts.file}`);
2030
2038
  return 1;
2031
2039
  }
2032
2040
  const rootDir = resolveProjectRoot({ project: process.cwd() });
@@ -2051,22 +2059,22 @@ function runTestFor(opts) {
2051
2059
  }
2052
2060
  if (opts.json === true) {
2053
2061
  const capped = guardJsonRows(results.map((r) => ({ ...r, testFile: toDisplayPath(rootDir, r.testFile) })));
2054
- emit2(displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }));
2062
+ emit(displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }));
2055
2063
  return 0;
2056
2064
  }
2057
2065
  if (results.length === 0) {
2058
- emit2(`No test files found referencing symbols in '${displaySafeText(opts.file)}'`);
2066
+ emit(`No test files found referencing symbols in '${displaySafeText(opts.file)}'`);
2059
2067
  return 0;
2060
2068
  }
2061
2069
  for (const r of results) {
2062
- emit2(toDisplayPath(rootDir, r.testFile));
2063
- for (const fn of r.testFunctions) emit2(` ${fn}`);
2070
+ emit(toDisplayPath(rootDir, r.testFile));
2071
+ for (const fn of r.testFunctions) emit(` ${fn}`);
2064
2072
  }
2065
2073
  return 0;
2066
2074
  }
2067
2075
  function runCoverageGaps(opts) {
2068
2076
  if (opts.top !== void 0 && opts.top <= 0) {
2069
- emitErr2(`--top must be a positive number, got: ${opts.top}`);
2077
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
2070
2078
  return 1;
2071
2079
  }
2072
2080
  const top = opts.top ?? 50;
@@ -2084,30 +2092,30 @@ function runCoverageGaps(opts) {
2084
2092
  }
2085
2093
  const sliced = gaps.slice(0, top);
2086
2094
  if (gaps.length > sliced.length) {
2087
- emitErr2(`Showing top ${sliced.length} of ${gaps.length} coverage gaps (raise --top to see the rest).`);
2095
+ emitErr(`Showing top ${sliced.length} of ${gaps.length} coverage gaps (raise --top to see the rest).`);
2088
2096
  }
2089
2097
  if (opts.json === true) {
2090
- emit2(displaySafeJson(sliced));
2098
+ emit(displaySafeJson(sliced));
2091
2099
  return 0;
2092
2100
  }
2093
2101
  if (sliced.length === 0) {
2094
- emit2("No coverage gaps found.");
2095
- if (isIndexEmptyForProject(globalDbPath(), rootDir)) emit2(emptyIndexMessage(rootDir));
2102
+ emit("No coverage gaps found.");
2103
+ if (isIndexEmptyForProject(globalDbPath(), rootDir)) emit(emptyIndexMessage(rootDir));
2096
2104
  return 0;
2097
2105
  }
2098
- for (const g of sliced) emit2(`${displaySafeText(g.name)} ${displaySafeText(g.kind)} ${displaySafeText(toDisplayPath(rootDir, g.file))}:${g.line}`);
2106
+ for (const g of sliced) emit(`${displaySafeText(g.name)} ${displaySafeText(g.kind)} ${displaySafeText(toDisplayPath(rootDir, g.file))}:${g.line}`);
2099
2107
  return 0;
2100
2108
  }
2101
2109
  function runArch(opts) {
2102
2110
  if (opts.top !== void 0 && opts.top <= 0) {
2103
- emitErr2(`--top must be a positive number, got: ${opts.top}`);
2111
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
2104
2112
  return 1;
2105
2113
  }
2106
2114
  const cwd = opts.cwd ?? process.cwd();
2107
2115
  const top = opts.top ?? 10;
2108
2116
  const { files, graph, importedBy, resolve: resolve8 } = buildImportGraph(cwd);
2109
2117
  if (files.length === 0 && opts.json !== true) {
2110
- emit2(`no tracked files found under '${toDisplayPath(getDisplayRoot(cwd), cwd)}' (is it a git repo?). Nothing to analyse.`);
2118
+ emit(`no tracked files found under '${toDisplayPath(getDisplayRoot(cwd), cwd)}' (is it a git repo?). Nothing to analyse.`);
2111
2119
  return 0;
2112
2120
  }
2113
2121
  const allHubs = [...importedBy.entries()].sort((a, b) => b[1].size - a[1].size).map(([f, importers]) => ({ file: f, importedBy: importers.size }));
@@ -2120,22 +2128,22 @@ function runArch(opts) {
2120
2128
  const moduleResult = opts.modules === true ? detectModules({ files, graph, importedBy, resolve: resolve8 }, cwd, top) : null;
2121
2129
  if (opts.json === true) {
2122
2130
  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 };
2123
- emit2(displaySafeJson({ hubs, hubsTotal, hubsTruncated: hubs.length < hubsTotal, entryPoints, entryPointsTotal, entryPointsTruncated: entryPoints.length < entryPointsTotal, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {}, ...modulePayload }));
2131
+ emit(displaySafeJson({ hubs, hubsTotal, hubsTruncated: hubs.length < hubsTotal, entryPoints, entryPointsTotal, entryPointsTruncated: entryPoints.length < entryPointsTotal, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {}, ...modulePayload }));
2124
2132
  return 0;
2125
2133
  }
2126
- emit2(hubs.length < hubsTotal ? `hubs (top ${hubs.length} of ${hubsTotal} most-imported):` : `hubs (${hubsTotal} most-imported):`);
2127
- for (const h of hubs) emit2(` ${h.importedBy} importers ${displaySafeText(toDisplayPath(getDisplayRoot(opts.cwd), h.file))}`);
2128
- emit2(entryPoints.length < entryPointsTotal ? `entry points (imported by nobody, top ${entryPoints.length} of ${entryPointsTotal}):` : `entry points (imported by nobody, ${entryPointsTotal} found):`);
2129
- for (const e of entryPoints) emit2(` ${displaySafeText(toDisplayPath(getDisplayRoot(opts.cwd), e.file))}`);
2130
- emit2(cyclesTruncated ? `cycles (first ${cycles.length}, truncated at the ${MAX_CYCLES}-cycle enumeration limit: more cycles exist):` : `cycles (${cycles.length} found):`);
2131
- for (const c of cycles) emit2(` ${c.map((f) => toDisplayPath(getDisplayRoot(opts.cwd), f)).join(" -> ")}`);
2132
- if (moduleResult !== null) for (const line of renderModules(moduleResult, top)) emit2(line);
2134
+ emit(hubs.length < hubsTotal ? `hubs (top ${hubs.length} of ${hubsTotal} most-imported):` : `hubs (${hubsTotal} most-imported):`);
2135
+ for (const h of hubs) emit(` ${h.importedBy} importers ${displaySafeText(toDisplayPath(getDisplayRoot(opts.cwd), h.file))}`);
2136
+ emit(entryPoints.length < entryPointsTotal ? `entry points (imported by nobody, top ${entryPoints.length} of ${entryPointsTotal}):` : `entry points (imported by nobody, ${entryPointsTotal} found):`);
2137
+ for (const e of entryPoints) emit(` ${displaySafeText(toDisplayPath(getDisplayRoot(opts.cwd), e.file))}`);
2138
+ emit(cyclesTruncated ? `cycles (first ${cycles.length}, truncated at the ${MAX_CYCLES}-cycle enumeration limit: more cycles exist):` : `cycles (${cycles.length} found):`);
2139
+ for (const c of cycles) emit(` ${c.map((f) => toDisplayPath(getDisplayRoot(opts.cwd), f)).join(" -> ")}`);
2140
+ if (moduleResult !== null) for (const line of renderModules(moduleResult, top)) emit(line);
2133
2141
  return 0;
2134
2142
  }
2135
2143
  function runBlame(opts) {
2136
2144
  const sepIdx = opts.spec.lastIndexOf("::");
2137
2145
  if (sepIdx < 0) {
2138
- emitErr2(`Invalid spec - expected "file::symbol", got: ${displaySafeText(opts.spec)}`);
2146
+ emitErr(`Invalid spec - expected "file::symbol", got: ${displaySafeText(opts.spec)}`);
2139
2147
  return 1;
2140
2148
  }
2141
2149
  const cwd = opts.cwd ?? process.cwd();
@@ -2148,12 +2156,12 @@ function runBlame(opts) {
2148
2156
  try {
2149
2157
  const result = runGit(["blame", "-L", `${start},${end}`, "--", filePath], { cwd });
2150
2158
  if (result.exitCode !== 0) {
2151
- emitErr2(`git blame failed: ${displaySafeText(result.stderr)}`);
2159
+ emitErr(`git blame failed: ${displaySafeText(result.stderr)}`);
2152
2160
  return 1;
2153
2161
  }
2154
2162
  raw = result.stdout;
2155
2163
  } catch (e) {
2156
- emitErr2(`git blame failed: ${extractErrorMessage(e)}`);
2164
+ emitErr(`git blame failed: ${extractErrorMessage(e)}`);
2157
2165
  return 1;
2158
2166
  }
2159
2167
  if (opts.json === true) {
@@ -2162,32 +2170,25 @@ function runBlame(opts) {
2162
2170
  if (!m) return { raw: l };
2163
2171
  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] };
2164
2172
  });
2165
- emit2(displaySafeJson({ symbol: sym.name, file: filePath, lines }));
2173
+ emit(displaySafeJson({ symbol: sym.name, file: filePath, lines }));
2166
2174
  return 0;
2167
2175
  }
2168
- emit2(`${displaySafeText(sym.name)} ${displaySafeText(toDisplayPath(getDisplayRoot(opts.cwd), filePath))}:${start}-${end}`);
2169
- emit2(fenceUntrusted(raw.trim(), UNTRUSTED_FILE_TAG));
2176
+ emit(`${displaySafeText(sym.name)} ${displaySafeText(toDisplayPath(getDisplayRoot(opts.cwd), filePath))}:${start}-${end}`);
2177
+ emit(fenceUntrusted(raw.trim(), UNTRUSTED_FILE_TAG));
2170
2178
  return 0;
2171
2179
  }
2172
2180
 
2173
2181
  // src/graph_commands.ts
2174
- function emit3(text) {
2175
- const payload = colorStdout() ? text : stripAnsi(text);
2176
- process.stdout.write(ensureNewline(payload));
2177
- }
2178
- function emitErr3(text) {
2179
- process.stderr.write(ensureNewline(text));
2180
- }
2181
2182
  function runCallers(opts) {
2182
2183
  if (opts.limit !== void 0 && opts.limit <= 0) {
2183
- emitErr3(`--limit must be a positive number, got: ${opts.limit}`);
2184
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
2184
2185
  return 1;
2185
2186
  }
2186
2187
  const rootDir = resolveProjectRoot({ project: process.cwd() });
2187
2188
  const { name, file } = parseGraphSymbolSpec(opts.symbol);
2188
2189
  const fileHint = file !== void 0 ? resolveIndexPath(file, rootDir) : void 0;
2189
2190
  if (fileHint !== void 0 && querySymbols({ name, filePath: fileHint, limit: 1 }).length === 0) {
2190
- emitErr3(`Symbol '${name}' not found in '${file}'`);
2191
+ emitErr(`Symbol '${name}' not found in '${file}'`);
2191
2192
  return 1;
2192
2193
  }
2193
2194
  const unbounded = opts.excludeTests === true || opts.grep !== void 0 || fileHint !== void 0;
@@ -2204,41 +2205,41 @@ function runCallers(opts) {
2204
2205
  if (entries.length === 0) {
2205
2206
  if (matchesGrep !== void 0 && preGrepCount > 0) {
2206
2207
  if (opts.json === true) {
2207
- emit3(displaySafeJson({ items: [], truncated: false, totalCount: 0, hiddenByGrep: preGrepCount }));
2208
+ emit(displaySafeJson({ items: [], truncated: false, totalCount: 0, hiddenByGrep: preGrepCount }));
2208
2209
  return 0;
2209
2210
  }
2210
- emit3(grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "caller", "callers"));
2211
+ emit(grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "caller", "callers"));
2211
2212
  return 0;
2212
2213
  }
2213
2214
  if (opts.excludeTests === true && suppressed > 0) {
2214
2215
  if (opts.json === true) {
2215
- emit3(displaySafeJson({ items: [], truncated: false, totalCount: 0, hiddenByExcludeTests: suppressed }));
2216
+ emit(displaySafeJson({ items: [], truncated: false, totalCount: 0, hiddenByExcludeTests: suppressed }));
2216
2217
  return 1;
2217
2218
  }
2218
- emitErr3(`No non-test references found for '${opts.symbol}' (${excludeTestsHiddenNote(suppressed)})`);
2219
+ emitErr(`No non-test references found for '${opts.symbol}' (${excludeTestsHiddenNote(suppressed)})`);
2219
2220
  return 1;
2220
2221
  }
2221
2222
  const defRows = querySymbols({ name, rootDir, limit: REF_BLIND_DEF_PROBE_LIMIT });
2222
2223
  if (defRows.length === 0) {
2223
- emitErr3(`Symbol not found: ${opts.symbol}${unknownSymbolSuggestion(name, rootDir)}`);
2224
- if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr3(emptyIndexMessage(rootDir));
2224
+ emitErr(`Symbol not found: ${opts.symbol}${unknownSymbolSuggestion(name, rootDir)}`);
2225
+ if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr(emptyIndexMessage(rootDir));
2225
2226
  return 1;
2226
2227
  }
2227
2228
  const defPaths = fileHint !== void 0 ? [fileHint] : defRows.map((r) => r.filePath);
2228
2229
  const firstDefPath = defPaths[0];
2229
2230
  if (firstDefPath !== void 0 && defPaths.every((fp) => !isRefIndexedFile(fp))) {
2230
- emitErr3(refBlindLanguageNotice(name, detectLanguageOfFile(firstDefPath), toDisplayPath(rootDir, firstDefPath)));
2231
+ emitErr(refBlindLanguageNotice(name, detectLanguageOfFile(firstDefPath), toDisplayPath(rootDir, firstDefPath)));
2231
2232
  return 1;
2232
2233
  }
2233
2234
  const kindRows = fileHint !== void 0 ? querySymbols({ name, filePath: fileHint, limit: REF_BLIND_DEF_PROBE_LIMIT }) : defRows;
2234
2235
  const kindVerdict = refBlindKindVerdict(kindRows);
2235
2236
  if (kindVerdict.allBlind) {
2236
- emitErr3(refBlindKindNotice(name, kindVerdict.blindKinds));
2237
+ emitErr(refBlindKindNotice(name, kindVerdict.blindKinds));
2237
2238
  return 1;
2238
2239
  }
2239
- emitErr3(`No references found for '${opts.symbol}'`);
2240
- if (kindVerdict.blindCount > 0) emitErr3(refBlindKindPartialNote(name, kindVerdict.blindKinds, kindVerdict.blindCount, kindRows.length));
2241
- if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr3(emptyIndexMessage(rootDir));
2240
+ emitErr(`No references found for '${opts.symbol}'`);
2241
+ if (kindVerdict.blindCount > 0) emitErr(refBlindKindPartialNote(name, kindVerdict.blindKinds, kindVerdict.blindCount, kindRows.length));
2242
+ if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr(emptyIndexMessage(rootDir));
2242
2243
  return 1;
2243
2244
  }
2244
2245
  const contextLines = opts.context ?? 0;
@@ -2251,7 +2252,7 @@ function runCallers(opts) {
2251
2252
  const capped = guardJsonRows(rows);
2252
2253
  const limitTruncated = entries.length < filtered.length || sqlTruncated;
2253
2254
  const hiddenByGrep = preGrepCount - filtered.length;
2254
- emit3(displaySafeJson({
2255
+ emit(displaySafeJson({
2255
2256
  items: capped.items,
2256
2257
  truncated: capped.truncated || limitTruncated,
2257
2258
  totalCount: filtered.length,
@@ -2261,19 +2262,24 @@ function runCallers(opts) {
2261
2262
  return 0;
2262
2263
  }
2263
2264
  if (opts.excludeTests === true && suppressed > 0) {
2264
- emit3(`${countNoun(entries.length, "caller")} found (${excludeTestsHiddenNote(suppressed)})`);
2265
+ emit(`${countNoun(entries.length, "caller")} found (${excludeTestsHiddenNote(suppressed)})`);
2266
+ }
2267
+ const overflowed = sqlTruncated || entries.length < filtered.length;
2268
+ if (overflowed) {
2269
+ const totalCallers = sqlTruncated ? countRefs({ name, rootDir }) : filtered.length;
2270
+ emitErr(`Showing the first ${entries.length} of ${countNoun(totalCallers, "caller")} (raise --limit to see the rest).`);
2265
2271
  }
2266
2272
  for (const e of entries) {
2267
2273
  const displayPath = toDisplayPath(rootDir, e.file);
2268
- emit3(`${e.caller} ${displayPath}:${e.line}`);
2274
+ emit(`${e.caller} ${displayPath}:${e.line}`);
2269
2275
  const window = buildContextWindow(e.file, e.line, contextLines);
2270
- if (window !== null) for (const l of renderContextWindow(displayPath, e.line, window, "", " ")) emit3(l);
2276
+ if (window !== null) for (const l of renderContextWindow(displayPath, e.line, window, "", " ")) emit(l);
2271
2277
  }
2272
2278
  return 0;
2273
2279
  }
2274
2280
  function runCallChain(opts) {
2275
2281
  if (opts.depth !== void 0 && opts.depth <= 0) {
2276
- emitErr3(`--depth must be a positive number, got: ${opts.depth}`);
2282
+ emitErr(`--depth must be a positive number, got: ${opts.depth}`);
2277
2283
  return 1;
2278
2284
  }
2279
2285
  const maxDepth = opts.depth ?? 8;
@@ -2282,13 +2288,13 @@ function runCallChain(opts) {
2282
2288
  const fileHint = file !== void 0 ? resolveIndexPath(file, rootDir) : void 0;
2283
2289
  if (fileHint !== void 0) {
2284
2290
  if (querySymbols({ name, filePath: fileHint, limit: 1 }).length === 0) {
2285
- emitErr3(`Symbol '${name}' not found in '${file}'`);
2286
- if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr3(emptyIndexMessage(rootDir));
2291
+ emitErr(`Symbol '${name}' not found in '${file}'`);
2292
+ if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr(emptyIndexMessage(rootDir));
2287
2293
  return 1;
2288
2294
  }
2289
2295
  } else if (querySymbols({ name, rootDir, limit: 1 }).length === 0) {
2290
- emitErr3(`Symbol not found: ${opts.symbol}${unknownSymbolSuggestion(name, rootDir)}`);
2291
- if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr3(emptyIndexMessage(rootDir));
2296
+ emitErr(`Symbol not found: ${opts.symbol}${unknownSymbolSuggestion(name, rootDir)}`);
2297
+ if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr(emptyIndexMessage(rootDir));
2292
2298
  return 1;
2293
2299
  }
2294
2300
  const refBlindRootPath = () => {
@@ -2326,7 +2332,7 @@ function runCallChain(opts) {
2326
2332
  const refBlindKinds = noCallers ? refBlindRootKinds().blindKinds : [];
2327
2333
  const blindRootPath = noCallers ? refBlindRootPath() : void 0;
2328
2334
  const refBlindLanguage = blindRootPath !== void 0 ? { language: detectLanguageOfFile(blindRootPath), definedIn: toDisplayPath(rootDir, blindRootPath) } : void 0;
2329
- emit3(displaySafeJson({
2335
+ emit(displaySafeJson({
2330
2336
  chains: filteredChains,
2331
2337
  ...hiddenByGrep > 0 ? { hiddenByGrep } : {},
2332
2338
  ...hiddenByExcludeTests > 0 ? { hiddenByExcludeTests } : {},
@@ -2337,37 +2343,37 @@ function runCallChain(opts) {
2337
2343
  }
2338
2344
  if (noCallers) {
2339
2345
  if (opts.excludeTests === true && suppressedCount > 0) {
2340
- emit3(`${name} (no non-test callers; ${excludeTestsHiddenNote(suppressedCount)})`);
2346
+ emit(`${name} (no non-test callers; ${excludeTestsHiddenNote(suppressedCount)})`);
2341
2347
  return 0;
2342
2348
  }
2343
2349
  const blindRoot = refBlindRootPath();
2344
2350
  if (blindRoot !== void 0) {
2345
- emitErr3(refBlindLanguageNotice(name, detectLanguageOfFile(blindRoot), toDisplayPath(rootDir, blindRoot)));
2346
- emit3(`${name} (no callers recorded)`);
2351
+ emitErr(refBlindLanguageNotice(name, detectLanguageOfFile(blindRoot), toDisplayPath(rootDir, blindRoot)));
2352
+ emit(`${name} (no callers recorded)`);
2347
2353
  return 0;
2348
2354
  }
2349
2355
  const rootKinds = refBlindRootKinds();
2350
2356
  if (rootKinds.allBlind) {
2351
- emitErr3(refBlindKindNotice(name, rootKinds.blindKinds));
2352
- emit3(`${name} (no callers recorded)`);
2357
+ emitErr(refBlindKindNotice(name, rootKinds.blindKinds));
2358
+ emit(`${name} (no callers recorded)`);
2353
2359
  return 0;
2354
2360
  }
2355
- emit3(`${name} (no callers)`);
2356
- if (rootKinds.blindCount > 0) emitErr3(refBlindKindPartialNote(name, rootKinds.blindKinds, rootKinds.blindCount, rootKinds.total));
2361
+ emit(`${name} (no callers)`);
2362
+ if (rootKinds.blindCount > 0) emitErr(refBlindKindPartialNote(name, rootKinds.blindKinds, rootKinds.blindCount, rootKinds.total));
2357
2363
  return 0;
2358
2364
  }
2359
2365
  if (matchesGrep !== void 0 && filteredChains.length === 0) {
2360
- emit3(grepFilteredToEmptyNotice(chains.length, opts.grep, "chain", "chains"));
2366
+ emit(grepFilteredToEmptyNotice(chains.length, opts.grep, "chain", "chains"));
2361
2367
  return 0;
2362
2368
  }
2363
2369
  for (const chain of filteredChains) {
2364
- emit3(chain.join(" -> "));
2370
+ emit(chain.join(" -> "));
2365
2371
  }
2366
2372
  return 0;
2367
2373
  }
2368
2374
  function runImpact(opts) {
2369
2375
  if (opts.top !== void 0 && opts.top <= 0) {
2370
- emitErr3(`--top must be a positive number, got: ${opts.top}`);
2376
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
2371
2377
  return 1;
2372
2378
  }
2373
2379
  const top = opts.top ?? 20;
@@ -2376,7 +2382,7 @@ function runImpact(opts) {
2376
2382
  const { name: rootName, file } = parseGraphSymbolSpec(opts.symbol);
2377
2383
  const fileHint = file !== void 0 ? resolveIndexPath(file, rootDir) : void 0;
2378
2384
  if (fileHint !== void 0 && querySymbols({ name: rootName, filePath: fileHint, limit: 1 }).length === 0) {
2379
- emitErr3(`Symbol '${rootName}' not found in '${file}'`);
2385
+ emitErr(`Symbol '${rootName}' not found in '${file}'`);
2380
2386
  return 1;
2381
2387
  }
2382
2388
  const getSyms = buildFileSymCache();
@@ -2421,64 +2427,64 @@ function runImpact(opts) {
2421
2427
  const grepped = matchesGrep !== void 0 ? allSorted.filter(([symbol]) => matchesGrep(symbol)) : allSorted;
2422
2428
  const sorted = grepped.slice(0, top);
2423
2429
  if (depthCapped > 0) {
2424
- emitErr3(`Impact truncated at the ${DEPTH_CAP}-hop depth limit: ${depthCapped} symbol${depthCapped === 1 ? "" : "s"} at that depth ${depthCapped === 1 ? "was" : "were"} not expanded, so callers reachable only beyond ${DEPTH_CAP} hops are missing.`);
2430
+ emitErr(`Impact truncated at the ${DEPTH_CAP}-hop depth limit: ${depthCapped} symbol${depthCapped === 1 ? "" : "s"} at that depth ${depthCapped === 1 ? "was" : "were"} not expanded, so callers reachable only beyond ${DEPTH_CAP} hops are missing.`);
2425
2431
  }
2426
2432
  if (grepped.length > sorted.length) {
2427
- emitErr3(`Showing top ${sorted.length} of ${grepped.length} impacted symbols (raise --top to see the rest).`);
2433
+ emitErr(`Showing top ${sorted.length} of ${grepped.length} impacted symbols (raise --top to see the rest).`);
2428
2434
  }
2429
2435
  if (matchesGrep !== void 0 && sorted.length === 0 && allSorted.length > 0) {
2430
2436
  if (opts.json === true) {
2431
- emitErr3(grepFilteredToEmptyNotice(allSorted.length, opts.grep, "impacted symbol", "impacted symbols"));
2432
- emit3(displaySafeJson([]));
2437
+ emitErr(grepFilteredToEmptyNotice(allSorted.length, opts.grep, "impacted symbol", "impacted symbols"));
2438
+ emit(displaySafeJson([]));
2433
2439
  return 0;
2434
2440
  }
2435
- emit3(grepFilteredToEmptyNotice(allSorted.length, opts.grep, "impacted symbol", "impacted symbols"));
2441
+ emit(grepFilteredToEmptyNotice(allSorted.length, opts.grep, "impacted symbol", "impacted symbols"));
2436
2442
  return 0;
2437
2443
  }
2438
2444
  if (sorted.length === 0) {
2439
2445
  if (opts.excludeTests === true && suppressedCount > 0) {
2440
2446
  if (opts.json === true) {
2441
- emit3(displaySafeJson([]));
2447
+ emit(displaySafeJson([]));
2442
2448
  return 1;
2443
2449
  }
2444
- emitErr3(`No non-test impact found for '${opts.symbol}' (${excludeTestsHiddenNote(suppressedCount)})`);
2450
+ emitErr(`No non-test impact found for '${opts.symbol}' (${excludeTestsHiddenNote(suppressedCount)})`);
2445
2451
  return 1;
2446
2452
  }
2447
2453
  const defRows = querySymbols({ name: rootName, rootDir, limit: REF_BLIND_DEF_PROBE_LIMIT });
2448
2454
  if (defRows.length === 0) {
2449
- emitErr3(`Symbol not found: ${opts.symbol}${unknownSymbolSuggestion(rootName, rootDir)}`);
2455
+ emitErr(`Symbol not found: ${opts.symbol}${unknownSymbolSuggestion(rootName, rootDir)}`);
2450
2456
  return 1;
2451
2457
  }
2452
2458
  const defPaths = fileHint !== void 0 ? [fileHint] : defRows.map((r) => r.filePath);
2453
2459
  const firstDefPath = defPaths[0];
2454
2460
  if (firstDefPath !== void 0 && defPaths.every((fp) => !isRefIndexedFile(fp))) {
2455
- emitErr3(refBlindLanguageNotice(rootName, detectLanguageOfFile(firstDefPath), toDisplayPath(rootDir, firstDefPath)));
2461
+ emitErr(refBlindLanguageNotice(rootName, detectLanguageOfFile(firstDefPath), toDisplayPath(rootDir, firstDefPath)));
2456
2462
  return 1;
2457
2463
  }
2458
2464
  const kindRows = fileHint !== void 0 ? querySymbols({ name: rootName, filePath: fileHint, limit: REF_BLIND_DEF_PROBE_LIMIT }) : defRows;
2459
2465
  const kindVerdict = refBlindKindVerdict(kindRows);
2460
2466
  if (kindVerdict.allBlind) {
2461
- emitErr3(refBlindKindNotice(rootName, kindVerdict.blindKinds));
2467
+ emitErr(refBlindKindNotice(rootName, kindVerdict.blindKinds));
2462
2468
  return 1;
2463
2469
  }
2464
- emitErr3(`No callers found for '${opts.symbol}'`);
2465
- if (kindVerdict.blindCount > 0) emitErr3(refBlindKindPartialNote(rootName, kindVerdict.blindKinds, kindVerdict.blindCount, kindRows.length));
2470
+ emitErr(`No callers found for '${opts.symbol}'`);
2471
+ if (kindVerdict.blindCount > 0) emitErr(refBlindKindPartialNote(rootName, kindVerdict.blindKinds, kindVerdict.blindCount, kindRows.length));
2466
2472
  return 1;
2467
2473
  }
2468
2474
  if (opts.json === true) {
2469
- emit3(displaySafeJson(sorted.map(([symbol, h]) => ({ symbol, hops: h }))));
2475
+ emit(displaySafeJson(sorted.map(([symbol, h]) => ({ symbol, hops: h }))));
2470
2476
  return 0;
2471
2477
  }
2472
2478
  const MODULE_SCOPE_PREFIX = "(module scope) ";
2473
2479
  for (const [symbol, h] of sorted) {
2474
2480
  const displaySymbol = symbol.startsWith(MODULE_SCOPE_PREFIX) ? MODULE_SCOPE_PREFIX + toDisplayPath(rootDir, symbol.slice(MODULE_SCOPE_PREFIX.length)) : symbol;
2475
- emit3(`${displaySymbol} (hops: ${h})`);
2481
+ emit(`${displaySymbol} (hops: ${h})`);
2476
2482
  }
2477
2483
  return 0;
2478
2484
  }
2479
2485
  function runAsk(opts) {
2480
2486
  if (opts.top !== void 0 && opts.top <= 0) {
2481
- emitErr3(`--top must be a positive number, got: ${opts.top}`);
2487
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
2482
2488
  return 1;
2483
2489
  }
2484
2490
  const top = opts.top ?? 8;
@@ -2487,7 +2493,7 @@ function runAsk(opts) {
2487
2493
  const moreBeyondTop = fetched.length > top;
2488
2494
  const hits = fetched.slice(0, top);
2489
2495
  if (moreBeyondTop) {
2490
- emitErr3(`Answer is grounded in the top ${countNoun(top, "match", "matches")}; more matched (raise --top to widen the evidence).`);
2496
+ emitErr(`Answer is grounded in the top ${countNoun(top, "match", "matches")}; more matched (raise --top to widen the evidence).`);
2491
2497
  }
2492
2498
  warnIfFilesStale(hits.map((h) => h.filePath));
2493
2499
  const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
@@ -2495,12 +2501,12 @@ function runAsk(opts) {
2495
2501
  const entries = hits.map((h) => ({ file: h.filePath, symbol: h.name, kind: h.kind, line: h.lineStart, readCmd: `token-goat read "${h.filePath}::${h.name}@${h.lineStart}"` }));
2496
2502
  const degrade = (reason, extraNote) => {
2497
2503
  if (opts.json === true) {
2498
- emit3(displaySafeJson({ degraded: true, note: reason, ...extraNote !== void 0 ? { hint: extraNote } : {}, context: entries }));
2504
+ emit(displaySafeJson({ degraded: true, note: reason, ...extraNote !== void 0 ? { hint: extraNote } : {}, context: entries }));
2499
2505
  return 0;
2500
2506
  }
2501
- emit3(`[degraded mode - ${reason}]`);
2502
- if (extraNote !== void 0) emit3(extraNote);
2503
- for (const e of entries) emit3(`token-goat read "${displaySafeText(toDisplayPath(rootDir, e.file))}::${displaySafeText(e.symbol)}@${e.line}"`);
2507
+ emit(`[degraded mode - ${reason}]`);
2508
+ if (extraNote !== void 0) emit(extraNote);
2509
+ for (const e of entries) emit(`token-goat read "${displaySafeText(toDisplayPath(rootDir, e.file))}::${displaySafeText(e.symbol)}@${e.line}"`);
2504
2510
  return 0;
2505
2511
  };
2506
2512
  if (!backendLabel) return degrade(`set ${BACKEND_ENV}=claude|codex for LLM synthesis`);
@@ -2547,9 +2553,9 @@ ANSWER:`;
2547
2553
  }
2548
2554
  if (answer) {
2549
2555
  if (opts.json === true) {
2550
- emit3(displaySafeJson({ answer, context: entries }));
2556
+ emit(displaySafeJson({ answer, context: entries }));
2551
2557
  } else {
2552
- emit3(answer);
2558
+ emit(answer);
2553
2559
  }
2554
2560
  return 0;
2555
2561
  }
@@ -3046,6 +3052,17 @@ async function takeScreenshot(url, destPath, opts) {
3046
3052
  return { path: finalPath, originalBytes: buffer.length, finalBytes: finalBuffer.length };
3047
3053
  }
3048
3054
 
3055
+ // src/symbol_scan.ts
3056
+ init_define_import_meta_env();
3057
+ var SYMBOL_SCAN_PAGE = 1e4;
3058
+ function forEachSymbol(queryOpts, visit) {
3059
+ for (let offset = 0; ; offset += SYMBOL_SCAN_PAGE) {
3060
+ const rows = querySymbols({ ...queryOpts, limit: SYMBOL_SCAN_PAGE, offset });
3061
+ for (const row of rows) visit(row);
3062
+ if (rows.length < SYMBOL_SCAN_PAGE) return;
3063
+ }
3064
+ }
3065
+
3049
3066
  // src/ts_refs.ts
3050
3067
  init_define_import_meta_env();
3051
3068
  import { createRequire } from "node:module";
@@ -7368,7 +7385,7 @@ function fenceHtmlText(text) {
7368
7385
  function runCsvQuery(opts) {
7369
7386
  const text = readFileText(opts.file);
7370
7387
  if (text === null) {
7371
- emitErr4(`Could not read: ${opts.file}`);
7388
+ emitErr(`Could not read: ${opts.file}`);
7372
7389
  return 1;
7373
7390
  }
7374
7391
  const columns = opts.columns ? opts.columns.split(",").map((c) => c.trim()).filter(Boolean) : void 0;
@@ -7376,7 +7393,7 @@ function runCsvQuery(opts) {
7376
7393
  try {
7377
7394
  head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
7378
7395
  } catch (e) {
7379
- emitErr4(extractErrorMessage(e));
7396
+ emitErr(extractErrorMessage(e));
7380
7397
  return 1;
7381
7398
  }
7382
7399
  try {
@@ -7389,7 +7406,7 @@ function runCsvQuery(opts) {
7389
7406
  ...opts.noHeader === true ? { noHeader: true } : {}
7390
7407
  });
7391
7408
  if (result.header.length === 0) {
7392
- emit4(`No data rows found in ${displaySafeText(opts.file)}`);
7409
+ emit(`No data rows found in ${displaySafeText(opts.file)}`);
7393
7410
  return 0;
7394
7411
  }
7395
7412
  const fullSourceBytes = sumFileSizes([opts.file]);
@@ -7398,23 +7415,23 @@ function runCsvQuery(opts) {
7398
7415
  const headTruncated = result.rows.length < result.totalRows;
7399
7416
  const capped = guardJsonRows(rowsJson);
7400
7417
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount: result.totalRows, ...result.totalRows === 0 && result.preFilterRows > 0 ? { filteredFromRows: result.preFilterRows } : {} }, 0);
7401
- emit4(jsonText);
7418
+ emit(jsonText);
7402
7419
  recordReadStat("csv_query", fullSourceBytes, jsonText, opts.file);
7403
7420
  } else {
7404
7421
  const tableText = formatCsvTable(result, (opts.where ?? []).map((w) => `--where ${w}`));
7405
- emit4(tableText);
7422
+ emit(tableText);
7406
7423
  recordReadStat("csv_query", fullSourceBytes, tableText, opts.file);
7407
7424
  }
7408
7425
  return 0;
7409
7426
  } catch (e) {
7410
- emitErr4(extractErrorMessage(e));
7427
+ emitErr(extractErrorMessage(e));
7411
7428
  return 1;
7412
7429
  }
7413
7430
  }
7414
7431
  function runCsvProfile(opts) {
7415
7432
  const text = readFileText(opts.file);
7416
7433
  if (text === null) {
7417
- emitErr4(`Could not read: ${opts.file}`);
7434
+ emitErr(`Could not read: ${opts.file}`);
7418
7435
  return 1;
7419
7436
  }
7420
7437
  try {
@@ -7423,41 +7440,41 @@ function runCsvProfile(opts) {
7423
7440
  ...opts.noHeader === true ? { noHeader: true } : {}
7424
7441
  });
7425
7442
  if (profiles.length === 0) {
7426
- emit4(`No data rows found in ${displaySafeText(opts.file)}`);
7443
+ emit(`No data rows found in ${displaySafeText(opts.file)}`);
7427
7444
  return 0;
7428
7445
  }
7429
7446
  const fullSourceBytes = sumFileSizes([opts.file]);
7430
7447
  const profileText = formatCsvProfile(profiles);
7431
- emit4(profileText);
7448
+ emit(profileText);
7432
7449
  recordReadStat("csv_profile", fullSourceBytes, profileText, opts.file);
7433
7450
  return 0;
7434
7451
  } catch (e) {
7435
- emitErr4(extractErrorMessage(e));
7452
+ emitErr(extractErrorMessage(e));
7436
7453
  return 1;
7437
7454
  }
7438
7455
  }
7439
7456
  function runOutlineCommand(opts, parse, formatLabel, kind) {
7440
7457
  const text = readFileText(opts.file);
7441
7458
  if (text === null) {
7442
- emitErr4(`Could not read: ${opts.file}`);
7459
+ emitErr(`Could not read: ${opts.file}`);
7443
7460
  return 1;
7444
7461
  }
7445
7462
  let data;
7446
7463
  try {
7447
7464
  data = parse(text);
7448
7465
  } catch {
7449
- emitErr4(`Failed to parse ${formatLabel}: ${opts.file}`);
7466
+ emitErr(`Failed to parse ${formatLabel}: ${opts.file}`);
7450
7467
  return 1;
7451
7468
  }
7452
7469
  const outline = outlineJson(data);
7453
7470
  const fullSourceBytes = sumFileSizes([opts.file]);
7454
7471
  if (opts.json === true) {
7455
7472
  const jsonText = displaySafeJson(outline, 0);
7456
- emit4(jsonText);
7473
+ emit(jsonText);
7457
7474
  recordReadStat(kind, fullSourceBytes, jsonText, opts.file);
7458
7475
  } else {
7459
7476
  const text2 = formatJsonOutline(outline);
7460
- emit4(text2);
7477
+ emit(text2);
7461
7478
  recordReadStat(kind, fullSourceBytes, text2, opts.file);
7462
7479
  }
7463
7480
  return 0;
@@ -7468,21 +7485,21 @@ function runJsonOutline(opts) {
7468
7485
  function runQueryCommand(opts, parse, formatLabel, guardTag, kind) {
7469
7486
  const text = readFileText(opts.file);
7470
7487
  if (text === null) {
7471
- emitErr4(`Could not read: ${opts.file}`);
7488
+ emitErr(`Could not read: ${opts.file}`);
7472
7489
  return 1;
7473
7490
  }
7474
7491
  let data;
7475
7492
  try {
7476
7493
  data = parse(text);
7477
7494
  } catch {
7478
- emitErr4(`Failed to parse ${formatLabel}: ${opts.file}`);
7495
+ emitErr(`Failed to parse ${formatLabel}: ${opts.file}`);
7479
7496
  return 1;
7480
7497
  }
7481
7498
  let head;
7482
7499
  try {
7483
7500
  head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
7484
7501
  } catch (e) {
7485
- emitErr4(extractErrorMessage(e));
7502
+ emitErr(extractErrorMessage(e));
7486
7503
  return 1;
7487
7504
  }
7488
7505
  try {
@@ -7491,7 +7508,7 @@ function runQueryCommand(opts, parse, formatLabel, guardTag, kind) {
7491
7508
  if (!result.fanned) {
7492
7509
  const value = result.items[0];
7493
7510
  const valueText = opts.json === true ? displaySafeJson(value, 0) : displaySafeJson(value);
7494
- emit4(valueText);
7511
+ emit(valueText);
7495
7512
  recordReadStat(kind, fullSourceBytes, valueText, opts.file);
7496
7513
  return 0;
7497
7514
  }
@@ -7501,7 +7518,7 @@ function runQueryCommand(opts, parse, formatLabel, guardTag, kind) {
7501
7518
  if (opts.json === true) {
7502
7519
  const capped = guardJsonRows(limited);
7503
7520
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated || headTruncated || result.truncated, totalCount }, 0);
7504
- emit4(jsonText);
7521
+ emit(jsonText);
7505
7522
  recordReadStat(kind, fullSourceBytes, jsonText, opts.file);
7506
7523
  } else {
7507
7524
  const lines = limited.map((item) => displaySafeJson(item, 0));
@@ -7517,7 +7534,7 @@ function runQueryCommand(opts, parse, formatLabel, guardTag, kind) {
7517
7534
  }
7518
7535
  return 0;
7519
7536
  } catch (e) {
7520
- emitErr4(extractErrorMessage(e));
7537
+ emitErr(extractErrorMessage(e));
7521
7538
  return 1;
7522
7539
  }
7523
7540
  }
@@ -7559,21 +7576,21 @@ function runYamlQuery(opts) {
7559
7576
  function runXmlOutline(opts) {
7560
7577
  const text = readFileText(opts.file);
7561
7578
  if (text === null) {
7562
- emitErr4(`Could not read: ${opts.file}`);
7579
+ emitErr(`Could not read: ${opts.file}`);
7563
7580
  return 1;
7564
7581
  }
7565
7582
  let summary;
7566
7583
  try {
7567
7584
  summary = outlineXml(text, { ...opts.maxDepth !== void 0 ? { maxDepth: opts.maxDepth } : {} });
7568
7585
  } catch (e) {
7569
- emitErr4(`Failed to parse XML: ${opts.file}
7586
+ emitErr(`Failed to parse XML: ${opts.file}
7570
7587
  ${extractErrorMessage(e)}`);
7571
7588
  return 1;
7572
7589
  }
7573
7590
  const fullSourceBytes = sumFileSizes([opts.file]);
7574
7591
  if (opts.json === true) {
7575
7592
  const jsonText = displaySafeJson(summary);
7576
- emit4(jsonText);
7593
+ emit(jsonText);
7577
7594
  recordReadStat("xml_outline", fullSourceBytes, jsonText, opts.file);
7578
7595
  } else {
7579
7596
  const outlineText = formatXmlOutline(summary);
@@ -7585,19 +7602,19 @@ ${extractErrorMessage(e)}`);
7585
7602
  function runXmlQuery(opts) {
7586
7603
  const text = readFileText(opts.file);
7587
7604
  if (text === null) {
7588
- emitErr4(`Could not read: ${opts.file}`);
7605
+ emitErr(`Could not read: ${opts.file}`);
7589
7606
  return 1;
7590
7607
  }
7591
7608
  const queryPath = (opts.xpath ?? opts.path ?? "").trim();
7592
7609
  if (!queryPath) {
7593
- emitErr4("Must provide a path or --xpath <expression>");
7610
+ emitErr("Must provide a path or --xpath <expression>");
7594
7611
  return 1;
7595
7612
  }
7596
7613
  let head;
7597
7614
  try {
7598
7615
  head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
7599
7616
  } catch (e) {
7600
- emitErr4(extractErrorMessage(e));
7617
+ emitErr(extractErrorMessage(e));
7601
7618
  return 1;
7602
7619
  }
7603
7620
  try {
@@ -7607,10 +7624,10 @@ function runXmlQuery(opts) {
7607
7624
  if (result.attributeValues.length === 0) {
7608
7625
  if (opts.json === true) {
7609
7626
  const jsonText = displaySafeJson({ items: [], truncated: false, totalCount: 0 }, 0);
7610
- emit4(jsonText);
7627
+ emit(jsonText);
7611
7628
  recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
7612
7629
  } else {
7613
- emit4(`No attributes matched path: '${displaySafeText(queryPath)}'`);
7630
+ emit(`No attributes matched path: '${displaySafeText(queryPath)}'`);
7614
7631
  }
7615
7632
  return 0;
7616
7633
  }
@@ -7642,7 +7659,7 @@ ${val}`;
7642
7659
  outText = val;
7643
7660
  }
7644
7661
  }
7645
- emit4(outText);
7662
+ emit(outText);
7646
7663
  recordReadStat("xml_query", fullSourceBytes, outText, opts.file);
7647
7664
  return 0;
7648
7665
  }
@@ -7672,7 +7689,7 @@ ${val}`;
7672
7689
  { items: capped.items, truncated: capped.truncated || headTruncated2, totalCount: totalCount2 },
7673
7690
  0
7674
7691
  );
7675
- emit4(jsonText);
7692
+ emit(jsonText);
7676
7693
  recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
7677
7694
  } else {
7678
7695
  const lines = [];
@@ -7702,10 +7719,10 @@ ${val}`);
7702
7719
  if (result.items.length === 0) {
7703
7720
  if (opts.json === true) {
7704
7721
  const jsonText = displaySafeJson({ items: [], truncated: false, totalCount: 0 }, 0);
7705
- emit4(jsonText);
7722
+ emit(jsonText);
7706
7723
  recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
7707
7724
  } else {
7708
- emit4(`No elements matched path: '${displaySafeText(queryPath)}'`);
7725
+ emit(`No elements matched path: '${displaySafeText(queryPath)}'`);
7709
7726
  }
7710
7727
  return 0;
7711
7728
  }
@@ -7717,7 +7734,7 @@ ${val}`);
7717
7734
  ...opts.decodeEmbeddedXml ? { decodeEmbedded: true } : {}
7718
7735
  });
7719
7736
  const jsonText = displaySafeJson(jsonVal);
7720
- emit4(jsonText);
7737
+ emit(jsonText);
7721
7738
  recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
7722
7739
  } else {
7723
7740
  let xmlText = serializeXmlNode(
@@ -7749,7 +7766,7 @@ ${xmlText}`;
7749
7766
  { items: capped.items, truncated: capped.truncated || headTruncated, totalCount },
7750
7767
  0
7751
7768
  );
7752
- emit4(jsonText);
7769
+ emit(jsonText);
7753
7770
  recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
7754
7771
  } else {
7755
7772
  const blocks = limited.map((node) => {
@@ -7773,21 +7790,21 @@ ${block}`;
7773
7790
  }
7774
7791
  return 0;
7775
7792
  } catch (e) {
7776
- emitErr4(extractErrorMessage(e));
7793
+ emitErr(extractErrorMessage(e));
7777
7794
  return 1;
7778
7795
  }
7779
7796
  }
7780
7797
  function runHtmlOutline(opts) {
7781
7798
  const text = readFileText(opts.file);
7782
7799
  if (text === null) {
7783
- emitErr4(`Could not read: ${opts.file}`);
7800
+ emitErr(`Could not read: ${opts.file}`);
7784
7801
  return 1;
7785
7802
  }
7786
7803
  const summary = outlineHtml(text);
7787
7804
  const fullSourceBytes = sumFileSizes([opts.file]);
7788
7805
  if (opts.json === true) {
7789
7806
  const jsonText = displaySafeJson(summary);
7790
- emit4(jsonText);
7807
+ emit(jsonText);
7791
7808
  recordReadStat("html_outline", fullSourceBytes, jsonText, opts.file);
7792
7809
  } else {
7793
7810
  const outlineText = formatHtmlOutline(summary);
@@ -7799,14 +7816,14 @@ function runHtmlOutline(opts) {
7799
7816
  function runHtmlQuery(opts) {
7800
7817
  const text = readFileText(opts.file);
7801
7818
  if (text === null) {
7802
- emitErr4(`Could not read: ${opts.file}`);
7819
+ emitErr(`Could not read: ${opts.file}`);
7803
7820
  return 1;
7804
7821
  }
7805
7822
  let head;
7806
7823
  try {
7807
7824
  head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
7808
7825
  } catch (e) {
7809
- emitErr4(extractErrorMessage(e));
7826
+ emitErr(extractErrorMessage(e));
7810
7827
  return 1;
7811
7828
  }
7812
7829
  try {
@@ -7817,10 +7834,10 @@ function runHtmlQuery(opts) {
7817
7834
  if (result.attributeValues.length === 0) {
7818
7835
  if (opts.json === true) {
7819
7836
  const jsonText = displaySafeJson({ items: [], truncated: false, totalCount: 0 }, 0);
7820
- emit4(jsonText);
7837
+ emit(jsonText);
7821
7838
  recordReadStat("html_query", fullSourceBytes, jsonText, opts.file);
7822
7839
  } else {
7823
- emit4(`No attributes matched selector: '${displaySafeText(opts.selector)}'`);
7840
+ emit(`No attributes matched selector: '${displaySafeText(opts.selector)}'`);
7824
7841
  }
7825
7842
  return 0;
7826
7843
  }
@@ -7830,7 +7847,7 @@ function runHtmlQuery(opts) {
7830
7847
  if (opts.json === true) {
7831
7848
  const capped = guardJsonRows(limited2);
7832
7849
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated || headTruncated2, totalCount: totalCount2 }, 0);
7833
- emit4(jsonText);
7850
+ emit(jsonText);
7834
7851
  recordReadStat("html_query", fullSourceBytes, jsonText, opts.file);
7835
7852
  } else {
7836
7853
  const lines = limited2.map((item) => item);
@@ -7846,10 +7863,10 @@ function runHtmlQuery(opts) {
7846
7863
  if (result.elements.length === 0) {
7847
7864
  if (opts.json === true) {
7848
7865
  const jsonText = displaySafeJson({ items: [], truncated: false, totalCount: 0 }, 0);
7849
- emit4(jsonText);
7866
+ emit(jsonText);
7850
7867
  recordReadStat("html_query", fullSourceBytes, jsonText, opts.file);
7851
7868
  } else {
7852
- emit4(`No elements matched selector: '${displaySafeText(opts.selector)}'`);
7869
+ emit(`No elements matched selector: '${displaySafeText(opts.selector)}'`);
7853
7870
  }
7854
7871
  return 0;
7855
7872
  }
@@ -7866,7 +7883,7 @@ function runHtmlQuery(opts) {
7866
7883
  }));
7867
7884
  const capped = guardJsonRows(jsonItems);
7868
7885
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount });
7869
- emit4(jsonText);
7886
+ emit(jsonText);
7870
7887
  recordReadStat("html_query", fullSourceBytes, jsonText, opts.file);
7871
7888
  } else if (opts.text === true) {
7872
7889
  const textLines = limited.map((n) => extractNodeText(n, text)).filter(Boolean);
@@ -7887,24 +7904,24 @@ function runHtmlQuery(opts) {
7887
7904
  }
7888
7905
  return 0;
7889
7906
  } catch (e) {
7890
- emitErr4(extractErrorMessage(e));
7907
+ emitErr(extractErrorMessage(e));
7891
7908
  return 1;
7892
7909
  }
7893
7910
  }
7894
7911
  function runHtmlLint(opts) {
7895
7912
  const text = readFileText(opts.file);
7896
7913
  if (text === null) {
7897
- emitErr4(`Could not read: ${opts.file}`);
7914
+ emitErr(`Could not read: ${opts.file}`);
7898
7915
  return 1;
7899
7916
  }
7900
7917
  const report = lintHtml(text);
7901
7918
  const isClean = opts.strict ? report.errors.length === 0 && report.warnings.length === 0 : report.errors.length === 0;
7902
7919
  if (opts.json === true) {
7903
- emit4(displaySafeJson(report));
7920
+ emit(displaySafeJson(report));
7904
7921
  return isClean ? 0 : 1;
7905
7922
  }
7906
7923
  if (report.errors.length === 0 && report.warnings.length === 0) {
7907
- emit4(`\u2713 ${displaySafeText(opts.file)}: HTML structure is valid (all tags balanced, no duplicate IDs, void elements respected).`);
7924
+ emit(`\u2713 ${displaySafeText(opts.file)}: HTML structure is valid (all tags balanced, no duplicate IDs, void elements respected).`);
7908
7925
  return 0;
7909
7926
  }
7910
7927
  const lines = [];
@@ -7928,20 +7945,20 @@ function runHtmlLint(opts) {
7928
7945
  lines.push(` <${tag}>: open=${counts.open}, close=${counts.close}, diff=${counts.diff}`);
7929
7946
  }
7930
7947
  }
7931
- emit4(lines.join("\n"));
7948
+ emit(lines.join("\n"));
7932
7949
  return isClean ? 0 : 1;
7933
7950
  }
7934
7951
  function loadOpenApiOperations(file) {
7935
7952
  const text = readFileText(file);
7936
7953
  if (text === null) {
7937
- emitErr4(`Could not read: ${file}`);
7954
+ emitErr(`Could not read: ${file}`);
7938
7955
  return null;
7939
7956
  }
7940
7957
  let spec;
7941
7958
  try {
7942
7959
  spec = parseOpenApiSpec(text, file);
7943
7960
  } catch {
7944
- emitErr4(`Failed to parse OpenAPI spec (not valid JSON or YAML): ${file}`);
7961
+ emitErr(`Failed to parse OpenAPI spec (not valid JSON or YAML): ${file}`);
7945
7962
  return null;
7946
7963
  }
7947
7964
  return extractOperations(spec);
@@ -7952,7 +7969,7 @@ function runOpenApiOutline(opts) {
7952
7969
  const fullSourceBytes = sumFileSizes([opts.file]);
7953
7970
  if (opts.json === true) {
7954
7971
  const jsonText = displaySafeJson(operations, 0);
7955
- emit4(jsonText);
7972
+ emit(jsonText);
7956
7973
  recordReadStat("openapi_outline", fullSourceBytes, jsonText, opts.file);
7957
7974
  } else {
7958
7975
  const text = formatOpenApiOutline(operations);
@@ -7970,13 +7987,13 @@ function runOpenApiOp(opts) {
7970
7987
  const closes = rankSimilarNames(operations.map(operationLabel), opts.operation);
7971
7988
  if (closes.length > 0) messages.push(didYouMean(closes));
7972
7989
  else if (operations.length > 0) messages.push(`Try: token-goat openapi-outline ${opts.file}`);
7973
- emitErr4(messages.join("\n"));
7990
+ emitErr(messages.join("\n"));
7974
7991
  return 1;
7975
7992
  }
7976
7993
  const fullSourceBytes = sumFileSizes([opts.file]);
7977
7994
  if (opts.json === true) {
7978
7995
  const jsonText = displaySafeJson(match, 0);
7979
- emit4(jsonText);
7996
+ emit(jsonText);
7980
7997
  recordReadStat("openapi_op", fullSourceBytes, jsonText, opts.operation);
7981
7998
  } else {
7982
7999
  const text = formatOperationDetail(match);
@@ -7995,7 +8012,6 @@ var MIN_WORD_SIMILARITY_LEN = 3;
7995
8012
  var STRUCTURED_MISS_MAX_BYTES = 128 * 1024;
7996
8013
  var STRUCTURED_MISS_MAX_FILES = 12;
7997
8014
  var STRUCTURED_MISS_MAX_NODES = 2e4;
7998
- var FIND_SCAN_LIMIT = 2e4;
7999
8015
  function endsWithPathBoundary(full, suffix) {
8000
8016
  if (!full.endsWith(suffix)) return false;
8001
8017
  if (full.length === suffix.length) return true;
@@ -8076,8 +8092,9 @@ function didYouMean(candidates) {
8076
8092
  return lines.join("\n");
8077
8093
  }
8078
8094
  function unknownSymbolSuggestion(name, rootDir) {
8079
- const rawSymbols = querySymbols({ limit: FIND_SCAN_LIMIT, rootDir });
8080
- const candidates = rankSimilarNames(rawSymbols.map((s) => s.name), name);
8095
+ const names = /* @__PURE__ */ new Set();
8096
+ forEachSymbol({ rootDir }, (s) => names.add(s.name));
8097
+ const candidates = rankSimilarNames([...names], name);
8081
8098
  return candidates.length > 0 ? `
8082
8099
  ${didYouMean(candidates)}` : "";
8083
8100
  }
@@ -8564,14 +8581,14 @@ ${sub.text}`);
8564
8581
  }
8565
8582
  function runBrief(opts) {
8566
8583
  if (opts.limit !== void 0 && opts.limit <= 0) {
8567
- emitErr4(`--limit must be a positive number, got: ${opts.limit}`);
8584
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
8568
8585
  return 1;
8569
8586
  }
8570
8587
  const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
8571
8588
  if (crossFilePairs !== null) {
8572
8589
  const { text: text2, code: code2 } = runBriefCrossFile(crossFilePairs, opts);
8573
- if (code2 === 0) emit4(text2);
8574
- else emitErr4(text2);
8590
+ if (code2 === 0) emit(text2);
8591
+ else emitErr(text2);
8575
8592
  return code2;
8576
8593
  }
8577
8594
  const { file, symbol } = parseReadSpec(opts.spec);
@@ -8579,14 +8596,14 @@ function runBrief(opts) {
8579
8596
  const multiSymbols = symbol.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8580
8597
  if (multiSymbols.length > 1) {
8581
8598
  const { text: text2, code: code2 } = runBriefMulti(file, multiSymbols, opts);
8582
- if (code2 === 0) emit4(text2);
8583
- else emitErr4(text2);
8599
+ if (code2 === 0) emit(text2);
8600
+ else emitErr(text2);
8584
8601
  return code2;
8585
8602
  }
8586
8603
  }
8587
8604
  const { text, code } = runBriefCore(opts);
8588
- if (code === 0) emit4(text);
8589
- else emitErr4(text);
8605
+ if (code === 0) emit(text);
8606
+ else emitErr(text);
8590
8607
  return code;
8591
8608
  }
8592
8609
 
@@ -8601,7 +8618,7 @@ var OURS_RE = /^<{7}(?:\s+(.*))?$/;
8601
8618
  var BASE_RE = /^\|{7}(?:\s+(.*))?$/;
8602
8619
  var SEP_RE = /^={7}\s*$/;
8603
8620
  var THEIRS_RE = /^>{7}(?:\s+(.*))?$/;
8604
- function parseConflicts(filePath, text) {
8621
+ function parseConflicts(filePath, text, contextLines = 3) {
8605
8622
  const lines = text.split(/\r?\n/);
8606
8623
  const regions = [];
8607
8624
  const warnings = [];
@@ -8664,13 +8681,29 @@ function parseConflicts(filePath, text) {
8664
8681
  const theirsMatch = THEIRS_RE.exec(line);
8665
8682
  if (theirsMatch !== null) {
8666
8683
  const theirsLabel = (theirsMatch[1] ?? "").trim();
8684
+ const beforeContext = [];
8685
+ if (contextLines > 0) {
8686
+ const ctxStart = Math.max(0, startLine - 1 - contextLines);
8687
+ for (let c = ctxStart; c < startLine - 1; c++) {
8688
+ beforeContext.push({ line: c + 1, text: lines[c] ?? "" });
8689
+ }
8690
+ }
8691
+ const afterContext = [];
8692
+ if (contextLines > 0) {
8693
+ const ctxEnd = Math.min(lines.length, lineNo + contextLines);
8694
+ for (let c = lineNo; c < ctxEnd; c++) {
8695
+ afterContext.push({ line: c + 1, text: lines[c] ?? "" });
8696
+ }
8697
+ }
8667
8698
  regions.push({
8668
8699
  filePath,
8669
8700
  lineStart: startLine,
8670
8701
  lineEnd: lineNo,
8671
8702
  ours: { label: oursLabel, content: oursLines.join("\n") },
8672
8703
  base: sawBase ? { label: baseLabel, content: baseLines.join("\n") } : null,
8673
- theirs: { label: theirsLabel, content: theirsLines.join("\n") }
8704
+ theirs: { label: theirsLabel, content: theirsLines.join("\n") },
8705
+ ...beforeContext.length > 0 ? { beforeContext } : {},
8706
+ ...afterContext.length > 0 ? { afterContext } : {}
8674
8707
  });
8675
8708
  resetRegion();
8676
8709
  continue;
@@ -8713,6 +8746,11 @@ function formatSingleFileConflicts(result) {
8713
8746
  lines.push(`${displaySafeText(result.filePath)} -- ${result.regions.length} conflict${result.regions.length === 1 ? "" : "s"}`);
8714
8747
  for (const r of result.regions) {
8715
8748
  lines.push(` lines ${r.lineStart}-${r.lineEnd}`);
8749
+ if (r.beforeContext && r.beforeContext.length > 0) {
8750
+ for (const c of r.beforeContext) {
8751
+ lines.push(` ${c.line} | ${displaySafeText(c.text)}`);
8752
+ }
8753
+ }
8716
8754
  lines.push(` <<<<<<< ${displaySafeText(r.ours.label || "(ours)")}`);
8717
8755
  lines.push(indentBlock(r.ours.content));
8718
8756
  if (r.base !== null) {
@@ -8722,6 +8760,11 @@ function formatSingleFileConflicts(result) {
8722
8760
  lines.push(" =======");
8723
8761
  lines.push(indentBlock(r.theirs.content));
8724
8762
  lines.push(` >>>>>>> ${displaySafeText(r.theirs.label || "(theirs)")}`);
8763
+ if (r.afterContext && r.afterContext.length > 0) {
8764
+ for (const c of r.afterContext) {
8765
+ lines.push(` ${c.line} | ${displaySafeText(c.text)}`);
8766
+ }
8767
+ }
8725
8768
  }
8726
8769
  } else {
8727
8770
  lines.push(`${displaySafeText(result.filePath)} -- no conflicts`);
@@ -8771,23 +8814,24 @@ function runConflicts(opts) {
8771
8814
  try {
8772
8815
  stat = fs8.statSync(abs);
8773
8816
  } catch {
8774
- emitErr4(`Could not read: ${opts.path}`);
8817
+ emitErr(`Could not read: ${opts.path}`);
8775
8818
  return 1;
8776
8819
  }
8777
8820
  files = stat.isDirectory() ? walkProject(abs).files : [abs];
8778
8821
  }
8822
+ const contextLines = opts.context !== void 0 ? opts.context : 3;
8779
8823
  const results = [];
8780
8824
  for (const f of files) {
8781
8825
  const text = readFileText(f);
8782
8826
  if (text === null) continue;
8783
- const parsed = parseConflicts(f, text);
8827
+ const parsed = parseConflicts(f, text, contextLines);
8784
8828
  if (parsed.regions.length > 0 || parsed.warnings.length > 0) results.push(parsed);
8785
8829
  }
8786
8830
  const fullSourceBytes = sumFileSizes(files);
8787
8831
  const detail = opts.path ?? ".";
8788
8832
  if (opts.json === true) {
8789
8833
  const jsonText = displaySafeJson(opts.summary === true ? results.map(summarizeFileConflicts) : results, 0);
8790
- emit4(jsonText);
8834
+ emit(jsonText);
8791
8835
  recordReadStat("conflicts", fullSourceBytes, jsonText, detail);
8792
8836
  } else if (opts.summary === true) {
8793
8837
  const text = formatConflictSummaries(results.map(summarizeFileConflicts));
@@ -8865,7 +8909,7 @@ function refIsSafe(ref) {
8865
8909
  return !ref.startsWith("-");
8866
8910
  }
8867
8911
  function emitUnsafeRef(ref) {
8868
- emitErr4(`Refusing a git ref that starts with '-': ${ref}`);
8912
+ emitErr(`Refusing a git ref that starts with '-': ${ref}`);
8869
8913
  }
8870
8914
  function changedDiffBaselineBytes(cwd, ref, files) {
8871
8915
  if (files.length === 0) return 0;
@@ -8888,25 +8932,25 @@ function runChanged(opts = {}) {
8888
8932
  try {
8889
8933
  const result = runGit(["diff", ref, "--name-only"], { cwd });
8890
8934
  if (result.exitCode !== 0) {
8891
- emitErr4(`git diff failed: ${result.stderr}`);
8935
+ emitErr(`git diff failed: ${result.stderr}`);
8892
8936
  const hint = buildChangedRefHint(cwd, ref);
8893
8937
  if (hint !== null) {
8894
- emitErr4(hint);
8938
+ emitErr(hint);
8895
8939
  }
8896
8940
  return 1;
8897
8941
  }
8898
8942
  changedFiles = result.stdout.trim().split(/\r?\n/).filter(Boolean);
8899
8943
  } catch {
8900
- emitErr4(`Could not run git diff against '${ref}'`);
8944
+ emitErr(`Could not run git diff against '${ref}'`);
8901
8945
  return 1;
8902
8946
  }
8903
8947
  const emptyEnvelope = () => {
8904
- emit4(displaySafeJson({ items: [], truncated: false, totalCount: 0 }));
8948
+ emit(displaySafeJson({ items: [], truncated: false, totalCount: 0 }));
8905
8949
  return 0;
8906
8950
  };
8907
8951
  if (changedFiles.length === 0) {
8908
8952
  if (opts.json === true) return emptyEnvelope();
8909
- emit4("No files changed.");
8953
+ emit("No files changed.");
8910
8954
  return 0;
8911
8955
  }
8912
8956
  const preGrepFileCount = changedFiles.length;
@@ -8914,7 +8958,7 @@ function runChanged(opts = {}) {
8914
8958
  if (matchesGrep !== void 0) changedFiles = changedFiles.filter((f) => matchesGrep(f));
8915
8959
  if (matchesGrep !== void 0 && changedFiles.length === 0) {
8916
8960
  if (opts.json === true) return emptyEnvelope();
8917
- emit4(grepFilteredToEmptyNotice(preGrepFileCount, opts.grep ?? "", "changed file", "changed files"));
8961
+ emit(grepFilteredToEmptyNotice(preGrepFileCount, opts.grep ?? "", "changed file", "changed files"));
8918
8962
  return 0;
8919
8963
  }
8920
8964
  const postGrepFileCount = changedFiles.length;
@@ -8928,12 +8972,12 @@ function runChanged(opts = {}) {
8928
8972
  if (opts.json === true) return emptyEnvelope();
8929
8973
  const grepRemoved = preGrepFileCount - postGrepFileCount;
8930
8974
  if (matchesGrep !== void 0 && grepRemoved > 0) {
8931
- emit4(
8975
+ emit(
8932
8976
  `No non-test files matched --grep ${opts.grep ?? ""} (${excludeTestsHiddenNote(hiddenTestFiles)}; ${countNoun(grepRemoved, "other changed file")} did not match the filter)`
8933
8977
  );
8934
8978
  return 0;
8935
8979
  }
8936
- emit4(`No non-test files changed (${excludeTestsHiddenNote(hiddenTestFiles)})`);
8980
+ emit(`No non-test files changed (${excludeTestsHiddenNote(hiddenTestFiles)})`);
8937
8981
  return 0;
8938
8982
  }
8939
8983
  if (opts.symbolMode === true) {
@@ -8949,27 +8993,27 @@ function runChanged(opts = {}) {
8949
8993
  }
8950
8994
  const allSymbols = [];
8951
8995
  for (const f of changedFiles) {
8952
- const fileSymbols = querySymbols({ filePath: resolveIndexPath(f, projectRoot), limit: FIND_SCAN_LIMIT2 });
8996
+ const fileSymbols = querySymbols({ filePath: resolveIndexPath(f, projectRoot), limit: FIND_SCAN_LIMIT });
8953
8997
  const hunks = hunksByFile.get(f);
8954
8998
  const scoped = hunks === void 0 || isVirtualIndexedPath(f) ? fileSymbols : fileSymbols.filter((s) => hunks.some((h) => h.start <= s.lineEnd && h.end >= s.lineStart));
8955
8999
  allSymbols.push(...scoped);
8956
9000
  }
8957
9001
  if (allSymbols.length === 0) {
8958
9002
  if (opts.json === true) return emptyEnvelope();
8959
- emit4("No symbols changed.");
9003
+ emit("No symbols changed.");
8960
9004
  return 0;
8961
9005
  }
8962
9006
  const symbolFullBytes = symbolDiffBaselineBytes;
8963
9007
  if (opts.json === true) {
8964
9008
  const capped = guardJsonRows(allSymbols);
8965
9009
  const text = displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount });
8966
- emit4(text);
9010
+ emit(text);
8967
9011
  recordReadStat("changed_lookup", symbolFullBytes, text, ref);
8968
9012
  return 0;
8969
9013
  }
8970
9014
  const symbolText = allSymbols.map((s) => `${s.name} (${s.kind}) \u2014 ${formatSymbolLocation(toDisplayPath(projectRoot, s.filePath), s.lineStart)}`).join("\n");
8971
9015
  for (const s of allSymbols) {
8972
- emit4(`${displaySafeText(s.name)} (${displaySafeText(s.kind)}) \u2014 ${formatSymbolLocation(displaySafeText(toDisplayPath(projectRoot, s.filePath)), s.lineStart)}`);
9016
+ emit(`${displaySafeText(s.name)} (${displaySafeText(s.kind)}) \u2014 ${formatSymbolLocation(displaySafeText(toDisplayPath(projectRoot, s.filePath)), s.lineStart)}`);
8973
9017
  }
8974
9018
  recordReadStat("changed_lookup", symbolFullBytes, symbolText, ref);
8975
9019
  return 0;
@@ -8978,12 +9022,12 @@ function runChanged(opts = {}) {
8978
9022
  if (opts.json === true) {
8979
9023
  const capped = guardJsonRows(changedFiles);
8980
9024
  const text = displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount });
8981
- emit4(text);
9025
+ emit(text);
8982
9026
  recordReadStat("changed_lookup", fullBytes, text, ref);
8983
9027
  return 0;
8984
9028
  }
8985
9029
  for (const f of changedFiles) {
8986
- emit4(f);
9030
+ emit(f);
8987
9031
  }
8988
9032
  recordReadStat("changed_lookup", fullBytes, changedFiles.join("\n"), ref);
8989
9033
  return 0;
@@ -9033,27 +9077,27 @@ function runDiff(opts) {
9033
9077
  try {
9034
9078
  diffResult = runGit(diffArgs, { cwd });
9035
9079
  } catch {
9036
- emitErr4(`Could not run git diff for '${match.filePath}'`);
9080
+ emitErr(`Could not run git diff for '${match.filePath}'`);
9037
9081
  return 1;
9038
9082
  }
9039
9083
  if (diffResult.exitCode !== 0) {
9040
- emitErr4(`git diff failed: ${diffResult.stderr}`);
9084
+ emitErr(`git diff failed: ${diffResult.stderr}`);
9041
9085
  return 1;
9042
9086
  }
9043
9087
  if (diffResult.stdout.trim() === "") {
9044
- emit4(`No changes to '${displaySafeText(match.name)}' in '${displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath))}'.`);
9088
+ emit(`No changes to '${displaySafeText(match.name)}' in '${displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath))}'.`);
9045
9089
  return 0;
9046
9090
  }
9047
9091
  const { hunks } = splitDiffHunks(diffResult.stdout);
9048
9092
  const diffFileScoped = isVirtualIndexedPath(match.filePath);
9049
9093
  const overlapping = diffFileScoped ? hunks : hunks.filter((h) => h.start <= match.lineEnd && h.end >= match.lineStart);
9050
9094
  if (overlapping.length === 0) {
9051
- emit4(`No changes to '${displaySafeText(match.name)}' (lines ${match.lineStart}-${match.lineEnd}) in '${displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath))}'.`);
9095
+ emit(`No changes to '${displaySafeText(match.name)}' (lines ${match.lineStart}-${match.lineEnd}) in '${displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath))}'.`);
9052
9096
  return 0;
9053
9097
  }
9054
9098
  if (opts.json === true) {
9055
9099
  const capped = guardJsonRows(overlapping.map((h) => ({ start: h.start, end: h.end, text: h.text })));
9056
- emit4(
9100
+ emit(
9057
9101
  displaySafeJson(
9058
9102
  {
9059
9103
  symbol: match.name,
@@ -9070,7 +9114,7 @@ function runDiff(opts) {
9070
9114
  }
9071
9115
  const header = `# ${displaySafeText(match.name)} (${displaySafeText(match.kind)}) \u2014 ${formatSymbolLocation(displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath)), match.lineStart, match.lineEnd)}`;
9072
9116
  const diffNote = diffFileScoped ? [virtualIndexedScopeNote(displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath)), "these hunks cover the whole file rather than this symbol alone")] : [];
9073
- emit4(guardText([header, ...diffNote, ...overlapping.map((h) => h.text)].join("\n"), "diff"));
9117
+ emit(guardText([header, ...diffNote, ...overlapping.map((h) => h.text)].join("\n"), "diff"));
9074
9118
  return 0;
9075
9119
  }
9076
9120
  var DEFAULT_LOG_MAX_COUNT = 20;
@@ -9131,20 +9175,20 @@ function runLog(opts) {
9131
9175
  try {
9132
9176
  logResult = runGit(logArgs, { cwd });
9133
9177
  } catch {
9134
- emitErr4(`Could not run git log for '${match.filePath}'`);
9178
+ emitErr(`Could not run git log for '${match.filePath}'`);
9135
9179
  return 1;
9136
9180
  }
9137
9181
  if (logResult.exitCode !== 0) {
9138
- emitErr4(`git log failed: ${displaySafeText(logResult.stderr)}`);
9182
+ emitErr(`git log failed: ${displaySafeText(logResult.stderr)}`);
9139
9183
  return 1;
9140
9184
  }
9141
9185
  if (logResult.stdout.trim() === "") {
9142
- emit4(`No history for '${displaySafeText(match.name)}' (lines ${match.lineStart}-${match.lineEnd}) in '${displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath))}'.`);
9186
+ emit(`No history for '${displaySafeText(match.name)}' (lines ${match.lineStart}-${match.lineEnd}) in '${displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath))}'.`);
9143
9187
  return 0;
9144
9188
  }
9145
9189
  if (opts.json === true) {
9146
9190
  const capped = guardJsonRows(parseLogDashLOutput(logResult.stdout));
9147
- emit4(
9191
+ emit(
9148
9192
  displaySafeJson(
9149
9193
  {
9150
9194
  symbol: match.name,
@@ -9161,7 +9205,7 @@ function runLog(opts) {
9161
9205
  }
9162
9206
  const header = `# ${displaySafeText(match.name)} (${displaySafeText(match.kind)}) \u2014 ${formatSymbolLocation(displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath)), match.lineStart, match.lineEnd)}`;
9163
9207
  const logNote = fileScoped ? [virtualIndexedScopeNote(displaySafeText(toDisplayPath(getDisplayRoot(opts.projectRoot), match.filePath)), "this history covers the whole file rather than this symbol alone")] : [];
9164
- emit4(guardText([header, ...logNote, logResult.stdout].join("\n"), "diff"));
9208
+ emit(guardText([header, ...logNote, logResult.stdout].join("\n"), "diff"));
9165
9209
  return 0;
9166
9210
  }
9167
9211
 
@@ -9611,26 +9655,26 @@ async function runZipList(opts) {
9611
9655
  data = readFileBytes(opts.file);
9612
9656
  } catch (err) {
9613
9657
  if (err instanceof ZipInputTooLargeError) {
9614
- emitErr4(err.message);
9658
+ emitErr(err.message);
9615
9659
  return 1;
9616
9660
  }
9617
9661
  throw err;
9618
9662
  }
9619
9663
  if (data === null) {
9620
- emitErr4(`Could not read: ${opts.file}`);
9664
+ emitErr(`Could not read: ${opts.file}`);
9621
9665
  return 1;
9622
9666
  }
9623
9667
  let entries;
9624
9668
  try {
9625
9669
  entries = await listZipEntries(data);
9626
9670
  } catch (err) {
9627
- emitErr4(archiveReadFailure(err, opts.file));
9671
+ emitErr(archiveReadFailure(err, opts.file));
9628
9672
  return 1;
9629
9673
  }
9630
9674
  const fullSourceBytes = sumFileSizes([opts.file]);
9631
9675
  if (opts.json === true) {
9632
9676
  const jsonText = displaySafeJson(entries, 0);
9633
- emit4(jsonText);
9677
+ emit(jsonText);
9634
9678
  recordReadStat("zip_list", fullSourceBytes, jsonText, opts.file);
9635
9679
  } else {
9636
9680
  const text = formatZipList(entries);
@@ -9645,13 +9689,13 @@ async function runZipRead(opts) {
9645
9689
  data = readFileBytes(opts.file);
9646
9690
  } catch (err) {
9647
9691
  if (err instanceof ZipInputTooLargeError) {
9648
- emitErr4(err.message);
9692
+ emitErr(err.message);
9649
9693
  return 1;
9650
9694
  }
9651
9695
  throw err;
9652
9696
  }
9653
9697
  if (data === null) {
9654
- emitErr4(`Could not read: ${opts.file}`);
9698
+ emitErr(`Could not read: ${opts.file}`);
9655
9699
  return 1;
9656
9700
  }
9657
9701
  let entries;
@@ -9660,12 +9704,12 @@ async function runZipRead(opts) {
9660
9704
  entries = await listZipEntries(data);
9661
9705
  content = await extractZipEntry(data, opts.entry);
9662
9706
  } catch (err) {
9663
- emitErr4(archiveReadFailure(err, opts.file));
9707
+ emitErr(archiveReadFailure(err, opts.file));
9664
9708
  return 1;
9665
9709
  }
9666
9710
  const matchedEntry = entries.find((e) => e.path === opts.entry);
9667
9711
  if (matchedEntry?.isDirectory === true) {
9668
- emitErr4(`Entry '${opts.entry}' is a directory, not a file, in '${opts.file}'`);
9712
+ emitErr(`Entry '${opts.entry}' is a directory, not a file, in '${opts.file}'`);
9669
9713
  return 1;
9670
9714
  }
9671
9715
  if (content === void 0) {
@@ -9673,7 +9717,7 @@ async function runZipRead(opts) {
9673
9717
  const closes = rankSimilarNames(entries.map((e) => e.path), opts.entry);
9674
9718
  if (closes.length > 0) messages.push(didYouMean(closes));
9675
9719
  else if (entries.length > 0) messages.push(`Try: token-goat zip-list ${opts.file}`);
9676
- emitErr4(messages.join("\n"));
9720
+ emitErr(messages.join("\n"));
9677
9721
  return 1;
9678
9722
  }
9679
9723
  const buf = Buffer.from(content);
@@ -9681,7 +9725,7 @@ async function runZipRead(opts) {
9681
9725
  const fullSourceBytes = sumFileSizes([opts.file]);
9682
9726
  if (opts.json === true) {
9683
9727
  const jsonText = displaySafeJson({ path: opts.entry, text }, 0);
9684
- emit4(jsonText);
9728
+ emit(jsonText);
9685
9729
  recordReadStat("zip_read", fullSourceBytes, jsonText, opts.entry);
9686
9730
  } else {
9687
9731
  emitGuarded(text, "zip-read");
@@ -9695,16 +9739,16 @@ function runSqliteSchema(opts) {
9695
9739
  const fullSourceBytes = sumFileSizes([opts.file]);
9696
9740
  if (opts.json === true) {
9697
9741
  const jsonText = displaySafeJson(schema, 0);
9698
- emit4(jsonText);
9742
+ emit(jsonText);
9699
9743
  recordReadStat("sqlite_schema", fullSourceBytes, jsonText, opts.file);
9700
9744
  } else {
9701
9745
  const text = formatSqliteSchema(schema);
9702
- emit4(text);
9746
+ emit(text);
9703
9747
  recordReadStat("sqlite_schema", fullSourceBytes, text, opts.file);
9704
9748
  }
9705
9749
  return 0;
9706
9750
  } catch (e) {
9707
- emitErr4(extractErrorMessage(e));
9751
+ emitErr(extractErrorMessage(e));
9708
9752
  return 1;
9709
9753
  }
9710
9754
  }
@@ -9714,16 +9758,16 @@ function runSqliteTables(opts) {
9714
9758
  const fullSourceBytes = sumFileSizes([opts.file]);
9715
9759
  if (opts.json === true) {
9716
9760
  const jsonText = displaySafeJson(tables, 0);
9717
- emit4(jsonText);
9761
+ emit(jsonText);
9718
9762
  recordReadStat("sqlite_tables", fullSourceBytes, jsonText, opts.file);
9719
9763
  } else {
9720
9764
  const text = formatSqliteTables(tables);
9721
- emit4(text);
9765
+ emit(text);
9722
9766
  recordReadStat("sqlite_tables", fullSourceBytes, text, opts.file);
9723
9767
  }
9724
9768
  return 0;
9725
9769
  } catch (e) {
9726
- emitErr4(extractErrorMessage(e));
9770
+ emitErr(extractErrorMessage(e));
9727
9771
  return 1;
9728
9772
  }
9729
9773
  }
@@ -9732,7 +9776,7 @@ function runSqliteQuery(opts) {
9732
9776
  try {
9733
9777
  head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
9734
9778
  } catch (e) {
9735
- emitErr4(extractErrorMessage(e));
9779
+ emitErr(extractErrorMessage(e));
9736
9780
  return 1;
9737
9781
  }
9738
9782
  try {
@@ -9749,7 +9793,7 @@ function runSqliteQuery(opts) {
9749
9793
  totalCount,
9750
9794
  rowCapped: result.rowCapped
9751
9795
  }, 0);
9752
- emit4(jsonText);
9796
+ emit(jsonText);
9753
9797
  const uncappedFull = guardJsonRows(result.rows);
9754
9798
  const baselineJsonText = displaySafeJson({
9755
9799
  columns: result.columns,
@@ -9761,27 +9805,27 @@ function runSqliteQuery(opts) {
9761
9805
  recordReadStat("sqlite_query", Buffer.byteLength(baselineJsonText, "utf8"), jsonText, opts.file);
9762
9806
  } else {
9763
9807
  const text = formatSqliteQueryTable({ ...result, rows }, { headTruncated });
9764
- emit4(text);
9808
+ emit(text);
9765
9809
  const baselineText = formatSqliteQueryTable({ ...result, rows: result.rows }, { headTruncated: false });
9766
9810
  recordReadStat("sqlite_query", Buffer.byteLength(baselineText, "utf8"), text, opts.file);
9767
9811
  }
9768
9812
  return 0;
9769
9813
  } catch (e) {
9770
- emitErr4(extractErrorMessage(e));
9814
+ emitErr(extractErrorMessage(e));
9771
9815
  return 1;
9772
9816
  }
9773
9817
  }
9774
9818
  function runCoverageReportGaps(opts) {
9775
9819
  const text = readFileText(opts.file);
9776
9820
  if (text === null) {
9777
- emitErr4(`Could not read: ${opts.file}`);
9821
+ emitErr(`Could not read: ${opts.file}`);
9778
9822
  return 1;
9779
9823
  }
9780
9824
  let report;
9781
9825
  try {
9782
9826
  report = parseCoverageReport(text);
9783
9827
  } catch (e) {
9784
- emitErr4(`Failed to parse coverage report (not valid LCOV or Istanbul JSON): ${opts.file}
9828
+ emitErr(`Failed to parse coverage report (not valid LCOV or Istanbul JSON): ${opts.file}
9785
9829
  ${extractErrorMessage(e)}`);
9786
9830
  return 1;
9787
9831
  }
@@ -9789,7 +9833,7 @@ ${extractErrorMessage(e)}`);
9789
9833
  const fullSourceBytes = sumFileSizes([opts.file]);
9790
9834
  if (opts.json === true) {
9791
9835
  const jsonText = displaySafeJson(scoped, 0);
9792
- emit4(jsonText);
9836
+ emit(jsonText);
9793
9837
  recordReadStat("coverage_report_gaps", fullSourceBytes, jsonText, opts.file);
9794
9838
  } else {
9795
9839
  const text2 = formatCoverageGaps(scoped);
@@ -9867,17 +9911,17 @@ function extractFrontmatter(lines) {
9867
9911
  function runConfigGet(opts) {
9868
9912
  const text = readFileText(opts.file);
9869
9913
  if (text === null) {
9870
- emitErr4(`Could not read: ${opts.file}`);
9914
+ emitErr(`Could not read: ${opts.file}`);
9871
9915
  return 1;
9872
9916
  }
9873
9917
  const frontmatterLines = extractFrontmatter(text.split(/\r?\n/));
9874
9918
  if (frontmatterLines !== null) {
9875
9919
  const value = lookupYaml(frontmatterLines, opts.key);
9876
9920
  if (value === null) {
9877
- emitErr4(`Key '${opts.key}' not found in ${opts.file}`);
9921
+ emitErr(`Key '${opts.key}' not found in ${opts.file}`);
9878
9922
  return 1;
9879
9923
  }
9880
- emit4(value);
9924
+ emit(value);
9881
9925
  return 0;
9882
9926
  }
9883
9927
  const ext = path10.extname(opts.file).toLowerCase();
@@ -9886,29 +9930,29 @@ function runConfigGet(opts) {
9886
9930
  let obj = JSON.parse(text);
9887
9931
  for (const part of opts.key.split(".")) {
9888
9932
  if (typeof obj !== "object" || obj === null) {
9889
- emitErr4(`Key '${opts.key}' not found in ${opts.file}`);
9933
+ emitErr(`Key '${opts.key}' not found in ${opts.file}`);
9890
9934
  return 1;
9891
9935
  }
9892
9936
  obj = obj[part];
9893
9937
  if (obj === void 0) {
9894
- emitErr4(`Key '${opts.key}' not found in ${opts.file}`);
9938
+ emitErr(`Key '${opts.key}' not found in ${opts.file}`);
9895
9939
  return 1;
9896
9940
  }
9897
9941
  }
9898
- emit4(displaySafeJson(obj, 0));
9942
+ emit(displaySafeJson(obj, 0));
9899
9943
  return 0;
9900
9944
  } catch {
9901
- emitErr4(`Failed to parse JSON: ${opts.file}`);
9945
+ emitErr(`Failed to parse JSON: ${opts.file}`);
9902
9946
  return 1;
9903
9947
  }
9904
9948
  }
9905
9949
  if (ext === ".yaml" || ext === ".yml") {
9906
9950
  const value = lookupYaml(text.split(/\r?\n/), opts.key);
9907
9951
  if (value === null) {
9908
- emitErr4(`Key '${opts.key}' not found in ${opts.file}`);
9952
+ emitErr(`Key '${opts.key}' not found in ${opts.file}`);
9909
9953
  return 1;
9910
9954
  }
9911
- emit4(value);
9955
+ emit(value);
9912
9956
  return 0;
9913
9957
  }
9914
9958
  const keyParts = opts.key.split(".");
@@ -9929,18 +9973,18 @@ function runConfigGet(opts) {
9929
9973
  if (new RegExp(`^${escapeRegExp(leafKey)}\\s*=`).test(trimmed)) {
9930
9974
  const eqIdx = trimmed.indexOf("=");
9931
9975
  const rawValue = stripInlineComment(trimmed.slice(eqIdx + 1)).trim();
9932
- emit4(stripPairedQuotes(rawValue));
9976
+ emit(stripPairedQuotes(rawValue));
9933
9977
  return 0;
9934
9978
  }
9935
9979
  }
9936
- emitErr4(`Key '${opts.key}' not found in ${opts.file}`);
9980
+ emitErr(`Key '${opts.key}' not found in ${opts.file}`);
9937
9981
  return 1;
9938
9982
  }
9939
9983
  function runPerFileEmitting(files, label, run) {
9940
9984
  let anyOk = false;
9941
9985
  files.forEach((file, i) => {
9942
- if (i > 0) emit4("");
9943
- emit4(`# ${label}: ${file}`);
9986
+ if (i > 0) emit("");
9987
+ emit(`# ${label}: ${file}`);
9944
9988
  if (run(file) === 0) anyOk = true;
9945
9989
  });
9946
9990
  return anyOk ? 0 : 1;
@@ -9950,7 +9994,7 @@ function runExports(opts) {
9950
9994
  if (multiFiles.length > 1) return runPerFileEmitting(multiFiles, "Exports", (file) => runExports({ ...opts, file }));
9951
9995
  const confined = fileConfinementRefusal("This file", opts.file, opts.projectRoot);
9952
9996
  if (confined !== null) {
9953
- emitErr4(confined);
9997
+ emitErr(confined);
9954
9998
  return 1;
9955
9999
  }
9956
10000
  const diskPath = resolveAgainstProjectRoot(opts.file, opts.projectRoot);
@@ -9969,7 +10013,7 @@ function runExports(opts) {
9969
10013
  const ext = path10.extname(opts.file).toLowerCase();
9970
10014
  const text = readFileText(diskPath);
9971
10015
  if (text === null && symbols.length === 0) {
9972
- emitErr4(`Could not read: ${opts.file}`);
10016
+ emitErr(`Could not read: ${opts.file}`);
9973
10017
  return 1;
9974
10018
  }
9975
10019
  if (text !== null) {
@@ -9979,7 +10023,7 @@ function runExports(opts) {
9979
10023
  for (const n of extractExportNames(text, ext)) if (!names.includes(n)) names.push(n);
9980
10024
  }
9981
10025
  if (names.length === 0) {
9982
- emit4(`No exported symbols found in '${displaySafeText(opts.file)}'`);
10026
+ emit(`No exported symbols found in '${displaySafeText(opts.file)}'`);
9983
10027
  return 0;
9984
10028
  }
9985
10029
  const preFilterCount = names.length;
@@ -9989,12 +10033,12 @@ function runExports(opts) {
9989
10033
  if (filteredNames.length === 0) {
9990
10034
  if (opts.json === true) {
9991
10035
  const jsonText = displaySafeJson([]);
9992
- emit4(jsonText);
10036
+ emit(jsonText);
9993
10037
  recordReadStat("exports", fullSourceBytes, jsonText, opts.file);
9994
10038
  return 0;
9995
10039
  }
9996
10040
  const textOut = grepFilteredToEmptyNotice(preFilterCount, opts.grep ?? "", "exported symbol", "exported symbols");
9997
- emit4(textOut);
10041
+ emit(textOut);
9998
10042
  recordReadStat("exports", fullSourceBytes, textOut, opts.file);
9999
10043
  return 0;
10000
10044
  }
@@ -10005,7 +10049,7 @@ function runExports(opts) {
10005
10049
  return { name: n, kind: kindOf(n), lineStart: loc?.lineStart ?? null, lineEnd: loc?.lineEnd ?? null };
10006
10050
  })
10007
10051
  );
10008
- emit4(jsonText);
10052
+ emit(jsonText);
10009
10053
  recordReadStat("exports", fullSourceBytes, jsonText, opts.file);
10010
10054
  return 0;
10011
10055
  }
@@ -10015,7 +10059,7 @@ function runExports(opts) {
10015
10059
  return `${kindOf(n).padEnd(10)} ${n}${locSuffix}`;
10016
10060
  });
10017
10061
  for (const line of outLines) {
10018
- emit4(line);
10062
+ emit(line);
10019
10063
  }
10020
10064
  recordReadStat("exports", fullSourceBytes, outLines.join("\n"), opts.file);
10021
10065
  return 0;
@@ -10026,12 +10070,12 @@ function runImports(opts) {
10026
10070
  const diskPath = resolveAgainstProjectRoot(opts.file, opts.projectRoot);
10027
10071
  const text = readFileText(diskPath);
10028
10072
  if (text === null) {
10029
- emitErr4(`Could not read: ${opts.file}`);
10073
+ emitErr(`Could not read: ${opts.file}`);
10030
10074
  return 1;
10031
10075
  }
10032
10076
  const imports = extractImports(text, importsExtensionFor(opts.file));
10033
10077
  if (imports.length === 0) {
10034
- emit4(`No imports found in '${displaySafeText(opts.file)}'`);
10078
+ emit(`No imports found in '${displaySafeText(opts.file)}'`);
10035
10079
  return 0;
10036
10080
  }
10037
10081
  const preFilterCount = imports.length;
@@ -10041,25 +10085,25 @@ function runImports(opts) {
10041
10085
  if (filteredImports.length === 0) {
10042
10086
  if (opts.json === true) {
10043
10087
  const jsonText = displaySafeJson({ items: [], truncated: false, totalCount: 0 });
10044
- emit4(jsonText);
10088
+ emit(jsonText);
10045
10089
  recordReadStat("imports", fullSourceBytes, jsonText, opts.file);
10046
10090
  return 0;
10047
10091
  }
10048
10092
  const text2 = grepFilteredToEmptyNotice(preFilterCount, opts.grep ?? "", "import", "imports");
10049
- emit4(text2);
10093
+ emit(text2);
10050
10094
  recordReadStat("imports", fullSourceBytes, text2, opts.file);
10051
10095
  return 0;
10052
10096
  }
10053
10097
  if (opts.json === true) {
10054
10098
  const capped = guardJsonRows(filteredImports);
10055
10099
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount });
10056
- emit4(jsonText);
10100
+ emit(jsonText);
10057
10101
  recordReadStat("imports", fullSourceBytes, jsonText, opts.file);
10058
10102
  return 0;
10059
10103
  }
10060
10104
  const outLines = filteredImports.map((imp) => `import ${imp}`);
10061
10105
  for (const line of outLines) {
10062
- emit4(line);
10106
+ emit(line);
10063
10107
  }
10064
10108
  recordReadStat("imports", fullSourceBytes, outLines.join("\n"), opts.file);
10065
10109
  return 0;
@@ -10157,50 +10201,55 @@ function runNoteList(opts = {}) {
10157
10201
  }
10158
10202
  function runFind(opts) {
10159
10203
  if (opts.limit !== void 0 && opts.limit <= 0) {
10160
- emitErr4(`--limit must be a positive number, got: ${opts.limit}`);
10204
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
10161
10205
  return 1;
10162
10206
  }
10163
10207
  const rootDir = resolveProjectRoot({ project: process.cwd() });
10164
10208
  const patternLower = opts.pattern.toLowerCase();
10165
- const rawSymbols = querySymbols({ limit: FIND_SCAN_LIMIT2, rootDir });
10166
- const symbols = rawSymbols.filter(
10167
- (s) => s.name.toLowerCase().includes(patternLower)
10168
- );
10209
+ const matchedFiles = /* @__PURE__ */ new Set();
10210
+ const allNames = /* @__PURE__ */ new Set();
10211
+ forEachSymbol({ rootDir }, (s) => {
10212
+ allNames.add(s.name);
10213
+ if (s.name.toLowerCase().includes(patternLower)) matchedFiles.add(s.filePath);
10214
+ });
10169
10215
  let fuzzyNames = [];
10170
- if (symbols.length === 0) {
10171
- fuzzyNames = rankSimilarNames(rawSymbols.map((s) => s.name), opts.pattern);
10216
+ if (matchedFiles.size === 0) {
10217
+ fuzzyNames = rankSimilarNames([...allNames], opts.pattern);
10218
+ if (fuzzyNames.length > 0) {
10219
+ const byName = new Map(fuzzyNames.map((n) => [n, []]));
10220
+ forEachSymbol({ rootDir }, (s) => {
10221
+ byName.get(s.name)?.push(s.filePath);
10222
+ });
10223
+ for (const n of fuzzyNames) for (const f of byName.get(n) ?? []) matchedFiles.add(f);
10224
+ }
10172
10225
  }
10173
- const matched = fuzzyNames.length > 0 ? fuzzyNames.flatMap((n) => rawSymbols.filter((s) => s.name === n)) : symbols;
10174
- const allFiles = [...new Set(matched.map((s) => s.filePath))];
10226
+ const allFiles = [...matchedFiles];
10175
10227
  const files = allFiles.slice(0, opts.limit ?? 50);
10176
10228
  const limitDropped = allFiles.length - files.length;
10177
- const truncated = rawSymbols.length === FIND_SCAN_LIMIT2 || limitDropped > 0;
10229
+ const truncated = limitDropped > 0;
10178
10230
  if (files.length === 0) {
10179
- emitErr4(`No indexed files match '${opts.pattern}'`);
10231
+ emitErr(`No indexed files match '${opts.pattern}'`);
10180
10232
  return 1;
10181
10233
  }
10182
10234
  if (opts.json === true) {
10183
10235
  const fuzzyPayload = fuzzyNames.length > 0 ? { fuzzy: true, matchedNames: fuzzyNames } : {};
10184
- emit4(displaySafeJson({ files, truncated, totalCount: allFiles.length, ...fuzzyPayload }));
10236
+ emit(displaySafeJson({ files, truncated, totalCount: allFiles.length, ...fuzzyPayload }));
10185
10237
  return 0;
10186
10238
  }
10187
10239
  if (fuzzyNames.length > 0) {
10188
- emitErr4(`No symbol name contains '${opts.pattern}'; showing files for the nearest indexed ${fuzzyNames.length === 1 ? "name" : "names"}: ${fuzzyNames.join(", ")}`);
10240
+ emitErr(`No symbol name contains '${opts.pattern}'; showing files for the nearest indexed ${fuzzyNames.length === 1 ? "name" : "names"}: ${fuzzyNames.join(", ")}`);
10189
10241
  }
10190
10242
  for (const f of files) {
10191
- emit4(toDisplayPath(rootDir, f));
10243
+ emit(toDisplayPath(rootDir, f));
10192
10244
  }
10193
10245
  if (limitDropped > 0) {
10194
- emitErr4(`Showing ${files.length} of ${allFiles.length} matching files; rerun with --limit ${allFiles.length} to see them all`);
10195
- }
10196
- if (rawSymbols.length === FIND_SCAN_LIMIT2) {
10197
- emitErr4(`Results may be incomplete; index scan hit limit of ${FIND_SCAN_LIMIT2} symbols`);
10246
+ emitErr(`Showing ${files.length} of ${allFiles.length} matching files; rerun with --limit ${allFiles.length} to see them all`);
10198
10247
  }
10199
10248
  return 0;
10200
10249
  }
10201
10250
  function runLocate(opts) {
10202
10251
  if (opts.limit !== void 0 && opts.limit <= 0) {
10203
- emitErr4(`--limit must be a positive number, got: ${opts.limit}`);
10252
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
10204
10253
  return 1;
10205
10254
  }
10206
10255
  const rootDir = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
@@ -10211,35 +10260,55 @@ function runLocate(opts) {
10211
10260
  targetFile = targetSpec.slice(0, colonIdx);
10212
10261
  targetSpec = targetSpec.slice(colonIdx + 2);
10213
10262
  }
10214
- const queryOpts = {
10215
- limit: FIND_SCAN_LIMIT2
10216
- };
10263
+ const scanOpts = {};
10217
10264
  if (targetFile !== void 0) {
10218
- queryOpts.filePath = resolveIndexPath(targetFile, rootDir);
10219
- healStaleIndex(queryOpts.filePath);
10265
+ scanOpts.filePath = resolveIndexPath(targetFile, rootDir);
10266
+ healStaleIndex(scanOpts.filePath);
10220
10267
  } else {
10221
- queryOpts.rootDir = rootDir;
10268
+ scanOpts.rootDir = rootDir;
10222
10269
  }
10223
- let rawSymbols = querySymbols(queryOpts);
10270
+ const limit = opts.limit ?? 25;
10271
+ const specLower = targetSpec.toLowerCase();
10272
+ const scan = () => {
10273
+ const found2 = { exact: [], exactCount: 0, partial: [], partialCount: 0, names: /* @__PURE__ */ new Set(), files: /* @__PURE__ */ new Set() };
10274
+ forEachSymbol(scanOpts, (s) => {
10275
+ found2.names.add(s.name);
10276
+ found2.files.add(s.filePath);
10277
+ const nameLower = s.name.toLowerCase();
10278
+ if (nameLower === specLower) {
10279
+ found2.exactCount++;
10280
+ if (found2.exact.length < limit) found2.exact.push(s);
10281
+ } else if (nameLower.includes(specLower)) {
10282
+ found2.partialCount++;
10283
+ if (found2.partial.length < limit) found2.partial.push(s);
10284
+ }
10285
+ });
10286
+ return found2;
10287
+ };
10288
+ let found = scan();
10224
10289
  if (targetFile === void 0) {
10225
- const heal = healStaleResultFiles(rawSymbols.map((s) => s.filePath));
10226
- if (heal.healed) rawSymbols = querySymbols(queryOpts);
10290
+ const heal = healStaleResultFiles([...found.files]);
10291
+ if (heal.healed) found = scan();
10227
10292
  }
10228
- const specLower = targetSpec.toLowerCase();
10229
- const exactMatches = rawSymbols.filter((s) => s.name.toLowerCase() === specLower);
10230
- const partialMatches = rawSymbols.filter(
10231
- (s) => s.name.toLowerCase() !== specLower && s.name.toLowerCase().includes(specLower)
10232
- );
10233
- const combined = [...exactMatches, ...partialMatches];
10293
+ let matchCount = found.exactCount + found.partialCount;
10294
+ let combined = [...found.exact, ...found.partial];
10234
10295
  let fuzzyNames = [];
10235
- if (combined.length === 0) {
10236
- fuzzyNames = rankSimilarNames(rawSymbols.map((s) => s.name), targetSpec);
10296
+ if (matchCount === 0) {
10297
+ fuzzyNames = rankSimilarNames([...found.names], targetSpec);
10298
+ if (fuzzyNames.length > 0) {
10299
+ const byName = new Map(fuzzyNames.map((n) => [n, []]));
10300
+ forEachSymbol(scanOpts, (s) => {
10301
+ const bucket = byName.get(s.name);
10302
+ if (bucket === void 0) return;
10303
+ matchCount++;
10304
+ if (bucket.length < limit) bucket.push(s);
10305
+ });
10306
+ combined = fuzzyNames.flatMap((n) => byName.get(n) ?? []);
10307
+ }
10237
10308
  }
10238
- const matched = fuzzyNames.length > 0 ? fuzzyNames.flatMap((n) => rawSymbols.filter((s) => s.name === n)) : combined;
10239
- const limit = opts.limit ?? 25;
10240
- const shown = matched.slice(0, limit);
10309
+ const shown = combined.slice(0, limit);
10241
10310
  if (shown.length === 0) {
10242
- emitErr4(`No landmark or symbol located for '${targetSpec}'`);
10311
+ emitErr(`No landmark or symbol located for '${targetSpec}'`);
10243
10312
  return 1;
10244
10313
  }
10245
10314
  const hits = shown.map((s) => ({
@@ -10251,22 +10320,22 @@ function runLocate(opts) {
10251
10320
  span: `${s.lineStart}-${s.lineEnd}`
10252
10321
  }));
10253
10322
  if (opts.json === true) {
10254
- emit4(displaySafeJson({
10323
+ emit(displaySafeJson({
10255
10324
  items: hits,
10256
- totalCount: matched.length,
10257
- truncated: matched.length > limit,
10325
+ totalCount: matchCount,
10326
+ truncated: matchCount > limit,
10258
10327
  ...fuzzyNames.length > 0 ? { fuzzy: true, matchedNames: fuzzyNames } : {}
10259
10328
  }));
10260
10329
  return 0;
10261
10330
  }
10262
10331
  if (fuzzyNames.length > 0) {
10263
- emitErr4(`No exact landmark for '${targetSpec}'; nearest matches: ${fuzzyNames.join(", ")}`);
10332
+ emitErr(`No exact landmark for '${targetSpec}'; nearest matches: ${fuzzyNames.join(", ")}`);
10264
10333
  }
10265
10334
  for (const hit of hits) {
10266
- emit4(`${displaySafeText(hit.filePath)}:${hit.span} [${displaySafeText(hit.kind)}] ${displaySafeText(hit.name)}`);
10335
+ emit(`${displaySafeText(hit.filePath)}:${hit.span} [${displaySafeText(hit.kind)}] ${displaySafeText(hit.name)}`);
10267
10336
  }
10268
- if (matched.length > limit) {
10269
- emitErr4(`Showing ${limit} of ${matched.length} locations; rerun with --limit ${matched.length} to see them all`);
10337
+ if (matchCount > limit) {
10338
+ emitErr(`Showing ${limit} of ${matchCount} locations; rerun with --limit ${matchCount} to see them all`);
10270
10339
  }
10271
10340
  return 0;
10272
10341
  }
@@ -10274,10 +10343,10 @@ function runListSections(opts) {
10274
10343
  const sections = listSections(opts.file);
10275
10344
  if (sections.length === 0) {
10276
10345
  if (!fileExists(opts.file)) {
10277
- emitErr4(`Could not read: ${opts.file}`);
10346
+ emitErr(`Could not read: ${opts.file}`);
10278
10347
  return 1;
10279
10348
  }
10280
- emitErr4(`No sections found in '${opts.file}'`);
10349
+ emitErr(`No sections found in '${opts.file}'`);
10281
10350
  return 1;
10282
10351
  }
10283
10352
  const preFilterCount = sections.length;
@@ -10293,19 +10362,19 @@ function runListSections(opts) {
10293
10362
  const filtered = matchesGrep !== void 0 ? labelled.filter((_, i) => matchesGrep(sections[i] ?? "")) : labelled;
10294
10363
  if (filtered.length === 0) {
10295
10364
  if (opts.json === true) {
10296
- emit4(displaySafeJson({ items: [], truncated: false, totalCount: 0 }));
10365
+ emit(displaySafeJson({ items: [], truncated: false, totalCount: 0 }));
10297
10366
  return 0;
10298
10367
  }
10299
- emit4(grepFilteredToEmptyNotice(preFilterCount, opts.grep ?? "", "section", "sections"));
10368
+ emit(grepFilteredToEmptyNotice(preFilterCount, opts.grep ?? "", "section", "sections"));
10300
10369
  return 0;
10301
10370
  }
10302
10371
  if (opts.json === true) {
10303
10372
  const capped = guardJsonRows(filtered);
10304
- emit4(displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }));
10373
+ emit(displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }));
10305
10374
  return 0;
10306
10375
  }
10307
10376
  for (const heading of filtered) {
10308
- emit4(heading);
10377
+ emit(heading);
10309
10378
  }
10310
10379
  return 0;
10311
10380
  }
@@ -10314,6 +10383,16 @@ function runListSections(opts) {
10314
10383
  init_define_import_meta_env();
10315
10384
  import * as fs11 from "node:fs";
10316
10385
  import * as path11 from "node:path";
10386
+ function htmlIdSectionFallback(filePath, heading) {
10387
+ if (detectLanguage(filePath) !== "html") return null;
10388
+ healStaleIndex(filePath);
10389
+ const hit = querySymbols({ name: stripHtmlIdSpelling(heading, filePath), filePath, kind: "html_id", limit: 1 })[0];
10390
+ if (hit === void 0) return null;
10391
+ const text = readFileText(filePath);
10392
+ if (text === null) return null;
10393
+ const lines = text.split("\n");
10394
+ return { heading: hit.name, content: lines.slice(hit.lineStart - 1, hit.lineEnd).join("\n"), lineStart: hit.lineStart, lineEnd: hit.lineEnd };
10395
+ }
10317
10396
  var AMBIGUOUS_HEADING_LIMIT = 10;
10318
10397
  function literalHeadingExists(filePath, heading) {
10319
10398
  const ordinalMatch = /^([^#\r\n]+)#(\d+)$/.exec(heading);
@@ -10335,7 +10414,7 @@ function runSection(opts) {
10335
10414
  const multiHeadings = heading.split(",").map((h) => h.trim()).filter((h) => h.length > 0);
10336
10415
  if (multiHeadings.length > 1) return runSectionMulti(specFilePath, filePath, multiHeadings, opts);
10337
10416
  }
10338
- const result = readSection(filePath, heading, readFileText);
10417
+ const result = readSection(filePath, heading, readFileText) ?? htmlIdSectionFallback(filePath, heading);
10339
10418
  if (result === null) {
10340
10419
  if (!fs11.existsSync(filePath)) {
10341
10420
  return { text: `File not found: '${filePath}'`, code: 1 };
@@ -10374,15 +10453,38 @@ function runSection(opts) {
10374
10453
  }
10375
10454
  const kind = result.redirectedFrom !== void 0 ? "section_replacement" : "section_read";
10376
10455
  const fullSourceBytes = sumFileSizes([filePath]);
10456
+ let content = result.content;
10457
+ let lineEnd = result.lineEnd;
10458
+ let truncatedNotice = "";
10459
+ if (opts.maxLines !== void 0 && opts.maxLines > 0) {
10460
+ const lines = content.split("\n");
10461
+ if (lines.length > opts.maxLines) {
10462
+ const remaining = lines.length - opts.maxLines;
10463
+ content = lines.slice(0, opts.maxLines).join("\n");
10464
+ lineEnd = Math.min(result.lineEnd, result.lineStart + opts.maxLines - 1);
10465
+ truncatedNotice = `
10466
+ ... (${countNoun(remaining, "more line")} not shown; pass a larger --max-lines or omit to read full section)`;
10467
+ }
10468
+ }
10377
10469
  if (opts.json === true) {
10378
- const text2 = displaySafeJson(result);
10470
+ const jsonPayload = {
10471
+ ...result,
10472
+ content,
10473
+ lineEnd,
10474
+ ...opts.maxLines !== void 0 && opts.maxLines > 0 ? {
10475
+ totalLines: result.content.split("\n").length,
10476
+ shownLines: content.split("\n").length,
10477
+ truncated: truncatedNotice.length > 0
10478
+ } : {}
10479
+ };
10480
+ const text2 = displaySafeJson(jsonPayload);
10379
10481
  if (opts.suppressStat !== true) recordReadStat(kind, fullSourceBytes, text2, heading);
10380
10482
  return { text: text2, code: 0 };
10381
10483
  }
10382
10484
  const redirectNote = result.redirectedFrom !== void 0 ? ` (redirected from: '${result.redirectedFrom}')` : "";
10383
10485
  const text = guardText(
10384
- `# ${result.heading} \u2014 ${filePath}:${result.lineStart}-${result.lineEnd}${redirectNote}
10385
- ${result.content}`,
10486
+ `# ${result.heading} \u2014 ${filePath}:${result.lineStart}-${lineEnd}${redirectNote}
10487
+ ${content}${truncatedNotice}`,
10386
10488
  "heading"
10387
10489
  );
10388
10490
  if (opts.suppressStat !== true) recordReadStat(kind, fullSourceBytes, text, heading);
@@ -10503,8 +10605,7 @@ ${sub.text}`);
10503
10605
  // src/read_commands.ts
10504
10606
  var SYMBOL_PREVIEW_LINES = 5;
10505
10607
  var GREP_MAX_LINES = 200;
10506
- var FIND_SCAN_LIMIT2 = 2e4;
10507
- var REFS_TOP_SCAN_LIMIT = 2e4;
10608
+ var FIND_SCAN_LIMIT = 2e4;
10508
10609
  function fileExists2(p) {
10509
10610
  try {
10510
10611
  fs12.statSync(p);
@@ -10579,6 +10680,7 @@ function verifyPin(p, pinned) {
10579
10680
  verifyPinnedIdentity(p, pinned);
10580
10681
  }
10581
10682
  function indexFileSyncPinned(resolvedPath, dbPath) {
10683
+ recordKnownRootThrottled(resolvedPath, dataDir(), dbPath);
10582
10684
  const pinned = activePins?.get(pinKey(path12.resolve(resolvedPath)));
10583
10685
  if (pinned === void 0) {
10584
10686
  indexFileSync(resolvedPath, dbPath);
@@ -10719,15 +10821,8 @@ function warnIfFilesStale(filePaths) {
10719
10821
  );
10720
10822
  }
10721
10823
  }
10722
- function emit4(text) {
10723
- const out = colorStdout() ? text : stripAnsi(text);
10724
- process.stdout.write(ensureNewline(out));
10725
- }
10726
- function emitErr4(text) {
10727
- process.stderr.write(ensureNewline(text));
10728
- }
10729
10824
  function emitGuarded(text, command) {
10730
- emit4(guardText(text, command));
10825
+ emit(guardText(text, command));
10731
10826
  }
10732
10827
  function guardText(text, command) {
10733
10828
  const cfg = loadConfig();
@@ -10782,6 +10877,11 @@ function resolveEnclosingSymbol(filePath, chunkStartLine) {
10782
10877
  }
10783
10878
  return best === null ? null : { name: best.name, kind: best.kind, lineStart: best.lineStart };
10784
10879
  }
10880
+ var IMPORT_BIND_BODY_RE = /^\s*(?:const|let|var)\s*\{[^}]*\}\s*=\s*(?:await\s+)?(?:import|require)\s*\(/;
10881
+ function stableSortImportBindsLast(rows) {
10882
+ const isBind = (r) => typeof r.body === "string" && IMPORT_BIND_BODY_RE.test(r.body) ? 1 : 0;
10883
+ return [...rows].sort((a, b) => isBind(a) - isBind(b));
10884
+ }
10785
10885
  function runSymbol(opts) {
10786
10886
  if (opts.limit !== void 0 && opts.limit <= 0) {
10787
10887
  return { text: `--limit must be a positive number, got: ${opts.limit}`, code: 1 };
@@ -10797,6 +10897,7 @@ function runSymbol(opts) {
10797
10897
  }
10798
10898
  const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
10799
10899
  const excludeTests = opts.excludeTests === true;
10900
+ const excludeVendored = opts.excludeVendored === true;
10800
10901
  const baseConfined = confinedProjectRoot();
10801
10902
  const confinedRoot = opts.projectRoot !== void 0 && baseConfined !== null && isProjectRootAllowed(opts.projectRoot, baseConfined) ? confinedProjectRoot(opts.projectRoot) ?? baseConfined : baseConfined;
10802
10903
  if (baseConfined !== null) {
@@ -10809,33 +10910,48 @@ function runSymbol(opts) {
10809
10910
  }
10810
10911
  }
10811
10912
  const queryOpts = {};
10812
- if (opts.name !== void 0) queryOpts.name = opts.name;
10813
10913
  if (opts.file !== void 0) {
10814
10914
  queryOpts.filePath = resolveIndexPath(opts.file, opts.projectRoot ?? process.cwd());
10815
10915
  healStaleIndex(queryOpts.filePath);
10816
10916
  }
10917
+ if (opts.name !== void 0) queryOpts.name = queryOpts.filePath !== void 0 ? stripHtmlIdSpelling(opts.name, queryOpts.filePath) : opts.name;
10817
10918
  if (opts.kind !== void 0) queryOpts.kind = opts.kind;
10818
- if (matchesGrep !== void 0 || excludeTests) {
10819
- queryOpts.limit = FIND_SCAN_LIMIT2;
10820
- } else if (opts.limit !== void 0) {
10919
+ const anyClientFilter = matchesGrep !== void 0 || excludeTests || excludeVendored;
10920
+ const effectiveLimit = opts.limit ?? DEFAULT_QUERY_LIMIT;
10921
+ if (!anyClientFilter && opts.limit !== void 0) {
10821
10922
  queryOpts.limit = opts.limit;
10822
10923
  }
10823
10924
  if (opts.file === void 0 && opts.projectRoot !== void 0) queryOpts.rootDir = opts.projectRoot;
10824
10925
  if (opts.file === void 0 && queryOpts.rootDir === void 0 && confinedRoot !== null) queryOpts.rootDir = confinedRoot;
10825
- let rawResults = querySymbols(queryOpts);
10926
+ const runSweep = () => {
10927
+ const found = { kept: [], keptCount: 0, scanned: 0, hiddenByExcludeTests: 0, files: /* @__PURE__ */ new Set() };
10928
+ const take = (s) => {
10929
+ found.scanned++;
10930
+ found.files.add(s.filePath);
10931
+ const nameKept = matchesGrep === void 0 || matchesGrep(s.name);
10932
+ if (nameKept && !(excludeTests && isTestFile(s.filePath)) && !(excludeVendored && isIgnoredIndexPath(s.filePath))) {
10933
+ found.keptCount++;
10934
+ if (found.kept.length < effectiveLimit) found.kept.push(s);
10935
+ } else if (excludeTests && nameKept && isTestFile(s.filePath)) {
10936
+ found.hiddenByExcludeTests++;
10937
+ }
10938
+ };
10939
+ if (anyClientFilter) forEachSymbol(queryOpts, take);
10940
+ else for (const row of querySymbols(queryOpts)) take(row);
10941
+ return found;
10942
+ };
10943
+ let sweep = runSweep();
10826
10944
  let stillStale = EMPTY_PATH_SET;
10827
10945
  if (opts.file === void 0) {
10828
- const heal = healStaleResultFiles(rawResults.map((s) => s.filePath));
10946
+ const heal = healStaleResultFiles([...sweep.files]);
10829
10947
  stillStale = heal.stillStale;
10830
- if (heal.healed) rawResults = querySymbols(queryOpts);
10831
- }
10832
- const preFilterCount = rawResults.length;
10833
- const effectiveLimit = opts.limit ?? 100;
10834
- const anyClientFilter = matchesGrep !== void 0 || excludeTests;
10835
- const filtered = anyClientFilter ? rawResults.filter((s) => (matchesGrep === void 0 || matchesGrep(s.name)) && !(excludeTests && isTestFile(s.filePath))) : rawResults;
10836
- const results = anyClientFilter ? filtered.slice(0, effectiveLimit) : filtered;
10837
- const hiddenByExcludeTests = excludeTests ? rawResults.filter((s) => (matchesGrep === void 0 || matchesGrep(s.name)) && isTestFile(s.filePath)).length : 0;
10838
- if (excludeTests && filtered.length === 0 && hiddenByExcludeTests > 0) {
10948
+ if (heal.healed) sweep = runSweep();
10949
+ }
10950
+ const preFilterCount = sweep.scanned;
10951
+ const unordered = sweep.kept.slice(0, effectiveLimit);
10952
+ const results = opts.name === void 0 ? unordered : stableSortImportBindsLast(unordered);
10953
+ const hiddenByExcludeTests = sweep.hiddenByExcludeTests;
10954
+ if (excludeTests && sweep.keptCount === 0 && hiddenByExcludeTests > 0) {
10839
10955
  const label = opts.name ?? opts.grep ?? "*";
10840
10956
  const notice = `no non-test matches for '${label}' (${excludeTestsHiddenNote(hiddenByExcludeTests)})`;
10841
10957
  if (opts.json === true) {
@@ -10843,7 +10959,7 @@ function runSymbol(opts) {
10843
10959
  }
10844
10960
  return { text: `token-goat: ${notice}`, code: 0 };
10845
10961
  }
10846
- if (matchesGrep !== void 0 && filtered.length === 0 && preFilterCount > 0) {
10962
+ if (matchesGrep !== void 0 && sweep.keptCount === 0 && preFilterCount > 0) {
10847
10963
  if (opts.json === true) {
10848
10964
  const text2 = displaySafeJson({ items: [], truncated: false, totalCount: 0 });
10849
10965
  return { text: text2, code: 0 };
@@ -10856,8 +10972,14 @@ function runSymbol(opts) {
10856
10972
  const indexEmpty = emptyIndexRoot !== null && isIndexEmptyForProject(globalDbPath(), emptyIndexRoot);
10857
10973
  if (opts.name !== void 0 && emptyIndexRoot !== null) {
10858
10974
  const rootDir = emptyIndexRoot;
10859
- const rawSymbols = querySymbols({ limit: FIND_SCAN_LIMIT2, rootDir });
10860
- const exactMatches = rawSymbols.filter((s) => s.name === opts.name);
10975
+ const exactMatches = [];
10976
+ const allNames = /* @__PURE__ */ new Set();
10977
+ const structuredFileSet = /* @__PURE__ */ new Set();
10978
+ forEachSymbol({ rootDir }, (s) => {
10979
+ allNames.add(s.name);
10980
+ structuredFileSet.add(s.filePath);
10981
+ if (s.name === opts.name) exactMatches.push(s);
10982
+ });
10861
10983
  if (exactMatches.length > 0) {
10862
10984
  const shown = exactMatches.slice(0, DIDYOUMEAN_LIMIT);
10863
10985
  const where = shown.map((s) => `${s.kind} at ${formatSymbolLocation(toDisplayPath(rootDir, s.filePath), s.lineStart)}`).join("; ");
@@ -10867,12 +10989,12 @@ function runSymbol(opts) {
10867
10989
  text2 += `
10868
10990
  '${opts.name}' IS indexed (${where}${more}) -- ${widen}`;
10869
10991
  } else {
10870
- const candidates = rankSimilarNames(rawSymbols.map((s) => s.name), opts.name);
10992
+ const candidates = rankSimilarNames([...allNames], opts.name);
10871
10993
  text2 += candidates.length > 0 ? `
10872
10994
  ${didYouMean(candidates)}` : indexEmpty ? "" : `
10873
10995
  Try: token-goat semantic "${opts.name}"`;
10874
10996
  }
10875
- const structuredFiles = [...new Set(rawSymbols.map((s) => s.filePath))].sort();
10997
+ const structuredFiles = [...structuredFileSet].sort();
10876
10998
  const hit = findStructuredKeyPath(opts.name, structuredFiles);
10877
10999
  if (hit !== null) {
10878
11000
  const display = toDisplayPath(rootDir, hit.filePath);
@@ -10898,8 +11020,8 @@ ${emptyIndexMessage(emptyIndexRoot)}`;
10898
11020
  let trueTotal;
10899
11021
  let truncatedFlag;
10900
11022
  if (anyClientFilter) {
10901
- trueTotal = filtered.length;
10902
- truncatedFlag = capped.truncated || results.length < filtered.length;
11023
+ trueTotal = sweep.keptCount;
11024
+ truncatedFlag = capped.truncated || results.length < sweep.keptCount;
10903
11025
  } else {
10904
11026
  trueTotal = countSymbols(queryOpts);
10905
11027
  truncatedFlag = capped.truncated || trueTotal > results.length;
@@ -10935,7 +11057,7 @@ ${preview}${elided}` : header;
10935
11057
  const warning = opts.file !== void 0 ? staleWarning(resolveIndexPath(opts.file, opts.projectRoot ?? process.cwd())) : "";
10936
11058
  const text = guardText(warning + blocks.join("\n\n"), "symbol");
10937
11059
  recordReadStat("symbol_lookup", fullSourceBytes, text, opts.name ?? opts.file ?? opts.grep);
10938
- const symbolTotal = () => anyClientFilter ? { count: filtered.length, exact: rawResults.length < FIND_SCAN_LIMIT2 } : { count: countSymbols(queryOpts), exact: true };
11060
+ const symbolTotal = () => anyClientFilter ? { count: sweep.keptCount, exact: true } : { count: countSymbols(queryOpts), exact: true };
10939
11061
  return { text: text + truncationFooter(results.length, effectiveLimit, symbolTotal, "matches", "--limit"), code: 0 };
10940
11062
  }
10941
11063
  function truncationFooter(shown, limit, total, plural, flag) {
@@ -10946,6 +11068,7 @@ token-goat: ${notice}`;
10946
11068
  }
10947
11069
  function refsTotal(clientFiltered, filteredTotal, shown, countExact, preScanCount, scanLimit) {
10948
11070
  if (!clientFiltered) return { count: countExact(), exact: true };
11071
+ if (scanLimit < 0) return { count: filteredTotal ?? shown, exact: true };
10949
11072
  return { count: filteredTotal ?? shown, exact: preScanCount < scanLimit };
10950
11073
  }
10951
11074
  function truncationNotice(shown, limit, total, plural, flag) {
@@ -10957,6 +11080,8 @@ function truncationNotice(shown, limit, total, plural, flag) {
10957
11080
  function runRead(opts) {
10958
11081
  const range = parseLineRange(opts.spec);
10959
11082
  if (range !== null) return runLineRange(range, opts);
11083
+ const region = parseColonLineSpec(opts.spec);
11084
+ if (region !== null) return runLineRegion(region, opts);
10960
11085
  const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
10961
11086
  if (crossFilePairs !== null) return runReadMulti(crossFilePairs, opts);
10962
11087
  const { file, symbol } = parseReadSpec(opts.spec);
@@ -10996,7 +11121,7 @@ function runRead(opts) {
10996
11121
  const crossFileLead = formatCrossFileLead("read", symbol, file, opts.projectRoot);
10997
11122
  if (crossFileLead !== "") messages.push(crossFileLead);
10998
11123
  const resolved = resolveIndexPath(file, opts.projectRoot ?? process.cwd());
10999
- const scanned = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT2 }).map((s) => s.name);
11124
+ const scanned = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT }).map((s) => s.name);
11000
11125
  const closes = rankSimilarNames(scanned, symbol);
11001
11126
  if (closes.length > 0) messages.push(didYouMean(closes));
11002
11127
  else if (scanned.length > 0) {
@@ -11046,8 +11171,7 @@ Query subtree: token-goat json-query ${file} '<path>'`);
11046
11171
  {
11047
11172
  ...match,
11048
11173
  body: resolveBody(match),
11049
- // The text branch below prepends staleWarning's DELETED line; without this the JSON form
11050
- // would be the one surface that still passes a deleted file's body off as a live read.
11174
+ // The text branch below prepends staleWarning's DELETED line; without this the JSON form would be the one surface that still passes a deleted file's body off as a live read.
11051
11175
  ...fileIsGone(match.filePath) ? { deleted: true } : {},
11052
11176
  ...refCounts !== void 0 ? { refCount: refCounts.get(match.name) ?? 0 } : {}
11053
11177
  }
@@ -11063,7 +11187,9 @@ Query subtree: token-goat json-query ${file} '<path>'`);
11063
11187
  body
11064
11188
  ];
11065
11189
  const warning = staleWarning(match.filePath);
11066
- const text = guardText(warning + trimBlankLines(lines).join("\n"), "symbol");
11190
+ const narrowerSliceHint = bodyLen > LARGE_SYMBOL_LINE_THRESHOLD ? `
11191
+ # for a narrower slice: token-goat grep "<pattern>" ${file} -C 15 --symbol` : "";
11192
+ const text = guardText(warning + trimBlankLines(lines).join("\n"), "symbol") + narrowerSliceHint;
11067
11193
  if (opts.suppressStat !== true) recordReadStat("read_replacement", fullSourceBytes, text, opts.spec);
11068
11194
  return { text, code: 0 };
11069
11195
  }
@@ -11164,9 +11290,9 @@ function renderRefsTargets(targets, opts, {
11164
11290
  const refRows = [];
11165
11291
  for (const { file, symbol, key } of targets) {
11166
11292
  const queryOpts = { name: symbol };
11167
- if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
11293
+ if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = UNBOUNDED_QUERY_LIMIT;
11168
11294
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
11169
- else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
11295
+ else if (opts.top !== void 0) queryOpts.limit = UNBOUNDED_QUERY_LIMIT;
11170
11296
  const rootDir = refsRootDir(opts);
11171
11297
  if (rootDir !== void 0) queryOpts.rootDir = rootDir;
11172
11298
  const scanned = queryRefs(queryOpts);
@@ -11236,7 +11362,7 @@ function renderRefsTargets(targets, opts, {
11236
11362
  const fullSourceBytes = refsSearchBaselineBytes(refRows);
11237
11363
  if (opts.json === true) {
11238
11364
  const text2 = displaySafeJson(jsonOut);
11239
- emit4(text2);
11365
+ emit(text2);
11240
11366
  if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text2, opts.spec);
11241
11367
  return anyFound ? 0 : 1;
11242
11368
  }
@@ -11247,11 +11373,11 @@ function renderRefsTargets(targets, opts, {
11247
11373
  }
11248
11374
  function runRefs(opts) {
11249
11375
  if (opts.limit !== void 0 && opts.limit <= 0) {
11250
- emitErr4(`--limit must be a positive number, got: ${opts.limit}`);
11376
+ emitErr(`--limit must be a positive number, got: ${opts.limit}`);
11251
11377
  return 1;
11252
11378
  }
11253
11379
  if (opts.top !== void 0 && opts.top <= 0) {
11254
- emitErr4(`--top must be a positive number, got: ${opts.top}`);
11380
+ emitErr(`--top must be a positive number, got: ${opts.top}`);
11255
11381
  return 1;
11256
11382
  }
11257
11383
  const baseConfined = confinedProjectRoot();
@@ -11260,13 +11386,13 @@ function runRefs(opts) {
11260
11386
  const requested = opts.projectRoot;
11261
11387
  const projectDenial = requested === void 0 || confinedRoot !== baseConfined ? null : confinementRefusal("--project", requested, baseConfined);
11262
11388
  if (projectDenial !== null) {
11263
- emitErr4(projectDenial);
11389
+ emitErr(projectDenial);
11264
11390
  return 1;
11265
11391
  }
11266
11392
  for (const file2 of refsSpecFiles(opts.spec)) {
11267
11393
  const denial = confinementRefusal("This file", resolveIndexPath(file2, opts.projectRoot ?? process.cwd()), confinedRoot);
11268
11394
  if (denial !== null) {
11269
- emitErr4(denial);
11395
+ emitErr(denial);
11270
11396
  return 1;
11271
11397
  }
11272
11398
  }
@@ -11297,9 +11423,9 @@ function runRefsSingle(opts) {
11297
11423
  const symName = symbol ?? file;
11298
11424
  const queryOpts = { name: symName };
11299
11425
  const defFileHint = symbol !== void 0 ? resolveIndexPath(file, opts.projectRoot ?? process.cwd()) : void 0;
11300
- if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
11426
+ if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = UNBOUNDED_QUERY_LIMIT;
11301
11427
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
11302
- else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
11428
+ else if (opts.top !== void 0) queryOpts.limit = UNBOUNDED_QUERY_LIMIT;
11303
11429
  const rootDir = refsRootDir(opts);
11304
11430
  if (rootDir !== void 0) queryOpts.rootDir = rootDir;
11305
11431
  const scanned = queryRefs(queryOpts);
@@ -11325,39 +11451,39 @@ function runRefsSingle(opts) {
11325
11451
  if (results.length === 0) {
11326
11452
  if (matchesGrep !== void 0 && preGrepCount > 0) {
11327
11453
  if (opts.json === true) {
11328
- emit4(displaySafeJson({ items: [], truncated: false, totalCount: 0, hiddenByGrep: preGrepCount }));
11454
+ emit(displaySafeJson({ items: [], truncated: false, totalCount: 0, hiddenByGrep: preGrepCount }));
11329
11455
  return 0;
11330
11456
  }
11331
- emit4(grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references"));
11457
+ emit(grepFilteredToEmptyNotice(preGrepCount, opts.grep ?? "", "reference", "references"));
11332
11458
  return 0;
11333
11459
  }
11334
11460
  if (opts.excludeTests === true && suppressed > 0) {
11335
- emitErr4(`No non-test references found for '${symName}' (${excludeTestsHiddenNote(suppressed)})`);
11461
+ emitErr(`No non-test references found for '${symName}' (${excludeTestsHiddenNote(suppressed)})`);
11336
11462
  return 1;
11337
11463
  }
11338
11464
  const rootDir2 = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
11339
11465
  const defRows = querySymbols({ name: symName, rootDir: rootDir2, limit: REF_BLIND_DEF_PROBE_LIMIT });
11340
11466
  if (defRows.length === 0) {
11341
- emitErr4(`Symbol not found: ${symName}${unknownSymbolSuggestion(symName, rootDir2)}`);
11342
- if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir2)) emitErr4(emptyIndexMessage(rootDir2));
11467
+ emitErr(`Symbol not found: ${symName}${unknownSymbolSuggestion(symName, rootDir2)}`);
11468
+ if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir2)) emitErr(emptyIndexMessage(rootDir2));
11343
11469
  return 1;
11344
11470
  }
11345
11471
  const defPaths = defFileHint !== void 0 ? [defFileHint] : defRows.map((r) => r.filePath);
11346
11472
  const firstDefPath = defPaths[0];
11347
11473
  if (firstDefPath !== void 0 && defPaths.every((fp) => !isRefIndexedFile(fp))) {
11348
- emitErr4(refBlindLanguageNotice(symName, detectLanguageOfFile(firstDefPath), refsDisplayPath(firstDefPath)));
11474
+ emitErr(refBlindLanguageNotice(symName, detectLanguageOfFile(firstDefPath), refsDisplayPath(firstDefPath)));
11349
11475
  return 1;
11350
11476
  }
11351
11477
  const kindRows = defFileHint !== void 0 ? querySymbols({ name: symName, filePath: defFileHint, rootDir: rootDir2, limit: REF_BLIND_DEF_PROBE_LIMIT }) : defRows;
11352
11478
  const kindVerdict = refBlindKindVerdict(kindRows);
11353
11479
  if (kindVerdict.allBlind) {
11354
- emitErr4(refBlindKindNotice(symName, kindVerdict.blindKinds));
11480
+ emitErr(refBlindKindNotice(symName, kindVerdict.blindKinds));
11355
11481
  return 1;
11356
11482
  }
11357
- emitErr4(`No references found for '${symName}'`);
11358
- if (kindVerdict.blindCount > 0) emitErr4(refBlindKindPartialNote(symName, kindVerdict.blindKinds, kindVerdict.blindCount, kindRows.length));
11483
+ emitErr(`No references found for '${symName}'`);
11484
+ if (kindVerdict.blindCount > 0) emitErr(refBlindKindPartialNote(symName, kindVerdict.blindKinds, kindVerdict.blindCount, kindRows.length));
11359
11485
  if (opts.json !== true) {
11360
- if (isIndexEmptyForProject(globalDbPath(), rootDir2)) emitErr4(emptyIndexMessage(rootDir2));
11486
+ if (isIndexEmptyForProject(globalDbPath(), rootDir2)) emitErr(emptyIndexMessage(rootDir2));
11361
11487
  }
11362
11488
  return 1;
11363
11489
  }
@@ -11374,14 +11500,14 @@ function runRefsSingle(opts) {
11374
11500
  }
11375
11501
  const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - (filteredTotal ?? results.length) : 0;
11376
11502
  const text2 = displaySafeJson({ ...payload, ...hiddenByGrep > 0 ? { hiddenByGrep } : {} });
11377
- emit4(text2);
11503
+ emit(text2);
11378
11504
  recordReadStat("symbol_read", fullSourceBytes, text2, symName);
11379
11505
  return 0;
11380
11506
  }
11381
11507
  const lines = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, suppressed) : opts.callers === true ? [...opts.excludeTests === true && suppressed > 0 ? [`${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`] : [], ...renderCallerGroups(results, opts.context ?? 0)] : [...opts.excludeTests === true && suppressed > 0 ? [`${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`] : [], ...results.flatMap((ref) => renderRefLines(ref, opts.context ?? 0, ""))];
11382
11508
  const refsFooter = opts.top !== void 0 ? "" : truncationFooter(results.length, opts.limit ?? 100, () => refsTotal(clientFiltered, filteredTotal, results.length, () => countRefs(queryOpts), preScanCount, scanLimit), "references", "--limit");
11383
11509
  const text = lines.join("\n");
11384
- emit4(guardText(text, "symbol") + refsFooter);
11510
+ emit(guardText(text, "symbol") + refsFooter);
11385
11511
  recordReadStat("symbol_read", fullSourceBytes, text + refsFooter, symName);
11386
11512
  return 0;
11387
11513
  }
@@ -11437,11 +11563,11 @@ function renderCallerGroups(refs, contextLines = 0) {
11437
11563
  function runPrSlice(opts) {
11438
11564
  const parsed = parsePrSliceArg(opts.slice);
11439
11565
  if (parsed === null) {
11440
- emitErr4(`Invalid slice '${opts.slice}' -- expected one of: files, diff:<path>, comments, description`);
11566
+ emitErr(`Invalid slice '${opts.slice}' -- expected one of: files, diff:<path>, comments, description`);
11441
11567
  return 1;
11442
11568
  }
11443
11569
  if (!isGhAvailable()) {
11444
- emitErr4("gh (GitHub CLI) not found on PATH -- install it from https://cli.github.com and run `gh auth login`");
11570
+ emitErr("gh (GitHub CLI) not found on PATH -- install it from https://cli.github.com and run `gh auth login`");
11445
11571
  return 1;
11446
11572
  }
11447
11573
  let repo = opts.repo;
@@ -11455,21 +11581,21 @@ function runPrSlice(opts) {
11455
11581
  }
11456
11582
  const resolved = remoteUrl.length > 0 ? parseGithubRepoFromRemoteUrl(remoteUrl) : null;
11457
11583
  if (resolved === null) {
11458
- emitErr4("Could not resolve a GitHub repo from the current directory's git remote 'origin' -- pass --repo owner/repo");
11584
+ emitErr("Could not resolve a GitHub repo from the current directory's git remote 'origin' -- pass --repo owner/repo");
11459
11585
  return 1;
11460
11586
  }
11461
11587
  repo = resolved;
11462
11588
  }
11463
11589
  if (!isSafeRepoSlug(repo)) {
11464
- emitErr4(`"${repo}" is not a plain owner/name repository slug -- pass --repo owner/repo`);
11590
+ emitErr(`"${repo}" is not a plain owner/name repository slug -- pass --repo owner/repo`);
11465
11591
  return 1;
11466
11592
  }
11467
11593
  if (!isSafePrNumber(opts.pr)) {
11468
- emitErr4(`"${opts.pr}" is not a pull request number`);
11594
+ emitErr(`"${opts.pr}" is not a pull request number`);
11469
11595
  return 1;
11470
11596
  }
11471
11597
  if (!isGhAuthenticated()) {
11472
- emitErr4("gh is not authenticated -- run `gh auth login`");
11598
+ emitErr("gh is not authenticated -- run `gh auth login`");
11473
11599
  return 1;
11474
11600
  }
11475
11601
  try {
@@ -11480,7 +11606,7 @@ function runPrSlice(opts) {
11480
11606
  if (opts.json === true) {
11481
11607
  const capped = guardJsonRows(files);
11482
11608
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }, 0);
11483
- emit4(jsonText);
11609
+ emit(jsonText);
11484
11610
  recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} files`);
11485
11611
  } else {
11486
11612
  const text = formatFilesSlice(files);
@@ -11493,14 +11619,14 @@ function runPrSlice(opts) {
11493
11619
  const diffText = fetchPrDiff(opts.pr, repo);
11494
11620
  const rawFileDiff = extractFileDiff(diffText, parsed.path);
11495
11621
  if (rawFileDiff === null) {
11496
- emitErr4(`No diff found for '${parsed.path}' in PR #${opts.pr}`);
11622
+ emitErr(`No diff found for '${parsed.path}' in PR #${opts.pr}`);
11497
11623
  return 1;
11498
11624
  }
11499
11625
  const fileDiff = redactSecrets(rawFileDiff).text;
11500
11626
  const fullSourceBytes = Buffer.byteLength(diffText, "utf8");
11501
11627
  if (opts.json === true) {
11502
11628
  const jsonText = displaySafeJson({ path: parsed.path, diff: fenceGithubFieldIfMatched(fileDiff) }, 0);
11503
- emit4(jsonText);
11629
+ emit(jsonText);
11504
11630
  recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} diff:${parsed.path}`);
11505
11631
  } else {
11506
11632
  emitGuarded(fenceGithubText(fileDiff), "pr-slice");
@@ -11520,7 +11646,7 @@ function runPrSlice(opts) {
11520
11646
  const fencedComments = comments.map((c) => ({ ...c, body: fenceGithubFieldIfMatched(c.body) }));
11521
11647
  const capped = guardJsonRows(fencedComments);
11522
11648
  const jsonText = displaySafeJson({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount }, 0);
11523
- emit4(jsonText);
11649
+ emit(jsonText);
11524
11650
  recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} comments`);
11525
11651
  } else {
11526
11652
  const text = formatCommentsSlice(comments);
@@ -11544,7 +11670,7 @@ function runPrSlice(opts) {
11544
11670
  body: desc.body !== null ? fenceGithubFieldIfMatched(desc.body) : null
11545
11671
  };
11546
11672
  const jsonText = displaySafeJson(fencedDesc, 0);
11547
- emit4(jsonText);
11673
+ emit(jsonText);
11548
11674
  recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} description`);
11549
11675
  } else {
11550
11676
  const text = formatDescriptionSlice(desc);
@@ -11555,7 +11681,7 @@ function runPrSlice(opts) {
11555
11681
  }
11556
11682
  }
11557
11683
  } catch (e) {
11558
- emitErr4(extractErrorMessage(e));
11684
+ emitErr(extractErrorMessage(e));
11559
11685
  return 1;
11560
11686
  }
11561
11687
  }
@@ -11654,7 +11780,7 @@ function runGrep(opts) {
11654
11780
  const contextLines = opts.context ?? 0;
11655
11781
  const guarded = compileGuardedRegex(opts.pattern);
11656
11782
  if (!guarded.ok) {
11657
- emitErr4(`Invalid regex: ${opts.pattern} -- ${guarded.reason}`);
11783
+ emitErr(`Invalid regex: ${opts.pattern} -- ${guarded.reason}`);
11658
11784
  return 1;
11659
11785
  }
11660
11786
  const regex = guarded.re;
@@ -11740,7 +11866,7 @@ function runGrep(opts) {
11740
11866
  }
11741
11867
  for (const searchPath of searchPaths) {
11742
11868
  if (!fileExists2(searchPath)) {
11743
- emitErr4(`Path not found: ${searchPath}`);
11869
+ emitErr(`Path not found: ${searchPath}`);
11744
11870
  return 1;
11745
11871
  }
11746
11872
  const stat = fs12.statSync(searchPath);
@@ -11767,7 +11893,7 @@ function runGrep(opts) {
11767
11893
  }
11768
11894
  }
11769
11895
  if (hits.length === 0) {
11770
- emitErr4(`No matches for '${opts.pattern}'`);
11896
+ emitErr(`No matches for '${opts.pattern}'`);
11771
11897
  return 1;
11772
11898
  }
11773
11899
  const truncated = hits.slice(0, maxLines);
@@ -11783,32 +11909,44 @@ function runGrep(opts) {
11783
11909
  hit.symbol = enc === null ? null : { name: enc.name, kind: enc.kind, lineStart: enc.lineStart, lineEnd: enc.lineEnd };
11784
11910
  }
11785
11911
  for (const file of new Set(truncated.map((h) => h.file).filter(isVirtualIndexedPath))) {
11786
- emit4(virtualIndexedScopeNote(displaySafeText(file), "a match in it cannot be attributed to a symbol and these hits carry no label"));
11912
+ emit(virtualIndexedScopeNote(displaySafeText(file), "a match in it cannot be attributed to a symbol and these hits carry no label"));
11787
11913
  }
11788
11914
  }
11789
11915
  if (opts.json === true) {
11790
11916
  const payload = { items: truncated, truncated: hits.length > maxLines, totalCount: hits.length };
11791
- emit4(displaySafeJson(payload));
11917
+ emit(displaySafeJson(payload));
11792
11918
  return 0;
11793
11919
  }
11794
11920
  for (const hit of truncated) {
11795
11921
  const symbolTag = opts.symbol === true && hit.symbol != null ? ` [${hit.symbol.name} (${hit.symbol.kind})]` : "";
11796
11922
  if (hit.context !== void 0) {
11797
- for (const line of renderContextWindow(hit.file, hit.line, hit.context, symbolTag)) emit4(line);
11923
+ for (const line of renderContextWindow(hit.file, hit.line, hit.context, symbolTag)) emit(line);
11798
11924
  } else {
11799
- emit4(`${displaySafeText(hit.file)}:${hit.line}: ${hit.text}${symbolTag}`);
11925
+ emit(`${displaySafeText(hit.file)}:${hit.line}: ${hit.text}${symbolTag}`);
11800
11926
  }
11801
11927
  }
11802
11928
  if (hits.length > maxLines) {
11803
- emitErr4(`... (${hits.length - maxLines} more lines omitted)`);
11804
- emitErr4(`Tip: To avoid broad grepping and reduce token expenditure, locate specific symbols with:`);
11805
- emitErr4(` token-goat symbol <name>`);
11806
- emitErr4(` token-goat locate <name>`);
11807
- emitErr4(` token-goat outline <file> / token-goat skeleton <file>`);
11929
+ emitErr(`... (${hits.length - maxLines} more lines omitted)`);
11930
+ emitErr(`Tip: To avoid broad grepping and reduce token expenditure, locate specific symbols with:`);
11931
+ emitErr(` token-goat symbol <name>`);
11932
+ emitErr(` token-goat locate <name>`);
11933
+ emitErr(` token-goat outline <file> / token-goat skeleton <file>`);
11808
11934
  }
11809
11935
  return 0;
11810
11936
  }
11811
11937
  var RRF_K = 60;
11938
+ function applyRelevanceFloor(hits, floor) {
11939
+ const kept = [];
11940
+ let nearestRejected = null;
11941
+ for (const h of hits) {
11942
+ if (h.distance <= floor) {
11943
+ kept.push(h);
11944
+ } else if (nearestRejected === null || h.distance < nearestRejected) {
11945
+ nearestRejected = h.distance;
11946
+ }
11947
+ }
11948
+ return { kept, nearestRejected };
11949
+ }
11812
11950
  async function runSemantic(query, opts) {
11813
11951
  if (opts.limit !== void 0 && opts.limit <= 0) {
11814
11952
  const message = `--limit must be a positive number, got: ${opts.limit}`;
@@ -11897,7 +12035,19 @@ async function runSemantic(query, opts) {
11897
12035
  }
11898
12036
  }
11899
12037
  }
11900
- if (rawHits.length === 0 && isAvailable() && preflight.status === "ready" && !searchSemanticError) {
12038
+ let floorNearestRejected = null;
12039
+ if (rawHits.length > 0) {
12040
+ const floor = loadConfig().semantic.max_distance;
12041
+ const { kept, nearestRejected } = applyRelevanceFloor(rawHits, floor);
12042
+ floorNearestRejected = nearestRejected;
12043
+ if (kept.length === 0 && nearestRejected !== null) {
12044
+ console.warn(
12045
+ `Matching on meaning found nothing within ${floor} (closest was ${nearestRejected.toFixed(3)}); these results come from keyword search alone. Raise semantic.max_distance to see weaker matches.`
12046
+ );
12047
+ }
12048
+ rawHits = kept;
12049
+ }
12050
+ if (rawHits.length === 0 && floorNearestRejected === null && isAvailable() && preflight.status === "ready" && !searchSemanticError) {
11901
12051
  try {
11902
12052
  const enabled = loadConfig().indexing.embeddings_enabled;
11903
12053
  const { indexedFiles, embeddedFiles } = enabled ? getEmbeddingCoverage(globalDbPath(), rootDir) : { indexedFiles: 0, embeddedFiles: 0 };
@@ -11925,7 +12075,9 @@ async function runSemantic(query, opts) {
11925
12075
  kind: enclosing?.kind ?? null,
11926
12076
  distance: h.distance,
11927
12077
  previewText: h.text,
11928
- rrf: 1 / (RRF_K + denseRank)
12078
+ rrf: 1 / (RRF_K + denseRank),
12079
+ inDense: true,
12080
+ inLexical: false
11929
12081
  });
11930
12082
  });
11931
12083
  ftsRows.forEach((s, ftsRank) => {
@@ -11933,6 +12085,7 @@ async function runSemantic(query, opts) {
11933
12085
  const existing = fused.get(key);
11934
12086
  if (existing !== void 0) {
11935
12087
  existing.rrf += 1 / (RRF_K + ftsRank);
12088
+ existing.inLexical = true;
11936
12089
  } else {
11937
12090
  fused.set(key, {
11938
12091
  filePath: s.filePath,
@@ -11942,7 +12095,9 @@ async function runSemantic(query, opts) {
11942
12095
  kind: s.kind,
11943
12096
  distance: null,
11944
12097
  previewText: s.body,
11945
- rrf: 1 / (RRF_K + ftsRank)
12098
+ rrf: 1 / (RRF_K + ftsRank),
12099
+ inDense: false,
12100
+ inLexical: true
11946
12101
  });
11947
12102
  }
11948
12103
  });
@@ -11965,12 +12120,15 @@ async function runSemantic(query, opts) {
11965
12120
  warnIfFilesStale(hits.map((h) => h.filePath));
11966
12121
  if (hits.length > 0) {
11967
12122
  if (opts.json === true) {
11968
- const items = hits.map((h) => ({
12123
+ const items = hits.map((h, i) => ({
11969
12124
  filePath: toDisplayPath(rootDir, h.filePath),
11970
12125
  name: h.name,
11971
12126
  kind: h.kind,
11972
12127
  startLine: h.startLine,
11973
12128
  endLine: h.endLine,
12129
+ rank: i + 1,
12130
+ rrf: h.rrf,
12131
+ retrieval: h.inDense && h.inLexical ? "both" : h.inDense ? "dense" : "lexical",
11974
12132
  distance: h.distance,
11975
12133
  preview: previewLines(h.previewText, 3)
11976
12134
  }));
@@ -11994,18 +12152,19 @@ async function runSemantic(query, opts) {
11994
12152
  recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
11995
12153
  return { text: text3, code: 0 };
11996
12154
  }
11997
- const blocks = hits.map((h) => {
12155
+ const blocks = hits.map((h, i) => {
12156
+ const mark = h.inDense && h.inLexical ? ", +keyword" : "";
11998
12157
  if (h.distance !== null) {
11999
12158
  const suffix = h.name !== null ? ` \u2014 inside ${h.name} (${h.kind})` : "";
12000
- return `# ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine} (distance ${h.distance.toFixed(3)})${suffix}
12159
+ return `# ${i + 1}. ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine} (distance ${h.distance.toFixed(3)}${mark})${suffix}
12001
12160
  ${previewLines(h.previewText, 3)}`;
12002
12161
  }
12003
- return `# ${h.name} (${h.kind}) \u2014 ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine}
12162
+ return `# ${i + 1}. ${h.name} (${h.kind}) \u2014 ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine} (keyword)
12004
12163
  ${previewLines(h.previewText, 3)}`;
12005
12164
  });
12006
12165
  const text2 = guardText(blocks.join("\n\n"), "semantic");
12007
12166
  if (hits.length < eligibleCount) {
12008
- emitErr4(`Showing ${hits.length} of ${candidatesClipped ? "at least " : ""}${countNoun(eligibleCount, "match", "matches")} (raise --limit to see the rest).`);
12167
+ emitErr(`Showing ${hits.length} of ${candidatesClipped ? "at least " : ""}${countNoun(eligibleCount, "match", "matches")} (raise --limit to see the rest).`);
12009
12168
  }
12010
12169
  recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text2, query);
12011
12170
  return { text: text2, code: 0 };
@@ -12105,7 +12264,7 @@ function fileExists3(p) {
12105
12264
  return false;
12106
12265
  }
12107
12266
  }
12108
- var FIND_SCAN_LIMIT3 = 2e4;
12267
+ var FIND_SCAN_LIMIT2 = FIND_SCAN_LIMIT;
12109
12268
  var PARENT_IDENTIFIER_RE2 = /^[\w$]+$/;
12110
12269
  function parseReadSpec(spec) {
12111
12270
  const colonIdx = findSpecSeparator(spec);
@@ -12142,7 +12301,7 @@ function parseMultiFileSpec(spec) {
12142
12301
  function extraFileArgsNote(command, first, extras, opts = {}) {
12143
12302
  const noun = opts.noun ?? "file";
12144
12303
  const head = `Note: ${extras.length} extra ${noun} argument(s) ignored (${extras.join(", ")}).`;
12145
- if (opts.mergeable === false) return `${head} ${command} takes one ${noun} at a time.`;
12304
+ if (opts.mergeable === false) return `${head} Run ${command} once per ${noun}.`;
12146
12305
  return `${head} ${command} reads one ${noun}, or a comma-separated list: token-goat ${command} "${[first, ...extras].join(",")}"`;
12147
12306
  }
12148
12307
  function parseLineRange(spec) {
@@ -12154,6 +12313,22 @@ function parseLineRange(spec) {
12154
12313
  const end = m[3] !== void 0 ? parseInt(m[3], 10) : start;
12155
12314
  return { file: m[1], start, end };
12156
12315
  }
12316
+ function parseColonLineSpec(spec) {
12317
+ const colonIdx = spec.lastIndexOf(":");
12318
+ if (colonIdx <= 0) return null;
12319
+ const suffix = spec.slice(colonIdx + 1);
12320
+ if (!/^\d+(?:-\d+)?$/.test(suffix)) return null;
12321
+ const file = spec.slice(0, colonIdx);
12322
+ if (file === "") return null;
12323
+ if (/^[A-Za-z]$/.test(file)) return null;
12324
+ const pastDrive = /^[A-Za-z]:/.test(file) ? file.slice(2) : file;
12325
+ if (pastDrive === "" || pastDrive.includes(":")) return null;
12326
+ if (fileExists3(spec)) return null;
12327
+ const dash = suffix.indexOf("-");
12328
+ const start = parseInt(dash === -1 ? suffix : suffix.slice(0, dash), 10);
12329
+ const end = dash === -1 ? start : parseInt(suffix.slice(dash + 1), 10);
12330
+ return { file, start, end };
12331
+ }
12157
12332
  function parseColonLineRange(symbol) {
12158
12333
  const m = /^(\d+)(?:[-:,](\d+))?$/.exec(symbol);
12159
12334
  if (m === null) return null;
@@ -12161,6 +12336,19 @@ function parseColonLineRange(symbol) {
12161
12336
  const end = m[2] !== void 0 ? parseInt(m[2], 10) : start;
12162
12337
  return { start, end };
12163
12338
  }
12339
+ function readSpecsMergeable(specs) {
12340
+ if (specs.length < 2) return true;
12341
+ const joined = specs.join(",");
12342
+ if (parseLineRange(joined) !== null) return false;
12343
+ if (parseColonLineSpec(joined) !== null) return false;
12344
+ if (parseCrossFileMultiSpec(joined) !== null) return true;
12345
+ const { symbol } = parseReadSpec(joined);
12346
+ return symbol !== void 0 && symbol !== "" && symbol.includes(",") && parseColonLineRange(symbol) === null;
12347
+ }
12348
+ function namedSpecsMergeable(specs) {
12349
+ if (specs.length < 2) return true;
12350
+ return specs.every((spec) => spec.includes("::"));
12351
+ }
12164
12352
  function runLineRange(range, opts) {
12165
12353
  const { file, start, end } = range;
12166
12354
  const diskPath = resolveAgainstProjectRoot(file, opts.projectRoot);
@@ -12193,6 +12381,64 @@ function runLineRange(range, opts) {
12193
12381
  code: 0
12194
12382
  };
12195
12383
  }
12384
+ function runLineRegion(range, opts) {
12385
+ const { file, start, end } = range;
12386
+ if (start < 1) return { text: `Invalid line range: start must be >= 1 (got ${start})`, code: 1 };
12387
+ if (end < start) return { text: `Invalid line range: end (${end}) is before start (${start})`, code: 1 };
12388
+ const resolved = resolveIndexPath(file, opts.projectRoot ?? process.cwd());
12389
+ const confined = confinementRefusal("This file", resolved, confinedProjectRoot(opts.projectRoot));
12390
+ if (confined !== null) return { text: confined, code: 1 };
12391
+ const text = readFileText(resolveAgainstProjectRoot(file, opts.projectRoot));
12392
+ if (text === null) return { text: `Could not read: ${file}`, code: 1 };
12393
+ const allLines = text.split(/\r?\n/);
12394
+ if (allLines.length > 1 && allLines[allLines.length - 1] === "") allLines.pop();
12395
+ if (start > allLines.length) {
12396
+ return { text: `Line ${start} is past end of file (${countNoun(allLines.length, "line")}): ${file}`, code: 1 };
12397
+ }
12398
+ if (opts.forceRefresh === true) {
12399
+ indexFileSyncPinned(resolved, globalDbPath());
12400
+ enqueueDirtyPathSafe(resolved, { alreadyResolved: true });
12401
+ } else {
12402
+ healStaleIndex(resolved);
12403
+ }
12404
+ const symbols = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT2 });
12405
+ const regions = resolveLineRegions(symbols, allLines.length, start, end);
12406
+ const asked = start === end ? `${start}` : `${start}-${end}`;
12407
+ if (regions.length === 0) {
12408
+ return {
12409
+ text: `No indexed symbols in '${file}', so line ${asked} cannot be resolved to a region.
12410
+ Read the raw lines instead: token-goat read "${file}@${asked}"`,
12411
+ code: 1
12412
+ };
12413
+ }
12414
+ const slice = (r) => allLines.slice(r.start - 1, Math.min(r.end, allLines.length)).join("\n");
12415
+ if (opts.json === true) {
12416
+ return {
12417
+ text: displaySafeJson({
12418
+ file,
12419
+ requested: { start, end },
12420
+ regions: regions.map((r) => ({
12421
+ kind: r.kind,
12422
+ label: r.label,
12423
+ start: r.start,
12424
+ end: Math.min(r.end, allLines.length),
12425
+ lines: allLines.slice(r.start - 1, Math.min(r.end, allLines.length))
12426
+ }))
12427
+ }),
12428
+ code: 0
12429
+ };
12430
+ }
12431
+ const blocks = [];
12432
+ if (regions.length > 1) blocks.push(`# ${file}:${asked} -> ${countNoun(regions.length, "region")}`);
12433
+ regions.forEach((r, i) => {
12434
+ const body = slice(r);
12435
+ const tag = regions.length > 1 ? `[${i + 1}/${regions.length}] ` : `${file}:${asked} -> `;
12436
+ const span = `lines ${r.start}-${Math.min(r.end, allLines.length)} of ${allLines.length}`;
12437
+ blocks.push(`# ${tag}${r.label} ${span} (~${Math.ceil(body.length / 4)} tok)
12438
+ ${body}`);
12439
+ });
12440
+ return { text: guardText(staleWarning(resolved) + blocks.join("\n\n"), "lines"), code: 0 };
12441
+ }
12196
12442
  function findParentName(entry, fileSymbols) {
12197
12443
  let best = null;
12198
12444
  for (const s of fileSymbols) {
@@ -12219,7 +12465,7 @@ function formatAmbiguity(symbol, file, candidates, explicitRoot, commandName = "
12219
12465
  const getFileSyms = (filePath) => {
12220
12466
  let fileSyms = fileSymCache.get(filePath);
12221
12467
  if (fileSyms === void 0) {
12222
- fileSyms = querySymbols({ filePath, limit: FIND_SCAN_LIMIT3 });
12468
+ fileSyms = querySymbols({ filePath, limit: FIND_SCAN_LIMIT2 });
12223
12469
  fileSymCache.set(filePath, fileSyms);
12224
12470
  }
12225
12471
  return fileSyms;
@@ -12267,13 +12513,17 @@ function fileConfinementRefusal(label, file, projectRoot) {
12267
12513
  if (root === null) return null;
12268
12514
  return confinementRefusal(label, resolveIndexPath(file, projectRoot ?? process.cwd()), root);
12269
12515
  }
12516
+ function stripHtmlIdSpelling(name, filePath) {
12517
+ return name.startsWith("#") && detectLanguage(filePath) === "html" ? name.slice(1) : name;
12518
+ }
12270
12519
  function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
12271
12520
  const { file, symbol: rawSymbol } = parseReadSpec(spec);
12272
12521
  if (rawSymbol === void 0 || rawSymbol === "") return { kind: "none" };
12273
12522
  const anchorMatch = /^(.+)@(\d+)$/.exec(rawSymbol);
12274
- const symbol = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
12523
+ const anchorSymbol = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
12275
12524
  const lineAnchor = anchorMatch !== null ? parseInt(anchorMatch[2], 10) : void 0;
12276
12525
  const resolved = resolveIndexPath(file, projectRoot ?? process.cwd());
12526
+ const symbol = stripHtmlIdSpelling(anchorSymbol, resolved);
12277
12527
  const confined = confinementRefusal("This file", resolved, confinedProjectRoot(projectRoot));
12278
12528
  if (confined !== null) return { kind: "confined", message: confined };
12279
12529
  if (forceRefresh === true) {
@@ -12343,16 +12593,16 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
12343
12593
  function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
12344
12594
  const { file, symbol } = parseReadSpec(spec);
12345
12595
  if (symbol === void 0 || symbol === "") {
12346
- emitErr4(`'token-goat ${commandName}' requires a 'file::symbol' spec (got '${spec}')`);
12596
+ emitErr(`'token-goat ${commandName}' requires a 'file::symbol' spec (got '${spec}')`);
12347
12597
  return null;
12348
12598
  }
12349
12599
  const resolution = resolveSymbolSpec(spec, void 0, projectRoot);
12350
12600
  if (resolution.kind === "confined") {
12351
- emitErr4(resolution.message);
12601
+ emitErr(resolution.message);
12352
12602
  return null;
12353
12603
  }
12354
12604
  if (resolution.kind === "ambiguous") {
12355
- emitErr4(
12605
+ emitErr(
12356
12606
  formatAmbiguity(
12357
12607
  resolution.symbol,
12358
12608
  resolution.file,
@@ -12368,11 +12618,11 @@ function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
12368
12618
  const crossFileLead = formatCrossFileLead(commandName, symbol, file, projectRoot);
12369
12619
  if (crossFileLead !== "") messages.push(crossFileLead);
12370
12620
  const resolved = resolveIndexPath(file, projectRoot ?? process.cwd());
12371
- const scanned = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT3 }).map((s) => s.name);
12621
+ const scanned = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT2 }).map((s) => s.name);
12372
12622
  const closes = rankSimilarNames(scanned, symbol);
12373
12623
  if (closes.length > 0) messages.push(didYouMean(closes));
12374
12624
  else if (scanned.length > 0) messages.push(`Try: token-goat outline ${file}`);
12375
- emitErr4(messages.join("\n"));
12625
+ emitErr(messages.join("\n"));
12376
12626
  return null;
12377
12627
  }
12378
12628
  return resolution.entry;
@@ -12387,13 +12637,13 @@ var CONTENT_MAX_ITEMS = 256;
12387
12637
  var CONTENT_MAX_BYTES = 32 * 1024 * 1024;
12388
12638
  var CONTENT_MAX_ITEM_BYTES = CONTENT_MAX_INPUT_CHARS * 7;
12389
12639
  var CONTENT_SUBDIR = "content";
12390
- var TEXT_BYTES_PER_TOKEN = 4;
12391
- var BASE64URL_BYTES_PER_TOKEN = 1.45;
12640
+ var TEXT_BYTES_PER_TOKEN = BYTES_PER_TOKEN.text;
12641
+ var BASE64URL_BYTES_PER_TOKEN = BYTES_PER_TOKEN.dense;
12392
12642
  function estimateTextTokens(bytes) {
12393
- return Math.round(bytes / TEXT_BYTES_PER_TOKEN);
12643
+ return savedTokensFromBytes(bytes, "text");
12394
12644
  }
12395
12645
  function estimateBase64urlTokens(bytes) {
12396
- return Math.round(bytes / BASE64URL_BYTES_PER_TOKEN);
12646
+ return savedTokensFromBytes(bytes, "dense");
12397
12647
  }
12398
12648
  function assertInput(text) {
12399
12649
  if (text.length > CONTENT_MAX_INPUT_CHARS) {
@@ -12504,6 +12754,8 @@ function resolveHandoff(name, opts = {}) {
12504
12754
  export {
12505
12755
  isAvailable2 as isAvailable,
12506
12756
  loadError,
12757
+ emit,
12758
+ emitErr,
12507
12759
  ALL_SYMBOLS_IN_FILE_LIMIT,
12508
12760
  enclosingSymbol,
12509
12761
  runDead,
@@ -12541,6 +12793,9 @@ export {
12541
12793
  didYouMean,
12542
12794
  extraFileArgsNote,
12543
12795
  parseLineRange,
12796
+ parseColonLineSpec,
12797
+ readSpecsMergeable,
12798
+ namedSpecsMergeable,
12544
12799
  runSkeleton,
12545
12800
  runOutline,
12546
12801
  runBrief,