token-goat 2.8.3 → 2.8.6
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.
- package/README.md +23 -17
- package/dist/{token-goat-chunk-6ODZ6PZK.mjs → token-goat-chunk-2JZ66BBE.mjs} +216 -72
- package/dist/{token-goat-chunk-PWVXXPCC.mjs → token-goat-chunk-DK4VLLYB.mjs} +563 -155
- package/dist/{token-goat-chunk-EFF2XCLB.mjs → token-goat-chunk-GUNYAGOZ.mjs} +175 -56
- package/dist/{token-goat-chunk-VAGPOZHO.mjs → token-goat-chunk-L2XHDICZ.mjs} +2 -2
- package/dist/{token-goat-chunk-DP3NNGGV.mjs → token-goat-chunk-LJEETTER.mjs} +5 -5
- package/dist/{token-goat-chunk-NKNCHJ4H.mjs → token-goat-chunk-MA5237JN.mjs} +327 -169
- package/dist/{token-goat-chunk-4OM2Q2SX.mjs → token-goat-chunk-SHN4UTL4.mjs} +5 -5
- package/dist/{token-goat-chunk-4HIMCBYK.mjs → token-goat-chunk-TELKICYU.mjs} +55 -6
- package/dist/{token-goat-chunk-222VPFP2.mjs → token-goat-chunk-U7M2LTGP.mjs} +1100 -269
- package/dist/{token-goat-chunk-TX4JFJTD.mjs → token-goat-chunk-XAWIELXH.mjs} +216 -105
- package/dist/{token-goat-chunk-C5IL6MJH.mjs → token-goat-chunk-ZIPBLIUZ.mjs} +6 -5
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +3 -1
|
@@ -23,9 +23,11 @@ import {
|
|
|
23
23
|
extractPdfMeta,
|
|
24
24
|
extractPdfOutline,
|
|
25
25
|
extractPdfText,
|
|
26
|
+
fenceUntrusted,
|
|
26
27
|
fenceUntrustedContent,
|
|
27
28
|
formatCsvProfile,
|
|
28
29
|
formatCsvTable,
|
|
30
|
+
getEmbeddingCoverage,
|
|
29
31
|
getFileEntry,
|
|
30
32
|
indexFileSync,
|
|
31
33
|
isAvailable,
|
|
@@ -39,6 +41,7 @@ import {
|
|
|
39
41
|
locatePdfPages,
|
|
40
42
|
mergeNearbyHits,
|
|
41
43
|
ocrImage,
|
|
44
|
+
ocrIntegrityFailed,
|
|
42
45
|
parseWhereSpecs,
|
|
43
46
|
pathEqClause,
|
|
44
47
|
probeImageMeta,
|
|
@@ -48,13 +51,14 @@ import {
|
|
|
48
51
|
queryRefs,
|
|
49
52
|
queryRefsByContext,
|
|
50
53
|
querySymbols,
|
|
51
|
-
|
|
54
|
+
scanAndRecord,
|
|
52
55
|
searchEvidenceSemantically,
|
|
53
56
|
searchSemantic,
|
|
54
57
|
searchSymbolsFts,
|
|
55
58
|
shrinkImage,
|
|
56
59
|
storeBlob,
|
|
57
60
|
stripLeadingAttributes,
|
|
61
|
+
stripTomlComment,
|
|
58
62
|
tomlBracketDelta,
|
|
59
63
|
trimToBudget,
|
|
60
64
|
unzipBounded,
|
|
@@ -62,7 +66,7 @@ import {
|
|
|
62
66
|
walkProject,
|
|
63
67
|
yamlLineClosesQuote,
|
|
64
68
|
yamlOpenQuoteAfter
|
|
65
|
-
} from "./token-goat-chunk-
|
|
69
|
+
} from "./token-goat-chunk-DK4VLLYB.mjs";
|
|
66
70
|
import {
|
|
67
71
|
Database,
|
|
68
72
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
@@ -82,6 +86,7 @@ import {
|
|
|
82
86
|
excludeTestsHiddenNote,
|
|
83
87
|
extractErrorMessage,
|
|
84
88
|
fileIsAbsent,
|
|
89
|
+
filtersFilteredToEmptyNotice,
|
|
85
90
|
findHtmlHeadingMatches,
|
|
86
91
|
findProject,
|
|
87
92
|
fingerprintContent,
|
|
@@ -107,6 +112,7 @@ import {
|
|
|
107
112
|
resolveIndexPath,
|
|
108
113
|
resolveProjectRoot,
|
|
109
114
|
runGit,
|
|
115
|
+
savedTokensFromBytes,
|
|
110
116
|
shortFingerprint,
|
|
111
117
|
stripAnsi,
|
|
112
118
|
stripBom,
|
|
@@ -114,7 +120,7 @@ import {
|
|
|
114
120
|
unsupportedLanguageName,
|
|
115
121
|
windowsCmdQuoteArg,
|
|
116
122
|
withExtension
|
|
117
|
-
} from "./token-goat-chunk-
|
|
123
|
+
} from "./token-goat-chunk-2JZ66BBE.mjs";
|
|
118
124
|
import {
|
|
119
125
|
registerReset
|
|
120
126
|
} from "./token-goat-chunk-AO2QD2AG.mjs";
|
|
@@ -198,18 +204,19 @@ function findTableHeaders(lines, isToml) {
|
|
|
198
204
|
const restStart = closeIdx + openDelim.length;
|
|
199
205
|
const m2 = TABLE_HEADER_RE.exec(line.slice(restStart));
|
|
200
206
|
if (m2 !== null && m2[1] !== void 0) headers.push({ heading: m2[1].trim(), level: 1, index: i });
|
|
201
|
-
openDelim = lineOpenDelimiterAfter(line,
|
|
207
|
+
openDelim = lineOpenDelimiterAfter(stripTomlComment(line.slice(restStart)), 0);
|
|
202
208
|
continue;
|
|
203
209
|
}
|
|
204
210
|
if (isToml && arrayDepth > 0) {
|
|
205
|
-
arrayDepth = Math.max(0, arrayDepth + tomlBracketDelta(line));
|
|
211
|
+
arrayDepth = Math.max(0, arrayDepth + tomlBracketDelta(stripTomlComment(line)));
|
|
206
212
|
continue;
|
|
207
213
|
}
|
|
208
214
|
const m = TABLE_HEADER_RE.exec(line);
|
|
209
215
|
if (m !== null && m[1] !== void 0) headers.push({ heading: m[1].trim(), level: 1, index: i });
|
|
210
216
|
if (isToml) {
|
|
211
|
-
|
|
212
|
-
|
|
217
|
+
const code = stripTomlComment(line);
|
|
218
|
+
openDelim = lineOpenDelimiterAfter(code, 0);
|
|
219
|
+
if (openDelim === null) arrayDepth = Math.max(0, tomlBracketDelta(code));
|
|
213
220
|
}
|
|
214
221
|
}
|
|
215
222
|
return headers;
|
|
@@ -255,10 +262,11 @@ function findKeyValueHeaders(lines, language) {
|
|
|
255
262
|
if (closed) openQuote = null;
|
|
256
263
|
continue;
|
|
257
264
|
}
|
|
258
|
-
const
|
|
265
|
+
const scanned = isEnv ? line.replace(/^[ \t]+/, "") : line;
|
|
266
|
+
const m = (isEnv ? ENV_KEYVALUE_HEADER_RE : KEYVALUE_HEADER_RE).exec(scanned);
|
|
259
267
|
if (m === null || m[1] === void 0) continue;
|
|
260
268
|
headers.push({ heading: m[1], level: 1, index: i });
|
|
261
|
-
openQuote = isEnv ? _detectOpenQuote(
|
|
269
|
+
openQuote = isEnv ? _detectOpenQuote(scanned.slice(m[0].length)) : yamlOpenQuoteAfter(line, m[0].length);
|
|
262
270
|
}
|
|
263
271
|
return headers;
|
|
264
272
|
}
|
|
@@ -5347,15 +5355,12 @@ function guardText(text, command) {
|
|
|
5347
5355
|
const cfg = loadConfig();
|
|
5348
5356
|
return cfg.overflow_guard.enabled ? trimToBudget(text, cfg.overflow_guard.max_tokens, command) : text;
|
|
5349
5357
|
}
|
|
5350
|
-
function
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
matches = [];
|
|
5356
|
-
}
|
|
5358
|
+
function fenceGithubText(text) {
|
|
5359
|
+
return fenceUntrusted(text, UNTRUSTED_GITHUB_TAG);
|
|
5360
|
+
}
|
|
5361
|
+
function fenceGithubFieldIfMatched(text) {
|
|
5362
|
+
const matches = scanAndRecord(text);
|
|
5357
5363
|
if (matches.length === 0) return text;
|
|
5358
|
-
recordStat("injection_detected", 0, 0, void 0, matches.join(","));
|
|
5359
5364
|
return fenceUntrustedContent(text, matches, UNTRUSTED_GITHUB_TAG);
|
|
5360
5365
|
}
|
|
5361
5366
|
function guardJsonRows(items) {
|
|
@@ -5376,7 +5381,7 @@ function sumFileSizes(filePaths) {
|
|
|
5376
5381
|
function recordReadStat(kind, fullSourceBytes, emittedText, detail) {
|
|
5377
5382
|
const emittedBytes = Buffer.byteLength(emittedText, "utf8");
|
|
5378
5383
|
const bytesSaved = Math.max(1, fullSourceBytes - emittedBytes);
|
|
5379
|
-
recordStat(kind, bytesSaved,
|
|
5384
|
+
recordStat(kind, bytesSaved, savedTokensFromBytes(bytesSaved), void 0, detail);
|
|
5380
5385
|
}
|
|
5381
5386
|
function findSpecSeparator(spec) {
|
|
5382
5387
|
return spec.lastIndexOf("::");
|
|
@@ -5659,10 +5664,21 @@ function runSymbol(opts) {
|
|
|
5659
5664
|
if (opts.name !== void 0 && emptyIndexRoot !== null) {
|
|
5660
5665
|
const rootDir = emptyIndexRoot;
|
|
5661
5666
|
const rawSymbols = querySymbols({ limit: FIND_SCAN_LIMIT, rootDir });
|
|
5662
|
-
const
|
|
5663
|
-
|
|
5667
|
+
const exactMatches = rawSymbols.filter((s) => s.name === opts.name);
|
|
5668
|
+
if (exactMatches.length > 0) {
|
|
5669
|
+
const shown = exactMatches.slice(0, DIDYOUMEAN_LIMIT);
|
|
5670
|
+
const where = shown.map((s) => `${s.kind} at ${toDisplayPath(rootDir, s.filePath)}:${s.lineStart}`).join("; ");
|
|
5671
|
+
const more = exactMatches.length > shown.length ? ` (+${exactMatches.length - shown.length} more)` : "";
|
|
5672
|
+
const flags = [opts.kind !== void 0 ? "--kind" : null, opts.file !== void 0 ? "--file" : null].filter((f) => f !== null);
|
|
5673
|
+
const widen = flags.length > 0 ? `drop ${flags.join("/")} to see it` : "widen the search scope to see it";
|
|
5674
|
+
text2 += `
|
|
5675
|
+
'${opts.name}' IS indexed (${where}${more}) -- ${widen}`;
|
|
5676
|
+
} else {
|
|
5677
|
+
const candidates = rankSimilarNames(rawSymbols.map((s) => s.name), opts.name);
|
|
5678
|
+
text2 += candidates.length > 0 ? `
|
|
5664
5679
|
${didYouMean(candidates)}` : indexEmpty ? "" : `
|
|
5665
5680
|
Try: token-goat semantic "${opts.name}"`;
|
|
5681
|
+
}
|
|
5666
5682
|
const structuredFiles = [...new Set(rawSymbols.map((s) => s.filePath))].sort();
|
|
5667
5683
|
const hit = findStructuredKeyPath(opts.name, structuredFiles);
|
|
5668
5684
|
if (hit !== null) {
|
|
@@ -5936,9 +5952,11 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
|
5936
5952
|
let candidates = querySymbols({ name: lookupName, filePath: resolved, limit: 10 });
|
|
5937
5953
|
if (candidates.length === 0) {
|
|
5938
5954
|
const foldedFile = foldPath(file);
|
|
5955
|
+
const baseName = file.slice(Math.max(file.lastIndexOf("/"), file.lastIndexOf("\\")) + 1);
|
|
5939
5956
|
candidates = querySymbols({
|
|
5940
5957
|
name: lookupName,
|
|
5941
5958
|
limit: 50,
|
|
5959
|
+
...baseName !== "" ? { fileBaseName: baseName } : {},
|
|
5942
5960
|
...projectRoot !== void 0 ? { rootDir: projectRoot } : {}
|
|
5943
5961
|
}).filter((s) => {
|
|
5944
5962
|
const foldedFilePath = foldPath(s.filePath);
|
|
@@ -6072,6 +6090,12 @@ ${sub.text}`);
|
|
|
6072
6090
|
function resolveAgainstProjectRoot(file, projectRoot) {
|
|
6073
6091
|
return projectRoot !== void 0 && !path5.isAbsolute(file) ? path5.resolve(projectRoot, file) : file;
|
|
6074
6092
|
}
|
|
6093
|
+
function literalHeadingExists(filePath, heading) {
|
|
6094
|
+
const ordinalMatch = /^([^#\r\n]+)#(\d+)$/.exec(heading);
|
|
6095
|
+
const base = (ordinalMatch?.[1] ?? heading).trim().toLowerCase();
|
|
6096
|
+
if (base.length === 0) return false;
|
|
6097
|
+
return listSections(filePath, readFileText).some((h) => h.trim().toLowerCase() === base);
|
|
6098
|
+
}
|
|
6075
6099
|
function runSection(opts) {
|
|
6076
6100
|
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
6077
6101
|
if (crossFilePairs !== null) return runSectionCrossFile(crossFilePairs, opts);
|
|
@@ -6082,7 +6106,7 @@ function runSection(opts) {
|
|
|
6082
6106
|
const specFilePath = opts.spec.slice(0, colonIdx);
|
|
6083
6107
|
const filePath = resolveAgainstProjectRoot(specFilePath, opts.projectRoot);
|
|
6084
6108
|
const heading = opts.spec.slice(colonIdx + 2);
|
|
6085
|
-
if (heading.includes(",")) {
|
|
6109
|
+
if (heading.includes(",") && !literalHeadingExists(filePath, heading)) {
|
|
6086
6110
|
const multiHeadings = heading.split(",").map((h) => h.trim()).filter((h) => h.length > 0);
|
|
6087
6111
|
if (multiHeadings.length > 1) return runSectionMulti(specFilePath, filePath, multiHeadings, opts);
|
|
6088
6112
|
}
|
|
@@ -6516,14 +6540,10 @@ function formatStatsSuffix(refCounts, sym) {
|
|
|
6516
6540
|
return refCounts !== void 0 ? ` [${countNoun(refCounts.get(sym.name) ?? 0, "ref")}, ${hasRealDocstring(sym.docstring) ? "documented" : "undocumented"}]` : "";
|
|
6517
6541
|
}
|
|
6518
6542
|
function filteredToEmptyNotice(preFilterCount, minLines, grep) {
|
|
6519
|
-
const plural = preFilterCount === 1 ? "symbol" : "symbols";
|
|
6520
6543
|
const parts = [];
|
|
6521
6544
|
if (minLines !== void 0) parts.push(`--min-lines ${minLines}`);
|
|
6522
6545
|
if (grep !== void 0) parts.push(`--grep ${grep}`);
|
|
6523
|
-
|
|
6524
|
-
const knob = parts.length > 1 ? "filters" : "filter";
|
|
6525
|
-
const verb = preFilterCount === 1 ? "was" : "were";
|
|
6526
|
-
return ` (all ${preFilterCount} indexed ${plural} ${verb} filtered out by ${cause}; the file is indexed -- widen or drop the ${knob} to see them)`;
|
|
6546
|
+
return filtersFilteredToEmptyNotice(preFilterCount, parts, "indexed symbol", "indexed symbols", "the file is indexed");
|
|
6527
6547
|
}
|
|
6528
6548
|
function prepareSymbolListing(file, opts) {
|
|
6529
6549
|
const resolved = resolveIndexPath(file, opts.projectRoot ?? process.cwd());
|
|
@@ -6632,6 +6652,12 @@ function runSkeleton(opts) {
|
|
|
6632
6652
|
recordReadStat("stub_view", fullSourceBytes, text, opts.file);
|
|
6633
6653
|
return { text, code: 0 };
|
|
6634
6654
|
}
|
|
6655
|
+
var DOC_SUMMARY_MAX_CHARS = 140;
|
|
6656
|
+
function clipDocSummary(firstLine) {
|
|
6657
|
+
if (firstLine.length <= DOC_SUMMARY_MAX_CHARS) return firstLine;
|
|
6658
|
+
const cut = firstLine.lastIndexOf(" ", DOC_SUMMARY_MAX_CHARS);
|
|
6659
|
+
return `${firstLine.slice(0, cut > 40 ? cut : DOC_SUMMARY_MAX_CHARS).trimEnd()}\u2026`;
|
|
6660
|
+
}
|
|
6635
6661
|
function runOutline(opts) {
|
|
6636
6662
|
const multiFiles = parseMultiFileSpec(opts.file);
|
|
6637
6663
|
if (multiFiles !== null) return runPerFileListing(multiFiles, (file) => runOutline({ ...opts, file, includeFilePath: true }), opts.json === true);
|
|
@@ -6669,7 +6695,7 @@ function runOutline(opts) {
|
|
|
6669
6695
|
const rangeStr = `${sym.lineStart.toString().padStart(4)}-${sym.lineEnd.toString().padEnd(6)}`;
|
|
6670
6696
|
const kindStr = sym.kind.padEnd(14);
|
|
6671
6697
|
const bodyLen = sym.lineEnd - sym.lineStart + 1;
|
|
6672
|
-
const docFirst = hasRealDocstring(sym.docstring) ? ` # ${sym.docstring.split("\n")[0] ?? ""}` : "";
|
|
6698
|
+
const docFirst = hasRealDocstring(sym.docstring) ? ` # ${clipDocSummary(sym.docstring.split("\n")[0] ?? "")}` : "";
|
|
6673
6699
|
const statsStr = formatStatsSuffix(refCounts, sym);
|
|
6674
6700
|
lines.push(` ${rangeStr} ${kindStr} ${sym.name} (${bodyLen}\u2113)${docFirst}${statsStr}`);
|
|
6675
6701
|
}
|
|
@@ -6709,11 +6735,11 @@ function runCsvQuery(opts) {
|
|
|
6709
6735
|
const rowsJson = result.rows.map((r) => Object.fromEntries(result.header.map((h, i) => [h, r[i]])));
|
|
6710
6736
|
const headTruncated = result.rows.length < result.totalRows;
|
|
6711
6737
|
const capped = guardJsonRows(rowsJson);
|
|
6712
|
-
const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount: result.totalRows });
|
|
6738
|
+
const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount: result.totalRows, ...result.totalRows === 0 && result.preFilterRows > 0 ? { filteredFromRows: result.preFilterRows } : {} });
|
|
6713
6739
|
emit(jsonText);
|
|
6714
6740
|
recordReadStat("csv_query", fullSourceBytes, jsonText, opts.file);
|
|
6715
6741
|
} else {
|
|
6716
|
-
const tableText = formatCsvTable(result);
|
|
6742
|
+
const tableText = formatCsvTable(result, (opts.where ?? []).map((w) => `--where ${w}`));
|
|
6717
6743
|
emit(tableText);
|
|
6718
6744
|
recordReadStat("csv_query", fullSourceBytes, tableText, opts.file);
|
|
6719
6745
|
}
|
|
@@ -6856,6 +6882,7 @@ function resolveYamlMergeKeys(node) {
|
|
|
6856
6882
|
}
|
|
6857
6883
|
function parseYamlDocument(text) {
|
|
6858
6884
|
const docs = loadAll(text).map(resolveYamlMergeKeys);
|
|
6885
|
+
if (docs.length === 0) return null;
|
|
6859
6886
|
return docs.length === 1 ? docs[0] : docs;
|
|
6860
6887
|
}
|
|
6861
6888
|
function runYamlOutline(opts) {
|
|
@@ -7189,11 +7216,11 @@ function runPrSlice(opts) {
|
|
|
7189
7216
|
}
|
|
7190
7217
|
const fullSourceBytes = Buffer.byteLength(diffText, "utf8");
|
|
7191
7218
|
if (opts.json === true) {
|
|
7192
|
-
const jsonText = JSON.stringify({ path: parsed.path, diff:
|
|
7219
|
+
const jsonText = JSON.stringify({ path: parsed.path, diff: fenceGithubFieldIfMatched(fileDiff) });
|
|
7193
7220
|
emit(jsonText);
|
|
7194
7221
|
recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} diff:${parsed.path}`);
|
|
7195
7222
|
} else {
|
|
7196
|
-
emitGuarded(
|
|
7223
|
+
emitGuarded(fenceGithubText(fileDiff), "pr-slice");
|
|
7197
7224
|
recordReadStat("pr_slice", fullSourceBytes, fileDiff, `${repo}#${opts.pr} diff:${parsed.path}`);
|
|
7198
7225
|
}
|
|
7199
7226
|
return 0;
|
|
@@ -7202,14 +7229,14 @@ function runPrSlice(opts) {
|
|
|
7202
7229
|
const comments = fetchPrComments(opts.pr, repo);
|
|
7203
7230
|
const fullSourceBytes = Buffer.byteLength(JSON.stringify(comments), "utf8");
|
|
7204
7231
|
if (opts.json === true) {
|
|
7205
|
-
const fencedComments = comments.map((c) => ({ ...c, body:
|
|
7232
|
+
const fencedComments = comments.map((c) => ({ ...c, body: fenceGithubFieldIfMatched(c.body) }));
|
|
7206
7233
|
const capped = guardJsonRows(fencedComments);
|
|
7207
7234
|
const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated, totalCount: capped.totalCount });
|
|
7208
7235
|
emit(jsonText);
|
|
7209
7236
|
recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} comments`);
|
|
7210
7237
|
} else {
|
|
7211
7238
|
const text = formatCommentsSlice(comments);
|
|
7212
|
-
emitGuarded(
|
|
7239
|
+
emitGuarded(fenceGithubText(text), "pr-slice");
|
|
7213
7240
|
recordReadStat("pr_slice", fullSourceBytes, text, `${repo}#${opts.pr} comments`);
|
|
7214
7241
|
}
|
|
7215
7242
|
return 0;
|
|
@@ -7220,15 +7247,15 @@ function runPrSlice(opts) {
|
|
|
7220
7247
|
if (opts.json === true) {
|
|
7221
7248
|
const fencedDesc = {
|
|
7222
7249
|
...desc,
|
|
7223
|
-
title:
|
|
7224
|
-
body: desc.body !== null ?
|
|
7250
|
+
title: fenceGithubFieldIfMatched(desc.title),
|
|
7251
|
+
body: desc.body !== null ? fenceGithubFieldIfMatched(desc.body) : null
|
|
7225
7252
|
};
|
|
7226
7253
|
const jsonText = JSON.stringify(fencedDesc);
|
|
7227
7254
|
emit(jsonText);
|
|
7228
7255
|
recordReadStat("pr_slice", fullSourceBytes, jsonText, `${repo}#${opts.pr} description`);
|
|
7229
7256
|
} else {
|
|
7230
7257
|
const text = formatDescriptionSlice(desc);
|
|
7231
|
-
emitGuarded(
|
|
7258
|
+
emitGuarded(fenceGithubText(text), "pr-slice");
|
|
7232
7259
|
recordReadStat("pr_slice", fullSourceBytes, text, `${repo}#${opts.pr} description`);
|
|
7233
7260
|
}
|
|
7234
7261
|
return 0;
|
|
@@ -7439,6 +7466,9 @@ async function runImageText(file) {
|
|
|
7439
7466
|
const data = fs5.readFileSync(file);
|
|
7440
7467
|
const ocr = await ocrImage(data);
|
|
7441
7468
|
if (ocr === null) {
|
|
7469
|
+
if (ocrIntegrityFailed()) {
|
|
7470
|
+
throw new Error(`${file} was not OCRed: the cached language model failed its checksum and was discarded, so the next run re-downloads it`);
|
|
7471
|
+
}
|
|
7442
7472
|
if (isOcrEngineAvailable()) {
|
|
7443
7473
|
throw new Error(`${file} could not be processed by OCR (unreadable image, timeout, or offline model fetch)`);
|
|
7444
7474
|
}
|
|
@@ -7489,15 +7519,14 @@ ${emptyIndexMessage(rootDir2)}`, code: 1 };
|
|
|
7489
7519
|
const match = resolution.entry;
|
|
7490
7520
|
const rootDir = resolveProjectRoot({ project: opts.projectRoot ?? process.cwd() });
|
|
7491
7521
|
const excludeTests = opts.excludeTests === true;
|
|
7492
|
-
const
|
|
7493
|
-
const allCallers = resolveCallers(match.name, void 0, match.filePath, rootDir, unboundedQuery);
|
|
7522
|
+
const allCallers = resolveCallers(match.name, void 0, match.filePath, rootDir, true);
|
|
7494
7523
|
const testFiltered = excludeTests ? allCallers.filter((c) => !isTestFile(c.file)) : allCallers;
|
|
7495
7524
|
const hiddenByExcludeTests = excludeTests ? allCallers.length - testFiltered.length : 0;
|
|
7496
7525
|
const preGrepCount = testFiltered.length;
|
|
7497
7526
|
const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
|
|
7498
7527
|
const callers = matchesGrep !== void 0 ? testFiltered.filter((c) => matchesGrep(c.caller)) : testFiltered;
|
|
7499
7528
|
const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - callers.length : 0;
|
|
7500
|
-
const totalCallers =
|
|
7529
|
+
const totalCallers = callers.length;
|
|
7501
7530
|
const section = findContainingSection(match.filePath, match.lineStart, match.lineEnd, readFileText);
|
|
7502
7531
|
const limit = opts.limit ?? 20;
|
|
7503
7532
|
const shown = callers.slice(0, limit);
|
|
@@ -7643,7 +7672,7 @@ function runFind(opts) {
|
|
|
7643
7672
|
}
|
|
7644
7673
|
if (opts.json === true) {
|
|
7645
7674
|
const fuzzyPayload = fuzzyNames.length > 0 ? { fuzzy: true, matchedNames: fuzzyNames } : {};
|
|
7646
|
-
emit(JSON.stringify({ files, truncated, ...fuzzyPayload }, null, 2));
|
|
7675
|
+
emit(JSON.stringify({ files, truncated, totalCount: allFiles.length, ...fuzzyPayload }, null, 2));
|
|
7647
7676
|
return 0;
|
|
7648
7677
|
}
|
|
7649
7678
|
if (fuzzyNames.length > 0) {
|
|
@@ -8913,6 +8942,18 @@ async function runSemantic(query, opts) {
|
|
|
8913
8942
|
`Matching on meaning is off (${extractErrorMessage(e)}); these results come from keyword search alone.`
|
|
8914
8943
|
);
|
|
8915
8944
|
}
|
|
8945
|
+
if (rawHits.length === 0 && isAvailable()) {
|
|
8946
|
+
try {
|
|
8947
|
+
const enabled = loadConfig().indexing.embeddings_enabled;
|
|
8948
|
+
const { indexedFiles, embeddedFiles } = enabled ? getEmbeddingCoverage(globalDbPath(), rootDir) : { indexedFiles: 0, embeddedFiles: 0 };
|
|
8949
|
+
if (indexedFiles > 0 && embeddedFiles < indexedFiles) {
|
|
8950
|
+
console.warn(
|
|
8951
|
+
`Matching on meaning found nothing, and only ${embeddedFiles} of ${indexedFiles} indexed file(s) in this project have embeddings \u2014 these results come from keyword search alone. Run 'token-goat doctor' for why.`
|
|
8952
|
+
);
|
|
8953
|
+
}
|
|
8954
|
+
} catch {
|
|
8955
|
+
}
|
|
8956
|
+
}
|
|
8916
8957
|
const mergedHits = mergeNearbyHits(rawHits);
|
|
8917
8958
|
const overFetchFts = Math.min(MAX_OVER_FETCH, n * OVER_FETCH_FACTOR);
|
|
8918
8959
|
const ftsRows = searchSymbolsFts(query, overFetchFts, void 0, rootDir);
|
|
@@ -8959,7 +9000,9 @@ async function runSemantic(query, opts) {
|
|
|
8959
9000
|
const anyFilter = matchesGrep !== void 0 || opts.excludeTests === true;
|
|
8960
9001
|
const filteredHits = anyFilter ? fusedHits.filter(keepHit) : fusedHits;
|
|
8961
9002
|
const suppressedTotal = opts.excludeTests === true ? fusedHits.filter((h) => (matchesGrep === void 0 || matchesGrep(toDisplayPath(rootDir, h.filePath))) && isTestFile(h.filePath)).length : 0;
|
|
9003
|
+
const eligibleCount = filteredHits.length;
|
|
8962
9004
|
const hits = filteredHits.slice(0, n);
|
|
9005
|
+
const candidatesClipped = rawHits.length >= overFetchForMerge || ftsRows.length >= overFetchFts;
|
|
8963
9006
|
const hadDense = mergedHits.length > 0;
|
|
8964
9007
|
const hadFts = ftsRows.length > 0;
|
|
8965
9008
|
const source = hadDense && hadFts ? "hybrid" : hadDense ? "embeddings" : "fts";
|
|
@@ -8975,7 +9018,8 @@ async function runSemantic(query, opts) {
|
|
|
8975
9018
|
preview: previewLines(h.previewText, 3)
|
|
8976
9019
|
}));
|
|
8977
9020
|
const capped = guardJsonRows(items);
|
|
8978
|
-
const
|
|
9021
|
+
const limitTruncated = hits.length < eligibleCount;
|
|
9022
|
+
const text3 = JSON.stringify({ source, ...capped, truncated: capped.truncated || limitTruncated, totalCount: eligibleCount, ...candidatesClipped ? { totalCountIsFloor: true } : {} }, null, 2);
|
|
8979
9023
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
|
|
8980
9024
|
return { text: text3, code: 0 };
|
|
8981
9025
|
}
|
|
@@ -8989,6 +9033,9 @@ ${previewLines(h.previewText, 3)}`;
|
|
|
8989
9033
|
${previewLines(h.previewText, 3)}`;
|
|
8990
9034
|
});
|
|
8991
9035
|
const text2 = guardText(blocks.join("\n\n"), "semantic");
|
|
9036
|
+
if (hits.length < eligibleCount) {
|
|
9037
|
+
emitErr(`Showing ${hits.length} of ${candidatesClipped ? "at least " : ""}${countNoun(eligibleCount, "match", "matches")} (raise --limit to see the rest).`);
|
|
9038
|
+
}
|
|
8992
9039
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text2, query);
|
|
8993
9040
|
return { text: text2, code: 0 };
|
|
8994
9041
|
}
|
|
@@ -9146,10 +9193,14 @@ function bfsCallChains(start, callersOf, maxDepth) {
|
|
|
9146
9193
|
const tip = chain[chain.length - 1];
|
|
9147
9194
|
if (tip === void 0) continue;
|
|
9148
9195
|
const callers = callersOf(tip);
|
|
9149
|
-
if (callers.length === 0
|
|
9196
|
+
if (callers.length === 0) {
|
|
9150
9197
|
complete.push(chain);
|
|
9151
9198
|
continue;
|
|
9152
9199
|
}
|
|
9200
|
+
if (chain.length > maxDepth) {
|
|
9201
|
+
complete.push([...chain, "(depth-limit)"]);
|
|
9202
|
+
continue;
|
|
9203
|
+
}
|
|
9153
9204
|
let expanded = false;
|
|
9154
9205
|
for (const caller of callers) {
|
|
9155
9206
|
if (chain.includes(caller)) {
|
|
@@ -9235,8 +9286,11 @@ function runCallers(opts) {
|
|
|
9235
9286
|
emitErr2(`Symbol '${name}' not found in '${file}'`);
|
|
9236
9287
|
return 1;
|
|
9237
9288
|
}
|
|
9238
|
-
const unbounded = opts.excludeTests === true || opts.grep !== void 0;
|
|
9239
|
-
const
|
|
9289
|
+
const unbounded = opts.excludeTests === true || opts.grep !== void 0 || fileHint !== void 0;
|
|
9290
|
+
const requestedLimit = opts.limit ?? DEFAULT_REF_QUERY_LIMIT;
|
|
9291
|
+
const probed = resolveCallers(name, unbounded ? opts.limit : requestedLimit + 1, fileHint, rootDir, unbounded);
|
|
9292
|
+
const sqlTruncated = !unbounded && probed.length > requestedLimit;
|
|
9293
|
+
const resolved = sqlTruncated ? probed.slice(0, requestedLimit) : probed;
|
|
9240
9294
|
const suppressed = opts.excludeTests === true ? resolved.filter((e) => isTestFile(e.file)).length : 0;
|
|
9241
9295
|
let filtered = opts.excludeTests === true ? resolved.filter((e) => !isTestFile(e.file)) : resolved;
|
|
9242
9296
|
const preGrepCount = filtered.length;
|
|
@@ -9277,7 +9331,7 @@ function runCallers(opts) {
|
|
|
9277
9331
|
return { ...e, file: displayPath, filePath: displayPath };
|
|
9278
9332
|
});
|
|
9279
9333
|
const capped = guardJsonRows(rows);
|
|
9280
|
-
const limitTruncated = entries.length < filtered.length;
|
|
9334
|
+
const limitTruncated = entries.length < filtered.length || sqlTruncated;
|
|
9281
9335
|
const hiddenByGrep = preGrepCount - filtered.length;
|
|
9282
9336
|
emit2(JSON.stringify({ items: capped.items, truncated: capped.truncated || limitTruncated, totalCount: filtered.length, ...hiddenByGrep > 0 ? { hiddenByGrep } : {}, ...opts.excludeTests === true && suppressed > 0 ? { hiddenByExcludeTests: suppressed } : {} }, null, 2));
|
|
9283
9337
|
return 0;
|
|
@@ -9316,7 +9370,7 @@ function runCallChain(opts) {
|
|
|
9316
9370
|
const getSyms = buildFileSymCache();
|
|
9317
9371
|
let suppressedCount = 0;
|
|
9318
9372
|
const callersOf = (n) => {
|
|
9319
|
-
const refs = queryRefs({ name: n, limit:
|
|
9373
|
+
const refs = queryRefs({ name: n, limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
9320
9374
|
if (refs.length === 0) return [];
|
|
9321
9375
|
const scoped = fileHint !== void 0 && n === name ? filterRefsForSymbol(refs, n, fileHint, getSyms) : refs;
|
|
9322
9376
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -9379,12 +9433,16 @@ function runImpact(opts) {
|
|
|
9379
9433
|
const hops = /* @__PURE__ */ new Map([[rootName, 0]]);
|
|
9380
9434
|
const queue = [[rootName, 0]];
|
|
9381
9435
|
let suppressedCount = 0;
|
|
9436
|
+
let depthCapped = 0;
|
|
9382
9437
|
while (queue.length > 0) {
|
|
9383
9438
|
const item = queue.shift();
|
|
9384
9439
|
if (item === void 0) break;
|
|
9385
9440
|
const [name, depth] = item;
|
|
9386
|
-
if (depth >= DEPTH_CAP)
|
|
9387
|
-
|
|
9441
|
+
if (depth >= DEPTH_CAP) {
|
|
9442
|
+
depthCapped += 1;
|
|
9443
|
+
continue;
|
|
9444
|
+
}
|
|
9445
|
+
const refs = queryRefs({ name, limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
9388
9446
|
const scoped = fileHint !== void 0 && name === rootName ? filterRefsForSymbol(refs, name, fileHint, getSyms) : refs;
|
|
9389
9447
|
for (const ref of scoped) {
|
|
9390
9448
|
if (opts.excludeTests === true && isTestFile(ref.filePath)) {
|
|
@@ -9412,6 +9470,12 @@ function runImpact(opts) {
|
|
|
9412
9470
|
const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
|
|
9413
9471
|
const grepped = matchesGrep !== void 0 ? allSorted.filter(([symbol]) => matchesGrep(symbol)) : allSorted;
|
|
9414
9472
|
const sorted = grepped.slice(0, top);
|
|
9473
|
+
if (depthCapped > 0) {
|
|
9474
|
+
emitErr2(`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.`);
|
|
9475
|
+
}
|
|
9476
|
+
if (grepped.length > sorted.length) {
|
|
9477
|
+
emitErr2(`Showing top ${sorted.length} of ${grepped.length} impacted symbols (raise --top to see the rest).`);
|
|
9478
|
+
}
|
|
9415
9479
|
if (matchesGrep !== void 0 && sorted.length === 0 && allSorted.length > 0) {
|
|
9416
9480
|
if (opts.json === true) {
|
|
9417
9481
|
emitErr2(grepFilteredToEmptyNotice(allSorted.length, opts.grep, "impacted symbol", "impacted symbols"));
|
|
@@ -9541,14 +9605,17 @@ function runDead(opts) {
|
|
|
9541
9605
|
}
|
|
9542
9606
|
return 1;
|
|
9543
9607
|
}
|
|
9544
|
-
const syms = kinds.flatMap((k) => querySymbols({ kind: k, limit:
|
|
9608
|
+
const syms = kinds.flatMap((k) => querySymbols({ kind: k, limit: UNBOUNDED_REF_LIMIT, rootDir }));
|
|
9545
9609
|
const getSyms = buildFileSymCache();
|
|
9546
9610
|
const results = [];
|
|
9547
9611
|
let suppressed = 0;
|
|
9548
9612
|
for (const sym of syms) {
|
|
9549
9613
|
if (opts.includePrivate !== true && sym.name.startsWith("_")) continue;
|
|
9550
9614
|
const refs = queryRefs({ name: sym.name, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
9551
|
-
|
|
9615
|
+
let scoped = filterRefsForSymbol(refs, sym.name, sym.filePath, getSyms);
|
|
9616
|
+
if (scoped.length === 0 && refs.length >= DEFAULT_REF_QUERY_LIMIT) {
|
|
9617
|
+
scoped = filterRefsForSymbol(queryRefs({ name: sym.name, limit: UNBOUNDED_REF_LIMIT, rootDir }), sym.name, sym.filePath, getSyms);
|
|
9618
|
+
}
|
|
9552
9619
|
if (!isDeadSymbol(sym.name, scoped.length)) continue;
|
|
9553
9620
|
if (sym.kind === "method") {
|
|
9554
9621
|
const ownScope = enclosingNamedScope(getSyms(sym.filePath), sym.lineStart);
|
|
@@ -9769,7 +9836,7 @@ var TYPE_KINDS = [
|
|
|
9769
9836
|
var CORE_SYMBOL_KINDS = [
|
|
9770
9837
|
.../* @__PURE__ */ new Set([...GENERIC_SYMBOL_KINDS, ...TYPE_KINDS, ...OTHER_ADAPTER_SYMBOL_KINDS])
|
|
9771
9838
|
];
|
|
9772
|
-
var
|
|
9839
|
+
var TYPES_SCAN_LIMIT = 5e3;
|
|
9773
9840
|
function runTypes(opts) {
|
|
9774
9841
|
if (opts.limit !== void 0 && opts.limit <= 0) {
|
|
9775
9842
|
emitErr2(`--limit must be a positive number, got: ${opts.limit}`);
|
|
@@ -9782,34 +9849,25 @@ function runTypes(opts) {
|
|
|
9782
9849
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
9783
9850
|
const results = [];
|
|
9784
9851
|
let suppressed = 0;
|
|
9785
|
-
const queryLimit = excludeTests ? TYPES_EXCLUDE_TESTS_SCAN_LIMIT : limit;
|
|
9786
9852
|
for (const k of TYPE_KINDS) {
|
|
9787
|
-
const syms = querySymbols({ kind: k, ...fpOpt, limit:
|
|
9788
|
-
if (!excludeTests) {
|
|
9789
|
-
results.push(...syms);
|
|
9790
|
-
continue;
|
|
9791
|
-
}
|
|
9792
|
-
const kept = [];
|
|
9853
|
+
const syms = querySymbols({ kind: k, ...fpOpt, limit: TYPES_SCAN_LIMIT, rootDir });
|
|
9793
9854
|
for (const sym of syms) {
|
|
9794
|
-
if (isTestFile(sym.filePath)) {
|
|
9855
|
+
if (excludeTests && isTestFile(sym.filePath)) {
|
|
9795
9856
|
suppressed += 1;
|
|
9796
9857
|
continue;
|
|
9797
9858
|
}
|
|
9798
|
-
|
|
9859
|
+
results.push(sym);
|
|
9799
9860
|
}
|
|
9800
|
-
results.push(...kept.slice(0, limit));
|
|
9801
9861
|
}
|
|
9802
|
-
const classes = querySymbols({ kind: "class", ...fpOpt, limit:
|
|
9803
|
-
const typeClasses = [];
|
|
9862
|
+
const classes = querySymbols({ kind: "class", ...fpOpt, limit: TYPES_SCAN_LIMIT, rootDir });
|
|
9804
9863
|
for (const cls of classes) {
|
|
9805
9864
|
if (!looksLikeTypeClass(cls.body)) continue;
|
|
9806
9865
|
if (excludeTests && isTestFile(cls.filePath)) {
|
|
9807
9866
|
suppressed += 1;
|
|
9808
9867
|
continue;
|
|
9809
9868
|
}
|
|
9810
|
-
|
|
9869
|
+
results.push(cls);
|
|
9811
9870
|
}
|
|
9812
|
-
results.push(...excludeTests ? typeClasses.slice(0, limit) : typeClasses);
|
|
9813
9871
|
results.sort(
|
|
9814
9872
|
// Pin the locale: an unlocaled localeCompare() sorts differently across
|
|
9815
9873
|
// Node's small-icu vs full-icu builds and different system default
|
|
@@ -9836,7 +9894,16 @@ function runTypes(opts) {
|
|
|
9836
9894
|
}
|
|
9837
9895
|
const preFilterCount = results.length;
|
|
9838
9896
|
const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
|
|
9839
|
-
const
|
|
9897
|
+
const matched = matchesGrep !== void 0 ? results.filter((r) => matchesGrep(r.name)) : results;
|
|
9898
|
+
const eligibleCount = matched.length;
|
|
9899
|
+
const perKindShown = /* @__PURE__ */ new Map();
|
|
9900
|
+
const filtered = matched.filter((r) => {
|
|
9901
|
+
const seen = perKindShown.get(r.kind) ?? 0;
|
|
9902
|
+
if (seen >= limit) return false;
|
|
9903
|
+
perKindShown.set(r.kind, seen + 1);
|
|
9904
|
+
return true;
|
|
9905
|
+
});
|
|
9906
|
+
const cappedOut = eligibleCount - filtered.length;
|
|
9840
9907
|
if (filtered.length === 0) {
|
|
9841
9908
|
if (opts.json === true) {
|
|
9842
9909
|
emit2(JSON.stringify({ items: [], truncated: false, totalCount: 0, hiddenByGrep: preFilterCount }, null, 2));
|
|
@@ -9847,8 +9914,8 @@ function runTypes(opts) {
|
|
|
9847
9914
|
}
|
|
9848
9915
|
if (opts.json === true) {
|
|
9849
9916
|
const capped = guardJsonRows(filtered.map((r) => ({ ...r, filePath: toDisplayPath(rootDir, r.filePath) })));
|
|
9850
|
-
const hiddenByGrep = preFilterCount -
|
|
9851
|
-
emit2(JSON.stringify({ items: capped.items, truncated: capped.truncated, totalCount:
|
|
9917
|
+
const hiddenByGrep = preFilterCount - eligibleCount;
|
|
9918
|
+
emit2(JSON.stringify({ items: capped.items, truncated: capped.truncated || cappedOut > 0, totalCount: eligibleCount, ...hiddenByGrep > 0 ? { hiddenByGrep } : {}, ...excludeTests && suppressed > 0 ? { hiddenByExcludeTests: suppressed } : {} }, null, 2));
|
|
9852
9919
|
return 0;
|
|
9853
9920
|
}
|
|
9854
9921
|
if (excludeTests && suppressed > 0) {
|
|
@@ -9857,6 +9924,9 @@ function runTypes(opts) {
|
|
|
9857
9924
|
for (const r of filtered) {
|
|
9858
9925
|
emit2(`${r.name} ${r.kind} ${toDisplayPath(rootDir, r.filePath)}:${r.lineStart}`);
|
|
9859
9926
|
}
|
|
9927
|
+
if (cappedOut > 0) {
|
|
9928
|
+
emitErr2(`Showing ${filtered.length} of ${eligibleCount} type declarations (--limit is per kind; raise it to see the rest).`);
|
|
9929
|
+
}
|
|
9860
9930
|
return 0;
|
|
9861
9931
|
}
|
|
9862
9932
|
function runScope(opts) {
|
|
@@ -9872,7 +9942,7 @@ function runScope(opts) {
|
|
|
9872
9942
|
return 1;
|
|
9873
9943
|
}
|
|
9874
9944
|
const line = Number.parseInt(lineStr, 10);
|
|
9875
|
-
if (!Number.
|
|
9945
|
+
if (!Number.isSafeInteger(line) || line < 1) {
|
|
9876
9946
|
emitErr2(`Invalid line number: ${lineStr}`);
|
|
9877
9947
|
return 1;
|
|
9878
9948
|
}
|
|
@@ -9882,25 +9952,32 @@ function runScope(opts) {
|
|
|
9882
9952
|
return 1;
|
|
9883
9953
|
}
|
|
9884
9954
|
const filePath = resolveIndexPath(file);
|
|
9885
|
-
const
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9955
|
+
const enclosing = querySymbols({ filePath, enclosingLine: line, limit: ALL_SYMBOLS_IN_FILE_LIMIT }).sort((a, b) => b.lineStart - a.lineStart || a.lineEnd - b.lineEnd || a.name.localeCompare(b.name, "en"));
|
|
9956
|
+
if (enclosing.length === 0) {
|
|
9957
|
+
if (querySymbols({ filePath, limit: 1 }).length === 0) {
|
|
9958
|
+
if (!fs6.existsSync(filePath)) {
|
|
9959
|
+
emitErr2(`Could not read: ${file}`);
|
|
9960
|
+
return 1;
|
|
9961
|
+
}
|
|
9962
|
+
emitErr2(
|
|
9963
|
+
`No indexed symbols in '${file}' \u2014 the file exists but nothing is indexed for it, so every line looks empty`
|
|
9964
|
+
);
|
|
9890
9965
|
return 1;
|
|
9891
9966
|
}
|
|
9892
|
-
emitErr2(`No indexed symbols in '${file}' \u2014 the file exists but nothing is indexed for it, so every line looks empty`);
|
|
9893
|
-
return 1;
|
|
9894
|
-
}
|
|
9895
|
-
if (enclosing.length === 0) {
|
|
9896
9967
|
emitErr2(`No symbols enclosing line ${line} in '${file}'`);
|
|
9897
9968
|
return 1;
|
|
9898
9969
|
}
|
|
9970
|
+
const scopeDisplayRoot = getDisplayRoot();
|
|
9899
9971
|
if (opts.json === true) {
|
|
9900
|
-
emit2(
|
|
9972
|
+
emit2(
|
|
9973
|
+
JSON.stringify(
|
|
9974
|
+
enclosing.map((s) => ({ ...s, filePath: toDisplayPath(scopeDisplayRoot, s.filePath) })),
|
|
9975
|
+
null,
|
|
9976
|
+
2
|
|
9977
|
+
)
|
|
9978
|
+
);
|
|
9901
9979
|
return 0;
|
|
9902
9980
|
}
|
|
9903
|
-
const scopeDisplayRoot = getDisplayRoot();
|
|
9904
9981
|
for (const s of enclosing) {
|
|
9905
9982
|
emit2(`${s.name} ${s.kind} ${toDisplayPath(scopeDisplayRoot, s.filePath)}:${s.lineStart}-${s.lineEnd}`);
|
|
9906
9983
|
}
|
|
@@ -9967,17 +10044,18 @@ function canonicalCycleKey(cyclePath) {
|
|
|
9967
10044
|
}
|
|
9968
10045
|
return [...nodes.slice(minIdx), ...nodes.slice(0, minIdx)].join(" ");
|
|
9969
10046
|
}
|
|
9970
|
-
function
|
|
10047
|
+
function findCyclesCapped(graph) {
|
|
9971
10048
|
const cycles = [];
|
|
9972
10049
|
const seen = /* @__PURE__ */ new Set();
|
|
9973
10050
|
const sccs = tarjanSCCs(graph);
|
|
10051
|
+
const probeLimit = MAX_CYCLES + 1;
|
|
9974
10052
|
for (const component of sccs) {
|
|
9975
10053
|
let dfs2 = function(start, node, pathSoFar) {
|
|
9976
|
-
if (cycles.length >=
|
|
10054
|
+
if (cycles.length >= probeLimit) return;
|
|
9977
10055
|
stack.add(node);
|
|
9978
10056
|
for (const nb of graph.get(node) ?? []) {
|
|
9979
10057
|
if (!sccSet.has(nb)) continue;
|
|
9980
|
-
if (cycles.length >=
|
|
10058
|
+
if (cycles.length >= probeLimit) break;
|
|
9981
10059
|
if (nb === start) {
|
|
9982
10060
|
const cyclePath = [...pathSoFar, node, start];
|
|
9983
10061
|
const key = canonicalCycleKey(cyclePath);
|
|
@@ -9992,6 +10070,7 @@ function findCycles(graph) {
|
|
|
9992
10070
|
stack.delete(node);
|
|
9993
10071
|
};
|
|
9994
10072
|
var dfs = dfs2;
|
|
10073
|
+
if (cycles.length >= probeLimit) break;
|
|
9995
10074
|
if (component.length === 1) {
|
|
9996
10075
|
const [only] = component;
|
|
9997
10076
|
if (only !== void 0 && (graph.get(only) ?? []).includes(only)) cycles.push([only, only]);
|
|
@@ -10000,11 +10079,12 @@ function findCycles(graph) {
|
|
|
10000
10079
|
const sccSet = new Set(component);
|
|
10001
10080
|
const stack = /* @__PURE__ */ new Set();
|
|
10002
10081
|
for (const start of component) {
|
|
10003
|
-
if (cycles.length >=
|
|
10082
|
+
if (cycles.length >= probeLimit) break;
|
|
10004
10083
|
dfs2(start, start, []);
|
|
10005
10084
|
}
|
|
10006
10085
|
}
|
|
10007
|
-
return cycles;
|
|
10086
|
+
if (cycles.length > MAX_CYCLES) return { cycles: cycles.slice(0, MAX_CYCLES), truncated: true };
|
|
10087
|
+
return { cycles, truncated: false };
|
|
10008
10088
|
}
|
|
10009
10089
|
function runSimilar(opts) {
|
|
10010
10090
|
if (opts.top !== void 0 && opts.top <= 0) {
|
|
@@ -10022,8 +10102,12 @@ function runSimilar(opts) {
|
|
|
10022
10102
|
const words = [anchor.name, ...(anchor.docstring ?? "").split(/\s+/).filter((w) => w.length > 4)];
|
|
10023
10103
|
const query = words.slice(0, 8).join(" ");
|
|
10024
10104
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
10025
|
-
const hits = searchSymbolsFts(query,
|
|
10026
|
-
const
|
|
10105
|
+
const hits = searchSymbolsFts(query, UNBOUNDED_REF_LIMIT, void 0, rootDir);
|
|
10106
|
+
const matches = hits.filter((h) => !(h.filePath === anchor.filePath && h.name === anchor.name));
|
|
10107
|
+
const results = matches.slice(0, top);
|
|
10108
|
+
if (matches.length > results.length) {
|
|
10109
|
+
emitErr2(`Showing top ${results.length} of ${matches.length} similar symbols (raise --top to see the rest).`);
|
|
10110
|
+
}
|
|
10027
10111
|
if (opts.json === true) {
|
|
10028
10112
|
emit2(JSON.stringify(results.map((h) => ({ name: h.name, kind: h.kind, file: h.filePath, line: h.lineStart })), null, 2));
|
|
10029
10113
|
return 0;
|
|
@@ -10039,7 +10123,9 @@ function runContextFor(opts) {
|
|
|
10039
10123
|
const top = opts.top ?? 12;
|
|
10040
10124
|
const budget = opts.budget;
|
|
10041
10125
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
10042
|
-
const
|
|
10126
|
+
const fetched = searchSymbolsFts(opts.task, top + 1, void 0, rootDir);
|
|
10127
|
+
const moreBeyondTop = fetched.length > top;
|
|
10128
|
+
const hits = fetched.slice(0, top);
|
|
10043
10129
|
if (hits.length === 0) {
|
|
10044
10130
|
emitErr2(`No matches found for '${opts.task}'`);
|
|
10045
10131
|
return 1;
|
|
@@ -10052,6 +10138,15 @@ function runContextFor(opts) {
|
|
|
10052
10138
|
tokensSoFar += bodyTokens;
|
|
10053
10139
|
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, line: h.lineStart, readCmd: `token-goat read "${h.filePath}::${h.name}@${h.lineStart}"` });
|
|
10054
10140
|
}
|
|
10141
|
+
const skippedByBudget = hits.length - entries.length;
|
|
10142
|
+
const notices = [];
|
|
10143
|
+
if (skippedByBudget > 0) {
|
|
10144
|
+
notices.push(
|
|
10145
|
+
entries.length === 0 ? `All ${countNoun(skippedByBudget, "matching symbol")} were larger than --budget ${String(opts.budget)} tokens, so none are shown. Raise --budget.` : `Showing ${entries.length} of ${countNoun(hits.length, "matching symbol")}; ${skippedByBudget} did not fit --budget ${String(opts.budget)} tokens.`
|
|
10146
|
+
);
|
|
10147
|
+
}
|
|
10148
|
+
if (moreBeyondTop) notices.push(`More matches exist beyond --top ${top} (raise it to see them).`);
|
|
10149
|
+
for (const n of notices) emitErr2(n);
|
|
10055
10150
|
if (opts.json === true) {
|
|
10056
10151
|
emit2(JSON.stringify(entries, null, 2));
|
|
10057
10152
|
return 0;
|
|
@@ -10108,8 +10203,8 @@ function runCoverageGaps(opts) {
|
|
|
10108
10203
|
}
|
|
10109
10204
|
const top = opts.top ?? 50;
|
|
10110
10205
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
10111
|
-
const allFns = querySymbols({ kind: "function", limit:
|
|
10112
|
-
const allMethods = querySymbols({ kind: "method", limit:
|
|
10206
|
+
const allFns = querySymbols({ kind: "function", limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
10207
|
+
const allMethods = querySymbols({ kind: "method", limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
10113
10208
|
const candidates = [...allFns, ...allMethods];
|
|
10114
10209
|
const gaps = [];
|
|
10115
10210
|
for (const sym of candidates) {
|
|
@@ -10120,6 +10215,9 @@ function runCoverageGaps(opts) {
|
|
|
10120
10215
|
if (!hasTestRef) gaps.push({ name: sym.name, kind: sym.kind, file: sym.filePath, line: sym.lineStart });
|
|
10121
10216
|
}
|
|
10122
10217
|
const sliced = gaps.slice(0, top);
|
|
10218
|
+
if (gaps.length > sliced.length) {
|
|
10219
|
+
emitErr2(`Showing top ${sliced.length} of ${gaps.length} coverage gaps (raise --top to see the rest).`);
|
|
10220
|
+
}
|
|
10123
10221
|
if (opts.json === true) {
|
|
10124
10222
|
emit2(JSON.stringify(sliced, null, 2));
|
|
10125
10223
|
return 0;
|
|
@@ -10186,18 +10284,22 @@ function runArch(opts) {
|
|
|
10186
10284
|
}
|
|
10187
10285
|
graph.set(file, internal);
|
|
10188
10286
|
}
|
|
10189
|
-
const
|
|
10190
|
-
const
|
|
10191
|
-
const
|
|
10287
|
+
const allHubs = [...importedBy.entries()].sort((a, b) => b[1].size - a[1].size).map(([f, importers]) => ({ file: f, importedBy: importers.size }));
|
|
10288
|
+
const hubsTotal = allHubs.length;
|
|
10289
|
+
const hubs = allHubs.slice(0, top);
|
|
10290
|
+
const allEntryPoints = files.filter((f) => !importedBy.has(f) && (graph.get(f) ?? []).length > 0).map((f) => ({ file: f }));
|
|
10291
|
+
const entryPointsTotal = allEntryPoints.length;
|
|
10292
|
+
const entryPoints = allEntryPoints.slice(0, top);
|
|
10293
|
+
const { cycles, truncated: cyclesTruncated } = findCyclesCapped(graph);
|
|
10192
10294
|
if (opts.json === true) {
|
|
10193
|
-
emit2(JSON.stringify({ hubs, entryPoints, cycles }, null, 2));
|
|
10295
|
+
emit2(JSON.stringify({ hubs, hubsTotal, hubsTruncated: hubs.length < hubsTotal, entryPoints, entryPointsTotal, entryPointsTruncated: entryPoints.length < entryPointsTotal, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {} }, null, 2));
|
|
10194
10296
|
return 0;
|
|
10195
10297
|
}
|
|
10196
|
-
emit2(`hubs (top ${
|
|
10298
|
+
emit2(hubs.length < hubsTotal ? `hubs (top ${hubs.length} of ${hubsTotal} most-imported):` : `hubs (${hubsTotal} most-imported):`);
|
|
10197
10299
|
for (const h of hubs) emit2(` ${h.importedBy} importers ${toDisplayPath(getDisplayRoot(opts.cwd), h.file)}`);
|
|
10198
|
-
emit2(`entry points (imported by nobody, top ${
|
|
10300
|
+
emit2(entryPoints.length < entryPointsTotal ? `entry points (imported by nobody, top ${entryPoints.length} of ${entryPointsTotal}):` : `entry points (imported by nobody, ${entryPointsTotal} found):`);
|
|
10199
10301
|
for (const e of entryPoints) emit2(` ${toDisplayPath(getDisplayRoot(opts.cwd), e.file)}`);
|
|
10200
|
-
emit2(`cycles (${cycles.length} found):`);
|
|
10302
|
+
emit2(cyclesTruncated ? `cycles (first ${cycles.length}, truncated at the ${MAX_CYCLES}-cycle enumeration limit: more cycles exist):` : `cycles (${cycles.length} found):`);
|
|
10201
10303
|
for (const c of cycles) emit2(` ${c.map((f) => toDisplayPath(getDisplayRoot(opts.cwd), f)).join(" -> ")}`);
|
|
10202
10304
|
return 0;
|
|
10203
10305
|
}
|
|
@@ -10245,21 +10347,30 @@ function runAsk(opts) {
|
|
|
10245
10347
|
}
|
|
10246
10348
|
const top = opts.top ?? 8;
|
|
10247
10349
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
10248
|
-
const
|
|
10350
|
+
const fetched = searchSymbolsFts(opts.question, top + 1, void 0, rootDir);
|
|
10351
|
+
const moreBeyondTop = fetched.length > top;
|
|
10352
|
+
const hits = fetched.slice(0, top);
|
|
10353
|
+
if (moreBeyondTop) {
|
|
10354
|
+
emitErr2(`Answer is grounded in the top ${countNoun(top, "match", "matches")}; more matched (raise --top to widen the evidence).`);
|
|
10355
|
+
}
|
|
10249
10356
|
const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
|
|
10250
10357
|
const backendLabel = process.env[BACKEND_ENV] ?? "";
|
|
10251
10358
|
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}"` }));
|
|
10252
|
-
const degrade = () => {
|
|
10359
|
+
const degrade = (reason, extraNote) => {
|
|
10253
10360
|
if (opts.json === true) {
|
|
10254
|
-
emit2(JSON.stringify({ degraded: true, note:
|
|
10361
|
+
emit2(JSON.stringify({ degraded: true, note: reason, ...extraNote !== void 0 ? { hint: extraNote } : {}, context: entries }, null, 2));
|
|
10255
10362
|
return 0;
|
|
10256
10363
|
}
|
|
10257
|
-
emit2(`[degraded mode -
|
|
10364
|
+
emit2(`[degraded mode - ${reason}]`);
|
|
10365
|
+
if (extraNote !== void 0) emit2(extraNote);
|
|
10258
10366
|
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
10259
10367
|
return 0;
|
|
10260
10368
|
};
|
|
10261
|
-
if (!backendLabel) return degrade();
|
|
10262
|
-
if (hits.length === 0)
|
|
10369
|
+
if (!backendLabel) return degrade(`set ${BACKEND_ENV}=claude|codex for LLM synthesis`);
|
|
10370
|
+
if (hits.length === 0) {
|
|
10371
|
+
if (isIndexEmptyForProject(globalDbPath(), rootDir)) return degrade(`${BACKEND_ENV}=${backendLabel} is set, but nothing is indexed for this project yet, so there is no context to answer from`, emptyIndexMessage(rootDir));
|
|
10372
|
+
return degrade(`${BACKEND_ENV}=${backendLabel} is set, but no indexed symbol matched this question, so there is no context to answer from -- try different wording or token-goat semantic`);
|
|
10373
|
+
}
|
|
10263
10374
|
const isWin = process.platform === "win32";
|
|
10264
10375
|
let backendPath = null;
|
|
10265
10376
|
try {
|
|
@@ -10268,7 +10379,7 @@ function runAsk(opts) {
|
|
|
10268
10379
|
if (found) backendPath = found;
|
|
10269
10380
|
} catch {
|
|
10270
10381
|
}
|
|
10271
|
-
if (!backendPath) return degrade();
|
|
10382
|
+
if (!backendPath) return degrade(`${BACKEND_ENV}=${backendLabel} is set, but '${backendLabel}' was not found on PATH`);
|
|
10272
10383
|
const context = hits.map((h, i) => `[${i + 1}] ${h.filePath}
|
|
10273
10384
|
${h.body ?? ""}`).join("\n\n");
|
|
10274
10385
|
const prompt = `Answer the QUESTION using only the CODE SNIPPETS below.
|
|
@@ -10319,7 +10430,7 @@ ANSWER:`;
|
|
|
10319
10430
|
}
|
|
10320
10431
|
}
|
|
10321
10432
|
}
|
|
10322
|
-
return degrade();
|
|
10433
|
+
return degrade(`${BACKEND_ENV}=${backendLabel} ran but returned no answer`);
|
|
10323
10434
|
}
|
|
10324
10435
|
|
|
10325
10436
|
// src/content_store.ts
|