token-goat 2.9.18 → 2.9.19

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 (42) hide show
  1. package/README.md +2 -0
  2. package/dist/{token-goat-chunk-V3ZWBEHM.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
  3. package/dist/{token-goat-chunk-ARNRFR6M.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
  4. package/dist/{token-goat-chunk-YQZE5NS6.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
  5. package/dist/{token-goat-chunk-CTOYJ232.mjs → token-goat-chunk-7DGTVBWP.mjs} +45 -8
  6. package/dist/{token-goat-chunk-5MMJAJF3.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
  7. package/dist/{token-goat-chunk-7VLWNQDQ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
  8. package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
  9. package/dist/{token-goat-chunk-6LGVCCMY.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
  10. package/dist/{token-goat-chunk-UENEL6KX.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
  11. package/dist/{token-goat-chunk-W3E65QOW.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
  12. package/dist/{token-goat-chunk-HIZXQVNU.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
  13. package/dist/{token-goat-chunk-H3FONFBO.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
  14. package/dist/{token-goat-chunk-LA3O4JGD.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
  15. package/dist/{token-goat-chunk-BFEWOMA3.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -352
  16. package/dist/{token-goat-chunk-OWSJJY5R.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
  17. package/dist/{token-goat-chunk-K4DG6EEF.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
  18. package/dist/{token-goat-chunk-WFX2V3OF.mjs → token-goat-chunk-HPMMLV37.mjs} +358 -116
  19. package/dist/{token-goat-chunk-I67CPTDD.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
  20. package/dist/{token-goat-chunk-L7VCX33I.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
  21. package/dist/{token-goat-chunk-EPXNOKIV.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
  22. package/dist/{token-goat-chunk-BWCRC23L.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
  23. package/dist/{token-goat-chunk-52IMXJ2L.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
  24. package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
  25. package/dist/{token-goat-chunk-GRI6HWX3.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
  26. package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
  27. package/dist/{token-goat-chunk-34OUJ2IE.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
  28. package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
  29. package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
  30. package/dist/{token-goat-chunk-QML7ITHJ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
  31. package/dist/{token-goat-chunk-HPOFR6QX.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
  32. package/dist/{token-goat-chunk-CA6V36KW.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
  33. package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
  34. package/dist/{token-goat-chunk-LTYWLKAG.mjs → token-goat-chunk-WKO5TVNA.mjs} +922 -611
  35. package/dist/{token-goat-chunk-QIDLJGXZ.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
  36. package/dist/token-goat-hook.mjs +12 -12
  37. package/dist/token-goat.core.mjs +21 -20
  38. package/docs/cli.md +2 -1
  39. package/docs/security.md +1 -1
  40. package/package.json +1 -1
  41. package/dist/token-goat-chunk-2KTZ6J7T.mjs +0 -173
  42. package/dist/token-goat-chunk-RHHFEXKF.mjs +0 -93
@@ -4,24 +4,24 @@ import {
4
4
  buildEvent,
5
5
  relay,
6
6
  relayInProcess
7
- } from "./token-goat-chunk-WFX2V3OF.mjs";
8
- import "./token-goat-chunk-RHHFEXKF.mjs";
7
+ } from "./token-goat-chunk-HPMMLV37.mjs";
8
+ import "./token-goat-chunk-MU6I4IDI.mjs";
9
9
  import {
10
10
  MAX_STDIN_BYTES,
11
11
  readStdinJson
12
- } from "./token-goat-chunk-V3ZWBEHM.mjs";
13
- import "./token-goat-chunk-BFEWOMA3.mjs";
14
- import "./token-goat-chunk-CTOYJ232.mjs";
15
- import "./token-goat-chunk-HPOFR6QX.mjs";
12
+ } from "./token-goat-chunk-2GTB5RBZ.mjs";
13
+ import "./token-goat-chunk-FQYMZASI.mjs";
14
+ import "./token-goat-chunk-7DGTVBWP.mjs";
15
+ import "./token-goat-chunk-S7RFIX4Y.mjs";
16
16
  import "./token-goat-chunk-3BTK54F3.mjs";
17
- import "./token-goat-chunk-W3E65QOW.mjs";
18
- import "./token-goat-chunk-I67CPTDD.mjs";
19
- import "./token-goat-chunk-L7VCX33I.mjs";
20
- import "./token-goat-chunk-CA6V36KW.mjs";
17
+ import "./token-goat-chunk-EP7TCWHM.mjs";
18
+ import "./token-goat-chunk-IBTFGZBX.mjs";
19
+ import "./token-goat-chunk-JNIFSVWM.mjs";
20
+ import "./token-goat-chunk-SOKAPOOC.mjs";
21
21
  import "./token-goat-chunk-EEIDFMEM.mjs";
22
22
  import "./token-goat-chunk-OSUFN2FV.mjs";
23
- import "./token-goat-chunk-QIDLJGXZ.mjs";
24
- import "./token-goat-chunk-ERTXEKB6.mjs";
23
+ import "./token-goat-chunk-ZGQXRYKE.mjs";
24
+ import "./token-goat-chunk-NMTKNYGF.mjs";
25
25
  import "./token-goat-chunk-Y4AFKTHK.mjs";
26
26
  import "./token-goat-chunk-GMOUBOX4.mjs";
27
27
  import "./token-goat-chunk-RRNZMM3A.mjs";
@@ -11,9 +11,9 @@ import {
11
11
  openReadonlySqlite,
12
12
  runReadOnlySqliteQuery,
13
13
  validateReadOnlySelect
14
- } from "./token-goat-chunk-LMFO66YD.mjs";
14
+ } from "./token-goat-chunk-DA43OE7L.mjs";
15
15
  import "./token-goat-chunk-OSUFN2FV.mjs";
16
- import "./token-goat-chunk-ERTXEKB6.mjs";
16
+ import "./token-goat-chunk-NMTKNYGF.mjs";
17
17
  import "./token-goat-chunk-A37V4PBF.mjs";
18
18
  export {
19
19
  SQLITE_QUERY_ROW_CAP,
@@ -0,0 +1,27 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ DEFAULT_HEARTBEAT_INTERVAL_MS,
5
+ DEFAULT_TIMEOUT_SECONDS,
6
+ MAX_CAPTURE_BYTES,
7
+ run,
8
+ runRaw
9
+ } from "./token-goat-chunk-MU6I4IDI.mjs";
10
+ import "./token-goat-chunk-7DGTVBWP.mjs";
11
+ import "./token-goat-chunk-S7RFIX4Y.mjs";
12
+ import "./token-goat-chunk-IBTFGZBX.mjs";
13
+ import "./token-goat-chunk-JNIFSVWM.mjs";
14
+ import "./token-goat-chunk-SOKAPOOC.mjs";
15
+ import "./token-goat-chunk-EEIDFMEM.mjs";
16
+ import "./token-goat-chunk-OSUFN2FV.mjs";
17
+ import "./token-goat-chunk-ZGQXRYKE.mjs";
18
+ import "./token-goat-chunk-NMTKNYGF.mjs";
19
+ import "./token-goat-chunk-GMOUBOX4.mjs";
20
+ import "./token-goat-chunk-A37V4PBF.mjs";
21
+ export {
22
+ DEFAULT_HEARTBEAT_INTERVAL_MS,
23
+ DEFAULT_TIMEOUT_SECONDS,
24
+ MAX_CAPTURE_BYTES,
25
+ run,
26
+ runRaw
27
+ };
@@ -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-F24NE3FO.mjs";
9
9
  import {
10
10
  formatSqliteQueryTable,
11
11
  formatSqliteSchema,
@@ -13,9 +13,10 @@ 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,
@@ -28,13 +29,14 @@ import {
28
29
  getEmbeddingCoverage,
29
30
  getTrackedFiles,
30
31
  indexedSourceText,
31
- isImagePath,
32
+ isIgnoredIndexPath,
32
33
  isIndexEmptyForProject,
33
34
  isPrivateIpv4Octets,
34
35
  isPrivateIpv6Groups,
35
36
  isVirtualIndexedPath,
36
37
  parseIpv6Groups,
37
38
  probeImageMeta,
39
+ resolveLineRegions,
38
40
  scanAndRecord,
39
41
  searchEvidenceSemantically,
40
42
  shrinkImage,
@@ -42,7 +44,7 @@ import {
42
44
  urlPolicyDenialReason,
43
45
  virtualIndexedScopeNote,
44
46
  walkProject
45
- } from "./token-goat-chunk-BFEWOMA3.mjs";
47
+ } from "./token-goat-chunk-FQYMZASI.mjs";
46
48
  import {
47
49
  colorStdout,
48
50
  isBlobStale,
@@ -52,7 +54,7 @@ import {
52
54
  savedTokensFromBytes,
53
55
  storeBlob,
54
56
  stripAnsi
55
- } from "./token-goat-chunk-HPOFR6QX.mjs";
57
+ } from "./token-goat-chunk-S7RFIX4Y.mjs";
56
58
  import {
57
59
  extractPdfMeta,
58
60
  extractPdfOutline,
@@ -66,7 +68,7 @@ import {
66
68
  parseWhereSpecs,
67
69
  profileCsv,
68
70
  queryCsv
69
- } from "./token-goat-chunk-EPXNOKIV.mjs";
71
+ } from "./token-goat-chunk-KWM6KBPA.mjs";
70
72
  import {
71
73
  MAX_ZIP_INPUT_BYTES,
72
74
  MAX_ZIP_OUTPUT_BYTES,
@@ -101,7 +103,7 @@ import {
101
103
  extractSolidity,
102
104
  extractThrift,
103
105
  stripLeadingAttributes
104
- } from "./token-goat-chunk-GRI6HWX3.mjs";
106
+ } from "./token-goat-chunk-NJKNAC37.mjs";
105
107
  import {
106
108
  MAX_OVER_FETCH,
107
109
  OVER_FETCH_FACTOR,
@@ -109,12 +111,13 @@ import {
109
111
  findContainingSection,
110
112
  indexFileSync,
111
113
  isAvailable,
114
+ isImagePath,
112
115
  isTreeSitterAvailable,
113
116
  listSections,
114
117
  mergeNearbyHits,
115
118
  readSection,
116
119
  searchSemantic
117
- } from "./token-goat-chunk-W3E65QOW.mjs";
120
+ } from "./token-goat-chunk-EP7TCWHM.mjs";
118
121
  import {
119
122
  DEFAULT_QUERY_LIMIT,
120
123
  countRefs,
@@ -127,7 +130,7 @@ import {
127
130
  queryRefsByContext,
128
131
  querySymbols,
129
132
  searchSymbolsFts
130
- } from "./token-goat-chunk-I67CPTDD.mjs";
133
+ } from "./token-goat-chunk-IBTFGZBX.mjs";
131
134
  import {
132
135
  FILENAME_LANGUAGE,
133
136
  TREE_SITTER_LANGUAGES,
@@ -146,7 +149,7 @@ import {
146
149
  partialRefsReason,
147
150
  redactIfDotenv,
148
151
  unsupportedLanguageName
149
- } from "./token-goat-chunk-L7VCX33I.mjs";
152
+ } from "./token-goat-chunk-JNIFSVWM.mjs";
150
153
  import {
151
154
  fileIsAbsent,
152
155
  findProject,
@@ -156,7 +159,7 @@ import {
156
159
  loadConfig,
157
160
  resolveProjectRoot,
158
161
  shortFingerprint
159
- } from "./token-goat-chunk-CA6V36KW.mjs";
162
+ } from "./token-goat-chunk-SOKAPOOC.mjs";
160
163
  import {
161
164
  registerReset
162
165
  } from "./token-goat-chunk-EEIDFMEM.mjs";
@@ -188,7 +191,7 @@ import {
188
191
  supportRequestLine,
189
192
  windowsCmdQuoteArg,
190
193
  withExtension
191
- } from "./token-goat-chunk-QIDLJGXZ.mjs";
194
+ } from "./token-goat-chunk-ZGQXRYKE.mjs";
192
195
  import {
193
196
  UNTRUSTED_FILE_TAG,
194
197
  UNTRUSTED_GITHUB_TAG,
@@ -200,7 +203,7 @@ import {
200
203
  normalizePath,
201
204
  resolveIndexPath,
202
205
  toDisplayPath
203
- } from "./token-goat-chunk-ERTXEKB6.mjs";
206
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
204
207
  import {
205
208
  compileGuardedRegex
206
209
  } from "./token-goat-chunk-GMOUBOX4.mjs";
@@ -2263,6 +2266,11 @@ function runCallers(opts) {
2263
2266
  if (opts.excludeTests === true && suppressed > 0) {
2264
2267
  emit3(`${countNoun(entries.length, "caller")} found (${excludeTestsHiddenNote(suppressed)})`);
2265
2268
  }
2269
+ const overflowed = sqlTruncated || entries.length < filtered.length;
2270
+ if (overflowed) {
2271
+ const totalCallers = sqlTruncated ? countRefs({ name, rootDir }) : filtered.length;
2272
+ emitErr3(`Showing the first ${entries.length} of ${countNoun(totalCallers, "caller")} (raise --limit to see the rest).`);
2273
+ }
2266
2274
  for (const e of entries) {
2267
2275
  const displayPath = toDisplayPath(rootDir, e.file);
2268
2276
  emit3(`${e.caller} ${displayPath}:${e.line}`);
@@ -10314,6 +10322,16 @@ function runListSections(opts) {
10314
10322
  init_define_import_meta_env();
10315
10323
  import * as fs11 from "node:fs";
10316
10324
  import * as path11 from "node:path";
10325
+ function htmlIdSectionFallback(filePath, heading) {
10326
+ if (detectLanguage(filePath) !== "html") return null;
10327
+ healStaleIndex(filePath);
10328
+ const hit = querySymbols({ name: stripHtmlIdSpelling(heading, filePath), filePath, kind: "html_id", limit: 1 })[0];
10329
+ if (hit === void 0) return null;
10330
+ const text = readFileText(filePath);
10331
+ if (text === null) return null;
10332
+ const lines = text.split("\n");
10333
+ return { heading: hit.name, content: lines.slice(hit.lineStart - 1, hit.lineEnd).join("\n"), lineStart: hit.lineStart, lineEnd: hit.lineEnd };
10334
+ }
10317
10335
  var AMBIGUOUS_HEADING_LIMIT = 10;
10318
10336
  function literalHeadingExists(filePath, heading) {
10319
10337
  const ordinalMatch = /^([^#\r\n]+)#(\d+)$/.exec(heading);
@@ -10335,7 +10353,7 @@ function runSection(opts) {
10335
10353
  const multiHeadings = heading.split(",").map((h) => h.trim()).filter((h) => h.length > 0);
10336
10354
  if (multiHeadings.length > 1) return runSectionMulti(specFilePath, filePath, multiHeadings, opts);
10337
10355
  }
10338
- const result = readSection(filePath, heading, readFileText);
10356
+ const result = readSection(filePath, heading, readFileText) ?? htmlIdSectionFallback(filePath, heading);
10339
10357
  if (result === null) {
10340
10358
  if (!fs11.existsSync(filePath)) {
10341
10359
  return { text: `File not found: '${filePath}'`, code: 1 };
@@ -10797,6 +10815,7 @@ function runSymbol(opts) {
10797
10815
  }
10798
10816
  const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
10799
10817
  const excludeTests = opts.excludeTests === true;
10818
+ const excludeVendored = opts.excludeVendored === true;
10800
10819
  const baseConfined = confinedProjectRoot();
10801
10820
  const confinedRoot = opts.projectRoot !== void 0 && baseConfined !== null && isProjectRootAllowed(opts.projectRoot, baseConfined) ? confinedProjectRoot(opts.projectRoot) ?? baseConfined : baseConfined;
10802
10821
  if (baseConfined !== null) {
@@ -10809,13 +10828,13 @@ function runSymbol(opts) {
10809
10828
  }
10810
10829
  }
10811
10830
  const queryOpts = {};
10812
- if (opts.name !== void 0) queryOpts.name = opts.name;
10813
10831
  if (opts.file !== void 0) {
10814
10832
  queryOpts.filePath = resolveIndexPath(opts.file, opts.projectRoot ?? process.cwd());
10815
10833
  healStaleIndex(queryOpts.filePath);
10816
10834
  }
10835
+ if (opts.name !== void 0) queryOpts.name = queryOpts.filePath !== void 0 ? stripHtmlIdSpelling(opts.name, queryOpts.filePath) : opts.name;
10817
10836
  if (opts.kind !== void 0) queryOpts.kind = opts.kind;
10818
- if (matchesGrep !== void 0 || excludeTests) {
10837
+ if (matchesGrep !== void 0 || excludeTests || excludeVendored) {
10819
10838
  queryOpts.limit = FIND_SCAN_LIMIT2;
10820
10839
  } else if (opts.limit !== void 0) {
10821
10840
  queryOpts.limit = opts.limit;
@@ -10831,8 +10850,8 @@ function runSymbol(opts) {
10831
10850
  }
10832
10851
  const preFilterCount = rawResults.length;
10833
10852
  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;
10853
+ const anyClientFilter = matchesGrep !== void 0 || excludeTests || excludeVendored;
10854
+ const filtered = anyClientFilter ? rawResults.filter((s) => (matchesGrep === void 0 || matchesGrep(s.name)) && !(excludeTests && isTestFile(s.filePath)) && !(excludeVendored && isIgnoredIndexPath(s.filePath))) : rawResults;
10836
10855
  const results = anyClientFilter ? filtered.slice(0, effectiveLimit) : filtered;
10837
10856
  const hiddenByExcludeTests = excludeTests ? rawResults.filter((s) => (matchesGrep === void 0 || matchesGrep(s.name)) && isTestFile(s.filePath)).length : 0;
10838
10857
  if (excludeTests && filtered.length === 0 && hiddenByExcludeTests > 0) {
@@ -10957,6 +10976,8 @@ function truncationNotice(shown, limit, total, plural, flag) {
10957
10976
  function runRead(opts) {
10958
10977
  const range = parseLineRange(opts.spec);
10959
10978
  if (range !== null) return runLineRange(range, opts);
10979
+ const region = parseColonLineSpec(opts.spec);
10980
+ if (region !== null) return runLineRegion(region, opts);
10960
10981
  const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
10961
10982
  if (crossFilePairs !== null) return runReadMulti(crossFilePairs, opts);
10962
10983
  const { file, symbol } = parseReadSpec(opts.spec);
@@ -11046,8 +11067,7 @@ Query subtree: token-goat json-query ${file} '<path>'`);
11046
11067
  {
11047
11068
  ...match,
11048
11069
  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.
11070
+ // 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
11071
  ...fileIsGone(match.filePath) ? { deleted: true } : {},
11052
11072
  ...refCounts !== void 0 ? { refCount: refCounts.get(match.name) ?? 0 } : {}
11053
11073
  }
@@ -11063,7 +11083,9 @@ Query subtree: token-goat json-query ${file} '<path>'`);
11063
11083
  body
11064
11084
  ];
11065
11085
  const warning = staleWarning(match.filePath);
11066
- const text = guardText(warning + trimBlankLines(lines).join("\n"), "symbol");
11086
+ const narrowerSliceHint = bodyLen > LARGE_SYMBOL_LINE_THRESHOLD ? `
11087
+ # for a narrower slice: token-goat grep "<pattern>" ${file} -C 15 --symbol` : "";
11088
+ const text = guardText(warning + trimBlankLines(lines).join("\n"), "symbol") + narrowerSliceHint;
11067
11089
  if (opts.suppressStat !== true) recordReadStat("read_replacement", fullSourceBytes, text, opts.spec);
11068
11090
  return { text, code: 0 };
11069
11091
  }
@@ -12142,7 +12164,7 @@ function parseMultiFileSpec(spec) {
12142
12164
  function extraFileArgsNote(command, first, extras, opts = {}) {
12143
12165
  const noun = opts.noun ?? "file";
12144
12166
  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.`;
12167
+ if (opts.mergeable === false) return `${head} Run ${command} once per ${noun}.`;
12146
12168
  return `${head} ${command} reads one ${noun}, or a comma-separated list: token-goat ${command} "${[first, ...extras].join(",")}"`;
12147
12169
  }
12148
12170
  function parseLineRange(spec) {
@@ -12154,6 +12176,22 @@ function parseLineRange(spec) {
12154
12176
  const end = m[3] !== void 0 ? parseInt(m[3], 10) : start;
12155
12177
  return { file: m[1], start, end };
12156
12178
  }
12179
+ function parseColonLineSpec(spec) {
12180
+ const colonIdx = spec.lastIndexOf(":");
12181
+ if (colonIdx <= 0) return null;
12182
+ const suffix = spec.slice(colonIdx + 1);
12183
+ if (!/^\d+(?:-\d+)?$/.test(suffix)) return null;
12184
+ const file = spec.slice(0, colonIdx);
12185
+ if (file === "") return null;
12186
+ if (/^[A-Za-z]$/.test(file)) return null;
12187
+ const pastDrive = /^[A-Za-z]:/.test(file) ? file.slice(2) : file;
12188
+ if (pastDrive === "" || pastDrive.includes(":")) return null;
12189
+ if (fileExists3(spec)) return null;
12190
+ const dash = suffix.indexOf("-");
12191
+ const start = parseInt(dash === -1 ? suffix : suffix.slice(0, dash), 10);
12192
+ const end = dash === -1 ? start : parseInt(suffix.slice(dash + 1), 10);
12193
+ return { file, start, end };
12194
+ }
12157
12195
  function parseColonLineRange(symbol) {
12158
12196
  const m = /^(\d+)(?:[-:,](\d+))?$/.exec(symbol);
12159
12197
  if (m === null) return null;
@@ -12161,6 +12199,19 @@ function parseColonLineRange(symbol) {
12161
12199
  const end = m[2] !== void 0 ? parseInt(m[2], 10) : start;
12162
12200
  return { start, end };
12163
12201
  }
12202
+ function readSpecsMergeable(specs) {
12203
+ if (specs.length < 2) return true;
12204
+ const joined = specs.join(",");
12205
+ if (parseLineRange(joined) !== null) return false;
12206
+ if (parseColonLineSpec(joined) !== null) return false;
12207
+ if (parseCrossFileMultiSpec(joined) !== null) return true;
12208
+ const { symbol } = parseReadSpec(joined);
12209
+ return symbol !== void 0 && symbol !== "" && symbol.includes(",") && parseColonLineRange(symbol) === null;
12210
+ }
12211
+ function namedSpecsMergeable(specs) {
12212
+ if (specs.length < 2) return true;
12213
+ return specs.every((spec) => spec.includes("::"));
12214
+ }
12164
12215
  function runLineRange(range, opts) {
12165
12216
  const { file, start, end } = range;
12166
12217
  const diskPath = resolveAgainstProjectRoot(file, opts.projectRoot);
@@ -12193,6 +12244,64 @@ function runLineRange(range, opts) {
12193
12244
  code: 0
12194
12245
  };
12195
12246
  }
12247
+ function runLineRegion(range, opts) {
12248
+ const { file, start, end } = range;
12249
+ if (start < 1) return { text: `Invalid line range: start must be >= 1 (got ${start})`, code: 1 };
12250
+ if (end < start) return { text: `Invalid line range: end (${end}) is before start (${start})`, code: 1 };
12251
+ const resolved = resolveIndexPath(file, opts.projectRoot ?? process.cwd());
12252
+ const confined = confinementRefusal("This file", resolved, confinedProjectRoot(opts.projectRoot));
12253
+ if (confined !== null) return { text: confined, code: 1 };
12254
+ const text = readFileText(resolveAgainstProjectRoot(file, opts.projectRoot));
12255
+ if (text === null) return { text: `Could not read: ${file}`, code: 1 };
12256
+ const allLines = text.split(/\r?\n/);
12257
+ if (allLines.length > 1 && allLines[allLines.length - 1] === "") allLines.pop();
12258
+ if (start > allLines.length) {
12259
+ return { text: `Line ${start} is past end of file (${countNoun(allLines.length, "line")}): ${file}`, code: 1 };
12260
+ }
12261
+ if (opts.forceRefresh === true) {
12262
+ indexFileSyncPinned(resolved, globalDbPath());
12263
+ enqueueDirtyPathSafe(resolved, { alreadyResolved: true });
12264
+ } else {
12265
+ healStaleIndex(resolved);
12266
+ }
12267
+ const symbols = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT3 });
12268
+ const regions = resolveLineRegions(symbols, allLines.length, start, end);
12269
+ const asked = start === end ? `${start}` : `${start}-${end}`;
12270
+ if (regions.length === 0) {
12271
+ return {
12272
+ text: `No indexed symbols in '${file}', so line ${asked} cannot be resolved to a region.
12273
+ Read the raw lines instead: token-goat read "${file}@${asked}"`,
12274
+ code: 1
12275
+ };
12276
+ }
12277
+ const slice = (r) => allLines.slice(r.start - 1, Math.min(r.end, allLines.length)).join("\n");
12278
+ if (opts.json === true) {
12279
+ return {
12280
+ text: displaySafeJson({
12281
+ file,
12282
+ requested: { start, end },
12283
+ regions: regions.map((r) => ({
12284
+ kind: r.kind,
12285
+ label: r.label,
12286
+ start: r.start,
12287
+ end: Math.min(r.end, allLines.length),
12288
+ lines: allLines.slice(r.start - 1, Math.min(r.end, allLines.length))
12289
+ }))
12290
+ }),
12291
+ code: 0
12292
+ };
12293
+ }
12294
+ const blocks = [];
12295
+ if (regions.length > 1) blocks.push(`# ${file}:${asked} -> ${countNoun(regions.length, "region")}`);
12296
+ regions.forEach((r, i) => {
12297
+ const body = slice(r);
12298
+ const tag = regions.length > 1 ? `[${i + 1}/${regions.length}] ` : `${file}:${asked} -> `;
12299
+ const span = `lines ${r.start}-${Math.min(r.end, allLines.length)} of ${allLines.length}`;
12300
+ blocks.push(`# ${tag}${r.label} ${span} (~${Math.ceil(body.length / 4)} tok)
12301
+ ${body}`);
12302
+ });
12303
+ return { text: guardText(staleWarning(resolved) + blocks.join("\n\n"), "lines"), code: 0 };
12304
+ }
12196
12305
  function findParentName(entry, fileSymbols) {
12197
12306
  let best = null;
12198
12307
  for (const s of fileSymbols) {
@@ -12267,13 +12376,17 @@ function fileConfinementRefusal(label, file, projectRoot) {
12267
12376
  if (root === null) return null;
12268
12377
  return confinementRefusal(label, resolveIndexPath(file, projectRoot ?? process.cwd()), root);
12269
12378
  }
12379
+ function stripHtmlIdSpelling(name, filePath) {
12380
+ return name.startsWith("#") && detectLanguage(filePath) === "html" ? name.slice(1) : name;
12381
+ }
12270
12382
  function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
12271
12383
  const { file, symbol: rawSymbol } = parseReadSpec(spec);
12272
12384
  if (rawSymbol === void 0 || rawSymbol === "") return { kind: "none" };
12273
12385
  const anchorMatch = /^(.+)@(\d+)$/.exec(rawSymbol);
12274
- const symbol = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
12386
+ const anchorSymbol = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
12275
12387
  const lineAnchor = anchorMatch !== null ? parseInt(anchorMatch[2], 10) : void 0;
12276
12388
  const resolved = resolveIndexPath(file, projectRoot ?? process.cwd());
12389
+ const symbol = stripHtmlIdSpelling(anchorSymbol, resolved);
12277
12390
  const confined = confinementRefusal("This file", resolved, confinedProjectRoot(projectRoot));
12278
12391
  if (confined !== null) return { kind: "confined", message: confined };
12279
12392
  if (forceRefresh === true) {
@@ -12541,6 +12654,9 @@ export {
12541
12654
  didYouMean,
12542
12655
  extraFileArgsNote,
12543
12656
  parseLineRange,
12657
+ parseColonLineSpec,
12658
+ readSpecsMergeable,
12659
+ namedSpecsMergeable,
12544
12660
  runSkeleton,
12545
12661
  runOutline,
12546
12662
  runBrief,