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-hook.mjs
CHANGED
|
@@ -49,7 +49,7 @@ var init_define_import_meta_env = __esm({
|
|
|
49
49
|
import { createRequire } from "node:module";
|
|
50
50
|
function resolveVersion() {
|
|
51
51
|
if (true) {
|
|
52
|
-
return "2.6.
|
|
52
|
+
return "2.6.25";
|
|
53
53
|
}
|
|
54
54
|
const require2 = createRequire(import.meta.url);
|
|
55
55
|
const pkg = require2("../package.json");
|
|
@@ -1469,7 +1469,7 @@ function resolveIndexPath(file2, base = process.cwd()) {
|
|
|
1469
1469
|
}
|
|
1470
1470
|
function toDisplayPath(root, target) {
|
|
1471
1471
|
if (root === void 0) return target;
|
|
1472
|
-
const rel = path2.relative(root, target).replace(/\\/g, "/");
|
|
1472
|
+
const rel = path2.relative(normalizePath(root), normalizePath(target)).replace(/\\/g, "/");
|
|
1473
1473
|
if (rel === "" || rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
1474
1474
|
return rel === "" ? "." : target;
|
|
1475
1475
|
}
|
|
@@ -1531,6 +1531,15 @@ function foldCase(s) {
|
|
|
1531
1531
|
function runGit(args, opts = {}) {
|
|
1532
1532
|
const subArgs = args[0] === "diff" ? [args[0], "--no-ext-diff", "--no-textconv", ...args.slice(1)] : args;
|
|
1533
1533
|
const fullArgs = [
|
|
1534
|
+
// Never take an optional lock. Every git call here is on someone else's
|
|
1535
|
+
// working repo, and a `status` that refreshes the index writes
|
|
1536
|
+
// `.git/index.lock`; if this process is killed mid-call -- which the hint
|
|
1537
|
+
// paths deliberately invite, since they spawn under a short timeout -- the
|
|
1538
|
+
// orphaned lock blocks every subsequent commit in that repo until a human
|
|
1539
|
+
// deletes it. Observed doing exactly that on 2026-08-05. `--no-optional-
|
|
1540
|
+
// locks` suppresses only locks git considers optional, so write commands
|
|
1541
|
+
// that genuinely need one are unaffected.
|
|
1542
|
+
"--no-optional-locks",
|
|
1534
1543
|
"-c",
|
|
1535
1544
|
"core.fsmonitor=",
|
|
1536
1545
|
"-c",
|
|
@@ -1854,6 +1863,28 @@ function writeIfDifferent(p, content, backup = false) {
|
|
|
1854
1863
|
atomicWriteText(p, content);
|
|
1855
1864
|
return true;
|
|
1856
1865
|
}
|
|
1866
|
+
function buildContextWindow(absPath, line, contextLines) {
|
|
1867
|
+
if (!Number.isFinite(contextLines) || contextLines <= 0) return null;
|
|
1868
|
+
let text;
|
|
1869
|
+
try {
|
|
1870
|
+
text = readFileSync(absPath, "utf-8");
|
|
1871
|
+
} catch {
|
|
1872
|
+
return null;
|
|
1873
|
+
}
|
|
1874
|
+
const lines2 = text.split(/\r?\n/);
|
|
1875
|
+
const idx = line - 1;
|
|
1876
|
+
if (idx < 0 || idx >= lines2.length) return null;
|
|
1877
|
+
const start = Math.max(0, idx - contextLines);
|
|
1878
|
+
const end = Math.min(lines2.length - 1, idx + contextLines);
|
|
1879
|
+
const out2 = [];
|
|
1880
|
+
for (let i = start; i <= end; i++) out2.push({ line: i + 1, text: lines2[i] ?? "" });
|
|
1881
|
+
return out2;
|
|
1882
|
+
}
|
|
1883
|
+
function renderContextWindow(displayFile, matchLine3, window, matchSuffix = "", indent = "") {
|
|
1884
|
+
return window.map(
|
|
1885
|
+
(c) => c.line === matchLine3 ? `${indent}${displayFile}:${c.line}: ${c.text}${matchSuffix}` : `${indent}${displayFile}-${c.line}- ${c.text}`
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1857
1888
|
function toKB(bytes) {
|
|
1858
1889
|
return Math.round(bytes / 1024);
|
|
1859
1890
|
}
|
|
@@ -3724,7 +3755,8 @@ function defaultConfig() {
|
|
|
3724
3755
|
compression: getDefaultConfig("compression"),
|
|
3725
3756
|
context: getDefaultConfig("context"),
|
|
3726
3757
|
injection: getDefaultConfig("injection"),
|
|
3727
|
-
hint_stats: getDefaultConfig("hint_stats")
|
|
3758
|
+
hint_stats: getDefaultConfig("hint_stats"),
|
|
3759
|
+
semantic: getDefaultConfig("semantic")
|
|
3728
3760
|
};
|
|
3729
3761
|
}
|
|
3730
3762
|
function validatedBool(raw, def) {
|
|
@@ -4166,6 +4198,10 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
4166
4198
|
const hs = getDefaultConfig("hint_stats");
|
|
4167
4199
|
hs.suppress_threshold_pct = validatedInt(hs_raw["suppress_threshold_pct"], hs.suppress_threshold_pct, ...boundsOf("hint_stats.suppress_threshold_pct"));
|
|
4168
4200
|
hs.min_sample_size = validatedInt(hs_raw["min_sample_size"], hs.min_sample_size, ...boundsOf("hint_stats.min_sample_size"));
|
|
4201
|
+
const sem_raw = section(raw, "semantic");
|
|
4202
|
+
const sem = getDefaultConfig("semantic");
|
|
4203
|
+
sem.archive_weight = validatedFloat(sem_raw["archive_weight"], sem.archive_weight, ...boundsOf("semantic.archive_weight"));
|
|
4204
|
+
sem.docs_weight = validatedFloat(sem_raw["docs_weight"], sem.docs_weight, ...boundsOf("semantic.docs_weight"));
|
|
4169
4205
|
return {
|
|
4170
4206
|
compact_assist: ca,
|
|
4171
4207
|
bash_compress: bc,
|
|
@@ -4188,7 +4224,8 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
4188
4224
|
compression: cpr,
|
|
4189
4225
|
context: ctx,
|
|
4190
4226
|
injection: inj,
|
|
4191
|
-
hint_stats: hs
|
|
4227
|
+
hint_stats: hs,
|
|
4228
|
+
semantic: sem
|
|
4192
4229
|
};
|
|
4193
4230
|
}
|
|
4194
4231
|
function saveConfig(config2) {
|
|
@@ -4345,6 +4382,10 @@ function saveConfig(config2) {
|
|
|
4345
4382
|
hint_stats: {
|
|
4346
4383
|
suppress_threshold_pct: config2.hint_stats.suppress_threshold_pct,
|
|
4347
4384
|
min_sample_size: config2.hint_stats.min_sample_size
|
|
4385
|
+
},
|
|
4386
|
+
semantic: {
|
|
4387
|
+
archive_weight: config2.semantic.archive_weight,
|
|
4388
|
+
docs_weight: config2.semantic.docs_weight
|
|
4348
4389
|
}
|
|
4349
4390
|
};
|
|
4350
4391
|
const toml = stringify(data);
|
|
@@ -4537,6 +4578,10 @@ var init_config = __esm({
|
|
|
4537
4578
|
hint_stats: {
|
|
4538
4579
|
suppress_threshold_pct: 15,
|
|
4539
4580
|
min_sample_size: 5
|
|
4581
|
+
},
|
|
4582
|
+
semantic: {
|
|
4583
|
+
archive_weight: 0.7,
|
|
4584
|
+
docs_weight: 0.92
|
|
4540
4585
|
}
|
|
4541
4586
|
};
|
|
4542
4587
|
NUMERIC_FIELD_BOUNDS = {
|
|
@@ -4600,7 +4645,9 @@ var init_config = __esm({
|
|
|
4600
4645
|
"indexing.large_file_skip_kb": { min: 1, max: 1048576 },
|
|
4601
4646
|
"context.model_window_tokens": { min: 1e4, max: 1e7 },
|
|
4602
4647
|
"hint_stats.suppress_threshold_pct": { min: 0, max: 100 },
|
|
4603
|
-
"hint_stats.min_sample_size": { min: 1, max: 1e4 }
|
|
4648
|
+
"hint_stats.min_sample_size": { min: 1, max: 1e4 },
|
|
4649
|
+
"semantic.archive_weight": { min: 0.05, max: 1 },
|
|
4650
|
+
"semantic.docs_weight": { min: 0.05, max: 1 }
|
|
4604
4651
|
};
|
|
4605
4652
|
ENUM_FIELD_VALUES = {
|
|
4606
4653
|
"compression.profile": ["auto", "aggressive", "balanced", "minimal"],
|
|
@@ -13971,7 +14018,8 @@ function rerankHits(hits, query, topK) {
|
|
|
13971
14018
|
boost = Math.min(matches2 * _VERBATIM_TOKEN_BOOST, _MAX_VERBATIM_BOOST);
|
|
13972
14019
|
}
|
|
13973
14020
|
const penalty = _isGeneratedPath(hit.filePath) ? _GENERATED_PATH_PENALTY : 0;
|
|
13974
|
-
|
|
14021
|
+
const pathPenalty = _pathPriorityPenalty(hit.filePath);
|
|
14022
|
+
return { hit, index, adjusted: hit.distance - boost + penalty + pathPenalty };
|
|
13975
14023
|
});
|
|
13976
14024
|
scored.sort((a, b) => a.adjusted - b.adjusted || a.index - b.index);
|
|
13977
14025
|
return scored.slice(0, topK).map((entry) => ({ ...entry.hit, adjustedDistance: entry.adjusted }));
|
|
@@ -14097,11 +14145,26 @@ function _isGeneratedPath(filePath) {
|
|
|
14097
14145
|
}
|
|
14098
14146
|
return false;
|
|
14099
14147
|
}
|
|
14100
|
-
|
|
14148
|
+
function _pathPriorityPenalty(filePath) {
|
|
14149
|
+
const segments = filePath.split(/[/\\]+/);
|
|
14150
|
+
const basename22 = segments[segments.length - 1] ?? filePath;
|
|
14151
|
+
const weights = loadConfig().semantic;
|
|
14152
|
+
const isArchive = _ARCHIVE_FILE_RE.test(basename22) || segments.some((seg) => _ARCHIVE_PATH_SEGMENTS.has(seg.toLowerCase()));
|
|
14153
|
+
if (isArchive) {
|
|
14154
|
+
return 1 - weights.archive_weight;
|
|
14155
|
+
}
|
|
14156
|
+
const isDocs = _DOCS_FILE_RE.test(basename22) || segments.some((seg) => seg.toLowerCase() === _DOCS_DIR_SEGMENT);
|
|
14157
|
+
if (isDocs) {
|
|
14158
|
+
return 1 - weights.docs_weight;
|
|
14159
|
+
}
|
|
14160
|
+
return 0;
|
|
14161
|
+
}
|
|
14162
|
+
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;
|
|
14101
14163
|
var init_embeddings = __esm({
|
|
14102
14164
|
"src/embeddings.ts"() {
|
|
14103
14165
|
"use strict";
|
|
14104
14166
|
init_define_import_meta_env();
|
|
14167
|
+
init_config();
|
|
14105
14168
|
init_sql_path();
|
|
14106
14169
|
init_util2();
|
|
14107
14170
|
init_reset();
|
|
@@ -14150,6 +14213,17 @@ var init_embeddings = __esm({
|
|
|
14150
14213
|
".ruff_cache"
|
|
14151
14214
|
]);
|
|
14152
14215
|
_GENERATED_PATH_PENALTY = 0.5;
|
|
14216
|
+
_ARCHIVE_PATH_SEGMENTS = /* @__PURE__ */ new Set([
|
|
14217
|
+
"archive",
|
|
14218
|
+
"archived",
|
|
14219
|
+
"old",
|
|
14220
|
+
"deprecated",
|
|
14221
|
+
"plans",
|
|
14222
|
+
"drafts"
|
|
14223
|
+
]);
|
|
14224
|
+
_ARCHIVE_FILE_RE = /(^changelog|\.bak$|\.orig$)/i;
|
|
14225
|
+
_DOCS_FILE_RE = /\.md$/i;
|
|
14226
|
+
_DOCS_DIR_SEGMENT = "docs";
|
|
14153
14227
|
_VERBATIM_TOKEN_BOOST = 0.05;
|
|
14154
14228
|
_MAX_VERBATIM_BOOST = 0.25;
|
|
14155
14229
|
_TOKEN_RE = /\w+/g;
|
|
@@ -24529,14 +24603,32 @@ function fetchTopSymbols(limit, dbPath, rootDir) {
|
|
|
24529
24603
|
try {
|
|
24530
24604
|
const db = getDb(dbPath);
|
|
24531
24605
|
const { clause, param } = projectScopeClause("file_path");
|
|
24606
|
+
const refScope = projectScopeClause("file_path");
|
|
24532
24607
|
const rows = db.prepare(
|
|
24608
|
+
// 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.
|
|
24533
24609
|
`SELECT file_path, name, kind, line_start, line_end, body, docstring, parent
|
|
24534
|
-
FROM
|
|
24535
|
-
|
|
24536
|
-
|
|
24610
|
+
FROM (
|
|
24611
|
+
SELECT s.file_path, s.name, s.kind, s.line_start, s.line_end, s.body, s.docstring, s.parent,
|
|
24612
|
+
COALESCE(r.ref_count, 0) * 1.0 / COUNT(*) OVER (PARTITION BY s.name) AS score,
|
|
24613
|
+
ROW_NUMBER() OVER (
|
|
24614
|
+
PARTITION BY s.name
|
|
24615
|
+
ORDER BY LENGTH(COALESCE(s.body, '')) DESC, s.file_path
|
|
24616
|
+
) AS rn
|
|
24617
|
+
FROM symbols s
|
|
24618
|
+
LEFT JOIN (
|
|
24619
|
+
SELECT name, COUNT(*) AS ref_count
|
|
24620
|
+
FROM refs
|
|
24621
|
+
WHERE ${refScope.clause}
|
|
24622
|
+
GROUP BY name
|
|
24623
|
+
) r ON r.name = s.name
|
|
24624
|
+
WHERE s.kind IN ('class', 'function', 'interface') AND ${clause}
|
|
24625
|
+
)
|
|
24626
|
+
WHERE rn = 1
|
|
24627
|
+
ORDER BY score DESC,
|
|
24628
|
+
CASE kind WHEN 'class' THEN 0 WHEN 'interface' THEN 1 ELSE 2 END,
|
|
24537
24629
|
LENGTH(COALESCE(body, '')) DESC
|
|
24538
24630
|
LIMIT ?`
|
|
24539
|
-
).all(param(rootDir), limit);
|
|
24631
|
+
).all(refScope.param(rootDir), param(rootDir), limit);
|
|
24540
24632
|
return rows.map((r) => ({
|
|
24541
24633
|
filePath: r.file_path,
|
|
24542
24634
|
name: r.name,
|
|
@@ -24588,13 +24680,12 @@ function formatProjectMap(map3, compact = false) {
|
|
|
24588
24680
|
lines2.push("");
|
|
24589
24681
|
lines2.push("## Top symbols");
|
|
24590
24682
|
for (const s of map3.topSymbols) {
|
|
24591
|
-
|
|
24592
|
-
|
|
24593
|
-
} else {
|
|
24594
|
-
const loc = `${path34.basename(s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
24595
|
-
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
24596
|
-
}
|
|
24683
|
+
const loc = `${toDisplayPath(map3.rootDir, s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
24684
|
+
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
24597
24685
|
}
|
|
24686
|
+
} else {
|
|
24687
|
+
lines2.push("");
|
|
24688
|
+
lines2.push("## Top symbols: none \u2014 no files indexed for this project; run 'token-goat index .'");
|
|
24598
24689
|
}
|
|
24599
24690
|
if (!compact && map3.recentFiles.length > 0) {
|
|
24600
24691
|
lines2.push("");
|
|
@@ -25102,6 +25193,11 @@ function isDeadSymbol(name2, refCount) {
|
|
|
25102
25193
|
if (ENTRY_NAMES.has(name2)) return false;
|
|
25103
25194
|
return refCount === 0;
|
|
25104
25195
|
}
|
|
25196
|
+
function parseGraphSymbolSpec(spec) {
|
|
25197
|
+
const colonIdx = findSpecSeparator(spec);
|
|
25198
|
+
if (colonIdx === -1) return { name: spec };
|
|
25199
|
+
return { name: spec.slice(colonIdx + 2), file: spec.slice(0, colonIdx) };
|
|
25200
|
+
}
|
|
25105
25201
|
function buildFileSymCache() {
|
|
25106
25202
|
const cache = /* @__PURE__ */ new Map();
|
|
25107
25203
|
return (fp) => {
|
|
@@ -25119,9 +25215,10 @@ function fileDefinesName(fp, name2, getSyms) {
|
|
|
25119
25215
|
function filterRefsForSymbol(refs, name2, filePath, getSyms) {
|
|
25120
25216
|
return refs.filter((ref2) => ref2.filePath === filePath || !fileDefinesName(ref2.filePath, name2, getSyms));
|
|
25121
25217
|
}
|
|
25122
|
-
function resolveCallers(name2, limit, filePath, rootDir) {
|
|
25218
|
+
function resolveCallers(name2, limit, filePath, rootDir, excludeTests) {
|
|
25123
25219
|
const resolvedRootDir = rootDir ?? resolveProjectRoot({ project: process.cwd() });
|
|
25124
|
-
const
|
|
25220
|
+
const queryLimit = excludeTests === true ? UNBOUNDED_REF_LIMIT : limit ?? 500;
|
|
25221
|
+
const refs = queryRefs({ name: name2, limit: queryLimit, rootDir: resolvedRootDir });
|
|
25125
25222
|
const getSyms = buildFileSymCache();
|
|
25126
25223
|
const scoped = filePath === void 0 ? refs : filterRefsForSymbol(refs, name2, filePath, getSyms);
|
|
25127
25224
|
return scoped.map((ref2) => {
|
|
@@ -25140,17 +25237,37 @@ function runCallers(opts) {
|
|
|
25140
25237
|
return 1;
|
|
25141
25238
|
}
|
|
25142
25239
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25143
|
-
const
|
|
25240
|
+
const { name: name2, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25241
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25242
|
+
if (fileHint !== void 0 && querySymbols({ name: name2, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25243
|
+
emitErr(`Symbol '${name2}' not found in '${file2}'`);
|
|
25244
|
+
return 1;
|
|
25245
|
+
}
|
|
25246
|
+
const resolved = resolveCallers(name2, opts.limit, fileHint, rootDir, opts.excludeTests);
|
|
25247
|
+
const suppressed = opts.excludeTests === true ? resolved.filter((e) => isTestFile(e.file)).length : 0;
|
|
25248
|
+
const entries = opts.excludeTests === true ? resolved.filter((e) => !isTestFile(e.file)).slice(0, opts.limit ?? 500) : resolved;
|
|
25144
25249
|
if (entries.length === 0) {
|
|
25250
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25251
|
+
emitErr(`No non-test references found for '${opts.symbol}' (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25252
|
+
return 1;
|
|
25253
|
+
}
|
|
25145
25254
|
emitErr(`No references found for '${opts.symbol}'`);
|
|
25146
25255
|
return 1;
|
|
25147
25256
|
}
|
|
25257
|
+
const contextLines = opts.context ?? 0;
|
|
25148
25258
|
if (opts.json === true) {
|
|
25149
|
-
|
|
25259
|
+
const payload = contextLines > 0 ? entries.map((e) => ({ ...e, contextLines: buildContextWindow(e.file, e.line, contextLines) ?? [] })) : entries;
|
|
25260
|
+
emit2(JSON.stringify(payload, null, 2));
|
|
25150
25261
|
return 0;
|
|
25151
25262
|
}
|
|
25263
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25264
|
+
emit2(`${entries.length} callers found (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25265
|
+
}
|
|
25152
25266
|
for (const e of entries) {
|
|
25153
|
-
|
|
25267
|
+
const displayPath = toDisplayPath(rootDir, e.file);
|
|
25268
|
+
emit2(`${e.caller} ${displayPath}:${e.line}`);
|
|
25269
|
+
const window = buildContextWindow(e.file, e.line, contextLines);
|
|
25270
|
+
if (window !== null) for (const l of renderContextWindow(displayPath, e.line, window, "", " ")) emit2(l);
|
|
25154
25271
|
}
|
|
25155
25272
|
return 0;
|
|
25156
25273
|
}
|
|
@@ -25161,28 +25278,36 @@ function runCallChain(opts) {
|
|
|
25161
25278
|
}
|
|
25162
25279
|
const maxDepth = opts.depth ?? 8;
|
|
25163
25280
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25164
|
-
|
|
25281
|
+
const { name: name2, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25282
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25283
|
+
if (fileHint !== void 0) {
|
|
25284
|
+
if (querySymbols({ name: name2, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25285
|
+
emitErr(`Symbol '${name2}' not found in '${file2}'`);
|
|
25286
|
+
return 1;
|
|
25287
|
+
}
|
|
25288
|
+
} else if (querySymbols({ name: name2, rootDir, limit: 1 }).length === 0) {
|
|
25165
25289
|
emitErr(`Symbol not found: ${opts.symbol}`);
|
|
25166
25290
|
return 1;
|
|
25167
25291
|
}
|
|
25168
25292
|
const getSyms = buildFileSymCache();
|
|
25169
|
-
const callersOf = (
|
|
25170
|
-
const refs = queryRefs({ name:
|
|
25293
|
+
const callersOf = (n) => {
|
|
25294
|
+
const refs = queryRefs({ name: n, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25171
25295
|
if (refs.length === 0) return [];
|
|
25296
|
+
const scoped = fileHint !== void 0 && n === name2 ? filterRefsForSymbol(refs, n, fileHint, getSyms) : refs;
|
|
25172
25297
|
const names = /* @__PURE__ */ new Set();
|
|
25173
|
-
for (const ref2 of
|
|
25298
|
+
for (const ref2 of scoped) {
|
|
25174
25299
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25175
25300
|
if (enc !== null) names.add(enc.name);
|
|
25176
25301
|
}
|
|
25177
25302
|
return [...names];
|
|
25178
25303
|
};
|
|
25179
|
-
const chains = bfsCallChains(
|
|
25304
|
+
const chains = bfsCallChains(name2, callersOf, maxDepth);
|
|
25180
25305
|
if (opts.json === true) {
|
|
25181
25306
|
emit2(JSON.stringify({ chains }, null, 2));
|
|
25182
25307
|
return 0;
|
|
25183
25308
|
}
|
|
25184
|
-
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] ===
|
|
25185
|
-
emit2(`${
|
|
25309
|
+
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] === name2) {
|
|
25310
|
+
emit2(`${name2} (no callers)`);
|
|
25186
25311
|
return 0;
|
|
25187
25312
|
}
|
|
25188
25313
|
for (const chain2 of chains) {
|
|
@@ -25202,16 +25327,23 @@ function runImpact(opts) {
|
|
|
25202
25327
|
const top = opts.top ?? 20;
|
|
25203
25328
|
const DEPTH_CAP = 8;
|
|
25204
25329
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25330
|
+
const { name: rootName, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25331
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25332
|
+
if (fileHint !== void 0 && querySymbols({ name: rootName, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25333
|
+
emitErr(`Symbol '${rootName}' not found in '${file2}'`);
|
|
25334
|
+
return 1;
|
|
25335
|
+
}
|
|
25205
25336
|
const getSyms = buildFileSymCache();
|
|
25206
|
-
const hops = /* @__PURE__ */ new Map([[
|
|
25207
|
-
const queue = [[
|
|
25337
|
+
const hops = /* @__PURE__ */ new Map([[rootName, 0]]);
|
|
25338
|
+
const queue = [[rootName, 0]];
|
|
25208
25339
|
while (queue.length > 0) {
|
|
25209
25340
|
const item = queue.shift();
|
|
25210
25341
|
if (item === void 0) break;
|
|
25211
25342
|
const [name2, depth] = item;
|
|
25212
25343
|
if (depth >= DEPTH_CAP) continue;
|
|
25213
25344
|
const refs = queryRefs({ name: name2, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25214
|
-
|
|
25345
|
+
const scoped = fileHint !== void 0 && name2 === rootName ? filterRefsForSymbol(refs, name2, fileHint, getSyms) : refs;
|
|
25346
|
+
for (const ref2 of scoped) {
|
|
25215
25347
|
const newHop = depth + 1;
|
|
25216
25348
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25217
25349
|
if (enc === null) {
|
|
@@ -25228,7 +25360,7 @@ function runImpact(opts) {
|
|
|
25228
25360
|
}
|
|
25229
25361
|
}
|
|
25230
25362
|
}
|
|
25231
|
-
hops.delete(
|
|
25363
|
+
hops.delete(rootName);
|
|
25232
25364
|
const sorted = [...hops.entries()].sort(compareHopEntries).slice(0, top);
|
|
25233
25365
|
if (sorted.length === 0) {
|
|
25234
25366
|
emitErr(`No callers found for '${opts.symbol}'`);
|
|
@@ -25280,6 +25412,7 @@ function runDead(opts) {
|
|
|
25280
25412
|
const syms = querySymbols({ kind, limit: 5e3, rootDir });
|
|
25281
25413
|
const getSyms = buildFileSymCache();
|
|
25282
25414
|
const results = [];
|
|
25415
|
+
let suppressed = 0;
|
|
25283
25416
|
for (const sym of syms) {
|
|
25284
25417
|
if (opts.includePrivate !== true && sym.name.startsWith("_")) continue;
|
|
25285
25418
|
const refs = queryRefs({ name: sym.name, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
@@ -25289,6 +25422,10 @@ function runDead(opts) {
|
|
|
25289
25422
|
const ownScope = enclosingNamedScope(getSyms(sym.filePath), sym.lineStart);
|
|
25290
25423
|
if (ownScope !== null && hasAncestorDispatchRef(sym.name, ownScope.name, sym.filePath, rootDir)) continue;
|
|
25291
25424
|
}
|
|
25425
|
+
if (opts.excludeTests === true && isTestFile(sym.filePath)) {
|
|
25426
|
+
suppressed += 1;
|
|
25427
|
+
continue;
|
|
25428
|
+
}
|
|
25292
25429
|
results.push({ name: sym.name, kind: sym.kind, file: sym.filePath, line: sym.lineStart });
|
|
25293
25430
|
}
|
|
25294
25431
|
const sliced = results.slice(0, opts.top ?? results.length);
|
|
@@ -25297,9 +25434,16 @@ function runDead(opts) {
|
|
|
25297
25434
|
return 0;
|
|
25298
25435
|
}
|
|
25299
25436
|
if (sliced.length === 0) {
|
|
25300
|
-
|
|
25437
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25438
|
+
emit2(`No dead symbols found (${suppressed} in test files hidden by --exclude-tests).`);
|
|
25439
|
+
} else {
|
|
25440
|
+
emit2("No dead symbols found.");
|
|
25441
|
+
}
|
|
25301
25442
|
return 0;
|
|
25302
25443
|
}
|
|
25444
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25445
|
+
emit2(`${sliced.length} dead symbols (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25446
|
+
}
|
|
25303
25447
|
for (const r of sliced) {
|
|
25304
25448
|
emit2(`${r.name} ${toDisplayPath(rootDir, r.file)}:${r.line}`);
|
|
25305
25449
|
}
|
|
@@ -25549,16 +25693,9 @@ function runSimilar(opts) {
|
|
|
25549
25693
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
25550
25694
|
return 1;
|
|
25551
25695
|
}
|
|
25552
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
25553
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
25554
25696
|
const top = opts.top ?? 10;
|
|
25555
|
-
const
|
|
25556
|
-
|
|
25557
|
-
if (anchors.length === 0) {
|
|
25558
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
25559
|
-
return 1;
|
|
25560
|
-
}
|
|
25561
|
-
const anchor = anchors[0];
|
|
25697
|
+
const anchor = resolveSymbolSpecOrEmitError("similar", opts.spec, void 0);
|
|
25698
|
+
if (anchor === null) return 1;
|
|
25562
25699
|
const words = [anchor.name, ...(anchor.docstring ?? "").split(/\s+/).filter((w) => w.length > 4)];
|
|
25563
25700
|
const query = words.slice(0, 8).join(" ");
|
|
25564
25701
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
@@ -25590,13 +25727,13 @@ function runContextFor(opts) {
|
|
|
25590
25727
|
const bodyTokens = estimateTokens(h.body ?? "");
|
|
25591
25728
|
if (budget !== void 0 && tokensSoFar + bodyTokens > budget) continue;
|
|
25592
25729
|
tokensSoFar += bodyTokens;
|
|
25593
|
-
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, readCmd: `token-goat read "${h.filePath}::${h.name}"` });
|
|
25730
|
+
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, line: h.lineStart, readCmd: `token-goat read "${h.filePath}::${h.name}@${h.lineStart}"` });
|
|
25594
25731
|
}
|
|
25595
25732
|
if (opts.json === true) {
|
|
25596
25733
|
emit2(JSON.stringify(entries, null, 2));
|
|
25597
25734
|
return 0;
|
|
25598
25735
|
}
|
|
25599
|
-
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}"`);
|
|
25736
|
+
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
25600
25737
|
return 0;
|
|
25601
25738
|
}
|
|
25602
25739
|
function runTestFor(opts) {
|
|
@@ -25741,16 +25878,10 @@ function runBlame(opts) {
|
|
|
25741
25878
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
25742
25879
|
return 1;
|
|
25743
25880
|
}
|
|
25744
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
25745
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
25746
25881
|
const cwd = opts.cwd ?? process.cwd();
|
|
25747
|
-
const
|
|
25748
|
-
|
|
25749
|
-
|
|
25750
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
25751
|
-
return 1;
|
|
25752
|
-
}
|
|
25753
|
-
const sym = syms[0];
|
|
25882
|
+
const sym = resolveSymbolSpecOrEmitError("blame", opts.spec, void 0);
|
|
25883
|
+
if (sym === null) return 1;
|
|
25884
|
+
const filePath = sym.filePath;
|
|
25754
25885
|
const start = sym.lineStart;
|
|
25755
25886
|
const end = sym.lineEnd;
|
|
25756
25887
|
let raw;
|
|
@@ -25771,10 +25902,10 @@ function runBlame(opts) {
|
|
|
25771
25902
|
if (!m) return { raw: l };
|
|
25772
25903
|
return { commit: m[1], author: (m[2] ?? "").trim(), date: (m[3] ?? "").trim(), line: Number.parseInt(m[4] ?? "0", 10), content: m[5] };
|
|
25773
25904
|
});
|
|
25774
|
-
emit2(JSON.stringify({ symbol:
|
|
25905
|
+
emit2(JSON.stringify({ symbol: sym.name, file: filePath, lines: lines2 }, null, 2));
|
|
25775
25906
|
return 0;
|
|
25776
25907
|
}
|
|
25777
|
-
emit2(`${
|
|
25908
|
+
emit2(`${sym.name} ${toDisplayPath(getDisplayRoot(opts.cwd), filePath)}:${start}-${end}`);
|
|
25778
25909
|
emit2(raw.trim());
|
|
25779
25910
|
return 0;
|
|
25780
25911
|
}
|
|
@@ -25788,14 +25919,14 @@ function runAsk(opts) {
|
|
|
25788
25919
|
const hits = searchSymbolsFts(opts.question, top, void 0, rootDir);
|
|
25789
25920
|
const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
|
|
25790
25921
|
const backendLabel = process.env[BACKEND_ENV] ?? "";
|
|
25791
|
-
const entries = hits.map((h) => ({ file: h.filePath, symbol: h.name, kind: h.kind, readCmd: `token-goat read "${h.filePath}::${h.name}"` }));
|
|
25922
|
+
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}"` }));
|
|
25792
25923
|
const degrade = () => {
|
|
25793
25924
|
if (opts.json === true) {
|
|
25794
25925
|
emit2(JSON.stringify({ degraded: true, note: `Set ${BACKEND_ENV}=claude|codex for LLM synthesis`, context: entries }, null, 2));
|
|
25795
25926
|
return 0;
|
|
25796
25927
|
}
|
|
25797
25928
|
emit2(`[degraded mode - set ${BACKEND_ENV}=claude|codex for LLM synthesis]`);
|
|
25798
|
-
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}"`);
|
|
25929
|
+
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
25799
25930
|
return 0;
|
|
25800
25931
|
};
|
|
25801
25932
|
if (!backendLabel) return degrade();
|
|
@@ -29963,6 +30094,42 @@ function formatBareNameSpecError(command, name2, projectRoot) {
|
|
|
29963
30094
|
}
|
|
29964
30095
|
return lines2.join("\n");
|
|
29965
30096
|
}
|
|
30097
|
+
function formatCrossFileLead(command, name2, excludeFilePath, projectRoot) {
|
|
30098
|
+
const rootDir = projectRoot ?? process.cwd();
|
|
30099
|
+
const matches2 = querySymbols({ name: name2, limit: 50, rootDir });
|
|
30100
|
+
const excludeResolved = resolveIndexPath(excludeFilePath, rootDir);
|
|
30101
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30102
|
+
const specs = [];
|
|
30103
|
+
for (const m of matches2) {
|
|
30104
|
+
if (foldPath(m.filePath) === foldPath(excludeResolved)) continue;
|
|
30105
|
+
const spec = `${toDisplayPath(rootDir, m.filePath)}::${m.name}`;
|
|
30106
|
+
if (seen.has(spec)) continue;
|
|
30107
|
+
seen.add(spec);
|
|
30108
|
+
specs.push(spec);
|
|
30109
|
+
}
|
|
30110
|
+
if (specs.length === 0) return "";
|
|
30111
|
+
const firstSpec = specs[0];
|
|
30112
|
+
const lines2 = [`'${name2}' is defined in ${firstSpec !== void 0 ? firstSpec.split("::")[0] : ""}`];
|
|
30113
|
+
for (const spec of specs.slice(0, DIDYOUMEAN_LIMIT)) {
|
|
30114
|
+
lines2.push(` - token-goat ${command} "${spec}"`);
|
|
30115
|
+
}
|
|
30116
|
+
if (specs.length > DIDYOUMEAN_LIMIT) {
|
|
30117
|
+
lines2.push(` (${specs.length - DIDYOUMEAN_LIMIT} more not shown)`);
|
|
30118
|
+
}
|
|
30119
|
+
return lines2.join("\n");
|
|
30120
|
+
}
|
|
30121
|
+
function resolveEnclosingSymbol(filePath, chunkStartLine) {
|
|
30122
|
+
const symbols = querySymbols({ filePath, limit: 1e5 }, globalDbPath());
|
|
30123
|
+
let best = null;
|
|
30124
|
+
for (const s of symbols) {
|
|
30125
|
+
if (s.lineStart <= chunkStartLine && chunkStartLine <= s.lineEnd) {
|
|
30126
|
+
if (best === null || s.lineEnd - s.lineStart < best.lineEnd - best.lineStart) {
|
|
30127
|
+
best = s;
|
|
30128
|
+
}
|
|
30129
|
+
}
|
|
30130
|
+
}
|
|
30131
|
+
return best === null ? null : { name: best.name, kind: best.kind };
|
|
30132
|
+
}
|
|
29966
30133
|
function trimBlankLines(lines2) {
|
|
29967
30134
|
let start = 0;
|
|
29968
30135
|
let end = lines2.length;
|
|
@@ -30058,10 +30225,21 @@ function parseCrossFileMultiSpec(spec) {
|
|
|
30058
30225
|
}
|
|
30059
30226
|
return pairs.length > 1 ? pairs : null;
|
|
30060
30227
|
}
|
|
30228
|
+
function parseMultiFileSpec(spec) {
|
|
30229
|
+
if (!spec.includes(",")) return null;
|
|
30230
|
+
if (fileExists(spec)) return null;
|
|
30231
|
+
if (findSpecSeparator(spec) !== -1) return null;
|
|
30232
|
+
const parts = spec.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
30233
|
+
return parts.length > 1 ? parts : null;
|
|
30234
|
+
}
|
|
30235
|
+
function extraFileArgsNote(command, first2, extras) {
|
|
30236
|
+
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(",")}"`;
|
|
30237
|
+
}
|
|
30061
30238
|
function parseLineRange(spec) {
|
|
30062
30239
|
const m = /^(.+)@(\d+)(?:-(\d+))?$/.exec(spec);
|
|
30063
30240
|
if (m === null) return null;
|
|
30064
30241
|
if (fileExists(spec)) return null;
|
|
30242
|
+
if (m[1].includes("::")) return null;
|
|
30065
30243
|
const start = parseInt(m[2], 10);
|
|
30066
30244
|
const end = m[3] !== void 0 ? parseInt(m[3], 10) : start;
|
|
30067
30245
|
return { file: m[1], start, end };
|
|
@@ -30120,30 +30298,49 @@ function findParentName(entry, fileSymbols) {
|
|
|
30120
30298
|
if (doc !== "" && PARENT_IDENTIFIER_RE.test(doc)) return doc;
|
|
30121
30299
|
return null;
|
|
30122
30300
|
}
|
|
30123
|
-
function formatAmbiguity(symbol3, file2, candidates, explicitRoot) {
|
|
30301
|
+
function formatAmbiguity(symbol3, file2, candidates, explicitRoot, commandName = "read") {
|
|
30124
30302
|
const multiFile = new Set(candidates.map((c) => c.filePath)).size > 1;
|
|
30125
30303
|
const displayRoot = getDisplayRoot(explicitRoot);
|
|
30126
30304
|
const lines2 = [
|
|
30127
30305
|
`Ambiguous symbol '${symbol3}' in '${file2}': ${candidates.length} definitions match. Retry with one of the qualified commands below to pick one:`
|
|
30128
30306
|
];
|
|
30129
30307
|
const fileSymCache = /* @__PURE__ */ new Map();
|
|
30130
|
-
|
|
30131
|
-
let fileSyms = fileSymCache.get(
|
|
30308
|
+
const getFileSyms = (filePath) => {
|
|
30309
|
+
let fileSyms = fileSymCache.get(filePath);
|
|
30132
30310
|
if (fileSyms === void 0) {
|
|
30133
|
-
fileSyms = querySymbols({ filePath
|
|
30134
|
-
fileSymCache.set(
|
|
30311
|
+
fileSyms = querySymbols({ filePath, limit: 1e3 });
|
|
30312
|
+
fileSymCache.set(filePath, fileSyms);
|
|
30135
30313
|
}
|
|
30136
|
-
|
|
30137
|
-
|
|
30314
|
+
return fileSyms;
|
|
30315
|
+
};
|
|
30316
|
+
const parents = candidates.map((c) => findParentName(c, getFileSyms(c.filePath)));
|
|
30317
|
+
const plainQualifiers = candidates.map((c, i) => parents[i] !== null ? `${parents[i]}.${symbol3}` : symbol3);
|
|
30318
|
+
const qualifierCounts = /* @__PURE__ */ new Map();
|
|
30319
|
+
const fileGroupSize = /* @__PURE__ */ new Map();
|
|
30320
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30321
|
+
const c = candidates[i];
|
|
30322
|
+
const key = `${c.filePath} ${plainQualifiers[i]}`;
|
|
30323
|
+
qualifierCounts.set(key, (qualifierCounts.get(key) ?? 0) + 1);
|
|
30324
|
+
fileGroupSize.set(c.filePath, (fileGroupSize.get(c.filePath) ?? 0) + 1);
|
|
30325
|
+
}
|
|
30326
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30327
|
+
const c = candidates[i];
|
|
30328
|
+
const parent = parents[i];
|
|
30329
|
+
const plainQualifier = plainQualifiers[i];
|
|
30330
|
+
const collides = (qualifierCounts.get(`${c.filePath} ${plainQualifier}`) ?? 0) > 1 || parent === null && (fileGroupSize.get(c.filePath) ?? 0) > 1;
|
|
30331
|
+
const qualifier = collides ? `${plainQualifier}@${c.lineStart}` : plainQualifier;
|
|
30138
30332
|
const retryFile = multiFile ? toDisplayPath(displayRoot, c.filePath) : file2;
|
|
30139
30333
|
const label = multiFile ? `${toDisplayPath(displayRoot, c.filePath)}::${qualifier}` : qualifier;
|
|
30140
|
-
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat
|
|
30334
|
+
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat ${commandName} "${retryFile}::${qualifier}"`);
|
|
30141
30335
|
}
|
|
30142
30336
|
return lines2.join("\n");
|
|
30143
30337
|
}
|
|
30144
30338
|
function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
30145
|
-
const { file: file2, symbol:
|
|
30146
|
-
if (
|
|
30339
|
+
const { file: file2, symbol: rawSymbol } = parseReadSpec(spec);
|
|
30340
|
+
if (rawSymbol === void 0 || rawSymbol === "") return { kind: "none" };
|
|
30341
|
+
const anchorMatch = /^(.+)@(\d+)$/.exec(rawSymbol);
|
|
30342
|
+
const symbol3 = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
|
|
30343
|
+
const lineAnchor = anchorMatch !== null ? parseInt(anchorMatch[2], 10) : void 0;
|
|
30147
30344
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
30148
30345
|
if (forceRefresh === true) {
|
|
30149
30346
|
indexFileSync(resolved, globalDbPath());
|
|
@@ -30160,9 +30357,10 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
|
30160
30357
|
seen.add(key);
|
|
30161
30358
|
distinct.push(c);
|
|
30162
30359
|
}
|
|
30163
|
-
|
|
30164
|
-
if (
|
|
30165
|
-
return { kind: "
|
|
30360
|
+
const anchored = lineAnchor === void 0 ? distinct : distinct.filter((c) => c.lineStart === lineAnchor);
|
|
30361
|
+
if (anchored.length === 0) return { kind: "none" };
|
|
30362
|
+
if (anchored.length === 1) return { kind: "ok", entry: anchored[0] };
|
|
30363
|
+
return { kind: "ambiguous", symbol: displaySymbol, file: file2, candidates: anchored };
|
|
30166
30364
|
};
|
|
30167
30365
|
if (symbol3.includes(".")) {
|
|
30168
30366
|
const exactMatch = querySymbols({ name: symbol3, filePath: resolved, limit: 10 });
|
|
@@ -30242,6 +30440,8 @@ function runRead(opts) {
|
|
|
30242
30440
|
return runLineRange({ file: file2, start: lineSpec.start, end: lineSpec.end }, opts);
|
|
30243
30441
|
}
|
|
30244
30442
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
30443
|
+
const crossFileLead = formatCrossFileLead("read", symbol3, file2, opts.projectRoot);
|
|
30444
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
30245
30445
|
const resolved = resolveIndexPath(file2, opts.projectRoot ?? process.cwd());
|
|
30246
30446
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
30247
30447
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -30302,6 +30502,8 @@ ${sub.text}`);
|
|
|
30302
30502
|
return { text, code: 1 };
|
|
30303
30503
|
}
|
|
30304
30504
|
function runSection(opts) {
|
|
30505
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
30506
|
+
if (crossFilePairs !== null) return runSectionCrossFile(crossFilePairs, opts);
|
|
30305
30507
|
const colonIdx = findSpecSeparator(opts.spec);
|
|
30306
30508
|
if (colonIdx === -1) {
|
|
30307
30509
|
return { text: `Invalid section spec \u2014 expected "file::Heading", got: ${opts.spec}`, code: 1 };
|
|
@@ -30358,6 +30560,42 @@ ${sub.text}`);
|
|
|
30358
30560
|
if (anyFound) recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
30359
30561
|
return { text, code: anyFound ? 0 : 1 };
|
|
30360
30562
|
}
|
|
30563
|
+
function runSectionCrossFile(pairs, opts) {
|
|
30564
|
+
let anyFound = false;
|
|
30565
|
+
const jsonOut = {};
|
|
30566
|
+
const textBlocks = [];
|
|
30567
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
30568
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
30569
|
+
for (const { file: file2, symbol: heading } of pairs) {
|
|
30570
|
+
const sub = runSection({ ...opts, spec: `${file2}::${heading}`, suppressStat: true });
|
|
30571
|
+
if (sub.code === 0) anyFound = true;
|
|
30572
|
+
const key = keyFor({ file: file2, symbol: heading });
|
|
30573
|
+
if (opts.json === true) {
|
|
30574
|
+
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
30575
|
+
continue;
|
|
30576
|
+
}
|
|
30577
|
+
textBlocks.push(`${key}:
|
|
30578
|
+
${sub.text}`);
|
|
30579
|
+
}
|
|
30580
|
+
const resolvePath = (f) => opts.projectRoot !== void 0 && !path47.isAbsolute(f) ? path47.resolve(opts.projectRoot, f) : f;
|
|
30581
|
+
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
30582
|
+
if (anyFound) {
|
|
30583
|
+
const fullSourceBytes = sumFileSizes(Array.from(distinctFiles, resolvePath));
|
|
30584
|
+
recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
30585
|
+
}
|
|
30586
|
+
return { text, code: anyFound ? 0 : 1 };
|
|
30587
|
+
}
|
|
30588
|
+
function renderRefLines(ref2, displayRoot, contextLines, indent = " ") {
|
|
30589
|
+
const displayPath = toDisplayPath(displayRoot, ref2.filePath);
|
|
30590
|
+
const base = `${indent}${displayPath}:${ref2.line}: ${ref2.context}`;
|
|
30591
|
+
const window = buildContextWindow(ref2.filePath, ref2.line, contextLines);
|
|
30592
|
+
if (window === null) return [base];
|
|
30593
|
+
return [base, ...renderContextWindow(displayPath, ref2.line, window, "", `${indent} `)];
|
|
30594
|
+
}
|
|
30595
|
+
function withContextLines(items, contextLines) {
|
|
30596
|
+
if (!(contextLines > 0)) return items;
|
|
30597
|
+
return items.map((r) => ({ ...r, contextLines: buildContextWindow(r.filePath, r.line, contextLines) ?? [] }));
|
|
30598
|
+
}
|
|
30361
30599
|
function applyTypedRefsTier(symName, file2, results) {
|
|
30362
30600
|
if (results.length === 0) return results;
|
|
30363
30601
|
try {
|
|
@@ -30395,6 +30633,8 @@ function runRefs(opts) {
|
|
|
30395
30633
|
emitErr2(`--top must be a positive number, got: ${opts.top}`);
|
|
30396
30634
|
return 1;
|
|
30397
30635
|
}
|
|
30636
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
30637
|
+
if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
|
|
30398
30638
|
const { file: file2, symbols } = parseMultiRefsSpec(opts.spec);
|
|
30399
30639
|
if (symbols.length <= 1) return runRefsSingle(opts);
|
|
30400
30640
|
const jsonOut = {};
|
|
@@ -30403,10 +30643,18 @@ function runRefs(opts) {
|
|
|
30403
30643
|
const refFilePaths = [];
|
|
30404
30644
|
for (const sym of symbols) {
|
|
30405
30645
|
const queryOpts = { name: sym };
|
|
30406
|
-
if (
|
|
30407
|
-
if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30646
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30647
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30408
30648
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30409
|
-
|
|
30649
|
+
let results = applyTypedRefsTier(sym, file2, queryRefs(queryOpts));
|
|
30650
|
+
let suppressed = 0;
|
|
30651
|
+
let filteredTotal;
|
|
30652
|
+
if (opts.excludeTests === true) {
|
|
30653
|
+
const f = applyExcludeTestsFilter(results);
|
|
30654
|
+
suppressed = f.suppressed;
|
|
30655
|
+
filteredTotal = f.refs.length;
|
|
30656
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
30657
|
+
}
|
|
30410
30658
|
if (results.length > 0) anyFound = true;
|
|
30411
30659
|
refFilePaths.push(...results.map((r) => r.filePath));
|
|
30412
30660
|
if (opts.json === true) {
|
|
@@ -30414,22 +30662,85 @@ function runRefs(opts) {
|
|
|
30414
30662
|
jsonOut[sym] = topFilesJsonPayload(results, opts.top);
|
|
30415
30663
|
} else {
|
|
30416
30664
|
const capped = guardJsonRows(results);
|
|
30417
|
-
const trueTotal = countRefs(queryOpts);
|
|
30418
|
-
jsonOut[sym] = { items: capped.items, truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30665
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
30666
|
+
jsonOut[sym] = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30419
30667
|
}
|
|
30420
30668
|
continue;
|
|
30421
30669
|
}
|
|
30422
30670
|
if (results.length === 0) {
|
|
30423
|
-
lines2.push(`${sym}: (no references found)`);
|
|
30671
|
+
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)`);
|
|
30424
30672
|
continue;
|
|
30425
30673
|
}
|
|
30426
30674
|
lines2.push(`${sym}:`);
|
|
30427
30675
|
if (opts.top !== void 0) {
|
|
30428
|
-
lines2.push(...renderTopFilesSummary(results, opts.top));
|
|
30676
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
30429
30677
|
} else if (opts.callers === true) {
|
|
30430
|
-
lines2.push(
|
|
30678
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
30679
|
+
lines2.push(...renderCallerGroups(results, void 0, opts.context ?? 0));
|
|
30431
30680
|
} else {
|
|
30432
|
-
|
|
30681
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
30682
|
+
for (const ref2 of results) lines2.push(...renderRefLines(ref2, void 0, opts.context ?? 0));
|
|
30683
|
+
}
|
|
30684
|
+
}
|
|
30685
|
+
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
30686
|
+
if (opts.json === true) {
|
|
30687
|
+
const text2 = JSON.stringify(jsonOut, null, 2);
|
|
30688
|
+
emit3(text2);
|
|
30689
|
+
if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text2, opts.spec);
|
|
30690
|
+
return anyFound ? 0 : 1;
|
|
30691
|
+
}
|
|
30692
|
+
const text = lines2.join("\n");
|
|
30693
|
+
emitGuarded(text, "symbol");
|
|
30694
|
+
if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
|
|
30695
|
+
return anyFound ? 0 : 1;
|
|
30696
|
+
}
|
|
30697
|
+
function runRefsCrossFile(pairs, opts) {
|
|
30698
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
30699
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
30700
|
+
const jsonOut = {};
|
|
30701
|
+
let anyFound = false;
|
|
30702
|
+
const lines2 = [];
|
|
30703
|
+
const refFilePaths = [];
|
|
30704
|
+
for (const { file: file2, symbol: symbol3 } of pairs) {
|
|
30705
|
+
const key = keyFor({ file: file2, symbol: symbol3 });
|
|
30706
|
+
const queryOpts = { name: symbol3 };
|
|
30707
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30708
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30709
|
+
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30710
|
+
let results = applyTypedRefsTier(symbol3, file2, queryRefs(queryOpts));
|
|
30711
|
+
let suppressed = 0;
|
|
30712
|
+
let filteredTotal;
|
|
30713
|
+
if (opts.excludeTests === true) {
|
|
30714
|
+
const f = applyExcludeTestsFilter(results);
|
|
30715
|
+
suppressed = f.suppressed;
|
|
30716
|
+
filteredTotal = f.refs.length;
|
|
30717
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
30718
|
+
}
|
|
30719
|
+
if (results.length > 0) anyFound = true;
|
|
30720
|
+
refFilePaths.push(...results.map((r) => r.filePath));
|
|
30721
|
+
if (opts.json === true) {
|
|
30722
|
+
if (opts.top !== void 0) {
|
|
30723
|
+
jsonOut[key] = topFilesJsonPayload(results, opts.top);
|
|
30724
|
+
} else {
|
|
30725
|
+
const capped = guardJsonRows(results);
|
|
30726
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
30727
|
+
jsonOut[key] = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30728
|
+
}
|
|
30729
|
+
continue;
|
|
30730
|
+
}
|
|
30731
|
+
if (results.length === 0) {
|
|
30732
|
+
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)`);
|
|
30733
|
+
continue;
|
|
30734
|
+
}
|
|
30735
|
+
lines2.push(`${key}:`);
|
|
30736
|
+
if (opts.top !== void 0) {
|
|
30737
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
30738
|
+
} else if (opts.callers === true) {
|
|
30739
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
30740
|
+
lines2.push(...renderCallerGroups(results, void 0, opts.context ?? 0));
|
|
30741
|
+
} else {
|
|
30742
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
30743
|
+
for (const ref2 of results) lines2.push(...renderRefLines(ref2, void 0, opts.context ?? 0));
|
|
30433
30744
|
}
|
|
30434
30745
|
}
|
|
30435
30746
|
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
@@ -30449,11 +30760,23 @@ function runRefsSingle(opts) {
|
|
|
30449
30760
|
const symName = symbol3 ?? file2;
|
|
30450
30761
|
const queryOpts = { name: symName };
|
|
30451
30762
|
const defFileHint = symbol3 !== void 0 ? resolveIndexPath(file2) : void 0;
|
|
30452
|
-
if (
|
|
30453
|
-
if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30763
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30764
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30454
30765
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30455
|
-
|
|
30766
|
+
let results = applyTypedRefsTier(symName, defFileHint, queryRefs(queryOpts));
|
|
30767
|
+
let suppressed = 0;
|
|
30768
|
+
let filteredTotal;
|
|
30769
|
+
if (opts.excludeTests === true) {
|
|
30770
|
+
const f = applyExcludeTestsFilter(results);
|
|
30771
|
+
suppressed = f.suppressed;
|
|
30772
|
+
filteredTotal = f.refs.length;
|
|
30773
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
30774
|
+
}
|
|
30456
30775
|
if (results.length === 0) {
|
|
30776
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
30777
|
+
emitErr2(`No non-test references found for '${symName}' (${suppressed} in test files hidden by --exclude-tests)`);
|
|
30778
|
+
return 1;
|
|
30779
|
+
}
|
|
30457
30780
|
emitErr2(`No references found for '${symName}'`);
|
|
30458
30781
|
return 1;
|
|
30459
30782
|
}
|
|
@@ -30464,8 +30787,8 @@ function runRefsSingle(opts) {
|
|
|
30464
30787
|
payload = topFilesJsonPayload(results, opts.top);
|
|
30465
30788
|
} else {
|
|
30466
30789
|
const capped = guardJsonRows(results);
|
|
30467
|
-
const trueTotal = countRefs(queryOpts);
|
|
30468
|
-
payload = { items: capped.items, truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30790
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
30791
|
+
payload = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30469
30792
|
}
|
|
30470
30793
|
const text2 = JSON.stringify(payload, null, 2);
|
|
30471
30794
|
emit3(text2);
|
|
@@ -30473,21 +30796,26 @@ function runRefsSingle(opts) {
|
|
|
30473
30796
|
return 0;
|
|
30474
30797
|
}
|
|
30475
30798
|
const displayRoot = getDisplayRoot();
|
|
30476
|
-
const lines2 = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, displayRoot) : opts.callers === true ? renderCallerGroups(results, displayRoot) : results.
|
|
30799
|
+
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, ""))];
|
|
30477
30800
|
const text = lines2.join("\n");
|
|
30478
30801
|
emitGuarded(text, "symbol");
|
|
30479
30802
|
recordReadStat("symbol_read", fullSourceBytes, text, symName);
|
|
30480
30803
|
return 0;
|
|
30481
30804
|
}
|
|
30805
|
+
function applyExcludeTestsFilter(refs) {
|
|
30806
|
+
const filtered = refs.filter((r) => !isTestFile(r.filePath));
|
|
30807
|
+
return { refs: filtered, suppressed: refs.length - filtered.length };
|
|
30808
|
+
}
|
|
30482
30809
|
function groupRefsByFile(refs) {
|
|
30483
30810
|
const byFile = /* @__PURE__ */ new Map();
|
|
30484
30811
|
for (const ref2 of refs) byFile.set(ref2.filePath, (byFile.get(ref2.filePath) ?? 0) + 1);
|
|
30485
30812
|
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));
|
|
30486
30813
|
}
|
|
30487
|
-
function renderTopFilesSummary(refs, topN, displayRoot) {
|
|
30814
|
+
function renderTopFilesSummary(refs, topN, displayRoot, suppressed) {
|
|
30488
30815
|
const grouped = groupRefsByFile(refs);
|
|
30489
30816
|
const shown = grouped.slice(0, topN);
|
|
30490
|
-
const
|
|
30817
|
+
const suppressedNote = suppressed !== void 0 && suppressed > 0 ? ` (${suppressed} in test files hidden by --exclude-tests)` : "";
|
|
30818
|
+
const lines2 = [`${refs.length} references across ${grouped.length} files (showing top ${shown.length})${suppressedNote}`];
|
|
30491
30819
|
for (const { file: file2, count } of shown) lines2.push(` ${count} ${toDisplayPath(displayRoot, file2)}`);
|
|
30492
30820
|
const omittedFiles = grouped.length - shown.length;
|
|
30493
30821
|
if (omittedFiles > 0) {
|
|
@@ -30501,7 +30829,7 @@ function topFilesJsonPayload(refs, topN) {
|
|
|
30501
30829
|
const shown = grouped.slice(0, topN);
|
|
30502
30830
|
return { fileCounts: shown, totalFiles: grouped.length, totalRefs: refs.length, shown: shown.length };
|
|
30503
30831
|
}
|
|
30504
|
-
function renderCallerGroups(refs, displayRoot) {
|
|
30832
|
+
function renderCallerGroups(refs, displayRoot, contextLines = 0) {
|
|
30505
30833
|
const byFile = /* @__PURE__ */ new Map();
|
|
30506
30834
|
for (const ref2 of refs) {
|
|
30507
30835
|
const bucket = byFile.get(ref2.filePath);
|
|
@@ -30513,9 +30841,12 @@ function renderCallerGroups(refs, displayRoot) {
|
|
|
30513
30841
|
}
|
|
30514
30842
|
const lines2 = [];
|
|
30515
30843
|
for (const [file2, fileRefs] of byFile) {
|
|
30516
|
-
|
|
30844
|
+
const displayPath = toDisplayPath(displayRoot, file2);
|
|
30845
|
+
lines2.push(`${displayPath}:`);
|
|
30517
30846
|
for (const ref2 of fileRefs) {
|
|
30518
30847
|
lines2.push(` :${ref2.line} ${ref2.context !== "" ? ref2.context : "(module scope)"}`);
|
|
30848
|
+
const window = buildContextWindow(file2, ref2.line, contextLines);
|
|
30849
|
+
if (window !== null) lines2.push(...renderContextWindow(displayPath, ref2.line, window, "", " "));
|
|
30519
30850
|
}
|
|
30520
30851
|
}
|
|
30521
30852
|
return lines2;
|
|
@@ -30554,7 +30885,19 @@ function prepareSymbolListing(file2, opts) {
|
|
|
30554
30885
|
const fullSourceBytes = sumFileSizes([resolved]);
|
|
30555
30886
|
return { kind: "ok", resolved, filtered, refCounts, fullSourceBytes, symbolsTruncated, trueSymbolCount };
|
|
30556
30887
|
}
|
|
30888
|
+
function runPerFileListing(files, run2) {
|
|
30889
|
+
const blocks = [];
|
|
30890
|
+
let anyOk = false;
|
|
30891
|
+
for (const file2 of files) {
|
|
30892
|
+
const r = run2(file2);
|
|
30893
|
+
if (r.code === 0) anyOk = true;
|
|
30894
|
+
blocks.push(r.text);
|
|
30895
|
+
}
|
|
30896
|
+
return { text: blocks.join("\n\n"), code: anyOk ? 0 : 1 };
|
|
30897
|
+
}
|
|
30557
30898
|
function runSkeleton(opts) {
|
|
30899
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
30900
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runSkeleton({ ...opts, file: file2 }));
|
|
30558
30901
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
30559
30902
|
if (prep.kind === "empty") {
|
|
30560
30903
|
return { text: prep.text, code: 1 };
|
|
@@ -30590,6 +30933,8 @@ function runSkeleton(opts) {
|
|
|
30590
30933
|
return { text, code: 0 };
|
|
30591
30934
|
}
|
|
30592
30935
|
function runOutline(opts) {
|
|
30936
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
30937
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runOutline({ ...opts, file: file2 }));
|
|
30593
30938
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
30594
30939
|
if (prep.kind === "empty") {
|
|
30595
30940
|
return { text: prep.text, code: 1 };
|
|
@@ -31164,10 +31509,13 @@ function runBriefCore(opts) {
|
|
|
31164
31509
|
const resolution = resolveSymbolSpec(opts.spec);
|
|
31165
31510
|
if (resolution.kind === "ambiguous") {
|
|
31166
31511
|
return {
|
|
31512
|
+
// 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.
|
|
31167
31513
|
text: formatAmbiguity(
|
|
31168
31514
|
resolution.symbol,
|
|
31169
31515
|
resolution.file,
|
|
31170
|
-
resolution.candidates
|
|
31516
|
+
resolution.candidates,
|
|
31517
|
+
void 0,
|
|
31518
|
+
"brief"
|
|
31171
31519
|
),
|
|
31172
31520
|
code: 1
|
|
31173
31521
|
};
|
|
@@ -31190,7 +31538,7 @@ function runBriefCore(opts) {
|
|
|
31190
31538
|
if (opts.json === true) {
|
|
31191
31539
|
const result = {
|
|
31192
31540
|
symbol: match2,
|
|
31193
|
-
callers: shown,
|
|
31541
|
+
callers: (opts.context ?? 0) > 0 ? shown.map((c) => ({ ...c, contextLines: buildContextWindow(c.file, c.line, opts.context ?? 0) ?? [] })) : shown,
|
|
31194
31542
|
totalCallers,
|
|
31195
31543
|
truncated,
|
|
31196
31544
|
section: section2
|
|
@@ -31209,7 +31557,10 @@ function runBriefCore(opts) {
|
|
|
31209
31557
|
];
|
|
31210
31558
|
lines2.push(`Callers (${totalCallers}):`);
|
|
31211
31559
|
for (const c of shown) {
|
|
31212
|
-
|
|
31560
|
+
const callerDisplayPath = toDisplayPath(rootDir, c.file);
|
|
31561
|
+
lines2.push(` ${c.caller} ${callerDisplayPath}:${c.line}`);
|
|
31562
|
+
const window = buildContextWindow(c.file, c.line, opts.context ?? 0);
|
|
31563
|
+
if (window !== null) lines2.push(...renderContextWindow(callerDisplayPath, c.line, window, "", " "));
|
|
31213
31564
|
}
|
|
31214
31565
|
if (truncated) {
|
|
31215
31566
|
lines2.push(` ...(${totalCallers - shown.length} more elided)`);
|
|
@@ -31241,11 +31592,40 @@ ${sub.text}`);
|
|
|
31241
31592
|
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
31242
31593
|
return { text, code: anyFound ? 0 : 1 };
|
|
31243
31594
|
}
|
|
31595
|
+
function runBriefCrossFile(pairs, opts) {
|
|
31596
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
31597
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
31598
|
+
let anyFound = false;
|
|
31599
|
+
const jsonOut = {};
|
|
31600
|
+
const textBlocks = [];
|
|
31601
|
+
for (const { file: file2, symbol: symbol3 } of pairs) {
|
|
31602
|
+
const key = keyFor({ file: file2, symbol: symbol3 });
|
|
31603
|
+
const sub = runBriefCore({ ...opts, spec: `${file2}::${symbol3}`, suppressStat: true });
|
|
31604
|
+
if (sub.code === 0) anyFound = true;
|
|
31605
|
+
if (opts.json === true) {
|
|
31606
|
+
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
31607
|
+
continue;
|
|
31608
|
+
}
|
|
31609
|
+
textBlocks.push(`${key}:
|
|
31610
|
+
${sub.text}`);
|
|
31611
|
+
}
|
|
31612
|
+
const fullSourceBytes = sumFileSizes([...distinctFiles].map((f) => resolveIndexPath(f, process.cwd())));
|
|
31613
|
+
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
31614
|
+
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
31615
|
+
return { text, code: anyFound ? 0 : 1 };
|
|
31616
|
+
}
|
|
31244
31617
|
function runBrief(opts) {
|
|
31245
31618
|
if (opts.limit !== void 0 && opts.limit <= 0) {
|
|
31246
31619
|
emitErr2(`--limit must be a positive number, got: ${opts.limit}`);
|
|
31247
31620
|
return 1;
|
|
31248
31621
|
}
|
|
31622
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
31623
|
+
if (crossFilePairs !== null) {
|
|
31624
|
+
const { text: text2, code: code2 } = runBriefCrossFile(crossFilePairs, opts);
|
|
31625
|
+
if (code2 === 0) emit3(text2);
|
|
31626
|
+
else emitErr2(text2);
|
|
31627
|
+
return code2;
|
|
31628
|
+
}
|
|
31249
31629
|
const { file: file2, symbol: symbol3 } = parseReadSpec(opts.spec);
|
|
31250
31630
|
if (symbol3 !== void 0 && symbol3 !== "" && symbol3.includes(",")) {
|
|
31251
31631
|
const multiSymbols = symbol3.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -31337,6 +31717,34 @@ function parseDiffHunks(diffText) {
|
|
|
31337
31717
|
}
|
|
31338
31718
|
return hunksByFile;
|
|
31339
31719
|
}
|
|
31720
|
+
function buildChangedRefHint(cwd, ref2) {
|
|
31721
|
+
const countResult = runGit(["rev-list", "--count", "HEAD"], { cwd });
|
|
31722
|
+
if (countResult.exitCode !== 0) {
|
|
31723
|
+
return null;
|
|
31724
|
+
}
|
|
31725
|
+
const commitCount = Number.parseInt(countResult.stdout.trim(), 10);
|
|
31726
|
+
if (!Number.isFinite(commitCount) || commitCount < 1) {
|
|
31727
|
+
return null;
|
|
31728
|
+
}
|
|
31729
|
+
const refResolves = runGit(["rev-parse", "--verify", "--quiet", ref2], { cwd });
|
|
31730
|
+
if (refResolves.exitCode === 0) {
|
|
31731
|
+
return null;
|
|
31732
|
+
}
|
|
31733
|
+
let suggestedRef = null;
|
|
31734
|
+
for (let n = commitCount - 1; n >= 1; n--) {
|
|
31735
|
+
const candidate = `HEAD~${n}`;
|
|
31736
|
+
const candidateResolves = runGit(["rev-parse", "--verify", "--quiet", candidate], { cwd });
|
|
31737
|
+
if (candidateResolves.exitCode === 0) {
|
|
31738
|
+
suggestedRef = candidate;
|
|
31739
|
+
break;
|
|
31740
|
+
}
|
|
31741
|
+
}
|
|
31742
|
+
if (suggestedRef === null) {
|
|
31743
|
+
suggestedRef = EMPTY_TREE_HASH;
|
|
31744
|
+
}
|
|
31745
|
+
const commitWord = commitCount === 1 ? "1 commit" : `${commitCount} commits`;
|
|
31746
|
+
return `Hint: this repo has only ${commitWord}; '${ref2}' does not exist. Try: token-goat changed --since ${suggestedRef}`;
|
|
31747
|
+
}
|
|
31340
31748
|
function runChanged(opts = {}) {
|
|
31341
31749
|
const ref2 = opts.ref ?? "HEAD~5";
|
|
31342
31750
|
const cwd = opts.projectRoot ?? process.cwd();
|
|
@@ -31346,6 +31754,10 @@ function runChanged(opts = {}) {
|
|
|
31346
31754
|
const result = runGit(["diff", ref2, "--name-only"], { cwd });
|
|
31347
31755
|
if (result.exitCode !== 0) {
|
|
31348
31756
|
emitErr2(`git diff failed: ${result.stderr}`);
|
|
31757
|
+
const hint = buildChangedRefHint(cwd, ref2);
|
|
31758
|
+
if (hint !== null) {
|
|
31759
|
+
emitErr2(hint);
|
|
31760
|
+
}
|
|
31349
31761
|
return 1;
|
|
31350
31762
|
}
|
|
31351
31763
|
changedFiles = result.stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
@@ -31451,13 +31863,16 @@ function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
|
|
|
31451
31863
|
resolution.symbol,
|
|
31452
31864
|
resolution.file,
|
|
31453
31865
|
resolution.candidates,
|
|
31454
|
-
projectRoot
|
|
31866
|
+
projectRoot,
|
|
31867
|
+
commandName
|
|
31455
31868
|
)
|
|
31456
31869
|
);
|
|
31457
31870
|
return null;
|
|
31458
31871
|
}
|
|
31459
31872
|
if (resolution.kind === "none") {
|
|
31460
31873
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
31874
|
+
const crossFileLead = formatCrossFileLead(commandName, symbol3, file2, projectRoot);
|
|
31875
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
31461
31876
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
31462
31877
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
31463
31878
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -31667,22 +32082,29 @@ function runGrep(opts) {
|
|
|
31667
32082
|
return 1;
|
|
31668
32083
|
}
|
|
31669
32084
|
const truncated = hits.slice(0, maxLines);
|
|
32085
|
+
if (opts.symbol === true) {
|
|
32086
|
+
const symbolsByFile = /* @__PURE__ */ new Map();
|
|
32087
|
+
for (const hit of truncated) {
|
|
32088
|
+
let syms = symbolsByFile.get(hit.file);
|
|
32089
|
+
if (syms === void 0) {
|
|
32090
|
+
syms = querySymbols({ filePath: resolveIndexPath(hit.file), limit: ALL_SYMBOLS_IN_FILE_LIMIT });
|
|
32091
|
+
symbolsByFile.set(hit.file, syms);
|
|
32092
|
+
}
|
|
32093
|
+
const enc = enclosingSymbol(syms, hit.line);
|
|
32094
|
+
hit.symbol = enc === null ? null : { name: enc.name, kind: enc.kind, lineStart: enc.lineStart, lineEnd: enc.lineEnd };
|
|
32095
|
+
}
|
|
32096
|
+
}
|
|
31670
32097
|
if (opts.json === true) {
|
|
31671
32098
|
const payload = { items: truncated, truncated: hits.length > maxLines, totalCount: hits.length };
|
|
31672
32099
|
emit3(JSON.stringify(payload, null, 2));
|
|
31673
32100
|
return 0;
|
|
31674
32101
|
}
|
|
31675
32102
|
for (const hit of truncated) {
|
|
32103
|
+
const symbolTag = opts.symbol === true && hit.symbol != null ? ` [${hit.symbol.name} (${hit.symbol.kind})]` : "";
|
|
31676
32104
|
if (hit.context !== void 0) {
|
|
31677
|
-
for (const
|
|
31678
|
-
if (ctxLine.line === hit.line) {
|
|
31679
|
-
emit3(`${hit.file}:${ctxLine.line}: ${ctxLine.text}`);
|
|
31680
|
-
} else {
|
|
31681
|
-
emit3(`${hit.file}-${ctxLine.line}- ${ctxLine.text}`);
|
|
31682
|
-
}
|
|
31683
|
-
}
|
|
32105
|
+
for (const line of renderContextWindow(hit.file, hit.line, hit.context, symbolTag)) emit3(line);
|
|
31684
32106
|
} else {
|
|
31685
|
-
emit3(`${hit.file}:${hit.line}: ${hit.text}`);
|
|
32107
|
+
emit3(`${hit.file}:${hit.line}: ${hit.text}${symbolTag}`);
|
|
31686
32108
|
}
|
|
31687
32109
|
}
|
|
31688
32110
|
if (hits.length > maxLines) {
|
|
@@ -31865,9 +32287,24 @@ function extractExportNames(text, ext2) {
|
|
|
31865
32287
|
}
|
|
31866
32288
|
return names;
|
|
31867
32289
|
}
|
|
32290
|
+
function runPerFileEmitting(files, label, run2) {
|
|
32291
|
+
let anyOk = false;
|
|
32292
|
+
files.forEach((file2, i) => {
|
|
32293
|
+
if (i > 0) emit3("");
|
|
32294
|
+
emit3(`# ${label}: ${file2}`);
|
|
32295
|
+
if (run2(file2) === 0) anyOk = true;
|
|
32296
|
+
});
|
|
32297
|
+
return anyOk ? 0 : 1;
|
|
32298
|
+
}
|
|
31868
32299
|
function runExports(opts) {
|
|
32300
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
32301
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Exports", (file2) => runExports({ ...opts, file: file2 }));
|
|
31869
32302
|
const symbols = querySymbols({ filePath: resolveIndexPath(opts.file), limit: 500 });
|
|
31870
32303
|
const kindOf = (name2) => symbols.find((s) => s.name === name2)?.kind ?? "export";
|
|
32304
|
+
const locOf = (name2) => {
|
|
32305
|
+
const s = symbols.find((sym) => sym.name === name2);
|
|
32306
|
+
return s === void 0 ? null : { lineStart: s.lineStart, lineEnd: s.lineEnd };
|
|
32307
|
+
};
|
|
31871
32308
|
const names = [];
|
|
31872
32309
|
for (const s of symbols) {
|
|
31873
32310
|
if (/^(?:export|pub\b|public\b)/.test(s.body.trimStart()) && !names.includes(s.name)) {
|
|
@@ -31892,12 +32329,23 @@ function runExports(opts) {
|
|
|
31892
32329
|
}
|
|
31893
32330
|
const fullSourceBytes = sumFileSizes([opts.file]);
|
|
31894
32331
|
if (opts.json === true) {
|
|
31895
|
-
const jsonText = JSON.stringify(
|
|
32332
|
+
const jsonText = JSON.stringify(
|
|
32333
|
+
names.map((n) => {
|
|
32334
|
+
const loc = locOf(n);
|
|
32335
|
+
return { name: n, kind: kindOf(n), lineStart: loc?.lineStart ?? null, lineEnd: loc?.lineEnd ?? null };
|
|
32336
|
+
}),
|
|
32337
|
+
null,
|
|
32338
|
+
2
|
|
32339
|
+
);
|
|
31896
32340
|
emit3(jsonText);
|
|
31897
32341
|
recordReadStat("exports", fullSourceBytes, jsonText, opts.file);
|
|
31898
32342
|
return 0;
|
|
31899
32343
|
}
|
|
31900
|
-
const outLines = names.map((n) =>
|
|
32344
|
+
const outLines = names.map((n) => {
|
|
32345
|
+
const loc = locOf(n);
|
|
32346
|
+
const locSuffix = loc === null ? "" : ` (${loc.lineStart}-${loc.lineEnd})`;
|
|
32347
|
+
return `${kindOf(n).padEnd(10)} ${n}${locSuffix}`;
|
|
32348
|
+
});
|
|
31901
32349
|
for (const line of outLines) {
|
|
31902
32350
|
emit3(line);
|
|
31903
32351
|
}
|
|
@@ -32191,6 +32639,8 @@ function importsExtensionFor(filePath) {
|
|
|
32191
32639
|
return path47.extname(filePath);
|
|
32192
32640
|
}
|
|
32193
32641
|
function runImports(opts) {
|
|
32642
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
32643
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Imports", (file2) => runImports({ ...opts, file: file2 }));
|
|
32194
32644
|
const text = readFileText(opts.file);
|
|
32195
32645
|
if (text === null) {
|
|
32196
32646
|
emitErr2(`Could not read: ${opts.file}`);
|
|
@@ -32284,11 +32734,12 @@ async function runSemantic(query, opts) {
|
|
|
32284
32734
|
);
|
|
32285
32735
|
const hits = mergeNearbyHits(rawHits).slice(0, n);
|
|
32286
32736
|
if (hits.length > 0) {
|
|
32737
|
+
const enclosing = hits.map((h) => resolveEnclosingSymbol(h.filePath, h.startLine));
|
|
32287
32738
|
if (opts.json === true) {
|
|
32288
|
-
const items = hits.map((h) => ({
|
|
32739
|
+
const items = hits.map((h, i) => ({
|
|
32289
32740
|
filePath: h.filePath,
|
|
32290
|
-
name: null,
|
|
32291
|
-
kind: null,
|
|
32741
|
+
name: enclosing[i]?.name ?? null,
|
|
32742
|
+
kind: enclosing[i]?.kind ?? null,
|
|
32292
32743
|
startLine: h.startLine,
|
|
32293
32744
|
endLine: h.endLine,
|
|
32294
32745
|
distance: h.distance,
|
|
@@ -32299,10 +32750,12 @@ async function runSemantic(query, opts) {
|
|
|
32299
32750
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
|
|
32300
32751
|
return { text: text3, code: 0 };
|
|
32301
32752
|
}
|
|
32302
|
-
const blocks2 = hits.map(
|
|
32303
|
-
|
|
32304
|
-
${
|
|
32305
|
-
|
|
32753
|
+
const blocks2 = hits.map((h, i) => {
|
|
32754
|
+
const enc = enclosing[i] ?? null;
|
|
32755
|
+
const suffix = enc !== null ? ` \u2014 inside ${enc.name} (${enc.kind})` : "";
|
|
32756
|
+
return `# ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine} (distance ${h.distance.toFixed(3)})${suffix}
|
|
32757
|
+
${previewLines(h.text, 3)}`;
|
|
32758
|
+
});
|
|
32306
32759
|
const text2 = guardText(blocks2.join("\n\n"), "semantic");
|
|
32307
32760
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text2, query);
|
|
32308
32761
|
return { text: text2, code: 0 };
|
|
@@ -32391,7 +32844,7 @@ function runNoteList(opts = {}) {
|
|
|
32391
32844
|
});
|
|
32392
32845
|
return { text: lines2.join("\n"), code: 0 };
|
|
32393
32846
|
}
|
|
32394
|
-
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;
|
|
32847
|
+
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;
|
|
32395
32848
|
var init_read_commands = __esm({
|
|
32396
32849
|
"src/read_commands.ts"() {
|
|
32397
32850
|
"use strict";
|
|
@@ -32436,6 +32889,7 @@ var init_read_commands = __esm({
|
|
|
32436
32889
|
PARENT_IDENTIFIER_RE = /^[\w$]+$/;
|
|
32437
32890
|
SKELETON_SYMBOL_CAP = 5e3;
|
|
32438
32891
|
HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
|
|
32892
|
+
EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
32439
32893
|
DEFAULT_LOG_MAX_COUNT = 20;
|
|
32440
32894
|
}
|
|
32441
32895
|
});
|
|
@@ -49289,6 +49743,7 @@ init_index_reader();
|
|
|
49289
49743
|
init_parser_types();
|
|
49290
49744
|
init_doc_embed_extract();
|
|
49291
49745
|
init_paths();
|
|
49746
|
+
init_project();
|
|
49292
49747
|
init_hooks_index();
|
|
49293
49748
|
init_install();
|
|
49294
49749
|
init_codex_install();
|
|
@@ -64888,7 +65343,7 @@ function checkDbExists(dataDir2) {
|
|
|
64888
65343
|
return {
|
|
64889
65344
|
name: "Database",
|
|
64890
65345
|
status: "ok",
|
|
64891
|
-
message: `global.db exists (${toKB(sizeBytes)} KB)`
|
|
65346
|
+
message: `global.db exists (${toKB(sizeBytes)} KB) at ${dbPath}`
|
|
64892
65347
|
};
|
|
64893
65348
|
}
|
|
64894
65349
|
function checkSymbolBodySize(dbPath) {
|
|
@@ -64936,6 +65391,13 @@ function checkSymbolCount(dbPath, rootDir) {
|
|
|
64936
65391
|
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'`
|
|
64937
65392
|
};
|
|
64938
65393
|
}
|
|
65394
|
+
if (fileCount === 0 && symbolCount === 0) {
|
|
65395
|
+
return {
|
|
65396
|
+
name: "Symbols",
|
|
65397
|
+
status: "warn",
|
|
65398
|
+
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`
|
|
65399
|
+
};
|
|
65400
|
+
}
|
|
64939
65401
|
return {
|
|
64940
65402
|
name: "Symbols",
|
|
64941
65403
|
status: "ok",
|
|
@@ -72036,6 +72498,9 @@ function runHintStatsCommand(opts = {}) {
|
|
|
72036
72498
|
`);
|
|
72037
72499
|
return;
|
|
72038
72500
|
}
|
|
72501
|
+
if (rows.every((r) => r.emitted === 0 && r.actedOn === 0)) {
|
|
72502
|
+
process.stdout.write("No hint emissions recorded yet \u2014 the zeros below are absence of data, not measured ineffectiveness.\n");
|
|
72503
|
+
}
|
|
72039
72504
|
printSummary(rows);
|
|
72040
72505
|
}
|
|
72041
72506
|
|
|
@@ -72294,7 +72759,11 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
72294
72759
|
function cmdMap(opts) {
|
|
72295
72760
|
const map3 = buildProjectMap(process.cwd(), { compact: opts.compact === true });
|
|
72296
72761
|
const text = formatProjectMap(map3, map3.compact);
|
|
72297
|
-
|
|
72762
|
+
if (opts.json === true) {
|
|
72763
|
+
out(JSON.stringify(map3));
|
|
72764
|
+
} else {
|
|
72765
|
+
out(text);
|
|
72766
|
+
}
|
|
72298
72767
|
const bytesSaved = mapLookupBytesSaved(map3, text);
|
|
72299
72768
|
recordStat("map_lookup", bytesSaved, Math.round(bytesSaved / 4));
|
|
72300
72769
|
}
|
|
@@ -72544,6 +73013,14 @@ async function cmdDoctor(opts) {
|
|
|
72544
73013
|
if (project !== null) {
|
|
72545
73014
|
doctorOpts.rootDir = project.root;
|
|
72546
73015
|
}
|
|
73016
|
+
if (opts.json === true) {
|
|
73017
|
+
const results = runDoctor(doctorOpts.dataDir, doctorOpts.configPath, doctorOpts.rootDir);
|
|
73018
|
+
out(JSON.stringify(results));
|
|
73019
|
+
if (results.some((r) => r.status === "fail")) {
|
|
73020
|
+
throw new CliError("doctor checks failed");
|
|
73021
|
+
}
|
|
73022
|
+
return;
|
|
73023
|
+
}
|
|
72547
73024
|
const code = await runDoctorAndExit(doctorOpts);
|
|
72548
73025
|
if (code !== 0) {
|
|
72549
73026
|
throw new CliError("doctor checks failed");
|
|
@@ -73047,6 +73524,16 @@ function runExitText(fn) {
|
|
|
73047
73524
|
process.exitCode = 1;
|
|
73048
73525
|
}
|
|
73049
73526
|
}
|
|
73527
|
+
function noteExtraFileArgs(command, first2, extras, fn) {
|
|
73528
|
+
const result = fn();
|
|
73529
|
+
if (extras === void 0 || extras.length === 0) return result;
|
|
73530
|
+
return { text: `${extraFileArgsNote(command, first2, extras)}
|
|
73531
|
+
${result.text}`, code: result.code };
|
|
73532
|
+
}
|
|
73533
|
+
function emitExtraFileArgsNote(command, first2, extras) {
|
|
73534
|
+
if (extras === void 0 || extras.length === 0) return;
|
|
73535
|
+
out(extraFileArgsNote(command, first2, extras));
|
|
73536
|
+
}
|
|
73050
73537
|
function cmdCompress(opts) {
|
|
73051
73538
|
try {
|
|
73052
73539
|
if (opts.compress === false) {
|
|
@@ -73171,6 +73658,10 @@ async function cmdSkillList(opts) {
|
|
|
73171
73658
|
return `${s.name.padEnd(25)} ${bodyKb.padStart(6)}K ${compactKb.padStart(6)}K ${marker} ${s.hitCount.toString().padStart(3)} ${age.padStart(3)} ${staleStatus}`;
|
|
73172
73659
|
});
|
|
73173
73660
|
const header = `${"Name".padEnd(25)} ${"Body".padStart(6)} ${"Compact".padStart(6)} Marker Hits Age Status`;
|
|
73661
|
+
if (skills.length === 0) {
|
|
73662
|
+
out("No skills cached yet.");
|
|
73663
|
+
return;
|
|
73664
|
+
}
|
|
73174
73665
|
out([header, ...lines2].join("\n"));
|
|
73175
73666
|
}
|
|
73176
73667
|
}
|
|
@@ -74008,19 +74499,26 @@ function buildProgram() {
|
|
|
74008
74499
|
process.exitCode = 1;
|
|
74009
74500
|
}
|
|
74010
74501
|
};
|
|
74011
|
-
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(
|
|
74012
|
-
|
|
74502
|
+
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) => {
|
|
74503
|
+
let projectRoot;
|
|
74504
|
+
if (opts.project === true) {
|
|
74505
|
+
projectRoot = resolveProjectRoot({ project: process.cwd() });
|
|
74506
|
+
} else if (typeof opts.project === "string") {
|
|
74507
|
+
projectRoot = resolveProjectRoot({ project: opts.project });
|
|
74508
|
+
}
|
|
74509
|
+
return runExitText(
|
|
74013
74510
|
() => runSymbol({
|
|
74014
74511
|
name: name2,
|
|
74015
74512
|
limit: opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20,
|
|
74016
74513
|
...opts.file !== void 0 ? { file: opts.file } : {},
|
|
74017
74514
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
74515
|
+
...projectRoot !== void 0 ? { projectRoot } : {},
|
|
74018
74516
|
...opts.json === true ? { json: true } : {}
|
|
74019
74517
|
})
|
|
74020
|
-
)
|
|
74021
|
-
);
|
|
74518
|
+
);
|
|
74519
|
+
});
|
|
74022
74520
|
program2.command("read <spec>").description(
|
|
74023
|
-
"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)"
|
|
74521
|
+
"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)"
|
|
74024
74522
|
).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(
|
|
74025
74523
|
(spec, opts) => runExitText(
|
|
74026
74524
|
() => runRead({
|
|
@@ -74032,13 +74530,14 @@ function buildProgram() {
|
|
|
74032
74530
|
)
|
|
74033
74531
|
);
|
|
74034
74532
|
program2.command("brief <spec>").description(
|
|
74035
|
-
"symbol body + callers + containing doc section in one call (spec: file::symbol; comma-separated file::a,b for a merged multi-symbol view)"
|
|
74036
|
-
).option("-j, --json", "output as JSON").option("--limit <n>", "max callers to show (default: 20)").action(
|
|
74533
|
+
"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)"
|
|
74534
|
+
).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(
|
|
74037
74535
|
(spec, opts) => runExit(
|
|
74038
74536
|
() => runBrief({
|
|
74039
74537
|
spec,
|
|
74040
74538
|
...opts.json === true ? { json: true } : {},
|
|
74041
|
-
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {}
|
|
74539
|
+
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
74540
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {}
|
|
74042
74541
|
})
|
|
74043
74542
|
)
|
|
74044
74543
|
);
|
|
@@ -74048,44 +74547,56 @@ function buildProgram() {
|
|
|
74048
74547
|
(spec, opts) => opts.list === true ? runExit(() => runListSections({ file: spec, ...opts.json === true ? { json: true } : {} })) : runExitText(() => runSection({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
74049
74548
|
);
|
|
74050
74549
|
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));
|
|
74051
|
-
program2.command("skeleton <file>").description(
|
|
74052
|
-
(file2, opts) => runExitText(
|
|
74053
|
-
() =>
|
|
74054
|
-
|
|
74055
|
-
|
|
74056
|
-
|
|
74057
|
-
|
|
74058
|
-
|
|
74059
|
-
|
|
74550
|
+
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(
|
|
74551
|
+
(file2, more, opts) => runExitText(
|
|
74552
|
+
() => noteExtraFileArgs(
|
|
74553
|
+
"skeleton",
|
|
74554
|
+
file2,
|
|
74555
|
+
more,
|
|
74556
|
+
() => runSkeleton({
|
|
74557
|
+
file: file2,
|
|
74558
|
+
...opts.json === true ? { json: true } : {},
|
|
74559
|
+
...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
|
|
74560
|
+
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
74561
|
+
...opts.stats === true ? { stats: true } : {}
|
|
74562
|
+
})
|
|
74563
|
+
)
|
|
74060
74564
|
)
|
|
74061
74565
|
);
|
|
74062
|
-
program2.command("outline <file>").description(
|
|
74063
|
-
(file2, opts) => runExitText(
|
|
74064
|
-
() =>
|
|
74065
|
-
|
|
74066
|
-
|
|
74067
|
-
|
|
74068
|
-
|
|
74069
|
-
|
|
74070
|
-
|
|
74566
|
+
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(
|
|
74567
|
+
(file2, more, opts) => runExitText(
|
|
74568
|
+
() => noteExtraFileArgs(
|
|
74569
|
+
"outline",
|
|
74570
|
+
file2,
|
|
74571
|
+
more,
|
|
74572
|
+
() => runOutline({
|
|
74573
|
+
file: file2,
|
|
74574
|
+
...opts.json === true ? { json: true } : {},
|
|
74575
|
+
...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
|
|
74576
|
+
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
74577
|
+
...opts.stats === true ? { stats: true } : {}
|
|
74578
|
+
})
|
|
74579
|
+
)
|
|
74071
74580
|
)
|
|
74072
74581
|
);
|
|
74073
|
-
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(
|
|
74582
|
+
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(
|
|
74074
74583
|
"--top <n>",
|
|
74075
74584
|
"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"
|
|
74076
|
-
).option("-j, --json", "output as JSON").action(
|
|
74585
|
+
).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(
|
|
74077
74586
|
(spec, opts) => runExit(
|
|
74078
74587
|
() => runRefs({
|
|
74079
74588
|
spec,
|
|
74080
74589
|
...opts.callers === true ? { callers: true } : {},
|
|
74081
74590
|
...opts.json === true ? { json: true } : {},
|
|
74082
74591
|
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
74083
|
-
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {}
|
|
74592
|
+
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
74593
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
74594
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
74084
74595
|
})
|
|
74085
74596
|
)
|
|
74086
74597
|
);
|
|
74087
74598
|
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));
|
|
74088
|
-
program2.command("map").description("project overview").option("-c, --compact", "compact, low-token summary").action(guard(cmdMap));
|
|
74599
|
+
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));
|
|
74089
74600
|
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));
|
|
74090
74601
|
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));
|
|
74091
74602
|
program2.command("mcp-serve").description("run token-goat as an MCP stdio server exposing surgical reads and local compression/handoff tools").action(guard(cmdMcpServe));
|
|
@@ -74101,7 +74612,7 @@ function buildProgram() {
|
|
|
74101
74612
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
74102
74613
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
74103
74614
|
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));
|
|
74104
|
-
program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").action(guard(cmdDoctor));
|
|
74615
|
+
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));
|
|
74105
74616
|
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));
|
|
74106
74617
|
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));
|
|
74107
74618
|
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));
|
|
@@ -74115,11 +74626,17 @@ function buildProgram() {
|
|
|
74115
74626
|
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));
|
|
74116
74627
|
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));
|
|
74117
74628
|
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));
|
|
74118
|
-
program2.command("exports <file>").description(
|
|
74119
|
-
(file2, opts) => runExit(() =>
|
|
74629
|
+
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(
|
|
74630
|
+
(file2, more, opts) => runExit(() => {
|
|
74631
|
+
emitExtraFileArgsNote("exports", file2, more);
|
|
74632
|
+
return runExports({ file: file2, ...opts.json === true ? { json: true } : {} });
|
|
74633
|
+
})
|
|
74120
74634
|
);
|
|
74121
|
-
program2.command("imports <file>").description(
|
|
74122
|
-
(file2, opts) => runExit(() =>
|
|
74635
|
+
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(
|
|
74636
|
+
(file2, more, opts) => runExit(() => {
|
|
74637
|
+
emitExtraFileArgsNote("imports", file2, more);
|
|
74638
|
+
return runImports({ file: file2, ...opts.json === true ? { json: true } : {} });
|
|
74639
|
+
})
|
|
74123
74640
|
);
|
|
74124
74641
|
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(
|
|
74125
74642
|
(pattern, opts) => runExit(
|
|
@@ -74130,7 +74647,7 @@ function buildProgram() {
|
|
|
74130
74647
|
})
|
|
74131
74648
|
)
|
|
74132
74649
|
);
|
|
74133
|
-
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(
|
|
74650
|
+
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(
|
|
74134
74651
|
(pattern, paths, opts) => runExit(
|
|
74135
74652
|
() => runGrep({
|
|
74136
74653
|
pattern,
|
|
@@ -74138,7 +74655,8 @@ function buildProgram() {
|
|
|
74138
74655
|
...opts.json === true ? { json: true } : {},
|
|
74139
74656
|
...opts.maxLines !== void 0 ? { maxLines: requirePositiveInt("--max-lines", opts.maxLines) } : {},
|
|
74140
74657
|
...opts.recursive === false ? { recursive: false } : {},
|
|
74141
|
-
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {}
|
|
74658
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
74659
|
+
...opts.symbol === true ? { symbol: true } : {}
|
|
74142
74660
|
})
|
|
74143
74661
|
)
|
|
74144
74662
|
);
|
|
@@ -74149,16 +74667,18 @@ function buildProgram() {
|
|
|
74149
74667
|
program2.command("skill-history").description("list cached skill versions newest-first").option("-j, --json", "output as JSON").action(guard(cmdSkillHistory));
|
|
74150
74668
|
program2.command("skill-diff <name>").description("show diff between two cached versions of a skill").action(guard(cmdSkillDiff));
|
|
74151
74669
|
program2.command("skill-section <nameHeading> [headingArg]").description("extract a named section from a skill").action(guard(cmdSkillSection));
|
|
74152
|
-
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(
|
|
74670
|
+
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(
|
|
74153
74671
|
(symbol3, opts) => runExit(
|
|
74154
74672
|
() => runCallers({
|
|
74155
74673
|
symbol: symbol3,
|
|
74156
74674
|
...opts.json === true ? { json: true } : {},
|
|
74157
|
-
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {}
|
|
74675
|
+
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
74676
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
74677
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
74158
74678
|
})
|
|
74159
74679
|
)
|
|
74160
74680
|
);
|
|
74161
|
-
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(
|
|
74681
|
+
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(
|
|
74162
74682
|
(symbol3, opts) => runExit(
|
|
74163
74683
|
() => runCallChain({
|
|
74164
74684
|
symbol: symbol3,
|
|
@@ -74167,7 +74687,7 @@ function buildProgram() {
|
|
|
74167
74687
|
})
|
|
74168
74688
|
)
|
|
74169
74689
|
);
|
|
74170
|
-
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(
|
|
74690
|
+
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(
|
|
74171
74691
|
(symbol3, opts) => runExit(
|
|
74172
74692
|
() => runImpact({
|
|
74173
74693
|
symbol: symbol3,
|
|
@@ -74176,13 +74696,14 @@ function buildProgram() {
|
|
|
74176
74696
|
})
|
|
74177
74697
|
)
|
|
74178
74698
|
);
|
|
74179
|
-
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(
|
|
74699
|
+
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(
|
|
74180
74700
|
(opts) => runExit(
|
|
74181
74701
|
() => runDead({
|
|
74182
74702
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
74183
74703
|
...opts.includePrivate === true ? { includePrivate: true } : {},
|
|
74184
74704
|
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
74185
|
-
...opts.json === true ? { json: true } : {}
|
|
74705
|
+
...opts.json === true ? { json: true } : {},
|
|
74706
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
74186
74707
|
})
|
|
74187
74708
|
)
|
|
74188
74709
|
);
|
|
@@ -74201,7 +74722,7 @@ function buildProgram() {
|
|
|
74201
74722
|
program2.command("scope <fileColonLine>").description("list symbols enclosing a file:line position, innermost first").option("-j, --json", "output as JSON").action(
|
|
74202
74723
|
(spec, opts) => runExit(() => runScope({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
74203
74724
|
);
|
|
74204
|
-
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(
|
|
74725
|
+
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(
|
|
74205
74726
|
(spec, opts) => runExit(
|
|
74206
74727
|
() => runSimilar({
|
|
74207
74728
|
spec,
|
|
@@ -74240,7 +74761,7 @@ function buildProgram() {
|
|
|
74240
74761
|
})
|
|
74241
74762
|
)
|
|
74242
74763
|
);
|
|
74243
|
-
program2.command("blame <spec>").description('git blame for the line range of a symbol ("file::symbol")').option("-j, --json", "output as JSON").action(
|
|
74764
|
+
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(
|
|
74244
74765
|
(spec, opts) => runExit(() => runBlame({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
74245
74766
|
);
|
|
74246
74767
|
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(
|
|
@@ -74324,16 +74845,16 @@ function buildProgram() {
|
|
|
74324
74845
|
}))());
|
|
74325
74846
|
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 } : {} }))());
|
|
74326
74847
|
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))());
|
|
74327
|
-
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(
|
|
74328
|
-
(opts) => runExit(
|
|
74848
|
+
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(
|
|
74849
|
+
(ref2, opts) => runExit(
|
|
74329
74850
|
() => runChanged({
|
|
74330
|
-
ref: opts.since ?? "HEAD~5",
|
|
74851
|
+
ref: opts.since ?? ref2 ?? "HEAD~5",
|
|
74331
74852
|
...opts.symbol === true ? { symbolMode: true } : {},
|
|
74332
74853
|
...opts.json === true ? { json: true } : {}
|
|
74333
74854
|
})
|
|
74334
74855
|
)
|
|
74335
74856
|
);
|
|
74336
|
-
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(
|
|
74857
|
+
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(
|
|
74337
74858
|
(spec, ref2, opts) => runExit(
|
|
74338
74859
|
() => runDiff({
|
|
74339
74860
|
spec,
|
|
@@ -74342,7 +74863,7 @@ function buildProgram() {
|
|
|
74342
74863
|
})
|
|
74343
74864
|
)
|
|
74344
74865
|
);
|
|
74345
|
-
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(
|
|
74866
|
+
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(
|
|
74346
74867
|
(spec, ref2, opts) => runExit(
|
|
74347
74868
|
() => runLog({
|
|
74348
74869
|
spec,
|