token-goat 2.8.3 → 2.8.4
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 +14 -12
- package/dist/{token-goat-chunk-PWVXXPCC.mjs → token-goat-chunk-4OTIB7SB.mjs} +450 -138
- package/dist/{token-goat-chunk-EFF2XCLB.mjs → token-goat-chunk-CZALRRGN.mjs} +175 -56
- package/dist/{token-goat-chunk-6ODZ6PZK.mjs → token-goat-chunk-E76UNTVK.mjs} +189 -69
- package/dist/{token-goat-chunk-4OM2Q2SX.mjs → token-goat-chunk-HNODKNWO.mjs} +5 -5
- package/dist/{token-goat-chunk-4HIMCBYK.mjs → token-goat-chunk-IZRXU64B.mjs} +51 -4
- package/dist/{token-goat-chunk-DP3NNGGV.mjs → token-goat-chunk-L6YP6FKQ.mjs} +5 -5
- package/dist/{token-goat-chunk-C5IL6MJH.mjs → token-goat-chunk-LZOAPGWR.mjs} +6 -5
- package/dist/{token-goat-chunk-VAGPOZHO.mjs → token-goat-chunk-NZFTWBGV.mjs} +2 -2
- package/dist/{token-goat-chunk-TX4JFJTD.mjs → token-goat-chunk-RE7S7H26.mjs} +110 -50
- package/dist/{token-goat-chunk-222VPFP2.mjs → token-goat-chunk-U4UI5F3S.mjs} +953 -188
- package/dist/{token-goat-chunk-NKNCHJ4H.mjs → token-goat-chunk-ZQ3PUOP3.mjs} +310 -109
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +3 -1
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runSkeleton,
|
|
26
26
|
runSymbol,
|
|
27
27
|
withPinnedReads
|
|
28
|
-
} from "./token-goat-chunk-
|
|
28
|
+
} from "./token-goat-chunk-RE7S7H26.mjs";
|
|
29
29
|
import {
|
|
30
30
|
buildProjectMap,
|
|
31
31
|
embeddingsDepsAvailable,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
getProjectIndexCounts,
|
|
35
35
|
isWorkerRunning,
|
|
36
36
|
mapLookupBytesSaved
|
|
37
|
-
} from "./token-goat-chunk-
|
|
37
|
+
} from "./token-goat-chunk-4OTIB7SB.mjs";
|
|
38
38
|
import {
|
|
39
39
|
VERSION,
|
|
40
40
|
dataDir,
|
|
@@ -44,8 +44,9 @@ import {
|
|
|
44
44
|
loadConfig,
|
|
45
45
|
normalizePath,
|
|
46
46
|
recordStat,
|
|
47
|
-
resolveProjectRoot
|
|
48
|
-
|
|
47
|
+
resolveProjectRoot,
|
|
48
|
+
savedTokensFromBytes
|
|
49
|
+
} from "./token-goat-chunk-E76UNTVK.mjs";
|
|
49
50
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
50
51
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
51
52
|
|
|
@@ -533,7 +534,7 @@ async function createMcpServer() {
|
|
|
533
534
|
const map = buildProjectMap(resolveToolRoot(projectRoot), { compact: compact === true });
|
|
534
535
|
const text = formatProjectMap(map, map.compact);
|
|
535
536
|
const bytesSaved = mapLookupBytesSaved(map, text);
|
|
536
|
-
recordStat("map_lookup", bytesSaved,
|
|
537
|
+
recordStat("map_lookup", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
537
538
|
return toCallToolResult({ text, code: 0 });
|
|
538
539
|
}
|
|
539
540
|
);
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
selectFilter,
|
|
11
11
|
shlexSplit,
|
|
12
12
|
wrappedShell
|
|
13
|
-
} from "./token-goat-chunk-
|
|
13
|
+
} from "./token-goat-chunk-CZALRRGN.mjs";
|
|
14
14
|
import {
|
|
15
15
|
loadConfig,
|
|
16
16
|
recordStat
|
|
17
|
-
} from "./token-goat-chunk-
|
|
17
|
+
} from "./token-goat-chunk-E76UNTVK.mjs";
|
|
18
18
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
19
19
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
20
20
|
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
locatePdfPages,
|
|
40
40
|
mergeNearbyHits,
|
|
41
41
|
ocrImage,
|
|
42
|
+
ocrIntegrityFailed,
|
|
42
43
|
parseWhereSpecs,
|
|
43
44
|
pathEqClause,
|
|
44
45
|
probeImageMeta,
|
|
@@ -55,6 +56,7 @@ import {
|
|
|
55
56
|
shrinkImage,
|
|
56
57
|
storeBlob,
|
|
57
58
|
stripLeadingAttributes,
|
|
59
|
+
stripTomlComment,
|
|
58
60
|
tomlBracketDelta,
|
|
59
61
|
trimToBudget,
|
|
60
62
|
unzipBounded,
|
|
@@ -62,7 +64,7 @@ import {
|
|
|
62
64
|
walkProject,
|
|
63
65
|
yamlLineClosesQuote,
|
|
64
66
|
yamlOpenQuoteAfter
|
|
65
|
-
} from "./token-goat-chunk-
|
|
67
|
+
} from "./token-goat-chunk-4OTIB7SB.mjs";
|
|
66
68
|
import {
|
|
67
69
|
Database,
|
|
68
70
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
@@ -82,6 +84,7 @@ import {
|
|
|
82
84
|
excludeTestsHiddenNote,
|
|
83
85
|
extractErrorMessage,
|
|
84
86
|
fileIsAbsent,
|
|
87
|
+
filtersFilteredToEmptyNotice,
|
|
85
88
|
findHtmlHeadingMatches,
|
|
86
89
|
findProject,
|
|
87
90
|
fingerprintContent,
|
|
@@ -107,6 +110,7 @@ import {
|
|
|
107
110
|
resolveIndexPath,
|
|
108
111
|
resolveProjectRoot,
|
|
109
112
|
runGit,
|
|
113
|
+
savedTokensFromBytes,
|
|
110
114
|
shortFingerprint,
|
|
111
115
|
stripAnsi,
|
|
112
116
|
stripBom,
|
|
@@ -114,7 +118,7 @@ import {
|
|
|
114
118
|
unsupportedLanguageName,
|
|
115
119
|
windowsCmdQuoteArg,
|
|
116
120
|
withExtension
|
|
117
|
-
} from "./token-goat-chunk-
|
|
121
|
+
} from "./token-goat-chunk-E76UNTVK.mjs";
|
|
118
122
|
import {
|
|
119
123
|
registerReset
|
|
120
124
|
} from "./token-goat-chunk-AO2QD2AG.mjs";
|
|
@@ -198,18 +202,19 @@ function findTableHeaders(lines, isToml) {
|
|
|
198
202
|
const restStart = closeIdx + openDelim.length;
|
|
199
203
|
const m2 = TABLE_HEADER_RE.exec(line.slice(restStart));
|
|
200
204
|
if (m2 !== null && m2[1] !== void 0) headers.push({ heading: m2[1].trim(), level: 1, index: i });
|
|
201
|
-
openDelim = lineOpenDelimiterAfter(line,
|
|
205
|
+
openDelim = lineOpenDelimiterAfter(stripTomlComment(line.slice(restStart)), 0);
|
|
202
206
|
continue;
|
|
203
207
|
}
|
|
204
208
|
if (isToml && arrayDepth > 0) {
|
|
205
|
-
arrayDepth = Math.max(0, arrayDepth + tomlBracketDelta(line));
|
|
209
|
+
arrayDepth = Math.max(0, arrayDepth + tomlBracketDelta(stripTomlComment(line)));
|
|
206
210
|
continue;
|
|
207
211
|
}
|
|
208
212
|
const m = TABLE_HEADER_RE.exec(line);
|
|
209
213
|
if (m !== null && m[1] !== void 0) headers.push({ heading: m[1].trim(), level: 1, index: i });
|
|
210
214
|
if (isToml) {
|
|
211
|
-
|
|
212
|
-
|
|
215
|
+
const code = stripTomlComment(line);
|
|
216
|
+
openDelim = lineOpenDelimiterAfter(code, 0);
|
|
217
|
+
if (openDelim === null) arrayDepth = Math.max(0, tomlBracketDelta(code));
|
|
213
218
|
}
|
|
214
219
|
}
|
|
215
220
|
return headers;
|
|
@@ -255,10 +260,11 @@ function findKeyValueHeaders(lines, language) {
|
|
|
255
260
|
if (closed) openQuote = null;
|
|
256
261
|
continue;
|
|
257
262
|
}
|
|
258
|
-
const
|
|
263
|
+
const scanned = isEnv ? line.replace(/^[ \t]+/, "") : line;
|
|
264
|
+
const m = (isEnv ? ENV_KEYVALUE_HEADER_RE : KEYVALUE_HEADER_RE).exec(scanned);
|
|
259
265
|
if (m === null || m[1] === void 0) continue;
|
|
260
266
|
headers.push({ heading: m[1], level: 1, index: i });
|
|
261
|
-
openQuote = isEnv ? _detectOpenQuote(
|
|
267
|
+
openQuote = isEnv ? _detectOpenQuote(scanned.slice(m[0].length)) : yamlOpenQuoteAfter(line, m[0].length);
|
|
262
268
|
}
|
|
263
269
|
return headers;
|
|
264
270
|
}
|
|
@@ -5376,7 +5382,7 @@ function sumFileSizes(filePaths) {
|
|
|
5376
5382
|
function recordReadStat(kind, fullSourceBytes, emittedText, detail) {
|
|
5377
5383
|
const emittedBytes = Buffer.byteLength(emittedText, "utf8");
|
|
5378
5384
|
const bytesSaved = Math.max(1, fullSourceBytes - emittedBytes);
|
|
5379
|
-
recordStat(kind, bytesSaved,
|
|
5385
|
+
recordStat(kind, bytesSaved, savedTokensFromBytes(bytesSaved), void 0, detail);
|
|
5380
5386
|
}
|
|
5381
5387
|
function findSpecSeparator(spec) {
|
|
5382
5388
|
return spec.lastIndexOf("::");
|
|
@@ -5659,10 +5665,21 @@ function runSymbol(opts) {
|
|
|
5659
5665
|
if (opts.name !== void 0 && emptyIndexRoot !== null) {
|
|
5660
5666
|
const rootDir = emptyIndexRoot;
|
|
5661
5667
|
const rawSymbols = querySymbols({ limit: FIND_SCAN_LIMIT, rootDir });
|
|
5662
|
-
const
|
|
5663
|
-
|
|
5668
|
+
const exactMatches = rawSymbols.filter((s) => s.name === opts.name);
|
|
5669
|
+
if (exactMatches.length > 0) {
|
|
5670
|
+
const shown = exactMatches.slice(0, DIDYOUMEAN_LIMIT);
|
|
5671
|
+
const where = shown.map((s) => `${s.kind} at ${toDisplayPath(rootDir, s.filePath)}:${s.lineStart}`).join("; ");
|
|
5672
|
+
const more = exactMatches.length > shown.length ? ` (+${exactMatches.length - shown.length} more)` : "";
|
|
5673
|
+
const flags = [opts.kind !== void 0 ? "--kind" : null, opts.file !== void 0 ? "--file" : null].filter((f) => f !== null);
|
|
5674
|
+
const widen = flags.length > 0 ? `drop ${flags.join("/")} to see it` : "widen the search scope to see it";
|
|
5675
|
+
text2 += `
|
|
5676
|
+
'${opts.name}' IS indexed (${where}${more}) -- ${widen}`;
|
|
5677
|
+
} else {
|
|
5678
|
+
const candidates = rankSimilarNames(rawSymbols.map((s) => s.name), opts.name);
|
|
5679
|
+
text2 += candidates.length > 0 ? `
|
|
5664
5680
|
${didYouMean(candidates)}` : indexEmpty ? "" : `
|
|
5665
5681
|
Try: token-goat semantic "${opts.name}"`;
|
|
5682
|
+
}
|
|
5666
5683
|
const structuredFiles = [...new Set(rawSymbols.map((s) => s.filePath))].sort();
|
|
5667
5684
|
const hit = findStructuredKeyPath(opts.name, structuredFiles);
|
|
5668
5685
|
if (hit !== null) {
|
|
@@ -5936,9 +5953,11 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
|
5936
5953
|
let candidates = querySymbols({ name: lookupName, filePath: resolved, limit: 10 });
|
|
5937
5954
|
if (candidates.length === 0) {
|
|
5938
5955
|
const foldedFile = foldPath(file);
|
|
5956
|
+
const baseName = file.slice(Math.max(file.lastIndexOf("/"), file.lastIndexOf("\\")) + 1);
|
|
5939
5957
|
candidates = querySymbols({
|
|
5940
5958
|
name: lookupName,
|
|
5941
5959
|
limit: 50,
|
|
5960
|
+
...baseName !== "" ? { fileBaseName: baseName } : {},
|
|
5942
5961
|
...projectRoot !== void 0 ? { rootDir: projectRoot } : {}
|
|
5943
5962
|
}).filter((s) => {
|
|
5944
5963
|
const foldedFilePath = foldPath(s.filePath);
|
|
@@ -6072,6 +6091,12 @@ ${sub.text}`);
|
|
|
6072
6091
|
function resolveAgainstProjectRoot(file, projectRoot) {
|
|
6073
6092
|
return projectRoot !== void 0 && !path5.isAbsolute(file) ? path5.resolve(projectRoot, file) : file;
|
|
6074
6093
|
}
|
|
6094
|
+
function literalHeadingExists(filePath, heading) {
|
|
6095
|
+
const ordinalMatch = /^([^#\r\n]+)#(\d+)$/.exec(heading);
|
|
6096
|
+
const base = (ordinalMatch?.[1] ?? heading).trim().toLowerCase();
|
|
6097
|
+
if (base.length === 0) return false;
|
|
6098
|
+
return listSections(filePath, readFileText).some((h) => h.trim().toLowerCase() === base);
|
|
6099
|
+
}
|
|
6075
6100
|
function runSection(opts) {
|
|
6076
6101
|
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
6077
6102
|
if (crossFilePairs !== null) return runSectionCrossFile(crossFilePairs, opts);
|
|
@@ -6082,7 +6107,7 @@ function runSection(opts) {
|
|
|
6082
6107
|
const specFilePath = opts.spec.slice(0, colonIdx);
|
|
6083
6108
|
const filePath = resolveAgainstProjectRoot(specFilePath, opts.projectRoot);
|
|
6084
6109
|
const heading = opts.spec.slice(colonIdx + 2);
|
|
6085
|
-
if (heading.includes(",")) {
|
|
6110
|
+
if (heading.includes(",") && !literalHeadingExists(filePath, heading)) {
|
|
6086
6111
|
const multiHeadings = heading.split(",").map((h) => h.trim()).filter((h) => h.length > 0);
|
|
6087
6112
|
if (multiHeadings.length > 1) return runSectionMulti(specFilePath, filePath, multiHeadings, opts);
|
|
6088
6113
|
}
|
|
@@ -6516,14 +6541,10 @@ function formatStatsSuffix(refCounts, sym) {
|
|
|
6516
6541
|
return refCounts !== void 0 ? ` [${countNoun(refCounts.get(sym.name) ?? 0, "ref")}, ${hasRealDocstring(sym.docstring) ? "documented" : "undocumented"}]` : "";
|
|
6517
6542
|
}
|
|
6518
6543
|
function filteredToEmptyNotice(preFilterCount, minLines, grep) {
|
|
6519
|
-
const plural = preFilterCount === 1 ? "symbol" : "symbols";
|
|
6520
6544
|
const parts = [];
|
|
6521
6545
|
if (minLines !== void 0) parts.push(`--min-lines ${minLines}`);
|
|
6522
6546
|
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)`;
|
|
6547
|
+
return filtersFilteredToEmptyNotice(preFilterCount, parts, "indexed symbol", "indexed symbols", "the file is indexed");
|
|
6527
6548
|
}
|
|
6528
6549
|
function prepareSymbolListing(file, opts) {
|
|
6529
6550
|
const resolved = resolveIndexPath(file, opts.projectRoot ?? process.cwd());
|
|
@@ -6632,6 +6653,12 @@ function runSkeleton(opts) {
|
|
|
6632
6653
|
recordReadStat("stub_view", fullSourceBytes, text, opts.file);
|
|
6633
6654
|
return { text, code: 0 };
|
|
6634
6655
|
}
|
|
6656
|
+
var DOC_SUMMARY_MAX_CHARS = 140;
|
|
6657
|
+
function clipDocSummary(firstLine) {
|
|
6658
|
+
if (firstLine.length <= DOC_SUMMARY_MAX_CHARS) return firstLine;
|
|
6659
|
+
const cut = firstLine.lastIndexOf(" ", DOC_SUMMARY_MAX_CHARS);
|
|
6660
|
+
return `${firstLine.slice(0, cut > 40 ? cut : DOC_SUMMARY_MAX_CHARS).trimEnd()}\u2026`;
|
|
6661
|
+
}
|
|
6635
6662
|
function runOutline(opts) {
|
|
6636
6663
|
const multiFiles = parseMultiFileSpec(opts.file);
|
|
6637
6664
|
if (multiFiles !== null) return runPerFileListing(multiFiles, (file) => runOutline({ ...opts, file, includeFilePath: true }), opts.json === true);
|
|
@@ -6669,7 +6696,7 @@ function runOutline(opts) {
|
|
|
6669
6696
|
const rangeStr = `${sym.lineStart.toString().padStart(4)}-${sym.lineEnd.toString().padEnd(6)}`;
|
|
6670
6697
|
const kindStr = sym.kind.padEnd(14);
|
|
6671
6698
|
const bodyLen = sym.lineEnd - sym.lineStart + 1;
|
|
6672
|
-
const docFirst = hasRealDocstring(sym.docstring) ? ` # ${sym.docstring.split("\n")[0] ?? ""}` : "";
|
|
6699
|
+
const docFirst = hasRealDocstring(sym.docstring) ? ` # ${clipDocSummary(sym.docstring.split("\n")[0] ?? "")}` : "";
|
|
6673
6700
|
const statsStr = formatStatsSuffix(refCounts, sym);
|
|
6674
6701
|
lines.push(` ${rangeStr} ${kindStr} ${sym.name} (${bodyLen}\u2113)${docFirst}${statsStr}`);
|
|
6675
6702
|
}
|
|
@@ -6709,11 +6736,11 @@ function runCsvQuery(opts) {
|
|
|
6709
6736
|
const rowsJson = result.rows.map((r) => Object.fromEntries(result.header.map((h, i) => [h, r[i]])));
|
|
6710
6737
|
const headTruncated = result.rows.length < result.totalRows;
|
|
6711
6738
|
const capped = guardJsonRows(rowsJson);
|
|
6712
|
-
const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount: result.totalRows });
|
|
6739
|
+
const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount: result.totalRows, ...result.totalRows === 0 && result.preFilterRows > 0 ? { filteredFromRows: result.preFilterRows } : {} });
|
|
6713
6740
|
emit(jsonText);
|
|
6714
6741
|
recordReadStat("csv_query", fullSourceBytes, jsonText, opts.file);
|
|
6715
6742
|
} else {
|
|
6716
|
-
const tableText = formatCsvTable(result);
|
|
6743
|
+
const tableText = formatCsvTable(result, (opts.where ?? []).map((w) => `--where ${w}`));
|
|
6717
6744
|
emit(tableText);
|
|
6718
6745
|
recordReadStat("csv_query", fullSourceBytes, tableText, opts.file);
|
|
6719
6746
|
}
|
|
@@ -6856,6 +6883,7 @@ function resolveYamlMergeKeys(node) {
|
|
|
6856
6883
|
}
|
|
6857
6884
|
function parseYamlDocument(text) {
|
|
6858
6885
|
const docs = loadAll(text).map(resolveYamlMergeKeys);
|
|
6886
|
+
if (docs.length === 0) return null;
|
|
6859
6887
|
return docs.length === 1 ? docs[0] : docs;
|
|
6860
6888
|
}
|
|
6861
6889
|
function runYamlOutline(opts) {
|
|
@@ -7439,6 +7467,9 @@ async function runImageText(file) {
|
|
|
7439
7467
|
const data = fs5.readFileSync(file);
|
|
7440
7468
|
const ocr = await ocrImage(data);
|
|
7441
7469
|
if (ocr === null) {
|
|
7470
|
+
if (ocrIntegrityFailed()) {
|
|
7471
|
+
throw new Error(`${file} was not OCRed: the cached language model failed its checksum and was discarded, so the next run re-downloads it`);
|
|
7472
|
+
}
|
|
7442
7473
|
if (isOcrEngineAvailable()) {
|
|
7443
7474
|
throw new Error(`${file} could not be processed by OCR (unreadable image, timeout, or offline model fetch)`);
|
|
7444
7475
|
}
|
|
@@ -7489,15 +7520,14 @@ ${emptyIndexMessage(rootDir2)}`, code: 1 };
|
|
|
7489
7520
|
const match = resolution.entry;
|
|
7490
7521
|
const rootDir = resolveProjectRoot({ project: opts.projectRoot ?? process.cwd() });
|
|
7491
7522
|
const excludeTests = opts.excludeTests === true;
|
|
7492
|
-
const
|
|
7493
|
-
const allCallers = resolveCallers(match.name, void 0, match.filePath, rootDir, unboundedQuery);
|
|
7523
|
+
const allCallers = resolveCallers(match.name, void 0, match.filePath, rootDir, true);
|
|
7494
7524
|
const testFiltered = excludeTests ? allCallers.filter((c) => !isTestFile(c.file)) : allCallers;
|
|
7495
7525
|
const hiddenByExcludeTests = excludeTests ? allCallers.length - testFiltered.length : 0;
|
|
7496
7526
|
const preGrepCount = testFiltered.length;
|
|
7497
7527
|
const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
|
|
7498
7528
|
const callers = matchesGrep !== void 0 ? testFiltered.filter((c) => matchesGrep(c.caller)) : testFiltered;
|
|
7499
7529
|
const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - callers.length : 0;
|
|
7500
|
-
const totalCallers =
|
|
7530
|
+
const totalCallers = callers.length;
|
|
7501
7531
|
const section = findContainingSection(match.filePath, match.lineStart, match.lineEnd, readFileText);
|
|
7502
7532
|
const limit = opts.limit ?? 20;
|
|
7503
7533
|
const shown = callers.slice(0, limit);
|
|
@@ -9146,10 +9176,14 @@ function bfsCallChains(start, callersOf, maxDepth) {
|
|
|
9146
9176
|
const tip = chain[chain.length - 1];
|
|
9147
9177
|
if (tip === void 0) continue;
|
|
9148
9178
|
const callers = callersOf(tip);
|
|
9149
|
-
if (callers.length === 0
|
|
9179
|
+
if (callers.length === 0) {
|
|
9150
9180
|
complete.push(chain);
|
|
9151
9181
|
continue;
|
|
9152
9182
|
}
|
|
9183
|
+
if (chain.length > maxDepth) {
|
|
9184
|
+
complete.push([...chain, "(depth-limit)"]);
|
|
9185
|
+
continue;
|
|
9186
|
+
}
|
|
9153
9187
|
let expanded = false;
|
|
9154
9188
|
for (const caller of callers) {
|
|
9155
9189
|
if (chain.includes(caller)) {
|
|
@@ -9235,8 +9269,11 @@ function runCallers(opts) {
|
|
|
9235
9269
|
emitErr2(`Symbol '${name}' not found in '${file}'`);
|
|
9236
9270
|
return 1;
|
|
9237
9271
|
}
|
|
9238
|
-
const unbounded = opts.excludeTests === true || opts.grep !== void 0;
|
|
9239
|
-
const
|
|
9272
|
+
const unbounded = opts.excludeTests === true || opts.grep !== void 0 || fileHint !== void 0;
|
|
9273
|
+
const requestedLimit = opts.limit ?? DEFAULT_REF_QUERY_LIMIT;
|
|
9274
|
+
const probed = resolveCallers(name, unbounded ? opts.limit : requestedLimit + 1, fileHint, rootDir, unbounded);
|
|
9275
|
+
const sqlTruncated = !unbounded && probed.length > requestedLimit;
|
|
9276
|
+
const resolved = sqlTruncated ? probed.slice(0, requestedLimit) : probed;
|
|
9240
9277
|
const suppressed = opts.excludeTests === true ? resolved.filter((e) => isTestFile(e.file)).length : 0;
|
|
9241
9278
|
let filtered = opts.excludeTests === true ? resolved.filter((e) => !isTestFile(e.file)) : resolved;
|
|
9242
9279
|
const preGrepCount = filtered.length;
|
|
@@ -9277,7 +9314,7 @@ function runCallers(opts) {
|
|
|
9277
9314
|
return { ...e, file: displayPath, filePath: displayPath };
|
|
9278
9315
|
});
|
|
9279
9316
|
const capped = guardJsonRows(rows);
|
|
9280
|
-
const limitTruncated = entries.length < filtered.length;
|
|
9317
|
+
const limitTruncated = entries.length < filtered.length || sqlTruncated;
|
|
9281
9318
|
const hiddenByGrep = preGrepCount - filtered.length;
|
|
9282
9319
|
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
9320
|
return 0;
|
|
@@ -9316,7 +9353,7 @@ function runCallChain(opts) {
|
|
|
9316
9353
|
const getSyms = buildFileSymCache();
|
|
9317
9354
|
let suppressedCount = 0;
|
|
9318
9355
|
const callersOf = (n) => {
|
|
9319
|
-
const refs = queryRefs({ name: n, limit:
|
|
9356
|
+
const refs = queryRefs({ name: n, limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
9320
9357
|
if (refs.length === 0) return [];
|
|
9321
9358
|
const scoped = fileHint !== void 0 && n === name ? filterRefsForSymbol(refs, n, fileHint, getSyms) : refs;
|
|
9322
9359
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -9379,12 +9416,16 @@ function runImpact(opts) {
|
|
|
9379
9416
|
const hops = /* @__PURE__ */ new Map([[rootName, 0]]);
|
|
9380
9417
|
const queue = [[rootName, 0]];
|
|
9381
9418
|
let suppressedCount = 0;
|
|
9419
|
+
let depthCapped = 0;
|
|
9382
9420
|
while (queue.length > 0) {
|
|
9383
9421
|
const item = queue.shift();
|
|
9384
9422
|
if (item === void 0) break;
|
|
9385
9423
|
const [name, depth] = item;
|
|
9386
|
-
if (depth >= DEPTH_CAP)
|
|
9387
|
-
|
|
9424
|
+
if (depth >= DEPTH_CAP) {
|
|
9425
|
+
depthCapped += 1;
|
|
9426
|
+
continue;
|
|
9427
|
+
}
|
|
9428
|
+
const refs = queryRefs({ name, limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
9388
9429
|
const scoped = fileHint !== void 0 && name === rootName ? filterRefsForSymbol(refs, name, fileHint, getSyms) : refs;
|
|
9389
9430
|
for (const ref of scoped) {
|
|
9390
9431
|
if (opts.excludeTests === true && isTestFile(ref.filePath)) {
|
|
@@ -9412,6 +9453,12 @@ function runImpact(opts) {
|
|
|
9412
9453
|
const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
|
|
9413
9454
|
const grepped = matchesGrep !== void 0 ? allSorted.filter(([symbol]) => matchesGrep(symbol)) : allSorted;
|
|
9414
9455
|
const sorted = grepped.slice(0, top);
|
|
9456
|
+
if (depthCapped > 0) {
|
|
9457
|
+
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.`);
|
|
9458
|
+
}
|
|
9459
|
+
if (grepped.length > sorted.length) {
|
|
9460
|
+
emitErr2(`Showing top ${sorted.length} of ${grepped.length} impacted symbols (raise --top to see the rest).`);
|
|
9461
|
+
}
|
|
9415
9462
|
if (matchesGrep !== void 0 && sorted.length === 0 && allSorted.length > 0) {
|
|
9416
9463
|
if (opts.json === true) {
|
|
9417
9464
|
emitErr2(grepFilteredToEmptyNotice(allSorted.length, opts.grep, "impacted symbol", "impacted symbols"));
|
|
@@ -9541,14 +9588,17 @@ function runDead(opts) {
|
|
|
9541
9588
|
}
|
|
9542
9589
|
return 1;
|
|
9543
9590
|
}
|
|
9544
|
-
const syms = kinds.flatMap((k) => querySymbols({ kind: k, limit:
|
|
9591
|
+
const syms = kinds.flatMap((k) => querySymbols({ kind: k, limit: UNBOUNDED_REF_LIMIT, rootDir }));
|
|
9545
9592
|
const getSyms = buildFileSymCache();
|
|
9546
9593
|
const results = [];
|
|
9547
9594
|
let suppressed = 0;
|
|
9548
9595
|
for (const sym of syms) {
|
|
9549
9596
|
if (opts.includePrivate !== true && sym.name.startsWith("_")) continue;
|
|
9550
9597
|
const refs = queryRefs({ name: sym.name, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
9551
|
-
|
|
9598
|
+
let scoped = filterRefsForSymbol(refs, sym.name, sym.filePath, getSyms);
|
|
9599
|
+
if (scoped.length === 0 && refs.length >= DEFAULT_REF_QUERY_LIMIT) {
|
|
9600
|
+
scoped = filterRefsForSymbol(queryRefs({ name: sym.name, limit: UNBOUNDED_REF_LIMIT, rootDir }), sym.name, sym.filePath, getSyms);
|
|
9601
|
+
}
|
|
9552
9602
|
if (!isDeadSymbol(sym.name, scoped.length)) continue;
|
|
9553
9603
|
if (sym.kind === "method") {
|
|
9554
9604
|
const ownScope = enclosingNamedScope(getSyms(sym.filePath), sym.lineStart);
|
|
@@ -9883,7 +9933,7 @@ function runScope(opts) {
|
|
|
9883
9933
|
}
|
|
9884
9934
|
const filePath = resolveIndexPath(file);
|
|
9885
9935
|
const syms = querySymbols({ filePath, limit: ALL_SYMBOLS_IN_FILE_LIMIT });
|
|
9886
|
-
const enclosing = syms.filter((s) => s.lineStart <= line && line <= s.lineEnd).sort((a, b) => b.lineStart - a.lineStart);
|
|
9936
|
+
const enclosing = syms.filter((s) => s.lineStart <= line && line <= s.lineEnd).sort((a, b) => b.lineStart - a.lineStart || a.lineEnd - b.lineEnd || a.name.localeCompare(b.name, "en"));
|
|
9887
9937
|
if (syms.length === 0) {
|
|
9888
9938
|
if (!fs6.existsSync(filePath)) {
|
|
9889
9939
|
emitErr2(`Could not read: ${file}`);
|
|
@@ -9967,17 +10017,18 @@ function canonicalCycleKey(cyclePath) {
|
|
|
9967
10017
|
}
|
|
9968
10018
|
return [...nodes.slice(minIdx), ...nodes.slice(0, minIdx)].join(" ");
|
|
9969
10019
|
}
|
|
9970
|
-
function
|
|
10020
|
+
function findCyclesCapped(graph) {
|
|
9971
10021
|
const cycles = [];
|
|
9972
10022
|
const seen = /* @__PURE__ */ new Set();
|
|
9973
10023
|
const sccs = tarjanSCCs(graph);
|
|
10024
|
+
const probeLimit = MAX_CYCLES + 1;
|
|
9974
10025
|
for (const component of sccs) {
|
|
9975
10026
|
let dfs2 = function(start, node, pathSoFar) {
|
|
9976
|
-
if (cycles.length >=
|
|
10027
|
+
if (cycles.length >= probeLimit) return;
|
|
9977
10028
|
stack.add(node);
|
|
9978
10029
|
for (const nb of graph.get(node) ?? []) {
|
|
9979
10030
|
if (!sccSet.has(nb)) continue;
|
|
9980
|
-
if (cycles.length >=
|
|
10031
|
+
if (cycles.length >= probeLimit) break;
|
|
9981
10032
|
if (nb === start) {
|
|
9982
10033
|
const cyclePath = [...pathSoFar, node, start];
|
|
9983
10034
|
const key = canonicalCycleKey(cyclePath);
|
|
@@ -9992,6 +10043,7 @@ function findCycles(graph) {
|
|
|
9992
10043
|
stack.delete(node);
|
|
9993
10044
|
};
|
|
9994
10045
|
var dfs = dfs2;
|
|
10046
|
+
if (cycles.length >= probeLimit) break;
|
|
9995
10047
|
if (component.length === 1) {
|
|
9996
10048
|
const [only] = component;
|
|
9997
10049
|
if (only !== void 0 && (graph.get(only) ?? []).includes(only)) cycles.push([only, only]);
|
|
@@ -10000,11 +10052,12 @@ function findCycles(graph) {
|
|
|
10000
10052
|
const sccSet = new Set(component);
|
|
10001
10053
|
const stack = /* @__PURE__ */ new Set();
|
|
10002
10054
|
for (const start of component) {
|
|
10003
|
-
if (cycles.length >=
|
|
10055
|
+
if (cycles.length >= probeLimit) break;
|
|
10004
10056
|
dfs2(start, start, []);
|
|
10005
10057
|
}
|
|
10006
10058
|
}
|
|
10007
|
-
return cycles;
|
|
10059
|
+
if (cycles.length > MAX_CYCLES) return { cycles: cycles.slice(0, MAX_CYCLES), truncated: true };
|
|
10060
|
+
return { cycles, truncated: false };
|
|
10008
10061
|
}
|
|
10009
10062
|
function runSimilar(opts) {
|
|
10010
10063
|
if (opts.top !== void 0 && opts.top <= 0) {
|
|
@@ -10108,8 +10161,8 @@ function runCoverageGaps(opts) {
|
|
|
10108
10161
|
}
|
|
10109
10162
|
const top = opts.top ?? 50;
|
|
10110
10163
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
10111
|
-
const allFns = querySymbols({ kind: "function", limit:
|
|
10112
|
-
const allMethods = querySymbols({ kind: "method", limit:
|
|
10164
|
+
const allFns = querySymbols({ kind: "function", limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
10165
|
+
const allMethods = querySymbols({ kind: "method", limit: UNBOUNDED_REF_LIMIT, rootDir });
|
|
10113
10166
|
const candidates = [...allFns, ...allMethods];
|
|
10114
10167
|
const gaps = [];
|
|
10115
10168
|
for (const sym of candidates) {
|
|
@@ -10120,6 +10173,9 @@ function runCoverageGaps(opts) {
|
|
|
10120
10173
|
if (!hasTestRef) gaps.push({ name: sym.name, kind: sym.kind, file: sym.filePath, line: sym.lineStart });
|
|
10121
10174
|
}
|
|
10122
10175
|
const sliced = gaps.slice(0, top);
|
|
10176
|
+
if (gaps.length > sliced.length) {
|
|
10177
|
+
emitErr2(`Showing top ${sliced.length} of ${gaps.length} coverage gaps (raise --top to see the rest).`);
|
|
10178
|
+
}
|
|
10123
10179
|
if (opts.json === true) {
|
|
10124
10180
|
emit2(JSON.stringify(sliced, null, 2));
|
|
10125
10181
|
return 0;
|
|
@@ -10188,16 +10244,16 @@ function runArch(opts) {
|
|
|
10188
10244
|
}
|
|
10189
10245
|
const hubs = [...importedBy.entries()].sort((a, b) => b[1].size - a[1].size).slice(0, top).map(([f, importers]) => ({ file: f, importedBy: importers.size }));
|
|
10190
10246
|
const entryPoints = files.filter((f) => !importedBy.has(f) && (graph.get(f) ?? []).length > 0).slice(0, top).map((f) => ({ file: f }));
|
|
10191
|
-
const cycles =
|
|
10247
|
+
const { cycles, truncated: cyclesTruncated } = findCyclesCapped(graph);
|
|
10192
10248
|
if (opts.json === true) {
|
|
10193
|
-
emit2(JSON.stringify({ hubs, entryPoints, cycles }, null, 2));
|
|
10249
|
+
emit2(JSON.stringify({ hubs, entryPoints, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {} }, null, 2));
|
|
10194
10250
|
return 0;
|
|
10195
10251
|
}
|
|
10196
10252
|
emit2(`hubs (top ${top} most-imported):`);
|
|
10197
10253
|
for (const h of hubs) emit2(` ${h.importedBy} importers ${toDisplayPath(getDisplayRoot(opts.cwd), h.file)}`);
|
|
10198
10254
|
emit2(`entry points (imported by nobody, top ${top}):`);
|
|
10199
10255
|
for (const e of entryPoints) emit2(` ${toDisplayPath(getDisplayRoot(opts.cwd), e.file)}`);
|
|
10200
|
-
emit2(`cycles (${cycles.length} found):`);
|
|
10256
|
+
emit2(cyclesTruncated ? `cycles (first ${cycles.length}, truncated at the ${MAX_CYCLES}-cycle enumeration limit: more cycles exist):` : `cycles (${cycles.length} found):`);
|
|
10201
10257
|
for (const c of cycles) emit2(` ${c.map((f) => toDisplayPath(getDisplayRoot(opts.cwd), f)).join(" -> ")}`);
|
|
10202
10258
|
return 0;
|
|
10203
10259
|
}
|
|
@@ -10249,17 +10305,21 @@ function runAsk(opts) {
|
|
|
10249
10305
|
const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
|
|
10250
10306
|
const backendLabel = process.env[BACKEND_ENV] ?? "";
|
|
10251
10307
|
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 = () => {
|
|
10308
|
+
const degrade = (reason, extraNote) => {
|
|
10253
10309
|
if (opts.json === true) {
|
|
10254
|
-
emit2(JSON.stringify({ degraded: true, note:
|
|
10310
|
+
emit2(JSON.stringify({ degraded: true, note: reason, ...extraNote !== void 0 ? { hint: extraNote } : {}, context: entries }, null, 2));
|
|
10255
10311
|
return 0;
|
|
10256
10312
|
}
|
|
10257
|
-
emit2(`[degraded mode -
|
|
10313
|
+
emit2(`[degraded mode - ${reason}]`);
|
|
10314
|
+
if (extraNote !== void 0) emit2(extraNote);
|
|
10258
10315
|
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
10259
10316
|
return 0;
|
|
10260
10317
|
};
|
|
10261
|
-
if (!backendLabel) return degrade();
|
|
10262
|
-
if (hits.length === 0)
|
|
10318
|
+
if (!backendLabel) return degrade(`set ${BACKEND_ENV}=claude|codex for LLM synthesis`);
|
|
10319
|
+
if (hits.length === 0) {
|
|
10320
|
+
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));
|
|
10321
|
+
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`);
|
|
10322
|
+
}
|
|
10263
10323
|
const isWin = process.platform === "win32";
|
|
10264
10324
|
let backendPath = null;
|
|
10265
10325
|
try {
|
|
@@ -10268,7 +10328,7 @@ function runAsk(opts) {
|
|
|
10268
10328
|
if (found) backendPath = found;
|
|
10269
10329
|
} catch {
|
|
10270
10330
|
}
|
|
10271
|
-
if (!backendPath) return degrade();
|
|
10331
|
+
if (!backendPath) return degrade(`${BACKEND_ENV}=${backendLabel} is set, but '${backendLabel}' was not found on PATH`);
|
|
10272
10332
|
const context = hits.map((h, i) => `[${i + 1}] ${h.filePath}
|
|
10273
10333
|
${h.body ?? ""}`).join("\n\n");
|
|
10274
10334
|
const prompt = `Answer the QUESTION using only the CODE SNIPPETS below.
|
|
@@ -10319,7 +10379,7 @@ ANSWER:`;
|
|
|
10319
10379
|
}
|
|
10320
10380
|
}
|
|
10321
10381
|
}
|
|
10322
|
-
return degrade();
|
|
10382
|
+
return degrade(`${BACKEND_ENV}=${backendLabel} ran but returned no answer`);
|
|
10323
10383
|
}
|
|
10324
10384
|
|
|
10325
10385
|
// src/content_store.ts
|