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.mjs
CHANGED
|
@@ -3050,7 +3050,7 @@ var require_commander = __commonJS({
|
|
|
3050
3050
|
import { createRequire } from "node:module";
|
|
3051
3051
|
function resolveVersion() {
|
|
3052
3052
|
if (true) {
|
|
3053
|
-
return "2.6.
|
|
3053
|
+
return "2.6.25";
|
|
3054
3054
|
}
|
|
3055
3055
|
const require2 = createRequire(import.meta.url);
|
|
3056
3056
|
const pkg = require2("../package.json");
|
|
@@ -4283,12 +4283,12 @@ function normalizeDarwinSystemAlias(p) {
|
|
|
4283
4283
|
}
|
|
4284
4284
|
function resolveIndexPath(file2, base = process.cwd()) {
|
|
4285
4285
|
const isWindowsAbsolute = (s) => /^[a-zA-Z]:[/\\]/.test(s);
|
|
4286
|
-
const
|
|
4287
|
-
return normalizePath(
|
|
4286
|
+
const resolve25 = isWindowsAbsolute(file2) || isWindowsAbsolute(base) ? path2.win32.resolve : path2.resolve;
|
|
4287
|
+
return normalizePath(resolve25(base, file2));
|
|
4288
4288
|
}
|
|
4289
4289
|
function toDisplayPath(root, target) {
|
|
4290
4290
|
if (root === void 0) return target;
|
|
4291
|
-
const rel = path2.relative(root, target).replace(/\\/g, "/");
|
|
4291
|
+
const rel = path2.relative(normalizePath(root), normalizePath(target)).replace(/\\/g, "/");
|
|
4292
4292
|
if (rel === "" || rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
4293
4293
|
return rel === "" ? "." : target;
|
|
4294
4294
|
}
|
|
@@ -4350,6 +4350,15 @@ function foldCase(s) {
|
|
|
4350
4350
|
function runGit(args, opts = {}) {
|
|
4351
4351
|
const subArgs = args[0] === "diff" ? [args[0], "--no-ext-diff", "--no-textconv", ...args.slice(1)] : args;
|
|
4352
4352
|
const fullArgs = [
|
|
4353
|
+
// Never take an optional lock. Every git call here is on someone else's
|
|
4354
|
+
// working repo, and a `status` that refreshes the index writes
|
|
4355
|
+
// `.git/index.lock`; if this process is killed mid-call -- which the hint
|
|
4356
|
+
// paths deliberately invite, since they spawn under a short timeout -- the
|
|
4357
|
+
// orphaned lock blocks every subsequent commit in that repo until a human
|
|
4358
|
+
// deletes it. Observed doing exactly that on 2026-08-05. `--no-optional-
|
|
4359
|
+
// locks` suppresses only locks git considers optional, so write commands
|
|
4360
|
+
// that genuinely need one are unaffected.
|
|
4361
|
+
"--no-optional-locks",
|
|
4353
4362
|
"-c",
|
|
4354
4363
|
"core.fsmonitor=",
|
|
4355
4364
|
"-c",
|
|
@@ -4673,6 +4682,28 @@ function writeIfDifferent(p, content, backup = false) {
|
|
|
4673
4682
|
atomicWriteText(p, content);
|
|
4674
4683
|
return true;
|
|
4675
4684
|
}
|
|
4685
|
+
function buildContextWindow(absPath, line, contextLines) {
|
|
4686
|
+
if (!Number.isFinite(contextLines) || contextLines <= 0) return null;
|
|
4687
|
+
let text;
|
|
4688
|
+
try {
|
|
4689
|
+
text = readFileSync2(absPath, "utf-8");
|
|
4690
|
+
} catch {
|
|
4691
|
+
return null;
|
|
4692
|
+
}
|
|
4693
|
+
const lines2 = text.split(/\r?\n/);
|
|
4694
|
+
const idx = line - 1;
|
|
4695
|
+
if (idx < 0 || idx >= lines2.length) return null;
|
|
4696
|
+
const start = Math.max(0, idx - contextLines);
|
|
4697
|
+
const end = Math.min(lines2.length - 1, idx + contextLines);
|
|
4698
|
+
const out2 = [];
|
|
4699
|
+
for (let i = start; i <= end; i++) out2.push({ line: i + 1, text: lines2[i] ?? "" });
|
|
4700
|
+
return out2;
|
|
4701
|
+
}
|
|
4702
|
+
function renderContextWindow(displayFile, matchLine3, window, matchSuffix = "", indent = "") {
|
|
4703
|
+
return window.map(
|
|
4704
|
+
(c) => c.line === matchLine3 ? `${indent}${displayFile}:${c.line}: ${c.text}${matchSuffix}` : `${indent}${displayFile}-${c.line}- ${c.text}`
|
|
4705
|
+
);
|
|
4706
|
+
}
|
|
4676
4707
|
function toKB(bytes) {
|
|
4677
4708
|
return Math.round(bytes / 1024);
|
|
4678
4709
|
}
|
|
@@ -4725,6 +4756,19 @@ function isCodeFenceDelimiter(line) {
|
|
|
4725
4756
|
function pad(s, n) {
|
|
4726
4757
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
4727
4758
|
}
|
|
4759
|
+
function installEpipeGuard(streams) {
|
|
4760
|
+
const targets = (streams ?? [process.stdout, process.stderr]).filter((s) => s !== void 0);
|
|
4761
|
+
for (const stream of targets) {
|
|
4762
|
+
stream.on("error", (err2) => {
|
|
4763
|
+
if (err2.code === "EPIPE") {
|
|
4764
|
+
process.exitCode = 0;
|
|
4765
|
+
return;
|
|
4766
|
+
}
|
|
4767
|
+
throw err2;
|
|
4768
|
+
});
|
|
4769
|
+
}
|
|
4770
|
+
return targets;
|
|
4771
|
+
}
|
|
4728
4772
|
function normalizePathForwardSlash(p, toLowerCase) {
|
|
4729
4773
|
let result = normalizePath(p).replace(/\\/g, "/");
|
|
4730
4774
|
if (toLowerCase) result = result.toLowerCase();
|
|
@@ -5056,7 +5100,8 @@ function defaultConfig() {
|
|
|
5056
5100
|
compression: getDefaultConfig("compression"),
|
|
5057
5101
|
context: getDefaultConfig("context"),
|
|
5058
5102
|
injection: getDefaultConfig("injection"),
|
|
5059
|
-
hint_stats: getDefaultConfig("hint_stats")
|
|
5103
|
+
hint_stats: getDefaultConfig("hint_stats"),
|
|
5104
|
+
semantic: getDefaultConfig("semantic")
|
|
5060
5105
|
};
|
|
5061
5106
|
}
|
|
5062
5107
|
function validatedBool(raw, def) {
|
|
@@ -5498,6 +5543,10 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
5498
5543
|
const hs = getDefaultConfig("hint_stats");
|
|
5499
5544
|
hs.suppress_threshold_pct = validatedInt(hs_raw["suppress_threshold_pct"], hs.suppress_threshold_pct, ...boundsOf("hint_stats.suppress_threshold_pct"));
|
|
5500
5545
|
hs.min_sample_size = validatedInt(hs_raw["min_sample_size"], hs.min_sample_size, ...boundsOf("hint_stats.min_sample_size"));
|
|
5546
|
+
const sem_raw = section(raw, "semantic");
|
|
5547
|
+
const sem = getDefaultConfig("semantic");
|
|
5548
|
+
sem.archive_weight = validatedFloat(sem_raw["archive_weight"], sem.archive_weight, ...boundsOf("semantic.archive_weight"));
|
|
5549
|
+
sem.docs_weight = validatedFloat(sem_raw["docs_weight"], sem.docs_weight, ...boundsOf("semantic.docs_weight"));
|
|
5501
5550
|
return {
|
|
5502
5551
|
compact_assist: ca,
|
|
5503
5552
|
bash_compress: bc,
|
|
@@ -5520,7 +5569,8 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
5520
5569
|
compression: cpr,
|
|
5521
5570
|
context: ctx,
|
|
5522
5571
|
injection: inj,
|
|
5523
|
-
hint_stats: hs
|
|
5572
|
+
hint_stats: hs,
|
|
5573
|
+
semantic: sem
|
|
5524
5574
|
};
|
|
5525
5575
|
}
|
|
5526
5576
|
function saveConfig(config2) {
|
|
@@ -5677,6 +5727,10 @@ function saveConfig(config2) {
|
|
|
5677
5727
|
hint_stats: {
|
|
5678
5728
|
suppress_threshold_pct: config2.hint_stats.suppress_threshold_pct,
|
|
5679
5729
|
min_sample_size: config2.hint_stats.min_sample_size
|
|
5730
|
+
},
|
|
5731
|
+
semantic: {
|
|
5732
|
+
archive_weight: config2.semantic.archive_weight,
|
|
5733
|
+
docs_weight: config2.semantic.docs_weight
|
|
5680
5734
|
}
|
|
5681
5735
|
};
|
|
5682
5736
|
const toml = stringify(data);
|
|
@@ -5869,6 +5923,10 @@ var init_config = __esm({
|
|
|
5869
5923
|
hint_stats: {
|
|
5870
5924
|
suppress_threshold_pct: 15,
|
|
5871
5925
|
min_sample_size: 5
|
|
5926
|
+
},
|
|
5927
|
+
semantic: {
|
|
5928
|
+
archive_weight: 0.7,
|
|
5929
|
+
docs_weight: 0.92
|
|
5872
5930
|
}
|
|
5873
5931
|
};
|
|
5874
5932
|
NUMERIC_FIELD_BOUNDS = {
|
|
@@ -5932,7 +5990,9 @@ var init_config = __esm({
|
|
|
5932
5990
|
"indexing.large_file_skip_kb": { min: 1, max: 1048576 },
|
|
5933
5991
|
"context.model_window_tokens": { min: 1e4, max: 1e7 },
|
|
5934
5992
|
"hint_stats.suppress_threshold_pct": { min: 0, max: 100 },
|
|
5935
|
-
"hint_stats.min_sample_size": { min: 1, max: 1e4 }
|
|
5993
|
+
"hint_stats.min_sample_size": { min: 1, max: 1e4 },
|
|
5994
|
+
"semantic.archive_weight": { min: 0.05, max: 1 },
|
|
5995
|
+
"semantic.docs_weight": { min: 0.05, max: 1 }
|
|
5936
5996
|
};
|
|
5937
5997
|
ENUM_FIELD_VALUES = {
|
|
5938
5998
|
"compression.profile": ["auto", "aggressive", "balanced", "minimal"],
|
|
@@ -12084,14 +12144,23 @@ const path = require('node:path')
|
|
|
12084
12144
|
const { pathToFileURL } = require('node:url')
|
|
12085
12145
|
|
|
12086
12146
|
// Copilot event name -> token-goat internal HookEventName (src/types.ts's
|
|
12087
|
-
// HOOK_EVENTS). Only these
|
|
12147
|
+
// HOOK_EVENTS). Only these seven have a token-goat handler; every other real
|
|
12088
12148
|
// Copilot event (sessionEnd, postToolUseFailure, subagentStart,
|
|
12089
12149
|
// errorOccurred, notification, permissionRequest) is left unimplemented
|
|
12090
12150
|
// rather than guessed at, and falls through to the default no-op below.
|
|
12091
|
-
// 'sessionStart'
|
|
12092
|
-
//
|
|
12093
|
-
//
|
|
12151
|
+
// 'sessionStart' was previously a permanent no-op on the stated grounds that
|
|
12152
|
+
// token-goat has no internal session_start handler. That was simply wrong --
|
|
12153
|
+
// hooks_session_start.ts has long emitted the command-routing reminder that
|
|
12154
|
+
// every other harness receives -- and the no-op was the reason Copilot CLI
|
|
12155
|
+
// sessions alone never got told token-goat exists. It is wired now: verified
|
|
12156
|
+
// against Copilot CLI 1.0.77 that a hooks.json sessionStart entry returning
|
|
12157
|
+
// {additionalContext} does reach the model. The github/copilot-cli#2142
|
|
12158
|
+
// fire-and-forget bug that would have made this dead wiring was fixed in a
|
|
12159
|
+
// pre-release months before that version, and its companion multi-extension
|
|
12160
|
+
// hook-overwrite bug never applied here: it hit runtime *extension* hooks,
|
|
12161
|
+
// while this config-file hooks.json path goes through Copilot's own merge.
|
|
12094
12162
|
const COPILOT_TO_TG_EVENT = {
|
|
12163
|
+
sessionStart: 'session_start',
|
|
12095
12164
|
preToolUse: 'pre_tool_use',
|
|
12096
12165
|
postToolUse: 'post_tool_use',
|
|
12097
12166
|
preCompact: 'pre_compact',
|
|
@@ -12203,11 +12272,6 @@ async function tryInProcess(entryPath, tgEvent, canonical) {
|
|
|
12203
12272
|
async function main() {
|
|
12204
12273
|
const copilotEvent = process.argv[2] || ''
|
|
12205
12274
|
|
|
12206
|
-
if (copilotEvent === 'sessionStart') {
|
|
12207
|
-
process.stdout.write('{}')
|
|
12208
|
-
return
|
|
12209
|
-
}
|
|
12210
|
-
|
|
12211
12275
|
const tgEvent = COPILOT_TO_TG_EVENT[copilotEvent]
|
|
12212
12276
|
if (!tgEvent) {
|
|
12213
12277
|
process.stdout.write('{}')
|
|
@@ -12333,7 +12397,10 @@ function translate(copilotEvent, resp) {
|
|
|
12333
12397
|
return {}
|
|
12334
12398
|
}
|
|
12335
12399
|
|
|
12336
|
-
if (copilotEvent === 'postToolUse') {
|
|
12400
|
+
if (copilotEvent === 'postToolUse' || copilotEvent === 'sessionStart') {
|
|
12401
|
+
// Both surface token-goat's context through the same field. sessionStart is
|
|
12402
|
+
// the one channel that reaches the model before it picks its first read
|
|
12403
|
+
// tool, so this is where the routing reminder has to land.
|
|
12337
12404
|
const context = extractContext(resp)
|
|
12338
12405
|
if (context) return { additionalContext: context }
|
|
12339
12406
|
return {}
|
|
@@ -12357,9 +12424,8 @@ function translate(copilotEvent, resp) {
|
|
|
12357
12424
|
// doc that both are notification-only -- Copilot never reads a response
|
|
12358
12425
|
// body for either, so any additionalContext/systemMessage token-goat
|
|
12359
12426
|
// produces has no surfacing channel here. This still routes through the
|
|
12360
|
-
// token-goat hook call above
|
|
12361
|
-
//
|
|
12362
|
-
// discarded.
|
|
12427
|
+
// token-goat hook call above so the internal handler's own side effects keep
|
|
12428
|
+
// running; only the response is discarded.
|
|
12363
12429
|
return {}
|
|
12364
12430
|
}
|
|
12365
12431
|
|
|
@@ -12399,8 +12465,13 @@ main()
|
|
|
12399
12465
|
import * as fs11 from "node:fs";
|
|
12400
12466
|
import * as os5 from "node:os";
|
|
12401
12467
|
import * as path11 from "node:path";
|
|
12468
|
+
function copilotCliUserRoot() {
|
|
12469
|
+
const override = process.env["COPILOT_HOME"];
|
|
12470
|
+
if (override !== void 0 && override.trim() !== "") return path11.resolve(override);
|
|
12471
|
+
return path11.join(os5.homedir(), ".copilot");
|
|
12472
|
+
}
|
|
12402
12473
|
function copilotCliUserHooksDir() {
|
|
12403
|
-
return path11.join(
|
|
12474
|
+
return path11.join(copilotCliUserRoot(), "hooks");
|
|
12404
12475
|
}
|
|
12405
12476
|
function copilotCliProjectHooksDir() {
|
|
12406
12477
|
return path11.join(process.cwd(), ".github", "hooks");
|
|
@@ -12505,6 +12576,7 @@ var init_copilot_cli_install = __esm({
|
|
|
12505
12576
|
init_copilot_cli();
|
|
12506
12577
|
init_guidance_block();
|
|
12507
12578
|
COPILOT_CLI_HOOK_EVENTS = [
|
|
12579
|
+
"sessionStart",
|
|
12508
12580
|
"preToolUse",
|
|
12509
12581
|
"postToolUse",
|
|
12510
12582
|
"preCompact",
|
|
@@ -15646,16 +15718,16 @@ var init_file_type_handler = __esm({
|
|
|
15646
15718
|
|
|
15647
15719
|
// src/skill_cache.ts
|
|
15648
15720
|
import * as fs18 from "fs/promises";
|
|
15649
|
-
import { resolve as
|
|
15721
|
+
import { resolve as resolve8 } from "path";
|
|
15650
15722
|
import { homedir as homedir7 } from "os";
|
|
15651
15723
|
import { readdirSync as readdirSync8, readFileSync as readFileSync13, existsSync as existsSync14, statSync as statSync8, unlinkSync as unlinkSync9 } from "node:fs";
|
|
15652
15724
|
function skillOutputsDir() {
|
|
15653
15725
|
if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
|
|
15654
|
-
return
|
|
15726
|
+
return resolve8(dataDir(), SKILLS_OUTPUT_SUBDIR);
|
|
15655
15727
|
}
|
|
15656
15728
|
function skillsSourceDir() {
|
|
15657
15729
|
if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
|
|
15658
|
-
return
|
|
15730
|
+
return resolve8(homedir7(), ".claude", "skills");
|
|
15659
15731
|
}
|
|
15660
15732
|
async function ensureSkillsDir() {
|
|
15661
15733
|
try {
|
|
@@ -15798,7 +15870,7 @@ async function listOutputs() {
|
|
|
15798
15870
|
continue;
|
|
15799
15871
|
}
|
|
15800
15872
|
try {
|
|
15801
|
-
const content = await fs18.readFile(
|
|
15873
|
+
const content = await fs18.readFile(resolve8(dir, entry.name), "utf-8");
|
|
15802
15874
|
const meta3 = JSON.parse(content);
|
|
15803
15875
|
metas.push(meta3);
|
|
15804
15876
|
} catch {
|
|
@@ -15832,7 +15904,7 @@ async function findCrossSessionEntry(skillName, contentSha) {
|
|
|
15832
15904
|
continue;
|
|
15833
15905
|
}
|
|
15834
15906
|
const dir = skillOutputsDir();
|
|
15835
|
-
const bodyPath =
|
|
15907
|
+
const bodyPath = resolve8(dir, `${meta3.outputId}.txt`);
|
|
15836
15908
|
try {
|
|
15837
15909
|
const bodyExists = await fs18.access(bodyPath).then(() => true).catch(() => false);
|
|
15838
15910
|
if (bodyExists) {
|
|
@@ -15887,7 +15959,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
15887
15959
|
const truncBuf = buf.slice(truncStart);
|
|
15888
15960
|
storedBody = truncBuf.toString("utf-8");
|
|
15889
15961
|
}
|
|
15890
|
-
await atomicWriteText(
|
|
15962
|
+
await atomicWriteText(resolve8(dir, `${outId}.txt`), storedBody);
|
|
15891
15963
|
const meta3 = {
|
|
15892
15964
|
outputId: outId,
|
|
15893
15965
|
skillName: name2,
|
|
@@ -15897,7 +15969,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
15897
15969
|
truncated,
|
|
15898
15970
|
sourcePath: opts?.sourcePath || ""
|
|
15899
15971
|
};
|
|
15900
|
-
await atomicWriteText(
|
|
15972
|
+
await atomicWriteText(resolve8(dir, `${outId}.meta`), JSON.stringify(meta3, null, 2));
|
|
15901
15973
|
pruneSkillOutputs();
|
|
15902
15974
|
return meta3;
|
|
15903
15975
|
} catch {
|
|
@@ -15917,7 +15989,7 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
|
|
|
15917
15989
|
text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
|
|
15918
15990
|
${text}`;
|
|
15919
15991
|
}
|
|
15920
|
-
await atomicWriteText(
|
|
15992
|
+
await atomicWriteText(resolve8(dir, fileId), text);
|
|
15921
15993
|
} catch {
|
|
15922
15994
|
}
|
|
15923
15995
|
}
|
|
@@ -15937,7 +16009,7 @@ function getCompactAnySessionSync(skillName) {
|
|
|
15937
16009
|
for (const entry of entries) {
|
|
15938
16010
|
if (!matchesCompactSuffix(entry.name, entry.isFile(), suffix)) continue;
|
|
15939
16011
|
try {
|
|
15940
|
-
const text = readFileSync13(
|
|
16012
|
+
const text = readFileSync13(resolve8(dir, entry.name), "utf-8");
|
|
15941
16013
|
if (text.trim()) return text;
|
|
15942
16014
|
} catch {
|
|
15943
16015
|
continue;
|
|
@@ -15965,7 +16037,7 @@ async function readSkillHits(skillName) {
|
|
|
15965
16037
|
const name2 = safeSkillName(skillName);
|
|
15966
16038
|
if (!name2) return { count: 0, lastTs: 0 };
|
|
15967
16039
|
const dir = skillOutputsDir();
|
|
15968
|
-
const hitsFile =
|
|
16040
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
15969
16041
|
const content = await fs18.readFile(hitsFile, "utf-8").catch(() => null);
|
|
15970
16042
|
if (content) {
|
|
15971
16043
|
const parsed = JSON.parse(content);
|
|
@@ -16015,7 +16087,7 @@ async function incrementSkillHit(skillName) {
|
|
|
16015
16087
|
if (!name2) return;
|
|
16016
16088
|
await ensureSkillsDir();
|
|
16017
16089
|
const dir = skillOutputsDir();
|
|
16018
|
-
const hitsFile =
|
|
16090
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
16019
16091
|
const lockPath = `${hitsFile}.lock`;
|
|
16020
16092
|
await runExclusiveInProcess(lockPath, async () => {
|
|
16021
16093
|
const locked = await acquireSkillHitLock(lockPath);
|
|
@@ -16062,14 +16134,14 @@ async function listSkills(sessionId) {
|
|
|
16062
16134
|
let compactLen = 0;
|
|
16063
16135
|
let compactText = "";
|
|
16064
16136
|
try {
|
|
16065
|
-
const stat2 = await fs18.stat(
|
|
16137
|
+
const stat2 = await fs18.stat(resolve8(dir, compactFileId));
|
|
16066
16138
|
compactLen = stat2.size;
|
|
16067
|
-
compactText = await fs18.readFile(
|
|
16139
|
+
compactText = await fs18.readFile(resolve8(dir, compactFileId), "utf-8").catch(() => "");
|
|
16068
16140
|
} catch {
|
|
16069
16141
|
compactLen = 0;
|
|
16070
16142
|
}
|
|
16071
16143
|
const hasMarker = extractCompactFromMarker(
|
|
16072
|
-
await fs18.readFile(
|
|
16144
|
+
await fs18.readFile(resolve8(dir, `${meta3.outputId}.txt`), "utf-8").catch(() => "")
|
|
16073
16145
|
) !== null;
|
|
16074
16146
|
const compactStale = isCompactStale(compactText, meta3.skillName, meta3.contentSha);
|
|
16075
16147
|
const { count: hitCount } = await readSkillHits(meta3.skillName);
|
|
@@ -16105,7 +16177,7 @@ async function getSkillFilePath(skillName) {
|
|
|
16105
16177
|
}
|
|
16106
16178
|
}
|
|
16107
16179
|
async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
16108
|
-
const manifestPath = _pluginsManifestPathOverride ??
|
|
16180
|
+
const manifestPath = _pluginsManifestPathOverride ?? resolve8(homedir7(), ".claude", "plugins", "installed_plugins.json");
|
|
16109
16181
|
let raw;
|
|
16110
16182
|
try {
|
|
16111
16183
|
raw = await fs18.readFile(manifestPath, "utf8");
|
|
@@ -16128,7 +16200,7 @@ async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
|
16128
16200
|
if (typeof entry !== "object" || entry === null) continue;
|
|
16129
16201
|
const installPath = entry["installPath"];
|
|
16130
16202
|
if (typeof installPath !== "string" || installPath === "") continue;
|
|
16131
|
-
const diskPath =
|
|
16203
|
+
const diskPath = resolve8(installPath, "skills", skillSlug, "SKILL.md");
|
|
16132
16204
|
try {
|
|
16133
16205
|
await fs18.access(diskPath);
|
|
16134
16206
|
return diskPath;
|
|
@@ -16147,7 +16219,7 @@ async function installedSkillPath(skillName) {
|
|
|
16147
16219
|
const pluginPath = await resolvePluginSkillPath(name2.slice(0, colonIdx), name2.slice(colonIdx + 1));
|
|
16148
16220
|
if (pluginPath !== null) return pluginPath;
|
|
16149
16221
|
}
|
|
16150
|
-
const diskPath =
|
|
16222
|
+
const diskPath = resolve8(skillsSourceDir(), name2, "SKILL.md");
|
|
16151
16223
|
try {
|
|
16152
16224
|
await fs18.access(diskPath);
|
|
16153
16225
|
return diskPath;
|
|
@@ -16167,11 +16239,11 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
16167
16239
|
const outputId = file2.slice(0, -".meta".length);
|
|
16168
16240
|
let ts;
|
|
16169
16241
|
try {
|
|
16170
|
-
const parsed = JSON.parse(readFileSync13(
|
|
16242
|
+
const parsed = JSON.parse(readFileSync13(resolve8(dir, file2), "utf-8"));
|
|
16171
16243
|
ts = parsed.ts;
|
|
16172
16244
|
} catch {
|
|
16173
16245
|
try {
|
|
16174
|
-
ts = statSync8(
|
|
16246
|
+
ts = statSync8(resolve8(dir, file2)).mtimeMs;
|
|
16175
16247
|
} catch {
|
|
16176
16248
|
continue;
|
|
16177
16249
|
}
|
|
@@ -16181,7 +16253,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
16181
16253
|
const removeEntry = (outputId) => {
|
|
16182
16254
|
for (const ext2 of [".meta", ".txt"]) {
|
|
16183
16255
|
try {
|
|
16184
|
-
unlinkSync9(
|
|
16256
|
+
unlinkSync9(resolve8(dir, `${outputId}${ext2}`));
|
|
16185
16257
|
} catch {
|
|
16186
16258
|
}
|
|
16187
16259
|
}
|
|
@@ -16273,7 +16345,7 @@ async function ocrImage(input) {
|
|
|
16273
16345
|
if (_ocrUnavailableThisProcess) return null;
|
|
16274
16346
|
const entryPath = resolveTesseractEntry();
|
|
16275
16347
|
if (entryPath === null) return null;
|
|
16276
|
-
return new Promise((
|
|
16348
|
+
return new Promise((resolve25) => {
|
|
16277
16349
|
let settled = false;
|
|
16278
16350
|
let child;
|
|
16279
16351
|
try {
|
|
@@ -16282,7 +16354,7 @@ async function ocrImage(input) {
|
|
|
16282
16354
|
});
|
|
16283
16355
|
} catch {
|
|
16284
16356
|
_ocrUnavailableThisProcess = true;
|
|
16285
|
-
|
|
16357
|
+
resolve25(null);
|
|
16286
16358
|
return;
|
|
16287
16359
|
}
|
|
16288
16360
|
const chunks = [];
|
|
@@ -16295,7 +16367,7 @@ async function ocrImage(input) {
|
|
|
16295
16367
|
child.kill();
|
|
16296
16368
|
} catch {
|
|
16297
16369
|
}
|
|
16298
|
-
|
|
16370
|
+
resolve25(result);
|
|
16299
16371
|
};
|
|
16300
16372
|
const timer = setTimeout(() => finish(null, true), _ocrTimeoutMs);
|
|
16301
16373
|
child.stdout?.on("data", (c) => chunks.push(c));
|
|
@@ -17537,7 +17609,7 @@ import * as readline from "node:readline";
|
|
|
17537
17609
|
async function defaultConfirm(question) {
|
|
17538
17610
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
17539
17611
|
try {
|
|
17540
|
-
const answer = await new Promise((
|
|
17612
|
+
const answer = await new Promise((resolve25) => rl.question(question, resolve25));
|
|
17541
17613
|
return /^y(es)?$/i.test(answer.trim());
|
|
17542
17614
|
} finally {
|
|
17543
17615
|
rl.close();
|
|
@@ -17603,7 +17675,7 @@ function ensureTransformerLoaded() {
|
|
|
17603
17675
|
}
|
|
17604
17676
|
}
|
|
17605
17677
|
function sleep(ms) {
|
|
17606
|
-
return new Promise((
|
|
17678
|
+
return new Promise((resolve25) => setTimeout(resolve25, ms));
|
|
17607
17679
|
}
|
|
17608
17680
|
async function buildExtractorWithRetry(pipelineFn, modelName) {
|
|
17609
17681
|
let lastError;
|
|
@@ -17943,7 +18015,8 @@ function rerankHits(hits, query, topK) {
|
|
|
17943
18015
|
boost = Math.min(matches2 * _VERBATIM_TOKEN_BOOST, _MAX_VERBATIM_BOOST);
|
|
17944
18016
|
}
|
|
17945
18017
|
const penalty = _isGeneratedPath(hit.filePath) ? _GENERATED_PATH_PENALTY : 0;
|
|
17946
|
-
|
|
18018
|
+
const pathPenalty = _pathPriorityPenalty(hit.filePath);
|
|
18019
|
+
return { hit, index, adjusted: hit.distance - boost + penalty + pathPenalty };
|
|
17947
18020
|
});
|
|
17948
18021
|
scored.sort((a, b) => a.adjusted - b.adjusted || a.index - b.index);
|
|
17949
18022
|
return scored.slice(0, topK).map((entry) => ({ ...entry.hit, adjustedDistance: entry.adjusted }));
|
|
@@ -18069,11 +18142,26 @@ function _isGeneratedPath(filePath) {
|
|
|
18069
18142
|
}
|
|
18070
18143
|
return false;
|
|
18071
18144
|
}
|
|
18072
|
-
|
|
18145
|
+
function _pathPriorityPenalty(filePath) {
|
|
18146
|
+
const segments = filePath.split(/[/\\]+/);
|
|
18147
|
+
const basename22 = segments[segments.length - 1] ?? filePath;
|
|
18148
|
+
const weights = loadConfig().semantic;
|
|
18149
|
+
const isArchive = _ARCHIVE_FILE_RE.test(basename22) || segments.some((seg) => _ARCHIVE_PATH_SEGMENTS.has(seg.toLowerCase()));
|
|
18150
|
+
if (isArchive) {
|
|
18151
|
+
return 1 - weights.archive_weight;
|
|
18152
|
+
}
|
|
18153
|
+
const isDocs = _DOCS_FILE_RE.test(basename22) || segments.some((seg) => seg.toLowerCase() === _DOCS_DIR_SEGMENT);
|
|
18154
|
+
if (isDocs) {
|
|
18155
|
+
return 1 - weights.docs_weight;
|
|
18156
|
+
}
|
|
18157
|
+
return 0;
|
|
18158
|
+
}
|
|
18159
|
+
var _require3, _transformer, _transformerError, _transformerLoadAttempted, DEFAULT_MODEL, DEFAULT_DIM, QUERY_INSTRUCTION_PREFIX, _extractorCache, _pipelineFnOverride, PIPELINE_RETRY_ATTEMPTS, PIPELINE_RETRY_DELAY_MS, DEFAULT_PIPELINE_RETRY_DELAY_MS, MIN_CHUNK_CHARS, MAX_CHUNK_CHARS, DEFAULT_DISTANCE_THRESHOLD, _GENERATED_PATH_SEGMENTS, _GENERATED_PATH_PENALTY, _ARCHIVE_PATH_SEGMENTS, _ARCHIVE_FILE_RE, _DOCS_FILE_RE, _DOCS_DIR_SEGMENT, _VERBATIM_TOKEN_BOOST, _MAX_VERBATIM_BOOST, _TOKEN_RE, _MIN_TOKEN_LEN, OVER_FETCH_FACTOR, MAX_OVER_FETCH, BACKFILL_MULTIPLIER, _chunkVectorsUsable;
|
|
18073
18160
|
var init_embeddings = __esm({
|
|
18074
18161
|
"src/embeddings.ts"() {
|
|
18075
18162
|
"use strict";
|
|
18076
18163
|
init_define_import_meta_env();
|
|
18164
|
+
init_config();
|
|
18077
18165
|
init_sql_path();
|
|
18078
18166
|
init_util2();
|
|
18079
18167
|
init_reset();
|
|
@@ -18122,6 +18210,17 @@ var init_embeddings = __esm({
|
|
|
18122
18210
|
".ruff_cache"
|
|
18123
18211
|
]);
|
|
18124
18212
|
_GENERATED_PATH_PENALTY = 0.5;
|
|
18213
|
+
_ARCHIVE_PATH_SEGMENTS = /* @__PURE__ */ new Set([
|
|
18214
|
+
"archive",
|
|
18215
|
+
"archived",
|
|
18216
|
+
"old",
|
|
18217
|
+
"deprecated",
|
|
18218
|
+
"plans",
|
|
18219
|
+
"drafts"
|
|
18220
|
+
]);
|
|
18221
|
+
_ARCHIVE_FILE_RE = /(^changelog|\.bak$|\.orig$)/i;
|
|
18222
|
+
_DOCS_FILE_RE = /\.md$/i;
|
|
18223
|
+
_DOCS_DIR_SEGMENT = "docs";
|
|
18125
18224
|
_VERBATIM_TOKEN_BOOST = 0.05;
|
|
18126
18225
|
_MAX_VERBATIM_BOOST = 0.25;
|
|
18127
18226
|
_TOKEN_RE = /\w+/g;
|
|
@@ -18664,14 +18763,32 @@ function fetchTopSymbols(limit, dbPath, rootDir) {
|
|
|
18664
18763
|
try {
|
|
18665
18764
|
const db = getDb(dbPath);
|
|
18666
18765
|
const { clause, param } = projectScopeClause("file_path");
|
|
18766
|
+
const refScope = projectScopeClause("file_path");
|
|
18667
18767
|
const rows = db.prepare(
|
|
18768
|
+
// refs carry only a bare name, so a name defined N times cannot claim all N copies' references: divide by the number of same-named definitions, and keep one representative per name so a generic helper like `apply` occupies one slot instead of seven.
|
|
18668
18769
|
`SELECT file_path, name, kind, line_start, line_end, body, docstring, parent
|
|
18669
|
-
FROM
|
|
18670
|
-
|
|
18671
|
-
|
|
18770
|
+
FROM (
|
|
18771
|
+
SELECT s.file_path, s.name, s.kind, s.line_start, s.line_end, s.body, s.docstring, s.parent,
|
|
18772
|
+
COALESCE(r.ref_count, 0) * 1.0 / COUNT(*) OVER (PARTITION BY s.name) AS score,
|
|
18773
|
+
ROW_NUMBER() OVER (
|
|
18774
|
+
PARTITION BY s.name
|
|
18775
|
+
ORDER BY LENGTH(COALESCE(s.body, '')) DESC, s.file_path
|
|
18776
|
+
) AS rn
|
|
18777
|
+
FROM symbols s
|
|
18778
|
+
LEFT JOIN (
|
|
18779
|
+
SELECT name, COUNT(*) AS ref_count
|
|
18780
|
+
FROM refs
|
|
18781
|
+
WHERE ${refScope.clause}
|
|
18782
|
+
GROUP BY name
|
|
18783
|
+
) r ON r.name = s.name
|
|
18784
|
+
WHERE s.kind IN ('class', 'function', 'interface') AND ${clause}
|
|
18785
|
+
)
|
|
18786
|
+
WHERE rn = 1
|
|
18787
|
+
ORDER BY score DESC,
|
|
18788
|
+
CASE kind WHEN 'class' THEN 0 WHEN 'interface' THEN 1 ELSE 2 END,
|
|
18672
18789
|
LENGTH(COALESCE(body, '')) DESC
|
|
18673
18790
|
LIMIT ?`
|
|
18674
|
-
).all(param(rootDir), limit);
|
|
18791
|
+
).all(refScope.param(rootDir), param(rootDir), limit);
|
|
18675
18792
|
return rows.map((r) => ({
|
|
18676
18793
|
filePath: r.file_path,
|
|
18677
18794
|
name: r.name,
|
|
@@ -18723,13 +18840,12 @@ function formatProjectMap(map3, compact = false) {
|
|
|
18723
18840
|
lines2.push("");
|
|
18724
18841
|
lines2.push("## Top symbols");
|
|
18725
18842
|
for (const s of map3.topSymbols) {
|
|
18726
|
-
|
|
18727
|
-
|
|
18728
|
-
} else {
|
|
18729
|
-
const loc = `${path25.basename(s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
18730
|
-
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
18731
|
-
}
|
|
18843
|
+
const loc = `${toDisplayPath(map3.rootDir, s.filePath)}:${s.lineStart}-${s.lineEnd}`;
|
|
18844
|
+
lines2.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
|
|
18732
18845
|
}
|
|
18846
|
+
} else {
|
|
18847
|
+
lines2.push("");
|
|
18848
|
+
lines2.push("## Top symbols: none \u2014 no files indexed for this project; run 'token-goat index .'");
|
|
18733
18849
|
}
|
|
18734
18850
|
if (!compact && map3.recentFiles.length > 0) {
|
|
18735
18851
|
lines2.push("");
|
|
@@ -24655,10 +24771,10 @@ function embedFileSerialized(absPath, dbPath, sha) {
|
|
|
24655
24771
|
activeEmbedSlots += 1;
|
|
24656
24772
|
return dispatchEmbed();
|
|
24657
24773
|
}
|
|
24658
|
-
return new Promise((
|
|
24774
|
+
return new Promise((resolve25) => {
|
|
24659
24775
|
embedSlotWaiters.push(() => {
|
|
24660
24776
|
activeEmbedSlots += 1;
|
|
24661
|
-
|
|
24777
|
+
resolve25(dispatchEmbed());
|
|
24662
24778
|
});
|
|
24663
24779
|
});
|
|
24664
24780
|
};
|
|
@@ -25064,7 +25180,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
|
|
|
25064
25180
|
lastKnownRootsSweepMs = Date.now();
|
|
25065
25181
|
}
|
|
25066
25182
|
if (shouldStop()) break;
|
|
25067
|
-
await new Promise((
|
|
25183
|
+
await new Promise((resolve25) => setTimeout(resolve25, pollIntervalMs));
|
|
25068
25184
|
}
|
|
25069
25185
|
}
|
|
25070
25186
|
function runDetachedWorkerDaemon() {
|
|
@@ -25750,6 +25866,11 @@ function isDeadSymbol(name2, refCount) {
|
|
|
25750
25866
|
if (ENTRY_NAMES.has(name2)) return false;
|
|
25751
25867
|
return refCount === 0;
|
|
25752
25868
|
}
|
|
25869
|
+
function parseGraphSymbolSpec(spec) {
|
|
25870
|
+
const colonIdx = findSpecSeparator(spec);
|
|
25871
|
+
if (colonIdx === -1) return { name: spec };
|
|
25872
|
+
return { name: spec.slice(colonIdx + 2), file: spec.slice(0, colonIdx) };
|
|
25873
|
+
}
|
|
25753
25874
|
function buildFileSymCache() {
|
|
25754
25875
|
const cache = /* @__PURE__ */ new Map();
|
|
25755
25876
|
return (fp) => {
|
|
@@ -25767,9 +25888,10 @@ function fileDefinesName(fp, name2, getSyms) {
|
|
|
25767
25888
|
function filterRefsForSymbol(refs, name2, filePath, getSyms) {
|
|
25768
25889
|
return refs.filter((ref2) => ref2.filePath === filePath || !fileDefinesName(ref2.filePath, name2, getSyms));
|
|
25769
25890
|
}
|
|
25770
|
-
function resolveCallers(name2, limit, filePath, rootDir) {
|
|
25891
|
+
function resolveCallers(name2, limit, filePath, rootDir, excludeTests) {
|
|
25771
25892
|
const resolvedRootDir = rootDir ?? resolveProjectRoot({ project: process.cwd() });
|
|
25772
|
-
const
|
|
25893
|
+
const queryLimit = excludeTests === true ? UNBOUNDED_REF_LIMIT : limit ?? 500;
|
|
25894
|
+
const refs = queryRefs({ name: name2, limit: queryLimit, rootDir: resolvedRootDir });
|
|
25773
25895
|
const getSyms = buildFileSymCache();
|
|
25774
25896
|
const scoped = filePath === void 0 ? refs : filterRefsForSymbol(refs, name2, filePath, getSyms);
|
|
25775
25897
|
return scoped.map((ref2) => {
|
|
@@ -25788,17 +25910,37 @@ function runCallers(opts) {
|
|
|
25788
25910
|
return 1;
|
|
25789
25911
|
}
|
|
25790
25912
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25791
|
-
const
|
|
25913
|
+
const { name: name2, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25914
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25915
|
+
if (fileHint !== void 0 && querySymbols({ name: name2, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25916
|
+
emitErr(`Symbol '${name2}' not found in '${file2}'`);
|
|
25917
|
+
return 1;
|
|
25918
|
+
}
|
|
25919
|
+
const resolved = resolveCallers(name2, opts.limit, fileHint, rootDir, opts.excludeTests);
|
|
25920
|
+
const suppressed = opts.excludeTests === true ? resolved.filter((e) => isTestFile(e.file)).length : 0;
|
|
25921
|
+
const entries = opts.excludeTests === true ? resolved.filter((e) => !isTestFile(e.file)).slice(0, opts.limit ?? 500) : resolved;
|
|
25792
25922
|
if (entries.length === 0) {
|
|
25923
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25924
|
+
emitErr(`No non-test references found for '${opts.symbol}' (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25925
|
+
return 1;
|
|
25926
|
+
}
|
|
25793
25927
|
emitErr(`No references found for '${opts.symbol}'`);
|
|
25794
25928
|
return 1;
|
|
25795
25929
|
}
|
|
25930
|
+
const contextLines = opts.context ?? 0;
|
|
25796
25931
|
if (opts.json === true) {
|
|
25797
|
-
|
|
25932
|
+
const payload = contextLines > 0 ? entries.map((e) => ({ ...e, contextLines: buildContextWindow(e.file, e.line, contextLines) ?? [] })) : entries;
|
|
25933
|
+
emit2(JSON.stringify(payload, null, 2));
|
|
25798
25934
|
return 0;
|
|
25799
25935
|
}
|
|
25936
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
25937
|
+
emit2(`${entries.length} callers found (${suppressed} in test files hidden by --exclude-tests)`);
|
|
25938
|
+
}
|
|
25800
25939
|
for (const e of entries) {
|
|
25801
|
-
|
|
25940
|
+
const displayPath = toDisplayPath(rootDir, e.file);
|
|
25941
|
+
emit2(`${e.caller} ${displayPath}:${e.line}`);
|
|
25942
|
+
const window = buildContextWindow(e.file, e.line, contextLines);
|
|
25943
|
+
if (window !== null) for (const l of renderContextWindow(displayPath, e.line, window, "", " ")) emit2(l);
|
|
25802
25944
|
}
|
|
25803
25945
|
return 0;
|
|
25804
25946
|
}
|
|
@@ -25809,28 +25951,36 @@ function runCallChain(opts) {
|
|
|
25809
25951
|
}
|
|
25810
25952
|
const maxDepth = opts.depth ?? 8;
|
|
25811
25953
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
25812
|
-
|
|
25954
|
+
const { name: name2, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
25955
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
25956
|
+
if (fileHint !== void 0) {
|
|
25957
|
+
if (querySymbols({ name: name2, filePath: fileHint, limit: 1 }).length === 0) {
|
|
25958
|
+
emitErr(`Symbol '${name2}' not found in '${file2}'`);
|
|
25959
|
+
return 1;
|
|
25960
|
+
}
|
|
25961
|
+
} else if (querySymbols({ name: name2, rootDir, limit: 1 }).length === 0) {
|
|
25813
25962
|
emitErr(`Symbol not found: ${opts.symbol}`);
|
|
25814
25963
|
return 1;
|
|
25815
25964
|
}
|
|
25816
25965
|
const getSyms = buildFileSymCache();
|
|
25817
|
-
const callersOf = (
|
|
25818
|
-
const refs = queryRefs({ name:
|
|
25966
|
+
const callersOf = (n) => {
|
|
25967
|
+
const refs = queryRefs({ name: n, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25819
25968
|
if (refs.length === 0) return [];
|
|
25969
|
+
const scoped = fileHint !== void 0 && n === name2 ? filterRefsForSymbol(refs, n, fileHint, getSyms) : refs;
|
|
25820
25970
|
const names = /* @__PURE__ */ new Set();
|
|
25821
|
-
for (const ref2 of
|
|
25971
|
+
for (const ref2 of scoped) {
|
|
25822
25972
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25823
25973
|
if (enc !== null) names.add(enc.name);
|
|
25824
25974
|
}
|
|
25825
25975
|
return [...names];
|
|
25826
25976
|
};
|
|
25827
|
-
const chains = bfsCallChains(
|
|
25977
|
+
const chains = bfsCallChains(name2, callersOf, maxDepth);
|
|
25828
25978
|
if (opts.json === true) {
|
|
25829
25979
|
emit2(JSON.stringify({ chains }, null, 2));
|
|
25830
25980
|
return 0;
|
|
25831
25981
|
}
|
|
25832
|
-
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] ===
|
|
25833
|
-
emit2(`${
|
|
25982
|
+
if (chains.length === 1 && chains[0]?.length === 1 && chains[0][0] === name2) {
|
|
25983
|
+
emit2(`${name2} (no callers)`);
|
|
25834
25984
|
return 0;
|
|
25835
25985
|
}
|
|
25836
25986
|
for (const chain2 of chains) {
|
|
@@ -25850,16 +26000,23 @@ function runImpact(opts) {
|
|
|
25850
26000
|
const top = opts.top ?? 20;
|
|
25851
26001
|
const DEPTH_CAP = 8;
|
|
25852
26002
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
26003
|
+
const { name: rootName, file: file2 } = parseGraphSymbolSpec(opts.symbol);
|
|
26004
|
+
const fileHint = file2 !== void 0 ? resolveIndexPath(file2, rootDir) : void 0;
|
|
26005
|
+
if (fileHint !== void 0 && querySymbols({ name: rootName, filePath: fileHint, limit: 1 }).length === 0) {
|
|
26006
|
+
emitErr(`Symbol '${rootName}' not found in '${file2}'`);
|
|
26007
|
+
return 1;
|
|
26008
|
+
}
|
|
25853
26009
|
const getSyms = buildFileSymCache();
|
|
25854
|
-
const hops = /* @__PURE__ */ new Map([[
|
|
25855
|
-
const queue = [[
|
|
26010
|
+
const hops = /* @__PURE__ */ new Map([[rootName, 0]]);
|
|
26011
|
+
const queue = [[rootName, 0]];
|
|
25856
26012
|
while (queue.length > 0) {
|
|
25857
26013
|
const item = queue.shift();
|
|
25858
26014
|
if (item === void 0) break;
|
|
25859
26015
|
const [name2, depth] = item;
|
|
25860
26016
|
if (depth >= DEPTH_CAP) continue;
|
|
25861
26017
|
const refs = queryRefs({ name: name2, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
25862
|
-
|
|
26018
|
+
const scoped = fileHint !== void 0 && name2 === rootName ? filterRefsForSymbol(refs, name2, fileHint, getSyms) : refs;
|
|
26019
|
+
for (const ref2 of scoped) {
|
|
25863
26020
|
const newHop = depth + 1;
|
|
25864
26021
|
const enc = enclosingSymbol(getSyms(ref2.filePath), ref2.line);
|
|
25865
26022
|
if (enc === null) {
|
|
@@ -25876,7 +26033,7 @@ function runImpact(opts) {
|
|
|
25876
26033
|
}
|
|
25877
26034
|
}
|
|
25878
26035
|
}
|
|
25879
|
-
hops.delete(
|
|
26036
|
+
hops.delete(rootName);
|
|
25880
26037
|
const sorted = [...hops.entries()].sort(compareHopEntries).slice(0, top);
|
|
25881
26038
|
if (sorted.length === 0) {
|
|
25882
26039
|
emitErr(`No callers found for '${opts.symbol}'`);
|
|
@@ -25928,6 +26085,7 @@ function runDead(opts) {
|
|
|
25928
26085
|
const syms = querySymbols({ kind, limit: 5e3, rootDir });
|
|
25929
26086
|
const getSyms = buildFileSymCache();
|
|
25930
26087
|
const results = [];
|
|
26088
|
+
let suppressed = 0;
|
|
25931
26089
|
for (const sym of syms) {
|
|
25932
26090
|
if (opts.includePrivate !== true && sym.name.startsWith("_")) continue;
|
|
25933
26091
|
const refs = queryRefs({ name: sym.name, limit: DEFAULT_REF_QUERY_LIMIT, rootDir });
|
|
@@ -25937,6 +26095,10 @@ function runDead(opts) {
|
|
|
25937
26095
|
const ownScope = enclosingNamedScope(getSyms(sym.filePath), sym.lineStart);
|
|
25938
26096
|
if (ownScope !== null && hasAncestorDispatchRef(sym.name, ownScope.name, sym.filePath, rootDir)) continue;
|
|
25939
26097
|
}
|
|
26098
|
+
if (opts.excludeTests === true && isTestFile(sym.filePath)) {
|
|
26099
|
+
suppressed += 1;
|
|
26100
|
+
continue;
|
|
26101
|
+
}
|
|
25940
26102
|
results.push({ name: sym.name, kind: sym.kind, file: sym.filePath, line: sym.lineStart });
|
|
25941
26103
|
}
|
|
25942
26104
|
const sliced = results.slice(0, opts.top ?? results.length);
|
|
@@ -25945,9 +26107,16 @@ function runDead(opts) {
|
|
|
25945
26107
|
return 0;
|
|
25946
26108
|
}
|
|
25947
26109
|
if (sliced.length === 0) {
|
|
25948
|
-
|
|
26110
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
26111
|
+
emit2(`No dead symbols found (${suppressed} in test files hidden by --exclude-tests).`);
|
|
26112
|
+
} else {
|
|
26113
|
+
emit2("No dead symbols found.");
|
|
26114
|
+
}
|
|
25949
26115
|
return 0;
|
|
25950
26116
|
}
|
|
26117
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
26118
|
+
emit2(`${sliced.length} dead symbols (${suppressed} in test files hidden by --exclude-tests)`);
|
|
26119
|
+
}
|
|
25951
26120
|
for (const r of sliced) {
|
|
25952
26121
|
emit2(`${r.name} ${toDisplayPath(rootDir, r.file)}:${r.line}`);
|
|
25953
26122
|
}
|
|
@@ -26197,16 +26366,9 @@ function runSimilar(opts) {
|
|
|
26197
26366
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
26198
26367
|
return 1;
|
|
26199
26368
|
}
|
|
26200
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
26201
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
26202
26369
|
const top = opts.top ?? 10;
|
|
26203
|
-
const
|
|
26204
|
-
|
|
26205
|
-
if (anchors.length === 0) {
|
|
26206
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
26207
|
-
return 1;
|
|
26208
|
-
}
|
|
26209
|
-
const anchor = anchors[0];
|
|
26370
|
+
const anchor = resolveSymbolSpecOrEmitError("similar", opts.spec, void 0);
|
|
26371
|
+
if (anchor === null) return 1;
|
|
26210
26372
|
const words = [anchor.name, ...(anchor.docstring ?? "").split(/\s+/).filter((w) => w.length > 4)];
|
|
26211
26373
|
const query = words.slice(0, 8).join(" ");
|
|
26212
26374
|
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
@@ -26238,13 +26400,13 @@ function runContextFor(opts) {
|
|
|
26238
26400
|
const bodyTokens = estimateTokens(h.body ?? "");
|
|
26239
26401
|
if (budget !== void 0 && tokensSoFar + bodyTokens > budget) continue;
|
|
26240
26402
|
tokensSoFar += bodyTokens;
|
|
26241
|
-
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, readCmd: `token-goat read "${h.filePath}::${h.name}"` });
|
|
26403
|
+
entries.push({ file: h.filePath, symbol: h.name, kind: h.kind, line: h.lineStart, readCmd: `token-goat read "${h.filePath}::${h.name}@${h.lineStart}"` });
|
|
26242
26404
|
}
|
|
26243
26405
|
if (opts.json === true) {
|
|
26244
26406
|
emit2(JSON.stringify(entries, null, 2));
|
|
26245
26407
|
return 0;
|
|
26246
26408
|
}
|
|
26247
|
-
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}"`);
|
|
26409
|
+
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
26248
26410
|
return 0;
|
|
26249
26411
|
}
|
|
26250
26412
|
function runTestFor(opts) {
|
|
@@ -26389,16 +26551,10 @@ function runBlame(opts) {
|
|
|
26389
26551
|
emitErr(`Invalid spec - expected "file::symbol", got: ${opts.spec}`);
|
|
26390
26552
|
return 1;
|
|
26391
26553
|
}
|
|
26392
|
-
const fileArg = opts.spec.slice(0, sepIdx);
|
|
26393
|
-
const symbolArg = opts.spec.slice(sepIdx + 2);
|
|
26394
26554
|
const cwd = opts.cwd ?? process.cwd();
|
|
26395
|
-
const
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
emitErr(`Symbol '${symbolArg}' not found in '${fileArg}'`);
|
|
26399
|
-
return 1;
|
|
26400
|
-
}
|
|
26401
|
-
const sym = syms[0];
|
|
26555
|
+
const sym = resolveSymbolSpecOrEmitError("blame", opts.spec, void 0);
|
|
26556
|
+
if (sym === null) return 1;
|
|
26557
|
+
const filePath = sym.filePath;
|
|
26402
26558
|
const start = sym.lineStart;
|
|
26403
26559
|
const end = sym.lineEnd;
|
|
26404
26560
|
let raw;
|
|
@@ -26419,10 +26575,10 @@ function runBlame(opts) {
|
|
|
26419
26575
|
if (!m) return { raw: l };
|
|
26420
26576
|
return { commit: m[1], author: (m[2] ?? "").trim(), date: (m[3] ?? "").trim(), line: Number.parseInt(m[4] ?? "0", 10), content: m[5] };
|
|
26421
26577
|
});
|
|
26422
|
-
emit2(JSON.stringify({ symbol:
|
|
26578
|
+
emit2(JSON.stringify({ symbol: sym.name, file: filePath, lines: lines2 }, null, 2));
|
|
26423
26579
|
return 0;
|
|
26424
26580
|
}
|
|
26425
|
-
emit2(`${
|
|
26581
|
+
emit2(`${sym.name} ${toDisplayPath(getDisplayRoot(opts.cwd), filePath)}:${start}-${end}`);
|
|
26426
26582
|
emit2(raw.trim());
|
|
26427
26583
|
return 0;
|
|
26428
26584
|
}
|
|
@@ -26436,14 +26592,14 @@ function runAsk(opts) {
|
|
|
26436
26592
|
const hits = searchSymbolsFts(opts.question, top, void 0, rootDir);
|
|
26437
26593
|
const BACKEND_ENV = "TOKEN_GOAT_ASK_BACKEND";
|
|
26438
26594
|
const backendLabel = process.env[BACKEND_ENV] ?? "";
|
|
26439
|
-
const entries = hits.map((h) => ({ file: h.filePath, symbol: h.name, kind: h.kind, readCmd: `token-goat read "${h.filePath}::${h.name}"` }));
|
|
26595
|
+
const entries = hits.map((h) => ({ file: h.filePath, symbol: h.name, kind: h.kind, line: h.lineStart, readCmd: `token-goat read "${h.filePath}::${h.name}@${h.lineStart}"` }));
|
|
26440
26596
|
const degrade = () => {
|
|
26441
26597
|
if (opts.json === true) {
|
|
26442
26598
|
emit2(JSON.stringify({ degraded: true, note: `Set ${BACKEND_ENV}=claude|codex for LLM synthesis`, context: entries }, null, 2));
|
|
26443
26599
|
return 0;
|
|
26444
26600
|
}
|
|
26445
26601
|
emit2(`[degraded mode - set ${BACKEND_ENV}=claude|codex for LLM synthesis]`);
|
|
26446
|
-
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}"`);
|
|
26602
|
+
for (const e of entries) emit2(`token-goat read "${toDisplayPath(rootDir, e.file)}::${e.symbol}@${e.line}"`);
|
|
26447
26603
|
return 0;
|
|
26448
26604
|
};
|
|
26449
26605
|
if (!backendLabel) return degrade();
|
|
@@ -30426,6 +30582,42 @@ function formatBareNameSpecError(command, name2, projectRoot) {
|
|
|
30426
30582
|
}
|
|
30427
30583
|
return lines2.join("\n");
|
|
30428
30584
|
}
|
|
30585
|
+
function formatCrossFileLead(command, name2, excludeFilePath, projectRoot) {
|
|
30586
|
+
const rootDir = projectRoot ?? process.cwd();
|
|
30587
|
+
const matches2 = querySymbols({ name: name2, limit: 50, rootDir });
|
|
30588
|
+
const excludeResolved = resolveIndexPath(excludeFilePath, rootDir);
|
|
30589
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30590
|
+
const specs = [];
|
|
30591
|
+
for (const m of matches2) {
|
|
30592
|
+
if (foldPath(m.filePath) === foldPath(excludeResolved)) continue;
|
|
30593
|
+
const spec = `${toDisplayPath(rootDir, m.filePath)}::${m.name}`;
|
|
30594
|
+
if (seen.has(spec)) continue;
|
|
30595
|
+
seen.add(spec);
|
|
30596
|
+
specs.push(spec);
|
|
30597
|
+
}
|
|
30598
|
+
if (specs.length === 0) return "";
|
|
30599
|
+
const firstSpec = specs[0];
|
|
30600
|
+
const lines2 = [`'${name2}' is defined in ${firstSpec !== void 0 ? firstSpec.split("::")[0] : ""}`];
|
|
30601
|
+
for (const spec of specs.slice(0, DIDYOUMEAN_LIMIT)) {
|
|
30602
|
+
lines2.push(` - token-goat ${command} "${spec}"`);
|
|
30603
|
+
}
|
|
30604
|
+
if (specs.length > DIDYOUMEAN_LIMIT) {
|
|
30605
|
+
lines2.push(` (${specs.length - DIDYOUMEAN_LIMIT} more not shown)`);
|
|
30606
|
+
}
|
|
30607
|
+
return lines2.join("\n");
|
|
30608
|
+
}
|
|
30609
|
+
function resolveEnclosingSymbol(filePath, chunkStartLine) {
|
|
30610
|
+
const symbols = querySymbols({ filePath, limit: 1e5 }, globalDbPath());
|
|
30611
|
+
let best = null;
|
|
30612
|
+
for (const s of symbols) {
|
|
30613
|
+
if (s.lineStart <= chunkStartLine && chunkStartLine <= s.lineEnd) {
|
|
30614
|
+
if (best === null || s.lineEnd - s.lineStart < best.lineEnd - best.lineStart) {
|
|
30615
|
+
best = s;
|
|
30616
|
+
}
|
|
30617
|
+
}
|
|
30618
|
+
}
|
|
30619
|
+
return best === null ? null : { name: best.name, kind: best.kind };
|
|
30620
|
+
}
|
|
30429
30621
|
function trimBlankLines(lines2) {
|
|
30430
30622
|
let start = 0;
|
|
30431
30623
|
let end = lines2.length;
|
|
@@ -30521,10 +30713,21 @@ function parseCrossFileMultiSpec(spec) {
|
|
|
30521
30713
|
}
|
|
30522
30714
|
return pairs.length > 1 ? pairs : null;
|
|
30523
30715
|
}
|
|
30716
|
+
function parseMultiFileSpec(spec) {
|
|
30717
|
+
if (!spec.includes(",")) return null;
|
|
30718
|
+
if (fileExists(spec)) return null;
|
|
30719
|
+
if (findSpecSeparator(spec) !== -1) return null;
|
|
30720
|
+
const parts = spec.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
30721
|
+
return parts.length > 1 ? parts : null;
|
|
30722
|
+
}
|
|
30723
|
+
function extraFileArgsNote(command, first2, extras) {
|
|
30724
|
+
return `Note: ${extras.length} extra file argument(s) ignored (${extras.join(", ")}). ${command} reads one file, or a comma-separated list: token-goat ${command} "${[first2, ...extras].join(",")}"`;
|
|
30725
|
+
}
|
|
30524
30726
|
function parseLineRange(spec) {
|
|
30525
30727
|
const m = /^(.+)@(\d+)(?:-(\d+))?$/.exec(spec);
|
|
30526
30728
|
if (m === null) return null;
|
|
30527
30729
|
if (fileExists(spec)) return null;
|
|
30730
|
+
if (m[1].includes("::")) return null;
|
|
30528
30731
|
const start = parseInt(m[2], 10);
|
|
30529
30732
|
const end = m[3] !== void 0 ? parseInt(m[3], 10) : start;
|
|
30530
30733
|
return { file: m[1], start, end };
|
|
@@ -30583,30 +30786,49 @@ function findParentName(entry, fileSymbols) {
|
|
|
30583
30786
|
if (doc !== "" && PARENT_IDENTIFIER_RE.test(doc)) return doc;
|
|
30584
30787
|
return null;
|
|
30585
30788
|
}
|
|
30586
|
-
function formatAmbiguity(symbol3, file2, candidates, explicitRoot) {
|
|
30789
|
+
function formatAmbiguity(symbol3, file2, candidates, explicitRoot, commandName = "read") {
|
|
30587
30790
|
const multiFile = new Set(candidates.map((c) => c.filePath)).size > 1;
|
|
30588
30791
|
const displayRoot = getDisplayRoot(explicitRoot);
|
|
30589
30792
|
const lines2 = [
|
|
30590
30793
|
`Ambiguous symbol '${symbol3}' in '${file2}': ${candidates.length} definitions match. Retry with one of the qualified commands below to pick one:`
|
|
30591
30794
|
];
|
|
30592
30795
|
const fileSymCache = /* @__PURE__ */ new Map();
|
|
30593
|
-
|
|
30594
|
-
let fileSyms = fileSymCache.get(
|
|
30796
|
+
const getFileSyms = (filePath) => {
|
|
30797
|
+
let fileSyms = fileSymCache.get(filePath);
|
|
30595
30798
|
if (fileSyms === void 0) {
|
|
30596
|
-
fileSyms = querySymbols({ filePath
|
|
30597
|
-
fileSymCache.set(
|
|
30799
|
+
fileSyms = querySymbols({ filePath, limit: 1e3 });
|
|
30800
|
+
fileSymCache.set(filePath, fileSyms);
|
|
30598
30801
|
}
|
|
30599
|
-
|
|
30600
|
-
|
|
30802
|
+
return fileSyms;
|
|
30803
|
+
};
|
|
30804
|
+
const parents = candidates.map((c) => findParentName(c, getFileSyms(c.filePath)));
|
|
30805
|
+
const plainQualifiers = candidates.map((c, i) => parents[i] !== null ? `${parents[i]}.${symbol3}` : symbol3);
|
|
30806
|
+
const qualifierCounts = /* @__PURE__ */ new Map();
|
|
30807
|
+
const fileGroupSize = /* @__PURE__ */ new Map();
|
|
30808
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30809
|
+
const c = candidates[i];
|
|
30810
|
+
const key = `${c.filePath} ${plainQualifiers[i]}`;
|
|
30811
|
+
qualifierCounts.set(key, (qualifierCounts.get(key) ?? 0) + 1);
|
|
30812
|
+
fileGroupSize.set(c.filePath, (fileGroupSize.get(c.filePath) ?? 0) + 1);
|
|
30813
|
+
}
|
|
30814
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30815
|
+
const c = candidates[i];
|
|
30816
|
+
const parent = parents[i];
|
|
30817
|
+
const plainQualifier = plainQualifiers[i];
|
|
30818
|
+
const collides = (qualifierCounts.get(`${c.filePath} ${plainQualifier}`) ?? 0) > 1 || parent === null && (fileGroupSize.get(c.filePath) ?? 0) > 1;
|
|
30819
|
+
const qualifier = collides ? `${plainQualifier}@${c.lineStart}` : plainQualifier;
|
|
30601
30820
|
const retryFile = multiFile ? toDisplayPath(displayRoot, c.filePath) : file2;
|
|
30602
30821
|
const label = multiFile ? `${toDisplayPath(displayRoot, c.filePath)}::${qualifier}` : qualifier;
|
|
30603
|
-
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat
|
|
30822
|
+
lines2.push(` - ${label} (line ${c.lineStart}) -> token-goat ${commandName} "${retryFile}::${qualifier}"`);
|
|
30604
30823
|
}
|
|
30605
30824
|
return lines2.join("\n");
|
|
30606
30825
|
}
|
|
30607
30826
|
function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
30608
|
-
const { file: file2, symbol:
|
|
30609
|
-
if (
|
|
30827
|
+
const { file: file2, symbol: rawSymbol } = parseReadSpec(spec);
|
|
30828
|
+
if (rawSymbol === void 0 || rawSymbol === "") return { kind: "none" };
|
|
30829
|
+
const anchorMatch = /^(.+)@(\d+)$/.exec(rawSymbol);
|
|
30830
|
+
const symbol3 = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
|
|
30831
|
+
const lineAnchor = anchorMatch !== null ? parseInt(anchorMatch[2], 10) : void 0;
|
|
30610
30832
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
30611
30833
|
if (forceRefresh === true) {
|
|
30612
30834
|
indexFileSync(resolved, globalDbPath());
|
|
@@ -30623,9 +30845,10 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
|
|
|
30623
30845
|
seen.add(key);
|
|
30624
30846
|
distinct.push(c);
|
|
30625
30847
|
}
|
|
30626
|
-
|
|
30627
|
-
if (
|
|
30628
|
-
return { kind: "
|
|
30848
|
+
const anchored = lineAnchor === void 0 ? distinct : distinct.filter((c) => c.lineStart === lineAnchor);
|
|
30849
|
+
if (anchored.length === 0) return { kind: "none" };
|
|
30850
|
+
if (anchored.length === 1) return { kind: "ok", entry: anchored[0] };
|
|
30851
|
+
return { kind: "ambiguous", symbol: displaySymbol, file: file2, candidates: anchored };
|
|
30629
30852
|
};
|
|
30630
30853
|
if (symbol3.includes(".")) {
|
|
30631
30854
|
const exactMatch = querySymbols({ name: symbol3, filePath: resolved, limit: 10 });
|
|
@@ -30705,6 +30928,8 @@ function runRead(opts) {
|
|
|
30705
30928
|
return runLineRange({ file: file2, start: lineSpec.start, end: lineSpec.end }, opts);
|
|
30706
30929
|
}
|
|
30707
30930
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
30931
|
+
const crossFileLead = formatCrossFileLead("read", symbol3, file2, opts.projectRoot);
|
|
30932
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
30708
30933
|
const resolved = resolveIndexPath(file2, opts.projectRoot ?? process.cwd());
|
|
30709
30934
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
30710
30935
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -30765,6 +30990,8 @@ ${sub.text}`);
|
|
|
30765
30990
|
return { text, code: 1 };
|
|
30766
30991
|
}
|
|
30767
30992
|
function runSection(opts) {
|
|
30993
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
30994
|
+
if (crossFilePairs !== null) return runSectionCrossFile(crossFilePairs, opts);
|
|
30768
30995
|
const colonIdx = findSpecSeparator(opts.spec);
|
|
30769
30996
|
if (colonIdx === -1) {
|
|
30770
30997
|
return { text: `Invalid section spec \u2014 expected "file::Heading", got: ${opts.spec}`, code: 1 };
|
|
@@ -30821,6 +31048,42 @@ ${sub.text}`);
|
|
|
30821
31048
|
if (anyFound) recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
30822
31049
|
return { text, code: anyFound ? 0 : 1 };
|
|
30823
31050
|
}
|
|
31051
|
+
function runSectionCrossFile(pairs, opts) {
|
|
31052
|
+
let anyFound = false;
|
|
31053
|
+
const jsonOut = {};
|
|
31054
|
+
const textBlocks = [];
|
|
31055
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
31056
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
31057
|
+
for (const { file: file2, symbol: heading } of pairs) {
|
|
31058
|
+
const sub = runSection({ ...opts, spec: `${file2}::${heading}`, suppressStat: true });
|
|
31059
|
+
if (sub.code === 0) anyFound = true;
|
|
31060
|
+
const key = keyFor({ file: file2, symbol: heading });
|
|
31061
|
+
if (opts.json === true) {
|
|
31062
|
+
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
31063
|
+
continue;
|
|
31064
|
+
}
|
|
31065
|
+
textBlocks.push(`${key}:
|
|
31066
|
+
${sub.text}`);
|
|
31067
|
+
}
|
|
31068
|
+
const resolvePath = (f) => opts.projectRoot !== void 0 && !path48.isAbsolute(f) ? path48.resolve(opts.projectRoot, f) : f;
|
|
31069
|
+
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
31070
|
+
if (anyFound) {
|
|
31071
|
+
const fullSourceBytes = sumFileSizes(Array.from(distinctFiles, resolvePath));
|
|
31072
|
+
recordReadStat("section_read", fullSourceBytes, text, opts.spec);
|
|
31073
|
+
}
|
|
31074
|
+
return { text, code: anyFound ? 0 : 1 };
|
|
31075
|
+
}
|
|
31076
|
+
function renderRefLines(ref2, displayRoot, contextLines, indent = " ") {
|
|
31077
|
+
const displayPath = toDisplayPath(displayRoot, ref2.filePath);
|
|
31078
|
+
const base = `${indent}${displayPath}:${ref2.line}: ${ref2.context}`;
|
|
31079
|
+
const window = buildContextWindow(ref2.filePath, ref2.line, contextLines);
|
|
31080
|
+
if (window === null) return [base];
|
|
31081
|
+
return [base, ...renderContextWindow(displayPath, ref2.line, window, "", `${indent} `)];
|
|
31082
|
+
}
|
|
31083
|
+
function withContextLines(items, contextLines) {
|
|
31084
|
+
if (!(contextLines > 0)) return items;
|
|
31085
|
+
return items.map((r) => ({ ...r, contextLines: buildContextWindow(r.filePath, r.line, contextLines) ?? [] }));
|
|
31086
|
+
}
|
|
30824
31087
|
function applyTypedRefsTier(symName, file2, results) {
|
|
30825
31088
|
if (results.length === 0) return results;
|
|
30826
31089
|
try {
|
|
@@ -30858,6 +31121,8 @@ function runRefs(opts) {
|
|
|
30858
31121
|
emitErr2(`--top must be a positive number, got: ${opts.top}`);
|
|
30859
31122
|
return 1;
|
|
30860
31123
|
}
|
|
31124
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
31125
|
+
if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
|
|
30861
31126
|
const { file: file2, symbols } = parseMultiRefsSpec(opts.spec);
|
|
30862
31127
|
if (symbols.length <= 1) return runRefsSingle(opts);
|
|
30863
31128
|
const jsonOut = {};
|
|
@@ -30866,10 +31131,18 @@ function runRefs(opts) {
|
|
|
30866
31131
|
const refFilePaths = [];
|
|
30867
31132
|
for (const sym of symbols) {
|
|
30868
31133
|
const queryOpts = { name: sym };
|
|
30869
|
-
if (
|
|
30870
|
-
if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
31134
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31135
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30871
31136
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30872
|
-
|
|
31137
|
+
let results = applyTypedRefsTier(sym, file2, queryRefs(queryOpts));
|
|
31138
|
+
let suppressed = 0;
|
|
31139
|
+
let filteredTotal;
|
|
31140
|
+
if (opts.excludeTests === true) {
|
|
31141
|
+
const f = applyExcludeTestsFilter(results);
|
|
31142
|
+
suppressed = f.suppressed;
|
|
31143
|
+
filteredTotal = f.refs.length;
|
|
31144
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
31145
|
+
}
|
|
30873
31146
|
if (results.length > 0) anyFound = true;
|
|
30874
31147
|
refFilePaths.push(...results.map((r) => r.filePath));
|
|
30875
31148
|
if (opts.json === true) {
|
|
@@ -30877,22 +31150,85 @@ function runRefs(opts) {
|
|
|
30877
31150
|
jsonOut[sym] = topFilesJsonPayload(results, opts.top);
|
|
30878
31151
|
} else {
|
|
30879
31152
|
const capped = guardJsonRows(results);
|
|
30880
|
-
const trueTotal = countRefs(queryOpts);
|
|
30881
|
-
jsonOut[sym] = { items: capped.items, truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
31153
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
31154
|
+
jsonOut[sym] = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30882
31155
|
}
|
|
30883
31156
|
continue;
|
|
30884
31157
|
}
|
|
30885
31158
|
if (results.length === 0) {
|
|
30886
|
-
lines2.push(`${sym}: (no references found)`);
|
|
31159
|
+
lines2.push(opts.excludeTests === true && suppressed > 0 ? `${sym}: (no non-test references found; ${suppressed} in test files hidden by --exclude-tests)` : `${sym}: (no references found)`);
|
|
30887
31160
|
continue;
|
|
30888
31161
|
}
|
|
30889
31162
|
lines2.push(`${sym}:`);
|
|
30890
31163
|
if (opts.top !== void 0) {
|
|
30891
|
-
lines2.push(...renderTopFilesSummary(results, opts.top));
|
|
31164
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
31165
|
+
} else if (opts.callers === true) {
|
|
31166
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31167
|
+
lines2.push(...renderCallerGroups(results, void 0, opts.context ?? 0));
|
|
31168
|
+
} else {
|
|
31169
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31170
|
+
for (const ref2 of results) lines2.push(...renderRefLines(ref2, void 0, opts.context ?? 0));
|
|
31171
|
+
}
|
|
31172
|
+
}
|
|
31173
|
+
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
31174
|
+
if (opts.json === true) {
|
|
31175
|
+
const text2 = JSON.stringify(jsonOut, null, 2);
|
|
31176
|
+
emit3(text2);
|
|
31177
|
+
if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text2, opts.spec);
|
|
31178
|
+
return anyFound ? 0 : 1;
|
|
31179
|
+
}
|
|
31180
|
+
const text = lines2.join("\n");
|
|
31181
|
+
emitGuarded(text, "symbol");
|
|
31182
|
+
if (anyFound) recordReadStat("symbol_read", fullSourceBytes, text, opts.spec);
|
|
31183
|
+
return anyFound ? 0 : 1;
|
|
31184
|
+
}
|
|
31185
|
+
function runRefsCrossFile(pairs, opts) {
|
|
31186
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
31187
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
31188
|
+
const jsonOut = {};
|
|
31189
|
+
let anyFound = false;
|
|
31190
|
+
const lines2 = [];
|
|
31191
|
+
const refFilePaths = [];
|
|
31192
|
+
for (const { file: file2, symbol: symbol3 } of pairs) {
|
|
31193
|
+
const key = keyFor({ file: file2, symbol: symbol3 });
|
|
31194
|
+
const queryOpts = { name: symbol3 };
|
|
31195
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31196
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
31197
|
+
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31198
|
+
let results = applyTypedRefsTier(symbol3, file2, queryRefs(queryOpts));
|
|
31199
|
+
let suppressed = 0;
|
|
31200
|
+
let filteredTotal;
|
|
31201
|
+
if (opts.excludeTests === true) {
|
|
31202
|
+
const f = applyExcludeTestsFilter(results);
|
|
31203
|
+
suppressed = f.suppressed;
|
|
31204
|
+
filteredTotal = f.refs.length;
|
|
31205
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
31206
|
+
}
|
|
31207
|
+
if (results.length > 0) anyFound = true;
|
|
31208
|
+
refFilePaths.push(...results.map((r) => r.filePath));
|
|
31209
|
+
if (opts.json === true) {
|
|
31210
|
+
if (opts.top !== void 0) {
|
|
31211
|
+
jsonOut[key] = topFilesJsonPayload(results, opts.top);
|
|
31212
|
+
} else {
|
|
31213
|
+
const capped = guardJsonRows(results);
|
|
31214
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
31215
|
+
jsonOut[key] = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
31216
|
+
}
|
|
31217
|
+
continue;
|
|
31218
|
+
}
|
|
31219
|
+
if (results.length === 0) {
|
|
31220
|
+
lines2.push(opts.excludeTests === true && suppressed > 0 ? `${key}: (no non-test references found; ${suppressed} in test files hidden by --exclude-tests)` : `${key}: (no references found)`);
|
|
31221
|
+
continue;
|
|
31222
|
+
}
|
|
31223
|
+
lines2.push(`${key}:`);
|
|
31224
|
+
if (opts.top !== void 0) {
|
|
31225
|
+
lines2.push(...renderTopFilesSummary(results, opts.top, void 0, suppressed));
|
|
30892
31226
|
} else if (opts.callers === true) {
|
|
30893
|
-
lines2.push(
|
|
31227
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31228
|
+
lines2.push(...renderCallerGroups(results, void 0, opts.context ?? 0));
|
|
30894
31229
|
} else {
|
|
30895
|
-
|
|
31230
|
+
if (opts.excludeTests === true && suppressed > 0) lines2.push(` ${results.length} references (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31231
|
+
for (const ref2 of results) lines2.push(...renderRefLines(ref2, void 0, opts.context ?? 0));
|
|
30896
31232
|
}
|
|
30897
31233
|
}
|
|
30898
31234
|
const fullSourceBytes = sumFileSizes(refFilePaths);
|
|
@@ -30912,11 +31248,23 @@ function runRefsSingle(opts) {
|
|
|
30912
31248
|
const symName = symbol3 ?? file2;
|
|
30913
31249
|
const queryOpts = { name: symName };
|
|
30914
31250
|
const defFileHint = symbol3 !== void 0 ? resolveIndexPath(file2) : void 0;
|
|
30915
|
-
if (
|
|
30916
|
-
if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
31251
|
+
if (opts.excludeTests === true) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
31252
|
+
else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
|
|
30917
31253
|
else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
30918
|
-
|
|
31254
|
+
let results = applyTypedRefsTier(symName, defFileHint, queryRefs(queryOpts));
|
|
31255
|
+
let suppressed = 0;
|
|
31256
|
+
let filteredTotal;
|
|
31257
|
+
if (opts.excludeTests === true) {
|
|
31258
|
+
const f = applyExcludeTestsFilter(results);
|
|
31259
|
+
suppressed = f.suppressed;
|
|
31260
|
+
filteredTotal = f.refs.length;
|
|
31261
|
+
results = opts.top !== void 0 ? f.refs : f.refs.slice(0, opts.limit ?? 100);
|
|
31262
|
+
}
|
|
30919
31263
|
if (results.length === 0) {
|
|
31264
|
+
if (opts.excludeTests === true && suppressed > 0) {
|
|
31265
|
+
emitErr2(`No non-test references found for '${symName}' (${suppressed} in test files hidden by --exclude-tests)`);
|
|
31266
|
+
return 1;
|
|
31267
|
+
}
|
|
30920
31268
|
emitErr2(`No references found for '${symName}'`);
|
|
30921
31269
|
return 1;
|
|
30922
31270
|
}
|
|
@@ -30927,8 +31275,8 @@ function runRefsSingle(opts) {
|
|
|
30927
31275
|
payload = topFilesJsonPayload(results, opts.top);
|
|
30928
31276
|
} else {
|
|
30929
31277
|
const capped = guardJsonRows(results);
|
|
30930
|
-
const trueTotal = countRefs(queryOpts);
|
|
30931
|
-
payload = { items: capped.items, truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
31278
|
+
const trueTotal = opts.excludeTests === true ? filteredTotal ?? results.length : countRefs(queryOpts);
|
|
31279
|
+
payload = { items: withContextLines(capped.items, opts.context ?? 0), truncated: capped.truncated || trueTotal > results.length, totalCount: trueTotal };
|
|
30932
31280
|
}
|
|
30933
31281
|
const text2 = JSON.stringify(payload, null, 2);
|
|
30934
31282
|
emit3(text2);
|
|
@@ -30936,21 +31284,26 @@ function runRefsSingle(opts) {
|
|
|
30936
31284
|
return 0;
|
|
30937
31285
|
}
|
|
30938
31286
|
const displayRoot = getDisplayRoot();
|
|
30939
|
-
const lines2 = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, displayRoot) : opts.callers === true ? renderCallerGroups(results, displayRoot) : results.
|
|
31287
|
+
const lines2 = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, displayRoot, suppressed) : opts.callers === true ? [...opts.excludeTests === true && suppressed > 0 ? [`${results.length} references (${suppressed} in test files hidden by --exclude-tests)`] : [], ...renderCallerGroups(results, displayRoot, opts.context ?? 0)] : [...opts.excludeTests === true && suppressed > 0 ? [`${results.length} references (${suppressed} in test files hidden by --exclude-tests)`] : [], ...results.flatMap((ref2) => renderRefLines(ref2, displayRoot, opts.context ?? 0, ""))];
|
|
30940
31288
|
const text = lines2.join("\n");
|
|
30941
31289
|
emitGuarded(text, "symbol");
|
|
30942
31290
|
recordReadStat("symbol_read", fullSourceBytes, text, symName);
|
|
30943
31291
|
return 0;
|
|
30944
31292
|
}
|
|
31293
|
+
function applyExcludeTestsFilter(refs) {
|
|
31294
|
+
const filtered = refs.filter((r) => !isTestFile(r.filePath));
|
|
31295
|
+
return { refs: filtered, suppressed: refs.length - filtered.length };
|
|
31296
|
+
}
|
|
30945
31297
|
function groupRefsByFile(refs) {
|
|
30946
31298
|
const byFile = /* @__PURE__ */ new Map();
|
|
30947
31299
|
for (const ref2 of refs) byFile.set(ref2.filePath, (byFile.get(ref2.filePath) ?? 0) + 1);
|
|
30948
31300
|
return [...byFile.entries()].map(([file2, count]) => ({ file: file2, count })).sort((a, b) => b.count - a.count || (a.file < b.file ? -1 : a.file > b.file ? 1 : 0));
|
|
30949
31301
|
}
|
|
30950
|
-
function renderTopFilesSummary(refs, topN, displayRoot) {
|
|
31302
|
+
function renderTopFilesSummary(refs, topN, displayRoot, suppressed) {
|
|
30951
31303
|
const grouped = groupRefsByFile(refs);
|
|
30952
31304
|
const shown = grouped.slice(0, topN);
|
|
30953
|
-
const
|
|
31305
|
+
const suppressedNote = suppressed !== void 0 && suppressed > 0 ? ` (${suppressed} in test files hidden by --exclude-tests)` : "";
|
|
31306
|
+
const lines2 = [`${refs.length} references across ${grouped.length} files (showing top ${shown.length})${suppressedNote}`];
|
|
30954
31307
|
for (const { file: file2, count } of shown) lines2.push(` ${count} ${toDisplayPath(displayRoot, file2)}`);
|
|
30955
31308
|
const omittedFiles = grouped.length - shown.length;
|
|
30956
31309
|
if (omittedFiles > 0) {
|
|
@@ -30964,7 +31317,7 @@ function topFilesJsonPayload(refs, topN) {
|
|
|
30964
31317
|
const shown = grouped.slice(0, topN);
|
|
30965
31318
|
return { fileCounts: shown, totalFiles: grouped.length, totalRefs: refs.length, shown: shown.length };
|
|
30966
31319
|
}
|
|
30967
|
-
function renderCallerGroups(refs, displayRoot) {
|
|
31320
|
+
function renderCallerGroups(refs, displayRoot, contextLines = 0) {
|
|
30968
31321
|
const byFile = /* @__PURE__ */ new Map();
|
|
30969
31322
|
for (const ref2 of refs) {
|
|
30970
31323
|
const bucket = byFile.get(ref2.filePath);
|
|
@@ -30976,9 +31329,12 @@ function renderCallerGroups(refs, displayRoot) {
|
|
|
30976
31329
|
}
|
|
30977
31330
|
const lines2 = [];
|
|
30978
31331
|
for (const [file2, fileRefs] of byFile) {
|
|
30979
|
-
|
|
31332
|
+
const displayPath = toDisplayPath(displayRoot, file2);
|
|
31333
|
+
lines2.push(`${displayPath}:`);
|
|
30980
31334
|
for (const ref2 of fileRefs) {
|
|
30981
31335
|
lines2.push(` :${ref2.line} ${ref2.context !== "" ? ref2.context : "(module scope)"}`);
|
|
31336
|
+
const window = buildContextWindow(file2, ref2.line, contextLines);
|
|
31337
|
+
if (window !== null) lines2.push(...renderContextWindow(displayPath, ref2.line, window, "", " "));
|
|
30982
31338
|
}
|
|
30983
31339
|
}
|
|
30984
31340
|
return lines2;
|
|
@@ -31017,7 +31373,19 @@ function prepareSymbolListing(file2, opts) {
|
|
|
31017
31373
|
const fullSourceBytes = sumFileSizes([resolved]);
|
|
31018
31374
|
return { kind: "ok", resolved, filtered, refCounts, fullSourceBytes, symbolsTruncated, trueSymbolCount };
|
|
31019
31375
|
}
|
|
31376
|
+
function runPerFileListing(files, run3) {
|
|
31377
|
+
const blocks = [];
|
|
31378
|
+
let anyOk = false;
|
|
31379
|
+
for (const file2 of files) {
|
|
31380
|
+
const r = run3(file2);
|
|
31381
|
+
if (r.code === 0) anyOk = true;
|
|
31382
|
+
blocks.push(r.text);
|
|
31383
|
+
}
|
|
31384
|
+
return { text: blocks.join("\n\n"), code: anyOk ? 0 : 1 };
|
|
31385
|
+
}
|
|
31020
31386
|
function runSkeleton(opts) {
|
|
31387
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
31388
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runSkeleton({ ...opts, file: file2 }));
|
|
31021
31389
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
31022
31390
|
if (prep.kind === "empty") {
|
|
31023
31391
|
return { text: prep.text, code: 1 };
|
|
@@ -31053,6 +31421,8 @@ function runSkeleton(opts) {
|
|
|
31053
31421
|
return { text, code: 0 };
|
|
31054
31422
|
}
|
|
31055
31423
|
function runOutline(opts) {
|
|
31424
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
31425
|
+
if (multiFiles !== null) return runPerFileListing(multiFiles, (file2) => runOutline({ ...opts, file: file2 }));
|
|
31056
31426
|
const prep = prepareSymbolListing(opts.file, opts);
|
|
31057
31427
|
if (prep.kind === "empty") {
|
|
31058
31428
|
return { text: prep.text, code: 1 };
|
|
@@ -31627,10 +31997,13 @@ function runBriefCore(opts) {
|
|
|
31627
31997
|
const resolution = resolveSymbolSpec(opts.spec);
|
|
31628
31998
|
if (resolution.kind === "ambiguous") {
|
|
31629
31999
|
return {
|
|
32000
|
+
// Name the command explicitly: formatAmbiguity defaults to 'read', so brief's retry lines would otherwise tell the user to run `token-goat read`, which answers a different question than the one they asked.
|
|
31630
32001
|
text: formatAmbiguity(
|
|
31631
32002
|
resolution.symbol,
|
|
31632
32003
|
resolution.file,
|
|
31633
|
-
resolution.candidates
|
|
32004
|
+
resolution.candidates,
|
|
32005
|
+
void 0,
|
|
32006
|
+
"brief"
|
|
31634
32007
|
),
|
|
31635
32008
|
code: 1
|
|
31636
32009
|
};
|
|
@@ -31653,7 +32026,7 @@ function runBriefCore(opts) {
|
|
|
31653
32026
|
if (opts.json === true) {
|
|
31654
32027
|
const result = {
|
|
31655
32028
|
symbol: match2,
|
|
31656
|
-
callers: shown,
|
|
32029
|
+
callers: (opts.context ?? 0) > 0 ? shown.map((c) => ({ ...c, contextLines: buildContextWindow(c.file, c.line, opts.context ?? 0) ?? [] })) : shown,
|
|
31657
32030
|
totalCallers,
|
|
31658
32031
|
truncated,
|
|
31659
32032
|
section: section2
|
|
@@ -31672,7 +32045,10 @@ function runBriefCore(opts) {
|
|
|
31672
32045
|
];
|
|
31673
32046
|
lines2.push(`Callers (${totalCallers}):`);
|
|
31674
32047
|
for (const c of shown) {
|
|
31675
|
-
|
|
32048
|
+
const callerDisplayPath = toDisplayPath(rootDir, c.file);
|
|
32049
|
+
lines2.push(` ${c.caller} ${callerDisplayPath}:${c.line}`);
|
|
32050
|
+
const window = buildContextWindow(c.file, c.line, opts.context ?? 0);
|
|
32051
|
+
if (window !== null) lines2.push(...renderContextWindow(callerDisplayPath, c.line, window, "", " "));
|
|
31676
32052
|
}
|
|
31677
32053
|
if (truncated) {
|
|
31678
32054
|
lines2.push(` ...(${totalCallers - shown.length} more elided)`);
|
|
@@ -31704,11 +32080,40 @@ ${sub.text}`);
|
|
|
31704
32080
|
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
31705
32081
|
return { text, code: anyFound ? 0 : 1 };
|
|
31706
32082
|
}
|
|
32083
|
+
function runBriefCrossFile(pairs, opts) {
|
|
32084
|
+
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
32085
|
+
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
32086
|
+
let anyFound = false;
|
|
32087
|
+
const jsonOut = {};
|
|
32088
|
+
const textBlocks = [];
|
|
32089
|
+
for (const { file: file2, symbol: symbol3 } of pairs) {
|
|
32090
|
+
const key = keyFor({ file: file2, symbol: symbol3 });
|
|
32091
|
+
const sub = runBriefCore({ ...opts, spec: `${file2}::${symbol3}`, suppressStat: true });
|
|
32092
|
+
if (sub.code === 0) anyFound = true;
|
|
32093
|
+
if (opts.json === true) {
|
|
32094
|
+
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
32095
|
+
continue;
|
|
32096
|
+
}
|
|
32097
|
+
textBlocks.push(`${key}:
|
|
32098
|
+
${sub.text}`);
|
|
32099
|
+
}
|
|
32100
|
+
const fullSourceBytes = sumFileSizes([...distinctFiles].map((f) => resolveIndexPath(f, process.cwd())));
|
|
32101
|
+
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
32102
|
+
if (anyFound) recordReadStat("brief_view", fullSourceBytes, text, opts.spec);
|
|
32103
|
+
return { text, code: anyFound ? 0 : 1 };
|
|
32104
|
+
}
|
|
31707
32105
|
function runBrief(opts) {
|
|
31708
32106
|
if (opts.limit !== void 0 && opts.limit <= 0) {
|
|
31709
32107
|
emitErr2(`--limit must be a positive number, got: ${opts.limit}`);
|
|
31710
32108
|
return 1;
|
|
31711
32109
|
}
|
|
32110
|
+
const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
|
|
32111
|
+
if (crossFilePairs !== null) {
|
|
32112
|
+
const { text: text2, code: code2 } = runBriefCrossFile(crossFilePairs, opts);
|
|
32113
|
+
if (code2 === 0) emit3(text2);
|
|
32114
|
+
else emitErr2(text2);
|
|
32115
|
+
return code2;
|
|
32116
|
+
}
|
|
31712
32117
|
const { file: file2, symbol: symbol3 } = parseReadSpec(opts.spec);
|
|
31713
32118
|
if (symbol3 !== void 0 && symbol3 !== "" && symbol3.includes(",")) {
|
|
31714
32119
|
const multiSymbols = symbol3.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -31800,6 +32205,34 @@ function parseDiffHunks(diffText) {
|
|
|
31800
32205
|
}
|
|
31801
32206
|
return hunksByFile;
|
|
31802
32207
|
}
|
|
32208
|
+
function buildChangedRefHint(cwd, ref2) {
|
|
32209
|
+
const countResult = runGit(["rev-list", "--count", "HEAD"], { cwd });
|
|
32210
|
+
if (countResult.exitCode !== 0) {
|
|
32211
|
+
return null;
|
|
32212
|
+
}
|
|
32213
|
+
const commitCount = Number.parseInt(countResult.stdout.trim(), 10);
|
|
32214
|
+
if (!Number.isFinite(commitCount) || commitCount < 1) {
|
|
32215
|
+
return null;
|
|
32216
|
+
}
|
|
32217
|
+
const refResolves = runGit(["rev-parse", "--verify", "--quiet", ref2], { cwd });
|
|
32218
|
+
if (refResolves.exitCode === 0) {
|
|
32219
|
+
return null;
|
|
32220
|
+
}
|
|
32221
|
+
let suggestedRef = null;
|
|
32222
|
+
for (let n = commitCount - 1; n >= 1; n--) {
|
|
32223
|
+
const candidate = `HEAD~${n}`;
|
|
32224
|
+
const candidateResolves = runGit(["rev-parse", "--verify", "--quiet", candidate], { cwd });
|
|
32225
|
+
if (candidateResolves.exitCode === 0) {
|
|
32226
|
+
suggestedRef = candidate;
|
|
32227
|
+
break;
|
|
32228
|
+
}
|
|
32229
|
+
}
|
|
32230
|
+
if (suggestedRef === null) {
|
|
32231
|
+
suggestedRef = EMPTY_TREE_HASH;
|
|
32232
|
+
}
|
|
32233
|
+
const commitWord = commitCount === 1 ? "1 commit" : `${commitCount} commits`;
|
|
32234
|
+
return `Hint: this repo has only ${commitWord}; '${ref2}' does not exist. Try: token-goat changed --since ${suggestedRef}`;
|
|
32235
|
+
}
|
|
31803
32236
|
function runChanged(opts = {}) {
|
|
31804
32237
|
const ref2 = opts.ref ?? "HEAD~5";
|
|
31805
32238
|
const cwd = opts.projectRoot ?? process.cwd();
|
|
@@ -31809,6 +32242,10 @@ function runChanged(opts = {}) {
|
|
|
31809
32242
|
const result = runGit(["diff", ref2, "--name-only"], { cwd });
|
|
31810
32243
|
if (result.exitCode !== 0) {
|
|
31811
32244
|
emitErr2(`git diff failed: ${result.stderr}`);
|
|
32245
|
+
const hint = buildChangedRefHint(cwd, ref2);
|
|
32246
|
+
if (hint !== null) {
|
|
32247
|
+
emitErr2(hint);
|
|
32248
|
+
}
|
|
31812
32249
|
return 1;
|
|
31813
32250
|
}
|
|
31814
32251
|
changedFiles = result.stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
@@ -31914,13 +32351,16 @@ function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
|
|
|
31914
32351
|
resolution.symbol,
|
|
31915
32352
|
resolution.file,
|
|
31916
32353
|
resolution.candidates,
|
|
31917
|
-
projectRoot
|
|
32354
|
+
projectRoot,
|
|
32355
|
+
commandName
|
|
31918
32356
|
)
|
|
31919
32357
|
);
|
|
31920
32358
|
return null;
|
|
31921
32359
|
}
|
|
31922
32360
|
if (resolution.kind === "none") {
|
|
31923
32361
|
const messages = [`Symbol '${symbol3}' not found in '${file2}'`];
|
|
32362
|
+
const crossFileLead = formatCrossFileLead(commandName, symbol3, file2, projectRoot);
|
|
32363
|
+
if (crossFileLead !== "") messages.push(crossFileLead);
|
|
31924
32364
|
const resolved = resolveIndexPath(file2, projectRoot ?? process.cwd());
|
|
31925
32365
|
const closes = querySymbols({ filePath: resolved, limit: DIDYOUMEAN_LIMIT }).map((s) => s.name);
|
|
31926
32366
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
@@ -32130,22 +32570,29 @@ function runGrep(opts) {
|
|
|
32130
32570
|
return 1;
|
|
32131
32571
|
}
|
|
32132
32572
|
const truncated = hits.slice(0, maxLines);
|
|
32573
|
+
if (opts.symbol === true) {
|
|
32574
|
+
const symbolsByFile = /* @__PURE__ */ new Map();
|
|
32575
|
+
for (const hit of truncated) {
|
|
32576
|
+
let syms = symbolsByFile.get(hit.file);
|
|
32577
|
+
if (syms === void 0) {
|
|
32578
|
+
syms = querySymbols({ filePath: resolveIndexPath(hit.file), limit: ALL_SYMBOLS_IN_FILE_LIMIT });
|
|
32579
|
+
symbolsByFile.set(hit.file, syms);
|
|
32580
|
+
}
|
|
32581
|
+
const enc = enclosingSymbol(syms, hit.line);
|
|
32582
|
+
hit.symbol = enc === null ? null : { name: enc.name, kind: enc.kind, lineStart: enc.lineStart, lineEnd: enc.lineEnd };
|
|
32583
|
+
}
|
|
32584
|
+
}
|
|
32133
32585
|
if (opts.json === true) {
|
|
32134
32586
|
const payload = { items: truncated, truncated: hits.length > maxLines, totalCount: hits.length };
|
|
32135
32587
|
emit3(JSON.stringify(payload, null, 2));
|
|
32136
32588
|
return 0;
|
|
32137
32589
|
}
|
|
32138
32590
|
for (const hit of truncated) {
|
|
32591
|
+
const symbolTag = opts.symbol === true && hit.symbol != null ? ` [${hit.symbol.name} (${hit.symbol.kind})]` : "";
|
|
32139
32592
|
if (hit.context !== void 0) {
|
|
32140
|
-
for (const
|
|
32141
|
-
if (ctxLine.line === hit.line) {
|
|
32142
|
-
emit3(`${hit.file}:${ctxLine.line}: ${ctxLine.text}`);
|
|
32143
|
-
} else {
|
|
32144
|
-
emit3(`${hit.file}-${ctxLine.line}- ${ctxLine.text}`);
|
|
32145
|
-
}
|
|
32146
|
-
}
|
|
32593
|
+
for (const line of renderContextWindow(hit.file, hit.line, hit.context, symbolTag)) emit3(line);
|
|
32147
32594
|
} else {
|
|
32148
|
-
emit3(`${hit.file}:${hit.line}: ${hit.text}`);
|
|
32595
|
+
emit3(`${hit.file}:${hit.line}: ${hit.text}${symbolTag}`);
|
|
32149
32596
|
}
|
|
32150
32597
|
}
|
|
32151
32598
|
if (hits.length > maxLines) {
|
|
@@ -32328,9 +32775,24 @@ function extractExportNames(text, ext2) {
|
|
|
32328
32775
|
}
|
|
32329
32776
|
return names;
|
|
32330
32777
|
}
|
|
32778
|
+
function runPerFileEmitting(files, label, run3) {
|
|
32779
|
+
let anyOk = false;
|
|
32780
|
+
files.forEach((file2, i) => {
|
|
32781
|
+
if (i > 0) emit3("");
|
|
32782
|
+
emit3(`# ${label}: ${file2}`);
|
|
32783
|
+
if (run3(file2) === 0) anyOk = true;
|
|
32784
|
+
});
|
|
32785
|
+
return anyOk ? 0 : 1;
|
|
32786
|
+
}
|
|
32331
32787
|
function runExports(opts) {
|
|
32788
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
32789
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Exports", (file2) => runExports({ ...opts, file: file2 }));
|
|
32332
32790
|
const symbols = querySymbols({ filePath: resolveIndexPath(opts.file), limit: 500 });
|
|
32333
32791
|
const kindOf = (name2) => symbols.find((s) => s.name === name2)?.kind ?? "export";
|
|
32792
|
+
const locOf = (name2) => {
|
|
32793
|
+
const s = symbols.find((sym) => sym.name === name2);
|
|
32794
|
+
return s === void 0 ? null : { lineStart: s.lineStart, lineEnd: s.lineEnd };
|
|
32795
|
+
};
|
|
32334
32796
|
const names = [];
|
|
32335
32797
|
for (const s of symbols) {
|
|
32336
32798
|
if (/^(?:export|pub\b|public\b)/.test(s.body.trimStart()) && !names.includes(s.name)) {
|
|
@@ -32355,12 +32817,23 @@ function runExports(opts) {
|
|
|
32355
32817
|
}
|
|
32356
32818
|
const fullSourceBytes = sumFileSizes([opts.file]);
|
|
32357
32819
|
if (opts.json === true) {
|
|
32358
|
-
const jsonText = JSON.stringify(
|
|
32820
|
+
const jsonText = JSON.stringify(
|
|
32821
|
+
names.map((n) => {
|
|
32822
|
+
const loc = locOf(n);
|
|
32823
|
+
return { name: n, kind: kindOf(n), lineStart: loc?.lineStart ?? null, lineEnd: loc?.lineEnd ?? null };
|
|
32824
|
+
}),
|
|
32825
|
+
null,
|
|
32826
|
+
2
|
|
32827
|
+
);
|
|
32359
32828
|
emit3(jsonText);
|
|
32360
32829
|
recordReadStat("exports", fullSourceBytes, jsonText, opts.file);
|
|
32361
32830
|
return 0;
|
|
32362
32831
|
}
|
|
32363
|
-
const outLines = names.map((n) =>
|
|
32832
|
+
const outLines = names.map((n) => {
|
|
32833
|
+
const loc = locOf(n);
|
|
32834
|
+
const locSuffix = loc === null ? "" : ` (${loc.lineStart}-${loc.lineEnd})`;
|
|
32835
|
+
return `${kindOf(n).padEnd(10)} ${n}${locSuffix}`;
|
|
32836
|
+
});
|
|
32364
32837
|
for (const line of outLines) {
|
|
32365
32838
|
emit3(line);
|
|
32366
32839
|
}
|
|
@@ -32654,6 +33127,8 @@ function importsExtensionFor(filePath) {
|
|
|
32654
33127
|
return path48.extname(filePath);
|
|
32655
33128
|
}
|
|
32656
33129
|
function runImports(opts) {
|
|
33130
|
+
const multiFiles = parseMultiFileSpec(opts.file);
|
|
33131
|
+
if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Imports", (file2) => runImports({ ...opts, file: file2 }));
|
|
32657
33132
|
const text = readFileText(opts.file);
|
|
32658
33133
|
if (text === null) {
|
|
32659
33134
|
emitErr2(`Could not read: ${opts.file}`);
|
|
@@ -32747,11 +33222,12 @@ async function runSemantic(query, opts) {
|
|
|
32747
33222
|
);
|
|
32748
33223
|
const hits = mergeNearbyHits(rawHits).slice(0, n);
|
|
32749
33224
|
if (hits.length > 0) {
|
|
33225
|
+
const enclosing = hits.map((h) => resolveEnclosingSymbol(h.filePath, h.startLine));
|
|
32750
33226
|
if (opts.json === true) {
|
|
32751
|
-
const items = hits.map((h) => ({
|
|
33227
|
+
const items = hits.map((h, i) => ({
|
|
32752
33228
|
filePath: h.filePath,
|
|
32753
|
-
name: null,
|
|
32754
|
-
kind: null,
|
|
33229
|
+
name: enclosing[i]?.name ?? null,
|
|
33230
|
+
kind: enclosing[i]?.kind ?? null,
|
|
32755
33231
|
startLine: h.startLine,
|
|
32756
33232
|
endLine: h.endLine,
|
|
32757
33233
|
distance: h.distance,
|
|
@@ -32762,10 +33238,12 @@ async function runSemantic(query, opts) {
|
|
|
32762
33238
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
|
|
32763
33239
|
return { text: text3, code: 0 };
|
|
32764
33240
|
}
|
|
32765
|
-
const blocks2 = hits.map(
|
|
32766
|
-
|
|
32767
|
-
${
|
|
32768
|
-
|
|
33241
|
+
const blocks2 = hits.map((h, i) => {
|
|
33242
|
+
const enc = enclosing[i] ?? null;
|
|
33243
|
+
const suffix = enc !== null ? ` \u2014 inside ${enc.name} (${enc.kind})` : "";
|
|
33244
|
+
return `# ${toDisplayPath(rootDir, h.filePath)}:${h.startLine}-${h.endLine} (distance ${h.distance.toFixed(3)})${suffix}
|
|
33245
|
+
${previewLines(h.text, 3)}`;
|
|
33246
|
+
});
|
|
32769
33247
|
const text2 = guardText(blocks2.join("\n\n"), "semantic");
|
|
32770
33248
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text2, query);
|
|
32771
33249
|
return { text: text2, code: 0 };
|
|
@@ -32854,7 +33332,7 @@ function runNoteList(opts = {}) {
|
|
|
32854
33332
|
});
|
|
32855
33333
|
return { text: lines2.join("\n"), code: 0 };
|
|
32856
33334
|
}
|
|
32857
|
-
var DIDYOUMEAN_LIMIT, MIN_REVERSE_MATCH_LEN, GREP_MAX_LINES, FIND_SCAN_LIMIT, REFS_TOP_SCAN_LIMIT, STALE_WARNING, PARENT_IDENTIFIER_RE, SKELETON_SYMBOL_CAP, HUNK_HEADER_RE, DEFAULT_LOG_MAX_COUNT;
|
|
33335
|
+
var DIDYOUMEAN_LIMIT, MIN_REVERSE_MATCH_LEN, GREP_MAX_LINES, FIND_SCAN_LIMIT, REFS_TOP_SCAN_LIMIT, STALE_WARNING, PARENT_IDENTIFIER_RE, SKELETON_SYMBOL_CAP, HUNK_HEADER_RE, EMPTY_TREE_HASH, DEFAULT_LOG_MAX_COUNT;
|
|
32858
33336
|
var init_read_commands = __esm({
|
|
32859
33337
|
"src/read_commands.ts"() {
|
|
32860
33338
|
"use strict";
|
|
@@ -32899,6 +33377,7 @@ var init_read_commands = __esm({
|
|
|
32899
33377
|
PARENT_IDENTIFIER_RE = /^[\w$]+$/;
|
|
32900
33378
|
SKELETON_SYMBOL_CAP = 5e3;
|
|
32901
33379
|
HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
|
|
33380
|
+
EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
32902
33381
|
DEFAULT_LOG_MAX_COUNT = 20;
|
|
32903
33382
|
}
|
|
32904
33383
|
});
|
|
@@ -48689,6 +49168,7 @@ init_index_reader();
|
|
|
48689
49168
|
init_parser_types();
|
|
48690
49169
|
init_doc_embed_extract();
|
|
48691
49170
|
init_paths();
|
|
49171
|
+
init_project();
|
|
48692
49172
|
init_hooks_index();
|
|
48693
49173
|
|
|
48694
49174
|
// src/relay.ts
|
|
@@ -49089,7 +49569,7 @@ init_util2();
|
|
|
49089
49569
|
init_disk_cache();
|
|
49090
49570
|
init_lang_patterns();
|
|
49091
49571
|
import { readdirSync as readdirSync13, readFileSync as readFileSync24, statSync as statSync17 } from "fs";
|
|
49092
|
-
import { resolve as
|
|
49572
|
+
import { resolve as resolve13 } from "path";
|
|
49093
49573
|
|
|
49094
49574
|
// src/recall_index.ts
|
|
49095
49575
|
init_define_import_meta_env();
|
|
@@ -49322,7 +49802,7 @@ function depLockfileFingerprintSync(cmd, cwd) {
|
|
|
49322
49802
|
if (!candidates) return null;
|
|
49323
49803
|
for (const lockfile of candidates) {
|
|
49324
49804
|
try {
|
|
49325
|
-
const content = readFileSync24(
|
|
49805
|
+
const content = readFileSync24(resolve13(cwd, lockfile));
|
|
49326
49806
|
return shortFingerprint(content);
|
|
49327
49807
|
} catch {
|
|
49328
49808
|
continue;
|
|
@@ -49475,7 +49955,7 @@ function extractFirstPathArg(cmd, cwd, fallback) {
|
|
|
49475
49955
|
const token2 = tokens[i];
|
|
49476
49956
|
if (!token2.startsWith("-")) {
|
|
49477
49957
|
if (!token2.startsWith("/")) {
|
|
49478
|
-
return
|
|
49958
|
+
return resolve13(cwd, token2);
|
|
49479
49959
|
}
|
|
49480
49960
|
return token2;
|
|
49481
49961
|
}
|
|
@@ -50023,7 +50503,7 @@ function checkDbExists(dataDir2) {
|
|
|
50023
50503
|
return {
|
|
50024
50504
|
name: "Database",
|
|
50025
50505
|
status: "ok",
|
|
50026
|
-
message: `global.db exists (${toKB(sizeBytes)} KB)`
|
|
50506
|
+
message: `global.db exists (${toKB(sizeBytes)} KB) at ${dbPath}`
|
|
50027
50507
|
};
|
|
50028
50508
|
}
|
|
50029
50509
|
function checkSymbolBodySize(dbPath) {
|
|
@@ -50071,6 +50551,13 @@ function checkSymbolCount(dbPath, rootDir) {
|
|
|
50071
50551
|
message: `${fileCount} file(s) indexed but 0 symbols extracted \u2014 the parser may not be running (check the worker log); try 'token-goat index --force'`
|
|
50072
50552
|
};
|
|
50073
50553
|
}
|
|
50554
|
+
if (fileCount === 0 && symbolCount === 0) {
|
|
50555
|
+
return {
|
|
50556
|
+
name: "Symbols",
|
|
50557
|
+
status: "warn",
|
|
50558
|
+
message: `no files indexed for this project \u2014 every read command will return empty, which looks like a genuine "not found" rather than a missing index; run 'token-goat index .' here`
|
|
50559
|
+
};
|
|
50560
|
+
}
|
|
50074
50561
|
return {
|
|
50075
50562
|
name: "Symbols",
|
|
50076
50563
|
status: "ok",
|
|
@@ -72393,7 +72880,7 @@ init_image_shrink();
|
|
|
72393
72880
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
72394
72881
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
72395
72882
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
72396
|
-
return new Promise((
|
|
72883
|
+
return new Promise((resolve25, reject) => {
|
|
72397
72884
|
const chunks = [];
|
|
72398
72885
|
let totalBytes = 0;
|
|
72399
72886
|
let settled = false;
|
|
@@ -72428,7 +72915,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
72428
72915
|
return;
|
|
72429
72916
|
}
|
|
72430
72917
|
try {
|
|
72431
|
-
|
|
72918
|
+
resolve25(JSON.parse(text));
|
|
72432
72919
|
} catch (err2) {
|
|
72433
72920
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
72434
72921
|
}
|
|
@@ -73878,15 +74365,19 @@ var BRIDGE_CAPABILITY_MATRIX = [
|
|
|
73878
74365
|
harness: "copilot_cli",
|
|
73879
74366
|
label: "Copilot CLI",
|
|
73880
74367
|
sourceFile: "src/bridges/copilot_cli_install.ts (COPILOT_CLI_HOOK_EVENTS), src/bridges/copilot_cli.ts (COPILOT_TO_TG_EVENT)",
|
|
73881
|
-
implemented: /* @__PURE__ */ new Set([
|
|
74368
|
+
implemented: /* @__PURE__ */ new Set([
|
|
74369
|
+
"session_start",
|
|
74370
|
+
"pre_tool_use",
|
|
74371
|
+
"post_tool_use",
|
|
74372
|
+
"pre_compact",
|
|
74373
|
+
"stop",
|
|
74374
|
+
"subagent_stop",
|
|
74375
|
+
"user_prompt_submit"
|
|
74376
|
+
]),
|
|
73882
74377
|
reasons: [
|
|
73883
74378
|
{
|
|
73884
74379
|
events: ["notification"],
|
|
73885
74380
|
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"
|
|
73886
|
-
},
|
|
73887
|
-
{
|
|
73888
|
-
events: ["session_start"],
|
|
73889
|
-
reason: "COPILOT_TO_TG_EVENT has no session-start mapping wired yet -- left unimplemented rather than guessed at"
|
|
73890
74381
|
}
|
|
73891
74382
|
]
|
|
73892
74383
|
},
|
|
@@ -78722,7 +79213,7 @@ import { existsSync as existsSync34, readdirSync as readdirSync20, statSync as s
|
|
|
78722
79213
|
import * as http from "http";
|
|
78723
79214
|
import * as https from "https";
|
|
78724
79215
|
import { isIPv4, isIPv6 } from "net";
|
|
78725
|
-
import { resolve as
|
|
79216
|
+
import { resolve as resolve18, join as join41 } from "path";
|
|
78726
79217
|
import { URL as URL2 } from "url";
|
|
78727
79218
|
import { promisify } from "util";
|
|
78728
79219
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -78894,7 +79385,7 @@ function cleanupStaleDownloads() {
|
|
|
78894
79385
|
const files = readdirSync20(cacheDir);
|
|
78895
79386
|
for (const file2 of files) {
|
|
78896
79387
|
if (file2.endsWith(".tmp")) {
|
|
78897
|
-
const filePath =
|
|
79388
|
+
const filePath = resolve18(cacheDir, file2);
|
|
78898
79389
|
try {
|
|
78899
79390
|
const stat2 = statSync27(filePath);
|
|
78900
79391
|
if (Date.now() - stat2.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -81171,6 +81662,9 @@ function runHintStatsCommand(opts = {}) {
|
|
|
81171
81662
|
`);
|
|
81172
81663
|
return;
|
|
81173
81664
|
}
|
|
81665
|
+
if (rows.every((r) => r.emitted === 0 && r.actedOn === 0)) {
|
|
81666
|
+
process.stdout.write("No hint emissions recorded yet \u2014 the zeros below are absence of data, not measured ineffectiveness.\n");
|
|
81667
|
+
}
|
|
81174
81668
|
printSummary(rows);
|
|
81175
81669
|
}
|
|
81176
81670
|
|
|
@@ -81429,7 +81923,11 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
81429
81923
|
function cmdMap(opts) {
|
|
81430
81924
|
const map3 = buildProjectMap(process.cwd(), { compact: opts.compact === true });
|
|
81431
81925
|
const text = formatProjectMap(map3, map3.compact);
|
|
81432
|
-
|
|
81926
|
+
if (opts.json === true) {
|
|
81927
|
+
out(JSON.stringify(map3));
|
|
81928
|
+
} else {
|
|
81929
|
+
out(text);
|
|
81930
|
+
}
|
|
81433
81931
|
const bytesSaved = mapLookupBytesSaved(map3, text);
|
|
81434
81932
|
recordStat("map_lookup", bytesSaved, Math.round(bytesSaved / 4));
|
|
81435
81933
|
}
|
|
@@ -81471,8 +81969,8 @@ async function cmdMcpServe() {
|
|
|
81471
81969
|
const server = createMcpServer2();
|
|
81472
81970
|
const transport = new StdioServerTransport();
|
|
81473
81971
|
await server.connect(transport);
|
|
81474
|
-
await new Promise((
|
|
81475
|
-
server.server.onclose =
|
|
81972
|
+
await new Promise((resolve25) => {
|
|
81973
|
+
server.server.onclose = resolve25;
|
|
81476
81974
|
});
|
|
81477
81975
|
}
|
|
81478
81976
|
async function cmdHook(event, opts) {
|
|
@@ -81679,6 +82177,14 @@ async function cmdDoctor(opts) {
|
|
|
81679
82177
|
if (project !== null) {
|
|
81680
82178
|
doctorOpts.rootDir = project.root;
|
|
81681
82179
|
}
|
|
82180
|
+
if (opts.json === true) {
|
|
82181
|
+
const results = runDoctor(doctorOpts.dataDir, doctorOpts.configPath, doctorOpts.rootDir);
|
|
82182
|
+
out(JSON.stringify(results));
|
|
82183
|
+
if (results.some((r) => r.status === "fail")) {
|
|
82184
|
+
throw new CliError("doctor checks failed");
|
|
82185
|
+
}
|
|
82186
|
+
return;
|
|
82187
|
+
}
|
|
81682
82188
|
const code = await runDoctorAndExit(doctorOpts);
|
|
81683
82189
|
if (code !== 0) {
|
|
81684
82190
|
throw new CliError("doctor checks failed");
|
|
@@ -82182,6 +82688,16 @@ function runExitText(fn) {
|
|
|
82182
82688
|
process.exitCode = 1;
|
|
82183
82689
|
}
|
|
82184
82690
|
}
|
|
82691
|
+
function noteExtraFileArgs(command, first2, extras, fn) {
|
|
82692
|
+
const result = fn();
|
|
82693
|
+
if (extras === void 0 || extras.length === 0) return result;
|
|
82694
|
+
return { text: `${extraFileArgsNote(command, first2, extras)}
|
|
82695
|
+
${result.text}`, code: result.code };
|
|
82696
|
+
}
|
|
82697
|
+
function emitExtraFileArgsNote(command, first2, extras) {
|
|
82698
|
+
if (extras === void 0 || extras.length === 0) return;
|
|
82699
|
+
out(extraFileArgsNote(command, first2, extras));
|
|
82700
|
+
}
|
|
82185
82701
|
function cmdCompress(opts) {
|
|
82186
82702
|
try {
|
|
82187
82703
|
if (opts.compress === false) {
|
|
@@ -82306,6 +82822,10 @@ async function cmdSkillList(opts) {
|
|
|
82306
82822
|
return `${s.name.padEnd(25)} ${bodyKb.padStart(6)}K ${compactKb.padStart(6)}K ${marker} ${s.hitCount.toString().padStart(3)} ${age.padStart(3)} ${staleStatus}`;
|
|
82307
82823
|
});
|
|
82308
82824
|
const header = `${"Name".padEnd(25)} ${"Body".padStart(6)} ${"Compact".padStart(6)} Marker Hits Age Status`;
|
|
82825
|
+
if (skills.length === 0) {
|
|
82826
|
+
out("No skills cached yet.");
|
|
82827
|
+
return;
|
|
82828
|
+
}
|
|
82309
82829
|
out([header, ...lines2].join("\n"));
|
|
82310
82830
|
}
|
|
82311
82831
|
}
|
|
@@ -82684,7 +83204,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
82684
83204
|
throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
|
|
82685
83205
|
}
|
|
82686
83206
|
const maxBytes = maxMB * 1024 * 1024;
|
|
82687
|
-
return new Promise((
|
|
83207
|
+
return new Promise((resolve25, reject) => {
|
|
82688
83208
|
const chunks = [];
|
|
82689
83209
|
let totalBytes = 0;
|
|
82690
83210
|
let settled = false;
|
|
@@ -82708,7 +83228,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
82708
83228
|
try {
|
|
82709
83229
|
atomicWriteBuffer(dest, Buffer.concat(chunks));
|
|
82710
83230
|
enqueueDirtyPathSafe(dest);
|
|
82711
|
-
|
|
83231
|
+
resolve25();
|
|
82712
83232
|
} catch (e) {
|
|
82713
83233
|
try {
|
|
82714
83234
|
mapFsError(e, void 0, dest);
|
|
@@ -83143,19 +83663,26 @@ function buildProgram() {
|
|
|
83143
83663
|
process.exitCode = 1;
|
|
83144
83664
|
}
|
|
83145
83665
|
};
|
|
83146
|
-
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(
|
|
83147
|
-
|
|
83666
|
+
program2.command("symbol <name>").description("search for a symbol by name").option("-l, --limit <n>", "max results").option("-f, --file <path>", "restrict to one file").option("-k, --kind <kind>", "restrict to one kind (function, class, ...)").option("-p, --project [path]", "scope search to one project root instead of the global index (defaults to cwd)").option("-j, --json", "output as JSON").action((name2, opts) => {
|
|
83667
|
+
let projectRoot;
|
|
83668
|
+
if (opts.project === true) {
|
|
83669
|
+
projectRoot = resolveProjectRoot({ project: process.cwd() });
|
|
83670
|
+
} else if (typeof opts.project === "string") {
|
|
83671
|
+
projectRoot = resolveProjectRoot({ project: opts.project });
|
|
83672
|
+
}
|
|
83673
|
+
return runExitText(
|
|
83148
83674
|
() => runSymbol({
|
|
83149
83675
|
name: name2,
|
|
83150
83676
|
limit: opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20,
|
|
83151
83677
|
...opts.file !== void 0 ? { file: opts.file } : {},
|
|
83152
83678
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
83679
|
+
...projectRoot !== void 0 ? { projectRoot } : {},
|
|
83153
83680
|
...opts.json === true ? { json: true } : {}
|
|
83154
83681
|
})
|
|
83155
|
-
)
|
|
83156
|
-
);
|
|
83682
|
+
);
|
|
83683
|
+
});
|
|
83157
83684
|
program2.command("read <spec>").description(
|
|
83158
|
-
"read one symbol's full body (spec: file::symbol; disambiguate a name shared by several classes with file::Parent.symbol; comma-separated file::a,b for a merged multi-symbol view, or a::x,b::y to merge symbols across several files)"
|
|
83685
|
+
"read one symbol's full body (spec: file::symbol; disambiguate a name shared by several classes with file::Parent.symbol; a trailing @LINE anchor -- file::symbol@LINE, or combined as file::Parent.symbol@LINE -- picks out a specific candidate by its exact starting line, for the case a Parent qualifier can't reach (e.g. a top-level definition); comma-separated file::a,b for a merged multi-symbol view, or a::x,b::y to merge symbols across several files)"
|
|
83159
83686
|
).option("-j, --json", "output as JSON").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
83160
83687
|
(spec, opts) => runExitText(
|
|
83161
83688
|
() => runRead({
|
|
@@ -83167,13 +83694,14 @@ function buildProgram() {
|
|
|
83167
83694
|
)
|
|
83168
83695
|
);
|
|
83169
83696
|
program2.command("brief <spec>").description(
|
|
83170
|
-
"symbol body + callers + containing doc section in one call (spec: file::symbol; comma-separated file::a,b for a merged multi-symbol view)"
|
|
83171
|
-
).option("-j, --json", "output as JSON").option("--limit <n>", "max callers to show (default: 20)").action(
|
|
83697
|
+
"symbol body + callers + containing doc section in one call (spec: file::symbol; also accepts the file::symbol@LINE anchor form documented under `read`; comma-separated file::a,b for a merged multi-symbol view; cross-file a.ts::x,b.ts::y is also supported)"
|
|
83698
|
+
).option("-j, --json", "output as JSON").option("--limit <n>", "max callers to show (default: 20)").option("-C, --context <n>", "lines of call-site source to show before and after each caller (default 0)").action(
|
|
83172
83699
|
(spec, opts) => runExit(
|
|
83173
83700
|
() => runBrief({
|
|
83174
83701
|
spec,
|
|
83175
83702
|
...opts.json === true ? { json: true } : {},
|
|
83176
|
-
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {}
|
|
83703
|
+
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
83704
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {}
|
|
83177
83705
|
})
|
|
83178
83706
|
)
|
|
83179
83707
|
);
|
|
@@ -83183,44 +83711,56 @@ function buildProgram() {
|
|
|
83183
83711
|
(spec, opts) => opts.list === true ? runExit(() => runListSections({ file: spec, ...opts.json === true ? { json: true } : {} })) : runExitText(() => runSection({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
83184
83712
|
);
|
|
83185
83713
|
program2.command("semantic <query>").description("semantic search (falls back to full-text search)").option("-l, --limit <n>", "max results").option("-j, --json", "output as JSON").action(guard(cmdSemantic));
|
|
83186
|
-
program2.command("skeleton <file>").description(
|
|
83187
|
-
(file2, opts) => runExitText(
|
|
83188
|
-
() =>
|
|
83189
|
-
|
|
83190
|
-
|
|
83191
|
-
|
|
83192
|
-
|
|
83193
|
-
|
|
83194
|
-
|
|
83714
|
+
program2.command("skeleton <file> [more...]").description('list all symbols in a file without bodies (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").option("--min-lines <n>", "only show symbols at least N lines long").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
83715
|
+
(file2, more, opts) => runExitText(
|
|
83716
|
+
() => noteExtraFileArgs(
|
|
83717
|
+
"skeleton",
|
|
83718
|
+
file2,
|
|
83719
|
+
more,
|
|
83720
|
+
() => runSkeleton({
|
|
83721
|
+
file: file2,
|
|
83722
|
+
...opts.json === true ? { json: true } : {},
|
|
83723
|
+
...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
|
|
83724
|
+
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
83725
|
+
...opts.stats === true ? { stats: true } : {}
|
|
83726
|
+
})
|
|
83727
|
+
)
|
|
83195
83728
|
)
|
|
83196
83729
|
);
|
|
83197
|
-
program2.command("outline <file>").description(
|
|
83198
|
-
(file2, opts) => runExitText(
|
|
83199
|
-
() =>
|
|
83200
|
-
|
|
83201
|
-
|
|
83202
|
-
|
|
83203
|
-
|
|
83204
|
-
|
|
83205
|
-
|
|
83730
|
+
program2.command("outline <file> [more...]").description('list symbols with line ranges and docstrings (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").option("--min-lines <n>", "only show symbols at least N lines long").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
83731
|
+
(file2, more, opts) => runExitText(
|
|
83732
|
+
() => noteExtraFileArgs(
|
|
83733
|
+
"outline",
|
|
83734
|
+
file2,
|
|
83735
|
+
more,
|
|
83736
|
+
() => runOutline({
|
|
83737
|
+
file: file2,
|
|
83738
|
+
...opts.json === true ? { json: true } : {},
|
|
83739
|
+
...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
|
|
83740
|
+
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
83741
|
+
...opts.stats === true ? { stats: true } : {}
|
|
83742
|
+
})
|
|
83743
|
+
)
|
|
83206
83744
|
)
|
|
83207
83745
|
);
|
|
83208
|
-
program2.command("refs <spec>").description("find references to one or more symbols (spec: file::symbol, symbol, or comma-separated a,b,c / file::a,b for a merged multi-symbol view). For an unambiguous TypeScript symbol, automatically type-resolves candidates via the TypeScript compiler API to drop same-named-different-symbol false positives; falls back to name-based matching when that is not possible.").option("--callers", "group references by their enclosing caller symbol").option("-l, --limit <n>", "max results").option(
|
|
83746
|
+
program2.command("refs <spec>").description("find references to one or more symbols (spec: file::symbol, symbol, or comma-separated a,b,c / file::a,b for a merged multi-symbol view; cross-file a.ts::x,b.ts::y is also supported). For an unambiguous TypeScript symbol, automatically type-resolves candidates via the TypeScript compiler API to drop same-named-different-symbol false positives; falls back to name-based matching when that is not possible.").option("--callers", "group references by their enclosing caller symbol").option("-l, --limit <n>", "max results").option(
|
|
83209
83747
|
"--top <n>",
|
|
83210
83748
|
"for a high-fanout symbol, group references by file (count only) and show only the top N files by reference count instead of a per-line dump"
|
|
83211
|
-
).option("-j, --json", "output as JSON").action(
|
|
83749
|
+
).option("-C, --context <n>", "lines of call-site source to show before and after each reference (default 0)").option("-j, --json", "output as JSON").option("--exclude-tests", "hide references whose call site lives in a test file (opt-in; default output is unchanged)").action(
|
|
83212
83750
|
(spec, opts) => runExit(
|
|
83213
83751
|
() => runRefs({
|
|
83214
83752
|
spec,
|
|
83215
83753
|
...opts.callers === true ? { callers: true } : {},
|
|
83216
83754
|
...opts.json === true ? { json: true } : {},
|
|
83217
83755
|
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
83218
|
-
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {}
|
|
83756
|
+
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
83757
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
83758
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
83219
83759
|
})
|
|
83220
83760
|
)
|
|
83221
83761
|
);
|
|
83222
83762
|
program2.command("index [path]").description("parse all git-tracked files and (re)build the symbol index").option("--walk", "if not a git repo, index a bounded directory walk instead (skips .env / generated / oversized trees)").option("--force", "bypass the SHA-freshness cache and reindex every tracked file, even byte-identical ones (e.g. after a parser upgrade changes what gets extracted)").option("--force-walk", `index a non-git folder via --walk and raise its ${MAX_FILES_SCANNED} source-file refusal to ${MAX_FILES_SCANNED_FORCED} (slow; produces a large index)`).action(guard(cmdIndex));
|
|
83223
|
-
program2.command("map").description("project overview").option("-c, --compact", "compact, low-token summary").action(guard(cmdMap));
|
|
83763
|
+
program2.command("map").description("project overview").option("-c, --compact", "compact, low-token summary").option("--json", "emit the project map as JSON instead of text").action(guard(cmdMap));
|
|
83224
83764
|
program2.command("bridges-status").description("hook-event parity matrix across every AI-harness bridge (read-only static analysis, never invokes a real harness binary)").option("--json", "emit the matrix as JSON instead of text").action(guard(cmdBridgesStatus));
|
|
83225
83765
|
program2.command("commands").description("machine-readable manifest of every registered command, its options, and its arguments").option("--json", "emit the manifest as JSON instead of text").option("--grep <pattern>", "filter to commands whose name, description, or aliases match this regex").action(guard(cmdCommands));
|
|
83226
83766
|
program2.command("mcp-serve").description("run token-goat as an MCP stdio server exposing surgical reads and local compression/handoff tools").action(guard(cmdMcpServe));
|
|
@@ -83236,7 +83776,7 @@ function buildProgram() {
|
|
|
83236
83776
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
83237
83777
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
83238
83778
|
program2.command("stats").description("show session statistics (bare = totals only; --full for the breakdown)").option("-j, --json", "output as JSON").option("--full", "show the full breakdown (by source, by command, by day)").option("--short", "force the rich short KPI view even when stdout is not a TTY (e.g. piped)").option("--window-days <days>", "days to include (0 = all time)", "30").option("--home-dir <path>", "home directory (for testing)").action(guard(cmdStats));
|
|
83239
|
-
program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").action(guard(cmdDoctor));
|
|
83779
|
+
program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").option("--json", "emit check results as JSON instead of text").action(guard(cmdDoctor));
|
|
83240
83780
|
program2.command("context-stats").description("show context statistics").option("--project <path>", "project root to analyze").option("-j, --json", "output as JSON").option("--fix", "apply automatic fixes (confirm-gated; shows a diff before writing)").option("-y, --yes", "with --fix, apply without prompting (non-interactive / scripted use)").action(guard(cmdContextStats));
|
|
83241
83781
|
program2.command("bootstrap-audit").description("audit Claude Code startup-context contributors without reading prompt bodies").option("--project <path>", "project root to analyze").option("--home <path>", "home directory override (for CI/testing)").option("--follow-links", "follow external symlink/junction roots and direct children").option("-j, --json", "output as JSON").option("--top <n>", "largest metadata entries to show (default 10)", "10").option("--warn-tokens <n>", "warn when total estimated startup tokens exceed n").option("--fail-tokens <n>", "fail when total estimated startup tokens exceed n").option("--warn-bytes <n>", "warn when agent/skill metadata bytes exceed n").option("--fail-bytes <n>", "fail when agent/skill metadata bytes exceed n").action(guard(cmdBootstrapAudit));
|
|
83242
83782
|
program2.command("memory").description("analyze CLAUDE.md files for duplicate/overlapping content (--fix to apply safe mechanical fixes)").option("--project <path>", "project root to analyze").option("--analyze", "report-only analysis (default)").option("--fix", "remove exact-duplicate lines (confirm-gated; shows a diff before writing)").option("--yes", "apply --fix changes without prompting (non-interactive)").action(guard(cmdMemory));
|
|
@@ -83250,11 +83790,17 @@ function buildProgram() {
|
|
|
83250
83790
|
program2.command("bash-output [id]").description("retrieve cached bash output by ID or file").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the output").option("--full", "print the entire cached entry with no head/tail elision").option("--file <path>", "read from raw output file instead of cache").option("--transcript", "parse the --file as a JSONL agent transcript: keep assistant text blocks in order before filtering").action(guard(cmdBashOutput));
|
|
83251
83791
|
program2.command("web-output [id]").description("retrieve a cached WebFetch response body by ID").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the response").option("--full", "print the entire cached entry with no head/tail elision").action(guard(cmdWebOutput));
|
|
83252
83792
|
program2.command("mcp-output [id]").description("retrieve a cached MCP tool result by ID (the id an MCP post_tool_use hook cached, or a `[token-goat: compressed, full via mcp-output <id>]` label points here)").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the result").option("--full", "print the entire cached entry with no head/tail elision").action(guard(cmdMcpOutput));
|
|
83253
|
-
program2.command("exports <file>").description(
|
|
83254
|
-
(file2, opts) => runExit(() =>
|
|
83793
|
+
program2.command("exports <file> [more...]").description('list exported (public) symbols in a file (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").action(
|
|
83794
|
+
(file2, more, opts) => runExit(() => {
|
|
83795
|
+
emitExtraFileArgsNote("exports", file2, more);
|
|
83796
|
+
return runExports({ file: file2, ...opts.json === true ? { json: true } : {} });
|
|
83797
|
+
})
|
|
83255
83798
|
);
|
|
83256
|
-
program2.command("imports <file>").description(
|
|
83257
|
-
(file2, opts) => runExit(() =>
|
|
83799
|
+
program2.command("imports <file> [more...]").description('list the modules a file imports (also accepts a comma-separated file list "a,b,c" for one headed block per file)').option("-j, --json", "output as JSON").action(
|
|
83800
|
+
(file2, more, opts) => runExit(() => {
|
|
83801
|
+
emitExtraFileArgsNote("imports", file2, more);
|
|
83802
|
+
return runImports({ file: file2, ...opts.json === true ? { json: true } : {} });
|
|
83803
|
+
})
|
|
83258
83804
|
);
|
|
83259
83805
|
program2.command("find <pattern>").description("find files containing a symbol matching a pattern").option("-j, --json", "output as JSON").option("-l, --limit <n>", "max results").action(
|
|
83260
83806
|
(pattern, opts) => runExit(
|
|
@@ -83265,7 +83811,7 @@ function buildProgram() {
|
|
|
83265
83811
|
})
|
|
83266
83812
|
)
|
|
83267
83813
|
);
|
|
83268
|
-
program2.command("grep <pattern> [paths...]").description("regex search over files, caching nothing (session-aware grep)").option("-j, --json", "output as JSON").option("--max-lines <n>", "max matching lines to print").option("--no-recursive", "do not descend into subdirectories").option("-C, --context <n>", "lines of context to show before and after each match").action(
|
|
83814
|
+
program2.command("grep <pattern> [paths...]").description("regex search over files, caching nothing (session-aware grep)").option("-j, --json", "output as JSON").option("--max-lines <n>", "max matching lines to print").option("--no-recursive", "do not descend into subdirectories").option("-C, --context <n>", "lines of context to show before and after each match").option("--symbol", "annotate each hit with its enclosing symbol (name and kind)").action(
|
|
83269
83815
|
(pattern, paths, opts) => runExit(
|
|
83270
83816
|
() => runGrep({
|
|
83271
83817
|
pattern,
|
|
@@ -83273,7 +83819,8 @@ function buildProgram() {
|
|
|
83273
83819
|
...opts.json === true ? { json: true } : {},
|
|
83274
83820
|
...opts.maxLines !== void 0 ? { maxLines: requirePositiveInt("--max-lines", opts.maxLines) } : {},
|
|
83275
83821
|
...opts.recursive === false ? { recursive: false } : {},
|
|
83276
|
-
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {}
|
|
83822
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
83823
|
+
...opts.symbol === true ? { symbol: true } : {}
|
|
83277
83824
|
})
|
|
83278
83825
|
)
|
|
83279
83826
|
);
|
|
@@ -83284,16 +83831,18 @@ function buildProgram() {
|
|
|
83284
83831
|
program2.command("skill-history").description("list cached skill versions newest-first").option("-j, --json", "output as JSON").action(guard(cmdSkillHistory));
|
|
83285
83832
|
program2.command("skill-diff <name>").description("show diff between two cached versions of a skill").action(guard(cmdSkillDiff));
|
|
83286
83833
|
program2.command("skill-section <nameHeading> [headingArg]").description("extract a named section from a skill").action(guard(cmdSkillSection));
|
|
83287
|
-
program2.command("callers <symbol>").description("find all callers of a symbol, resolved to their enclosing function").option("-j, --json", "output as JSON").option("-l, --limit <n>", "max references to scan").action(
|
|
83834
|
+
program2.command("callers <symbol>").description("find all callers of a symbol, resolved to their enclosing function (accepts file::symbol to disambiguate which same-named definition is meant)").option("-j, --json", "output as JSON").option("-l, --limit <n>", "max references to scan").option("-C, --context <n>", "lines of call-site source to show before and after each caller (default 0)").option("--exclude-tests", "hide callers whose call site lives in a test file (opt-in; default output is unchanged)").action(
|
|
83288
83835
|
(symbol3, opts) => runExit(
|
|
83289
83836
|
() => runCallers({
|
|
83290
83837
|
symbol: symbol3,
|
|
83291
83838
|
...opts.json === true ? { json: true } : {},
|
|
83292
|
-
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {}
|
|
83839
|
+
...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
|
|
83840
|
+
...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
|
|
83841
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
83293
83842
|
})
|
|
83294
83843
|
)
|
|
83295
83844
|
);
|
|
83296
|
-
program2.command("call-chain <symbol>").description("transitive callers up toward entry points (BFS, cycle-safe)").option("-d, --depth <n>", "max BFS depth (default 8)").option("-j, --json", "output as JSON").action(
|
|
83845
|
+
program2.command("call-chain <symbol>").description("transitive callers up toward entry points (BFS, cycle-safe; accepts file::symbol to disambiguate which same-named definition is meant)").option("-d, --depth <n>", "max BFS depth (default 8)").option("-j, --json", "output as JSON").action(
|
|
83297
83846
|
(symbol3, opts) => runExit(
|
|
83298
83847
|
() => runCallChain({
|
|
83299
83848
|
symbol: symbol3,
|
|
@@ -83302,7 +83851,7 @@ function buildProgram() {
|
|
|
83302
83851
|
})
|
|
83303
83852
|
)
|
|
83304
83853
|
);
|
|
83305
|
-
program2.command("impact <symbol>").description("transitive set of callers impacted by a change (with hop depth)").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").action(
|
|
83854
|
+
program2.command("impact <symbol>").description("transitive set of callers impacted by a change (with hop depth; accepts file::symbol to disambiguate which same-named definition is meant)").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").action(
|
|
83306
83855
|
(symbol3, opts) => runExit(
|
|
83307
83856
|
() => runImpact({
|
|
83308
83857
|
symbol: symbol3,
|
|
@@ -83311,13 +83860,14 @@ function buildProgram() {
|
|
|
83311
83860
|
})
|
|
83312
83861
|
)
|
|
83313
83862
|
);
|
|
83314
|
-
program2.command("dead").description("symbols with zero references (default kind: function)").option("-k, --kind <kind>", "symbol kind to check (function, method, class, ...)").option("--include-private", "include _-prefixed names").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").action(
|
|
83863
|
+
program2.command("dead").description("symbols with zero references (default kind: function)").option("-k, --kind <kind>", "symbol kind to check (function, method, class, ...)").option("--include-private", "include _-prefixed names").option("--top <n>", "limit output to top N results").option("-j, --json", "output as JSON").option("--exclude-tests", "hide dead symbols defined in a test file (opt-in; default output is unchanged)").action(
|
|
83315
83864
|
(opts) => runExit(
|
|
83316
83865
|
() => runDead({
|
|
83317
83866
|
...opts.kind !== void 0 ? { kind: opts.kind } : {},
|
|
83318
83867
|
...opts.includePrivate === true ? { includePrivate: true } : {},
|
|
83319
83868
|
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
83320
|
-
...opts.json === true ? { json: true } : {}
|
|
83869
|
+
...opts.json === true ? { json: true } : {},
|
|
83870
|
+
...opts.excludeTests === true ? { excludeTests: true } : {}
|
|
83321
83871
|
})
|
|
83322
83872
|
)
|
|
83323
83873
|
);
|
|
@@ -83336,7 +83886,7 @@ function buildProgram() {
|
|
|
83336
83886
|
program2.command("scope <fileColonLine>").description("list symbols enclosing a file:line position, innermost first").option("-j, --json", "output as JSON").action(
|
|
83337
83887
|
(spec, opts) => runExit(() => runScope({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
83338
83888
|
);
|
|
83339
|
-
program2.command("similar <spec>").description('find symbols similar to a given "file::symbol" anchor using FTS').option("--top <n>", "max results (default 10)").option("-j, --json", "output as JSON").action(
|
|
83889
|
+
program2.command("similar <spec>").description('find symbols similar to a given "file::symbol" anchor using FTS (also accepts the file::symbol@LINE anchor form documented under `read`)').option("--top <n>", "max results (default 10)").option("-j, --json", "output as JSON").action(
|
|
83340
83890
|
(spec, opts) => runExit(
|
|
83341
83891
|
() => runSimilar({
|
|
83342
83892
|
spec,
|
|
@@ -83375,7 +83925,7 @@ function buildProgram() {
|
|
|
83375
83925
|
})
|
|
83376
83926
|
)
|
|
83377
83927
|
);
|
|
83378
|
-
program2.command("blame <spec>").description('git blame for the line range of a symbol ("file::symbol")').option("-j, --json", "output as JSON").action(
|
|
83928
|
+
program2.command("blame <spec>").description('git blame for the line range of a symbol ("file::symbol"; also accepts the file::symbol@LINE anchor form documented under `read`)').option("-j, --json", "output as JSON").action(
|
|
83379
83929
|
(spec, opts) => runExit(() => runBlame({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
83380
83930
|
);
|
|
83381
83931
|
program2.command("ask <question>").description("(experimental) find relevant code context; synthesize with an LLM if TOKEN_GOAT_ASK_BACKEND is set").option("--top <n>", "max FTS hits to surface (default 8)").option("-j, --json", "output as JSON").action(
|
|
@@ -83459,16 +84009,16 @@ function buildProgram() {
|
|
|
83459
84009
|
}))());
|
|
83460
84010
|
program2.command("fetch-image <url>").description("fetch an image URL and shrink it (saves to --out path or a temp file)").option("--out <path>", "output file path").option("-j, --json", "output as JSON").action((url2, opts) => guard(() => cmdFetchImage({ url: url2, ...opts.out !== void 0 ? { out: opts.out } : {}, ...opts.json === true ? { json: true } : {} }))());
|
|
83461
84011
|
program2.command("history").description("show recent session history: bash commands and web fetches (current-session or recent cache)").option("--limit <n>", "max entries to show (default: 30)").option("-j, --json", "output as JSON").action((opts) => guard(() => cmdHistory(opts))());
|
|
83462
|
-
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(
|
|
83463
|
-
(opts) => runExit(
|
|
84012
|
+
program2.command("changed [ref]").description("list files or symbols changed since a git ref").option("--since <ref>", "git ref to compare against (default: HEAD~5)").option("--symbol", "list symbols instead of files").option("-j, --json", "output as JSON").action(
|
|
84013
|
+
(ref2, opts) => runExit(
|
|
83464
84014
|
() => runChanged({
|
|
83465
|
-
ref: opts.since ?? "HEAD~5",
|
|
84015
|
+
ref: opts.since ?? ref2 ?? "HEAD~5",
|
|
83466
84016
|
...opts.symbol === true ? { symbolMode: true } : {},
|
|
83467
84017
|
...opts.json === true ? { json: true } : {}
|
|
83468
84018
|
})
|
|
83469
84019
|
)
|
|
83470
84020
|
);
|
|
83471
|
-
program2.command("diff <spec> [ref]").description('show only the git diff hunk(s) that fall within one symbol\'s line range, e.g. `token-goat diff "file.ts::myFn" HEAD~3..HEAD`').option("-j, --json", "output as JSON").action(
|
|
84021
|
+
program2.command("diff <spec> [ref]").description('show only the git diff hunk(s) that fall within one symbol\'s line range, e.g. `token-goat diff "file.ts::myFn" HEAD~3..HEAD` (also accepts the file::symbol@LINE anchor form documented under `read`)').option("-j, --json", "output as JSON").action(
|
|
83472
84022
|
(spec, ref2, opts) => runExit(
|
|
83473
84023
|
() => runDiff({
|
|
83474
84024
|
spec,
|
|
@@ -83477,7 +84027,7 @@ function buildProgram() {
|
|
|
83477
84027
|
})
|
|
83478
84028
|
)
|
|
83479
84029
|
);
|
|
83480
|
-
program2.command("log <spec> [ref]").description('show git commit history scoped to one symbol\'s line range, e.g. `token-goat log "file.ts::myFn" HEAD~10`').option("--max-count <n>", "maximum number of commits to show (default 20)").option("-j, --json", "output as JSON").action(
|
|
84030
|
+
program2.command("log <spec> [ref]").description('show git commit history scoped to one symbol\'s line range, e.g. `token-goat log "file.ts::myFn" HEAD~10` (also accepts the file::symbol@LINE anchor form documented under `read`)').option("--max-count <n>", "maximum number of commits to show (default 20)").option("-j, --json", "output as JSON").action(
|
|
83481
84031
|
(spec, ref2, opts) => runExit(
|
|
83482
84032
|
() => runLog({
|
|
83483
84033
|
spec,
|
|
@@ -83610,6 +84160,8 @@ async function run2(argv = process.argv) {
|
|
|
83610
84160
|
}
|
|
83611
84161
|
|
|
83612
84162
|
// src/main.ts
|
|
84163
|
+
init_util2();
|
|
84164
|
+
installEpipeGuard();
|
|
83613
84165
|
void run2();
|
|
83614
84166
|
/*! Bundled license information:
|
|
83615
84167
|
|