token-goat 2.6.24 → 2.6.25
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 +24 -24
- package/dist/token-goat-hook.mjs +685 -164
- package/dist/token-goat.mjs +700 -164
- package/package.json +1 -1
package/dist/token-goat.mjs
CHANGED
|
@@ -3050,7 +3050,7 @@ var require_commander = __commonJS({
|
|
|
3050
3050
|
import { createRequire } from "node:module";
|
|
3051
3051
|
function resolveVersion() {
|
|
3052
3052
|
if (true) {
|
|
3053
|
-
return "2.6.
|
|
3053
|
+
return "2.6.25";
|
|
3054
3054
|
}
|
|
3055
3055
|
const require2 = createRequire(import.meta.url);
|
|
3056
3056
|
const pkg = require2("../package.json");
|
|
@@ -4288,7 +4288,7 @@ function resolveIndexPath(file2, base = process.cwd()) {
|
|
|
4288
4288
|
}
|
|
4289
4289
|
function toDisplayPath(root, target) {
|
|
4290
4290
|
if (root === void 0) return target;
|
|
4291
|
-
const rel = path2.relative(root, target).replace(/\\/g, "/");
|
|
4291
|
+
const rel = path2.relative(normalizePath(root), normalizePath(target)).replace(/\\/g, "/");
|
|
4292
4292
|
if (rel === "" || rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
4293
4293
|
return rel === "" ? "." : target;
|
|
4294
4294
|
}
|
|
@@ -4350,6 +4350,15 @@ function foldCase(s) {
|
|
|
4350
4350
|
function runGit(args, opts = {}) {
|
|
4351
4351
|
const subArgs = args[0] === "diff" ? [args[0], "--no-ext-diff", "--no-textconv", ...args.slice(1)] : args;
|
|
4352
4352
|
const fullArgs = [
|
|
4353
|
+
// Never take an optional lock. Every git call here is on someone else's
|
|
4354
|
+
// working repo, and a `status` that refreshes the index writes
|
|
4355
|
+
// `.git/index.lock`; if this process is killed mid-call -- which the hint
|
|
4356
|
+
// paths deliberately invite, since they spawn under a short timeout -- the
|
|
4357
|
+
// orphaned lock blocks every subsequent commit in that repo until a human
|
|
4358
|
+
// deletes it. Observed doing exactly that on 2026-08-05. `--no-optional-
|
|
4359
|
+
// locks` suppresses only locks git considers optional, so write commands
|
|
4360
|
+
// that genuinely need one are unaffected.
|
|
4361
|
+
"--no-optional-locks",
|
|
4353
4362
|
"-c",
|
|
4354
4363
|
"core.fsmonitor=",
|
|
4355
4364
|
"-c",
|
|
@@ -4673,6 +4682,28 @@ function writeIfDifferent(p, content, backup = false) {
|
|
|
4673
4682
|
atomicWriteText(p, content);
|
|
4674
4683
|
return true;
|
|
4675
4684
|
}
|
|
4685
|
+
function buildContextWindow(absPath, line, contextLines) {
|
|
4686
|
+
if (!Number.isFinite(contextLines) || contextLines <= 0) return null;
|
|
4687
|
+
let text;
|
|
4688
|
+
try {
|
|
4689
|
+
text = readFileSync2(absPath, "utf-8");
|
|
4690
|
+
} catch {
|
|
4691
|
+
return null;
|
|
4692
|
+
}
|
|
4693
|
+
const lines2 = text.split(/\r?\n/);
|
|
4694
|
+
const idx = line - 1;
|
|
4695
|
+
if (idx < 0 || idx >= lines2.length) return null;
|
|
4696
|
+
const start = Math.max(0, idx - contextLines);
|
|
4697
|
+
const end = Math.min(lines2.length - 1, idx + contextLines);
|
|
4698
|
+
const out2 = [];
|
|
4699
|
+
for (let i = start; i <= end; i++) out2.push({ line: i + 1, text: lines2[i] ?? "" });
|
|
4700
|
+
return out2;
|
|
4701
|
+
}
|
|
4702
|
+
function renderContextWindow(displayFile, matchLine3, window, matchSuffix = "", indent = "") {
|
|
4703
|
+
return window.map(
|
|
4704
|
+
(c) => c.line === matchLine3 ? `${indent}${displayFile}:${c.line}: ${c.text}${matchSuffix}` : `${indent}${displayFile}-${c.line}- ${c.text}`
|
|
4705
|
+
);
|
|
4706
|
+
}
|
|
4676
4707
|
function toKB(bytes) {
|
|
4677
4708
|
return Math.round(bytes / 1024);
|
|
4678
4709
|
}
|
|
@@ -4725,6 +4756,19 @@ function isCodeFenceDelimiter(line) {
|
|
|
4725
4756
|
function pad(s, n) {
|
|
4726
4757
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
4727
4758
|
}
|
|
4759
|
+
function installEpipeGuard(streams) {
|
|
4760
|
+
const targets = (streams ?? [process.stdout, process.stderr]).filter((s) => s !== void 0);
|
|
4761
|
+
for (const stream of targets) {
|
|
4762
|
+
stream.on("error", (err2) => {
|
|
4763
|
+
if (err2.code === "EPIPE") {
|
|
4764
|
+
process.exitCode = 0;
|
|
4765
|
+
return;
|
|
4766
|
+
}
|
|
4767
|
+
throw err2;
|
|
4768
|
+
});
|
|
4769
|
+
}
|
|
4770
|
+
return targets;
|
|
4771
|
+
}
|
|
4728
4772
|
function normalizePathForwardSlash(p, toLowerCase) {
|
|
4729
4773
|
let result = normalizePath(p).replace(/\\/g, "/");
|
|
4730
4774
|
if (toLowerCase) result = result.toLowerCase();
|
|
@@ -5056,7 +5100,8 @@ function defaultConfig() {
|
|
|
5056
5100
|
compression: getDefaultConfig("compression"),
|
|
5057
5101
|
context: getDefaultConfig("context"),
|
|
5058
5102
|
injection: getDefaultConfig("injection"),
|
|
5059
|
-
hint_stats: getDefaultConfig("hint_stats")
|
|
5103
|
+
hint_stats: getDefaultConfig("hint_stats"),
|
|
5104
|
+
semantic: getDefaultConfig("semantic")
|
|
5060
5105
|
};
|
|
5061
5106
|
}
|
|
5062
5107
|
function validatedBool(raw, def) {
|
|
@@ -5498,6 +5543,10 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
5498
5543
|
const hs = getDefaultConfig("hint_stats");
|
|
5499
5544
|
hs.suppress_threshold_pct = validatedInt(hs_raw["suppress_threshold_pct"], hs.suppress_threshold_pct, ...boundsOf("hint_stats.suppress_threshold_pct"));
|
|
5500
5545
|
hs.min_sample_size = validatedInt(hs_raw["min_sample_size"], hs.min_sample_size, ...boundsOf("hint_stats.min_sample_size"));
|
|
5546
|
+
const sem_raw = section(raw, "semantic");
|
|
5547
|
+
const sem = getDefaultConfig("semantic");
|
|
5548
|
+
sem.archive_weight = validatedFloat(sem_raw["archive_weight"], sem.archive_weight, ...boundsOf("semantic.archive_weight"));
|
|
5549
|
+
sem.docs_weight = validatedFloat(sem_raw["docs_weight"], sem.docs_weight, ...boundsOf("semantic.docs_weight"));
|
|
5501
5550
|
return {
|
|
5502
5551
|
compact_assist: ca,
|
|
5503
5552
|
bash_compress: bc,
|
|
@@ -5520,7 +5569,8 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
5520
5569
|
compression: cpr,
|
|
5521
5570
|
context: ctx,
|
|
5522
5571
|
injection: inj,
|
|
5523
|
-
hint_stats: hs
|
|
5572
|
+
hint_stats: hs,
|
|
5573
|
+
semantic: sem
|
|
5524
5574
|
};
|
|
5525
5575
|
}
|
|
5526
5576
|
function saveConfig(config2) {
|
|
@@ -5677,6 +5727,10 @@ function saveConfig(config2) {
|
|
|
5677
5727
|
hint_stats: {
|
|
5678
5728
|
suppress_threshold_pct: config2.hint_stats.suppress_threshold_pct,
|
|
5679
5729
|
min_sample_size: config2.hint_stats.min_sample_size
|
|
5730
|
+
},
|
|
5731
|
+
semantic: {
|
|
5732
|
+
archive_weight: config2.semantic.archive_weight,
|
|
5733
|
+
docs_weight: config2.semantic.docs_weight
|
|
5680
5734
|
}
|
|
5681
5735
|
};
|
|
5682
5736
|
const toml = stringify(data);
|
|
@@ -5869,6 +5923,10 @@ var init_config = __esm({
|
|
|
5869
5923
|
hint_stats: {
|
|
5870
5924
|
suppress_threshold_pct: 15,
|
|
5871
5925
|
min_sample_size: 5
|
|
5926
|
+
},
|
|
5927
|
+
semantic: {
|
|
5928
|
+
archive_weight: 0.7,
|
|
5929
|
+
docs_weight: 0.92
|
|
5872
5930
|
}
|
|
5873
5931
|
};
|
|
5874
5932
|
NUMERIC_FIELD_BOUNDS = {
|
|
@@ -5932,7 +5990,9 @@ var init_config = __esm({
|
|
|
5932
5990
|
"indexing.large_file_skip_kb": { min: 1, max: 1048576 },
|
|
5933
5991
|
"context.model_window_tokens": { min: 1e4, max: 1e7 },
|
|
5934
5992
|
"hint_stats.suppress_threshold_pct": { min: 0, max: 100 },
|
|
5935
|
-
"hint_stats.min_sample_size": { min: 1, max: 1e4 }
|
|
5993
|
+
"hint_stats.min_sample_size": { min: 1, max: 1e4 },
|
|
5994
|
+
"semantic.archive_weight": { min: 0.05, max: 1 },
|
|
5995
|
+
"semantic.docs_weight": { min: 0.05, max: 1 }
|
|
5936
5996
|
};
|
|
5937
5997
|
ENUM_FIELD_VALUES = {
|
|
5938
5998
|
"compression.profile": ["auto", "aggressive", "balanced", "minimal"],
|
|
@@ -17955,7 +18015,8 @@ function rerankHits(hits, query, topK) {
|
|
|
17955
18015
|
boost = Math.min(matches2 * _VERBATIM_TOKEN_BOOST, _MAX_VERBATIM_BOOST);
|
|
17956
18016
|
}
|
|
17957
18017
|
const penalty = _isGeneratedPath(hit.filePath) ? _GENERATED_PATH_PENALTY : 0;
|
|
17958
|
-
|
|
18018
|
+
const pathPenalty = _pathPriorityPenalty(hit.filePath);
|
|
18019
|
+
return { hit, index, adjusted: hit.distance - boost + penalty + pathPenalty };
|
|
17959
18020
|
});
|
|
17960
18021
|
scored.sort((a, b) => a.adjusted - b.adjusted || a.index - b.index);
|
|
17961
18022
|
return scored.slice(0, topK).map((entry) => ({ ...entry.hit, adjustedDistance: entry.adjusted }));
|
|
@@ -18081,11 +18142,26 @@ function _isGeneratedPath(filePath) {
|
|
|
18081
18142
|
}
|
|
18082
18143
|
return false;
|
|
18083
18144
|
}
|
|
18084
|
-
|
|
18145
|
+
function _pathPriorityPenalty(filePath) {
|
|
18146
|
+
const segments = filePath.split(/[/\\]+/);
|
|
18147
|
+
const basename22 = segments[segments.length - 1] ?? filePath;
|
|
18148
|
+
const weights = loadConfig().semantic;
|
|
18149
|
+
const isArchive = _ARCHIVE_FILE_RE.test(basename22) || segments.some((seg) => _ARCHIVE_PATH_SEGMENTS.has(seg.toLowerCase()));
|
|
18150
|
+
if (isArchive) {
|
|
18151
|
+
return 1 - weights.archive_weight;
|
|
18152
|
+
}
|
|
18153
|
+
const isDocs = _DOCS_FILE_RE.test(basename22) || segments.some((seg) => seg.toLowerCase() === _DOCS_DIR_SEGMENT);
|
|
18154
|
+
if (isDocs) {
|
|
18155
|
+
return 1 - weights.docs_weight;
|
|
18156
|
+
}
|
|
18157
|
+
return 0;
|
|
18158
|
+
}
|
|
18159
|
+
var _require3, _transformer, _transformerError, _transformerLoadAttempted, DEFAULT_MODEL, DEFAULT_DIM, QUERY_INSTRUCTION_PREFIX, _extractorCache, _pipelineFnOverride, PIPELINE_RETRY_ATTEMPTS, PIPELINE_RETRY_DELAY_MS, DEFAULT_PIPELINE_RETRY_DELAY_MS, MIN_CHUNK_CHARS, MAX_CHUNK_CHARS, DEFAULT_DISTANCE_THRESHOLD, _GENERATED_PATH_SEGMENTS, _GENERATED_PATH_PENALTY, _ARCHIVE_PATH_SEGMENTS, _ARCHIVE_FILE_RE, _DOCS_FILE_RE, _DOCS_DIR_SEGMENT, _VERBATIM_TOKEN_BOOST, _MAX_VERBATIM_BOOST, _TOKEN_RE, _MIN_TOKEN_LEN, OVER_FETCH_FACTOR, MAX_OVER_FETCH, BACKFILL_MULTIPLIER, _chunkVectorsUsable;
|
|
18085
18160
|
var init_embeddings = __esm({
|
|
18086
18161
|
"src/embeddings.ts"() {
|
|
18087
18162
|
"use strict";
|
|
18088
18163
|
init_define_import_meta_env();
|
|
18164
|
+
init_config();
|
|
18089
18165
|
init_sql_path();
|
|
18090
18166
|
init_util2();
|
|
18091
18167
|
init_reset();
|
|
@@ -18134,6 +18210,17 @@ var init_embeddings = __esm({
|
|
|
18134
18210
|
".ruff_cache"
|
|
18135
18211
|
]);
|
|
18136
18212
|
_GENERATED_PATH_PENALTY = 0.5;
|
|
18213
|
+
_ARCHIVE_PATH_SEGMENTS = /* @__PURE__ */ new Set([
|
|
18214
|
+
"archive",
|
|
18215
|
+
"archived",
|
|
18216
|
+
"old",
|
|
18217
|
+
"deprecated",
|
|
18218
|
+
"plans",
|
|
18219
|
+
"drafts"
|
|
18220
|
+
]);
|
|
18221
|
+
_ARCHIVE_FILE_RE = /(^changelog|\.bak$|\.orig$)/i;
|
|
18222
|
+
_DOCS_FILE_RE = /\.md$/i;
|
|
18223
|
+
_DOCS_DIR_SEGMENT = "docs";
|
|
18137
18224
|
_VERBATIM_TOKEN_BOOST = 0.05;
|
|
18138
18225
|
_MAX_VERBATIM_BOOST = 0.25;
|
|
18139
18226
|
_TOKEN_RE = /\w+/g;
|
|
@@ -18676,14 +18763,32 @@ function fetchTopSymbols(limit, dbPath, rootDir) {
|
|
|
18676
18763
|
try {
|
|
18677
18764
|
const db = getDb(dbPath);
|
|
18678
18765
|
const { clause, param } = projectScopeClause("file_path");
|
|
18766
|
+
const refScope = projectScopeClause("file_path");
|
|
18679
18767
|
const rows = db.prepare(
|
|
18768
|
+
// refs carry only a bare name, so a name defined N times cannot claim all N copies' references: divide by the number of same-named definitions, and keep one representative per name so a generic helper like `apply` occupies one slot instead of seven.
|
|
18680
18769
|
`SELECT file_path, name, kind, line_start, line_end, body, docstring, parent
|
|
18681
|
-
FROM
|
|
18682
|
-
|
|
18683
|
-
|
|
18770
|
+
FROM (
|
|
18771
|
+
SELECT s.file_path, s.name, s.kind, s.line_start, s.line_end, s.body, s.docstring, s.parent,
|
|
18772
|
+
COALESCE(r.ref_count, 0) * 1.0 / COUNT(*) OVER (PARTITION BY s.name) AS score,
|
|
18773
|
+
ROW_NUMBER() OVER (
|
|
18774
|
+
PARTITION BY s.name
|
|
18775
|
+
ORDER BY LENGTH(COALESCE(s.body, '')) DESC, s.file_path
|
|
18776
|
+
) AS rn
|
|
18777
|
+
FROM symbols s
|
|
18778
|
+
LEFT JOIN (
|
|
18779
|
+
SELECT name, COUNT(*) AS ref_count
|
|
18780
|
+
FROM refs
|
|
18781
|
+
WHERE ${refScope.clause}
|
|
18782
|
+
GROUP BY name
|
|
18783
|
+
) r ON r.name = s.name
|
|
18784
|
+
WHERE s.kind IN ('class', 'function', 'interface') AND ${clause}
|
|
18785
|
+
)
|
|
18786
|
+
WHERE rn = 1
|
|
18787
|
+
ORDER BY score DESC,
|
|
18788
|
+
CASE kind WHEN 'class' THEN 0 WHEN 'interface' THEN 1 ELSE 2 END,
|
|
18684
18789
|
LENGTH(COALESCE(body, '')) DESC
|
|
18685
18790
|
LIMIT ?`
|
|
18686
|
-
).all(param(rootDir), limit);
|
|
18791
|
+
).all(refScope.param(rootDir), param(rootDir), limit);
|
|
18687
18792
|
return rows.map((r) => ({
|
|
18688
18793
|
filePath: r.file_path,
|
|
18689
18794
|
name: r.name,
|
|
@@ -18735,13 +18840,12 @@ function formatProjectMap(map3, compact = false) {
|
|
|
18735
18840
|
lines2.push("");
|
|
18736
18841
|
lines2.push("## Top symbols");
|
|
18737
18842
|
for (const s of map3.topSymbols) {
|
|
18738
|
-
|
|
18739
|
-
|
|
18740
|
-
} else {
|
|
18741
|
-
const loc = `${path25.basename(s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
18742
|
-
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
18743
|
-
}
|
|
18843
|
+
const loc = `${toDisplayPath(map3.rootDir, s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
18844
|
+
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
18744
18845
|
}
|
|
18846
|
+
} else {
|
|
18847
|
+
lines2.push("");
|
|
18848
|
+
lines2.push("## Top symbols: none \u2014 no files indexed for this project; run 'token-goat index .'");
|
|
18745
18849
|
}
|
|
18746
18850
|
if (!compact && map3.recentFiles.length > 0) {
|
|
18747
18851
|
lines2.push("");
|
|
@@ -25762,6 +25866,11 @@ function isDeadSymbol(name2, refCount) {
|
|
|
25762
25866
|
if (ENTRY_NAMES.has(name2)) return false;
|
|
25763
25867
|
return refCount === 0;
|
|
25764
25868
|
}
|
|
25869
|
+
function parseGraphSymbolSpec(spec) {
|
|
25870
|
+
const colonIdx = findSpecSeparator(spec);
|
|
25871
|
+
if (colonIdx === -1) return { name: spec };
|
|
25872
|
+
return { name: spec.slice(colonIdx + 2), file: spec.slice(0, colonIdx) };
|
|
25873
|
+
}
|
|
25765
25874
|
function buildFileSymCache() {
|
|
25766
25875
|
const cache = /* @__PURE__ */ new Map();
|
|
25767
25876
|
return (fp) => {
|
|
@@ -25779,9 +25888,10 @@ function fileDefinesName(fp, name2, getSyms) {
|
|
|
25779
25888
|
function filterRefsForSymbol(refs, name2, filePath, getSyms) {
|
|
25780
25889
|
return refs.filter((ref2) => ref2.filePath === filePath || !fileDefinesName(ref2.filePath, name2, getSyms));
|
|
25781
25890
|
}
|
|
25782
|
-
function resolveCallers(name2, limit, filePath, rootDir) {
|
|
25891
|
+
function resolveCallers(name2, limit, filePath, rootDir, excludeTests) {
|
|
25783
25892
|
const resolvedRootDir = rootDir ?? resolveProjectRoot({ project: process.cwd() });
|
|
25784
|
-
const
|
|
25893
|
+
const queryLimit = excludeTests === true ? UNBOUNDED_REF_LIMIT : limit ?? 500;
|
|
25894
|
+
const refs = queryRefs({ name: name2, limit: queryLimit, rootDir: resolvedRootDir });
|
|
25785
25895
|
const getSyms = buildFileSymCache();
|
|
25786
25896
|
const scoped = filePath === void 0 ? refs : filterRefsForSymbol(refs, name2, filePath, getSyms);
|
|
25787
25897
|
return scoped.map((ref2) => {
|
|
@@ -25800,17 +25910,37 @@ function runCallers(opts) {
|
|
|
25800
25910
|
return 1;
|
|
25801
25911
|
}
|
|
25802
25912
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25803
|
-
const
|
|
25913
|
+
const { name: name2, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25914
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25915
|
+
if (fileHint !== void 0 && querySymbols({ name: name2, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25916
|
+
emitErr(`Symbol '${name2}' not found in '${file2}'`);
|
|
25917
|
+
return 1;
|
|
25918
|
+
}
|
|
25919
|
+
const resolved = resolveCallers(name2, opts.limit, fileHint, rootDir, opts.excludeTests);
|
|
25920
|
+
const suppressed = opts.excludeTests === true ? resolved.filter((e) => isTestFile(e.file)).length : 0;
|
|
25921
|
+
const entries = opts.excludeTests === true ? resolved.filter((e) => !isTestFile(e.file)).slice(0, opts.limit ?? 500) : resolved;
|
|
25804
25922
|
if (entries.length === 0) {
|
|
25923
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25924
|
+
emitErr(`No non-test references found for '${opts.symbol}' (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25925
|
+
return 1;
|
|
25926
|
+
}
|
|
25805
25927
|
emitErr(`No references found for '${opts.symbol}'`);
|
|
25806
25928
|
return 1;
|
|
25807
25929
|
}
|
|
25930
|
+
const contextLines = opts.context ?? 0;
|
|
25808
25931
|
if (opts.json === true) {
|
|
25809
|
-
|
|
25932
|
+
const payload = contextLines > 0 ? entries.map((e) => ({ ...e, contextLines: buildContextWindow(e.file, e.line, contextLines) ?? [] })) : entries;
|
|
25933
|
+
emit2(JSON.stringify(payload, null, 2));
|
|
25810
25934
|
return 0;
|
|
25811
25935
|
}
|
|
25936
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25937
|
+
emit2(`${entries.length} callers found (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25938
|
+
}
|
|
25812
25939
|
for (const e of entries) {
|
|
25813
|
-
|
|
25940
|
+
const displayPath = toDisplayPath(rootDir, e.file);
|
|
25941
|
+
emit2(`${e.caller} ${displayPath}:${e.line}`);
|
|
25942
|
+
const window = buildContextWindow(e.file, e.line, contextLines);
|
|
25943
|
+
if (window !== null) for (const l of renderContextWindow(displayPath, e.line, window, "", " ")) emit2(l);
|
|
25814
25944
|
}
|
|
25815
25945
|
return 0;
|
|
25816
25946
|
}
|
|
@@ -25821,28 +25951,36 @@ function runCallChain(opts) {
|
|
|
25821
25951
|
}
|
|
25822
25952
|
const maxDepth = opts.depth ?? 8;
|
|
25823
25953
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25824
|
-
|
|
25954
|
+
const { name: name2, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25955
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25956
|
+
if (fileHint !== void 0) {
|
|
25957
|
+
if (querySymbols({ name: name2, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25958
|
+
emitErr(`Symbol '${name2}' not found in '${file2}'`);
|
|
25959
|
+
return 1;
|
|
25960
|
+
}
|
|
25961
|
+
} else if (querySymbols({ name: name2, rootDir, limit: 1 }).length === 0) {
|
|
25825
25962
|
emitErr(`Symbol not found: ${opts.symbol}`);
|
|
25826
25963
|
return 1;
|
|
25827
25964
|
}
|
|
25828
25965
|
const getSyms = buildFileSymCache();
|
|
25829
|
-
const callersOf = (
|
|
25830
|
-
const refs = queryRefs({ name:
|
|
25966
|
+
const callersOf = (n) => {
|
|
25967
|
+
const refs = queryRefs({ name: n, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25831
25968
|
if (refs.length === 0) return [];
|
|
25969
|
+
const scoped = fileHint !== void 0 && n === name2 ? filterRefsForSymbol(refs, n, fileHint, getSyms) : refs;
|
|
25832
25970
|
const names = /* @__PURE__ */ new Set();
|
|
25833
|
-
for (const ref2 of
|
|
25971
|
+
for (const ref2 of scoped) {
|
|
25834
25972
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25835
25973
|
if (enc !== null) names.add(enc.name);
|
|
25836
25974
|
}
|
|
25837
25975
|
return [...names];
|
|
25838
25976
|
};
|
|
25839
|
-
const chains = bfsCallChains(
|
|
25977
|
+
const chains = bfsCallChains(name2, callersOf, maxDepth);
|
|
25840
25978
|
if (opts.json === true) {
|
|
25841
25979
|
emit2(JSON.stringify({ chains }, null, 2));
|
|
25842
25980
|
return 0;
|
|
25843
25981
|
}
|
|
25844
|
-
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] ===
|
|
25845
|
-
emit2(`${
|
|
25982
|
+
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] === name2) {
|
|
25983
|
+
emit2(`${name2} (no callers)`);
|
|
25846
25984
|
return 0;
|
|
25847
25985
|
}
|
|
25848
25986
|
for (const chain2 of chains) {
|
|
@@ -25862,16 +26000,23 @@ function runImpact(opts) {
|
|
|
25862
26000
|
const top = opts.top ?? 20;
|
|
25863
26001
|
const DEPTH_CAP = 8;
|
|
25864
26002
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
26003
|
+
const { name: rootName, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
26004
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
26005
|
+
if (fileHint !== void 0 && querySymbols({ name: rootName, filePath: fileHint, limit: 1 }).length === 0) {
|
|
26006
|
+
emitErr(`Symbol '${rootName}' not found in '${file2}'`);
|
|
26007
|
+
return 1;
|
|
26008
|
+
}
|
|
25865
26009
|
const getSyms = buildFileSymCache();
|
|
25866
|
-
const hops = /* @__PURE__ */ new Map([[
|
|
25867
|
-
const queue = [[
|
|
26010
|
+
const hops = /* @__PURE__ */ new Map([[rootName, 0]]);
|
|
26011
|
+
const queue = [[rootName, 0]];
|
|
25868
26012
|
while (queue.length > 0) {
|
|
25869
26013
|
const item = queue.shift();
|
|
25870
26014
|
if (item === void 0) break;
|
|
25871
26015
|
const [name2, depth] = item;
|
|
25872
26016
|
if (depth >= DEPTH_CAP) continue;
|
|
25873
26017
|
const refs = queryRefs({ name: name2, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25874
|
-
|
|
26018
|
+
const scoped = fileHint !== void 0 && name2 === rootName ? filterRefsForSymbol(refs, name2, fileHint, getSyms) : refs;
|
|
26019
|
+
for (const ref2 of scoped) {
|
|
25875
26020
|
const newHop = depth + 1;
|
|
25876
26021
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25877
26022
|
if (enc === null) {
|
|
@@ -25888,7 +26033,7 @@ function runImpact(opts) {
|
|
|
25888
26033
|
}
|
|
25889
26034
|
}
|
|
25890
26035
|
}
|
|
25891
|
-
hops.delete(
|
|
26036
|
+
hops.delete(rootName);
|
|
25892
26037
|
const sorted = [...hops.entries()].sort(compareHopEntries).slice(0, top);
|
|
25893
26038
|
if (sorted.length === 0) {
|
|
25894
26039
|
emitErr(`No callers found for '${opts.symbol}'`);
|
|
@@ -25940,6 +26085,7 @@ function runDead(opts) {
|
|
|
25940
26085
|
const syms = querySymbols({ kind, limit: 5e3, rootDir });
|
|
25941
26086
|
const getSyms = buildFileSymCache();
|
|
25942
26087
|
const results = [];
|
|
26088
|
+
let suppressed = 0;
|
|
25943
26089
|
for (const sym of syms) {
|
|
25944
26090
|
if (opts.includePrivate !== true && sym.name.startsWith("_")) continue;
|
|
25945
26091
|
const refs = queryRefs({ name: sym.name, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
@@ -25949,6 +26095,10 @@ function runDead(opts) {
|
|
|
25949
26095
|
const ownScope = enclosingNamedScope(getSyms(sym.filePath), sym.lineStart);
|
|
25950
26096
|
if (ownScope !== null && hasAncestorDispatchRef(sym.name, ownScope.name, sym.filePath, rootDir)) continue;
|
|
25951
26097
|
}
|
|
26098
|
+
if (opts.excludeTests === true && isTestFile(sym.filePath)) {
|
|
26099
|
+
suppressed += 1;
|
|
26100
|
+
continue;
|
|
26101
|
+
}
|
|
25952
26102
|
results.push({ name: sym.name, kind: sym.kind, file: sym.filePath, line: sym.lineStart });
|
|
25953
26103
|
}
|
|
25954
26104
|
const sliced = results.slice(0, opts.top ?? results.length);
|
|
@@ -25957,9 +26107,16 @@ function runDead(opts) {
|
|
|
25957
26107
|
return 0;
|
|
25958
26108
|
}
|
|
25959
26109
|
if (sliced.length === 0) {
|
|
25960
|
-
|
|
26110
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
26111
|
+
emit2(`No dead symbols found (${suppressed} in test files hidden by --exclude-tests).`);
|
|
26112
|
+
} else {
|
|
26113
|
+
emit2("No dead symbols found.");
|
|
26114
|
+
}
|
|
25961
26115
|
return 0;
|
|
25962
26116
|
}
|
|
26117
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
26118
|
+
emit2(`${sliced.length} dead symbols (${suppressed} in test files hidden by --exclude-tests)`);
|
|
26119
|
+
}
|
|
25963
26120
|
for (const r of sliced) {
|
|
25964
26121
|
emit2(`${r.name} ${toDisplayPath(rootDir, r.file)}:${r.line}`);
|
|
25965
26122
|
}
|
|
@@ -26209,16 +26366,9 @@ function runSimilar(opts) {
|
|
|
26209
26366
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
26210
26367
|
return 1;
|
|
26211
26368
|
}
|
|
26212
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
26213
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
26214
26369
|
const top = opts.top ?? 10;
|
|
26215
|
-
const
|
|
26216
|
-
|
|
26217
|
-
if (anchors.length === 0) {
|
|
26218
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
26219
|
-
return 1;
|
|
26220
|
-
}
|
|
26221
|
-
const anchor = anchors[0];
|
|
26370
|
+
const anchor = resolveSymbolSpecOrEmitError("similar", opts.spec, void 0);
|
|
26371
|
+
if (anchor === null) return 1;
|
|
26222
26372
|
const words = [anchor.name, ...(anchor.docstring ?? "").split(/\s+/).filter((w) => w.length > 4)];
|
|
26223
26373
|
const query = words.slice(0, 8).join(" ");
|
|
26224
26374
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
@@ -26250,13 +26400,13 @@ function runContextFor(opts) {
|
|
|
26250
26400
|
const bodyTokens = estimateTokens(h.body ?? "");
|
|
26251
26401
|
if (budget !== void 0 && tokensSoFar + bodyTokens > budget) continue;
|
|
26252
26402
|
tokensSoFar += bodyTokens;
|
|
26253
|
-
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, readCmd: `token-goat read "${h.filePath}::${h.name}"` });
|
|
26403
|
+
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, line: h.lineStart, readCmd: `token-goat read "${h.filePath}::${h.name}@${h.lineStart}"` });
|
|
26254
26404
|
}
|
|
26255
26405
|
if (opts.json === true) {
|
|
26256
26406
|
emit2(JSON.stringify(entries, null, 2));
|
|
26257
26407
|
return 0;
|
|
26258
26408
|
}
|
|
26259
|
-
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}"`);
|
|
26409
|
+
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
26260
26410
|
return 0;
|
|
26261
26411
|
}
|
|
26262
26412
|
function runTestFor(opts) {
|
|
@@ -26401,16 +26551,10 @@ function runBlame(opts) {
|
|
|
26401
26551
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
26402
26552
|
return 1;
|
|
26403
26553
|
}
|
|
26404
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
26405
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
26406
26554
|
const cwd = opts.cwd ?? process.cwd();
|
|
26407
|
-
const
|
|
26408
|
-
|
|
26409
|
-
|
|
26410
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
26411
|
-
return 1;
|
|
26412
|
-
}
|
|
26413
|
-
const sym = syms[0];
|
|
26555
|
+
const sym = resolveSymbolSpecOrEmitError("blame", opts.spec, void 0);
|
|
26556
|
+
if (sym === null) return 1;
|
|
26557
|
+
const filePath = sym.filePath;
|
|
26414
26558
|
const start = sym.lineStart;
|
|
26415
26559
|
const end = sym.lineEnd;
|
|
26416
26560
|
let raw;
|
|
@@ -26431,10 +26575,10 @@ function runBlame(opts) {
|
|
|
26431
26575
|
if (!m) return { raw: l };
|
|
26432
26576
|
return { commit: m[1], author: (m[2] ?? "").trim(), date: (m[3] ?? "").trim(), line: Number.parseInt(m[4] ?? "0", 10), content: m[5] };
|
|
26433
26577
|
});
|
|
26434
|
-
emit2(JSON.stringify({ symbol:
|
|
26578
|
+
emit2(JSON.stringify({ symbol: sym.name, file: filePath, lines: lines2 }, null, 2));
|
|
26435
26579
|
return 0;
|
|
26436
26580
|
}
|
|
26437
|
-
emit2(`${
|
|
26581
|
+
emit2(`${sym.name} ${toDisplayPath(getDisplayRoot(opts.cwd), filePath)}:${start}-${end}`);
|
|
26438
26582
|
emit2(raw.trim());
|
|
26439
26583
|
return 0;
|
|
26440
26584
|
}
|
|
@@ -26448,14 +26592,14 @@ function runAsk(opts) {
|
|
|
26448
26592
|
const hits = searchSymbolsFts(opts.question, top, void 0, rootDir);
|
|
26449
26593
|
const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
|
|
26450
26594
|
const backendLabel = process.env[BACKEND_ENV] ?? "";
|
|
26451
|
-
const entries = hits.map((h) => ({ file: h.filePath, symbol: h.name, kind: h.kind, readCmd: `token-goat read "${h.filePath}::${h.name}"` }));
|
|
26595
|
+
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}"` }));
|
|
26452
26596
|
const degrade = () => {
|
|
26453
26597
|
if (opts.json === true) {
|
|
26454
26598
|
emit2(JSON.stringify({ degraded: true, note: `Set ${BACKEND_ENV}=claude|codex for LLM synthesis`, context: entries }, null, 2));
|
|
26455
26599
|
return 0;
|
|
26456
26600
|
}
|
|
26457
26601
|
emit2(`[degraded mode - set ${BACKEND_ENV}=claude|codex for LLM synthesis]`);
|
|
26458
|
-
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}"`);
|
|
26602
|
+
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
26459
26603
|
return 0;
|
|
26460
26604
|
};
|
|
26461
26605
|
if (!backendLabel) return degrade();
|
|
@@ -30438,6 +30582,42 @@ function formatBareNameSpecError(command, name2, projectRoot) {
|
|
|
30438
30582
|
}
|
|
30439
30583
|
return lines2.join("\n");
|
|
30440
30584
|
}
|
|
30585
|
+
function formatCrossFileLead(command, name2, excludeFilePath, projectRoot) {
|
|
30586
|
+
const rootDir = projectRoot ?? process.cwd();
|
|
30587
|
+
const matches2 = querySymbols({ name: name2, limit: 50, rootDir });
|
|
30588
|
+
const excludeResolved = resolveIndexPath(excludeFilePath, rootDir);
|
|
30589
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30590
|
+
const specs = [];
|
|
30591
|
+
for (const m of matches2) {
|
|
30592
|
+
if (foldPath(m.filePath) === foldPath(excludeResolved)) continue;
|
|
30593
|
+
const spec = `${toDisplayPath(rootDir, m.filePath)}::${m.name}`;
|
|
30594
|
+
if (seen.has(spec)) continue;
|
|
30595
|
+
seen.add(spec);
|
|
30596
|
+
specs.push(spec);
|
|
30597
|
+
}
|
|
30598
|
+
if (specs.length === 0) return "";
|
|
30599
|
+
const firstSpec = specs[0];
|
|
30600
|
+
const lines2 = [`'${name2}' is defined in ${firstSpec !== void 0 ? firstSpec.split("::")[0] : ""}`];
|
|
30601
|
+
for (const spec of specs.slice(0, DIDYOUMEAN_LIMIT)) {
|
|
30602
|
+
lines2.push(` - token-goat ${command} "${spec}"`);
|
|
30603
|
+
}
|
|
30604
|
+
if (specs.length > DIDYOUMEAN_LIMIT) {
|
|
30605
|
+
lines2.push(` (${specs.length - DIDYOUMEAN_LIMIT} more not shown)`);
|
|
30606
|
+
}
|
|
30607
|
+
return lines2.join("\n");
|
|
30608
|
+
}
|
|
30609
|
+
function resolveEnclosingSymbol(filePath, chunkStartLine) {
|
|
30610
|
+
const symbols = querySymbols({ filePath, limit: 1e5 }, globalDbPath());
|
|
30611
|
+
let best = null;
|
|
30612
|
+
for (const s of symbols) {
|
|
30613
|
+
if (s.lineStart <= chunkStartLine && chunkStartLine <= s.lineEnd) {
|
|
30614
|
+
if (best === null || s.lineEnd - s.lineStart < best.lineEnd - best.lineStart) {
|
|
30615
|
+
best = s;
|
|
30616
|
+
}
|
|
30617
|
+
}
|
|
30618
|
+
}
|
|
30619
|
+
return best === null ? null : { name: best.name, kind: best.kind };
|
|
30620
|
+
}
|
|
30441
30621
|
function trimBlankLines(lines2) {
|
|
30442
30622
|
let start = 0;
|
|
30443
30623
|
let end = lines2.length;
|
|
@@ -30533,10 +30713,21 @@ function parseCrossFileMultiSpec(spec) {
|
|
|
30533
30713
|
}
|
|
30534
30714
|
return pairs.length > 1 ? pairs : null;
|
|
30535
30715
|
}
|
|
30716
|
+
function parseMultiFileSpec(spec) {
|
|
30717
|
+
if (!spec.includes(",")) return null;
|
|
30718
|
+
if (fileExists(spec)) return null;
|
|
30719
|
+
if (findSpecSeparator(spec) !== -1) return null;
|
|
30720
|
+
const parts = spec.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
30721
|
+
return parts.length > 1 ? parts : null;
|
|
30722
|
+
}
|
|
30723
|
+
function extraFileArgsNote(command, first2, extras) {
|
|
30724
|
+
return `Note: ${extras.length} extra file argument(s) ignored (${extras.join(", ")}). ${command} reads one file, or a comma-separated list: token-goat ${command} "${[first2, ...extras].join(",")}"`;
|
|
30725
|
+
}
|
|
30536
30726
|
function parseLineRange(spec) {
|
|
30537
30727
|
const m = /^(.+)@(\d+)(?:-(\d+))?$/.exec(spec);
|
|
30538
30728
|
if (m === null) return null;
|
|
30539
30729
|
if (fileExists(spec)) return null;
|
|
30730
|
+
if (m[1].includes("::")) return null;
|
|
30540
30731
|
const start = parseInt(m[2], 10);
|
|
30541
30732
|
const end = m[3] !== void 0 ? parseInt(m[3], 10) : start;
|
|
30542
30733
|
return { file: m[1], start, end };
|
|
@@ -30595,30 +30786,49 @@ function findParentName(entry, fileSymbols) {
|
|
|
30595
30786
|
if (doc !== "" && PARENT_IDENTIFIER_RE.test(doc)) return doc;
|
|
30596
30787
|
return null;
|
|
30597
30788
|
}
|
|
30598
|
-
function formatAmbiguity(symbol3, file2, candidates, explicitRoot) {
|
|
30789
|
+
function formatAmbiguity(symbol3, file2, candidates, explicitRoot, commandName = "read") {
|
|
30599
30790
|
const multiFile = new Set(candidates.map((c) => c.filePath)).size > 1;
|
|
30600
30791
|
const displayRoot = getDisplayRoot(explicitRoot);
|
|
30601
30792
|
const lines2 = [
|
|
30602
30793
|
`Ambiguous symbol '${symbol3}' in '${file2}': ${candidates.length} definitions match. Retry with one of the qualified commands below to pick one:`
|
|
30603
30794
|
];
|
|
30604
30795
|
const fileSymCache = /* @__PURE__ */ new Map();
|
|
30605
|
-
|
|
30606
|
-
let fileSyms = fileSymCache.get(
|
|
30796
|
+
const getFileSyms = (filePath) => {
|
|
30797
|
+
let fileSyms = fileSymCache.get(filePath);
|
|
30607
30798
|
if (fileSyms === void 0) {
|
|
30608
|
-
fileSyms = querySymbols({ filePath
|
|
30609
|
-
fileSymCache.set(
|
|
30799
|
+
fileSyms = querySymbols({ filePath, limit: 1e3 });
|
|
30800
|
+
fileSymCache.set(filePath, fileSyms);
|
|
30610
30801
|
}
|
|
30611
|
-
|
|
30612
|
-
|
|
30802
|
+
return fileSyms;
|
|
30803
|
+
};
|
|
30804
|
+
const parents = candidates.map((c) => findParentName(c, getFileSyms(c.filePath)));
|
|
30805
|
+
const plainQualifiers = candidates.map((c, i) => parents[i] !== null ? `${parents[i]}.${symbol3}` : symbol3);
|
|
30806
|
+
const qualifierCounts = /* @__PURE__ */ new Map();
|
|
30807
|
+
const fileGroupSize = /* @__PURE__ */ new Map();
|
|
30808
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30809
|
+
const c = candidates[i];
|
|
30810
|
+
const key = `${c.filePath} ${plainQualifiers[i]}`;
|
|
30811
|
+
qualifierCounts.set(key, (qualifierCounts.get(key) ?? 0) + 1);
|
|
30812
|
+
fileGroupSize.set(c.filePath, (fileGroupSize.get(c.filePath) ?? 0) + 1);
|
|
30813
|
+
}
|
|
30814
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30815
|
+
const c = candidates[i];
|
|
30816
|
+
const parent = parents[i];
|
|
30817
|
+
const plainQualifier = plainQualifiers[i];
|
|
30818
|
+
const collides = (qualifierCounts.get(`${c.filePath} ${plainQualifier}`) ?? 0) > 1 || parent === null && (fileGroupSize.get(c.filePath) ?? 0) > 1;
|
|
30819
|
+
const qualifier = collides ? `${plainQualifier}@${c.lineStart}` : plainQualifier;
|
|
30613
30820
|
const retryFile = multiFile ? toDisplayPath(displayRoot, c.filePath) : file2;
|
|
30614
30821
|
const label = multiFile ? `${toDisplayPath(displayRoot, c.filePath)}::${qualifier}` : qualifier;
|
|
30615
|
-
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat
|
|
30822
|
+
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat ${commandName} "${retryFile}::${qualifier}"`);
|
|
30616
30823
|
}
|
|
30617
30824
|
return lines2.join("\n");
|
|
30618
30825
|
}
|
|
30619
30826
|
function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
30620
|
-
const { file: file2, symbol:
|
|
30621
|
-
if (
|
|
30827
|
+
const { file: file2, symbol: rawSymbol } = parseReadSpec(spec);
|
|
30828
|
+
if (rawSymbol === void 0 || rawSymbol === "") return { kind: "none" };
|
|
30829
|
+
const anchorMatch = /^(.+)@(\d+)$/.exec(rawSymbol);
|
|
30830
|
+
const symbol3 = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
|
|
30831
|
+
const lineAnchor = anchorMatch !== null ? parseInt(anchorMatch[2], 10) : void 0;
|
|
30622
30832
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
30623
30833
|
if (forceRefresh === true) {
|
|
30624
30834
|
indexFileSync(resolved, globalDbPath());
|
|
@@ -30635,9 +30845,10 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
|
30635
30845
|
seen.add(key);
|
|
30636
30846
|
distinct.push(c);
|
|
30637
30847
|
}
|
|
30638
|
-
|
|
30639
|
-
if (
|
|
30640
|
-
return { kind: "
|
|
30848
|
+
const anchored = lineAnchor === void 0 ? distinct : distinct.filter((c) => c.lineStart === lineAnchor);
|
|
30849
|
+
if (anchored.length === 0) return { kind: "none" };
|
|
30850
|
+
if (anchored.length === 1) return { kind: "ok", entry: anchored[0] };
|
|
30851
|
+
return { kind: "ambiguous", symbol: displaySymbol, file: file2, candidates: anchored };
|
|
30641
30852
|
};
|
|
30642
30853
|
if (symbol3.includes(".")) {
|
|
30643
30854
|
const exactMatch = querySymbols({ name: symbol3, filePath: resolved, limit: 10 });
|
|
@@ -30717,6 +30928,8 @@ function runRead(opts) {
|
|
|
30717
30928
|
return runLineRange({ file: file2, start: lineSpec.start, end: lineSpec.end }, opts);
|
|
30718
30929
|
}
|
|
30719
30930
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
30931
|
+
const crossFileLead = formatCrossFileLead("read", symbol3, file2, opts.projectRoot);
|
|
30932
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
30720
30933
|
const resolved = resolveIndexPath(file2, opts.projectRoot ?? process.cwd());
|
|
30721
30934
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
30722
30935
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -30777,6 +30990,8 @@ ${sub.text}`);
|
|
|
30777
30990
|
return { text, code: 1 };
|
|
30778
30991
|
}
|
|
30779
30992
|
function runSection(opts) {
|
|
30993
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
30994
|
+
if (crossFilePairs !== null) return runSectionCrossFile(crossFilePairs, opts);
|
|
30780
30995
|
const colonIdx = findSpecSeparator(opts.spec);
|
|
30781
30996
|
if (colonIdx === -1) {
|
|
30782
30997
|
return { text: `Invalid section spec \u2014 expected "file::Heading", got: ${opts.spec}`, code: 1 };
|
|
@@ -30833,6 +31048,42 @@ ${sub.text}`);
|
|
|
30833
31048
|
if (anyFound) recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
30834
31049
|
return { text, code: anyFound ? 0 : 1 };
|
|
30835
31050
|
}
|
|
31051
|
+
function runSectionCrossFile(pairs, opts) {
|
|
31052
|
+
let anyFound = false;
|
|
31053
|
+
const jsonOut = {};
|
|
31054
|
+
const textBlocks = [];
|
|
31055
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
31056
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
31057
|
+
for (const { file: file2, symbol: heading } of pairs) {
|
|
31058
|
+
const sub = runSection({ ...opts, spec: `${file2}::${heading}`, suppressStat: true });
|
|
31059
|
+
if (sub.code === 0) anyFound = true;
|
|
31060
|
+
const key = keyFor({ file: file2, symbol: heading });
|
|
31061
|
+
if (opts.json === true) {
|
|
31062
|
+
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
31063
|
+
continue;
|
|
31064
|
+
}
|
|
31065
|
+
textBlocks.push(`${key}:
|
|
31066
|
+
${sub.text}`);
|
|
31067
|
+
}
|
|
31068
|
+
const resolvePath = (f) => opts.projectRoot !== void 0 && !path48.isAbsolute(f) ? path48.resolve(opts.projectRoot, f) : f;
|
|
31069
|
+
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
31070
|
+
if (anyFound) {
|
|
31071
|
+
const fullSourceBytes = sumFileSizes(Array.from(distinctFiles, resolvePath));
|
|
31072
|
+
recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
31073
|
+
}
|
|
31074
|
+
return { text, code: anyFound ? 0 : 1 };
|
|
31075
|
+
}
|
|
31076
|
+
function renderRefLines(ref2, displayRoot, contextLines, indent = " ") {
|
|
31077
|
+
const displayPath = toDisplayPath(displayRoot, ref2.filePath);
|
|
31078
|
+
const base = `${indent}${displayPath}:${ref2.line}: ${ref2.context}`;
|
|
31079
|
+
const window = buildContextWindow(ref2.filePath, ref2.line, contextLines);
|
|
31080
|
+
if (window === null) return [base];
|
|
31081
|
+
return [base, ...renderContextWindow(displayPath, ref2.line, window, "", `${indent} `)];
|
|
31082
|
+
}
|
|
31083
|
+
function withContextLines(items, contextLines) {
|
|
31084
|
+
if (!(contextLines > 0)) return items;
|
|
31085
|
+
return items.map((r) => ({ ...r, contextLines: buildContextWindow(r.filePath, r.line, contextLines) ?? [] }));
|
|
31086
|
+
}
|
|
30836
31087
|
function applyTypedRefsTier(symName, file2, results) {
|
|
30837
31088
|
if (results.length === 0) return results;
|
|
30838
31089
|
try {
|
|
@@ -30870,6 +31121,8 @@ function runRefs(opts) {
|
|
|
30870
31121
|
emitErr2(`--top must be a positive number, got: ${opts.top}`);
|
|
30871
31122
|
return 1;
|
|
30872
31123
|
}
|
|
31124
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
31125
|
+
if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
|
|
30873
31126
|
const { file: file2, symbols } = parseMultiRefsSpec(opts.spec);
|
|
30874
31127
|
if (symbols.length <= 1) return runRefsSingle(opts);
|
|
30875
31128
|
const jsonOut = {};
|
|
@@ -30878,10 +31131,18 @@ function runRefs(opts) {
|
|
|
30878
31131
|
const refFilePaths = [];
|
|
30879
31132
|
for (const sym of symbols) {
|
|
30880
31133
|
const queryOpts = { name: sym };
|
|
30881
|
-
if (
|
|
30882
|
-
if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
31134
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31135
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30883
31136
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30884
|
-
|
|
31137
|
+
let results = applyTypedRefsTier(sym, file2, queryRefs(queryOpts));
|
|
31138
|
+
let suppressed = 0;
|
|
31139
|
+
let filteredTotal;
|
|
31140
|
+
if (opts.excludeTests === true) {
|
|
31141
|
+
const f = applyExcludeTestsFilter(results);
|
|
31142
|
+
suppressed = f.suppressed;
|
|
31143
|
+
filteredTotal = f.refs.length;
|
|
31144
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
31145
|
+
}
|
|
30885
31146
|
if (results.length > 0) anyFound = true;
|
|
30886
31147
|
refFilePaths.push(...results.map((r) => r.filePath));
|
|
30887
31148
|
if (opts.json === true) {
|
|
@@ -30889,22 +31150,85 @@ function runRefs(opts) {
|
|
|
30889
31150
|
jsonOut[sym] = topFilesJsonPayload(results, opts.top);
|
|
30890
31151
|
} else {
|
|
30891
31152
|
const capped = guardJsonRows(results);
|
|
30892
|
-
const trueTotal = countRefs(queryOpts);
|
|
30893
|
-
jsonOut[sym] = { items: capped.items, truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
31153
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
31154
|
+
jsonOut[sym] = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30894
31155
|
}
|
|
30895
31156
|
continue;
|
|
30896
31157
|
}
|
|
30897
31158
|
if (results.length === 0) {
|
|
30898
|
-
lines2.push(`${sym}: (no references found)`);
|
|
31159
|
+
lines2.push(opts.excludeTests === true && suppressed > 0 ? `${sym}: (no non-test references found; ${suppressed} in test files hidden by --exclude-tests)` : `${sym}: (no references found)`);
|
|
30899
31160
|
continue;
|
|
30900
31161
|
}
|
|
30901
31162
|
lines2.push(`${sym}:`);
|
|
30902
31163
|
if (opts.top !== void 0) {
|
|
30903
|
-
lines2.push(...renderTopFilesSummary(results, opts.top));
|
|
31164
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
30904
31165
|
} else if (opts.callers === true) {
|
|
30905
|
-
lines2.push(
|
|
31166
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31167
|
+
lines2.push(...renderCallerGroups(results, void 0, opts.context ?? 0));
|
|
30906
31168
|
} else {
|
|
30907
|
-
|
|
31169
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31170
|
+
for (const ref2 of results) lines2.push(...renderRefLines(ref2, void 0, opts.context ?? 0));
|
|
31171
|
+
}
|
|
31172
|
+
}
|
|
31173
|
+
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
31174
|
+
if (opts.json === true) {
|
|
31175
|
+
const text2 = JSON.stringify(jsonOut, null, 2);
|
|
31176
|
+
emit3(text2);
|
|
31177
|
+
if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text2, opts.spec);
|
|
31178
|
+
return anyFound ? 0 : 1;
|
|
31179
|
+
}
|
|
31180
|
+
const text = lines2.join("\n");
|
|
31181
|
+
emitGuarded(text, "symbol");
|
|
31182
|
+
if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
|
|
31183
|
+
return anyFound ? 0 : 1;
|
|
31184
|
+
}
|
|
31185
|
+
function runRefsCrossFile(pairs, opts) {
|
|
31186
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
31187
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
31188
|
+
const jsonOut = {};
|
|
31189
|
+
let anyFound = false;
|
|
31190
|
+
const lines2 = [];
|
|
31191
|
+
const refFilePaths = [];
|
|
31192
|
+
for (const { file: file2, symbol: symbol3 } of pairs) {
|
|
31193
|
+
const key = keyFor({ file: file2, symbol: symbol3 });
|
|
31194
|
+
const queryOpts = { name: symbol3 };
|
|
31195
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31196
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
31197
|
+
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31198
|
+
let results = applyTypedRefsTier(symbol3, file2, queryRefs(queryOpts));
|
|
31199
|
+
let suppressed = 0;
|
|
31200
|
+
let filteredTotal;
|
|
31201
|
+
if (opts.excludeTests === true) {
|
|
31202
|
+
const f = applyExcludeTestsFilter(results);
|
|
31203
|
+
suppressed = f.suppressed;
|
|
31204
|
+
filteredTotal = f.refs.length;
|
|
31205
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
31206
|
+
}
|
|
31207
|
+
if (results.length > 0) anyFound = true;
|
|
31208
|
+
refFilePaths.push(...results.map((r) => r.filePath));
|
|
31209
|
+
if (opts.json === true) {
|
|
31210
|
+
if (opts.top !== void 0) {
|
|
31211
|
+
jsonOut[key] = topFilesJsonPayload(results, opts.top);
|
|
31212
|
+
} else {
|
|
31213
|
+
const capped = guardJsonRows(results);
|
|
31214
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
31215
|
+
jsonOut[key] = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
31216
|
+
}
|
|
31217
|
+
continue;
|
|
31218
|
+
}
|
|
31219
|
+
if (results.length === 0) {
|
|
31220
|
+
lines2.push(opts.excludeTests === true && suppressed > 0 ? `${key}: (no non-test references found; ${suppressed} in test files hidden by --exclude-tests)` : `${key}: (no references found)`);
|
|
31221
|
+
continue;
|
|
31222
|
+
}
|
|
31223
|
+
lines2.push(`${key}:`);
|
|
31224
|
+
if (opts.top !== void 0) {
|
|
31225
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
31226
|
+
} else if (opts.callers === true) {
|
|
31227
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31228
|
+
lines2.push(...renderCallerGroups(results, void 0, opts.context ?? 0));
|
|
31229
|
+
} else {
|
|
31230
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31231
|
+
for (const ref2 of results) lines2.push(...renderRefLines(ref2, void 0, opts.context ?? 0));
|
|
30908
31232
|
}
|
|
30909
31233
|
}
|
|
30910
31234
|
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
@@ -30924,11 +31248,23 @@ function runRefsSingle(opts) {
|
|
|
30924
31248
|
const symName = symbol3 ?? file2;
|
|
30925
31249
|
const queryOpts = { name: symName };
|
|
30926
31250
|
const defFileHint = symbol3 !== void 0 ? resolveIndexPath(file2) : void 0;
|
|
30927
|
-
if (
|
|
30928
|
-
if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
31251
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31252
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30929
31253
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30930
|
-
|
|
31254
|
+
let results = applyTypedRefsTier(symName, defFileHint, queryRefs(queryOpts));
|
|
31255
|
+
let suppressed = 0;
|
|
31256
|
+
let filteredTotal;
|
|
31257
|
+
if (opts.excludeTests === true) {
|
|
31258
|
+
const f = applyExcludeTestsFilter(results);
|
|
31259
|
+
suppressed = f.suppressed;
|
|
31260
|
+
filteredTotal = f.refs.length;
|
|
31261
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
31262
|
+
}
|
|
30931
31263
|
if (results.length === 0) {
|
|
31264
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
31265
|
+
emitErr2(`No non-test references found for '${symName}' (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31266
|
+
return 1;
|
|
31267
|
+
}
|
|
30932
31268
|
emitErr2(`No references found for '${symName}'`);
|
|
30933
31269
|
return 1;
|
|
30934
31270
|
}
|
|
@@ -30939,8 +31275,8 @@ function runRefsSingle(opts) {
|
|
|
30939
31275
|
payload = topFilesJsonPayload(results, opts.top);
|
|
30940
31276
|
} else {
|
|
30941
31277
|
const capped = guardJsonRows(results);
|
|
30942
|
-
const trueTotal = countRefs(queryOpts);
|
|
30943
|
-
payload = { items: capped.items, truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
31278
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
31279
|
+
payload = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30944
31280
|
}
|
|
30945
31281
|
const text2 = JSON.stringify(payload, null, 2);
|
|
30946
31282
|
emit3(text2);
|
|
@@ -30948,21 +31284,26 @@ function runRefsSingle(opts) {
|
|
|
30948
31284
|
return 0;
|
|
30949
31285
|
}
|
|
30950
31286
|
const displayRoot = getDisplayRoot();
|
|
30951
|
-
const lines2 = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, displayRoot) : opts.callers === true ? renderCallerGroups(results, displayRoot) : results.
|
|
31287
|
+
const lines2 = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, displayRoot, suppressed) : opts.callers === true ? [...opts.excludeTests === true && suppressed > 0 ? [`${results.length} references (${suppressed} in test files hidden by --exclude-tests)`] : [], ...renderCallerGroups(results, displayRoot, opts.context ?? 0)] : [...opts.excludeTests === true && suppressed > 0 ? [`${results.length} references (${suppressed} in test files hidden by --exclude-tests)`] : [], ...results.flatMap((ref2) => renderRefLines(ref2, displayRoot, opts.context ?? 0, ""))];
|
|
30952
31288
|
const text = lines2.join("\n");
|
|
30953
31289
|
emitGuarded(text, "symbol");
|
|
30954
31290
|
recordReadStat("symbol_read", fullSourceBytes, text, symName);
|
|
30955
31291
|
return 0;
|
|
30956
31292
|
}
|
|
31293
|
+
function applyExcludeTestsFilter(refs) {
|
|
31294
|
+
const filtered = refs.filter((r) => !isTestFile(r.filePath));
|
|
31295
|
+
return { refs: filtered, suppressed: refs.length - filtered.length };
|
|
31296
|
+
}
|
|
30957
31297
|
function groupRefsByFile(refs) {
|
|
30958
31298
|
const byFile = /* @__PURE__ */ new Map();
|
|
30959
31299
|
for (const ref2 of refs) byFile.set(ref2.filePath, (byFile.get(ref2.filePath) ?? 0) + 1);
|
|
30960
31300
|
return [...byFile.entries()].map(([file2, count]) => ({ file: file2, count })).sort((a, b) => b.count - a.count || (a.file < b.file ? -1 : a.file > b.file ? 1 : 0));
|
|
30961
31301
|
}
|
|
30962
|
-
function renderTopFilesSummary(refs, topN, displayRoot) {
|
|
31302
|
+
function renderTopFilesSummary(refs, topN, displayRoot, suppressed) {
|
|
30963
31303
|
const grouped = groupRefsByFile(refs);
|
|
30964
31304
|
const shown = grouped.slice(0, topN);
|
|
30965
|
-
const
|
|
31305
|
+
const suppressedNote = suppressed !== void 0 && suppressed > 0 ? ` (${suppressed} in test files hidden by --exclude-tests)` : "";
|
|
31306
|
+
const lines2 = [`${refs.length} references across ${grouped.length} files (showing top ${shown.length})${suppressedNote}`];
|
|
30966
31307
|
for (const { file: file2, count } of shown) lines2.push(` ${count} ${toDisplayPath(displayRoot, file2)}`);
|
|
30967
31308
|
const omittedFiles = grouped.length - shown.length;
|
|
30968
31309
|
if (omittedFiles > 0) {
|
|
@@ -30976,7 +31317,7 @@ function topFilesJsonPayload(refs, topN) {
|
|
|
30976
31317
|
const shown = grouped.slice(0, topN);
|
|
30977
31318
|
return { fileCounts: shown, totalFiles: grouped.length, totalRefs: refs.length, shown: shown.length };
|
|
30978
31319
|
}
|
|
30979
|
-
function renderCallerGroups(refs, displayRoot) {
|
|
31320
|
+
function renderCallerGroups(refs, displayRoot, contextLines = 0) {
|
|
30980
31321
|
const byFile = /* @__PURE__ */ new Map();
|
|
30981
31322
|
for (const ref2 of refs) {
|
|
30982
31323
|
const bucket = byFile.get(ref2.filePath);
|
|
@@ -30988,9 +31329,12 @@ function renderCallerGroups(refs, displayRoot) {
|
|
|
30988
31329
|
}
|
|
30989
31330
|
const lines2 = [];
|
|
30990
31331
|
for (const [file2, fileRefs] of byFile) {
|
|
30991
|
-
|
|
31332
|
+
const displayPath = toDisplayPath(displayRoot, file2);
|
|
31333
|
+
lines2.push(`${displayPath}:`);
|
|
30992
31334
|
for (const ref2 of fileRefs) {
|
|
30993
31335
|
lines2.push(` :${ref2.line} ${ref2.context !== "" ? ref2.context : "(module scope)"}`);
|
|
31336
|
+
const window = buildContextWindow(file2, ref2.line, contextLines);
|
|
31337
|
+
if (window !== null) lines2.push(...renderContextWindow(displayPath, ref2.line, window, "", " "));
|
|
30994
31338
|
}
|
|
30995
31339
|
}
|
|
30996
31340
|
return lines2;
|
|
@@ -31029,7 +31373,19 @@ function prepareSymbolListing(file2, opts) {
|
|
|
31029
31373
|
const fullSourceBytes = sumFileSizes([resolved]);
|
|
31030
31374
|
return { kind: "ok", resolved, filtered, refCounts, fullSourceBytes, symbolsTruncated, trueSymbolCount };
|
|
31031
31375
|
}
|
|
31376
|
+
function runPerFileListing(files, run3) {
|
|
31377
|
+
const blocks = [];
|
|
31378
|
+
let anyOk = false;
|
|
31379
|
+
for (const file2 of files) {
|
|
31380
|
+
const r = run3(file2);
|
|
31381
|
+
if (r.code === 0) anyOk = true;
|
|
31382
|
+
blocks.push(r.text);
|
|
31383
|
+
}
|
|
31384
|
+
return { text: blocks.join("\n\n"), code: anyOk ? 0 : 1 };
|
|
31385
|
+
}
|
|
31032
31386
|
function runSkeleton(opts) {
|
|
31387
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
31388
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runSkeleton({ ...opts, file: file2 }));
|
|
31033
31389
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
31034
31390
|
if (prep.kind === "empty") {
|
|
31035
31391
|
return { text: prep.text, code: 1 };
|
|
@@ -31065,6 +31421,8 @@ function runSkeleton(opts) {
|
|
|
31065
31421
|
return { text, code: 0 };
|
|
31066
31422
|
}
|
|
31067
31423
|
function runOutline(opts) {
|
|
31424
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
31425
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runOutline({ ...opts, file: file2 }));
|
|
31068
31426
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
31069
31427
|
if (prep.kind === "empty") {
|
|
31070
31428
|
return { text: prep.text, code: 1 };
|
|
@@ -31639,10 +31997,13 @@ function runBriefCore(opts) {
|
|
|
31639
31997
|
const resolution = resolveSymbolSpec(opts.spec);
|
|
31640
31998
|
if (resolution.kind === "ambiguous") {
|
|
31641
31999
|
return {
|
|
32000
|
+
// Name the command explicitly: formatAmbiguity defaults to 'read', so brief's retry lines would otherwise tell the user to run `token-goat read`, which answers a different question than the one they asked.
|
|
31642
32001
|
text: formatAmbiguity(
|
|
31643
32002
|
resolution.symbol,
|
|
31644
32003
|
resolution.file,
|
|
31645
|
-
resolution.candidates
|
|
32004
|
+
resolution.candidates,
|
|
32005
|
+
void 0,
|
|
32006
|
+
"brief"
|
|
31646
32007
|
),
|
|
31647
32008
|
code: 1
|
|
31648
32009
|
};
|
|
@@ -31665,7 +32026,7 @@ function runBriefCore(opts) {
|
|
|
31665
32026
|
if (opts.json === true) {
|
|
31666
32027
|
const result = {
|
|
31667
32028
|
symbol: match2,
|
|
31668
|
-
callers: shown,
|
|
32029
|
+
callers: (opts.context ?? 0) > 0 ? shown.map((c) => ({ ...c, contextLines: buildContextWindow(c.file, c.line, opts.context ?? 0) ?? [] })) : shown,
|
|
31669
32030
|
totalCallers,
|
|
31670
32031
|
truncated,
|
|
31671
32032
|
section: section2
|
|
@@ -31684,7 +32045,10 @@ function runBriefCore(opts) {
|
|
|
31684
32045
|
];
|
|
31685
32046
|
lines2.push(`Callers (${totalCallers}):`);
|
|
31686
32047
|
for (const c of shown) {
|
|
31687
|
-
|
|
32048
|
+
const callerDisplayPath = toDisplayPath(rootDir, c.file);
|
|
32049
|
+
lines2.push(` ${c.caller} ${callerDisplayPath}:${c.line}`);
|
|
32050
|
+
const window = buildContextWindow(c.file, c.line, opts.context ?? 0);
|
|
32051
|
+
if (window !== null) lines2.push(...renderContextWindow(callerDisplayPath, c.line, window, "", " "));
|
|
31688
32052
|
}
|
|
31689
32053
|
if (truncated) {
|
|
31690
32054
|
lines2.push(` ...(${totalCallers - shown.length} more elided)`);
|
|
@@ -31716,11 +32080,40 @@ ${sub.text}`);
|
|
|
31716
32080
|
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
31717
32081
|
return { text, code: anyFound ? 0 : 1 };
|
|
31718
32082
|
}
|
|
32083
|
+
function runBriefCrossFile(pairs, opts) {
|
|
32084
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
32085
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
32086
|
+
let anyFound = false;
|
|
32087
|
+
const jsonOut = {};
|
|
32088
|
+
const textBlocks = [];
|
|
32089
|
+
for (const { file: file2, symbol: symbol3 } of pairs) {
|
|
32090
|
+
const key = keyFor({ file: file2, symbol: symbol3 });
|
|
32091
|
+
const sub = runBriefCore({ ...opts, spec: `${file2}::${symbol3}`, suppressStat: true });
|
|
32092
|
+
if (sub.code === 0) anyFound = true;
|
|
32093
|
+
if (opts.json === true) {
|
|
32094
|
+
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
32095
|
+
continue;
|
|
32096
|
+
}
|
|
32097
|
+
textBlocks.push(`${key}:
|
|
32098
|
+
${sub.text}`);
|
|
32099
|
+
}
|
|
32100
|
+
const fullSourceBytes = sumFileSizes([...distinctFiles].map((f) => resolveIndexPath(f, process.cwd())));
|
|
32101
|
+
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
32102
|
+
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
32103
|
+
return { text, code: anyFound ? 0 : 1 };
|
|
32104
|
+
}
|
|
31719
32105
|
function runBrief(opts) {
|
|
31720
32106
|
if (opts.limit !== void 0 && opts.limit <= 0) {
|
|
31721
32107
|
emitErr2(`--limit must be a positive number, got: ${opts.limit}`);
|
|
31722
32108
|
return 1;
|
|
31723
32109
|
}
|
|
32110
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
32111
|
+
if (crossFilePairs !== null) {
|
|
32112
|
+
const { text: text2, code: code2 } = runBriefCrossFile(crossFilePairs, opts);
|
|
32113
|
+
if (code2 === 0) emit3(text2);
|
|
32114
|
+
else emitErr2(text2);
|
|
32115
|
+
return code2;
|
|
32116
|
+
}
|
|
31724
32117
|
const { file: file2, symbol: symbol3 } = parseReadSpec(opts.spec);
|
|
31725
32118
|
if (symbol3 !== void 0 && symbol3 !== "" && symbol3.includes(",")) {
|
|
31726
32119
|
const multiSymbols = symbol3.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -31812,6 +32205,34 @@ function parseDiffHunks(diffText) {
|
|
|
31812
32205
|
}
|
|
31813
32206
|
return hunksByFile;
|
|
31814
32207
|
}
|
|
32208
|
+
function buildChangedRefHint(cwd, ref2) {
|
|
32209
|
+
const countResult = runGit(["rev-list", "--count", "HEAD"], { cwd });
|
|
32210
|
+
if (countResult.exitCode !== 0) {
|
|
32211
|
+
return null;
|
|
32212
|
+
}
|
|
32213
|
+
const commitCount = Number.parseInt(countResult.stdout.trim(), 10);
|
|
32214
|
+
if (!Number.isFinite(commitCount) || commitCount < 1) {
|
|
32215
|
+
return null;
|
|
32216
|
+
}
|
|
32217
|
+
const refResolves = runGit(["rev-parse", "--verify", "--quiet", ref2], { cwd });
|
|
32218
|
+
if (refResolves.exitCode === 0) {
|
|
32219
|
+
return null;
|
|
32220
|
+
}
|
|
32221
|
+
let suggestedRef = null;
|
|
32222
|
+
for (let n = commitCount - 1; n >= 1; n--) {
|
|
32223
|
+
const candidate = `HEAD~${n}`;
|
|
32224
|
+
const candidateResolves = runGit(["rev-parse", "--verify", "--quiet", candidate], { cwd });
|
|
32225
|
+
if (candidateResolves.exitCode === 0) {
|
|
32226
|
+
suggestedRef = candidate;
|
|
32227
|
+
break;
|
|
32228
|
+
}
|
|
32229
|
+
}
|
|
32230
|
+
if (suggestedRef === null) {
|
|
32231
|
+
suggestedRef = EMPTY_TREE_HASH;
|
|
32232
|
+
}
|
|
32233
|
+
const commitWord = commitCount === 1 ? "1 commit" : `${commitCount} commits`;
|
|
32234
|
+
return `Hint: this repo has only ${commitWord}; '${ref2}' does not exist. Try: token-goat changed --since ${suggestedRef}`;
|
|
32235
|
+
}
|
|
31815
32236
|
function runChanged(opts = {}) {
|
|
31816
32237
|
const ref2 = opts.ref ?? "HEAD~5";
|
|
31817
32238
|
const cwd = opts.projectRoot ?? process.cwd();
|
|
@@ -31821,6 +32242,10 @@ function runChanged(opts = {}) {
|
|
|
31821
32242
|
const result = runGit(["diff", ref2, "--name-only"], { cwd });
|
|
31822
32243
|
if (result.exitCode !== 0) {
|
|
31823
32244
|
emitErr2(`git diff failed: ${result.stderr}`);
|
|
32245
|
+
const hint = buildChangedRefHint(cwd, ref2);
|
|
32246
|
+
if (hint !== null) {
|
|
32247
|
+
emitErr2(hint);
|
|
32248
|
+
}
|
|
31824
32249
|
return 1;
|
|
31825
32250
|
}
|
|
31826
32251
|
changedFiles = result.stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
@@ -31926,13 +32351,16 @@ function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
|
|
|
31926
32351
|
resolution.symbol,
|
|
31927
32352
|
resolution.file,
|
|
31928
32353
|
resolution.candidates,
|
|
31929
|
-
projectRoot
|
|
32354
|
+
projectRoot,
|
|
32355
|
+
commandName
|
|
31930
32356
|
)
|
|
31931
32357
|
);
|
|
31932
32358
|
return null;
|
|
31933
32359
|
}
|
|
31934
32360
|
if (resolution.kind === "none") {
|
|
31935
32361
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
32362
|
+
const crossFileLead = formatCrossFileLead(commandName, symbol3, file2, projectRoot);
|
|
32363
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
31936
32364
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
31937
32365
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
31938
32366
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -32142,22 +32570,29 @@ function runGrep(opts) {
|
|
|
32142
32570
|
return 1;
|
|
32143
32571
|
}
|
|
32144
32572
|
const truncated = hits.slice(0, maxLines);
|
|
32573
|
+
if (opts.symbol === true) {
|
|
32574
|
+
const symbolsByFile = /* @__PURE__ */ new Map();
|
|
32575
|
+
for (const hit of truncated) {
|
|
32576
|
+
let syms = symbolsByFile.get(hit.file);
|
|
32577
|
+
if (syms === void 0) {
|
|
32578
|
+
syms = querySymbols({ filePath: resolveIndexPath(hit.file), limit: ALL_SYMBOLS_IN_FILE_LIMIT });
|
|
32579
|
+
symbolsByFile.set(hit.file, syms);
|
|
32580
|
+
}
|
|
32581
|
+
const enc = enclosingSymbol(syms, hit.line);
|
|
32582
|
+
hit.symbol = enc === null ? null : { name: enc.name, kind: enc.kind, lineStart: enc.lineStart, lineEnd: enc.lineEnd };
|
|
32583
|
+
}
|
|
32584
|
+
}
|
|
32145
32585
|
if (opts.json === true) {
|
|
32146
32586
|
const payload = { items: truncated, truncated: hits.length > maxLines, totalCount: hits.length };
|
|
32147
32587
|
emit3(JSON.stringify(payload, null, 2));
|
|
32148
32588
|
return 0;
|
|
32149
32589
|
}
|
|
32150
32590
|
for (const hit of truncated) {
|
|
32591
|
+
const symbolTag = opts.symbol === true && hit.symbol != null ? ` [${hit.symbol.name} (${hit.symbol.kind})]` : "";
|
|
32151
32592
|
if (hit.context !== void 0) {
|
|
32152
|
-
for (const
|
|
32153
|
-
if (ctxLine.line === hit.line) {
|
|
32154
|
-
emit3(`${hit.file}:${ctxLine.line}: ${ctxLine.text}`);
|
|
32155
|
-
} else {
|
|
32156
|
-
emit3(`${hit.file}-${ctxLine.line}- ${ctxLine.text}`);
|
|
32157
|
-
}
|
|
32158
|
-
}
|
|
32593
|
+
for (const line of renderContextWindow(hit.file, hit.line, hit.context, symbolTag)) emit3(line);
|
|
32159
32594
|
} else {
|
|
32160
|
-
emit3(`${hit.file}:${hit.line}: ${hit.text}`);
|
|
32595
|
+
emit3(`${hit.file}:${hit.line}: ${hit.text}${symbolTag}`);
|
|
32161
32596
|
}
|
|
32162
32597
|
}
|
|
32163
32598
|
if (hits.length > maxLines) {
|
|
@@ -32340,9 +32775,24 @@ function extractExportNames(text, ext2) {
|
|
|
32340
32775
|
}
|
|
32341
32776
|
return names;
|
|
32342
32777
|
}
|
|
32778
|
+
function runPerFileEmitting(files, label, run3) {
|
|
32779
|
+
let anyOk = false;
|
|
32780
|
+
files.forEach((file2, i) => {
|
|
32781
|
+
if (i > 0) emit3("");
|
|
32782
|
+
emit3(`# ${label}: ${file2}`);
|
|
32783
|
+
if (run3(file2) === 0) anyOk = true;
|
|
32784
|
+
});
|
|
32785
|
+
return anyOk ? 0 : 1;
|
|
32786
|
+
}
|
|
32343
32787
|
function runExports(opts) {
|
|
32788
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
32789
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Exports", (file2) => runExports({ ...opts, file: file2 }));
|
|
32344
32790
|
const symbols = querySymbols({ filePath: resolveIndexPath(opts.file), limit: 500 });
|
|
32345
32791
|
const kindOf = (name2) => symbols.find((s) => s.name === name2)?.kind ?? "export";
|
|
32792
|
+
const locOf = (name2) => {
|
|
32793
|
+
const s = symbols.find((sym) => sym.name === name2);
|
|
32794
|
+
return s === void 0 ? null : { lineStart: s.lineStart, lineEnd: s.lineEnd };
|
|
32795
|
+
};
|
|
32346
32796
|
const names = [];
|
|
32347
32797
|
for (const s of symbols) {
|
|
32348
32798
|
if (/^(?:export|pub\b|public\b)/.test(s.body.trimStart()) && !names.includes(s.name)) {
|
|
@@ -32367,12 +32817,23 @@ function runExports(opts) {
|
|
|
32367
32817
|
}
|
|
32368
32818
|
const fullSourceBytes = sumFileSizes([opts.file]);
|
|
32369
32819
|
if (opts.json === true) {
|
|
32370
|
-
const jsonText = JSON.stringify(
|
|
32820
|
+
const jsonText = JSON.stringify(
|
|
32821
|
+
names.map((n) => {
|
|
32822
|
+
const loc = locOf(n);
|
|
32823
|
+
return { name: n, kind: kindOf(n), lineStart: loc?.lineStart ?? null, lineEnd: loc?.lineEnd ?? null };
|
|
32824
|
+
}),
|
|
32825
|
+
null,
|
|
32826
|
+
2
|
|
32827
|
+
);
|
|
32371
32828
|
emit3(jsonText);
|
|
32372
32829
|
recordReadStat("exports", fullSourceBytes, jsonText, opts.file);
|
|
32373
32830
|
return 0;
|
|
32374
32831
|
}
|
|
32375
|
-
const outLines = names.map((n) =>
|
|
32832
|
+
const outLines = names.map((n) => {
|
|
32833
|
+
const loc = locOf(n);
|
|
32834
|
+
const locSuffix = loc === null ? "" : ` (${loc.lineStart}-${loc.lineEnd})`;
|
|
32835
|
+
return `${kindOf(n).padEnd(10)} ${n}${locSuffix}`;
|
|
32836
|
+
});
|
|
32376
32837
|
for (const line of outLines) {
|
|
32377
32838
|
emit3(line);
|
|
32378
32839
|
}
|
|
@@ -32666,6 +33127,8 @@ function importsExtensionFor(filePath) {
|
|
|
32666
33127
|
return path48.extname(filePath);
|
|
32667
33128
|
}
|
|
32668
33129
|
function runImports(opts) {
|
|
33130
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
33131
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Imports", (file2) => runImports({ ...opts, file: file2 }));
|
|
32669
33132
|
const text = readFileText(opts.file);
|
|
32670
33133
|
if (text === null) {
|
|
32671
33134
|
emitErr2(`Could not read: ${opts.file}`);
|
|
@@ -32759,11 +33222,12 @@ async function runSemantic(query, opts) {
|
|
|
32759
33222
|
);
|
|
32760
33223
|
const hits = mergeNearbyHits(rawHits).slice(0, n);
|
|
32761
33224
|
if (hits.length > 0) {
|
|
33225
|
+
const enclosing = hits.map((h) => resolveEnclosingSymbol(h.filePath, h.startLine));
|
|
32762
33226
|
if (opts.json === true) {
|
|
32763
|
-
const items = hits.map((h) => ({
|
|
33227
|
+
const items = hits.map((h, i) => ({
|
|
32764
33228
|
filePath: h.filePath,
|
|
32765
|
-
name: null,
|
|
32766
|
-
kind: null,
|
|
33229
|
+
name: enclosing[i]?.name ?? null,
|
|
33230
|
+
kind: enclosing[i]?.kind ?? null,
|
|
32767
33231
|
startLine: h.startLine,
|
|
32768
33232
|
endLine: h.endLine,
|
|
32769
33233
|
distance: h.distance,
|
|
@@ -32774,10 +33238,12 @@ async function runSemantic(query, opts) {
|
|
|
32774
33238
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
|
|
32775
33239
|
return { text: text3, code: 0 };
|
|
32776
33240
|
}
|
|
32777
|
-
const blocks2 = hits.map(
|
|
32778
|
-
|
|
32779
|
-
${
|
|
32780
|
-
|
|
33241
|
+
const blocks2 = hits.map((h, i) => {
|
|
33242
|
+
const enc = enclosing[i] ?? null;
|
|
33243
|
+
const suffix = enc !== null ? ` \u2014 inside ${enc.name} (${enc.kind})` : "";
|
|
33244
|
+
return `# ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine} (distance ${h.distance.toFixed(3)})${suffix}
|
|
33245
|
+
${previewLines(h.text, 3)}`;
|
|
33246
|
+
});
|
|
32781
33247
|
const text2 = guardText(blocks2.join("\n\n"), "semantic");
|
|
32782
33248
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text2, query);
|
|
32783
33249
|
return { text: text2, code: 0 };
|
|
@@ -32866,7 +33332,7 @@ function runNoteList(opts = {}) {
|
|
|
32866
33332
|
});
|
|
32867
33333
|
return { text: lines2.join("\n"), code: 0 };
|
|
32868
33334
|
}
|
|
32869
|
-
var DIDYOUMEAN_LIMIT, MIN_REVERSE_MATCH_LEN, GREP_MAX_LINES, FIND_SCAN_LIMIT, REFS_TOP_SCAN_LIMIT, STALE_WARNING, PARENT_IDENTIFIER_RE, SKELETON_SYMBOL_CAP, HUNK_HEADER_RE, DEFAULT_LOG_MAX_COUNT;
|
|
33335
|
+
var DIDYOUMEAN_LIMIT, MIN_REVERSE_MATCH_LEN, GREP_MAX_LINES, FIND_SCAN_LIMIT, REFS_TOP_SCAN_LIMIT, STALE_WARNING, PARENT_IDENTIFIER_RE, SKELETON_SYMBOL_CAP, HUNK_HEADER_RE, EMPTY_TREE_HASH, DEFAULT_LOG_MAX_COUNT;
|
|
32870
33336
|
var init_read_commands = __esm({
|
|
32871
33337
|
"src/read_commands.ts"() {
|
|
32872
33338
|
"use strict";
|
|
@@ -32911,6 +33377,7 @@ var init_read_commands = __esm({
|
|
|
32911
33377
|
PARENT_IDENTIFIER_RE = /^[\w$]+$/;
|
|
32912
33378
|
SKELETON_SYMBOL_CAP = 5e3;
|
|
32913
33379
|
HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
|
|
33380
|
+
EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
32914
33381
|
DEFAULT_LOG_MAX_COUNT = 20;
|
|
32915
33382
|
}
|
|
32916
33383
|
});
|
|
@@ -48701,6 +49168,7 @@ init_index_reader();
|
|
|
48701
49168
|
init_parser_types();
|
|
48702
49169
|
init_doc_embed_extract();
|
|
48703
49170
|
init_paths();
|
|
49171
|
+
init_project();
|
|
48704
49172
|
init_hooks_index();
|
|
48705
49173
|
|
|
48706
49174
|
// src/relay.ts
|
|
@@ -50035,7 +50503,7 @@ function checkDbExists(dataDir2) {
|
|
|
50035
50503
|
return {
|
|
50036
50504
|
name: "Database",
|
|
50037
50505
|
status: "ok",
|
|
50038
|
-
message: `global.db exists (${toKB(sizeBytes)} KB)`
|
|
50506
|
+
message: `global.db exists (${toKB(sizeBytes)} KB) at ${dbPath}`
|
|
50039
50507
|
};
|
|
50040
50508
|
}
|
|
50041
50509
|
function checkSymbolBodySize(dbPath) {
|
|
@@ -50083,6 +50551,13 @@ function checkSymbolCount(dbPath, rootDir) {
|
|
|
50083
50551
|
message: `${fileCount} file(s) indexed but 0 symbols extracted \u2014 the parser may not be running (check the worker log); try 'token-goat index --force'`
|
|
50084
50552
|
};
|
|
50085
50553
|
}
|
|
50554
|
+
if (fileCount === 0 && symbolCount === 0) {
|
|
50555
|
+
return {
|
|
50556
|
+
name: "Symbols",
|
|
50557
|
+
status: "warn",
|
|
50558
|
+
message: `no files indexed for this project \u2014 every read command will return empty, which looks like a genuine "not found" rather than a missing index; run 'token-goat index .' here`
|
|
50559
|
+
};
|
|
50560
|
+
}
|
|
50086
50561
|
return {
|
|
50087
50562
|
name: "Symbols",
|
|
50088
50563
|
status: "ok",
|
|
@@ -81187,6 +81662,9 @@ function runHintStatsCommand(opts = {}) {
|
|
|
81187
81662
|
`);
|
|
81188
81663
|
return;
|
|
81189
81664
|
}
|
|
81665
|
+
if (rows.every((r) => r.emitted === 0 && r.actedOn === 0)) {
|
|
81666
|
+
process.stdout.write("No hint emissions recorded yet \u2014 the zeros below are absence of data, not measured ineffectiveness.\n");
|
|
81667
|
+
}
|
|
81190
81668
|
printSummary(rows);
|
|
81191
81669
|
}
|
|
81192
81670
|
|
|
@@ -81445,7 +81923,11 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
81445
81923
|
function cmdMap(opts) {
|
|
81446
81924
|
const map3 = buildProjectMap(process.cwd(), { compact: opts.compact === true });
|
|
81447
81925
|
const text = formatProjectMap(map3, map3.compact);
|
|
81448
|
-
|
|
81926
|
+
if (opts.json === true) {
|
|
81927
|
+
out(JSON.stringify(map3));
|
|
81928
|
+
} else {
|
|
81929
|
+
out(text);
|
|
81930
|
+
}
|
|
81449
81931
|
const bytesSaved = mapLookupBytesSaved(map3, text);
|
|
81450
81932
|
recordStat("map_lookup", bytesSaved, Math.round(bytesSaved / 4));
|
|
81451
81933
|
}
|
|
@@ -81695,6 +82177,14 @@ async function cmdDoctor(opts) {
|
|
|
81695
82177
|
if (project !== null) {
|
|
81696
82178
|
doctorOpts.rootDir = project.root;
|
|
81697
82179
|
}
|
|
82180
|
+
if (opts.json === true) {
|
|
82181
|
+
const results = runDoctor(doctorOpts.dataDir, doctorOpts.configPath, doctorOpts.rootDir);
|
|
82182
|
+
out(JSON.stringify(results));
|
|
82183
|
+
if (results.some((r) => r.status === "fail")) {
|
|
82184
|
+
throw new CliError("doctor checks failed");
|
|
82185
|
+
}
|
|
82186
|
+
return;
|
|
82187
|
+
}
|
|
81698
82188
|
const code = await runDoctorAndExit(doctorOpts);
|
|
81699
82189
|
if (code !== 0) {
|
|
81700
82190
|
throw new CliError("doctor checks failed");
|
|
@@ -82198,6 +82688,16 @@ function runExitText(fn) {
|
|
|
82198
82688
|
process.exitCode = 1;
|
|
82199
82689
|
}
|
|
82200
82690
|
}
|
|
82691
|
+
function noteExtraFileArgs(command, first2, extras, fn) {
|
|
82692
|
+
const result = fn();
|
|
82693
|
+
if (extras === void 0 || extras.length === 0) return result;
|
|
82694
|
+
return { text: `${extraFileArgsNote(command, first2, extras)}
|
|
82695
|
+
${result.text}`, code: result.code };
|
|
82696
|
+
}
|
|
82697
|
+
function emitExtraFileArgsNote(command, first2, extras) {
|
|
82698
|
+
if (extras === void 0 || extras.length === 0) return;
|
|
82699
|
+
out(extraFileArgsNote(command, first2, extras));
|
|
82700
|
+
}
|
|
82201
82701
|
function cmdCompress(opts) {
|
|
82202
82702
|
try {
|
|
82203
82703
|
if (opts.compress === false) {
|
|
@@ -82322,6 +82822,10 @@ async function cmdSkillList(opts) {
|
|
|
82322
82822
|
return `${s.name.padEnd(25)} ${bodyKb.padStart(6)}K ${compactKb.padStart(6)}K ${marker} ${s.hitCount.toString().padStart(3)} ${age.padStart(3)} ${staleStatus}`;
|
|
82323
82823
|
});
|
|
82324
82824
|
const header = `${"Name".padEnd(25)} ${"Body".padStart(6)} ${"Compact".padStart(6)} Marker Hits Age Status`;
|
|
82825
|
+
if (skills.length === 0) {
|
|
82826
|
+
out("No skills cached yet.");
|
|
82827
|
+
return;
|
|
82828
|
+
}
|
|
82325
82829
|
out([header, ...lines2].join("\n"));
|
|
82326
82830
|
}
|
|
82327
82831
|
}
|
|
@@ -83159,19 +83663,26 @@ function buildProgram() {
|
|
|
83159
83663
|
process.exitCode = 1;
|
|
83160
83664
|
}
|
|
83161
83665
|
};
|
|
83162
|
-
program2.command("symbol <name>").description("search for a symbol by name").option("-l, --limit <n>", "max results").option("-f, --file <path>", "restrict to one file").option("-k, --kind <kind>", "restrict to one kind (function, class, ...)").option("-j, --json", "output as JSON").action(
|
|
83163
|
-
|
|
83666
|
+
program2.command("symbol <name>").description("search for a symbol by name").option("-l, --limit <n>", "max results").option("-f, --file <path>", "restrict to one file").option("-k, --kind <kind>", "restrict to one kind (function, class, ...)").option("-p, --project [path]", "scope search to one project root instead of the global index (defaults to cwd)").option("-j, --json", "output as JSON").action((name2, opts) => {
|
|
83667
|
+
let projectRoot;
|
|
83668
|
+
if (opts.project === true) {
|
|
83669
|
+
projectRoot = resolveProjectRoot({ project: process.cwd() });
|
|
83670
|
+
} else if (typeof opts.project === "string") {
|
|
83671
|
+
projectRoot = resolveProjectRoot({ project: opts.project });
|
|
83672
|
+
}
|
|
83673
|
+
return runExitText(
|
|
83164
83674
|
() => runSymbol({
|
|
83165
83675
|
name: name2,
|
|
83166
83676
|
limit: opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20,
|
|
83167
83677
|
...opts.file !== void 0 ? { file: opts.file } : {},
|
|
83168
83678
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
83679
|
+
...projectRoot !== void 0 ? { projectRoot } : {},
|
|
83169
83680
|
...opts.json === true ? { json: true } : {}
|
|
83170
83681
|
})
|
|
83171
|
-
)
|
|
83172
|
-
);
|
|
83682
|
+
);
|
|
83683
|
+
});
|
|
83173
83684
|
program2.command("read <spec>").description(
|
|
83174
|
-
"read one symbol's full body (spec: file::symbol; disambiguate a name shared by several classes with file::Parent.symbol; comma-separated file::a,b for a merged multi-symbol view, or a::x,b::y to merge symbols across several files)"
|
|
83685
|
+
"read one symbol's full body (spec: file::symbol; disambiguate a name shared by several classes with file::Parent.symbol; a trailing @LINE anchor -- file::symbol@LINE, or combined as file::Parent.symbol@LINE -- picks out a specific candidate by its exact starting line, for the case a Parent qualifier can't reach (e.g. a top-level definition); comma-separated file::a,b for a merged multi-symbol view, or a::x,b::y to merge symbols across several files)"
|
|
83175
83686
|
).option("-j, --json", "output as JSON").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
83176
83687
|
(spec, opts) => runExitText(
|
|
83177
83688
|
() => runRead({
|
|
@@ -83183,13 +83694,14 @@ function buildProgram() {
|
|
|
83183
83694
|
)
|
|
83184
83695
|
);
|
|
83185
83696
|
program2.command("brief <spec>").description(
|
|
83186
|
-
"symbol body + callers + containing doc section in one call (spec: file::symbol; comma-separated file::a,b for a merged multi-symbol view)"
|
|
83187
|
-
).option("-j, --json", "output as JSON").option("--limit <n>", "max callers to show (default: 20)").action(
|
|
83697
|
+
"symbol body + callers + containing doc section in one call (spec: file::symbol; also accepts the file::symbol@LINE anchor form documented under `read`; comma-separated file::a,b for a merged multi-symbol view; cross-file a.ts::x,b.ts::y is also supported)"
|
|
83698
|
+
).option("-j, --json", "output as JSON").option("--limit <n>", "max callers to show (default: 20)").option("-C, --context <n>", "lines of call-site source to show before and after each caller (default 0)").action(
|
|
83188
83699
|
(spec, opts) => runExit(
|
|
83189
83700
|
() => runBrief({
|
|
83190
83701
|
spec,
|
|
83191
83702
|
...opts.json === true ? { json: true } : {},
|
|
83192
|
-
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {}
|
|
83703
|
+
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
83704
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {}
|
|
83193
83705
|
})
|
|
83194
83706
|
)
|
|
83195
83707
|
);
|
|
@@ -83199,44 +83711,56 @@ function buildProgram() {
|
|
|
83199
83711
|
(spec, opts) => opts.list === true ? runExit(() => runListSections({ file: spec, ...opts.json === true ? { json: true } : {} })) : runExitText(() => runSection({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
83200
83712
|
);
|
|
83201
83713
|
program2.command("semantic <query>").description("semantic search (falls back to full-text search)").option("-l, --limit <n>", "max results").option("-j, --json", "output as JSON").action(guard(cmdSemantic));
|
|
83202
|
-
program2.command("skeleton <file>").description(
|
|
83203
|
-
(file2, opts) => runExitText(
|
|
83204
|
-
() =>
|
|
83205
|
-
|
|
83206
|
-
|
|
83207
|
-
|
|
83208
|
-
|
|
83209
|
-
|
|
83210
|
-
|
|
83714
|
+
program2.command("skeleton <file> [more...]").description('list all symbols in a file without bodies (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").option("--min-lines <n>", "only show symbols at least N lines long").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
83715
|
+
(file2, more, opts) => runExitText(
|
|
83716
|
+
() => noteExtraFileArgs(
|
|
83717
|
+
"skeleton",
|
|
83718
|
+
file2,
|
|
83719
|
+
more,
|
|
83720
|
+
() => runSkeleton({
|
|
83721
|
+
file: file2,
|
|
83722
|
+
...opts.json === true ? { json: true } : {},
|
|
83723
|
+
...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
|
|
83724
|
+
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
83725
|
+
...opts.stats === true ? { stats: true } : {}
|
|
83726
|
+
})
|
|
83727
|
+
)
|
|
83211
83728
|
)
|
|
83212
83729
|
);
|
|
83213
|
-
program2.command("outline <file>").description(
|
|
83214
|
-
(file2, opts) => runExitText(
|
|
83215
|
-
() =>
|
|
83216
|
-
|
|
83217
|
-
|
|
83218
|
-
|
|
83219
|
-
|
|
83220
|
-
|
|
83221
|
-
|
|
83730
|
+
program2.command("outline <file> [more...]").description('list symbols with line ranges and docstrings (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").option("--min-lines <n>", "only show symbols at least N lines long").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
83731
|
+
(file2, more, opts) => runExitText(
|
|
83732
|
+
() => noteExtraFileArgs(
|
|
83733
|
+
"outline",
|
|
83734
|
+
file2,
|
|
83735
|
+
more,
|
|
83736
|
+
() => runOutline({
|
|
83737
|
+
file: file2,
|
|
83738
|
+
...opts.json === true ? { json: true } : {},
|
|
83739
|
+
...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
|
|
83740
|
+
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
83741
|
+
...opts.stats === true ? { stats: true } : {}
|
|
83742
|
+
})
|
|
83743
|
+
)
|
|
83222
83744
|
)
|
|
83223
83745
|
);
|
|
83224
|
-
program2.command("refs <spec>").description("find references to one or more symbols (spec: file::symbol, symbol, or comma-separated a,b,c / file::a,b for a merged multi-symbol view). For an unambiguous TypeScript symbol, automatically type-resolves candidates via the TypeScript compiler API to drop same-named-different-symbol false positives; falls back to name-based matching when that is not possible.").option("--callers", "group references by their enclosing caller symbol").option("-l, --limit <n>", "max results").option(
|
|
83746
|
+
program2.command("refs <spec>").description("find references to one or more symbols (spec: file::symbol, symbol, or comma-separated a,b,c / file::a,b for a merged multi-symbol view; cross-file a.ts::x,b.ts::y is also supported). For an unambiguous TypeScript symbol, automatically type-resolves candidates via the TypeScript compiler API to drop same-named-different-symbol false positives; falls back to name-based matching when that is not possible.").option("--callers", "group references by their enclosing caller symbol").option("-l, --limit <n>", "max results").option(
|
|
83225
83747
|
"--top <n>",
|
|
83226
83748
|
"for a high-fanout symbol, group references by file (count only) and show only the top N files by reference count instead of a per-line dump"
|
|
83227
|
-
).option("-j, --json", "output as JSON").action(
|
|
83749
|
+
).option("-C, --context <n>", "lines of call-site source to show before and after each reference (default 0)").option("-j, --json", "output as JSON").option("--exclude-tests", "hide references whose call site lives in a test file (opt-in; default output is unchanged)").action(
|
|
83228
83750
|
(spec, opts) => runExit(
|
|
83229
83751
|
() => runRefs({
|
|
83230
83752
|
spec,
|
|
83231
83753
|
...opts.callers === true ? { callers: true } : {},
|
|
83232
83754
|
...opts.json === true ? { json: true } : {},
|
|
83233
83755
|
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
83234
|
-
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {}
|
|
83756
|
+
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
83757
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
83758
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
83235
83759
|
})
|
|
83236
83760
|
)
|
|
83237
83761
|
);
|
|
83238
83762
|
program2.command("index [path]").description("parse all git-tracked files and (re)build the symbol index").option("--walk", "if not a git repo, index a bounded directory walk instead (skips .env / generated / oversized trees)").option("--force", "bypass the SHA-freshness cache and reindex every tracked file, even byte-identical ones (e.g. after a parser upgrade changes what gets extracted)").option("--force-walk", `index a non-git folder via --walk and raise its ${MAX_FILES_SCANNED} source-file refusal to ${MAX_FILES_SCANNED_FORCED} (slow; produces a large index)`).action(guard(cmdIndex));
|
|
83239
|
-
program2.command("map").description("project overview").option("-c, --compact", "compact, low-token summary").action(guard(cmdMap));
|
|
83763
|
+
program2.command("map").description("project overview").option("-c, --compact", "compact, low-token summary").option("--json", "emit the project map as JSON instead of text").action(guard(cmdMap));
|
|
83240
83764
|
program2.command("bridges-status").description("hook-event parity matrix across every AI-harness bridge (read-only static analysis, never invokes a real harness binary)").option("--json", "emit the matrix as JSON instead of text").action(guard(cmdBridgesStatus));
|
|
83241
83765
|
program2.command("commands").description("machine-readable manifest of every registered command, its options, and its arguments").option("--json", "emit the manifest as JSON instead of text").option("--grep <pattern>", "filter to commands whose name, description, or aliases match this regex").action(guard(cmdCommands));
|
|
83242
83766
|
program2.command("mcp-serve").description("run token-goat as an MCP stdio server exposing surgical reads and local compression/handoff tools").action(guard(cmdMcpServe));
|
|
@@ -83252,7 +83776,7 @@ function buildProgram() {
|
|
|
83252
83776
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
83253
83777
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
83254
83778
|
program2.command("stats").description("show session statistics (bare = totals only; --full for the breakdown)").option("-j, --json", "output as JSON").option("--full", "show the full breakdown (by source, by command, by day)").option("--short", "force the rich short KPI view even when stdout is not a TTY (e.g. piped)").option("--window-days <days>", "days to include (0 = all time)", "30").option("--home-dir <path>", "home directory (for testing)").action(guard(cmdStats));
|
|
83255
|
-
program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").action(guard(cmdDoctor));
|
|
83779
|
+
program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").option("--json", "emit check results as JSON instead of text").action(guard(cmdDoctor));
|
|
83256
83780
|
program2.command("context-stats").description("show context statistics").option("--project <path>", "project root to analyze").option("-j, --json", "output as JSON").option("--fix", "apply automatic fixes (confirm-gated; shows a diff before writing)").option("-y, --yes", "with --fix, apply without prompting (non-interactive / scripted use)").action(guard(cmdContextStats));
|
|
83257
83781
|
program2.command("bootstrap-audit").description("audit Claude Code startup-context contributors without reading prompt bodies").option("--project <path>", "project root to analyze").option("--home <path>", "home directory override (for CI/testing)").option("--follow-links", "follow external symlink/junction roots and direct children").option("-j, --json", "output as JSON").option("--top <n>", "largest metadata entries to show (default 10)", "10").option("--warn-tokens <n>", "warn when total estimated startup tokens exceed n").option("--fail-tokens <n>", "fail when total estimated startup tokens exceed n").option("--warn-bytes <n>", "warn when agent/skill metadata bytes exceed n").option("--fail-bytes <n>", "fail when agent/skill metadata bytes exceed n").action(guard(cmdBootstrapAudit));
|
|
83258
83782
|
program2.command("memory").description("analyze CLAUDE.md files for duplicate/overlapping content (--fix to apply safe mechanical fixes)").option("--project <path>", "project root to analyze").option("--analyze", "report-only analysis (default)").option("--fix", "remove exact-duplicate lines (confirm-gated; shows a diff before writing)").option("--yes", "apply --fix changes without prompting (non-interactive)").action(guard(cmdMemory));
|
|
@@ -83266,11 +83790,17 @@ function buildProgram() {
|
|
|
83266
83790
|
program2.command("bash-output [id]").description("retrieve cached bash output by ID or file").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the output").option("--full", "print the entire cached entry with no head/tail elision").option("--file <path>", "read from raw output file instead of cache").option("--transcript", "parse the --file as a JSONL agent transcript: keep assistant text blocks in order before filtering").action(guard(cmdBashOutput));
|
|
83267
83791
|
program2.command("web-output [id]").description("retrieve a cached WebFetch response body by ID").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the response").option("--full", "print the entire cached entry with no head/tail elision").action(guard(cmdWebOutput));
|
|
83268
83792
|
program2.command("mcp-output [id]").description("retrieve a cached MCP tool result by ID (the id an MCP post_tool_use hook cached, or a `[token-goat: compressed, full via mcp-output <id>]` label points here)").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the result").option("--full", "print the entire cached entry with no head/tail elision").action(guard(cmdMcpOutput));
|
|
83269
|
-
program2.command("exports <file>").description(
|
|
83270
|
-
(file2, opts) => runExit(() =>
|
|
83793
|
+
program2.command("exports <file> [more...]").description('list exported (public) symbols in a file (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").action(
|
|
83794
|
+
(file2, more, opts) => runExit(() => {
|
|
83795
|
+
emitExtraFileArgsNote("exports", file2, more);
|
|
83796
|
+
return runExports({ file: file2, ...opts.json === true ? { json: true } : {} });
|
|
83797
|
+
})
|
|
83271
83798
|
);
|
|
83272
|
-
program2.command("imports <file>").description(
|
|
83273
|
-
(file2, opts) => runExit(() =>
|
|
83799
|
+
program2.command("imports <file> [more...]").description('list the modules a file imports (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").action(
|
|
83800
|
+
(file2, more, opts) => runExit(() => {
|
|
83801
|
+
emitExtraFileArgsNote("imports", file2, more);
|
|
83802
|
+
return runImports({ file: file2, ...opts.json === true ? { json: true } : {} });
|
|
83803
|
+
})
|
|
83274
83804
|
);
|
|
83275
83805
|
program2.command("find <pattern>").description("find files containing a symbol matching a pattern").option("-j, --json", "output as JSON").option("-l, --limit <n>", "max results").action(
|
|
83276
83806
|
(pattern, opts) => runExit(
|
|
@@ -83281,7 +83811,7 @@ function buildProgram() {
|
|
|
83281
83811
|
})
|
|
83282
83812
|
)
|
|
83283
83813
|
);
|
|
83284
|
-
program2.command("grep <pattern> [paths...]").description("regex search over files, caching nothing (session-aware grep)").option("-j, --json", "output as JSON").option("--max-lines <n>", "max matching lines to print").option("--no-recursive", "do not descend into subdirectories").option("-C, --context <n>", "lines of context to show before and after each match").action(
|
|
83814
|
+
program2.command("grep <pattern> [paths...]").description("regex search over files, caching nothing (session-aware grep)").option("-j, --json", "output as JSON").option("--max-lines <n>", "max matching lines to print").option("--no-recursive", "do not descend into subdirectories").option("-C, --context <n>", "lines of context to show before and after each match").option("--symbol", "annotate each hit with its enclosing symbol (name and kind)").action(
|
|
83285
83815
|
(pattern, paths, opts) => runExit(
|
|
83286
83816
|
() => runGrep({
|
|
83287
83817
|
pattern,
|
|
@@ -83289,7 +83819,8 @@ function buildProgram() {
|
|
|
83289
83819
|
...opts.json === true ? { json: true } : {},
|
|
83290
83820
|
...opts.maxLines !== void 0 ? { maxLines: requirePositiveInt("--max-lines", opts.maxLines) } : {},
|
|
83291
83821
|
...opts.recursive === false ? { recursive: false } : {},
|
|
83292
|
-
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {}
|
|
83822
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
83823
|
+
...opts.symbol === true ? { symbol: true } : {}
|
|
83293
83824
|
})
|
|
83294
83825
|
)
|
|
83295
83826
|
);
|
|
@@ -83300,16 +83831,18 @@ function buildProgram() {
|
|
|
83300
83831
|
program2.command("skill-history").description("list cached skill versions newest-first").option("-j, --json", "output as JSON").action(guard(cmdSkillHistory));
|
|
83301
83832
|
program2.command("skill-diff <name>").description("show diff between two cached versions of a skill").action(guard(cmdSkillDiff));
|
|
83302
83833
|
program2.command("skill-section <nameHeading> [headingArg]").description("extract a named section from a skill").action(guard(cmdSkillSection));
|
|
83303
|
-
program2.command("callers <symbol>").description("find all callers of a symbol, resolved to their enclosing function").option("-j, --json", "output as JSON").option("-l, --limit <n>", "max references to scan").action(
|
|
83834
|
+
program2.command("callers <symbol>").description("find all callers of a symbol, resolved to their enclosing function (accepts file::symbol to disambiguate which same-named definition is meant)").option("-j, --json", "output as JSON").option("-l, --limit <n>", "max references to scan").option("-C, --context <n>", "lines of call-site source to show before and after each caller (default 0)").option("--exclude-tests", "hide callers whose call site lives in a test file (opt-in; default output is unchanged)").action(
|
|
83304
83835
|
(symbol3, opts) => runExit(
|
|
83305
83836
|
() => runCallers({
|
|
83306
83837
|
symbol: symbol3,
|
|
83307
83838
|
...opts.json === true ? { json: true } : {},
|
|
83308
|
-
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {}
|
|
83839
|
+
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
83840
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
83841
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
83309
83842
|
})
|
|
83310
83843
|
)
|
|
83311
83844
|
);
|
|
83312
|
-
program2.command("call-chain <symbol>").description("transitive callers up toward entry points (BFS, cycle-safe)").option("-d, --depth <n>", "max BFS depth (default 8)").option("-j, --json", "output as JSON").action(
|
|
83845
|
+
program2.command("call-chain <symbol>").description("transitive callers up toward entry points (BFS, cycle-safe; accepts file::symbol to disambiguate which same-named definition is meant)").option("-d, --depth <n>", "max BFS depth (default 8)").option("-j, --json", "output as JSON").action(
|
|
83313
83846
|
(symbol3, opts) => runExit(
|
|
83314
83847
|
() => runCallChain({
|
|
83315
83848
|
symbol: symbol3,
|
|
@@ -83318,7 +83851,7 @@ function buildProgram() {
|
|
|
83318
83851
|
})
|
|
83319
83852
|
)
|
|
83320
83853
|
);
|
|
83321
|
-
program2.command("impact <symbol>").description("transitive set of callers impacted by a change (with hop depth)").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").action(
|
|
83854
|
+
program2.command("impact <symbol>").description("transitive set of callers impacted by a change (with hop depth; accepts file::symbol to disambiguate which same-named definition is meant)").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").action(
|
|
83322
83855
|
(symbol3, opts) => runExit(
|
|
83323
83856
|
() => runImpact({
|
|
83324
83857
|
symbol: symbol3,
|
|
@@ -83327,13 +83860,14 @@ function buildProgram() {
|
|
|
83327
83860
|
})
|
|
83328
83861
|
)
|
|
83329
83862
|
);
|
|
83330
|
-
program2.command("dead").description("symbols with zero references (default kind: function)").option("-k, --kind <kind>", "symbol kind to check (function, method, class, ...)").option("--include-private", "include _-prefixed names").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").action(
|
|
83863
|
+
program2.command("dead").description("symbols with zero references (default kind: function)").option("-k, --kind <kind>", "symbol kind to check (function, method, class, ...)").option("--include-private", "include _-prefixed names").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").option("--exclude-tests", "hide dead symbols defined in a test file (opt-in; default output is unchanged)").action(
|
|
83331
83864
|
(opts) => runExit(
|
|
83332
83865
|
() => runDead({
|
|
83333
83866
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
83334
83867
|
...opts.includePrivate === true ? { includePrivate: true } : {},
|
|
83335
83868
|
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
83336
|
-
...opts.json === true ? { json: true } : {}
|
|
83869
|
+
...opts.json === true ? { json: true } : {},
|
|
83870
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
83337
83871
|
})
|
|
83338
83872
|
)
|
|
83339
83873
|
);
|
|
@@ -83352,7 +83886,7 @@ function buildProgram() {
|
|
|
83352
83886
|
program2.command("scope <fileColonLine>").description("list symbols enclosing a file:line position, innermost first").option("-j, --json", "output as JSON").action(
|
|
83353
83887
|
(spec, opts) => runExit(() => runScope({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
83354
83888
|
);
|
|
83355
|
-
program2.command("similar <spec>").description('find symbols similar to a given "file::symbol" anchor using FTS').option("--top <n>", "max results (default 10)").option("-j, --json", "output as JSON").action(
|
|
83889
|
+
program2.command("similar <spec>").description('find symbols similar to a given "file::symbol" anchor using FTS (also accepts the file::symbol@LINE anchor form documented under `read`)').option("--top <n>", "max results (default 10)").option("-j, --json", "output as JSON").action(
|
|
83356
83890
|
(spec, opts) => runExit(
|
|
83357
83891
|
() => runSimilar({
|
|
83358
83892
|
spec,
|
|
@@ -83391,7 +83925,7 @@ function buildProgram() {
|
|
|
83391
83925
|
})
|
|
83392
83926
|
)
|
|
83393
83927
|
);
|
|
83394
|
-
program2.command("blame <spec>").description('git blame for the line range of a symbol ("file::symbol")').option("-j, --json", "output as JSON").action(
|
|
83928
|
+
program2.command("blame <spec>").description('git blame for the line range of a symbol ("file::symbol"; also accepts the file::symbol@LINE anchor form documented under `read`)').option("-j, --json", "output as JSON").action(
|
|
83395
83929
|
(spec, opts) => runExit(() => runBlame({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
83396
83930
|
);
|
|
83397
83931
|
program2.command("ask <question>").description("(experimental) find relevant code context; synthesize with an LLM if TOKEN_GOAT_ASK_BACKEND is set").option("--top <n>", "max FTS hits to surface (default 8)").option("-j, --json", "output as JSON").action(
|
|
@@ -83475,16 +84009,16 @@ function buildProgram() {
|
|
|
83475
84009
|
}))());
|
|
83476
84010
|
program2.command("fetch-image <url>").description("fetch an image URL and shrink it (saves to --out path or a temp file)").option("--out <path>", "output file path").option("-j, --json", "output as JSON").action((url2, opts) => guard(() => cmdFetchImage({ url: url2, ...opts.out !== void 0 ? { out: opts.out } : {}, ...opts.json === true ? { json: true } : {} }))());
|
|
83477
84011
|
program2.command("history").description("show recent session history: bash commands and web fetches (current-session or recent cache)").option("--limit <n>", "max entries to show (default: 30)").option("-j, --json", "output as JSON").action((opts) => guard(() => cmdHistory(opts))());
|
|
83478
|
-
program2.command("changed").description("list files or symbols changed since a git ref").option("--since <ref>", "git ref to compare against (default: HEAD~5)").option("--symbol", "list symbols instead of files").option("-j, --json", "output as JSON").action(
|
|
83479
|
-
(opts) => runExit(
|
|
84012
|
+
program2.command("changed [ref]").description("list files or symbols changed since a git ref").option("--since <ref>", "git ref to compare against (default: HEAD~5)").option("--symbol", "list symbols instead of files").option("-j, --json", "output as JSON").action(
|
|
84013
|
+
(ref2, opts) => runExit(
|
|
83480
84014
|
() => runChanged({
|
|
83481
|
-
ref: opts.since ?? "HEAD~5",
|
|
84015
|
+
ref: opts.since ?? ref2 ?? "HEAD~5",
|
|
83482
84016
|
...opts.symbol === true ? { symbolMode: true } : {},
|
|
83483
84017
|
...opts.json === true ? { json: true } : {}
|
|
83484
84018
|
})
|
|
83485
84019
|
)
|
|
83486
84020
|
);
|
|
83487
|
-
program2.command("diff <spec> [ref]").description('show only the git diff hunk(s) that fall within one symbol\'s line range, e.g. `token-goat diff "file.ts::myFn" HEAD~3..HEAD`').option("-j, --json", "output as JSON").action(
|
|
84021
|
+
program2.command("diff <spec> [ref]").description('show only the git diff hunk(s) that fall within one symbol\'s line range, e.g. `token-goat diff "file.ts::myFn" HEAD~3..HEAD` (also accepts the file::symbol@LINE anchor form documented under `read`)').option("-j, --json", "output as JSON").action(
|
|
83488
84022
|
(spec, ref2, opts) => runExit(
|
|
83489
84023
|
() => runDiff({
|
|
83490
84024
|
spec,
|
|
@@ -83493,7 +84027,7 @@ function buildProgram() {
|
|
|
83493
84027
|
})
|
|
83494
84028
|
)
|
|
83495
84029
|
);
|
|
83496
|
-
program2.command("log <spec> [ref]").description('show git commit history scoped to one symbol\'s line range, e.g. `token-goat log "file.ts::myFn" HEAD~10`').option("--max-count <n>", "maximum number of commits to show (default 20)").option("-j, --json", "output as JSON").action(
|
|
84030
|
+
program2.command("log <spec> [ref]").description('show git commit history scoped to one symbol\'s line range, e.g. `token-goat log "file.ts::myFn" HEAD~10` (also accepts the file::symbol@LINE anchor form documented under `read`)').option("--max-count <n>", "maximum number of commits to show (default 20)").option("-j, --json", "output as JSON").action(
|
|
83497
84031
|
(spec, ref2, opts) => runExit(
|
|
83498
84032
|
() => runLog({
|
|
83499
84033
|
spec,
|
|
@@ -83626,6 +84160,8 @@ async function run2(argv = process.argv) {
|
|
|
83626
84160
|
}
|
|
83627
84161
|
|
|
83628
84162
|
// src/main.ts
|
|
84163
|
+
init_util2();
|
|
84164
|
+
installEpipeGuard();
|
|
83629
84165
|
void run2();
|
|
83630
84166
|
/*! Bundled license information:
|
|
83631
84167
|
|