token-goat 2.6.23 → 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 +27 -27
- package/dist/token-goat-hook.mjs +758 -221
- package/dist/token-goat.mjs +776 -224
- 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");
|
|
@@ -1464,12 +1464,12 @@ function normalizeDarwinSystemAlias(p) {
|
|
|
1464
1464
|
}
|
|
1465
1465
|
function resolveIndexPath(file2, base = process.cwd()) {
|
|
1466
1466
|
const isWindowsAbsolute = (s) => /^[a-zA-Z]:[/\\]/.test(s);
|
|
1467
|
-
const
|
|
1468
|
-
return normalizePath(
|
|
1467
|
+
const resolve25 = isWindowsAbsolute(file2) || isWindowsAbsolute(base) ? path2.win32.resolve : path2.resolve;
|
|
1468
|
+
return normalizePath(resolve25(base, file2));
|
|
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
|
}
|
|
@@ -2653,14 +2684,23 @@ const path = require('node:path')
|
|
|
2653
2684
|
const { pathToFileURL } = require('node:url')
|
|
2654
2685
|
|
|
2655
2686
|
// Copilot event name -> token-goat internal HookEventName (src/types.ts's
|
|
2656
|
-
// HOOK_EVENTS). Only these
|
|
2687
|
+
// HOOK_EVENTS). Only these seven have a token-goat handler; every other real
|
|
2657
2688
|
// Copilot event (sessionEnd, postToolUseFailure, subagentStart,
|
|
2658
2689
|
// errorOccurred, notification, permissionRequest) is left unimplemented
|
|
2659
2690
|
// rather than guessed at, and falls through to the default no-op below.
|
|
2660
|
-
// 'sessionStart'
|
|
2661
|
-
//
|
|
2662
|
-
//
|
|
2691
|
+
// 'sessionStart' was previously a permanent no-op on the stated grounds that
|
|
2692
|
+
// token-goat has no internal session_start handler. That was simply wrong --
|
|
2693
|
+
// hooks_session_start.ts has long emitted the command-routing reminder that
|
|
2694
|
+
// every other harness receives -- and the no-op was the reason Copilot CLI
|
|
2695
|
+
// sessions alone never got told token-goat exists. It is wired now: verified
|
|
2696
|
+
// against Copilot CLI 1.0.77 that a hooks.json sessionStart entry returning
|
|
2697
|
+
// {additionalContext} does reach the model. The github/copilot-cli#2142
|
|
2698
|
+
// fire-and-forget bug that would have made this dead wiring was fixed in a
|
|
2699
|
+
// pre-release months before that version, and its companion multi-extension
|
|
2700
|
+
// hook-overwrite bug never applied here: it hit runtime *extension* hooks,
|
|
2701
|
+
// while this config-file hooks.json path goes through Copilot's own merge.
|
|
2663
2702
|
const COPILOT_TO_TG_EVENT = {
|
|
2703
|
+
sessionStart: 'session_start',
|
|
2664
2704
|
preToolUse: 'pre_tool_use',
|
|
2665
2705
|
postToolUse: 'post_tool_use',
|
|
2666
2706
|
preCompact: 'pre_compact',
|
|
@@ -2772,11 +2812,6 @@ async function tryInProcess(entryPath, tgEvent, canonical) {
|
|
|
2772
2812
|
async function main() {
|
|
2773
2813
|
const copilotEvent = process.argv[2] || ''
|
|
2774
2814
|
|
|
2775
|
-
if (copilotEvent === 'sessionStart') {
|
|
2776
|
-
process.stdout.write('{}')
|
|
2777
|
-
return
|
|
2778
|
-
}
|
|
2779
|
-
|
|
2780
2815
|
const tgEvent = COPILOT_TO_TG_EVENT[copilotEvent]
|
|
2781
2816
|
if (!tgEvent) {
|
|
2782
2817
|
process.stdout.write('{}')
|
|
@@ -2902,7 +2937,10 @@ function translate(copilotEvent, resp) {
|
|
|
2902
2937
|
return {}
|
|
2903
2938
|
}
|
|
2904
2939
|
|
|
2905
|
-
if (copilotEvent === 'postToolUse') {
|
|
2940
|
+
if (copilotEvent === 'postToolUse' || copilotEvent === 'sessionStart') {
|
|
2941
|
+
// Both surface token-goat's context through the same field. sessionStart is
|
|
2942
|
+
// the one channel that reaches the model before it picks its first read
|
|
2943
|
+
// tool, so this is where the routing reminder has to land.
|
|
2906
2944
|
const context = extractContext(resp)
|
|
2907
2945
|
if (context) return { additionalContext: context }
|
|
2908
2946
|
return {}
|
|
@@ -2926,9 +2964,8 @@ function translate(copilotEvent, resp) {
|
|
|
2926
2964
|
// doc that both are notification-only -- Copilot never reads a response
|
|
2927
2965
|
// body for either, so any additionalContext/systemMessage token-goat
|
|
2928
2966
|
// produces has no surfacing channel here. This still routes through the
|
|
2929
|
-
// token-goat hook call above
|
|
2930
|
-
//
|
|
2931
|
-
// discarded.
|
|
2967
|
+
// token-goat hook call above so the internal handler's own side effects keep
|
|
2968
|
+
// running; only the response is discarded.
|
|
2932
2969
|
return {}
|
|
2933
2970
|
}
|
|
2934
2971
|
|
|
@@ -2968,8 +3005,13 @@ main()
|
|
|
2968
3005
|
import * as fs4 from "node:fs";
|
|
2969
3006
|
import * as os4 from "node:os";
|
|
2970
3007
|
import * as path6 from "node:path";
|
|
3008
|
+
function copilotCliUserRoot() {
|
|
3009
|
+
const override = process.env["COPILOT_HOME"];
|
|
3010
|
+
if (override !== void 0 && override.trim() !== "") return path6.resolve(override);
|
|
3011
|
+
return path6.join(os4.homedir(), ".copilot");
|
|
3012
|
+
}
|
|
2971
3013
|
function copilotCliUserHooksDir() {
|
|
2972
|
-
return path6.join(
|
|
3014
|
+
return path6.join(copilotCliUserRoot(), "hooks");
|
|
2973
3015
|
}
|
|
2974
3016
|
function copilotCliProjectHooksDir() {
|
|
2975
3017
|
return path6.join(process.cwd(), ".github", "hooks");
|
|
@@ -3074,6 +3116,7 @@ var init_copilot_cli_install = __esm({
|
|
|
3074
3116
|
init_copilot_cli();
|
|
3075
3117
|
init_guidance_block();
|
|
3076
3118
|
COPILOT_CLI_HOOK_EVENTS = [
|
|
3119
|
+
"sessionStart",
|
|
3077
3120
|
"preToolUse",
|
|
3078
3121
|
"postToolUse",
|
|
3079
3122
|
"preCompact",
|
|
@@ -3712,7 +3755,8 @@ function defaultConfig() {
|
|
|
3712
3755
|
compression: getDefaultConfig("compression"),
|
|
3713
3756
|
context: getDefaultConfig("context"),
|
|
3714
3757
|
injection: getDefaultConfig("injection"),
|
|
3715
|
-
hint_stats: getDefaultConfig("hint_stats")
|
|
3758
|
+
hint_stats: getDefaultConfig("hint_stats"),
|
|
3759
|
+
semantic: getDefaultConfig("semantic")
|
|
3716
3760
|
};
|
|
3717
3761
|
}
|
|
3718
3762
|
function validatedBool(raw, def) {
|
|
@@ -4154,6 +4198,10 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
4154
4198
|
const hs = getDefaultConfig("hint_stats");
|
|
4155
4199
|
hs.suppress_threshold_pct = validatedInt(hs_raw["suppress_threshold_pct"], hs.suppress_threshold_pct, ...boundsOf("hint_stats.suppress_threshold_pct"));
|
|
4156
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"));
|
|
4157
4205
|
return {
|
|
4158
4206
|
compact_assist: ca,
|
|
4159
4207
|
bash_compress: bc,
|
|
@@ -4176,7 +4224,8 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
4176
4224
|
compression: cpr,
|
|
4177
4225
|
context: ctx,
|
|
4178
4226
|
injection: inj,
|
|
4179
|
-
hint_stats: hs
|
|
4227
|
+
hint_stats: hs,
|
|
4228
|
+
semantic: sem
|
|
4180
4229
|
};
|
|
4181
4230
|
}
|
|
4182
4231
|
function saveConfig(config2) {
|
|
@@ -4333,6 +4382,10 @@ function saveConfig(config2) {
|
|
|
4333
4382
|
hint_stats: {
|
|
4334
4383
|
suppress_threshold_pct: config2.hint_stats.suppress_threshold_pct,
|
|
4335
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
|
|
4336
4389
|
}
|
|
4337
4390
|
};
|
|
4338
4391
|
const toml = stringify(data);
|
|
@@ -4525,6 +4578,10 @@ var init_config = __esm({
|
|
|
4525
4578
|
hint_stats: {
|
|
4526
4579
|
suppress_threshold_pct: 15,
|
|
4527
4580
|
min_sample_size: 5
|
|
4581
|
+
},
|
|
4582
|
+
semantic: {
|
|
4583
|
+
archive_weight: 0.7,
|
|
4584
|
+
docs_weight: 0.92
|
|
4528
4585
|
}
|
|
4529
4586
|
};
|
|
4530
4587
|
NUMERIC_FIELD_BOUNDS = {
|
|
@@ -4588,7 +4645,9 @@ var init_config = __esm({
|
|
|
4588
4645
|
"indexing.large_file_skip_kb": { min: 1, max: 1048576 },
|
|
4589
4646
|
"context.model_window_tokens": { min: 1e4, max: 1e7 },
|
|
4590
4647
|
"hint_stats.suppress_threshold_pct": { min: 0, max: 100 },
|
|
4591
|
-
"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 }
|
|
4592
4651
|
};
|
|
4593
4652
|
ENUM_FIELD_VALUES = {
|
|
4594
4653
|
"compression.profile": ["auto", "aggressive", "balanced", "minimal"],
|
|
@@ -11510,16 +11569,16 @@ var init_file_type_handler = __esm({
|
|
|
11510
11569
|
|
|
11511
11570
|
// src/skill_cache.ts
|
|
11512
11571
|
import * as fs16 from "fs/promises";
|
|
11513
|
-
import { resolve as
|
|
11572
|
+
import { resolve as resolve8 } from "path";
|
|
11514
11573
|
import { homedir as homedir7 } from "os";
|
|
11515
11574
|
import { readdirSync as readdirSync8, readFileSync as readFileSync12, existsSync as existsSync14, statSync as statSync7, unlinkSync as unlinkSync9 } from "node:fs";
|
|
11516
11575
|
function skillOutputsDir() {
|
|
11517
11576
|
if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
|
|
11518
|
-
return
|
|
11577
|
+
return resolve8(dataDir(), SKILLS_OUTPUT_SUBDIR);
|
|
11519
11578
|
}
|
|
11520
11579
|
function skillsSourceDir() {
|
|
11521
11580
|
if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
|
|
11522
|
-
return
|
|
11581
|
+
return resolve8(homedir7(), ".claude", "skills");
|
|
11523
11582
|
}
|
|
11524
11583
|
async function ensureSkillsDir() {
|
|
11525
11584
|
try {
|
|
@@ -11662,7 +11721,7 @@ async function listOutputs() {
|
|
|
11662
11721
|
continue;
|
|
11663
11722
|
}
|
|
11664
11723
|
try {
|
|
11665
|
-
const content = await fs16.readFile(
|
|
11724
|
+
const content = await fs16.readFile(resolve8(dir, entry.name), "utf-8");
|
|
11666
11725
|
const meta3 = JSON.parse(content);
|
|
11667
11726
|
metas.push(meta3);
|
|
11668
11727
|
} catch {
|
|
@@ -11696,7 +11755,7 @@ async function findCrossSessionEntry(skillName, contentSha) {
|
|
|
11696
11755
|
continue;
|
|
11697
11756
|
}
|
|
11698
11757
|
const dir = skillOutputsDir();
|
|
11699
|
-
const bodyPath =
|
|
11758
|
+
const bodyPath = resolve8(dir, `${meta3.outputId}.txt`);
|
|
11700
11759
|
try {
|
|
11701
11760
|
const bodyExists = await fs16.access(bodyPath).then(() => true).catch(() => false);
|
|
11702
11761
|
if (bodyExists) {
|
|
@@ -11751,7 +11810,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
11751
11810
|
const truncBuf = buf.slice(truncStart);
|
|
11752
11811
|
storedBody = truncBuf.toString("utf-8");
|
|
11753
11812
|
}
|
|
11754
|
-
await atomicWriteText(
|
|
11813
|
+
await atomicWriteText(resolve8(dir, `${outId}.txt`), storedBody);
|
|
11755
11814
|
const meta3 = {
|
|
11756
11815
|
outputId: outId,
|
|
11757
11816
|
skillName: name2,
|
|
@@ -11761,7 +11820,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
11761
11820
|
truncated,
|
|
11762
11821
|
sourcePath: opts?.sourcePath || ""
|
|
11763
11822
|
};
|
|
11764
|
-
await atomicWriteText(
|
|
11823
|
+
await atomicWriteText(resolve8(dir, `${outId}.meta`), JSON.stringify(meta3, null, 2));
|
|
11765
11824
|
pruneSkillOutputs();
|
|
11766
11825
|
return meta3;
|
|
11767
11826
|
} catch {
|
|
@@ -11781,7 +11840,7 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
|
|
|
11781
11840
|
text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
|
|
11782
11841
|
${text}`;
|
|
11783
11842
|
}
|
|
11784
|
-
await atomicWriteText(
|
|
11843
|
+
await atomicWriteText(resolve8(dir, fileId), text);
|
|
11785
11844
|
} catch {
|
|
11786
11845
|
}
|
|
11787
11846
|
}
|
|
@@ -11801,7 +11860,7 @@ function getCompactAnySessionSync(skillName) {
|
|
|
11801
11860
|
for (const entry of entries) {
|
|
11802
11861
|
if (!matchesCompactSuffix(entry.name, entry.isFile(), suffix)) continue;
|
|
11803
11862
|
try {
|
|
11804
|
-
const text = readFileSync12(
|
|
11863
|
+
const text = readFileSync12(resolve8(dir, entry.name), "utf-8");
|
|
11805
11864
|
if (text.trim()) return text;
|
|
11806
11865
|
} catch {
|
|
11807
11866
|
continue;
|
|
@@ -11829,7 +11888,7 @@ async function readSkillHits(skillName) {
|
|
|
11829
11888
|
const name2 = safeSkillName(skillName);
|
|
11830
11889
|
if (!name2) return { count: 0, lastTs: 0 };
|
|
11831
11890
|
const dir = skillOutputsDir();
|
|
11832
|
-
const hitsFile =
|
|
11891
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
11833
11892
|
const content = await fs16.readFile(hitsFile, "utf-8").catch(() => null);
|
|
11834
11893
|
if (content) {
|
|
11835
11894
|
const parsed = JSON.parse(content);
|
|
@@ -11879,7 +11938,7 @@ async function incrementSkillHit(skillName) {
|
|
|
11879
11938
|
if (!name2) return;
|
|
11880
11939
|
await ensureSkillsDir();
|
|
11881
11940
|
const dir = skillOutputsDir();
|
|
11882
|
-
const hitsFile =
|
|
11941
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
11883
11942
|
const lockPath = `${hitsFile}.lock`;
|
|
11884
11943
|
await runExclusiveInProcess(lockPath, async () => {
|
|
11885
11944
|
const locked = await acquireSkillHitLock(lockPath);
|
|
@@ -11926,14 +11985,14 @@ async function listSkills(sessionId) {
|
|
|
11926
11985
|
let compactLen = 0;
|
|
11927
11986
|
let compactText = "";
|
|
11928
11987
|
try {
|
|
11929
|
-
const stat2 = await fs16.stat(
|
|
11988
|
+
const stat2 = await fs16.stat(resolve8(dir, compactFileId));
|
|
11930
11989
|
compactLen = stat2.size;
|
|
11931
|
-
compactText = await fs16.readFile(
|
|
11990
|
+
compactText = await fs16.readFile(resolve8(dir, compactFileId), "utf-8").catch(() => "");
|
|
11932
11991
|
} catch {
|
|
11933
11992
|
compactLen = 0;
|
|
11934
11993
|
}
|
|
11935
11994
|
const hasMarker = extractCompactFromMarker(
|
|
11936
|
-
await fs16.readFile(
|
|
11995
|
+
await fs16.readFile(resolve8(dir, `${meta3.outputId}.txt`), "utf-8").catch(() => "")
|
|
11937
11996
|
) !== null;
|
|
11938
11997
|
const compactStale = isCompactStale(compactText, meta3.skillName, meta3.contentSha);
|
|
11939
11998
|
const { count: hitCount } = await readSkillHits(meta3.skillName);
|
|
@@ -11969,7 +12028,7 @@ async function getSkillFilePath(skillName) {
|
|
|
11969
12028
|
}
|
|
11970
12029
|
}
|
|
11971
12030
|
async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
11972
|
-
const manifestPath = _pluginsManifestPathOverride ??
|
|
12031
|
+
const manifestPath = _pluginsManifestPathOverride ?? resolve8(homedir7(), ".claude", "plugins", "installed_plugins.json");
|
|
11973
12032
|
let raw;
|
|
11974
12033
|
try {
|
|
11975
12034
|
raw = await fs16.readFile(manifestPath, "utf8");
|
|
@@ -11992,7 +12051,7 @@ async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
|
11992
12051
|
if (typeof entry !== "object" || entry === null) continue;
|
|
11993
12052
|
const installPath = entry["installPath"];
|
|
11994
12053
|
if (typeof installPath !== "string" || installPath === "") continue;
|
|
11995
|
-
const diskPath =
|
|
12054
|
+
const diskPath = resolve8(installPath, "skills", skillSlug, "SKILL.md");
|
|
11996
12055
|
try {
|
|
11997
12056
|
await fs16.access(diskPath);
|
|
11998
12057
|
return diskPath;
|
|
@@ -12011,7 +12070,7 @@ async function installedSkillPath(skillName) {
|
|
|
12011
12070
|
const pluginPath = await resolvePluginSkillPath(name2.slice(0, colonIdx), name2.slice(colonIdx + 1));
|
|
12012
12071
|
if (pluginPath !== null) return pluginPath;
|
|
12013
12072
|
}
|
|
12014
|
-
const diskPath =
|
|
12073
|
+
const diskPath = resolve8(skillsSourceDir(), name2, "SKILL.md");
|
|
12015
12074
|
try {
|
|
12016
12075
|
await fs16.access(diskPath);
|
|
12017
12076
|
return diskPath;
|
|
@@ -12031,11 +12090,11 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
12031
12090
|
const outputId = file2.slice(0, -".meta".length);
|
|
12032
12091
|
let ts;
|
|
12033
12092
|
try {
|
|
12034
|
-
const parsed = JSON.parse(readFileSync12(
|
|
12093
|
+
const parsed = JSON.parse(readFileSync12(resolve8(dir, file2), "utf-8"));
|
|
12035
12094
|
ts = parsed.ts;
|
|
12036
12095
|
} catch {
|
|
12037
12096
|
try {
|
|
12038
|
-
ts = statSync7(
|
|
12097
|
+
ts = statSync7(resolve8(dir, file2)).mtimeMs;
|
|
12039
12098
|
} catch {
|
|
12040
12099
|
continue;
|
|
12041
12100
|
}
|
|
@@ -12045,7 +12104,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
12045
12104
|
const removeEntry = (outputId) => {
|
|
12046
12105
|
for (const ext2 of [".meta", ".txt"]) {
|
|
12047
12106
|
try {
|
|
12048
|
-
unlinkSync9(
|
|
12107
|
+
unlinkSync9(resolve8(dir, `${outputId}${ext2}`));
|
|
12049
12108
|
} catch {
|
|
12050
12109
|
}
|
|
12051
12110
|
}
|
|
@@ -12159,7 +12218,7 @@ async function ocrImage(input) {
|
|
|
12159
12218
|
if (_ocrUnavailableThisProcess) return null;
|
|
12160
12219
|
const entryPath = resolveTesseractEntry();
|
|
12161
12220
|
if (entryPath === null) return null;
|
|
12162
|
-
return new Promise((
|
|
12221
|
+
return new Promise((resolve25) => {
|
|
12163
12222
|
let settled = false;
|
|
12164
12223
|
let child;
|
|
12165
12224
|
try {
|
|
@@ -12168,7 +12227,7 @@ async function ocrImage(input) {
|
|
|
12168
12227
|
});
|
|
12169
12228
|
} catch {
|
|
12170
12229
|
_ocrUnavailableThisProcess = true;
|
|
12171
|
-
|
|
12230
|
+
resolve25(null);
|
|
12172
12231
|
return;
|
|
12173
12232
|
}
|
|
12174
12233
|
const chunks = [];
|
|
@@ -12181,7 +12240,7 @@ async function ocrImage(input) {
|
|
|
12181
12240
|
child.kill();
|
|
12182
12241
|
} catch {
|
|
12183
12242
|
}
|
|
12184
|
-
|
|
12243
|
+
resolve25(result);
|
|
12185
12244
|
};
|
|
12186
12245
|
const timer = setTimeout(() => finish(null, true), _ocrTimeoutMs);
|
|
12187
12246
|
child.stdout?.on("data", (c) => chunks.push(c));
|
|
@@ -13619,7 +13678,7 @@ function ensureTransformerLoaded() {
|
|
|
13619
13678
|
}
|
|
13620
13679
|
}
|
|
13621
13680
|
function sleep(ms) {
|
|
13622
|
-
return new Promise((
|
|
13681
|
+
return new Promise((resolve25) => setTimeout(resolve25, ms));
|
|
13623
13682
|
}
|
|
13624
13683
|
async function buildExtractorWithRetry(pipelineFn, modelName) {
|
|
13625
13684
|
let lastError;
|
|
@@ -13959,7 +14018,8 @@ function rerankHits(hits, query, topK) {
|
|
|
13959
14018
|
boost = Math.min(matches2 * _VERBATIM_TOKEN_BOOST, _MAX_VERBATIM_BOOST);
|
|
13960
14019
|
}
|
|
13961
14020
|
const penalty = _isGeneratedPath(hit.filePath) ? _GENERATED_PATH_PENALTY : 0;
|
|
13962
|
-
|
|
14021
|
+
const pathPenalty = _pathPriorityPenalty(hit.filePath);
|
|
14022
|
+
return { hit, index, adjusted: hit.distance - boost + penalty + pathPenalty };
|
|
13963
14023
|
});
|
|
13964
14024
|
scored.sort((a, b) => a.adjusted - b.adjusted || a.index - b.index);
|
|
13965
14025
|
return scored.slice(0, topK).map((entry) => ({ ...entry.hit, adjustedDistance: entry.adjusted }));
|
|
@@ -14085,11 +14145,26 @@ function _isGeneratedPath(filePath) {
|
|
|
14085
14145
|
}
|
|
14086
14146
|
return false;
|
|
14087
14147
|
}
|
|
14088
|
-
|
|
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;
|
|
14089
14163
|
var init_embeddings = __esm({
|
|
14090
14164
|
"src/embeddings.ts"() {
|
|
14091
14165
|
"use strict";
|
|
14092
14166
|
init_define_import_meta_env();
|
|
14167
|
+
init_config();
|
|
14093
14168
|
init_sql_path();
|
|
14094
14169
|
init_util2();
|
|
14095
14170
|
init_reset();
|
|
@@ -14138,6 +14213,17 @@ var init_embeddings = __esm({
|
|
|
14138
14213
|
".ruff_cache"
|
|
14139
14214
|
]);
|
|
14140
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";
|
|
14141
14227
|
_VERBATIM_TOKEN_BOOST = 0.05;
|
|
14142
14228
|
_MAX_VERBATIM_BOOST = 0.25;
|
|
14143
14229
|
_TOKEN_RE = /\w+/g;
|
|
@@ -23932,7 +24018,7 @@ import * as readline from "node:readline";
|
|
|
23932
24018
|
async function defaultConfirm(question) {
|
|
23933
24019
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
23934
24020
|
try {
|
|
23935
|
-
const answer = await new Promise((
|
|
24021
|
+
const answer = await new Promise((resolve25) => rl.question(question, resolve25));
|
|
23936
24022
|
return /^y(es)?$/i.test(answer.trim());
|
|
23937
24023
|
} finally {
|
|
23938
24024
|
rl.close();
|
|
@@ -24517,14 +24603,32 @@ function fetchTopSymbols(limit, dbPath, rootDir) {
|
|
|
24517
24603
|
try {
|
|
24518
24604
|
const db = getDb(dbPath);
|
|
24519
24605
|
const { clause, param } = projectScopeClause("file_path");
|
|
24606
|
+
const refScope = projectScopeClause("file_path");
|
|
24520
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.
|
|
24521
24609
|
`SELECT file_path, name, kind, line_start, line_end, body, docstring, parent
|
|
24522
|
-
FROM
|
|
24523
|
-
|
|
24524
|
-
|
|
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,
|
|
24525
24629
|
LENGTH(COALESCE(body, '')) DESC
|
|
24526
24630
|
LIMIT ?`
|
|
24527
|
-
).all(param(rootDir), limit);
|
|
24631
|
+
).all(refScope.param(rootDir), param(rootDir), limit);
|
|
24528
24632
|
return rows.map((r) => ({
|
|
24529
24633
|
filePath: r.file_path,
|
|
24530
24634
|
name: r.name,
|
|
@@ -24576,13 +24680,12 @@ function formatProjectMap(map3, compact = false) {
|
|
|
24576
24680
|
lines2.push("");
|
|
24577
24681
|
lines2.push("## Top symbols");
|
|
24578
24682
|
for (const s of map3.topSymbols) {
|
|
24579
|
-
|
|
24580
|
-
|
|
24581
|
-
} else {
|
|
24582
|
-
const loc = `${path34.basename(s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
24583
|
-
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
24584
|
-
}
|
|
24683
|
+
const loc = `${toDisplayPath(map3.rootDir, s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
24684
|
+
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
24585
24685
|
}
|
|
24686
|
+
} else {
|
|
24687
|
+
lines2.push("");
|
|
24688
|
+
lines2.push("## Top symbols: none \u2014 no files indexed for this project; run 'token-goat index .'");
|
|
24586
24689
|
}
|
|
24587
24690
|
if (!compact && map3.recentFiles.length > 0) {
|
|
24588
24691
|
lines2.push("");
|
|
@@ -25090,6 +25193,11 @@ function isDeadSymbol(name2, refCount) {
|
|
|
25090
25193
|
if (ENTRY_NAMES.has(name2)) return false;
|
|
25091
25194
|
return refCount === 0;
|
|
25092
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
|
+
}
|
|
25093
25201
|
function buildFileSymCache() {
|
|
25094
25202
|
const cache = /* @__PURE__ */ new Map();
|
|
25095
25203
|
return (fp) => {
|
|
@@ -25107,9 +25215,10 @@ function fileDefinesName(fp, name2, getSyms) {
|
|
|
25107
25215
|
function filterRefsForSymbol(refs, name2, filePath, getSyms) {
|
|
25108
25216
|
return refs.filter((ref2) => ref2.filePath === filePath || !fileDefinesName(ref2.filePath, name2, getSyms));
|
|
25109
25217
|
}
|
|
25110
|
-
function resolveCallers(name2, limit, filePath, rootDir) {
|
|
25218
|
+
function resolveCallers(name2, limit, filePath, rootDir, excludeTests) {
|
|
25111
25219
|
const resolvedRootDir = rootDir ?? resolveProjectRoot({ project: process.cwd() });
|
|
25112
|
-
const
|
|
25220
|
+
const queryLimit = excludeTests === true ? UNBOUNDED_REF_LIMIT : limit ?? 500;
|
|
25221
|
+
const refs = queryRefs({ name: name2, limit: queryLimit, rootDir: resolvedRootDir });
|
|
25113
25222
|
const getSyms = buildFileSymCache();
|
|
25114
25223
|
const scoped = filePath === void 0 ? refs : filterRefsForSymbol(refs, name2, filePath, getSyms);
|
|
25115
25224
|
return scoped.map((ref2) => {
|
|
@@ -25128,17 +25237,37 @@ function runCallers(opts) {
|
|
|
25128
25237
|
return 1;
|
|
25129
25238
|
}
|
|
25130
25239
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25131
|
-
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;
|
|
25132
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
|
+
}
|
|
25133
25254
|
emitErr(`No references found for '${opts.symbol}'`);
|
|
25134
25255
|
return 1;
|
|
25135
25256
|
}
|
|
25257
|
+
const contextLines = opts.context ?? 0;
|
|
25136
25258
|
if (opts.json === true) {
|
|
25137
|
-
|
|
25259
|
+
const payload = contextLines > 0 ? entries.map((e) => ({ ...e, contextLines: buildContextWindow(e.file, e.line, contextLines) ?? [] })) : entries;
|
|
25260
|
+
emit2(JSON.stringify(payload, null, 2));
|
|
25138
25261
|
return 0;
|
|
25139
25262
|
}
|
|
25263
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25264
|
+
emit2(`${entries.length} callers found (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25265
|
+
}
|
|
25140
25266
|
for (const e of entries) {
|
|
25141
|
-
|
|
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);
|
|
25142
25271
|
}
|
|
25143
25272
|
return 0;
|
|
25144
25273
|
}
|
|
@@ -25149,28 +25278,36 @@ function runCallChain(opts) {
|
|
|
25149
25278
|
}
|
|
25150
25279
|
const maxDepth = opts.depth ?? 8;
|
|
25151
25280
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25152
|
-
|
|
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) {
|
|
25153
25289
|
emitErr(`Symbol not found: ${opts.symbol}`);
|
|
25154
25290
|
return 1;
|
|
25155
25291
|
}
|
|
25156
25292
|
const getSyms = buildFileSymCache();
|
|
25157
|
-
const callersOf = (
|
|
25158
|
-
const refs = queryRefs({ name:
|
|
25293
|
+
const callersOf = (n) => {
|
|
25294
|
+
const refs = queryRefs({ name: n, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25159
25295
|
if (refs.length === 0) return [];
|
|
25296
|
+
const scoped = fileHint !== void 0 && n === name2 ? filterRefsForSymbol(refs, n, fileHint, getSyms) : refs;
|
|
25160
25297
|
const names = /* @__PURE__ */ new Set();
|
|
25161
|
-
for (const ref2 of
|
|
25298
|
+
for (const ref2 of scoped) {
|
|
25162
25299
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25163
25300
|
if (enc !== null) names.add(enc.name);
|
|
25164
25301
|
}
|
|
25165
25302
|
return [...names];
|
|
25166
25303
|
};
|
|
25167
|
-
const chains = bfsCallChains(
|
|
25304
|
+
const chains = bfsCallChains(name2, callersOf, maxDepth);
|
|
25168
25305
|
if (opts.json === true) {
|
|
25169
25306
|
emit2(JSON.stringify({ chains }, null, 2));
|
|
25170
25307
|
return 0;
|
|
25171
25308
|
}
|
|
25172
|
-
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] ===
|
|
25173
|
-
emit2(`${
|
|
25309
|
+
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] === name2) {
|
|
25310
|
+
emit2(`${name2} (no callers)`);
|
|
25174
25311
|
return 0;
|
|
25175
25312
|
}
|
|
25176
25313
|
for (const chain2 of chains) {
|
|
@@ -25190,16 +25327,23 @@ function runImpact(opts) {
|
|
|
25190
25327
|
const top = opts.top ?? 20;
|
|
25191
25328
|
const DEPTH_CAP = 8;
|
|
25192
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
|
+
}
|
|
25193
25336
|
const getSyms = buildFileSymCache();
|
|
25194
|
-
const hops = /* @__PURE__ */ new Map([[
|
|
25195
|
-
const queue = [[
|
|
25337
|
+
const hops = /* @__PURE__ */ new Map([[rootName, 0]]);
|
|
25338
|
+
const queue = [[rootName, 0]];
|
|
25196
25339
|
while (queue.length > 0) {
|
|
25197
25340
|
const item = queue.shift();
|
|
25198
25341
|
if (item === void 0) break;
|
|
25199
25342
|
const [name2, depth] = item;
|
|
25200
25343
|
if (depth >= DEPTH_CAP) continue;
|
|
25201
25344
|
const refs = queryRefs({ name: name2, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25202
|
-
|
|
25345
|
+
const scoped = fileHint !== void 0 && name2 === rootName ? filterRefsForSymbol(refs, name2, fileHint, getSyms) : refs;
|
|
25346
|
+
for (const ref2 of scoped) {
|
|
25203
25347
|
const newHop = depth + 1;
|
|
25204
25348
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25205
25349
|
if (enc === null) {
|
|
@@ -25216,7 +25360,7 @@ function runImpact(opts) {
|
|
|
25216
25360
|
}
|
|
25217
25361
|
}
|
|
25218
25362
|
}
|
|
25219
|
-
hops.delete(
|
|
25363
|
+
hops.delete(rootName);
|
|
25220
25364
|
const sorted = [...hops.entries()].sort(compareHopEntries).slice(0, top);
|
|
25221
25365
|
if (sorted.length === 0) {
|
|
25222
25366
|
emitErr(`No callers found for '${opts.symbol}'`);
|
|
@@ -25268,6 +25412,7 @@ function runDead(opts) {
|
|
|
25268
25412
|
const syms = querySymbols({ kind, limit: 5e3, rootDir });
|
|
25269
25413
|
const getSyms = buildFileSymCache();
|
|
25270
25414
|
const results = [];
|
|
25415
|
+
let suppressed = 0;
|
|
25271
25416
|
for (const sym of syms) {
|
|
25272
25417
|
if (opts.includePrivate !== true && sym.name.startsWith("_")) continue;
|
|
25273
25418
|
const refs = queryRefs({ name: sym.name, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
@@ -25277,6 +25422,10 @@ function runDead(opts) {
|
|
|
25277
25422
|
const ownScope = enclosingNamedScope(getSyms(sym.filePath), sym.lineStart);
|
|
25278
25423
|
if (ownScope !== null && hasAncestorDispatchRef(sym.name, ownScope.name, sym.filePath, rootDir)) continue;
|
|
25279
25424
|
}
|
|
25425
|
+
if (opts.excludeTests === true && isTestFile(sym.filePath)) {
|
|
25426
|
+
suppressed += 1;
|
|
25427
|
+
continue;
|
|
25428
|
+
}
|
|
25280
25429
|
results.push({ name: sym.name, kind: sym.kind, file: sym.filePath, line: sym.lineStart });
|
|
25281
25430
|
}
|
|
25282
25431
|
const sliced = results.slice(0, opts.top ?? results.length);
|
|
@@ -25285,9 +25434,16 @@ function runDead(opts) {
|
|
|
25285
25434
|
return 0;
|
|
25286
25435
|
}
|
|
25287
25436
|
if (sliced.length === 0) {
|
|
25288
|
-
|
|
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
|
+
}
|
|
25289
25442
|
return 0;
|
|
25290
25443
|
}
|
|
25444
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25445
|
+
emit2(`${sliced.length} dead symbols (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25446
|
+
}
|
|
25291
25447
|
for (const r of sliced) {
|
|
25292
25448
|
emit2(`${r.name} ${toDisplayPath(rootDir, r.file)}:${r.line}`);
|
|
25293
25449
|
}
|
|
@@ -25537,16 +25693,9 @@ function runSimilar(opts) {
|
|
|
25537
25693
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
25538
25694
|
return 1;
|
|
25539
25695
|
}
|
|
25540
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
25541
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
25542
25696
|
const top = opts.top ?? 10;
|
|
25543
|
-
const
|
|
25544
|
-
|
|
25545
|
-
if (anchors.length === 0) {
|
|
25546
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
25547
|
-
return 1;
|
|
25548
|
-
}
|
|
25549
|
-
const anchor = anchors[0];
|
|
25697
|
+
const anchor = resolveSymbolSpecOrEmitError("similar", opts.spec, void 0);
|
|
25698
|
+
if (anchor === null) return 1;
|
|
25550
25699
|
const words = [anchor.name, ...(anchor.docstring ?? "").split(/\s+/).filter((w) => w.length > 4)];
|
|
25551
25700
|
const query = words.slice(0, 8).join(" ");
|
|
25552
25701
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
@@ -25578,13 +25727,13 @@ function runContextFor(opts) {
|
|
|
25578
25727
|
const bodyTokens = estimateTokens(h.body ?? "");
|
|
25579
25728
|
if (budget !== void 0 && tokensSoFar + bodyTokens > budget) continue;
|
|
25580
25729
|
tokensSoFar += bodyTokens;
|
|
25581
|
-
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}"` });
|
|
25582
25731
|
}
|
|
25583
25732
|
if (opts.json === true) {
|
|
25584
25733
|
emit2(JSON.stringify(entries, null, 2));
|
|
25585
25734
|
return 0;
|
|
25586
25735
|
}
|
|
25587
|
-
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}"`);
|
|
25588
25737
|
return 0;
|
|
25589
25738
|
}
|
|
25590
25739
|
function runTestFor(opts) {
|
|
@@ -25729,16 +25878,10 @@ function runBlame(opts) {
|
|
|
25729
25878
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
25730
25879
|
return 1;
|
|
25731
25880
|
}
|
|
25732
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
25733
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
25734
25881
|
const cwd = opts.cwd ?? process.cwd();
|
|
25735
|
-
const
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
25739
|
-
return 1;
|
|
25740
|
-
}
|
|
25741
|
-
const sym = syms[0];
|
|
25882
|
+
const sym = resolveSymbolSpecOrEmitError("blame", opts.spec, void 0);
|
|
25883
|
+
if (sym === null) return 1;
|
|
25884
|
+
const filePath = sym.filePath;
|
|
25742
25885
|
const start = sym.lineStart;
|
|
25743
25886
|
const end = sym.lineEnd;
|
|
25744
25887
|
let raw;
|
|
@@ -25759,10 +25902,10 @@ function runBlame(opts) {
|
|
|
25759
25902
|
if (!m) return { raw: l };
|
|
25760
25903
|
return { commit: m[1], author: (m[2] ?? "").trim(), date: (m[3] ?? "").trim(), line: Number.parseInt(m[4] ?? "0", 10), content: m[5] };
|
|
25761
25904
|
});
|
|
25762
|
-
emit2(JSON.stringify({ symbol:
|
|
25905
|
+
emit2(JSON.stringify({ symbol: sym.name, file: filePath, lines: lines2 }, null, 2));
|
|
25763
25906
|
return 0;
|
|
25764
25907
|
}
|
|
25765
|
-
emit2(`${
|
|
25908
|
+
emit2(`${sym.name} ${toDisplayPath(getDisplayRoot(opts.cwd), filePath)}:${start}-${end}`);
|
|
25766
25909
|
emit2(raw.trim());
|
|
25767
25910
|
return 0;
|
|
25768
25911
|
}
|
|
@@ -25776,14 +25919,14 @@ function runAsk(opts) {
|
|
|
25776
25919
|
const hits = searchSymbolsFts(opts.question, top, void 0, rootDir);
|
|
25777
25920
|
const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
|
|
25778
25921
|
const backendLabel = process.env[BACKEND_ENV] ?? "";
|
|
25779
|
-
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}"` }));
|
|
25780
25923
|
const degrade = () => {
|
|
25781
25924
|
if (opts.json === true) {
|
|
25782
25925
|
emit2(JSON.stringify({ degraded: true, note: `Set ${BACKEND_ENV}=claude|codex for LLM synthesis`, context: entries }, null, 2));
|
|
25783
25926
|
return 0;
|
|
25784
25927
|
}
|
|
25785
25928
|
emit2(`[degraded mode - set ${BACKEND_ENV}=claude|codex for LLM synthesis]`);
|
|
25786
|
-
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}"`);
|
|
25787
25930
|
return 0;
|
|
25788
25931
|
};
|
|
25789
25932
|
if (!backendLabel) return degrade();
|
|
@@ -29951,6 +30094,42 @@ function formatBareNameSpecError(command, name2, projectRoot) {
|
|
|
29951
30094
|
}
|
|
29952
30095
|
return lines2.join("\n");
|
|
29953
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
|
+
}
|
|
29954
30133
|
function trimBlankLines(lines2) {
|
|
29955
30134
|
let start = 0;
|
|
29956
30135
|
let end = lines2.length;
|
|
@@ -30046,10 +30225,21 @@ function parseCrossFileMultiSpec(spec) {
|
|
|
30046
30225
|
}
|
|
30047
30226
|
return pairs.length > 1 ? pairs : null;
|
|
30048
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
|
+
}
|
|
30049
30238
|
function parseLineRange(spec) {
|
|
30050
30239
|
const m = /^(.+)@(\d+)(?:-(\d+))?$/.exec(spec);
|
|
30051
30240
|
if (m === null) return null;
|
|
30052
30241
|
if (fileExists(spec)) return null;
|
|
30242
|
+
if (m[1].includes("::")) return null;
|
|
30053
30243
|
const start = parseInt(m[2], 10);
|
|
30054
30244
|
const end = m[3] !== void 0 ? parseInt(m[3], 10) : start;
|
|
30055
30245
|
return { file: m[1], start, end };
|
|
@@ -30108,30 +30298,49 @@ function findParentName(entry, fileSymbols) {
|
|
|
30108
30298
|
if (doc !== "" && PARENT_IDENTIFIER_RE.test(doc)) return doc;
|
|
30109
30299
|
return null;
|
|
30110
30300
|
}
|
|
30111
|
-
function formatAmbiguity(symbol3, file2, candidates, explicitRoot) {
|
|
30301
|
+
function formatAmbiguity(symbol3, file2, candidates, explicitRoot, commandName = "read") {
|
|
30112
30302
|
const multiFile = new Set(candidates.map((c) => c.filePath)).size > 1;
|
|
30113
30303
|
const displayRoot = getDisplayRoot(explicitRoot);
|
|
30114
30304
|
const lines2 = [
|
|
30115
30305
|
`Ambiguous symbol '${symbol3}' in '${file2}': ${candidates.length} definitions match. Retry with one of the qualified commands below to pick one:`
|
|
30116
30306
|
];
|
|
30117
30307
|
const fileSymCache = /* @__PURE__ */ new Map();
|
|
30118
|
-
|
|
30119
|
-
let fileSyms = fileSymCache.get(
|
|
30308
|
+
const getFileSyms = (filePath) => {
|
|
30309
|
+
let fileSyms = fileSymCache.get(filePath);
|
|
30120
30310
|
if (fileSyms === void 0) {
|
|
30121
|
-
fileSyms = querySymbols({ filePath
|
|
30122
|
-
fileSymCache.set(
|
|
30311
|
+
fileSyms = querySymbols({ filePath, limit: 1e3 });
|
|
30312
|
+
fileSymCache.set(filePath, fileSyms);
|
|
30123
30313
|
}
|
|
30124
|
-
|
|
30125
|
-
|
|
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;
|
|
30126
30332
|
const retryFile = multiFile ? toDisplayPath(displayRoot, c.filePath) : file2;
|
|
30127
30333
|
const label = multiFile ? `${toDisplayPath(displayRoot, c.filePath)}::${qualifier}` : qualifier;
|
|
30128
|
-
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat
|
|
30334
|
+
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat ${commandName} "${retryFile}::${qualifier}"`);
|
|
30129
30335
|
}
|
|
30130
30336
|
return lines2.join("\n");
|
|
30131
30337
|
}
|
|
30132
30338
|
function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
30133
|
-
const { file: file2, symbol:
|
|
30134
|
-
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;
|
|
30135
30344
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
30136
30345
|
if (forceRefresh === true) {
|
|
30137
30346
|
indexFileSync(resolved, globalDbPath());
|
|
@@ -30148,9 +30357,10 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
|
30148
30357
|
seen.add(key);
|
|
30149
30358
|
distinct.push(c);
|
|
30150
30359
|
}
|
|
30151
|
-
|
|
30152
|
-
if (
|
|
30153
|
-
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 };
|
|
30154
30364
|
};
|
|
30155
30365
|
if (symbol3.includes(".")) {
|
|
30156
30366
|
const exactMatch = querySymbols({ name: symbol3, filePath: resolved, limit: 10 });
|
|
@@ -30230,6 +30440,8 @@ function runRead(opts) {
|
|
|
30230
30440
|
return runLineRange({ file: file2, start: lineSpec.start, end: lineSpec.end }, opts);
|
|
30231
30441
|
}
|
|
30232
30442
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
30443
|
+
const crossFileLead = formatCrossFileLead("read", symbol3, file2, opts.projectRoot);
|
|
30444
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
30233
30445
|
const resolved = resolveIndexPath(file2, opts.projectRoot ?? process.cwd());
|
|
30234
30446
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
30235
30447
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -30290,6 +30502,8 @@ ${sub.text}`);
|
|
|
30290
30502
|
return { text, code: 1 };
|
|
30291
30503
|
}
|
|
30292
30504
|
function runSection(opts) {
|
|
30505
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
30506
|
+
if (crossFilePairs !== null) return runSectionCrossFile(crossFilePairs, opts);
|
|
30293
30507
|
const colonIdx = findSpecSeparator(opts.spec);
|
|
30294
30508
|
if (colonIdx === -1) {
|
|
30295
30509
|
return { text: `Invalid section spec \u2014 expected "file::Heading", got: ${opts.spec}`, code: 1 };
|
|
@@ -30346,6 +30560,42 @@ ${sub.text}`);
|
|
|
30346
30560
|
if (anyFound) recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
30347
30561
|
return { text, code: anyFound ? 0 : 1 };
|
|
30348
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
|
+
}
|
|
30349
30599
|
function applyTypedRefsTier(symName, file2, results) {
|
|
30350
30600
|
if (results.length === 0) return results;
|
|
30351
30601
|
try {
|
|
@@ -30383,6 +30633,8 @@ function runRefs(opts) {
|
|
|
30383
30633
|
emitErr2(`--top must be a positive number, got: ${opts.top}`);
|
|
30384
30634
|
return 1;
|
|
30385
30635
|
}
|
|
30636
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
30637
|
+
if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
|
|
30386
30638
|
const { file: file2, symbols } = parseMultiRefsSpec(opts.spec);
|
|
30387
30639
|
if (symbols.length <= 1) return runRefsSingle(opts);
|
|
30388
30640
|
const jsonOut = {};
|
|
@@ -30391,10 +30643,18 @@ function runRefs(opts) {
|
|
|
30391
30643
|
const refFilePaths = [];
|
|
30392
30644
|
for (const sym of symbols) {
|
|
30393
30645
|
const queryOpts = { name: sym };
|
|
30394
|
-
if (
|
|
30395
|
-
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;
|
|
30396
30648
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30397
|
-
|
|
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
|
+
}
|
|
30398
30658
|
if (results.length > 0) anyFound = true;
|
|
30399
30659
|
refFilePaths.push(...results.map((r) => r.filePath));
|
|
30400
30660
|
if (opts.json === true) {
|
|
@@ -30402,22 +30662,85 @@ function runRefs(opts) {
|
|
|
30402
30662
|
jsonOut[sym] = topFilesJsonPayload(results, opts.top);
|
|
30403
30663
|
} else {
|
|
30404
30664
|
const capped = guardJsonRows(results);
|
|
30405
|
-
const trueTotal = countRefs(queryOpts);
|
|
30406
|
-
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 };
|
|
30407
30667
|
}
|
|
30408
30668
|
continue;
|
|
30409
30669
|
}
|
|
30410
30670
|
if (results.length === 0) {
|
|
30411
|
-
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)`);
|
|
30412
30672
|
continue;
|
|
30413
30673
|
}
|
|
30414
30674
|
lines2.push(`${sym}:`);
|
|
30415
30675
|
if (opts.top !== void 0) {
|
|
30416
|
-
lines2.push(...renderTopFilesSummary(results, opts.top));
|
|
30676
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
30677
|
+
} else if (opts.callers === true) {
|
|
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));
|
|
30680
|
+
} else {
|
|
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));
|
|
30417
30738
|
} else if (opts.callers === true) {
|
|
30418
|
-
lines2.push(
|
|
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));
|
|
30419
30741
|
} else {
|
|
30420
|
-
|
|
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));
|
|
30421
30744
|
}
|
|
30422
30745
|
}
|
|
30423
30746
|
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
@@ -30437,11 +30760,23 @@ function runRefsSingle(opts) {
|
|
|
30437
30760
|
const symName = symbol3 ?? file2;
|
|
30438
30761
|
const queryOpts = { name: symName };
|
|
30439
30762
|
const defFileHint = symbol3 !== void 0 ? resolveIndexPath(file2) : void 0;
|
|
30440
|
-
if (
|
|
30441
|
-
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;
|
|
30442
30765
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30443
|
-
|
|
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
|
+
}
|
|
30444
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
|
+
}
|
|
30445
30780
|
emitErr2(`No references found for '${symName}'`);
|
|
30446
30781
|
return 1;
|
|
30447
30782
|
}
|
|
@@ -30452,8 +30787,8 @@ function runRefsSingle(opts) {
|
|
|
30452
30787
|
payload = topFilesJsonPayload(results, opts.top);
|
|
30453
30788
|
} else {
|
|
30454
30789
|
const capped = guardJsonRows(results);
|
|
30455
|
-
const trueTotal = countRefs(queryOpts);
|
|
30456
|
-
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 };
|
|
30457
30792
|
}
|
|
30458
30793
|
const text2 = JSON.stringify(payload, null, 2);
|
|
30459
30794
|
emit3(text2);
|
|
@@ -30461,21 +30796,26 @@ function runRefsSingle(opts) {
|
|
|
30461
30796
|
return 0;
|
|
30462
30797
|
}
|
|
30463
30798
|
const displayRoot = getDisplayRoot();
|
|
30464
|
-
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, ""))];
|
|
30465
30800
|
const text = lines2.join("\n");
|
|
30466
30801
|
emitGuarded(text, "symbol");
|
|
30467
30802
|
recordReadStat("symbol_read", fullSourceBytes, text, symName);
|
|
30468
30803
|
return 0;
|
|
30469
30804
|
}
|
|
30805
|
+
function applyExcludeTestsFilter(refs) {
|
|
30806
|
+
const filtered = refs.filter((r) => !isTestFile(r.filePath));
|
|
30807
|
+
return { refs: filtered, suppressed: refs.length - filtered.length };
|
|
30808
|
+
}
|
|
30470
30809
|
function groupRefsByFile(refs) {
|
|
30471
30810
|
const byFile = /* @__PURE__ */ new Map();
|
|
30472
30811
|
for (const ref2 of refs) byFile.set(ref2.filePath, (byFile.get(ref2.filePath) ?? 0) + 1);
|
|
30473
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));
|
|
30474
30813
|
}
|
|
30475
|
-
function renderTopFilesSummary(refs, topN, displayRoot) {
|
|
30814
|
+
function renderTopFilesSummary(refs, topN, displayRoot, suppressed) {
|
|
30476
30815
|
const grouped = groupRefsByFile(refs);
|
|
30477
30816
|
const shown = grouped.slice(0, topN);
|
|
30478
|
-
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}`];
|
|
30479
30819
|
for (const { file: file2, count } of shown) lines2.push(` ${count} ${toDisplayPath(displayRoot, file2)}`);
|
|
30480
30820
|
const omittedFiles = grouped.length - shown.length;
|
|
30481
30821
|
if (omittedFiles > 0) {
|
|
@@ -30489,7 +30829,7 @@ function topFilesJsonPayload(refs, topN) {
|
|
|
30489
30829
|
const shown = grouped.slice(0, topN);
|
|
30490
30830
|
return { fileCounts: shown, totalFiles: grouped.length, totalRefs: refs.length, shown: shown.length };
|
|
30491
30831
|
}
|
|
30492
|
-
function renderCallerGroups(refs, displayRoot) {
|
|
30832
|
+
function renderCallerGroups(refs, displayRoot, contextLines = 0) {
|
|
30493
30833
|
const byFile = /* @__PURE__ */ new Map();
|
|
30494
30834
|
for (const ref2 of refs) {
|
|
30495
30835
|
const bucket = byFile.get(ref2.filePath);
|
|
@@ -30501,9 +30841,12 @@ function renderCallerGroups(refs, displayRoot) {
|
|
|
30501
30841
|
}
|
|
30502
30842
|
const lines2 = [];
|
|
30503
30843
|
for (const [file2, fileRefs] of byFile) {
|
|
30504
|
-
|
|
30844
|
+
const displayPath = toDisplayPath(displayRoot, file2);
|
|
30845
|
+
lines2.push(`${displayPath}:`);
|
|
30505
30846
|
for (const ref2 of fileRefs) {
|
|
30506
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, "", " "));
|
|
30507
30850
|
}
|
|
30508
30851
|
}
|
|
30509
30852
|
return lines2;
|
|
@@ -30542,7 +30885,19 @@ function prepareSymbolListing(file2, opts) {
|
|
|
30542
30885
|
const fullSourceBytes = sumFileSizes([resolved]);
|
|
30543
30886
|
return { kind: "ok", resolved, filtered, refCounts, fullSourceBytes, symbolsTruncated, trueSymbolCount };
|
|
30544
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
|
+
}
|
|
30545
30898
|
function runSkeleton(opts) {
|
|
30899
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
30900
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runSkeleton({ ...opts, file: file2 }));
|
|
30546
30901
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
30547
30902
|
if (prep.kind === "empty") {
|
|
30548
30903
|
return { text: prep.text, code: 1 };
|
|
@@ -30578,6 +30933,8 @@ function runSkeleton(opts) {
|
|
|
30578
30933
|
return { text, code: 0 };
|
|
30579
30934
|
}
|
|
30580
30935
|
function runOutline(opts) {
|
|
30936
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
30937
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runOutline({ ...opts, file: file2 }));
|
|
30581
30938
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
30582
30939
|
if (prep.kind === "empty") {
|
|
30583
30940
|
return { text: prep.text, code: 1 };
|
|
@@ -31152,10 +31509,13 @@ function runBriefCore(opts) {
|
|
|
31152
31509
|
const resolution = resolveSymbolSpec(opts.spec);
|
|
31153
31510
|
if (resolution.kind === "ambiguous") {
|
|
31154
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.
|
|
31155
31513
|
text: formatAmbiguity(
|
|
31156
31514
|
resolution.symbol,
|
|
31157
31515
|
resolution.file,
|
|
31158
|
-
resolution.candidates
|
|
31516
|
+
resolution.candidates,
|
|
31517
|
+
void 0,
|
|
31518
|
+
"brief"
|
|
31159
31519
|
),
|
|
31160
31520
|
code: 1
|
|
31161
31521
|
};
|
|
@@ -31178,7 +31538,7 @@ function runBriefCore(opts) {
|
|
|
31178
31538
|
if (opts.json === true) {
|
|
31179
31539
|
const result = {
|
|
31180
31540
|
symbol: match2,
|
|
31181
|
-
callers: shown,
|
|
31541
|
+
callers: (opts.context ?? 0) > 0 ? shown.map((c) => ({ ...c, contextLines: buildContextWindow(c.file, c.line, opts.context ?? 0) ?? [] })) : shown,
|
|
31182
31542
|
totalCallers,
|
|
31183
31543
|
truncated,
|
|
31184
31544
|
section: section2
|
|
@@ -31197,7 +31557,10 @@ function runBriefCore(opts) {
|
|
|
31197
31557
|
];
|
|
31198
31558
|
lines2.push(`Callers (${totalCallers}):`);
|
|
31199
31559
|
for (const c of shown) {
|
|
31200
|
-
|
|
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, "", " "));
|
|
31201
31564
|
}
|
|
31202
31565
|
if (truncated) {
|
|
31203
31566
|
lines2.push(` ...(${totalCallers - shown.length} more elided)`);
|
|
@@ -31229,11 +31592,40 @@ ${sub.text}`);
|
|
|
31229
31592
|
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
31230
31593
|
return { text, code: anyFound ? 0 : 1 };
|
|
31231
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
|
+
}
|
|
31232
31617
|
function runBrief(opts) {
|
|
31233
31618
|
if (opts.limit !== void 0 && opts.limit <= 0) {
|
|
31234
31619
|
emitErr2(`--limit must be a positive number, got: ${opts.limit}`);
|
|
31235
31620
|
return 1;
|
|
31236
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
|
+
}
|
|
31237
31629
|
const { file: file2, symbol: symbol3 } = parseReadSpec(opts.spec);
|
|
31238
31630
|
if (symbol3 !== void 0 && symbol3 !== "" && symbol3.includes(",")) {
|
|
31239
31631
|
const multiSymbols = symbol3.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -31325,6 +31717,34 @@ function parseDiffHunks(diffText) {
|
|
|
31325
31717
|
}
|
|
31326
31718
|
return hunksByFile;
|
|
31327
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
|
+
}
|
|
31328
31748
|
function runChanged(opts = {}) {
|
|
31329
31749
|
const ref2 = opts.ref ?? "HEAD~5";
|
|
31330
31750
|
const cwd = opts.projectRoot ?? process.cwd();
|
|
@@ -31334,6 +31754,10 @@ function runChanged(opts = {}) {
|
|
|
31334
31754
|
const result = runGit(["diff", ref2, "--name-only"], { cwd });
|
|
31335
31755
|
if (result.exitCode !== 0) {
|
|
31336
31756
|
emitErr2(`git diff failed: ${result.stderr}`);
|
|
31757
|
+
const hint = buildChangedRefHint(cwd, ref2);
|
|
31758
|
+
if (hint !== null) {
|
|
31759
|
+
emitErr2(hint);
|
|
31760
|
+
}
|
|
31337
31761
|
return 1;
|
|
31338
31762
|
}
|
|
31339
31763
|
changedFiles = result.stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
@@ -31439,13 +31863,16 @@ function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
|
|
|
31439
31863
|
resolution.symbol,
|
|
31440
31864
|
resolution.file,
|
|
31441
31865
|
resolution.candidates,
|
|
31442
|
-
projectRoot
|
|
31866
|
+
projectRoot,
|
|
31867
|
+
commandName
|
|
31443
31868
|
)
|
|
31444
31869
|
);
|
|
31445
31870
|
return null;
|
|
31446
31871
|
}
|
|
31447
31872
|
if (resolution.kind === "none") {
|
|
31448
31873
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
31874
|
+
const crossFileLead = formatCrossFileLead(commandName, symbol3, file2, projectRoot);
|
|
31875
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
31449
31876
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
31450
31877
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
31451
31878
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -31655,22 +32082,29 @@ function runGrep(opts) {
|
|
|
31655
32082
|
return 1;
|
|
31656
32083
|
}
|
|
31657
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
|
+
}
|
|
31658
32097
|
if (opts.json === true) {
|
|
31659
32098
|
const payload = { items: truncated, truncated: hits.length > maxLines, totalCount: hits.length };
|
|
31660
32099
|
emit3(JSON.stringify(payload, null, 2));
|
|
31661
32100
|
return 0;
|
|
31662
32101
|
}
|
|
31663
32102
|
for (const hit of truncated) {
|
|
32103
|
+
const symbolTag = opts.symbol === true && hit.symbol != null ? ` [${hit.symbol.name} (${hit.symbol.kind})]` : "";
|
|
31664
32104
|
if (hit.context !== void 0) {
|
|
31665
|
-
for (const
|
|
31666
|
-
if (ctxLine.line === hit.line) {
|
|
31667
|
-
emit3(`${hit.file}:${ctxLine.line}: ${ctxLine.text}`);
|
|
31668
|
-
} else {
|
|
31669
|
-
emit3(`${hit.file}-${ctxLine.line}- ${ctxLine.text}`);
|
|
31670
|
-
}
|
|
31671
|
-
}
|
|
32105
|
+
for (const line of renderContextWindow(hit.file, hit.line, hit.context, symbolTag)) emit3(line);
|
|
31672
32106
|
} else {
|
|
31673
|
-
emit3(`${hit.file}:${hit.line}: ${hit.text}`);
|
|
32107
|
+
emit3(`${hit.file}:${hit.line}: ${hit.text}${symbolTag}`);
|
|
31674
32108
|
}
|
|
31675
32109
|
}
|
|
31676
32110
|
if (hits.length > maxLines) {
|
|
@@ -31853,9 +32287,24 @@ function extractExportNames(text, ext2) {
|
|
|
31853
32287
|
}
|
|
31854
32288
|
return names;
|
|
31855
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
|
+
}
|
|
31856
32299
|
function runExports(opts) {
|
|
32300
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
32301
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Exports", (file2) => runExports({ ...opts, file: file2 }));
|
|
31857
32302
|
const symbols = querySymbols({ filePath: resolveIndexPath(opts.file), limit: 500 });
|
|
31858
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
|
+
};
|
|
31859
32308
|
const names = [];
|
|
31860
32309
|
for (const s of symbols) {
|
|
31861
32310
|
if (/^(?:export|pub\b|public\b)/.test(s.body.trimStart()) && !names.includes(s.name)) {
|
|
@@ -31880,12 +32329,23 @@ function runExports(opts) {
|
|
|
31880
32329
|
}
|
|
31881
32330
|
const fullSourceBytes = sumFileSizes([opts.file]);
|
|
31882
32331
|
if (opts.json === true) {
|
|
31883
|
-
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
|
+
);
|
|
31884
32340
|
emit3(jsonText);
|
|
31885
32341
|
recordReadStat("exports", fullSourceBytes, jsonText, opts.file);
|
|
31886
32342
|
return 0;
|
|
31887
32343
|
}
|
|
31888
|
-
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
|
+
});
|
|
31889
32349
|
for (const line of outLines) {
|
|
31890
32350
|
emit3(line);
|
|
31891
32351
|
}
|
|
@@ -32179,6 +32639,8 @@ function importsExtensionFor(filePath) {
|
|
|
32179
32639
|
return path47.extname(filePath);
|
|
32180
32640
|
}
|
|
32181
32641
|
function runImports(opts) {
|
|
32642
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
32643
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Imports", (file2) => runImports({ ...opts, file: file2 }));
|
|
32182
32644
|
const text = readFileText(opts.file);
|
|
32183
32645
|
if (text === null) {
|
|
32184
32646
|
emitErr2(`Could not read: ${opts.file}`);
|
|
@@ -32272,11 +32734,12 @@ async function runSemantic(query, opts) {
|
|
|
32272
32734
|
);
|
|
32273
32735
|
const hits = mergeNearbyHits(rawHits).slice(0, n);
|
|
32274
32736
|
if (hits.length > 0) {
|
|
32737
|
+
const enclosing = hits.map((h) => resolveEnclosingSymbol(h.filePath, h.startLine));
|
|
32275
32738
|
if (opts.json === true) {
|
|
32276
|
-
const items = hits.map((h) => ({
|
|
32739
|
+
const items = hits.map((h, i) => ({
|
|
32277
32740
|
filePath: h.filePath,
|
|
32278
|
-
name: null,
|
|
32279
|
-
kind: null,
|
|
32741
|
+
name: enclosing[i]?.name ?? null,
|
|
32742
|
+
kind: enclosing[i]?.kind ?? null,
|
|
32280
32743
|
startLine: h.startLine,
|
|
32281
32744
|
endLine: h.endLine,
|
|
32282
32745
|
distance: h.distance,
|
|
@@ -32287,10 +32750,12 @@ async function runSemantic(query, opts) {
|
|
|
32287
32750
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
|
|
32288
32751
|
return { text: text3, code: 0 };
|
|
32289
32752
|
}
|
|
32290
|
-
const blocks2 = hits.map(
|
|
32291
|
-
|
|
32292
|
-
${
|
|
32293
|
-
|
|
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
|
+
});
|
|
32294
32759
|
const text2 = guardText(blocks2.join("\n\n"), "semantic");
|
|
32295
32760
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text2, query);
|
|
32296
32761
|
return { text: text2, code: 0 };
|
|
@@ -32379,7 +32844,7 @@ function runNoteList(opts = {}) {
|
|
|
32379
32844
|
});
|
|
32380
32845
|
return { text: lines2.join("\n"), code: 0 };
|
|
32381
32846
|
}
|
|
32382
|
-
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;
|
|
32383
32848
|
var init_read_commands = __esm({
|
|
32384
32849
|
"src/read_commands.ts"() {
|
|
32385
32850
|
"use strict";
|
|
@@ -32424,6 +32889,7 @@ var init_read_commands = __esm({
|
|
|
32424
32889
|
PARENT_IDENTIFIER_RE = /^[\w$]+$/;
|
|
32425
32890
|
SKELETON_SYMBOL_CAP = 5e3;
|
|
32426
32891
|
HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
|
|
32892
|
+
EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
32427
32893
|
DEFAULT_LOG_MAX_COUNT = 20;
|
|
32428
32894
|
}
|
|
32429
32895
|
});
|
|
@@ -48520,7 +48986,7 @@ init_util2();
|
|
|
48520
48986
|
init_disk_cache();
|
|
48521
48987
|
init_lang_patterns();
|
|
48522
48988
|
import { readdirSync as readdirSync11, readFileSync as readFileSync21, statSync as statSync15 } from "fs";
|
|
48523
|
-
import { resolve as
|
|
48989
|
+
import { resolve as resolve10 } from "path";
|
|
48524
48990
|
|
|
48525
48991
|
// src/recall_index.ts
|
|
48526
48992
|
init_define_import_meta_env();
|
|
@@ -48753,7 +49219,7 @@ function depLockfileFingerprintSync(cmd, cwd) {
|
|
|
48753
49219
|
if (!candidates) return null;
|
|
48754
49220
|
for (const lockfile of candidates) {
|
|
48755
49221
|
try {
|
|
48756
|
-
const content = readFileSync21(
|
|
49222
|
+
const content = readFileSync21(resolve10(cwd, lockfile));
|
|
48757
49223
|
return shortFingerprint(content);
|
|
48758
49224
|
} catch {
|
|
48759
49225
|
continue;
|
|
@@ -48906,7 +49372,7 @@ function extractFirstPathArg(cmd, cwd, fallback) {
|
|
|
48906
49372
|
const token2 = tokens[i];
|
|
48907
49373
|
if (!token2.startsWith("-")) {
|
|
48908
49374
|
if (!token2.startsWith("/")) {
|
|
48909
|
-
return
|
|
49375
|
+
return resolve10(cwd, token2);
|
|
48910
49376
|
}
|
|
48911
49377
|
return token2;
|
|
48912
49378
|
}
|
|
@@ -49277,6 +49743,7 @@ init_index_reader();
|
|
|
49277
49743
|
init_parser_types();
|
|
49278
49744
|
init_doc_embed_extract();
|
|
49279
49745
|
init_paths();
|
|
49746
|
+
init_project();
|
|
49280
49747
|
init_hooks_index();
|
|
49281
49748
|
init_install();
|
|
49282
49749
|
init_codex_install();
|
|
@@ -64250,15 +64717,19 @@ var BRIDGE_CAPABILITY_MATRIX = [
|
|
|
64250
64717
|
harness: "copilot_cli",
|
|
64251
64718
|
label: "Copilot CLI",
|
|
64252
64719
|
sourceFile: "src/bridges/copilot_cli_install.ts (COPILOT_CLI_HOOK_EVENTS), src/bridges/copilot_cli.ts (COPILOT_TO_TG_EVENT)",
|
|
64253
|
-
implemented: /* @__PURE__ */ new Set([
|
|
64720
|
+
implemented: /* @__PURE__ */ new Set([
|
|
64721
|
+
"session_start",
|
|
64722
|
+
"pre_tool_use",
|
|
64723
|
+
"post_tool_use",
|
|
64724
|
+
"pre_compact",
|
|
64725
|
+
"stop",
|
|
64726
|
+
"subagent_stop",
|
|
64727
|
+
"user_prompt_submit"
|
|
64728
|
+
]),
|
|
64254
64729
|
reasons: [
|
|
64255
64730
|
{
|
|
64256
64731
|
events: ["notification"],
|
|
64257
64732
|
reason: "Copilot CLI has a real 'notification' hook event, but copilot_cli.ts's COPILOT_TO_TG_EVENT deliberately leaves it (and sessionEnd/postToolUseFailure/subagentStart/errorOccurred/permissionRequest) unimplemented rather than guessed at"
|
|
64258
|
-
},
|
|
64259
|
-
{
|
|
64260
|
-
events: ["session_start"],
|
|
64261
|
-
reason: "COPILOT_TO_TG_EVENT has no session-start mapping wired yet -- left unimplemented rather than guessed at"
|
|
64262
64733
|
}
|
|
64263
64734
|
]
|
|
64264
64735
|
},
|
|
@@ -64872,7 +65343,7 @@ function checkDbExists(dataDir2) {
|
|
|
64872
65343
|
return {
|
|
64873
65344
|
name: "Database",
|
|
64874
65345
|
status: "ok",
|
|
64875
|
-
message: `global.db exists (${toKB(sizeBytes)} KB)`
|
|
65346
|
+
message: `global.db exists (${toKB(sizeBytes)} KB) at ${dbPath}`
|
|
64876
65347
|
};
|
|
64877
65348
|
}
|
|
64878
65349
|
function checkSymbolBodySize(dbPath) {
|
|
@@ -64920,6 +65391,13 @@ function checkSymbolCount(dbPath, rootDir) {
|
|
|
64920
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'`
|
|
64921
65392
|
};
|
|
64922
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
|
+
}
|
|
64923
65401
|
return {
|
|
64924
65402
|
name: "Symbols",
|
|
64925
65403
|
status: "ok",
|
|
@@ -69571,7 +70049,7 @@ import { existsSync as existsSync33, readdirSync as readdirSync20, statSync as s
|
|
|
69571
70049
|
import * as http from "http";
|
|
69572
70050
|
import * as https from "https";
|
|
69573
70051
|
import { isIPv4, isIPv6 } from "net";
|
|
69574
|
-
import { resolve as
|
|
70052
|
+
import { resolve as resolve18, join as join41 } from "path";
|
|
69575
70053
|
import { URL as URL2 } from "url";
|
|
69576
70054
|
import { promisify } from "util";
|
|
69577
70055
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -69743,7 +70221,7 @@ function cleanupStaleDownloads() {
|
|
|
69743
70221
|
const files = readdirSync20(cacheDir);
|
|
69744
70222
|
for (const file2 of files) {
|
|
69745
70223
|
if (file2.endsWith(".tmp")) {
|
|
69746
|
-
const filePath =
|
|
70224
|
+
const filePath = resolve18(cacheDir, file2);
|
|
69747
70225
|
try {
|
|
69748
70226
|
const stat2 = statSync26(filePath);
|
|
69749
70227
|
if (Date.now() - stat2.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -72020,6 +72498,9 @@ function runHintStatsCommand(opts = {}) {
|
|
|
72020
72498
|
`);
|
|
72021
72499
|
return;
|
|
72022
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
|
+
}
|
|
72023
72504
|
printSummary(rows);
|
|
72024
72505
|
}
|
|
72025
72506
|
|
|
@@ -72278,7 +72759,11 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
72278
72759
|
function cmdMap(opts) {
|
|
72279
72760
|
const map3 = buildProjectMap(process.cwd(), { compact: opts.compact === true });
|
|
72280
72761
|
const text = formatProjectMap(map3, map3.compact);
|
|
72281
|
-
|
|
72762
|
+
if (opts.json === true) {
|
|
72763
|
+
out(JSON.stringify(map3));
|
|
72764
|
+
} else {
|
|
72765
|
+
out(text);
|
|
72766
|
+
}
|
|
72282
72767
|
const bytesSaved = mapLookupBytesSaved(map3, text);
|
|
72283
72768
|
recordStat("map_lookup", bytesSaved, Math.round(bytesSaved / 4));
|
|
72284
72769
|
}
|
|
@@ -72320,8 +72805,8 @@ async function cmdMcpServe() {
|
|
|
72320
72805
|
const server = createMcpServer2();
|
|
72321
72806
|
const transport = new StdioServerTransport();
|
|
72322
72807
|
await server.connect(transport);
|
|
72323
|
-
await new Promise((
|
|
72324
|
-
server.server.onclose =
|
|
72808
|
+
await new Promise((resolve25) => {
|
|
72809
|
+
server.server.onclose = resolve25;
|
|
72325
72810
|
});
|
|
72326
72811
|
}
|
|
72327
72812
|
async function cmdHook(event, opts) {
|
|
@@ -72528,6 +73013,14 @@ async function cmdDoctor(opts) {
|
|
|
72528
73013
|
if (project !== null) {
|
|
72529
73014
|
doctorOpts.rootDir = project.root;
|
|
72530
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
|
+
}
|
|
72531
73024
|
const code = await runDoctorAndExit(doctorOpts);
|
|
72532
73025
|
if (code !== 0) {
|
|
72533
73026
|
throw new CliError("doctor checks failed");
|
|
@@ -73031,6 +73524,16 @@ function runExitText(fn) {
|
|
|
73031
73524
|
process.exitCode = 1;
|
|
73032
73525
|
}
|
|
73033
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
|
+
}
|
|
73034
73537
|
function cmdCompress(opts) {
|
|
73035
73538
|
try {
|
|
73036
73539
|
if (opts.compress === false) {
|
|
@@ -73155,6 +73658,10 @@ async function cmdSkillList(opts) {
|
|
|
73155
73658
|
return `${s.name.padEnd(25)} ${bodyKb.padStart(6)}K ${compactKb.padStart(6)}K ${marker} ${s.hitCount.toString().padStart(3)} ${age.padStart(3)} ${staleStatus}`;
|
|
73156
73659
|
});
|
|
73157
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
|
+
}
|
|
73158
73665
|
out([header, ...lines2].join("\n"));
|
|
73159
73666
|
}
|
|
73160
73667
|
}
|
|
@@ -73533,7 +74040,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
73533
74040
|
throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
|
|
73534
74041
|
}
|
|
73535
74042
|
const maxBytes = maxMB * 1024 * 1024;
|
|
73536
|
-
return new Promise((
|
|
74043
|
+
return new Promise((resolve25, reject) => {
|
|
73537
74044
|
const chunks = [];
|
|
73538
74045
|
let totalBytes = 0;
|
|
73539
74046
|
let settled = false;
|
|
@@ -73557,7 +74064,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
73557
74064
|
try {
|
|
73558
74065
|
atomicWriteBuffer(dest, Buffer.concat(chunks));
|
|
73559
74066
|
enqueueDirtyPathSafe(dest);
|
|
73560
|
-
|
|
74067
|
+
resolve25();
|
|
73561
74068
|
} catch (e) {
|
|
73562
74069
|
try {
|
|
73563
74070
|
mapFsError(e, void 0, dest);
|
|
@@ -73992,19 +74499,26 @@ function buildProgram() {
|
|
|
73992
74499
|
process.exitCode = 1;
|
|
73993
74500
|
}
|
|
73994
74501
|
};
|
|
73995
|
-
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(
|
|
73996
|
-
|
|
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(
|
|
73997
74510
|
() => runSymbol({
|
|
73998
74511
|
name: name2,
|
|
73999
74512
|
limit: opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20,
|
|
74000
74513
|
...opts.file !== void 0 ? { file: opts.file } : {},
|
|
74001
74514
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
74515
|
+
...projectRoot !== void 0 ? { projectRoot } : {},
|
|
74002
74516
|
...opts.json === true ? { json: true } : {}
|
|
74003
74517
|
})
|
|
74004
|
-
)
|
|
74005
|
-
);
|
|
74518
|
+
);
|
|
74519
|
+
});
|
|
74006
74520
|
program2.command("read <spec>").description(
|
|
74007
|
-
"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)"
|
|
74008
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(
|
|
74009
74523
|
(spec, opts) => runExitText(
|
|
74010
74524
|
() => runRead({
|
|
@@ -74016,13 +74530,14 @@ function buildProgram() {
|
|
|
74016
74530
|
)
|
|
74017
74531
|
);
|
|
74018
74532
|
program2.command("brief <spec>").description(
|
|
74019
|
-
"symbol body + callers + containing doc section in one call (spec: file::symbol; comma-separated file::a,b for a merged multi-symbol view)"
|
|
74020
|
-
).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(
|
|
74021
74535
|
(spec, opts) => runExit(
|
|
74022
74536
|
() => runBrief({
|
|
74023
74537
|
spec,
|
|
74024
74538
|
...opts.json === true ? { json: true } : {},
|
|
74025
|
-
...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) } : {}
|
|
74026
74541
|
})
|
|
74027
74542
|
)
|
|
74028
74543
|
);
|
|
@@ -74032,44 +74547,56 @@ function buildProgram() {
|
|
|
74032
74547
|
(spec, opts) => opts.list === true ? runExit(() => runListSections({ file: spec, ...opts.json === true ? { json: true } : {} })) : runExitText(() => runSection({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
74033
74548
|
);
|
|
74034
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));
|
|
74035
|
-
program2.command("skeleton <file>").description(
|
|
74036
|
-
(file2, opts) => runExitText(
|
|
74037
|
-
() =>
|
|
74038
|
-
|
|
74039
|
-
|
|
74040
|
-
|
|
74041
|
-
|
|
74042
|
-
|
|
74043
|
-
|
|
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
|
+
)
|
|
74044
74564
|
)
|
|
74045
74565
|
);
|
|
74046
|
-
program2.command("outline <file>").description(
|
|
74047
|
-
(file2, opts) => runExitText(
|
|
74048
|
-
() =>
|
|
74049
|
-
|
|
74050
|
-
|
|
74051
|
-
|
|
74052
|
-
|
|
74053
|
-
|
|
74054
|
-
|
|
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
|
+
)
|
|
74055
74580
|
)
|
|
74056
74581
|
);
|
|
74057
|
-
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(
|
|
74058
74583
|
"--top <n>",
|
|
74059
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"
|
|
74060
|
-
).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(
|
|
74061
74586
|
(spec, opts) => runExit(
|
|
74062
74587
|
() => runRefs({
|
|
74063
74588
|
spec,
|
|
74064
74589
|
...opts.callers === true ? { callers: true } : {},
|
|
74065
74590
|
...opts.json === true ? { json: true } : {},
|
|
74066
74591
|
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
74067
|
-
...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 } : {}
|
|
74068
74595
|
})
|
|
74069
74596
|
)
|
|
74070
74597
|
);
|
|
74071
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));
|
|
74072
|
-
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));
|
|
74073
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));
|
|
74074
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));
|
|
74075
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));
|
|
@@ -74085,7 +74612,7 @@ function buildProgram() {
|
|
|
74085
74612
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
74086
74613
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
74087
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));
|
|
74088
|
-
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));
|
|
74089
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));
|
|
74090
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));
|
|
74091
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));
|
|
@@ -74099,11 +74626,17 @@ function buildProgram() {
|
|
|
74099
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));
|
|
74100
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));
|
|
74101
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));
|
|
74102
|
-
program2.command("exports <file>").description(
|
|
74103
|
-
(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
|
+
})
|
|
74104
74634
|
);
|
|
74105
|
-
program2.command("imports <file>").description(
|
|
74106
|
-
(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
|
+
})
|
|
74107
74640
|
);
|
|
74108
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(
|
|
74109
74642
|
(pattern, opts) => runExit(
|
|
@@ -74114,7 +74647,7 @@ function buildProgram() {
|
|
|
74114
74647
|
})
|
|
74115
74648
|
)
|
|
74116
74649
|
);
|
|
74117
|
-
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(
|
|
74118
74651
|
(pattern, paths, opts) => runExit(
|
|
74119
74652
|
() => runGrep({
|
|
74120
74653
|
pattern,
|
|
@@ -74122,7 +74655,8 @@ function buildProgram() {
|
|
|
74122
74655
|
...opts.json === true ? { json: true } : {},
|
|
74123
74656
|
...opts.maxLines !== void 0 ? { maxLines: requirePositiveInt("--max-lines", opts.maxLines) } : {},
|
|
74124
74657
|
...opts.recursive === false ? { recursive: false } : {},
|
|
74125
|
-
...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 } : {}
|
|
74126
74660
|
})
|
|
74127
74661
|
)
|
|
74128
74662
|
);
|
|
@@ -74133,16 +74667,18 @@ function buildProgram() {
|
|
|
74133
74667
|
program2.command("skill-history").description("list cached skill versions newest-first").option("-j, --json", "output as JSON").action(guard(cmdSkillHistory));
|
|
74134
74668
|
program2.command("skill-diff <name>").description("show diff between two cached versions of a skill").action(guard(cmdSkillDiff));
|
|
74135
74669
|
program2.command("skill-section <nameHeading> [headingArg]").description("extract a named section from a skill").action(guard(cmdSkillSection));
|
|
74136
|
-
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(
|
|
74137
74671
|
(symbol3, opts) => runExit(
|
|
74138
74672
|
() => runCallers({
|
|
74139
74673
|
symbol: symbol3,
|
|
74140
74674
|
...opts.json === true ? { json: true } : {},
|
|
74141
|
-
...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 } : {}
|
|
74142
74678
|
})
|
|
74143
74679
|
)
|
|
74144
74680
|
);
|
|
74145
|
-
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(
|
|
74146
74682
|
(symbol3, opts) => runExit(
|
|
74147
74683
|
() => runCallChain({
|
|
74148
74684
|
symbol: symbol3,
|
|
@@ -74151,7 +74687,7 @@ function buildProgram() {
|
|
|
74151
74687
|
})
|
|
74152
74688
|
)
|
|
74153
74689
|
);
|
|
74154
|
-
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(
|
|
74155
74691
|
(symbol3, opts) => runExit(
|
|
74156
74692
|
() => runImpact({
|
|
74157
74693
|
symbol: symbol3,
|
|
@@ -74160,13 +74696,14 @@ function buildProgram() {
|
|
|
74160
74696
|
})
|
|
74161
74697
|
)
|
|
74162
74698
|
);
|
|
74163
|
-
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(
|
|
74164
74700
|
(opts) => runExit(
|
|
74165
74701
|
() => runDead({
|
|
74166
74702
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
74167
74703
|
...opts.includePrivate === true ? { includePrivate: true } : {},
|
|
74168
74704
|
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
74169
|
-
...opts.json === true ? { json: true } : {}
|
|
74705
|
+
...opts.json === true ? { json: true } : {},
|
|
74706
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
74170
74707
|
})
|
|
74171
74708
|
)
|
|
74172
74709
|
);
|
|
@@ -74185,7 +74722,7 @@ function buildProgram() {
|
|
|
74185
74722
|
program2.command("scope <fileColonLine>").description("list symbols enclosing a file:line position, innermost first").option("-j, --json", "output as JSON").action(
|
|
74186
74723
|
(spec, opts) => runExit(() => runScope({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
74187
74724
|
);
|
|
74188
|
-
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(
|
|
74189
74726
|
(spec, opts) => runExit(
|
|
74190
74727
|
() => runSimilar({
|
|
74191
74728
|
spec,
|
|
@@ -74224,7 +74761,7 @@ function buildProgram() {
|
|
|
74224
74761
|
})
|
|
74225
74762
|
)
|
|
74226
74763
|
);
|
|
74227
|
-
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(
|
|
74228
74765
|
(spec, opts) => runExit(() => runBlame({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
74229
74766
|
);
|
|
74230
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(
|
|
@@ -74308,16 +74845,16 @@ function buildProgram() {
|
|
|
74308
74845
|
}))());
|
|
74309
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 } : {} }))());
|
|
74310
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))());
|
|
74311
|
-
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(
|
|
74312
|
-
(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(
|
|
74313
74850
|
() => runChanged({
|
|
74314
|
-
ref: opts.since ?? "HEAD~5",
|
|
74851
|
+
ref: opts.since ?? ref2 ?? "HEAD~5",
|
|
74315
74852
|
...opts.symbol === true ? { symbolMode: true } : {},
|
|
74316
74853
|
...opts.json === true ? { json: true } : {}
|
|
74317
74854
|
})
|
|
74318
74855
|
)
|
|
74319
74856
|
);
|
|
74320
|
-
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(
|
|
74321
74858
|
(spec, ref2, opts) => runExit(
|
|
74322
74859
|
() => runDiff({
|
|
74323
74860
|
spec,
|
|
@@ -74326,7 +74863,7 @@ function buildProgram() {
|
|
|
74326
74863
|
})
|
|
74327
74864
|
)
|
|
74328
74865
|
);
|
|
74329
|
-
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(
|
|
74330
74867
|
(spec, ref2, opts) => runExit(
|
|
74331
74868
|
() => runLog({
|
|
74332
74869
|
spec,
|
|
@@ -82989,7 +83526,7 @@ init_image_shrink();
|
|
|
82989
83526
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
82990
83527
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
82991
83528
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
82992
|
-
return new Promise((
|
|
83529
|
+
return new Promise((resolve25, reject) => {
|
|
82993
83530
|
const chunks = [];
|
|
82994
83531
|
let totalBytes = 0;
|
|
82995
83532
|
let settled = false;
|
|
@@ -83024,7 +83561,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
83024
83561
|
return;
|
|
83025
83562
|
}
|
|
83026
83563
|
try {
|
|
83027
|
-
|
|
83564
|
+
resolve25(JSON.parse(text));
|
|
83028
83565
|
} catch (err2) {
|
|
83029
83566
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
83030
83567
|
}
|