token-goat 2.8.4 → 2.9.1
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 +20 -8
- package/dist/{token-goat-chunk-NZFTWBGV.mjs → token-goat-chunk-4KD2N46D.mjs} +28 -27
- package/dist/{token-goat-chunk-CZALRRGN.mjs → token-goat-chunk-4ZDRTNGV.mjs} +9942 -95
- package/dist/{token-goat-chunk-L6YP6FKQ.mjs → token-goat-chunk-AUM22UQ2.mjs} +5 -5
- package/dist/{token-goat-chunk-LZOAPGWR.mjs → token-goat-chunk-CD6U5OD6.mjs} +28 -4
- package/dist/{token-goat-chunk-4OTIB7SB.mjs → token-goat-chunk-FDXINYK4.mjs} +2120 -3658
- package/dist/token-goat-chunk-LHMC5ENL.mjs +79 -0
- package/dist/{token-goat-chunk-IZRXU64B.mjs → token-goat-chunk-MR7MNSDR.mjs} +155 -517
- package/dist/{token-goat-chunk-ZQ3PUOP3.mjs → token-goat-chunk-RRNRPCLX.mjs} +167 -116
- package/dist/{token-goat-chunk-U4UI5F3S.mjs → token-goat-chunk-SQSB5P5P.mjs} +842 -368
- package/dist/{token-goat-chunk-RE7S7H26.mjs → token-goat-chunk-SXSWQMNP.mjs} +512 -206
- package/dist/{token-goat-chunk-HNODKNWO.mjs → token-goat-chunk-V5T3YNYR.mjs} +5 -5
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +1 -1
- package/dist/token-goat-chunk-E76UNTVK.mjs +0 -7133
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
AMBIGUOUS_HEADING_LIMIT,
|
|
6
6
|
CONTENT_MAX_INPUT_CHARS,
|
|
7
7
|
WHOLE_FILE_NOTE_SYMBOL,
|
|
8
|
+
buildImportGraph,
|
|
8
9
|
compressText,
|
|
9
10
|
computeFileFingerprint,
|
|
10
11
|
createHandoff,
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
extractTranscriptText,
|
|
16
17
|
filterSimilarHeadings,
|
|
17
18
|
findSpecSeparator,
|
|
18
|
-
getTrackedFiles,
|
|
19
19
|
healStaleIndex,
|
|
20
20
|
isAvailable as isAvailable2,
|
|
21
21
|
listSections,
|
|
@@ -86,42 +86,38 @@ import {
|
|
|
86
86
|
runZipRead,
|
|
87
87
|
symbolNamesInFile,
|
|
88
88
|
upsertNote
|
|
89
|
-
} from "./token-goat-chunk-
|
|
89
|
+
} from "./token-goat-chunk-SXSWQMNP.mjs";
|
|
90
90
|
import {
|
|
91
|
-
|
|
91
|
+
DEFAULT_RECONCILE_BUDGET_MS,
|
|
92
92
|
GEMINI_TOOL_NAME_MAP,
|
|
93
93
|
HOOK_EVENTS,
|
|
94
94
|
WEB_OUTPUT_SUBDIR,
|
|
95
95
|
accumulateResidentLine,
|
|
96
96
|
buildCommandManifest,
|
|
97
97
|
checkSymbolBodySize,
|
|
98
|
-
commandHash,
|
|
99
98
|
createResidentContextStats,
|
|
100
99
|
filterCommandManifest,
|
|
101
100
|
formatBytes,
|
|
102
101
|
formatCommandManifest,
|
|
103
102
|
formatTokenEstimate,
|
|
104
|
-
getBashOutput,
|
|
105
103
|
getWebOutput,
|
|
106
104
|
getWebOutputByUrlFromDisk,
|
|
107
105
|
getWebOutputRaw,
|
|
108
|
-
isRecallCacheType,
|
|
109
|
-
listRecentRecall,
|
|
110
|
-
normalizeCommandForCacheKey,
|
|
111
106
|
readCopilotMcpTools,
|
|
112
107
|
readStdinJson,
|
|
113
|
-
|
|
108
|
+
runReconcile,
|
|
114
109
|
storeWebOutput,
|
|
115
110
|
summarizeResidentContext
|
|
116
|
-
} from "./token-goat-chunk-
|
|
111
|
+
} from "./token-goat-chunk-MR7MNSDR.mjs";
|
|
117
112
|
import {
|
|
118
113
|
AGENT_SALT_MARKER,
|
|
114
|
+
BASH_OUTPUT_SUBDIR,
|
|
119
115
|
CONTEXT_AUTOCOMPACT_TOKENS,
|
|
120
116
|
DEFAULT_MAX_AGE_MS,
|
|
121
117
|
DEFAULT_MAX_COUNT,
|
|
122
|
-
MATERIALIZE_SHRUNK_IMAGE_JS,
|
|
123
118
|
MAX_FILES_SCANNED,
|
|
124
119
|
PARSER_FINGERPRINT,
|
|
120
|
+
RECALL_DEFAULT_LIMIT,
|
|
125
121
|
SESSIONS_SUBDIR,
|
|
126
122
|
SKILLS_OUTPUT_SUBDIR,
|
|
127
123
|
SKIP_DIRS,
|
|
@@ -130,7 +126,7 @@ import {
|
|
|
130
126
|
UNTRUSTED_WEB_TAG,
|
|
131
127
|
WORKER_HEARTBEAT_STALE_MS,
|
|
132
128
|
WorkerAlreadyRunningError,
|
|
133
|
-
|
|
129
|
+
applyIndexingPriority,
|
|
134
130
|
auditClaudeMd,
|
|
135
131
|
buildExtractiveCompact,
|
|
136
132
|
buildLineDiff,
|
|
@@ -138,14 +134,11 @@ import {
|
|
|
138
134
|
buildProjectMap,
|
|
139
135
|
buildStats,
|
|
140
136
|
capJsonRows,
|
|
137
|
+
commandHash,
|
|
141
138
|
compactDoc,
|
|
142
139
|
compactPathFor,
|
|
143
140
|
confirmAndApply,
|
|
144
141
|
contentHash,
|
|
145
|
-
copilotCliConfigPath,
|
|
146
|
-
copilotCliMcpToolsDir,
|
|
147
|
-
copilotCliScriptPath,
|
|
148
|
-
copilotCliUserRoot,
|
|
149
142
|
detectWalkMode,
|
|
150
143
|
dirtyQueuePathFor,
|
|
151
144
|
docxOutline,
|
|
@@ -160,6 +153,7 @@ import {
|
|
|
160
153
|
extractChecklistSection,
|
|
161
154
|
extractCompactFromMarker,
|
|
162
155
|
extractNamedSection,
|
|
156
|
+
fenceUntrusted,
|
|
163
157
|
fenceUntrustedContent,
|
|
164
158
|
fenceUntrustedOcrText,
|
|
165
159
|
findClaudeMdFiles,
|
|
@@ -168,15 +162,16 @@ import {
|
|
|
168
162
|
findMemSuggestionCandidates,
|
|
169
163
|
findMemoryMd,
|
|
170
164
|
findOrphanedChunkPaths,
|
|
171
|
-
findStrayClaudeMdBlocks,
|
|
172
165
|
findSystemTempFiles,
|
|
173
166
|
formatAge,
|
|
174
167
|
formatCsvTable,
|
|
175
168
|
formatMemSuggestions,
|
|
176
169
|
formatProjectMap,
|
|
177
170
|
formatXlsxRange,
|
|
171
|
+
getBashOutput,
|
|
178
172
|
getContextPressure,
|
|
179
173
|
getDirtyPathsFor,
|
|
174
|
+
getEmbeddingCoverage,
|
|
180
175
|
getFileEntry,
|
|
181
176
|
getHintStatsSummary,
|
|
182
177
|
getHintStatsTotals,
|
|
@@ -184,34 +179,24 @@ import {
|
|
|
184
179
|
getSessionFiles,
|
|
185
180
|
getSessionId,
|
|
186
181
|
getSkillFilePath,
|
|
187
|
-
|
|
182
|
+
getTrackedFiles,
|
|
188
183
|
headSheet,
|
|
189
184
|
incrementSkillHit,
|
|
190
185
|
indexFileEmbeddings,
|
|
191
186
|
indexFileSync,
|
|
192
187
|
indexedPathSpellingIsStale,
|
|
193
|
-
|
|
194
|
-
installCodex,
|
|
195
|
-
installCopilotCli,
|
|
196
|
-
installGrok,
|
|
197
|
-
installHooks,
|
|
198
|
-
installKimi,
|
|
199
|
-
installSkill,
|
|
200
|
-
installVscode,
|
|
188
|
+
injectionFencingEnabled,
|
|
201
189
|
isAvailable,
|
|
202
|
-
isCodexInstalled,
|
|
203
190
|
isCompactFresh,
|
|
204
|
-
isCopilotCliInstalled,
|
|
205
191
|
isEmbedFresh,
|
|
206
192
|
isEmbeddableDocument,
|
|
207
|
-
isGrokInstalled,
|
|
208
193
|
isHintCategory,
|
|
209
|
-
isInstalled,
|
|
210
|
-
isKimiInstalled,
|
|
211
194
|
isParseSkipEligible,
|
|
195
|
+
isRecallCacheType,
|
|
212
196
|
isWorkerRunning,
|
|
213
197
|
listBlobs,
|
|
214
198
|
listOutputs,
|
|
199
|
+
listRecentRecall,
|
|
215
200
|
listSheets,
|
|
216
201
|
listSkills,
|
|
217
202
|
loadBlob,
|
|
@@ -219,7 +204,7 @@ import {
|
|
|
219
204
|
mapLookupBytesSaved,
|
|
220
205
|
markCategoryEffective,
|
|
221
206
|
markCategoryIneffective,
|
|
222
|
-
|
|
207
|
+
normalizeCommandForCacheKey,
|
|
223
208
|
parseWhereSpecs,
|
|
224
209
|
pptxNotesText,
|
|
225
210
|
pptxOutline,
|
|
@@ -239,7 +224,8 @@ import {
|
|
|
239
224
|
resetHintStats,
|
|
240
225
|
runContextStats,
|
|
241
226
|
runDetachedWorkerDaemon,
|
|
242
|
-
|
|
227
|
+
scanAndRecord,
|
|
228
|
+
searchRecall,
|
|
243
229
|
shrinkImage,
|
|
244
230
|
skillOutputsDir,
|
|
245
231
|
startDetachedWorker,
|
|
@@ -248,33 +234,32 @@ import {
|
|
|
248
234
|
storeOutput,
|
|
249
235
|
tokenGoatHome,
|
|
250
236
|
trimToBudget,
|
|
251
|
-
uninstallClaudeMd,
|
|
252
|
-
uninstallCodex,
|
|
253
|
-
uninstallCopilotCli,
|
|
254
|
-
uninstallGrok,
|
|
255
|
-
uninstallHooks,
|
|
256
|
-
uninstallKimi,
|
|
257
|
-
uninstallSkill,
|
|
258
|
-
uninstallVscode,
|
|
259
237
|
urlPolicyDenialReason,
|
|
260
|
-
|
|
238
|
+
visionTokensSavedByText,
|
|
261
239
|
walkProject,
|
|
262
240
|
writeCompact
|
|
263
|
-
} from "./token-goat-chunk-
|
|
241
|
+
} from "./token-goat-chunk-FDXINYK4.mjs";
|
|
264
242
|
import {
|
|
265
243
|
C,
|
|
266
244
|
CONFIG_KEY_ENV_OVERRIDES,
|
|
267
245
|
ENV_KEYS,
|
|
268
246
|
FILTERS,
|
|
269
247
|
LOCK_WAIT_MS_HARDENED,
|
|
248
|
+
MATERIALIZE_SHRUNK_IMAGE_JS,
|
|
270
249
|
RESET,
|
|
271
250
|
VERSION,
|
|
272
251
|
_useRichStats,
|
|
252
|
+
anchoredMarkerPattern,
|
|
273
253
|
atomicWriteBytes,
|
|
274
254
|
atomicWriteText,
|
|
275
255
|
canonicalize,
|
|
256
|
+
cappedSourceBytesSaved,
|
|
276
257
|
colorStdout,
|
|
277
258
|
configPath,
|
|
259
|
+
copilotCliConfigPath,
|
|
260
|
+
copilotCliMcpToolsDir,
|
|
261
|
+
copilotCliScriptPath,
|
|
262
|
+
copilotCliUserRoot,
|
|
278
263
|
countNoun,
|
|
279
264
|
dataDir,
|
|
280
265
|
decodeSource,
|
|
@@ -289,6 +274,7 @@ import {
|
|
|
289
274
|
fg,
|
|
290
275
|
fileIsAbsent,
|
|
291
276
|
findProject,
|
|
277
|
+
findStrayClaudeMdBlocks,
|
|
292
278
|
fingerprintContent,
|
|
293
279
|
fingerprintFile,
|
|
294
280
|
foldPath,
|
|
@@ -299,9 +285,24 @@ import {
|
|
|
299
285
|
getLastProjectConfigParseError,
|
|
300
286
|
getProjectConfigInfo,
|
|
301
287
|
globalDbPath,
|
|
288
|
+
groupHasTokenGoat,
|
|
289
|
+
installClaudeMd,
|
|
290
|
+
installCodex,
|
|
291
|
+
installCopilotCli,
|
|
292
|
+
installGrok,
|
|
293
|
+
installHooks,
|
|
294
|
+
installKimi,
|
|
302
295
|
installSingleFilePlugin,
|
|
296
|
+
installSkill,
|
|
297
|
+
installVscode,
|
|
303
298
|
invalidateConfigCache,
|
|
299
|
+
isCodexInstalled,
|
|
300
|
+
isCopilotCliInstalled,
|
|
304
301
|
isDotenvPath,
|
|
302
|
+
isGrokInstalled,
|
|
303
|
+
isInstalled,
|
|
304
|
+
isKimiInstalled,
|
|
305
|
+
isTestFile,
|
|
305
306
|
isUnderBlockedRoot,
|
|
306
307
|
isWindows,
|
|
307
308
|
lastProjectConfigLockedKeys,
|
|
@@ -309,6 +310,7 @@ import {
|
|
|
309
310
|
loadPersistedConfig,
|
|
310
311
|
normalizeDarwinSystemAlias,
|
|
311
312
|
normalizePath,
|
|
313
|
+
otherScopeHasManagedServer,
|
|
312
314
|
pad,
|
|
313
315
|
parse,
|
|
314
316
|
pushAll,
|
|
@@ -336,14 +338,23 @@ import {
|
|
|
336
338
|
summarize,
|
|
337
339
|
toDisplayPath,
|
|
338
340
|
toKB,
|
|
341
|
+
uninstallClaudeMd,
|
|
342
|
+
uninstallCodex,
|
|
343
|
+
uninstallCopilotCli,
|
|
344
|
+
uninstallGrok,
|
|
345
|
+
uninstallHooks,
|
|
346
|
+
uninstallKimi,
|
|
339
347
|
uninstallSingleFilePlugin,
|
|
348
|
+
uninstallSkill,
|
|
349
|
+
uninstallVscode,
|
|
340
350
|
validateEnumField,
|
|
341
351
|
validateNumericField,
|
|
352
|
+
vscodeDecoderConfigured,
|
|
342
353
|
withExtension,
|
|
343
354
|
withFileLock,
|
|
344
355
|
withRetryOnLock,
|
|
345
356
|
writeJsonSettings
|
|
346
|
-
} from "./token-goat-chunk-
|
|
357
|
+
} from "./token-goat-chunk-4ZDRTNGV.mjs";
|
|
347
358
|
import {
|
|
348
359
|
__export
|
|
349
360
|
} from "./token-goat-chunk-AEX54RUZ.mjs";
|
|
@@ -3462,9 +3473,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3462
3473
|
* @param {string} [path]
|
|
3463
3474
|
* @return {(string|null|Command)}
|
|
3464
3475
|
*/
|
|
3465
|
-
executableDir(
|
|
3466
|
-
if (
|
|
3467
|
-
this._executableDir =
|
|
3476
|
+
executableDir(path30) {
|
|
3477
|
+
if (path30 === void 0) return this._executableDir;
|
|
3478
|
+
this._executableDir = path30;
|
|
3468
3479
|
return this;
|
|
3469
3480
|
}
|
|
3470
3481
|
/**
|
|
@@ -3764,7 +3775,7 @@ function attemptedCommandName(argv) {
|
|
|
3764
3775
|
|
|
3765
3776
|
// src/cli.ts
|
|
3766
3777
|
import * as fs28 from "fs";
|
|
3767
|
-
import * as
|
|
3778
|
+
import * as path29 from "path";
|
|
3768
3779
|
import { homedir as homedir13 } from "os";
|
|
3769
3780
|
|
|
3770
3781
|
// src/walk_index.ts
|
|
@@ -5652,9 +5663,110 @@ function formatCues(cues) {
|
|
|
5652
5663
|
return cues.map((cue) => `[${formatTimestamp(cue.startSeconds)}]${cue.speaker !== null ? ` ${cue.speaker}:` : ""} ${cue.text}`).join("\n");
|
|
5653
5664
|
}
|
|
5654
5665
|
|
|
5666
|
+
// src/affected.ts
|
|
5667
|
+
import * as path9 from "node:path";
|
|
5668
|
+
var DEFAULT_AFFECTED_DEPTH = 5;
|
|
5669
|
+
function emit(text) {
|
|
5670
|
+
const payload = colorStdout() ? text : stripAnsi(text);
|
|
5671
|
+
process.stdout.write(ensureNewline(payload));
|
|
5672
|
+
}
|
|
5673
|
+
function emitErr(text) {
|
|
5674
|
+
process.stderr.write(ensureNewline(text));
|
|
5675
|
+
}
|
|
5676
|
+
function computeAffected(opts) {
|
|
5677
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
5678
|
+
const depth = opts.depth ?? DEFAULT_AFFECTED_DEPTH;
|
|
5679
|
+
const { files, importedBy } = buildImportGraph(cwd);
|
|
5680
|
+
const trackedByFolded = /* @__PURE__ */ new Map();
|
|
5681
|
+
for (const f of files) trackedByFolded.set(foldPath(normalizePath(f)), f);
|
|
5682
|
+
const unknownSeeds = [];
|
|
5683
|
+
let frontier = [];
|
|
5684
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5685
|
+
for (const raw of opts.files) {
|
|
5686
|
+
const abs = normalizePath(path9.resolve(cwd, raw));
|
|
5687
|
+
const tracked = trackedByFolded.get(foldPath(abs));
|
|
5688
|
+
if (tracked === void 0) {
|
|
5689
|
+
unknownSeeds.push(raw);
|
|
5690
|
+
continue;
|
|
5691
|
+
}
|
|
5692
|
+
if (seen.has(tracked)) continue;
|
|
5693
|
+
seen.add(tracked);
|
|
5694
|
+
frontier.push(tracked);
|
|
5695
|
+
}
|
|
5696
|
+
let hops = 0;
|
|
5697
|
+
while (frontier.length > 0 && hops < depth) {
|
|
5698
|
+
const next = [];
|
|
5699
|
+
for (const file of frontier) {
|
|
5700
|
+
for (const importer of importedBy.get(file) ?? []) {
|
|
5701
|
+
if (seen.has(importer)) continue;
|
|
5702
|
+
seen.add(importer);
|
|
5703
|
+
next.push(importer);
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
frontier = next;
|
|
5707
|
+
hops++;
|
|
5708
|
+
}
|
|
5709
|
+
const depthLimited = frontier.length > 0;
|
|
5710
|
+
const matcher = opts.filter === void 0 ? null : new RegExp(opts.filter);
|
|
5711
|
+
const isAffectedTest = (f) => matcher === null ? isTestFile(f) : matcher.test(f);
|
|
5712
|
+
const root = getDisplayRoot(cwd);
|
|
5713
|
+
const testFiles = [...seen].filter(isAffectedTest).map((f) => toDisplayPath(root, f)).sort();
|
|
5714
|
+
return {
|
|
5715
|
+
testFiles,
|
|
5716
|
+
reachedCount: seen.size,
|
|
5717
|
+
unknownSeeds,
|
|
5718
|
+
depthLimited,
|
|
5719
|
+
unexploredAtFrontier: frontier.length,
|
|
5720
|
+
depth
|
|
5721
|
+
};
|
|
5722
|
+
}
|
|
5723
|
+
function runAffected(opts) {
|
|
5724
|
+
if (opts.depth !== void 0 && opts.depth <= 0) {
|
|
5725
|
+
emitErr(`--depth must be a positive number, got: ${opts.depth}`);
|
|
5726
|
+
return 1;
|
|
5727
|
+
}
|
|
5728
|
+
if (opts.files.length === 0) {
|
|
5729
|
+
emitErr("No changed files given. Pass paths as arguments, or pipe them in with --stdin.");
|
|
5730
|
+
return 1;
|
|
5731
|
+
}
|
|
5732
|
+
if (opts.filter !== void 0) {
|
|
5733
|
+
try {
|
|
5734
|
+
new RegExp(opts.filter);
|
|
5735
|
+
} catch (err2) {
|
|
5736
|
+
emitErr(`--filter is not a valid regular expression: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
5737
|
+
return 1;
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
const result = computeAffected(opts);
|
|
5741
|
+
if (opts.json === true) {
|
|
5742
|
+
emit(JSON.stringify(result, null, 2));
|
|
5743
|
+
return 0;
|
|
5744
|
+
}
|
|
5745
|
+
if (opts.quiet === true) {
|
|
5746
|
+
for (const f of result.testFiles) emit(f);
|
|
5747
|
+
} else if (result.testFiles.length === 0) {
|
|
5748
|
+
const traced = opts.files.length - result.unknownSeeds.length;
|
|
5749
|
+
if (traced === 0) {
|
|
5750
|
+
emit(`Nothing could be traced: ${result.unknownSeeds.length === 1 ? "the file given is not tracked" : "none of the files given are tracked"} in this project.`);
|
|
5751
|
+
} else {
|
|
5752
|
+
emit(`No test files import ${countNoun(traced, "changed file")} (within ${countNoun(result.depth, "hop")}).`);
|
|
5753
|
+
}
|
|
5754
|
+
} else {
|
|
5755
|
+
emit(`${countNoun(result.testFiles.length, "affected test file")} (${countNoun(result.reachedCount, "file")} reached within ${countNoun(result.depth, "hop")}):`);
|
|
5756
|
+
for (const f of result.testFiles) emit(` ${f}`);
|
|
5757
|
+
}
|
|
5758
|
+
if (result.unknownSeeds.length > 0) {
|
|
5759
|
+
emitErr(`Not tracked in this project, so nothing was traced from ${result.unknownSeeds.length === 1 ? "it" : "them"}: ${result.unknownSeeds.join(", ")}`);
|
|
5760
|
+
}
|
|
5761
|
+
if (result.depthLimited) {
|
|
5762
|
+
emitErr(`Stopped at --depth ${result.depth} with ${countNoun(result.unexploredAtFrontier, "file")} still unexplored; more tests may be affected (raise --depth).`);
|
|
5763
|
+
}
|
|
5764
|
+
return 0;
|
|
5765
|
+
}
|
|
5766
|
+
|
|
5655
5767
|
// src/purge.ts
|
|
5656
5768
|
import * as fs10 from "node:fs";
|
|
5657
|
-
import * as
|
|
5769
|
+
import * as path10 from "node:path";
|
|
5658
5770
|
function directorySize(root) {
|
|
5659
5771
|
let total = 0;
|
|
5660
5772
|
let entries;
|
|
@@ -5664,7 +5776,7 @@ function directorySize(root) {
|
|
|
5664
5776
|
return 0;
|
|
5665
5777
|
}
|
|
5666
5778
|
for (const entry of entries) {
|
|
5667
|
-
const full =
|
|
5779
|
+
const full = path10.join(root, entry.name);
|
|
5668
5780
|
if (entry.isDirectory()) {
|
|
5669
5781
|
total += directorySize(full);
|
|
5670
5782
|
continue;
|
|
@@ -5707,7 +5819,7 @@ function formatBytes2(bytes) {
|
|
|
5707
5819
|
|
|
5708
5820
|
// src/cli_stats.ts
|
|
5709
5821
|
import * as fs11 from "node:fs";
|
|
5710
|
-
import * as
|
|
5822
|
+
import * as path11 from "node:path";
|
|
5711
5823
|
function writeRaw(text) {
|
|
5712
5824
|
const payload = colorStdout() ? text : stripAnsi(text);
|
|
5713
5825
|
process.stdout.write(ensureNewline(payload));
|
|
@@ -5719,7 +5831,7 @@ function formatTopFiles(ranked) {
|
|
|
5719
5831
|
if (ranked.length === 0) return "";
|
|
5720
5832
|
const lines = ["Top files this session:"];
|
|
5721
5833
|
for (const { path: filePath, count } of ranked) {
|
|
5722
|
-
const basename8 =
|
|
5834
|
+
const basename8 = path11.basename(filePath);
|
|
5723
5835
|
lines.push(` ${count.toString().padStart(3)}x ${basename8} (${filePath})`);
|
|
5724
5836
|
}
|
|
5725
5837
|
return lines.join("\n");
|
|
@@ -5736,12 +5848,12 @@ function renderTopSessionFiles(topN = 5) {
|
|
|
5736
5848
|
}
|
|
5737
5849
|
function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
|
|
5738
5850
|
try {
|
|
5739
|
-
const sessionsDir = overrideSessionsDir ??
|
|
5851
|
+
const sessionsDir = overrideSessionsDir ?? path11.join(dataDir(), "sessions");
|
|
5740
5852
|
if (!fs11.existsSync(sessionsDir)) return "";
|
|
5741
|
-
const files = fs11.readdirSync(sessionsDir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, mtime: fs11.statSync(
|
|
5853
|
+
const files = fs11.readdirSync(sessionsDir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, mtime: fs11.statSync(path11.join(sessionsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).slice(0, 3);
|
|
5742
5854
|
for (const { name } of files) {
|
|
5743
5855
|
try {
|
|
5744
|
-
const raw = fs11.readFileSync(
|
|
5856
|
+
const raw = fs11.readFileSync(path11.join(sessionsDir, name), "utf-8");
|
|
5745
5857
|
const data = JSON.parse(raw);
|
|
5746
5858
|
const filesList = data["files"];
|
|
5747
5859
|
if (!Array.isArray(filesList)) continue;
|
|
@@ -5761,8 +5873,8 @@ function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
|
|
|
5761
5873
|
var METHODOLOGY = {
|
|
5762
5874
|
estimate_scope: "Local estimate of content avoided or reduced by token-goat.",
|
|
5763
5875
|
billing: "They are not GitHub Copilot usage, provider-reported token consumption, or billing data.",
|
|
5764
|
-
byte_derived_formula: "Most read, hook, and command entries go through savedTokensFromBytes in src/stats.ts, which is Math.round(bytes_saved / 4).",
|
|
5765
|
-
filter_estimates: "Output compressors record their filter-calculated delta
|
|
5876
|
+
byte_derived_formula: "Most read, hook, and command entries go through savedTokensFromBytes in src/stats.ts, which is Math.round(bytes_saved / 4). Image entries are the exception: they are priced in visual tokens, not bytes.",
|
|
5877
|
+
filter_estimates: "Output compressors record their filter-calculated delta, measured against what the harness would actually have delivered rather than the full command output. Image entries are priced in visual tokens (28x28-pixel patches, capped at the configured vision tier), because an image is not billed by its byte size; where the pixel dimensions are unavailable the tier ceiling is used.",
|
|
5766
5878
|
advisory_events: "Zero-byte, zero-token advisory events show that guidance fired, not that an agent followed it.",
|
|
5767
5879
|
audit: "Use stats --full or stats --json for source and command breakdowns; reconcile billing with provider-exported usage data."
|
|
5768
5880
|
};
|
|
@@ -5830,12 +5942,12 @@ function runStats(opts = {}) {
|
|
|
5830
5942
|
// src/cli_doctor.ts
|
|
5831
5943
|
import * as fs12 from "fs";
|
|
5832
5944
|
import * as os8 from "os";
|
|
5833
|
-
import * as
|
|
5945
|
+
import * as path12 from "path";
|
|
5834
5946
|
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
5835
5947
|
function globalMcpConfigPath() {
|
|
5836
5948
|
const copilotHome = process.env["COPILOT_HOME"];
|
|
5837
|
-
const root = copilotHome !== void 0 && copilotHome.trim() !== "" ?
|
|
5838
|
-
return
|
|
5949
|
+
const root = copilotHome !== void 0 && copilotHome.trim() !== "" ? path12.resolve(copilotHome) : path12.join(os8.homedir(), ".copilot");
|
|
5950
|
+
return path12.join(root, "mcp-config.json");
|
|
5839
5951
|
}
|
|
5840
5952
|
function checkGlobalMcpConfig(configPath2 = globalMcpConfigPath()) {
|
|
5841
5953
|
if (!fs12.existsSync(configPath2)) {
|
|
@@ -5952,7 +6064,7 @@ function checkWorkerRunning(dataDir2) {
|
|
|
5952
6064
|
}
|
|
5953
6065
|
var DB_SIZE_WARN_BYTES = 1024 * 1024 * 1024;
|
|
5954
6066
|
function checkDbExists(dataDir2) {
|
|
5955
|
-
const dbPath =
|
|
6067
|
+
const dbPath = path12.join(dataDir2, "global.db");
|
|
5956
6068
|
if (!fs12.existsSync(dbPath)) {
|
|
5957
6069
|
return {
|
|
5958
6070
|
name: "Database",
|
|
@@ -6027,6 +6139,42 @@ function checkSymbolCount(dbPath, rootDir) {
|
|
|
6027
6139
|
};
|
|
6028
6140
|
}
|
|
6029
6141
|
}
|
|
6142
|
+
var EMBED_COVERAGE_WARN_FRACTION = 0.25;
|
|
6143
|
+
function checkEmbeddingCoverage(dbPath, rootDir) {
|
|
6144
|
+
if (!fs12.existsSync(dbPath)) {
|
|
6145
|
+
return { name: "Embedding coverage", status: "ok", message: "no database yet" };
|
|
6146
|
+
}
|
|
6147
|
+
const cfg = loadConfig();
|
|
6148
|
+
if (!cfg.indexing.embeddings_enabled) {
|
|
6149
|
+
return { name: "Embedding coverage", status: "ok", message: "disabled (indexing.embeddings_enabled = false)" };
|
|
6150
|
+
}
|
|
6151
|
+
try {
|
|
6152
|
+
const { indexedFiles, embeddedFiles } = getEmbeddingCoverage(dbPath, rootDir);
|
|
6153
|
+
if (indexedFiles === 0) {
|
|
6154
|
+
return { name: "Embedding coverage", status: "ok", message: "no indexed files yet" };
|
|
6155
|
+
}
|
|
6156
|
+
const pct2 = Math.round(embeddedFiles / indexedFiles * 100);
|
|
6157
|
+
const sizeKb = cfg.indexing.large_file_symbol_only_kb;
|
|
6158
|
+
if (embeddedFiles / indexedFiles < EMBED_COVERAGE_WARN_FRACTION) {
|
|
6159
|
+
return {
|
|
6160
|
+
name: "Embedding coverage",
|
|
6161
|
+
status: "warn",
|
|
6162
|
+
message: `only ${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct2}%) have embeddings \u2014 'semantic' searches those files only, and reports finding nothing in the same words it uses after searching everything. Files over indexing.large_file_symbol_only_kb (currently ${sizeKb} KB) are indexed for symbols only and are the usual reason; raise it with 'token-goat config set indexing.large_file_symbol_only_kb <KB>' and re-embed with 'token-goat index --force' to widen coverage. Exact symbol lookups are unaffected`
|
|
6163
|
+
};
|
|
6164
|
+
}
|
|
6165
|
+
return {
|
|
6166
|
+
name: "Embedding coverage",
|
|
6167
|
+
status: "ok",
|
|
6168
|
+
message: `${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct2}%) have embeddings`
|
|
6169
|
+
};
|
|
6170
|
+
} catch (err2) {
|
|
6171
|
+
return {
|
|
6172
|
+
name: "Embedding coverage",
|
|
6173
|
+
status: "warn",
|
|
6174
|
+
message: `could not query embedding coverage: ${extractErrorMessage(err2)}`
|
|
6175
|
+
};
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6030
6178
|
var DIRTY_QUEUE_BACKLOG_WARN_THRESHOLD = 500;
|
|
6031
6179
|
function checkDirtyQueueHealth(dataDir2) {
|
|
6032
6180
|
let pendingCount = 0;
|
|
@@ -6387,14 +6535,15 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
|
|
|
6387
6535
|
results.push(checkStrayClaudeMdBlocks());
|
|
6388
6536
|
results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
|
|
6389
6537
|
results.push(checkDbExists(actualDataDir));
|
|
6390
|
-
results.push(checkSymbolBodySize(
|
|
6391
|
-
results.push(checkSymbolCount(
|
|
6538
|
+
results.push(checkSymbolBodySize(path12.join(actualDataDir, "global.db")));
|
|
6539
|
+
results.push(checkSymbolCount(path12.join(actualDataDir, "global.db"), rootDir));
|
|
6392
6540
|
results.push(checkDirtyQueueHealth(actualDataDir));
|
|
6393
|
-
results.push(checkCompactionChannel(
|
|
6394
|
-
results.push(checkUnmappedTools(
|
|
6541
|
+
results.push(checkCompactionChannel(path12.join(actualDataDir, "global.db")));
|
|
6542
|
+
results.push(checkUnmappedTools(path12.join(actualDataDir, "global.db")));
|
|
6395
6543
|
const actualConfigPath = configPath2 || configPath();
|
|
6396
6544
|
results.push(checkConfigValid(actualConfigPath));
|
|
6397
6545
|
results.push(checkEmbeddings(loadConfig(rootDir)));
|
|
6546
|
+
results.push(checkEmbeddingCoverage(path12.join(actualDataDir, "global.db"), rootDir));
|
|
6398
6547
|
for (const result of checkSecurityPosture(loadConfig(rootDir), actualDataDir)) results.push(result);
|
|
6399
6548
|
results.push(checkDiskSpace(actualDataDir));
|
|
6400
6549
|
const copilotResult = checkCopilotCli(copilotCliConfigPath(), copilotCliScriptPath());
|
|
@@ -6435,7 +6584,7 @@ async function runDoctorAndExit(opts) {
|
|
|
6435
6584
|
console.log();
|
|
6436
6585
|
try {
|
|
6437
6586
|
const dir = skillOutputsDir();
|
|
6438
|
-
const pregenPath =
|
|
6587
|
+
const pregenPath = path12.join(dir, "pregen.json");
|
|
6439
6588
|
if (fs12.existsSync(pregenPath)) {
|
|
6440
6589
|
const content = JSON.parse(fs12.readFileSync(pregenPath, "utf-8"));
|
|
6441
6590
|
const pregenNames = new Set(content.names || []);
|
|
@@ -6445,7 +6594,7 @@ async function runDoctorAndExit(opts) {
|
|
|
6445
6594
|
for (const entry of entries) {
|
|
6446
6595
|
if (!entry.isFile() || !entry.name.endsWith(".meta")) continue;
|
|
6447
6596
|
try {
|
|
6448
|
-
const meta = JSON.parse(fs12.readFileSync(
|
|
6597
|
+
const meta = JSON.parse(fs12.readFileSync(path12.join(dir, entry.name), "utf-8"));
|
|
6449
6598
|
if (meta.skillName && !pregenNames.has(meta.skillName)) {
|
|
6450
6599
|
skillsSeen.add(meta.skillName);
|
|
6451
6600
|
}
|
|
@@ -6472,7 +6621,7 @@ import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as sta
|
|
|
6472
6621
|
import * as http from "http";
|
|
6473
6622
|
import * as https from "https";
|
|
6474
6623
|
import { isIPv4, isIPv6 } from "net";
|
|
6475
|
-
import { resolve as
|
|
6624
|
+
import { resolve as resolve5, join as join10 } from "path";
|
|
6476
6625
|
import { URL as URL2 } from "url";
|
|
6477
6626
|
import { promisify } from "util";
|
|
6478
6627
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -6661,7 +6810,7 @@ function cleanupStaleDownloads() {
|
|
|
6661
6810
|
const files = readdirSync5(cacheDir);
|
|
6662
6811
|
for (const file of files) {
|
|
6663
6812
|
if (file.endsWith(".tmp")) {
|
|
6664
|
-
const filePath =
|
|
6813
|
+
const filePath = resolve5(cacheDir, file);
|
|
6665
6814
|
try {
|
|
6666
6815
|
const stat = statSync4(filePath);
|
|
6667
6816
|
if (Date.now() - stat.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -6927,7 +7076,7 @@ async function getSectionContent(fileId, heading, opts = {}) {
|
|
|
6927
7076
|
|
|
6928
7077
|
// src/pack.ts
|
|
6929
7078
|
import * as fs13 from "node:fs";
|
|
6930
|
-
import * as
|
|
7079
|
+
import * as path14 from "node:path";
|
|
6931
7080
|
|
|
6932
7081
|
// node_modules/minimatch/dist/esm/index.js
|
|
6933
7082
|
var esm_exports = {};
|
|
@@ -8051,11 +8200,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
8051
8200
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
8052
8201
|
};
|
|
8053
8202
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
8054
|
-
var
|
|
8203
|
+
var path13 = {
|
|
8055
8204
|
win32: { sep: "\\" },
|
|
8056
8205
|
posix: { sep: "/" }
|
|
8057
8206
|
};
|
|
8058
|
-
var sep2 = defaultPlatform === "win32" ?
|
|
8207
|
+
var sep2 = defaultPlatform === "win32" ? path13.win32.sep : path13.posix.sep;
|
|
8059
8208
|
minimatch.sep = sep2;
|
|
8060
8209
|
var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
|
|
8061
8210
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -8840,7 +8989,7 @@ var LANG_MAP = {
|
|
|
8840
8989
|
".exs": "elixir"
|
|
8841
8990
|
};
|
|
8842
8991
|
function getLang(filePath) {
|
|
8843
|
-
const ext2 =
|
|
8992
|
+
const ext2 = path14.extname(filePath).toLowerCase();
|
|
8844
8993
|
return LANG_MAP[ext2] ?? "";
|
|
8845
8994
|
}
|
|
8846
8995
|
function matches(rel, patterns) {
|
|
@@ -8850,8 +8999,8 @@ function matches(rel, patterns) {
|
|
|
8850
8999
|
return patterns.some((pat) => mm.minimatch(norm, pat) || mm.minimatch(base, pat));
|
|
8851
9000
|
}
|
|
8852
9001
|
function isPathWithinRoot(rootReal, resolvedPath) {
|
|
8853
|
-
const rel =
|
|
8854
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(".." +
|
|
9002
|
+
const rel = path14.relative(rootReal, resolvedPath);
|
|
9003
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(".." + path14.sep) && !path14.isAbsolute(rel);
|
|
8855
9004
|
}
|
|
8856
9005
|
function openWithinRoot(rootReal, p) {
|
|
8857
9006
|
let fd;
|
|
@@ -8891,11 +9040,11 @@ function openWithinRoot(rootReal, p) {
|
|
|
8891
9040
|
}
|
|
8892
9041
|
function* resolveOpenCandidates(projectRoot, patterns, rootReal, ignorePatterns, seen, skipped) {
|
|
8893
9042
|
for (const pattern of patterns) {
|
|
8894
|
-
const p =
|
|
9043
|
+
const p = path14.isAbsolute(pattern) ? pattern : path14.join(projectRoot, pattern);
|
|
8895
9044
|
if (seen.has(p)) continue;
|
|
8896
9045
|
let rel;
|
|
8897
9046
|
try {
|
|
8898
|
-
rel =
|
|
9047
|
+
rel = path14.relative(projectRoot, p).replace(/\\/g, "/");
|
|
8899
9048
|
} catch {
|
|
8900
9049
|
skipped.push(`${p} (outside project root)`);
|
|
8901
9050
|
continue;
|
|
@@ -8969,7 +9118,7 @@ function stripBlockComments(content, pattern, lineStates) {
|
|
|
8969
9118
|
);
|
|
8970
9119
|
}
|
|
8971
9120
|
function stripComments(content, filePath) {
|
|
8972
|
-
const ext2 =
|
|
9121
|
+
const ext2 = path14.extname(filePath).toLowerCase();
|
|
8973
9122
|
const lineStates = computeLineStartQuoteStates(content);
|
|
8974
9123
|
if (ext2 === ".py") {
|
|
8975
9124
|
return stripLineComments(content, PY_LINE_COMMENT_RE, lineStates);
|
|
@@ -9007,7 +9156,7 @@ var SAFE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp
|
|
|
9007
9156
|
function scanSecrets(files) {
|
|
9008
9157
|
const hits = [];
|
|
9009
9158
|
for (const pf of files) {
|
|
9010
|
-
if (SAFE_EXTS.has(
|
|
9159
|
+
if (SAFE_EXTS.has(path14.extname(pf.path).toLowerCase())) {
|
|
9011
9160
|
continue;
|
|
9012
9161
|
}
|
|
9013
9162
|
for (const [lineno, line] of pf.content.split("\n").entries()) {
|
|
@@ -9030,7 +9179,7 @@ function scanSecrets(files) {
|
|
|
9030
9179
|
function collectFiles(projectRoot, patterns, opts = {}) {
|
|
9031
9180
|
const result = { files: [], skipped: [], total_lines: 0, total_tokens: 0 };
|
|
9032
9181
|
const seen = /* @__PURE__ */ new Set();
|
|
9033
|
-
const rootResolved =
|
|
9182
|
+
const rootResolved = path14.resolve(projectRoot);
|
|
9034
9183
|
let rootReal;
|
|
9035
9184
|
try {
|
|
9036
9185
|
rootReal = fs13.realpathSync(rootResolved);
|
|
@@ -9205,7 +9354,7 @@ function formatPack(result, style, opts = {}) {
|
|
|
9205
9354
|
function estimateBudget(projectRoot, patterns, opts = {}) {
|
|
9206
9355
|
const result = { entries: [], skipped: [], total_lines: 0, total_tokens: 0 };
|
|
9207
9356
|
const seen = /* @__PURE__ */ new Set();
|
|
9208
|
-
const rootResolved =
|
|
9357
|
+
const rootResolved = path14.resolve(projectRoot);
|
|
9209
9358
|
let rootReal;
|
|
9210
9359
|
try {
|
|
9211
9360
|
rootReal = fs13.realpathSync(rootResolved);
|
|
@@ -9702,7 +9851,7 @@ function formatFailureDeltaJson(delta, runner) {
|
|
|
9702
9851
|
|
|
9703
9852
|
// src/failures_state.ts
|
|
9704
9853
|
import * as fs14 from "node:fs";
|
|
9705
|
-
import * as
|
|
9854
|
+
import * as path15 from "node:path";
|
|
9706
9855
|
var KEY_RE = /^[A-Za-z0-9_-]{1,80}$/;
|
|
9707
9856
|
var DEFAULT_FAILURES_STATE_KEY = "default";
|
|
9708
9857
|
function validateKey(key) {
|
|
@@ -9714,7 +9863,7 @@ function validateKey(key) {
|
|
|
9714
9863
|
}
|
|
9715
9864
|
function failuresStatePath(projectHash, key) {
|
|
9716
9865
|
validateKey(key);
|
|
9717
|
-
return
|
|
9866
|
+
return path15.join(dataDir(), "projects", `${projectHash}_failures_${key}.json`);
|
|
9718
9867
|
}
|
|
9719
9868
|
function loadFailureSnapshot(projectHash, key) {
|
|
9720
9869
|
const p = failuresStatePath(projectHash, key);
|
|
@@ -9735,7 +9884,7 @@ function loadFailureSnapshot(projectHash, key) {
|
|
|
9735
9884
|
}
|
|
9736
9885
|
function saveFailureSnapshot(projectHash, key, snapshot) {
|
|
9737
9886
|
const p = failuresStatePath(projectHash, key);
|
|
9738
|
-
const dir =
|
|
9887
|
+
const dir = path15.dirname(p);
|
|
9739
9888
|
ensureDirSync(dir);
|
|
9740
9889
|
const content = JSON.stringify(snapshot);
|
|
9741
9890
|
const doSave = () => {
|
|
@@ -9747,18 +9896,18 @@ function saveFailureSnapshot(projectHash, key, snapshot) {
|
|
|
9747
9896
|
|
|
9748
9897
|
// src/text_commands.ts
|
|
9749
9898
|
import * as fs16 from "fs";
|
|
9750
|
-
import * as
|
|
9899
|
+
import * as path17 from "path";
|
|
9751
9900
|
|
|
9752
9901
|
// src/project_memory.ts
|
|
9753
9902
|
import * as fs15 from "node:fs";
|
|
9754
|
-
import * as
|
|
9903
|
+
import * as path16 from "node:path";
|
|
9755
9904
|
var MAX_ENTRIES = 30;
|
|
9756
9905
|
var KEY_RE2 = /^[A-Za-z0-9_-]{1,80}$/;
|
|
9757
9906
|
function ordinal(a, b) {
|
|
9758
9907
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
9759
9908
|
}
|
|
9760
9909
|
function memoryPath(projectHash) {
|
|
9761
|
-
return
|
|
9910
|
+
return path16.join(dataDir(), "projects", `${projectHash}_memory.toml`);
|
|
9762
9911
|
}
|
|
9763
9912
|
function validateKey2(key) {
|
|
9764
9913
|
if (!KEY_RE2.test(key)) {
|
|
@@ -9817,7 +9966,7 @@ function save(filePath, entries) {
|
|
|
9817
9966
|
lines.push(`${k} = "${escaped}"`);
|
|
9818
9967
|
}
|
|
9819
9968
|
const content = lines.length > 0 ? lines.join("\n") + "\n" : "";
|
|
9820
|
-
const dir =
|
|
9969
|
+
const dir = path16.dirname(filePath);
|
|
9821
9970
|
ensureDirSync(dir);
|
|
9822
9971
|
atomicWriteText(filePath, content);
|
|
9823
9972
|
}
|
|
@@ -9827,7 +9976,7 @@ function loadEntries(projectHash) {
|
|
|
9827
9976
|
function setEntry(projectHash, key, value) {
|
|
9828
9977
|
validateKey2(key);
|
|
9829
9978
|
const p = memoryPath(projectHash);
|
|
9830
|
-
const dir =
|
|
9979
|
+
const dir = path16.dirname(p);
|
|
9831
9980
|
ensureDirSync(dir);
|
|
9832
9981
|
const doSet = () => {
|
|
9833
9982
|
const entries = loadRaw(p);
|
|
@@ -9925,7 +10074,7 @@ function collectTodoFiles(patterns) {
|
|
|
9925
10074
|
if (patterns.length > 0) {
|
|
9926
10075
|
const results = [];
|
|
9927
10076
|
for (const p of patterns) {
|
|
9928
|
-
const abs =
|
|
10077
|
+
const abs = path17.resolve(p);
|
|
9929
10078
|
try {
|
|
9930
10079
|
const stat = fs16.statSync(abs);
|
|
9931
10080
|
if (stat.isDirectory()) {
|
|
@@ -10196,7 +10345,7 @@ function isProjectFrame(framePath, cwd) {
|
|
|
10196
10345
|
if (framePath.includes("/rustc/")) return false;
|
|
10197
10346
|
if (framePath.includes(".cargo/registry") || framePath.includes(".cargo\\registry")) return false;
|
|
10198
10347
|
if (/^<.+>$/.test(framePath)) return false;
|
|
10199
|
-
if (!
|
|
10348
|
+
if (!path17.isAbsolute(framePath) && !framePath.startsWith("..")) return true;
|
|
10200
10349
|
return false;
|
|
10201
10350
|
}
|
|
10202
10351
|
function resolveFrameSymbol(frame, projectRoot) {
|
|
@@ -10366,14 +10515,23 @@ function applyFiltersAndFold(lines, noNormalize, foldRepeats) {
|
|
|
10366
10515
|
}
|
|
10367
10516
|
function cmdLogfold(src, opts) {
|
|
10368
10517
|
const text = readInput(src);
|
|
10369
|
-
|
|
10518
|
+
const rawLines = splitLines(text);
|
|
10519
|
+
const allLines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
|
|
10520
|
+
let lines = allLines;
|
|
10370
10521
|
if (opts.tail !== void 0) {
|
|
10371
10522
|
const n = requireNonNegativeStrictInt("--tail", opts.tail);
|
|
10372
10523
|
lines = lines.slice(Math.max(0, lines.length - n));
|
|
10373
10524
|
}
|
|
10525
|
+
const inputLines = allLines.length;
|
|
10526
|
+
const shownLines = lines.length;
|
|
10527
|
+
const truncated = shownLines < inputLines;
|
|
10374
10528
|
const folded = applyFiltersAndFold(lines, opts.noNormalize === true, opts.foldRepeats === true);
|
|
10529
|
+
if (truncated) {
|
|
10530
|
+
process.stderr.write(`Showing last ${shownLines} of ${inputLines} lines (raise --tail to see more).
|
|
10531
|
+
`);
|
|
10532
|
+
}
|
|
10375
10533
|
if (opts.json === true) {
|
|
10376
|
-
process.stdout.write(JSON.stringify({ lines: folded }, null, 2) + "\n");
|
|
10534
|
+
process.stdout.write(JSON.stringify({ lines: folded, truncated, inputLines, shownLines }, null, 2) + "\n");
|
|
10377
10535
|
return;
|
|
10378
10536
|
}
|
|
10379
10537
|
for (const item of folded) {
|
|
@@ -10400,17 +10558,17 @@ function findLockfile(startPath) {
|
|
|
10400
10558
|
if (stat !== void 0 && stat.isFile()) {
|
|
10401
10559
|
return { file: startPath, others: [] };
|
|
10402
10560
|
}
|
|
10403
|
-
const dir = stat !== void 0 && stat.isDirectory() ? startPath :
|
|
10561
|
+
const dir = stat !== void 0 && stat.isDirectory() ? startPath : path17.dirname(startPath);
|
|
10404
10562
|
const found = [];
|
|
10405
10563
|
for (const name of LOCK_PRIORITY) {
|
|
10406
|
-
const candidate =
|
|
10564
|
+
const candidate = path17.join(dir, name);
|
|
10407
10565
|
if (fs16.existsSync(candidate)) found.push(candidate);
|
|
10408
10566
|
}
|
|
10409
10567
|
try {
|
|
10410
10568
|
const entries = fs16.readdirSync(dir);
|
|
10411
10569
|
for (const e of entries) {
|
|
10412
10570
|
if (/^requirements.*\.txt$/.test(e)) {
|
|
10413
|
-
const p =
|
|
10571
|
+
const p = path17.join(dir, e);
|
|
10414
10572
|
if (!found.includes(p)) found.push(p);
|
|
10415
10573
|
}
|
|
10416
10574
|
}
|
|
@@ -10607,7 +10765,7 @@ function parsePnpmLock(content) {
|
|
|
10607
10765
|
return deps;
|
|
10608
10766
|
}
|
|
10609
10767
|
function parseLockFile(filePath) {
|
|
10610
|
-
const base =
|
|
10768
|
+
const base = path17.basename(filePath);
|
|
10611
10769
|
const content = fs16.readFileSync(filePath, "utf8");
|
|
10612
10770
|
if (base === "package-lock.json") return { format: "npm", deps: parsePackageLockJson(content) };
|
|
10613
10771
|
if (base === "yarn.lock") return { format: "yarn", deps: parseYarnLock(content) };
|
|
@@ -10723,7 +10881,7 @@ Depended on by direct/top-level deps (${dependedOnBy.length}):
|
|
|
10723
10881
|
`);
|
|
10724
10882
|
}
|
|
10725
10883
|
function cmdLockdeps(filePath, opts) {
|
|
10726
|
-
const target = filePath !== void 0 ?
|
|
10884
|
+
const target = filePath !== void 0 ? path17.resolve(filePath) : process.cwd();
|
|
10727
10885
|
const found = findLockfile(target);
|
|
10728
10886
|
if (found === null) {
|
|
10729
10887
|
throw new Error("No lockfile found. Expected one of: " + LOCK_PRIORITY.join(", ") + ", requirements*.txt");
|
|
@@ -10811,7 +10969,7 @@ function cmdNote(action, key, value, opts) {
|
|
|
10811
10969
|
throw new Error(`Unknown note action: ${action}. Use: set, get, unset, list, clear`);
|
|
10812
10970
|
}
|
|
10813
10971
|
function loadAllSessionReadCounts() {
|
|
10814
|
-
const sessionsDir =
|
|
10972
|
+
const sessionsDir = path17.join(tokenGoatHome(), "sessions");
|
|
10815
10973
|
const totals = /* @__PURE__ */ new Map();
|
|
10816
10974
|
let entries;
|
|
10817
10975
|
try {
|
|
@@ -10821,7 +10979,7 @@ function loadAllSessionReadCounts() {
|
|
|
10821
10979
|
}
|
|
10822
10980
|
for (const entry of entries) {
|
|
10823
10981
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
10824
|
-
const filePath =
|
|
10982
|
+
const filePath = path17.join(sessionsDir, entry.name);
|
|
10825
10983
|
let raw;
|
|
10826
10984
|
try {
|
|
10827
10985
|
raw = JSON.parse(fs16.readFileSync(filePath, "utf8"));
|
|
@@ -10859,9 +11017,11 @@ function cmdHot(opts) {
|
|
|
10859
11017
|
}
|
|
10860
11018
|
}
|
|
10861
11019
|
entries.sort((a, b) => b.readCount - a.readCount);
|
|
11020
|
+
const eligibleCount = entries.length;
|
|
10862
11021
|
entries = entries.slice(0, limit);
|
|
11022
|
+
const truncated = entries.length < eligibleCount;
|
|
10863
11023
|
if (opts.json === true) {
|
|
10864
|
-
process.stdout.write(JSON.stringify({ entries }, null, 2) + "\n");
|
|
11024
|
+
process.stdout.write(JSON.stringify({ entries, truncated, totalCount: eligibleCount }, null, 2) + "\n");
|
|
10865
11025
|
return;
|
|
10866
11026
|
}
|
|
10867
11027
|
if (entries.length === 0) {
|
|
@@ -10877,6 +11037,10 @@ function cmdHot(opts) {
|
|
|
10877
11037
|
const hotDisplayRoot = getDisplayRoot();
|
|
10878
11038
|
for (const e of entries) {
|
|
10879
11039
|
process.stdout.write(`${e.readCount} ${toDisplayPath(hotDisplayRoot, e.path)}
|
|
11040
|
+
`);
|
|
11041
|
+
}
|
|
11042
|
+
if (truncated) {
|
|
11043
|
+
process.stdout.write(`...and ${eligibleCount - entries.length} more (raise --limit to see them).
|
|
10880
11044
|
`);
|
|
10881
11045
|
}
|
|
10882
11046
|
}
|
|
@@ -10951,7 +11115,7 @@ function cmdIgnores(opts) {
|
|
|
10951
11115
|
}
|
|
10952
11116
|
function countPackIgnorePatterns(root) {
|
|
10953
11117
|
try {
|
|
10954
|
-
return fs16.readFileSync(
|
|
11118
|
+
return fs16.readFileSync(path17.join(root, ".tokengoatignore"), "utf8").split("\n").map((ln) => ln.trim()).filter((ln) => ln.length > 0 && !ln.startsWith("#")).length;
|
|
10955
11119
|
} catch {
|
|
10956
11120
|
return 0;
|
|
10957
11121
|
}
|
|
@@ -10960,7 +11124,7 @@ function countPackIgnorePatterns(root) {
|
|
|
10960
11124
|
// src/dep_docs.ts
|
|
10961
11125
|
import { createRequire } from "node:module";
|
|
10962
11126
|
import * as fs17 from "node:fs";
|
|
10963
|
-
import * as
|
|
11127
|
+
import * as path18 from "node:path";
|
|
10964
11128
|
var _require = createRequire(import.meta.url);
|
|
10965
11129
|
var _ts = null;
|
|
10966
11130
|
var _tsLoadAttempted = false;
|
|
@@ -11011,7 +11175,7 @@ function listInstalledPackageNames(nodeModulesDir) {
|
|
|
11011
11175
|
if (entry.name.startsWith("@")) {
|
|
11012
11176
|
let scoped;
|
|
11013
11177
|
try {
|
|
11014
|
-
scoped = fs17.readdirSync(
|
|
11178
|
+
scoped = fs17.readdirSync(path18.join(nodeModulesDir, entry.name), { withFileTypes: true });
|
|
11015
11179
|
} catch {
|
|
11016
11180
|
continue;
|
|
11017
11181
|
}
|
|
@@ -11028,7 +11192,7 @@ var README_NAME_RE = /^readme(\.(md|markdown|txt))?$/i;
|
|
|
11028
11192
|
function findReadmeFile(pkgDir) {
|
|
11029
11193
|
const direct = findReadmeIn(pkgDir);
|
|
11030
11194
|
if (direct !== null) return direct;
|
|
11031
|
-
return findReadmeIn(
|
|
11195
|
+
return findReadmeIn(path18.join(pkgDir, "docs"));
|
|
11032
11196
|
}
|
|
11033
11197
|
function findReadmeIn(dir) {
|
|
11034
11198
|
let entries;
|
|
@@ -11045,7 +11209,7 @@ function findReadmeIn(dir) {
|
|
|
11045
11209
|
return 1;
|
|
11046
11210
|
};
|
|
11047
11211
|
matches2.sort((a, b) => rank(a.name) - rank(b.name));
|
|
11048
|
-
return
|
|
11212
|
+
return path18.join(dir, matches2[0].name);
|
|
11049
11213
|
}
|
|
11050
11214
|
function typesPackageDirName(pkgName) {
|
|
11051
11215
|
if (pkgName.startsWith("@")) {
|
|
@@ -11072,30 +11236,30 @@ function resolveTypesLocation(pkgDir, pkgJson, nodeModulesDir, pkgName) {
|
|
|
11072
11236
|
if (declared !== void 0) {
|
|
11073
11237
|
const candidates = [declared, declared.endsWith(".d.ts") ? declared : `${declared}.d.ts`, declared.replace(/\.[cm]?[jt]s$/, ".d.ts")];
|
|
11074
11238
|
for (const c of candidates) {
|
|
11075
|
-
const p =
|
|
11239
|
+
const p = path18.join(pkgDir, c);
|
|
11076
11240
|
if (fileExists(p)) return { path: p, source: "bundled" };
|
|
11077
11241
|
}
|
|
11078
11242
|
}
|
|
11079
11243
|
const main = typeof pkgJson["main"] === "string" ? pkgJson["main"] : "index.js";
|
|
11080
|
-
const mainDts =
|
|
11244
|
+
const mainDts = path18.join(pkgDir, main.replace(/\.[cm]?js$/, ".d.ts"));
|
|
11081
11245
|
if (fileExists(mainDts)) return { path: mainDts, source: "bundled" };
|
|
11082
|
-
const indexDts =
|
|
11246
|
+
const indexDts = path18.join(pkgDir, "index.d.ts");
|
|
11083
11247
|
if (fileExists(indexDts)) return { path: indexDts, source: "bundled" };
|
|
11084
11248
|
const typesDirName = typesPackageDirName(pkgName);
|
|
11085
|
-
const typesPkgDir =
|
|
11086
|
-
const typesPkgJsonRaw = readFileTextOrNull(
|
|
11249
|
+
const typesPkgDir = path18.join(nodeModulesDir, "@types", typesDirName);
|
|
11250
|
+
const typesPkgJsonRaw = readFileTextOrNull(path18.join(typesPkgDir, "package.json"));
|
|
11087
11251
|
if (typesPkgJsonRaw !== null) {
|
|
11088
11252
|
try {
|
|
11089
11253
|
const typesPkgJson = JSON.parse(typesPkgJsonRaw);
|
|
11090
11254
|
const entry = (typeof typesPkgJson["types"] === "string" ? typesPkgJson["types"] : void 0) ?? (typeof typesPkgJson["main"] === "string" ? typesPkgJson["main"] : void 0) ?? "index.d.ts";
|
|
11091
11255
|
const entryCandidates = [entry, entry.endsWith(".d.ts") ? entry : `${entry}.d.ts`, entry.replace(/\.[cm]?[jt]s$/, ".d.ts")];
|
|
11092
11256
|
for (const c of entryCandidates) {
|
|
11093
|
-
const entryPath =
|
|
11257
|
+
const entryPath = path18.join(typesPkgDir, c);
|
|
11094
11258
|
if (fileExists(entryPath)) return { path: entryPath, source: "@types" };
|
|
11095
11259
|
}
|
|
11096
11260
|
} catch {
|
|
11097
11261
|
}
|
|
11098
|
-
const fallback =
|
|
11262
|
+
const fallback = path18.join(typesPkgDir, "index.d.ts");
|
|
11099
11263
|
if (fileExists(fallback)) return { path: fallback, source: "@types" };
|
|
11100
11264
|
}
|
|
11101
11265
|
return null;
|
|
@@ -11162,9 +11326,9 @@ function recordDepDocsStat(fullSourceBytes, emittedText, packageName) {
|
|
|
11162
11326
|
}
|
|
11163
11327
|
function runDepDocs(opts) {
|
|
11164
11328
|
const root = resolveProjectRoot({ project: opts.projectRoot ?? process.cwd() });
|
|
11165
|
-
const nodeModulesDir =
|
|
11166
|
-
const pkgDir =
|
|
11167
|
-
const pkgJsonPath =
|
|
11329
|
+
const nodeModulesDir = path18.join(root, "node_modules");
|
|
11330
|
+
const pkgDir = path18.join(nodeModulesDir, opts.packageName);
|
|
11331
|
+
const pkgJsonPath = path18.join(pkgDir, "package.json");
|
|
11168
11332
|
if (!dirExists(pkgDir) || !fileExists(pkgJsonPath)) {
|
|
11169
11333
|
const known = listInstalledPackageNames(nodeModulesDir);
|
|
11170
11334
|
const suggestions = suggestPackageNames(opts.packageName, known);
|
|
@@ -11327,20 +11491,27 @@ async function buildResumePacket(sessionId) {
|
|
|
11327
11491
|
}
|
|
11328
11492
|
|
|
11329
11493
|
// src/cache_session_commands.ts
|
|
11330
|
-
function
|
|
11494
|
+
function emitErr2(text) {
|
|
11331
11495
|
process.stderr.write(ensureNewline(text));
|
|
11332
11496
|
}
|
|
11497
|
+
function capAndNote(rows, limit) {
|
|
11498
|
+
const shown = rows.slice(0, limit);
|
|
11499
|
+
if (shown.length < rows.length) {
|
|
11500
|
+
emitErr2(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
|
|
11501
|
+
}
|
|
11502
|
+
return shown;
|
|
11503
|
+
}
|
|
11333
11504
|
function parseLimitOpt(cmdName, limitStr, dflt = 30) {
|
|
11334
11505
|
if (limitStr === void 0) return dflt;
|
|
11335
11506
|
let n;
|
|
11336
11507
|
try {
|
|
11337
11508
|
n = requireNonNegativeStrictInt("--limit", limitStr);
|
|
11338
11509
|
} catch (e) {
|
|
11339
|
-
|
|
11510
|
+
emitErr2(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
|
|
11340
11511
|
throw new Error(`invalid --limit: ${limitStr}`, { cause: e });
|
|
11341
11512
|
}
|
|
11342
11513
|
if (n === 0) {
|
|
11343
|
-
|
|
11514
|
+
emitErr2(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
|
|
11344
11515
|
throw new Error(`invalid --limit: ${limitStr}`);
|
|
11345
11516
|
}
|
|
11346
11517
|
return n;
|
|
@@ -11369,7 +11540,7 @@ function getNewestSessionFiles() {
|
|
|
11369
11540
|
function cmdBashHistory(opts) {
|
|
11370
11541
|
const limit = parseLimitOpt("bash-history", opts.limit);
|
|
11371
11542
|
const blobs = listBlobs(BASH_OUTPUT_SUBDIR);
|
|
11372
|
-
const
|
|
11543
|
+
const allItems = blobs.map(({ id, mtime, value }) => {
|
|
11373
11544
|
if (typeof value !== "object" || value === null) return null;
|
|
11374
11545
|
const v = value;
|
|
11375
11546
|
return {
|
|
@@ -11379,7 +11550,8 @@ function cmdBashHistory(opts) {
|
|
|
11379
11550
|
exitCode: typeof v["exitCode"] === "number" ? v["exitCode"] : -1,
|
|
11380
11551
|
sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
|
|
11381
11552
|
};
|
|
11382
|
-
}).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt)
|
|
11553
|
+
}).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
|
|
11554
|
+
const items = capAndNote(allItems, limit);
|
|
11383
11555
|
if (opts.json === true) {
|
|
11384
11556
|
process.stdout.write(JSON.stringify(items, null, 2) + "\n");
|
|
11385
11557
|
return;
|
|
@@ -11400,7 +11572,7 @@ function cmdBashHistory(opts) {
|
|
|
11400
11572
|
function cmdWebHistory(opts) {
|
|
11401
11573
|
const limit = parseLimitOpt("web-history", opts.limit);
|
|
11402
11574
|
const blobs = listBlobs(WEB_OUTPUT_SUBDIR);
|
|
11403
|
-
const
|
|
11575
|
+
const allItems = blobs.map(({ id, mtime, value }) => {
|
|
11404
11576
|
if (typeof value !== "object" || value === null) return null;
|
|
11405
11577
|
const v = value;
|
|
11406
11578
|
return {
|
|
@@ -11409,7 +11581,8 @@ function cmdWebHistory(opts) {
|
|
|
11409
11581
|
bytes: typeof v["content"] === "string" ? Buffer.byteLength(v["content"], "utf8") : 0,
|
|
11410
11582
|
storedAt: mtime
|
|
11411
11583
|
};
|
|
11412
|
-
}).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt)
|
|
11584
|
+
}).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
|
|
11585
|
+
const items = capAndNote(allItems, limit);
|
|
11413
11586
|
if (opts.json === true) {
|
|
11414
11587
|
process.stdout.write(JSON.stringify(items, null, 2) + "\n");
|
|
11415
11588
|
return;
|
|
@@ -11428,7 +11601,7 @@ function cmdWebHistory(opts) {
|
|
|
11428
11601
|
function cmdMcpHistory(opts) {
|
|
11429
11602
|
const limit = parseLimitOpt("mcp-history", opts.limit);
|
|
11430
11603
|
const blobs = listBlobs(BASH_OUTPUT_SUBDIR).filter((b) => b.id.startsWith("mcp_"));
|
|
11431
|
-
const
|
|
11604
|
+
const allItems = blobs.map(({ id, mtime, value }) => {
|
|
11432
11605
|
if (typeof value !== "object" || value === null) return null;
|
|
11433
11606
|
const v = value;
|
|
11434
11607
|
const command = typeof v["command"] === "string" ? v["command"] : "";
|
|
@@ -11439,7 +11612,8 @@ function cmdMcpHistory(opts) {
|
|
|
11439
11612
|
storedAt: typeof v["storedAt"] === "number" ? v["storedAt"] : mtime,
|
|
11440
11613
|
sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
|
|
11441
11614
|
};
|
|
11442
|
-
}).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt)
|
|
11615
|
+
}).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
|
|
11616
|
+
const items = capAndNote(allItems, limit);
|
|
11443
11617
|
if (opts.json === true) {
|
|
11444
11618
|
process.stdout.write(JSON.stringify(items, null, 2) + "\n");
|
|
11445
11619
|
return;
|
|
@@ -11671,7 +11845,7 @@ function cmdBaseline(opts) {
|
|
|
11671
11845
|
|
|
11672
11846
|
// src/index_reclaim.ts
|
|
11673
11847
|
import * as fs18 from "node:fs";
|
|
11674
|
-
import * as
|
|
11848
|
+
import * as path19 from "node:path";
|
|
11675
11849
|
var DERIVED_TABLES = ["chunk_vectors", "chunks", "refs", "symbols", "files"];
|
|
11676
11850
|
function indexSizeBytes(dbPath) {
|
|
11677
11851
|
let total = 0;
|
|
@@ -11755,7 +11929,7 @@ function mb(bytes) {
|
|
|
11755
11929
|
}
|
|
11756
11930
|
function cmdReclaimIndex(opts) {
|
|
11757
11931
|
const dbPath = opts.dbPath ?? globalDbPath();
|
|
11758
|
-
if (opts.force !== true && isWorkerRunning(
|
|
11932
|
+
if (opts.force !== true && isWorkerRunning(path19.dirname(dbPath))) {
|
|
11759
11933
|
throw new Error(
|
|
11760
11934
|
"reclaim-index: the worker daemon is running and writing to this index. Stop it first with 'token-goat worker stop', then re-run. Pass --force to proceed anyway (results may be inaccurate if the worker is really live)."
|
|
11761
11935
|
);
|
|
@@ -11789,7 +11963,7 @@ function cmdReclaimIndex(opts) {
|
|
|
11789
11963
|
}
|
|
11790
11964
|
if (result.checkpointBusy) {
|
|
11791
11965
|
process.stdout.write(
|
|
11792
|
-
` note: a concurrent reader blocked WAL truncation, so some space is still held in ${
|
|
11966
|
+
` note: a concurrent reader blocked WAL truncation, so some space is still held in ${path19.basename(dbPath)}-wal and the figures above count the main file only. The next checkpoint releases it
|
|
11793
11967
|
`
|
|
11794
11968
|
);
|
|
11795
11969
|
}
|
|
@@ -11798,16 +11972,16 @@ function cmdReclaimIndex(opts) {
|
|
|
11798
11972
|
// src/config_commands.ts
|
|
11799
11973
|
import * as fs19 from "node:fs";
|
|
11800
11974
|
import * as os9 from "node:os";
|
|
11801
|
-
import * as
|
|
11802
|
-
function
|
|
11975
|
+
import * as path20 from "node:path";
|
|
11976
|
+
function emit2(text) {
|
|
11803
11977
|
const payload = colorStdout() ? text : stripAnsi(text);
|
|
11804
11978
|
process.stdout.write(ensureNewline(payload));
|
|
11805
11979
|
}
|
|
11806
|
-
function
|
|
11980
|
+
function emitErr3(text) {
|
|
11807
11981
|
process.stderr.write(ensureNewline(text));
|
|
11808
11982
|
}
|
|
11809
11983
|
function saveConfigSafe(cfg) {
|
|
11810
|
-
ensureDirSync(
|
|
11984
|
+
ensureDirSync(path20.dirname(configPath()));
|
|
11811
11985
|
saveConfig(cfg);
|
|
11812
11986
|
}
|
|
11813
11987
|
function levenshtein(a, b, cap = 3) {
|
|
@@ -11969,16 +12143,16 @@ function cmdConfig(opts) {
|
|
|
11969
12143
|
if (state.layer !== "global" && state.layer !== "project-unparsed") sources[k] = layerJson(state);
|
|
11970
12144
|
}
|
|
11971
12145
|
if (Object.keys(sources).length > 0) payload["_sources"] = sources;
|
|
11972
|
-
|
|
12146
|
+
emit2(JSON.stringify(payload, null, 2));
|
|
11973
12147
|
return;
|
|
11974
12148
|
}
|
|
11975
12149
|
for (const [k, v] of pairs) {
|
|
11976
12150
|
const state = resolveConfigKeyLayer(k, v, cfg, projectInfo);
|
|
11977
|
-
|
|
12151
|
+
emit2(`${k} = ${JSON.stringify(v)}${state.layer === "project-unparsed" ? "" : layerAnnotation(state)}`);
|
|
11978
12152
|
}
|
|
11979
12153
|
if (projectInfo !== null) {
|
|
11980
|
-
|
|
11981
|
-
|
|
12154
|
+
emit2("");
|
|
12155
|
+
emit2(
|
|
11982
12156
|
projectInfo.parseError !== null ? `# project override ${projectInfo.path} failed to parse (${projectInfo.parseError}); ignored` : `# project override: ${projectInfo.path}`
|
|
11983
12157
|
);
|
|
11984
12158
|
}
|
|
@@ -11996,11 +12170,11 @@ function cmdConfig(opts) {
|
|
|
11996
12170
|
}
|
|
11997
12171
|
const getState = resolveConfigKeyLayer(opts.key, result.value, cfg, getProjectConfigInfo());
|
|
11998
12172
|
if (opts.json === true) {
|
|
11999
|
-
|
|
12173
|
+
emit2(JSON.stringify({ key: opts.key, value: result.value, ...layerJson(getState) }, null, 2));
|
|
12000
12174
|
return;
|
|
12001
12175
|
}
|
|
12002
12176
|
const rendered = typeof result.value === "string" ? result.value : JSON.stringify(result.value);
|
|
12003
|
-
|
|
12177
|
+
emit2(`${rendered}${layerAnnotation(getState)}`);
|
|
12004
12178
|
return;
|
|
12005
12179
|
}
|
|
12006
12180
|
if (action === "set") {
|
|
@@ -12019,7 +12193,7 @@ function cmdConfig(opts) {
|
|
|
12019
12193
|
if (parseErrAtLoad !== null) {
|
|
12020
12194
|
try {
|
|
12021
12195
|
fs19.copyFileSync(configPath(), `${configPath()}.bak`);
|
|
12022
|
-
|
|
12196
|
+
emitErr3(`config set: warning: config.toml failed to parse (${parseErrAtLoad}); backed up the original to config.toml.bak and rewriting it from defaults`);
|
|
12023
12197
|
} catch {
|
|
12024
12198
|
}
|
|
12025
12199
|
}
|
|
@@ -12054,8 +12228,8 @@ function cmdConfig(opts) {
|
|
|
12054
12228
|
saveConfigSafe(cfg);
|
|
12055
12229
|
return coercedValue;
|
|
12056
12230
|
};
|
|
12057
|
-
ensureDirSync(
|
|
12058
|
-
const lockPath =
|
|
12231
|
+
ensureDirSync(path20.dirname(configPath()));
|
|
12232
|
+
const lockPath = path20.join(path20.dirname(configPath()), ".config.lock");
|
|
12059
12233
|
const lockResult = withFileLock(lockPath, applySet, { waitMs: LOCK_WAIT_MS_HARDENED });
|
|
12060
12234
|
const coerced = lockResult === void 0 ? applySet() : lockResult;
|
|
12061
12235
|
invalidateConfigCache();
|
|
@@ -12064,19 +12238,19 @@ function cmdConfig(opts) {
|
|
|
12064
12238
|
const setState = resolveConfigKeyLayer(key, setEffective.found ? setEffective.value : void 0, effectiveCfg, getProjectConfigInfo());
|
|
12065
12239
|
const shadowed = setEffective.found && setEffective.value !== coerced;
|
|
12066
12240
|
if (shadowed && setState.layer === "env") {
|
|
12067
|
-
|
|
12241
|
+
emitErr3(`config set: warning: ${key} was saved to config.toml, but ${setState.envVar} is currently set and overrides it at runtime \u2014 unset ${setState.envVar} for this change to take effect`);
|
|
12068
12242
|
} else if (shadowed && setState.layer === "env-invalid") {
|
|
12069
|
-
|
|
12243
|
+
emitErr3(`config set: warning: ${key} was saved to config.toml, but ${setState.envVar} is currently set to ${JSON.stringify(setState.rawValue)}${setState.reason !== null ? ` (${setState.reason})` : ""} and still overrides it at runtime, taking effect as ${JSON.stringify(setState.effectiveValue)} \u2014 unset ${setState.envVar} for this change to take effect`);
|
|
12070
12244
|
} else if (setState.layer === "project") {
|
|
12071
|
-
|
|
12245
|
+
emitErr3(`config set: warning: ${key} was saved to config.toml, but ${setState.path} also sets it and overrides it in this project \u2014 remove it there for this change to take effect here`);
|
|
12072
12246
|
} else if (setState.layer === "project-invalid") {
|
|
12073
|
-
|
|
12247
|
+
emitErr3(`config set: warning: ${key} was saved to config.toml, but ${setState.path} sets it to ${JSON.stringify(setState.rawValue)}${setState.reason !== null ? ` (${setState.reason})` : ""} and still overrides it in this project, taking effect as ${JSON.stringify(setState.effectiveValue)} \u2014 remove it there for this change to take effect here`);
|
|
12074
12248
|
}
|
|
12075
12249
|
if (opts.json === true) {
|
|
12076
|
-
|
|
12250
|
+
emit2(JSON.stringify({ key, value: coerced }, null, 2));
|
|
12077
12251
|
return;
|
|
12078
12252
|
}
|
|
12079
|
-
|
|
12253
|
+
emit2(`${key} = ${JSON.stringify(coerced)}`);
|
|
12080
12254
|
return;
|
|
12081
12255
|
}
|
|
12082
12256
|
if (action === "validate") {
|
|
@@ -12144,19 +12318,19 @@ function cmdConfig(opts) {
|
|
|
12144
12318
|
}
|
|
12145
12319
|
if (findings.length > 0) process.exitCode = 1;
|
|
12146
12320
|
if (opts.json === true) {
|
|
12147
|
-
|
|
12321
|
+
emit2(JSON.stringify({ findings, ok: findings.length === 0 }, null, 2));
|
|
12148
12322
|
return;
|
|
12149
12323
|
}
|
|
12150
12324
|
if (findings.length === 0) {
|
|
12151
|
-
|
|
12325
|
+
emit2("config validate: no issues found");
|
|
12152
12326
|
return;
|
|
12153
12327
|
}
|
|
12154
12328
|
for (const f of findings) {
|
|
12155
12329
|
const isNearMiss = f.kind === "unknown_section" || f.kind === "unknown_key";
|
|
12156
12330
|
const hint = f.suggestion === void 0 ? "" : isNearMiss ? ` (did you mean: ${f.suggestion}?)` : ` (${f.suggestion})`;
|
|
12157
|
-
|
|
12331
|
+
emit2(`[${f.kind}] ${f.key}${hint}`);
|
|
12158
12332
|
}
|
|
12159
|
-
|
|
12333
|
+
emit2(`config validate: ${findings.length} issue(s) found`);
|
|
12160
12334
|
return;
|
|
12161
12335
|
}
|
|
12162
12336
|
throw new Error(`config: unknown action '${action}'. Use list, get, set, or validate.`);
|
|
@@ -12168,33 +12342,33 @@ function cmdProject(opts) {
|
|
|
12168
12342
|
const active = findProject(process.cwd());
|
|
12169
12343
|
const blocked = cfg.worker.blocked_roots;
|
|
12170
12344
|
if (opts.json === true) {
|
|
12171
|
-
|
|
12345
|
+
emit2(JSON.stringify({ active: active ? { root: active.root, hash: active.hash, marker: active.marker } : null, blocked_roots: blocked }, null, 2));
|
|
12172
12346
|
return;
|
|
12173
12347
|
}
|
|
12174
12348
|
if (active) {
|
|
12175
|
-
|
|
12176
|
-
|
|
12349
|
+
emit2(`Active project: ${active.root}`);
|
|
12350
|
+
emit2(` marker: ${active.marker}`);
|
|
12177
12351
|
} else {
|
|
12178
|
-
|
|
12352
|
+
emit2("Active project: (none \u2014 not inside a recognized project root)");
|
|
12179
12353
|
}
|
|
12180
12354
|
if (blocked.length === 0) {
|
|
12181
|
-
|
|
12355
|
+
emit2("Blocked roots: (none)");
|
|
12182
12356
|
} else {
|
|
12183
|
-
|
|
12184
|
-
for (const r of blocked)
|
|
12357
|
+
emit2("Blocked roots:");
|
|
12358
|
+
for (const r of blocked) emit2(` ${r}`);
|
|
12185
12359
|
}
|
|
12186
12360
|
return;
|
|
12187
12361
|
}
|
|
12188
12362
|
if (action === "exclude") {
|
|
12189
12363
|
if (!opts.pathArg) {
|
|
12190
|
-
|
|
12364
|
+
emitErr3("project exclude requires a path argument");
|
|
12191
12365
|
throw new Error("missing path");
|
|
12192
12366
|
}
|
|
12193
|
-
const target =
|
|
12367
|
+
const target = path20.resolve(opts.pathArg);
|
|
12194
12368
|
const cfg = loadPersistedConfig();
|
|
12195
12369
|
const targetFolded = foldPath(normalizePath(target));
|
|
12196
12370
|
if (cfg.worker.blocked_roots.some((r) => foldPath(normalizePath(r)) === targetFolded)) {
|
|
12197
|
-
|
|
12371
|
+
emit2(`Already excluded: ${target}`);
|
|
12198
12372
|
return;
|
|
12199
12373
|
}
|
|
12200
12374
|
cfg.worker.blocked_roots = [...cfg.worker.blocked_roots, target];
|
|
@@ -12202,12 +12376,12 @@ function cmdProject(opts) {
|
|
|
12202
12376
|
invalidateConfigCache();
|
|
12203
12377
|
const purged = pruneBlockedRoot(target);
|
|
12204
12378
|
if (opts.json === true) {
|
|
12205
|
-
|
|
12379
|
+
emit2(JSON.stringify({ excluded: target, purgedFromIndex: purged.length, blocked_roots: cfg.worker.blocked_roots }, null, 2));
|
|
12206
12380
|
return;
|
|
12207
12381
|
}
|
|
12208
|
-
|
|
12382
|
+
emit2(`Excluded: ${target}`);
|
|
12209
12383
|
if (purged.length > 0) {
|
|
12210
|
-
|
|
12384
|
+
emit2(`Removed ${purged.length} already-indexed file${purged.length === 1 ? "" : "s"} under it from the index.`);
|
|
12211
12385
|
}
|
|
12212
12386
|
return;
|
|
12213
12387
|
}
|
|
@@ -12227,26 +12401,26 @@ function cmdProject(opts) {
|
|
|
12227
12401
|
const orphanChunkPaths = findOrphanedChunkPaths();
|
|
12228
12402
|
if (opts.dryRun === true) {
|
|
12229
12403
|
if (opts.json === true) {
|
|
12230
|
-
|
|
12404
|
+
emit2(JSON.stringify({ dryRun: true, wouldPrune: removed, stale, wouldPruneTempFiles: staleTempFiles.length, staleTempFiles, wouldPruneOrphanChunkFiles: orphanChunkPaths.length, orphanChunkPaths, blocked_roots: before }, null, 2));
|
|
12231
12405
|
return;
|
|
12232
12406
|
}
|
|
12233
12407
|
if (removed === 0) {
|
|
12234
|
-
|
|
12408
|
+
emit2("Would prune 0 stale root(s). Nothing to do.");
|
|
12235
12409
|
} else {
|
|
12236
|
-
|
|
12237
|
-
for (const r of stale)
|
|
12410
|
+
emit2(`Would prune ${removed} stale root(s):`);
|
|
12411
|
+
for (const r of stale) emit2(` ${r}`);
|
|
12238
12412
|
}
|
|
12239
12413
|
if (staleTempFiles.length === 0) {
|
|
12240
|
-
|
|
12414
|
+
emit2("Would prune 0 stale indexed temp-dir file(s). Nothing to do.");
|
|
12241
12415
|
} else {
|
|
12242
|
-
|
|
12243
|
-
for (const p of staleTempFiles)
|
|
12416
|
+
emit2(`Would prune ${staleTempFiles.length} stale indexed temp-dir file(s):`);
|
|
12417
|
+
for (const p of staleTempFiles) emit2(` ${p}`);
|
|
12244
12418
|
}
|
|
12245
12419
|
if (orphanChunkPaths.length === 0) {
|
|
12246
|
-
|
|
12420
|
+
emit2("Would prune 0 orphaned embedding chunk file(s). Nothing to do.");
|
|
12247
12421
|
} else {
|
|
12248
|
-
|
|
12249
|
-
for (const p of orphanChunkPaths)
|
|
12422
|
+
emit2(`Would prune orphaned embedding chunks for ${orphanChunkPaths.length} file(s):`);
|
|
12423
|
+
for (const p of orphanChunkPaths) emit2(` ${p}`);
|
|
12250
12424
|
}
|
|
12251
12425
|
return;
|
|
12252
12426
|
}
|
|
@@ -12256,37 +12430,37 @@ function cmdProject(opts) {
|
|
|
12256
12430
|
const prunedTempFiles = pruneSystemTempFiles();
|
|
12257
12431
|
const prunedOrphanChunks = pruneOrphanedChunks();
|
|
12258
12432
|
if (opts.json === true) {
|
|
12259
|
-
|
|
12433
|
+
emit2(JSON.stringify({ pruned: removed, blocked_roots: after, prunedTempFiles: prunedTempFiles.length, prunedOrphanChunkFiles: prunedOrphanChunks.length }, null, 2));
|
|
12260
12434
|
return;
|
|
12261
12435
|
}
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12436
|
+
emit2(`Pruned ${removed} stale root(s). Remaining: ${after.length}`);
|
|
12437
|
+
emit2(`Pruned ${prunedTempFiles.length} stale indexed temp-dir file(s).`);
|
|
12438
|
+
emit2(`Pruned orphaned embedding chunks for ${prunedOrphanChunks.length} file(s).`);
|
|
12265
12439
|
return;
|
|
12266
12440
|
}
|
|
12267
|
-
|
|
12441
|
+
emitErr3(`project: unknown action '${action}'. Use list, exclude, or prune.`);
|
|
12268
12442
|
throw new Error(`unknown project action: ${action}`);
|
|
12269
12443
|
}
|
|
12270
12444
|
function recordCompactDocStat(fullSourceBytes, emittedText, detail) {
|
|
12271
|
-
const bytesSaved =
|
|
12445
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(emittedText, "utf8"));
|
|
12272
12446
|
recordStat("compact_doc", bytesSaved, savedTokensFromBytes(bytesSaved), void 0, detail);
|
|
12273
12447
|
}
|
|
12274
12448
|
function cmdCompactDoc(opts) {
|
|
12275
|
-
const resolved =
|
|
12449
|
+
const resolved = path20.resolve(opts.filePath);
|
|
12276
12450
|
if (opts.heading !== void 0) {
|
|
12277
12451
|
const result = compactDoc(resolved, opts.heading);
|
|
12278
12452
|
if (result === null) {
|
|
12279
|
-
|
|
12453
|
+
emitErr3(`compact-doc: could not read or compact '${resolved}'`);
|
|
12280
12454
|
throw new Error(`could not compact: ${resolved}`);
|
|
12281
12455
|
}
|
|
12282
12456
|
const legacyFullBytes = fs19.statSync(resolved).size;
|
|
12283
12457
|
if (opts.json === true) {
|
|
12284
12458
|
const jsonText = JSON.stringify({ path: resolved, compact: result }, null, 2);
|
|
12285
|
-
|
|
12459
|
+
emit2(jsonText);
|
|
12286
12460
|
recordCompactDocStat(legacyFullBytes, jsonText, resolved);
|
|
12287
12461
|
return;
|
|
12288
12462
|
}
|
|
12289
|
-
|
|
12463
|
+
emit2(result);
|
|
12290
12464
|
recordCompactDocStat(legacyFullBytes, result, resolved);
|
|
12291
12465
|
return;
|
|
12292
12466
|
}
|
|
@@ -12295,7 +12469,7 @@ function cmdCompactDoc(opts) {
|
|
|
12295
12469
|
try {
|
|
12296
12470
|
sentences = requirePositiveStrictInt("--sentences", opts.sentences);
|
|
12297
12471
|
} catch (e) {
|
|
12298
|
-
|
|
12472
|
+
emitErr3(`compact-doc: --sentences must be a positive number, got: "${opts.sentences}"`);
|
|
12299
12473
|
throw new Error(`invalid --sentences: ${opts.sentences}`, { cause: e });
|
|
12300
12474
|
}
|
|
12301
12475
|
}
|
|
@@ -12308,7 +12482,7 @@ function cmdCompactDoc(opts) {
|
|
|
12308
12482
|
try {
|
|
12309
12483
|
sourceText = fs19.readFileSync(resolved, "utf-8");
|
|
12310
12484
|
} catch {
|
|
12311
|
-
|
|
12485
|
+
emitErr3(`compact-doc: could not read or compact '${resolved}'`);
|
|
12312
12486
|
throw new Error(`could not compact: ${resolved}`);
|
|
12313
12487
|
}
|
|
12314
12488
|
body = buildExtractiveCompact(sourceText, sentences);
|
|
@@ -12317,7 +12491,7 @@ function cmdCompactDoc(opts) {
|
|
|
12317
12491
|
} else {
|
|
12318
12492
|
const existing = readCompactBody(compactPath);
|
|
12319
12493
|
if (existing === null) {
|
|
12320
|
-
|
|
12494
|
+
emitErr3(`compact-doc: could not read or compact '${resolved}'`);
|
|
12321
12495
|
throw new Error(`could not compact: ${resolved}`);
|
|
12322
12496
|
}
|
|
12323
12497
|
body = existing;
|
|
@@ -12325,16 +12499,16 @@ function cmdCompactDoc(opts) {
|
|
|
12325
12499
|
const extractiveFullBytes = fs19.statSync(resolved).size;
|
|
12326
12500
|
if (opts.json === true) {
|
|
12327
12501
|
const jsonText = JSON.stringify({ path: resolved, compactPath, rebuilt, compact: body }, null, 2);
|
|
12328
|
-
|
|
12502
|
+
emit2(jsonText);
|
|
12329
12503
|
recordCompactDocStat(extractiveFullBytes, jsonText, resolved);
|
|
12330
12504
|
return;
|
|
12331
12505
|
}
|
|
12332
12506
|
if (opts.show === true) {
|
|
12333
|
-
|
|
12507
|
+
emit2(body);
|
|
12334
12508
|
recordCompactDocStat(extractiveFullBytes, body, resolved);
|
|
12335
12509
|
return;
|
|
12336
12510
|
}
|
|
12337
|
-
|
|
12511
|
+
emit2(
|
|
12338
12512
|
`Compact sidecar ${rebuilt ? "built" : "already fresh"} at ${compactPath} (source: ${resolved}). Use --show to print it, --force to rebuild.`
|
|
12339
12513
|
);
|
|
12340
12514
|
}
|
|
@@ -12375,11 +12549,11 @@ async function cmdFetchImage(opts) {
|
|
|
12375
12549
|
try {
|
|
12376
12550
|
fetched = await fetchBuffer(opts.url);
|
|
12377
12551
|
} catch (e) {
|
|
12378
|
-
|
|
12552
|
+
emitErr3(`fetch-image: network error \u2014 ${extractErrorMessage(e)}`);
|
|
12379
12553
|
throw new Error(`fetch failed: ${opts.url}`, { cause: e });
|
|
12380
12554
|
}
|
|
12381
12555
|
const buf = fetched.body;
|
|
12382
|
-
const outPath = opts.out ??
|
|
12556
|
+
const outPath = opts.out ?? path20.join(os9.tmpdir(), `tg-fetch-${Date.now()}${extensionForContentType(fetched.contentType)}`);
|
|
12383
12557
|
const originalBytes = buf.length;
|
|
12384
12558
|
let shrunkBytes;
|
|
12385
12559
|
let outData;
|
|
@@ -12402,11 +12576,11 @@ async function cmdFetchImage(opts) {
|
|
|
12402
12576
|
}
|
|
12403
12577
|
atomicWriteBytes(finalPath, outData);
|
|
12404
12578
|
if (opts.json === true) {
|
|
12405
|
-
|
|
12579
|
+
emit2(JSON.stringify({ url: opts.url, out: finalPath, originalBytes, shrunkBytes, wasShrunk }, null, 2));
|
|
12406
12580
|
return;
|
|
12407
12581
|
}
|
|
12408
12582
|
const savings = wasShrunk ? ` (saved ${originalBytes - shrunkBytes} bytes)` : " (not shrunk \u2014 already small or unsupported format)";
|
|
12409
|
-
|
|
12583
|
+
emit2(`Fetched ${originalBytes} bytes \u2192 ${finalPath}${savings}`);
|
|
12410
12584
|
}
|
|
12411
12585
|
function cmdHistory(opts) {
|
|
12412
12586
|
let limit = 30;
|
|
@@ -12414,11 +12588,11 @@ function cmdHistory(opts) {
|
|
|
12414
12588
|
try {
|
|
12415
12589
|
limit = requireNonNegativeStrictInt("--limit", opts.limit);
|
|
12416
12590
|
} catch (e) {
|
|
12417
|
-
|
|
12591
|
+
emitErr3(`history: --limit must be a non-negative number, got: "${opts.limit}"`);
|
|
12418
12592
|
throw new Error(`invalid --limit: ${opts.limit}`, { cause: e });
|
|
12419
12593
|
}
|
|
12420
12594
|
if (limit === 0) {
|
|
12421
|
-
|
|
12595
|
+
emitErr3(`history: --limit must be a positive number, got: "${opts.limit}"`);
|
|
12422
12596
|
throw new Error(`invalid --limit: ${opts.limit}`);
|
|
12423
12597
|
}
|
|
12424
12598
|
}
|
|
@@ -12442,27 +12616,32 @@ function cmdHistory(opts) {
|
|
|
12442
12616
|
summary: typeof v["url"] === "string" ? v["url"] : ""
|
|
12443
12617
|
};
|
|
12444
12618
|
}).filter((x) => x !== null);
|
|
12445
|
-
const
|
|
12619
|
+
const allItems = [...bashItems, ...webItems].sort((a, b) => b.storedAt - a.storedAt);
|
|
12620
|
+
const items = allItems.slice(0, limit);
|
|
12621
|
+
if (items.length < allItems.length) {
|
|
12622
|
+
process.stderr.write(`Showing ${items.length} of ${allItems.length} entries (raise --limit to see the rest).
|
|
12623
|
+
`);
|
|
12624
|
+
}
|
|
12446
12625
|
if (opts.json === true) {
|
|
12447
12626
|
process.stdout.write(JSON.stringify(items, null, 2) + "\n");
|
|
12448
12627
|
return;
|
|
12449
12628
|
}
|
|
12450
12629
|
if (items.length === 0) {
|
|
12451
|
-
|
|
12630
|
+
emit2("No history entries found (no cached bash outputs or web fetches).");
|
|
12452
12631
|
return;
|
|
12453
12632
|
}
|
|
12454
12633
|
const pad2 = (s, n) => s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
12455
|
-
|
|
12634
|
+
emit2(`${pad2("type", 5)} ${pad2("id", 18)} summary`);
|
|
12456
12635
|
for (const item of items) {
|
|
12457
12636
|
const preview = item.summary.length > 80 ? item.summary.slice(0, 77) + "..." : item.summary;
|
|
12458
|
-
|
|
12637
|
+
emit2(`${pad2(item.type, 5)} ${pad2(item.id, 18)} ${preview}`);
|
|
12459
12638
|
}
|
|
12460
12639
|
}
|
|
12461
12640
|
|
|
12462
12641
|
// src/cli_bootstrap_audit.ts
|
|
12463
12642
|
import * as fs20 from "node:fs";
|
|
12464
12643
|
import * as os10 from "node:os";
|
|
12465
|
-
import * as
|
|
12644
|
+
import * as path21 from "node:path";
|
|
12466
12645
|
import * as readline from "node:readline";
|
|
12467
12646
|
var FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["description", "tools", "allowed-tools", "allowed_tools"]);
|
|
12468
12647
|
function parseBudget(name, value) {
|
|
@@ -12521,17 +12700,17 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12521
12700
|
logicalRoot = await fs20.promises.realpath(root);
|
|
12522
12701
|
} catch (error) {
|
|
12523
12702
|
if (error.code === "ENOENT") return entries;
|
|
12524
|
-
diagnostics.push({ path:
|
|
12703
|
+
diagnostics.push({ path: path21.resolve(root), reason: "unreadable root" });
|
|
12525
12704
|
return entries;
|
|
12526
12705
|
}
|
|
12527
12706
|
if (rootIsLink && !followLinks) {
|
|
12528
|
-
diagnostics.push({ path:
|
|
12707
|
+
diagnostics.push({ path: path21.resolve(root), reason: "external root link skipped (use --follow-links)" });
|
|
12529
12708
|
return entries;
|
|
12530
12709
|
}
|
|
12531
12710
|
const canonicalKey = (candidate) => process.platform === "win32" ? candidate.toLocaleLowerCase() : candidate;
|
|
12532
12711
|
const isContained = (candidate, allowedRoot) => {
|
|
12533
|
-
const relative3 =
|
|
12534
|
-
return relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${
|
|
12712
|
+
const relative3 = path21.relative(canonicalKey(allowedRoot), canonicalKey(candidate));
|
|
12713
|
+
return relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${path21.sep}`) && !path21.isAbsolute(relative3);
|
|
12535
12714
|
};
|
|
12536
12715
|
async function visit(candidate, allowedRoot, topLevel = false) {
|
|
12537
12716
|
let real;
|
|
@@ -12540,13 +12719,13 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12540
12719
|
candidateIsLink = (await fs20.promises.lstat(candidate)).isSymbolicLink();
|
|
12541
12720
|
real = await fs20.promises.realpath(candidate);
|
|
12542
12721
|
} catch {
|
|
12543
|
-
diagnostics.push({ path:
|
|
12722
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "broken or unreadable link" });
|
|
12544
12723
|
return;
|
|
12545
12724
|
}
|
|
12546
12725
|
const withinAllowedRoot = allowedRoot === void 0 ? isContained(real, logicalRoot) : isContained(real, allowedRoot);
|
|
12547
12726
|
const canFollowExternal = followLinks && topLevel;
|
|
12548
12727
|
if (!withinAllowedRoot && !canFollowExternal) {
|
|
12549
|
-
diagnostics.push({ path:
|
|
12728
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: candidateIsLink ? "external link skipped (use --follow-links)" : "canonical target outside scan root" });
|
|
12550
12729
|
return;
|
|
12551
12730
|
}
|
|
12552
12731
|
const trustedRoot = !withinAllowedRoot && canFollowExternal ? real : allowedRoot ?? real;
|
|
@@ -12554,7 +12733,7 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12554
12733
|
try {
|
|
12555
12734
|
stat = await fs20.promises.stat(real);
|
|
12556
12735
|
} catch {
|
|
12557
|
-
diagnostics.push({ path:
|
|
12736
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "unreadable" });
|
|
12558
12737
|
return;
|
|
12559
12738
|
}
|
|
12560
12739
|
const canonical = process.platform === "win32" ? real.toLocaleLowerCase() : real;
|
|
@@ -12565,14 +12744,14 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12565
12744
|
try {
|
|
12566
12745
|
children = await fs20.promises.readdir(real, { withFileTypes: true });
|
|
12567
12746
|
} catch {
|
|
12568
|
-
diagnostics.push({ path:
|
|
12747
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "unreadable directory" });
|
|
12569
12748
|
return;
|
|
12570
12749
|
}
|
|
12571
12750
|
children.sort((a, b) => a.name.localeCompare(b.name));
|
|
12572
|
-
for (const child of children) await visit(
|
|
12751
|
+
for (const child of children) await visit(path21.join(real, child.name), trustedRoot);
|
|
12573
12752
|
return;
|
|
12574
12753
|
}
|
|
12575
|
-
if (!stat.isFile() ||
|
|
12754
|
+
if (!stat.isFile() || path21.extname(real).toLowerCase() !== ".md") return;
|
|
12576
12755
|
if (seenFiles.has(canonical)) return;
|
|
12577
12756
|
seenFiles.add(canonical);
|
|
12578
12757
|
try {
|
|
@@ -12582,36 +12761,38 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12582
12761
|
const toolsBytes = Buffer.byteLength(metadata.tools, "utf8");
|
|
12583
12762
|
entries.push({
|
|
12584
12763
|
kind,
|
|
12585
|
-
path:
|
|
12764
|
+
path: path21.resolve(real),
|
|
12586
12765
|
description_bytes: descriptionBytes,
|
|
12587
12766
|
tools_bytes: toolsBytes,
|
|
12588
12767
|
metadata_bytes: descriptionBytes + toolsBytes,
|
|
12589
12768
|
estimated_tokens: Math.floor((descriptionBytes + toolsBytes) / 4)
|
|
12590
12769
|
});
|
|
12591
12770
|
} catch {
|
|
12592
|
-
diagnostics.push({ path:
|
|
12771
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "unreadable file" });
|
|
12593
12772
|
}
|
|
12594
12773
|
}
|
|
12595
12774
|
try {
|
|
12596
12775
|
const children = await fs20.promises.readdir(logicalRoot, { withFileTypes: true });
|
|
12597
12776
|
children.sort((a, b) => a.name.localeCompare(b.name));
|
|
12598
|
-
for (const child of children) await visit(
|
|
12777
|
+
for (const child of children) await visit(path21.join(logicalRoot, child.name), void 0, true);
|
|
12599
12778
|
} catch {
|
|
12600
|
-
diagnostics.push({ path:
|
|
12779
|
+
diagnostics.push({ path: path21.resolve(root), reason: "unreadable root" });
|
|
12601
12780
|
}
|
|
12602
12781
|
return entries;
|
|
12603
12782
|
}
|
|
12604
12783
|
async function buildBootstrapAudit(opts = {}) {
|
|
12605
12784
|
const project = resolveProjectRoot(opts.project === void 0 ? {} : { project: opts.project });
|
|
12606
|
-
const home =
|
|
12785
|
+
const home = path21.resolve(opts.home ?? os10.homedir());
|
|
12607
12786
|
const context = buildStats(project, home, opts.project === void 0 ? [] : [opts.project]);
|
|
12608
12787
|
const diagnostics = [];
|
|
12609
12788
|
const top = opts.top === void 0 ? 10 : parseBudget("--top", opts.top) ?? 10;
|
|
12610
12789
|
const visitedDirs = /* @__PURE__ */ new Set();
|
|
12611
12790
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
12612
|
-
const agents = await scanMetadataRoot(
|
|
12613
|
-
const skills = await scanMetadataRoot(
|
|
12614
|
-
const
|
|
12791
|
+
const agents = await scanMetadataRoot(path21.join(home, ".claude", "agents"), "agent", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
|
|
12792
|
+
const skills = await scanMetadataRoot(path21.join(home, ".claude", "skills"), "skill", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
|
|
12793
|
+
const rankedEntries = [...agents, ...skills].sort((a, b) => b.metadata_bytes - a.metadata_bytes || a.path.localeCompare(b.path));
|
|
12794
|
+
const largestTotal = rankedEntries.length;
|
|
12795
|
+
const largest = rankedEntries.slice(0, top);
|
|
12615
12796
|
const metadataBytes = [...agents, ...skills].reduce((sum, entry) => sum + entry.metadata_bytes, 0);
|
|
12616
12797
|
const totalTokens = context.total_tokens + Math.floor(metadataBytes / 4);
|
|
12617
12798
|
const warnTokens = parseBudget("--warn-tokens", opts.warnTokens);
|
|
@@ -12635,6 +12816,8 @@ async function buildBootstrapAudit(opts = {}) {
|
|
|
12635
12816
|
total_estimated_tokens: totalTokens,
|
|
12636
12817
|
counts: { agents: agents.length, skills: skills.length, metadata_files: agents.length + skills.length },
|
|
12637
12818
|
largest,
|
|
12819
|
+
largestTotal,
|
|
12820
|
+
largestTruncated: largest.length < largestTotal,
|
|
12638
12821
|
diagnostics,
|
|
12639
12822
|
budgets: { warn_tokens: warnTokens, fail_tokens: failTokens, warn_bytes: warnBytes, fail_bytes: failBytes, warnings, failures }
|
|
12640
12823
|
};
|
|
@@ -12658,6 +12841,8 @@ async function runBootstrapAudit(opts = {}) {
|
|
|
12658
12841
|
`);
|
|
12659
12842
|
process.stdout.write("Largest metadata entries:\n");
|
|
12660
12843
|
for (const entry of result.largest) process.stdout.write(` ${entry.metadata_bytes.toString().padStart(7)} bytes ${entry.path}
|
|
12844
|
+
`);
|
|
12845
|
+
if (result.largestTruncated) process.stdout.write(` ...and ${result.largestTotal - result.largest.length} more (raise --top to see them).
|
|
12661
12846
|
`);
|
|
12662
12847
|
for (const diagnostic of result.diagnostics) process.stderr.write(`token-goat: bootstrap-audit: skipped ${diagnostic.path} (${diagnostic.reason})
|
|
12663
12848
|
`);
|
|
@@ -12671,7 +12856,7 @@ async function runBootstrapAudit(opts = {}) {
|
|
|
12671
12856
|
|
|
12672
12857
|
// src/cli_memory.ts
|
|
12673
12858
|
import * as fs21 from "node:fs";
|
|
12674
|
-
import * as
|
|
12859
|
+
import * as path22 from "node:path";
|
|
12675
12860
|
function removeExactDupLines(text, report) {
|
|
12676
12861
|
if (report.exactDupLines.length === 0) return text;
|
|
12677
12862
|
const toRemove = new Set(report.exactDupLines.map(([, dupIdx]) => dupIdx));
|
|
@@ -12729,7 +12914,7 @@ function printReport(reports, clusters) {
|
|
|
12729
12914
|
} else {
|
|
12730
12915
|
for (const cluster of clusters) {
|
|
12731
12916
|
w(
|
|
12732
|
-
` [${cluster.method}, similarity ${cluster.similarity}, ~${cluster.tokens} tok] ${cluster.members.map((m) =>
|
|
12917
|
+
` [${cluster.method}, similarity ${cluster.similarity}, ~${cluster.tokens} tok] ${cluster.members.map((m) => path22.basename(m)).join(", ")}
|
|
12733
12918
|
`
|
|
12734
12919
|
);
|
|
12735
12920
|
}
|
|
@@ -12744,7 +12929,7 @@ async function runMemoryCommand(opts = {}) {
|
|
|
12744
12929
|
const claudeMds = findClaudeMdFiles(projectRoot);
|
|
12745
12930
|
const reports = auditClaudeMd(claudeMds);
|
|
12746
12931
|
const memoryMdPath = findMemoryMd(projectRoot);
|
|
12747
|
-
const clusters = memoryMdPath !== null ? await findContentDuplicates(
|
|
12932
|
+
const clusters = memoryMdPath !== null ? await findContentDuplicates(path22.dirname(memoryMdPath)) : [];
|
|
12748
12933
|
printReport(reports, clusters);
|
|
12749
12934
|
if (opts.fix !== true) return;
|
|
12750
12935
|
const changes = [];
|
|
@@ -12790,16 +12975,16 @@ async function runMemoryCommand(opts = {}) {
|
|
|
12790
12975
|
|
|
12791
12976
|
// src/cli_waste.ts
|
|
12792
12977
|
import * as fs24 from "node:fs";
|
|
12793
|
-
import * as
|
|
12978
|
+
import * as path25 from "node:path";
|
|
12794
12979
|
|
|
12795
12980
|
// src/waste.ts
|
|
12796
12981
|
import * as fs22 from "node:fs";
|
|
12797
12982
|
import * as os11 from "node:os";
|
|
12798
|
-
import * as
|
|
12983
|
+
import * as path23 from "node:path";
|
|
12799
12984
|
function projectTranscriptsDir(projectRoot) {
|
|
12800
|
-
const rootStr =
|
|
12985
|
+
const rootStr = path23.resolve(projectRoot);
|
|
12801
12986
|
const slug = rootStr.replace(/[^A-Za-z0-9]/g, "-");
|
|
12802
|
-
return
|
|
12987
|
+
return path23.join(os11.homedir(), ".claude", "projects", slug);
|
|
12803
12988
|
}
|
|
12804
12989
|
function findLatestTranscript(projectRoot) {
|
|
12805
12990
|
const dir = projectTranscriptsDir(projectRoot);
|
|
@@ -12811,7 +12996,7 @@ function findLatestTranscript(projectRoot) {
|
|
|
12811
12996
|
}
|
|
12812
12997
|
let best = null;
|
|
12813
12998
|
for (const name of entries) {
|
|
12814
|
-
const full =
|
|
12999
|
+
const full = path23.join(dir, name);
|
|
12815
13000
|
let stat;
|
|
12816
13001
|
try {
|
|
12817
13002
|
stat = fs22.statSync(full);
|
|
@@ -13041,7 +13226,7 @@ async function buildWasteReport(transcriptPath, opts = {}) {
|
|
|
13041
13226
|
|
|
13042
13227
|
// src/copilot_waste.ts
|
|
13043
13228
|
import * as fs23 from "node:fs";
|
|
13044
|
-
import * as
|
|
13229
|
+
import * as path24 from "node:path";
|
|
13045
13230
|
var HOOK_RECORD_TYPES = /* @__PURE__ */ new Set(["hook.start", "hook.end"]);
|
|
13046
13231
|
function splitInjectedBlocks(transformed) {
|
|
13047
13232
|
const out2 = [];
|
|
@@ -13060,7 +13245,7 @@ function splitInjectedBlocks(transformed) {
|
|
|
13060
13245
|
return out2;
|
|
13061
13246
|
}
|
|
13062
13247
|
function findLatestCopilotSession() {
|
|
13063
|
-
const root =
|
|
13248
|
+
const root = path24.join(copilotCliUserRoot(), "session-state");
|
|
13064
13249
|
let entries;
|
|
13065
13250
|
try {
|
|
13066
13251
|
entries = fs23.readdirSync(root);
|
|
@@ -13070,7 +13255,7 @@ function findLatestCopilotSession() {
|
|
|
13070
13255
|
let best = null;
|
|
13071
13256
|
let bestMtime = -Infinity;
|
|
13072
13257
|
for (const entry of entries) {
|
|
13073
|
-
const candidate =
|
|
13258
|
+
const candidate = path24.join(root, entry, "events.jsonl");
|
|
13074
13259
|
try {
|
|
13075
13260
|
const st = fs23.statSync(candidate);
|
|
13076
13261
|
if (!st.isFile()) continue;
|
|
@@ -13091,7 +13276,7 @@ function buildCopilotWasteReport(eventsPath) {
|
|
|
13091
13276
|
const raw = fs23.readFileSync(eventsPath, "utf-8");
|
|
13092
13277
|
const report = {
|
|
13093
13278
|
sessionPath: eventsPath,
|
|
13094
|
-
sessionId:
|
|
13279
|
+
sessionId: path24.basename(path24.dirname(eventsPath)),
|
|
13095
13280
|
turns: 0,
|
|
13096
13281
|
tokens: null,
|
|
13097
13282
|
blocks: [],
|
|
@@ -13370,7 +13555,7 @@ function printResidentContext(resident, w) {
|
|
|
13370
13555
|
}
|
|
13371
13556
|
async function runWasteCommand(opts = {}) {
|
|
13372
13557
|
if (opts.copilot === true) {
|
|
13373
|
-
const eventsPath = opts.transcript !== void 0 ?
|
|
13558
|
+
const eventsPath = opts.transcript !== void 0 ? path25.resolve(opts.transcript) : findLatestCopilotSession();
|
|
13374
13559
|
if (eventsPath === null || !fs24.existsSync(eventsPath)) {
|
|
13375
13560
|
const detail = eventsPath === null ? "no Copilot CLI session found under <copilot-home>/session-state" : `Copilot session event log not found: ${eventsPath}`;
|
|
13376
13561
|
if (opts.json === true) {
|
|
@@ -13394,7 +13579,7 @@ async function runWasteCommand(opts = {}) {
|
|
|
13394
13579
|
return;
|
|
13395
13580
|
}
|
|
13396
13581
|
const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
13397
|
-
const transcriptPath = opts.transcript !== void 0 ?
|
|
13582
|
+
const transcriptPath = opts.transcript !== void 0 ? path25.resolve(opts.transcript) : findLatestTranscript(projectRoot);
|
|
13398
13583
|
if (transcriptPath === null) {
|
|
13399
13584
|
if (opts.json === true) {
|
|
13400
13585
|
process.stdout.write(`${JSON.stringify({ error: "no session transcript found", project: projectRoot })}
|
|
@@ -13425,7 +13610,7 @@ async function runWasteCommand(opts = {}) {
|
|
|
13425
13610
|
|
|
13426
13611
|
// src/session_read.ts
|
|
13427
13612
|
import * as fs25 from "node:fs";
|
|
13428
|
-
import * as
|
|
13613
|
+
import * as path26 from "node:path";
|
|
13429
13614
|
import * as readline2 from "node:readline";
|
|
13430
13615
|
function resolveSessionTranscript(arg, opts = {}) {
|
|
13431
13616
|
if (arg !== void 0 && arg !== "") {
|
|
@@ -13433,7 +13618,7 @@ function resolveSessionTranscript(arg, opts = {}) {
|
|
|
13433
13618
|
const projectRoot2 = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
13434
13619
|
const dir = projectTranscriptsDir(projectRoot2);
|
|
13435
13620
|
const byId = arg.endsWith(".jsonl") ? arg : `${arg}.jsonl`;
|
|
13436
|
-
const candidate =
|
|
13621
|
+
const candidate = path26.join(dir, byId);
|
|
13437
13622
|
if (fs25.existsSync(candidate) && fs25.statSync(candidate).isFile()) return candidate;
|
|
13438
13623
|
return null;
|
|
13439
13624
|
}
|
|
@@ -13592,7 +13777,7 @@ function formatSessionSlice(turns) {
|
|
|
13592
13777
|
// src/session_audit.ts
|
|
13593
13778
|
import * as fs26 from "node:fs";
|
|
13594
13779
|
import * as os12 from "node:os";
|
|
13595
|
-
import * as
|
|
13780
|
+
import * as path27 from "node:path";
|
|
13596
13781
|
import * as readline3 from "node:readline";
|
|
13597
13782
|
function emptyMeasured() {
|
|
13598
13783
|
return { apiCalls: 0, inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };
|
|
@@ -13639,6 +13824,64 @@ var CACHE_READ_MULTIPLIER = 0.1;
|
|
|
13639
13824
|
var READ_DIVERT_MARKER_RE = /(?:was already read this session|Already read |You've already read|Use `token-goat (?:section|read|bash-output|config-get|skeleton)|token-goat bash-output --file)/;
|
|
13640
13825
|
var READ_DIVERT_MAX_BYTES = 2500;
|
|
13641
13826
|
var READ_FULL_SERVE_MIN_BYTES = 10240;
|
|
13827
|
+
var DENY_TEMPLATES = [
|
|
13828
|
+
{ kind: "node_modules_deny", re: /node_modules is typically noise/ },
|
|
13829
|
+
{ kind: "lock_file_deny", re: /Lock files are rarely useful to read in full/ },
|
|
13830
|
+
{ kind: "tsbuildinfo_deny", re: /TypeScript incremental build cache file/ },
|
|
13831
|
+
{ kind: "generated_build_deny", re: /Generated\/build artifact — read the source file instead\./ },
|
|
13832
|
+
{ kind: "compact_sidecar_served", re: /Serving the extractive compact sidecar in place of the full file/ },
|
|
13833
|
+
{ kind: "notebook_sidecar_served", re: /Serving the output-stripped notebook in place of the full file/ },
|
|
13834
|
+
// Must stay ABOVE markdown_heading_tree_deny: this deny's message embeds the same "Large markdown file (N headings)" guidance block, so the generic alternative would swallow it and the new intervention would be uncountable. DENY_TEMPLATES is scanned with `.find`, so the more specific wording has to come first.
|
|
13835
|
+
{ kind: "subagent_markdown_first_read_deny", re: /Subagent first read of a large markdown file/ },
|
|
13836
|
+
{ kind: "markdown_heading_tree_deny", re: /Large markdown file \(\d+ headings\)/ },
|
|
13837
|
+
// The first alternative is a wording hooks_read.ts no longer emits (it claimed a compact-manifest
|
|
13838
|
+
// section that never existed, reworded in 3d044feb). It stays because this table classifies a
|
|
13839
|
+
// historical corpus, not just today's output: transcripts written before the rewording still
|
|
13840
|
+
// carry the old text, and dropping the alternative silently shrank this kind from 51 events to
|
|
13841
|
+
// 30 -- 41% of its history -- with a green suite. A superseded alternative is only removable
|
|
13842
|
+
// once no transcript contains it, which source code cannot tell you. See the superseded-wording
|
|
13843
|
+
// test in tests/deny_outcomes.test.ts.
|
|
13844
|
+
{ kind: "memory_md_reread_deny", re: /MEMORY\.md was read this session\. Its content is in the compact manifest|already read this session\. Memory files rarely change mid-session/ },
|
|
13845
|
+
{ kind: "improve_state_reread_deny", re: /Orchestrator state already read this session/ },
|
|
13846
|
+
{ kind: "env_reread_deny", re: /Environment files rarely change mid-session/ },
|
|
13847
|
+
{ kind: "session_artifact_truncated_deny", re: /File was truncated on last read\. Use `token-goat bash-output/ },
|
|
13848
|
+
{ kind: "session_artifact_unchanged_deny", re: /is unchanged since last read\. Use `token-goat bash-output/ },
|
|
13849
|
+
{ kind: "session_artifact_diff_deny", re: /Content changed since last read of [\s\S]*?bash-output --file/ },
|
|
13850
|
+
{ kind: "session_artifact_large_deny", re: /(?:Session transcript|Tool-result file) is large \(/ },
|
|
13851
|
+
{ kind: "session_artifact_generic_reread_deny", re: /already read this session\. Use `token-goat bash-output --file/ },
|
|
13852
|
+
{ kind: "truncated_read_deny", re: /File was truncated on last read \(>33K tokens\)/ },
|
|
13853
|
+
{ kind: "doc_unchanged_deny", re: /is unchanged since last read\. Use `token-goat (?:section|read)/ },
|
|
13854
|
+
{ kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?Use `token-goat (?:section|read)/ },
|
|
13855
|
+
{ kind: "read_served_deny", re: /was already served in this session, byte for byte/ },
|
|
13856
|
+
{ kind: "markdown_already_read_deny", re: /Markdown file already read this session\. Use `token-goat section/ },
|
|
13857
|
+
{ kind: "read_count_deny", re: /Read this file \d+ times already/ },
|
|
13858
|
+
{ kind: "generic_reread_deny", re: /was already read this session \(\d+ read/ },
|
|
13859
|
+
{ kind: "large_file_deny", re: /is very large \(\d+(?:\.\d+)?KB\)\./ },
|
|
13860
|
+
{ kind: "file_type_handler_deny", re: /too large to preview \(exceeds the in-hook scan cap\)|cannot be read as text\.|Read cannot return spreadsheet content|Read cannot return slide content|Read cannot return document content|Use Read with offset and limit parameters to read specific line ranges/ }
|
|
13861
|
+
];
|
|
13862
|
+
var DENY_KINDS_WITH_SIZE = /* @__PURE__ */ new Set(["large_file_deny", "session_artifact_large_deny", "file_type_handler_deny"]);
|
|
13863
|
+
var DENY_SIZE_RE = /(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)\b/;
|
|
13864
|
+
function parseWithheldBytes(kind, text) {
|
|
13865
|
+
if (!DENY_KINDS_WITH_SIZE.has(kind)) return null;
|
|
13866
|
+
const m = DENY_SIZE_RE.exec(text);
|
|
13867
|
+
if (m === null) return null;
|
|
13868
|
+
const n = Number.parseFloat(m[1]);
|
|
13869
|
+
const unit = m[2].toUpperCase();
|
|
13870
|
+
const mult = unit === "B" ? 1 : unit === "KB" ? 1024 : unit === "MB" ? 1024 * 1024 : unit === "GB" ? 1024 ** 3 : 1024 ** 4;
|
|
13871
|
+
return Math.round(n * mult);
|
|
13872
|
+
}
|
|
13873
|
+
var SURGICAL_COMMAND_RE = /(?:^|[\s;&|(])(?:token-goat|tg|node\s+\S*token-goat\.mjs)\s+(read|section|symbol|skeleton|outline|bash-output|config-get|skill-section|skill-body)\b/;
|
|
13874
|
+
var SHELL_READER_TOKEN_RE = /\b(?:sed|grep|cat|head|tail|awk|rg|less|nl|python3?|node)\b/;
|
|
13875
|
+
function escapeRegExp2(s) {
|
|
13876
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13877
|
+
}
|
|
13878
|
+
function shellReadMatch(command, basename8) {
|
|
13879
|
+
if (basename8 === "" || !SHELL_READER_TOKEN_RE.test(command)) return "none";
|
|
13880
|
+
if (!command.includes(basename8)) return "none";
|
|
13881
|
+
const escaped = escapeRegExp2(basename8);
|
|
13882
|
+
const strict = new RegExp(`(?:^|["'\`/\\\\\\s])${escaped}(?:$|["'\`/\\\\\\s,;:)])`);
|
|
13883
|
+
return strict.test(command) ? "match" : "ambiguous";
|
|
13884
|
+
}
|
|
13642
13885
|
var BASH_FILTER_MARKER_RE = /\[token-goat[:\]]/;
|
|
13643
13886
|
var BASH_SMALL_RESULT_MAX_BYTES = 1024;
|
|
13644
13887
|
function commandHead(raw) {
|
|
@@ -13693,7 +13936,7 @@ function listCorpusTranscripts(corpusDir) {
|
|
|
13693
13936
|
const found = [];
|
|
13694
13937
|
const walk = (dir, entries) => {
|
|
13695
13938
|
for (const entry of entries) {
|
|
13696
|
-
const full =
|
|
13939
|
+
const full = path27.join(dir, entry.name);
|
|
13697
13940
|
if (entry.name.endsWith(".jsonl")) {
|
|
13698
13941
|
found.push(full);
|
|
13699
13942
|
} else if (entry.isDirectory()) {
|
|
@@ -13711,9 +13954,9 @@ function listCorpusTranscripts(corpusDir) {
|
|
|
13711
13954
|
return found.sort();
|
|
13712
13955
|
}
|
|
13713
13956
|
function defaultCorpusDir() {
|
|
13714
|
-
return
|
|
13957
|
+
return path27.join(os12.homedir(), ".claude", "projects");
|
|
13715
13958
|
}
|
|
13716
|
-
async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap) {
|
|
13959
|
+
async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows) {
|
|
13717
13960
|
const isLane = filePath.split(/[\\/]/).includes("subagents");
|
|
13718
13961
|
let laneFirstPrefix = null;
|
|
13719
13962
|
let laneBriefBytes = -1;
|
|
@@ -13721,8 +13964,12 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13721
13964
|
const bashHeadById = /* @__PURE__ */ new Map();
|
|
13722
13965
|
let pendingBashReread = [];
|
|
13723
13966
|
const readCallById = /* @__PURE__ */ new Map();
|
|
13967
|
+
const readPathById = /* @__PURE__ */ new Map();
|
|
13968
|
+
const editPathById = /* @__PURE__ */ new Map();
|
|
13969
|
+
const editErrorById = /* @__PURE__ */ new Map();
|
|
13724
13970
|
const readPathEpoch = /* @__PURE__ */ new Map();
|
|
13725
13971
|
let compactEpoch = 0;
|
|
13972
|
+
const openDenies = [];
|
|
13726
13973
|
let sawTokenGoatHook = false;
|
|
13727
13974
|
let fileRepeats = 0;
|
|
13728
13975
|
let fileRepeatsFullNoRange = 0;
|
|
@@ -13817,13 +14064,38 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13817
14064
|
toolNameById.set(block["id"], name);
|
|
13818
14065
|
toolRollup(toolMap, name).calls += 1;
|
|
13819
14066
|
const input = block["input"] !== null && typeof block["input"] === "object" ? block["input"] : {};
|
|
14067
|
+
let readNorm;
|
|
14068
|
+
let editNorm;
|
|
13820
14069
|
if (name === "Bash" && typeof input["command"] === "string") {
|
|
13821
14070
|
bashHeadById.set(block["id"], commandHead(input["command"]));
|
|
13822
14071
|
} else if (name === "Read" && typeof input["file_path"] === "string") {
|
|
13823
|
-
|
|
13824
|
-
const priorEpoch = readPathEpoch.get(
|
|
14072
|
+
readNorm = normalizeReadPath(input["file_path"]);
|
|
14073
|
+
const priorEpoch = readPathEpoch.get(readNorm);
|
|
13825
14074
|
readCallById.set(block["id"], { wasSeenBefore: priorEpoch !== void 0, hasRange: input["offset"] !== void 0 || input["limit"] !== void 0, afterCompaction: priorEpoch !== void 0 && compactEpoch > priorEpoch });
|
|
13826
|
-
|
|
14075
|
+
readPathById.set(block["id"], readNorm);
|
|
14076
|
+
readPathEpoch.set(readNorm, compactEpoch);
|
|
14077
|
+
} else if (name === "Edit" && typeof input["file_path"] === "string") {
|
|
14078
|
+
editNorm = normalizeReadPath(input["file_path"]);
|
|
14079
|
+
editPathById.set(block["id"], editNorm);
|
|
14080
|
+
s.editErrorBaseline.totalEdits += 1;
|
|
14081
|
+
}
|
|
14082
|
+
if (openDenies.length > 0) {
|
|
14083
|
+
const bashCommand = name === "Bash" && typeof input["command"] === "string" ? input["command"] : void 0;
|
|
14084
|
+
for (const o of openDenies) {
|
|
14085
|
+
if (o.toolCalls.length >= 10) continue;
|
|
14086
|
+
const isReadSamePath = readNorm !== void 0 && o.path !== "" && readNorm === o.path;
|
|
14087
|
+
const isSurgicalSamePath = bashCommand !== void 0 && o.basename !== "" && SURGICAL_COMMAND_RE.test(bashCommand) && bashCommand.includes(o.basename);
|
|
14088
|
+
const shellRead = bashCommand !== void 0 && o.basename !== "" ? shellReadMatch(bashCommand, o.basename) : "none";
|
|
14089
|
+
const isEditSamePath = editNorm !== void 0 && o.path !== "" && editNorm === o.path;
|
|
14090
|
+
o.toolCalls.push({
|
|
14091
|
+
isReadSamePath,
|
|
14092
|
+
isSurgicalSamePath,
|
|
14093
|
+
isShellReadSamePath: shellRead === "match",
|
|
14094
|
+
isShellReadAmbiguous: shellRead === "ambiguous",
|
|
14095
|
+
editCallId: isEditSamePath ? block["id"] : void 0
|
|
14096
|
+
});
|
|
14097
|
+
if (o.toolCalls.length === 3) o.compactEpochAtWindow = compactEpoch;
|
|
14098
|
+
}
|
|
13827
14099
|
}
|
|
13828
14100
|
}
|
|
13829
14101
|
let inputBytes;
|
|
@@ -13852,8 +14124,27 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13852
14124
|
if (!toolNameById.has(id)) roll.calls += 1;
|
|
13853
14125
|
roll.resultBytes += bytes;
|
|
13854
14126
|
roll.resultEstTokens += estimateTokensFromLength(bytes);
|
|
14127
|
+
if (name === "Edit" && block["is_error"] === true) {
|
|
14128
|
+
s.editErrorBaseline.totalErrors += 1;
|
|
14129
|
+
editErrorById.set(id, true);
|
|
14130
|
+
}
|
|
13855
14131
|
if (name === "Read") {
|
|
13856
14132
|
s.readInterception.readResults += 1;
|
|
14133
|
+
const denyTemplate = DENY_TEMPLATES.find((t) => t.re.test(text));
|
|
14134
|
+
if (denyTemplate !== void 0) {
|
|
14135
|
+
const path30 = readPathById.get(id) ?? "";
|
|
14136
|
+
const lastSlash = path30.lastIndexOf("/");
|
|
14137
|
+
openDenies.push({
|
|
14138
|
+
kind: denyTemplate.kind,
|
|
14139
|
+
withheldBytes: parseWithheldBytes(denyTemplate.kind, text),
|
|
14140
|
+
path: path30,
|
|
14141
|
+
basename: lastSlash === -1 ? path30 : path30.slice(lastSlash + 1),
|
|
14142
|
+
callIndexAtDeny: perCall.length,
|
|
14143
|
+
compactEpochAtOpen: compactEpoch,
|
|
14144
|
+
compactEpochAtWindow: null,
|
|
14145
|
+
toolCalls: []
|
|
14146
|
+
});
|
|
14147
|
+
}
|
|
13857
14148
|
if (bytes < READ_DIVERT_MAX_BYTES && READ_DIVERT_MARKER_RE.test(text)) {
|
|
13858
14149
|
s.readInterception.divertedByMarker += 1;
|
|
13859
14150
|
s.readInterception.divertedBytes += bytes;
|
|
@@ -13990,6 +14281,42 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13990
14281
|
}
|
|
13991
14282
|
flushLane(0);
|
|
13992
14283
|
flushLane(1);
|
|
14284
|
+
for (const o of openDenies) {
|
|
14285
|
+
if (o.compactEpochAtWindow === null) o.compactEpochAtWindow = compactEpoch;
|
|
14286
|
+
const windowCalls = o.toolCalls.slice(0, 3);
|
|
14287
|
+
let outcome;
|
|
14288
|
+
if (o.compactEpochAtWindow > o.compactEpochAtOpen) {
|
|
14289
|
+
outcome = "compacted";
|
|
14290
|
+
} else if (windowCalls.some((c) => c.isReadSamePath)) {
|
|
14291
|
+
outcome = "retried";
|
|
14292
|
+
} else if (windowCalls.some((c) => c.isSurgicalSamePath)) {
|
|
14293
|
+
outcome = "substituted";
|
|
14294
|
+
} else if (windowCalls.some((c) => c.isShellReadSamePath)) {
|
|
14295
|
+
outcome = "shell_read";
|
|
14296
|
+
} else if (o.toolCalls.length < 3) {
|
|
14297
|
+
outcome = "unresolved";
|
|
14298
|
+
} else {
|
|
14299
|
+
outcome = "abandoned";
|
|
14300
|
+
}
|
|
14301
|
+
const retriedWithin10 = o.toolCalls.some((c) => c.isReadSamePath);
|
|
14302
|
+
const shellReadAmbiguous = windowCalls.some((c) => c.isShellReadAmbiguous);
|
|
14303
|
+
const editWithin10 = o.toolCalls.some((c) => c.editCallId !== void 0);
|
|
14304
|
+
const editErrorWithin10 = o.toolCalls.some((c) => c.editCallId !== void 0 && editErrorById.get(c.editCallId) === true);
|
|
14305
|
+
const R = perCall.length - o.callIndexAtDeny;
|
|
14306
|
+
const nextCall = perCall[o.callIndexAtDeny];
|
|
14307
|
+
denyRows.push({
|
|
14308
|
+
kind: o.kind,
|
|
14309
|
+
withheldBytes: o.withheldBytes,
|
|
14310
|
+
R,
|
|
14311
|
+
nextCallInputTotal: nextCall?.inputTotal ?? 0,
|
|
14312
|
+
nextCallCacheRead: nextCall?.cacheRead ?? 0,
|
|
14313
|
+
outcome,
|
|
14314
|
+
retriedWithin10,
|
|
14315
|
+
shellReadAmbiguous,
|
|
14316
|
+
editWithin10,
|
|
14317
|
+
editErrorWithin10
|
|
14318
|
+
});
|
|
14319
|
+
}
|
|
13993
14320
|
if (sawTokenGoatHook) {
|
|
13994
14321
|
s.readInterception.repeatInHookedSessions += fileRepeats;
|
|
13995
14322
|
s.readInterception.repeatFullNoRangeInHookedSessions += fileRepeatsFullNoRange;
|
|
@@ -14039,8 +14366,47 @@ function hookRollup(hookMap, origin, event) {
|
|
|
14039
14366
|
}
|
|
14040
14367
|
return roll;
|
|
14041
14368
|
}
|
|
14369
|
+
function median(arr) {
|
|
14370
|
+
if (arr.length === 0) return 0;
|
|
14371
|
+
const sorted = [...arr].sort((a, b) => a - b);
|
|
14372
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
14373
|
+
}
|
|
14374
|
+
function aggregateDenyOutcomes(rows) {
|
|
14375
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
14376
|
+
for (const r of rows) {
|
|
14377
|
+
const bucket = byKind.get(r.kind);
|
|
14378
|
+
if (bucket === void 0) byKind.set(r.kind, [r]);
|
|
14379
|
+
else bucket.push(r);
|
|
14380
|
+
}
|
|
14381
|
+
const result = [];
|
|
14382
|
+
for (const [kind, kindRows] of byKind) {
|
|
14383
|
+
const count = kindRows.length;
|
|
14384
|
+
const rateOf = (outcome) => kindRows.filter((r) => r.outcome === outcome).length / count;
|
|
14385
|
+
const knownBytes = kindRows.map((r) => r.withheldBytes).filter((b) => b !== null);
|
|
14386
|
+
result.push({
|
|
14387
|
+
kind,
|
|
14388
|
+
count,
|
|
14389
|
+
compactedRate: rateOf("compacted"),
|
|
14390
|
+
retriedRate: rateOf("retried"),
|
|
14391
|
+
substitutedRate: rateOf("substituted"),
|
|
14392
|
+
shellReadRate: rateOf("shell_read"),
|
|
14393
|
+
unresolvedRate: rateOf("unresolved"),
|
|
14394
|
+
abandonedRate: rateOf("abandoned"),
|
|
14395
|
+
retriedWithin10Rate: kindRows.filter((r) => r.retriedWithin10).length / count,
|
|
14396
|
+
medianWithheldBytes: knownBytes.length === 0 ? null : median(knownBytes),
|
|
14397
|
+
withheldBytesUnknownFraction: (count - knownBytes.length) / count,
|
|
14398
|
+
medianR: median(kindRows.map((r) => r.R)),
|
|
14399
|
+
medianNextCallInputTotal: median(kindRows.map((r) => r.nextCallInputTotal)),
|
|
14400
|
+
medianNextCallCacheRead: median(kindRows.map((r) => r.nextCallCacheRead)),
|
|
14401
|
+
shellReadAmbiguousCount: kindRows.filter((r) => r.shellReadAmbiguous).length,
|
|
14402
|
+
editWithin10Count: kindRows.filter((r) => r.editWithin10).length,
|
|
14403
|
+
editErrorWithin10Count: kindRows.filter((r) => r.editErrorWithin10).length
|
|
14404
|
+
});
|
|
14405
|
+
}
|
|
14406
|
+
return result.sort((a, b) => b.count - a.count || a.kind.localeCompare(b.kind));
|
|
14407
|
+
}
|
|
14042
14408
|
async function auditSessionCorpus(opts = {}) {
|
|
14043
|
-
const corpusDir =
|
|
14409
|
+
const corpusDir = path27.resolve(opts.dir ?? defaultCorpusDir());
|
|
14044
14410
|
if (!fs26.existsSync(corpusDir)) {
|
|
14045
14411
|
throw new Error(`session corpus directory not found: ${corpusDir}`);
|
|
14046
14412
|
}
|
|
@@ -14078,6 +14444,8 @@ async function auditSessionCorpus(opts = {}) {
|
|
|
14078
14444
|
laneAgentTypes: [],
|
|
14079
14445
|
readInterception: { readResults: 0, divertedByMarker: 0, divertedBytes: 0, fullServesOver10k: 0, fullServeBytesOver10k: 0, fullServesFirstRead: 0, fullServesRepeat: 0, repeatBytes: 0, repeatWithRange: 0, repeatFullNoRange: 0, repeatInHookedSessions: 0, repeatFullNoRangeInHookedSessions: 0, repeatFullNoRangeHookedAfterCompaction: 0, fullServesPathUnknown: 0 },
|
|
14080
14446
|
bashInterception: { bashResults: 0, markedByFilter: 0, markedBytes: 0, smallUntouched: 0, smallUntouchedBytes: 0, untouched: 0, untouchedBytes: 0, untouchedEstTokens: 0, untouchedRereadTokens: 0, untouchedBilledEquivTokens: 0, untouchedHeads: [] },
|
|
14447
|
+
denyOutcomes: [],
|
|
14448
|
+
editErrorBaseline: { totalEdits: 0, totalErrors: 0, rate: 0 },
|
|
14081
14449
|
lineTypes: {},
|
|
14082
14450
|
omissionMarkers: { fires: 0, linesOmitted: 0 }
|
|
14083
14451
|
};
|
|
@@ -14086,9 +14454,10 @@ async function auditSessionCorpus(opts = {}) {
|
|
|
14086
14454
|
const hookMap = /* @__PURE__ */ new Map();
|
|
14087
14455
|
const laneObservations = [];
|
|
14088
14456
|
const bashHeadMap = /* @__PURE__ */ new Map();
|
|
14457
|
+
const denyRows = [];
|
|
14089
14458
|
for (const file of files) {
|
|
14090
14459
|
try {
|
|
14091
|
-
await auditOneFile(file, summary, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap);
|
|
14460
|
+
await auditOneFile(file, summary, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows);
|
|
14092
14461
|
summary.filesScanned += 1;
|
|
14093
14462
|
} catch {
|
|
14094
14463
|
summary.filesFailed += 1;
|
|
@@ -14125,6 +14494,8 @@ async function auditSessionCorpus(opts = {}) {
|
|
|
14125
14494
|
}
|
|
14126
14495
|
summary.attachmentKinds = [...attachmentMap.values()].sort((a, b) => b.billedEquivTokens - a.billedEquivTokens || b.injections - a.injections || a.kind.localeCompare(b.kind));
|
|
14127
14496
|
summary.hookOutputs = [...hookMap.values()].sort((a, b) => b.contextBytes - a.contextBytes || b.fires - a.fires || a.origin.localeCompare(b.origin) || a.event.localeCompare(b.event));
|
|
14497
|
+
summary.denyOutcomes = aggregateDenyOutcomes(denyRows);
|
|
14498
|
+
summary.editErrorBaseline.rate = summary.editErrorBaseline.totalEdits === 0 ? 0 : summary.editErrorBaseline.totalErrors / summary.editErrorBaseline.totalEdits;
|
|
14128
14499
|
summary.runtimeMs = Date.now() - started;
|
|
14129
14500
|
return summary;
|
|
14130
14501
|
}
|
|
@@ -14210,6 +14581,15 @@ function formatSessionAudit(s) {
|
|
|
14210
14581
|
lines.push(`Full-serve split (same transcript file; a session's lanes are separate files, so repeats UNDER-count): first read ${fmt(ri.fullServesFirstRead)}, repeat ${fmt(ri.fullServesRepeat)} (${fmt(ri.repeatBytes)} bytes), path unknown ${fmt(ri.fullServesPathUnknown)}`);
|
|
14211
14582
|
lines.push(`Repeats: with offset/limit ${fmt(ri.repeatWithRange)} (deliberate paging), whole-file ${fmt(ri.repeatFullNoRange)} (divert-miss candidates), in sessions with a token-goat hook fire ${fmt(ri.repeatInHookedSessions)} (whole-file among them: ${fmt(ri.repeatFullNoRangeInHookedSessions)}, of which post-compaction and so correct by design: ${fmt(ri.repeatFullNoRangeHookedAfterCompaction)})`);
|
|
14212
14583
|
lines.push("");
|
|
14584
|
+
lines.push("## Deny outcomes (what actually happened after a token-goat Read deny; raw tokens, not billed units)");
|
|
14585
|
+
const eb = s.editErrorBaseline;
|
|
14586
|
+
lines.push(`Edit-error baseline (all Edit tool_results, corpus-wide, independent of any deny): ${fmt(eb.totalErrors)}/${fmt(eb.totalEdits)} (${(eb.rate * 100).toFixed(1)}%) -- compare each row's edit-error<=10 ratio against this rate.`);
|
|
14587
|
+
for (const d of s.denyOutcomes) {
|
|
14588
|
+
const editErrorRatio = d.editWithin10Count > 0 ? `${fmt(d.editErrorWithin10Count)}/${fmt(d.editWithin10Count)}` : "n/a";
|
|
14589
|
+
lines.push(`${d.kind.padEnd(34)} n ${fmt(d.count).padStart(6)} compacted ${pct(d.compactedRate * d.count, d.count)} retried ${pct(d.retriedRate * d.count, d.count)} substituted ${pct(d.substitutedRate * d.count, d.count)} shell-read ${pct(d.shellReadRate * d.count, d.count)} unresolved ${pct(d.unresolvedRate * d.count, d.count)} abandoned ${pct(d.abandonedRate * d.count, d.count)} retried<=10 ${pct(d.retriedWithin10Rate * d.count, d.count)} median-withheld ${d.medianWithheldBytes === null ? "n/a" : fmt(d.medianWithheldBytes) + "B"} (unknown ${pct(d.withheldBytesUnknownFraction * d.count, d.count)}) median-R ${fmt(d.medianR)} shell-read-ambiguous ${fmt(d.shellReadAmbiguousCount)} edit-error<=10 ${editErrorRatio}`);
|
|
14590
|
+
}
|
|
14591
|
+
if (s.denyOutcomes.length === 0) lines.push("(no denies matched a known template)");
|
|
14592
|
+
lines.push("");
|
|
14213
14593
|
lines.push("## Bash filter fire-rate (token-goat in-band markers inside Bash tool results)");
|
|
14214
14594
|
const bi = s.bashInterception;
|
|
14215
14595
|
lines.push(`Bash results: ${fmt(bi.bashResults)}; marked by a filter: ${fmt(bi.markedByFilter)} (${fmt(bi.markedBytes)} bytes); small unmarked <${fmt(BASH_SMALL_RESULT_MAX_BYTES)} B: ${fmt(bi.smallUntouched)} (${fmt(bi.smallUntouchedBytes)} bytes)`);
|
|
@@ -14228,7 +14608,7 @@ function formatSessionAudit(s) {
|
|
|
14228
14608
|
// src/cli_mcp_audit.ts
|
|
14229
14609
|
import * as fs27 from "node:fs";
|
|
14230
14610
|
import * as os13 from "node:os";
|
|
14231
|
-
import * as
|
|
14611
|
+
import * as path28 from "node:path";
|
|
14232
14612
|
function readMcpJsonFile(configPath2) {
|
|
14233
14613
|
try {
|
|
14234
14614
|
if (!fs27.existsSync(configPath2)) return null;
|
|
@@ -14267,8 +14647,8 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
|
|
|
14267
14647
|
}
|
|
14268
14648
|
}
|
|
14269
14649
|
function discoverMcpConfig(projectRoot, home) {
|
|
14270
|
-
const mcpJsonPath =
|
|
14271
|
-
const claudeJsonPath =
|
|
14650
|
+
const mcpJsonPath = path28.join(projectRoot, ".mcp.json");
|
|
14651
|
+
const claudeJsonPath = path28.join(home, ".claude.json");
|
|
14272
14652
|
const sourcesChecked = [mcpJsonPath, claudeJsonPath];
|
|
14273
14653
|
const fromMcpJson = readMcpJsonFile(mcpJsonPath);
|
|
14274
14654
|
if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
|
|
@@ -14387,15 +14767,14 @@ var RECALL_COMMAND = {
|
|
|
14387
14767
|
function fenceTagForCacheType(cacheType) {
|
|
14388
14768
|
return cacheType === "web" ? UNTRUSTED_WEB_TAG : UNTRUSTED_TOOL_TAG;
|
|
14389
14769
|
}
|
|
14770
|
+
function fenceRecallListing(text, hits) {
|
|
14771
|
+
const tags = new Set(hits.map((hit) => fenceTagForCacheType(hit.cacheType)));
|
|
14772
|
+
const tag = tags.size === 1 ? [...tags][0] ?? UNTRUSTED_TOOL_TAG : UNTRUSTED_TOOL_TAG;
|
|
14773
|
+
return fenceUntrusted(text, tag);
|
|
14774
|
+
}
|
|
14390
14775
|
function fenceSnippetIfMatched(hit) {
|
|
14391
|
-
|
|
14392
|
-
try {
|
|
14393
|
-
if (loadConfig().injection.enabled) matches2 = scanForInjectionPatterns(hit.snippet);
|
|
14394
|
-
} catch {
|
|
14395
|
-
matches2 = [];
|
|
14396
|
-
}
|
|
14776
|
+
const matches2 = scanAndRecord(hit.snippet);
|
|
14397
14777
|
if (matches2.length === 0) return hit.snippet;
|
|
14398
|
-
recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
|
|
14399
14778
|
return fenceUntrustedContent(hit.snippet, matches2, fenceTagForCacheType(hit.cacheType));
|
|
14400
14779
|
}
|
|
14401
14780
|
function printHits(query, hits) {
|
|
@@ -14407,16 +14786,15 @@ function printHits(query, hits) {
|
|
|
14407
14786
|
`);
|
|
14408
14787
|
return;
|
|
14409
14788
|
}
|
|
14410
|
-
|
|
14789
|
+
const body = hits.map((hit) => {
|
|
14411
14790
|
const label = hit.label.length > 80 ? hit.label.slice(0, 77) + "..." : hit.label;
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14791
|
+
return `[${pad(hit.cacheType, 4)}] ${hit.id} (token-goat ${RECALL_COMMAND[hit.cacheType]} ${hit.id})
|
|
14792
|
+
${label}
|
|
14793
|
+
${hit.snippet}
|
|
14794
|
+
`;
|
|
14795
|
+
}).join("\n");
|
|
14796
|
+
w(`${fenceRecallListing(body, hits)}
|
|
14418
14797
|
`);
|
|
14419
|
-
}
|
|
14420
14798
|
}
|
|
14421
14799
|
function runRecallCommand(query, opts = {}) {
|
|
14422
14800
|
const browse = query === void 0 || query.trim() === "";
|
|
@@ -14424,7 +14802,13 @@ function runRecallCommand(query, opts = {}) {
|
|
|
14424
14802
|
...opts.type !== void 0 ? { type: opts.type } : {},
|
|
14425
14803
|
...opts.limit !== void 0 ? { limit: opts.limit } : {}
|
|
14426
14804
|
};
|
|
14427
|
-
const
|
|
14805
|
+
const effectiveLimit = opts.limit ?? RECALL_DEFAULT_LIMIT;
|
|
14806
|
+
const fetched = browse ? listRecentRecall({ ...scope, limit: effectiveLimit + 1 }) : searchRecall(query, { ...scope, limit: effectiveLimit + 1 });
|
|
14807
|
+
const hits = fetched.slice(0, effectiveLimit);
|
|
14808
|
+
if (fetched.length > hits.length) {
|
|
14809
|
+
process.stderr.write(`Showing ${hits.length} entries; more are available (raise --limit to see them).
|
|
14810
|
+
`);
|
|
14811
|
+
}
|
|
14428
14812
|
if (opts.json === true) {
|
|
14429
14813
|
const fenced = hits.map((hit) => ({ ...hit, snippet: fenceSnippetIfMatched(hit) }));
|
|
14430
14814
|
process.stdout.write(`${JSON.stringify(fenced)}
|
|
@@ -14681,6 +15065,7 @@ async function cmdSemantic(query, opts) {
|
|
|
14681
15065
|
process.exitCode = code;
|
|
14682
15066
|
}
|
|
14683
15067
|
async function cmdIndex(pathArg, opts = {}) {
|
|
15068
|
+
applyIndexingPriority();
|
|
14684
15069
|
const root = pathArg ?? process.cwd();
|
|
14685
15070
|
const dbPath = opts.dbPath ?? globalDbPath();
|
|
14686
15071
|
const force = opts.force === true;
|
|
@@ -14823,7 +15208,7 @@ async function cmdMcpServe() {
|
|
|
14823
15208
|
let StdioServerTransport;
|
|
14824
15209
|
try {
|
|
14825
15210
|
;
|
|
14826
|
-
({ createMcpServer } = await import("./token-goat-chunk-
|
|
15211
|
+
({ createMcpServer } = await import("./token-goat-chunk-CD6U5OD6.mjs"));
|
|
14827
15212
|
({ StdioServerTransport } = await import("./token-goat-chunk-324QOJYZ.mjs"));
|
|
14828
15213
|
} catch (err2) {
|
|
14829
15214
|
process.stderr.write(
|
|
@@ -14836,8 +15221,8 @@ async function cmdMcpServe() {
|
|
|
14836
15221
|
const server = await createMcpServer();
|
|
14837
15222
|
const transport = new StdioServerTransport();
|
|
14838
15223
|
await server.connect(transport);
|
|
14839
|
-
await new Promise((
|
|
14840
|
-
server.onclose =
|
|
15224
|
+
await new Promise((resolve14) => {
|
|
15225
|
+
server.onclose = resolve14;
|
|
14841
15226
|
});
|
|
14842
15227
|
}
|
|
14843
15228
|
function printBridgeVerificationNotice(harness) {
|
|
@@ -14848,7 +15233,7 @@ async function cmdHook(event, opts) {
|
|
|
14848
15233
|
if (typeof opts.harness === "string" && opts.harness.length > 0) {
|
|
14849
15234
|
process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
|
|
14850
15235
|
}
|
|
14851
|
-
const { relay } = await import("./token-goat-chunk-
|
|
15236
|
+
const { relay } = await import("./token-goat-chunk-AUM22UQ2.mjs");
|
|
14852
15237
|
await relay(event);
|
|
14853
15238
|
}
|
|
14854
15239
|
async function cmdInstall(opts) {
|
|
@@ -14961,14 +15346,14 @@ async function cmdInstall(opts) {
|
|
|
14961
15346
|
);
|
|
14962
15347
|
}
|
|
14963
15348
|
try {
|
|
14964
|
-
const skillDir =
|
|
15349
|
+
const skillDir = path29.join(homedir13(), ".claude", "skills");
|
|
14965
15350
|
if (fs28.existsSync(skillDir)) {
|
|
14966
15351
|
const entries = fs28.readdirSync(skillDir, { withFileTypes: true });
|
|
14967
15352
|
const skillNames = [];
|
|
14968
15353
|
const sessionId = getSessionId();
|
|
14969
15354
|
for (const entry of entries) {
|
|
14970
15355
|
if (!entry.isDirectory()) continue;
|
|
14971
|
-
const skillFile =
|
|
15356
|
+
const skillFile = path29.join(skillDir, entry.name, "SKILL.md");
|
|
14972
15357
|
if (fs28.existsSync(skillFile)) {
|
|
14973
15358
|
const body = fs28.readFileSync(skillFile, "utf-8");
|
|
14974
15359
|
const compact = extractCompactFromMarker(body);
|
|
@@ -14981,7 +15366,7 @@ async function cmdInstall(opts) {
|
|
|
14981
15366
|
if (skillNames.length > 0) {
|
|
14982
15367
|
const dir = skillOutputsDir();
|
|
14983
15368
|
await fs28.promises.mkdir(dir, { recursive: true });
|
|
14984
|
-
const pregenPath =
|
|
15369
|
+
const pregenPath = path29.join(dir, "pregen.json");
|
|
14985
15370
|
const pregenData = { ts: Date.now(), names: skillNames };
|
|
14986
15371
|
await fs28.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
|
|
14987
15372
|
out(`Pre-generated ${skillNames.length} skill compacts.`);
|
|
@@ -15182,7 +15567,7 @@ async function cmdSessionOutline(sessionIdOrPath, opts = {}) {
|
|
|
15182
15567
|
${formatSessionOutline(turns)}`;
|
|
15183
15568
|
out(text);
|
|
15184
15569
|
const fullSourceBytes = sessionTranscriptSize(transcriptPath);
|
|
15185
|
-
const bytesSaved =
|
|
15570
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15186
15571
|
recordStat("session_outline", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15187
15572
|
}
|
|
15188
15573
|
function sessionTranscriptSize(transcriptPath) {
|
|
@@ -15204,7 +15589,7 @@ async function cmdSessionSlice(sessionIdOrPath, opts) {
|
|
|
15204
15589
|
const text = opts.json === true ? JSON.stringify({ transcriptPath, turns }) : formatSessionSlice(turns);
|
|
15205
15590
|
out(text);
|
|
15206
15591
|
const fullSourceBytes = sessionTranscriptSize(transcriptPath);
|
|
15207
|
-
const bytesSaved =
|
|
15592
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15208
15593
|
recordStat("session_slice", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15209
15594
|
}
|
|
15210
15595
|
function cmdMcpAudit(opts = {}) {
|
|
@@ -15244,24 +15629,13 @@ function cmdHintStats(opts = {}) {
|
|
|
15244
15629
|
...opts.markIneffective !== void 0 && isHintCategory(opts.markIneffective) ? { markIneffective: opts.markIneffective } : {}
|
|
15245
15630
|
});
|
|
15246
15631
|
}
|
|
15247
|
-
function _applyFiltersAndPrint(content, opts,
|
|
15248
|
-
const
|
|
15249
|
-
if (!
|
|
15250
|
-
out(text);
|
|
15251
|
-
return text;
|
|
15252
|
-
}
|
|
15253
|
-
let matches2 = [];
|
|
15254
|
-
try {
|
|
15255
|
-
if (loadConfig().injection.enabled) matches2 = scanForInjectionPatterns(text);
|
|
15256
|
-
} catch {
|
|
15257
|
-
matches2 = [];
|
|
15258
|
-
}
|
|
15259
|
-
if (matches2.length === 0) {
|
|
15632
|
+
function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTag = UNTRUSTED_WEB_TAG) {
|
|
15633
|
+
const emit3 = (text) => {
|
|
15634
|
+
if (!fenceByProvenance || text === "") {
|
|
15260
15635
|
out(text);
|
|
15261
15636
|
return text;
|
|
15262
15637
|
}
|
|
15263
|
-
|
|
15264
|
-
const fenced = fenceUntrustedContent(text, matches2, fenceTag);
|
|
15638
|
+
const fenced = fenceUntrusted(text, fenceTag);
|
|
15265
15639
|
out(fenced);
|
|
15266
15640
|
return fenced;
|
|
15267
15641
|
};
|
|
@@ -15293,12 +15667,17 @@ function _applyFiltersAndPrint(content, opts, fenceUntrusted = false, fenceTag =
|
|
|
15293
15667
|
}
|
|
15294
15668
|
const rawLines = content.split(/\r?\n/);
|
|
15295
15669
|
if (opts.full === true) {
|
|
15296
|
-
return
|
|
15670
|
+
return emit3(rawLines.join("\n"));
|
|
15297
15671
|
}
|
|
15298
15672
|
const lines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
|
|
15299
15673
|
const headN = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 30;
|
|
15300
15674
|
const tailN = opts.tail !== void 0 ? requireNonNegativeInt("--tail", opts.tail) : 80;
|
|
15301
15675
|
const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 + 1 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
|
|
15676
|
+
const noteLineCap = (which, flag, shown, total) => {
|
|
15677
|
+
if (shown >= total) return;
|
|
15678
|
+
process.stderr.write(`Showing ${which} ${shown} of ${total} lines (raise --${flag}, or --full for the whole body).
|
|
15679
|
+
`);
|
|
15680
|
+
};
|
|
15302
15681
|
let result = lines;
|
|
15303
15682
|
if (opts.head === void 0 && opts.tail === void 0) {
|
|
15304
15683
|
result = applyElision(lines, headN, tailN);
|
|
@@ -15306,20 +15685,19 @@ function _applyFiltersAndPrint(content, opts, fenceUntrusted = false, fenceTag =
|
|
|
15306
15685
|
result = applyElision(lines, headN, tailN);
|
|
15307
15686
|
} else if (opts.head !== void 0) {
|
|
15308
15687
|
result = lines.slice(0, headN);
|
|
15688
|
+
noteLineCap("first", "head", result.length, lines.length);
|
|
15309
15689
|
} else if (opts.tail !== void 0) {
|
|
15310
15690
|
result = lines.slice(Math.max(0, lines.length - tailN));
|
|
15691
|
+
noteLineCap("last", "tail", result.length, lines.length);
|
|
15311
15692
|
}
|
|
15312
|
-
return
|
|
15693
|
+
return emit3(result.join("\n"));
|
|
15313
15694
|
}
|
|
15314
|
-
function
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
matches2 = [];
|
|
15320
|
-
}
|
|
15695
|
+
function fenceFileText(text) {
|
|
15696
|
+
return fenceUntrusted(text, UNTRUSTED_FILE_TAG);
|
|
15697
|
+
}
|
|
15698
|
+
function fenceFileFieldIfMatched(text) {
|
|
15699
|
+
const matches2 = scanAndRecord(text);
|
|
15321
15700
|
if (matches2.length === 0) return text;
|
|
15322
|
-
recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
|
|
15323
15701
|
return fenceUntrustedContent(text, matches2, UNTRUSTED_FILE_TAG);
|
|
15324
15702
|
}
|
|
15325
15703
|
function fileSizeOrZero(filePath) {
|
|
@@ -15387,7 +15765,7 @@ async function cmdPdfExtract(file, opts) {
|
|
|
15387
15765
|
const text = await runPdfExtractText(file, opts.pages, opts.layout === true);
|
|
15388
15766
|
const printed = _applyFiltersAndPrint(text, opts, true, UNTRUSTED_FILE_TAG);
|
|
15389
15767
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15390
|
-
const bytesSaved =
|
|
15768
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(printed, "utf8"));
|
|
15391
15769
|
recordStat("pdf_extract", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15392
15770
|
}
|
|
15393
15771
|
async function cmdPdfLocate(file, pattern, opts) {
|
|
@@ -15398,24 +15776,26 @@ async function cmdPdfLocate(file, pattern, opts) {
|
|
|
15398
15776
|
if (opts.context !== void 0) locateOpts.context = requirePositiveInt("--context", opts.context);
|
|
15399
15777
|
if (opts.pages !== void 0) locateOpts.pages = opts.pages;
|
|
15400
15778
|
const matches2 = await runPdfLocate(file, pattern, locateOpts);
|
|
15401
|
-
const
|
|
15402
|
-
const pages = fencedMatches.map((m) => m.page);
|
|
15779
|
+
const pages = matches2.map((m) => m.page);
|
|
15403
15780
|
let printed;
|
|
15404
15781
|
if (opts.json === true) {
|
|
15782
|
+
const fencedMatches = matches2.map((m) => ({ ...m, snippet: fenceFileFieldIfMatched(m.snippet) }));
|
|
15405
15783
|
printed = JSON.stringify({ file, pattern, matchCount: fencedMatches.length, pages, matches: fencedMatches }, null, 2);
|
|
15406
15784
|
out(printed);
|
|
15407
|
-
} else if (
|
|
15785
|
+
} else if (matches2.length === 0) {
|
|
15408
15786
|
printed = "(no matches)";
|
|
15409
15787
|
out(printed);
|
|
15410
15788
|
} else {
|
|
15411
|
-
const lines =
|
|
15412
|
-
printed =
|
|
15789
|
+
const lines = matches2.map((m) => `p${m.page}: ${m.snippet}`);
|
|
15790
|
+
printed = fenceFileText(
|
|
15791
|
+
`${lines.join("\n")}
|
|
15413
15792
|
|
|
15414
|
-
${countNoun(
|
|
15793
|
+
${countNoun(matches2.length, "match", "matches")} across ${countNoun(pages.length, "page")}`
|
|
15794
|
+
);
|
|
15415
15795
|
out(printed);
|
|
15416
15796
|
}
|
|
15417
15797
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15418
|
-
const bytesSaved =
|
|
15798
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(printed, "utf8"));
|
|
15419
15799
|
recordStat("pdf_locate", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15420
15800
|
}
|
|
15421
15801
|
async function cmdPdfOutline(file, opts) {
|
|
@@ -15428,11 +15808,10 @@ async function cmdPdfOutline(file, opts) {
|
|
|
15428
15808
|
}
|
|
15429
15809
|
return;
|
|
15430
15810
|
}
|
|
15431
|
-
const
|
|
15432
|
-
const text = opts.json === true ? JSON.stringify(fencedEntries, null, 2) : fencedEntries.map((e) => `${" ".repeat(e.level)}${e.title}${e.page !== null ? ` (p.${e.page})` : ""}`).join("\n");
|
|
15811
|
+
const text = opts.json === true ? JSON.stringify(entries.map((e) => ({ ...e, title: fenceFileFieldIfMatched(e.title) })), null, 2) : fenceFileText(entries.map((e) => `${" ".repeat(e.level)}${e.title}${e.page !== null ? ` (p.${e.page})` : ""}`).join("\n"));
|
|
15433
15812
|
out(text);
|
|
15434
15813
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15435
|
-
const bytesSaved =
|
|
15814
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15436
15815
|
recordStat("pdf_outline", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15437
15816
|
}
|
|
15438
15817
|
async function cmdPdfMeta(file, opts = {}) {
|
|
@@ -15446,7 +15825,7 @@ async function cmdPdfMeta(file, opts = {}) {
|
|
|
15446
15825
|
const text = opts.json === true ? JSON.stringify({ pageCount: meta.pageCount, title: meta.title, author: meta.author, hasTextLayer: meta.hasTextLayer }, null, 2) : lines.join("\n");
|
|
15447
15826
|
out(text);
|
|
15448
15827
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15449
|
-
const bytesSaved =
|
|
15828
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15450
15829
|
recordStat("pdf_meta", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15451
15830
|
}
|
|
15452
15831
|
async function cmdImageMeta(file, opts = {}) {
|
|
@@ -15468,17 +15847,12 @@ ${msg}`;
|
|
|
15468
15847
|
const text = opts.json === true ? JSON.stringify(meta, null, 2) : lines.join("\n");
|
|
15469
15848
|
out(text);
|
|
15470
15849
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15471
|
-
const bytesSaved =
|
|
15472
|
-
recordStat("image_meta", bytesSaved,
|
|
15850
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15851
|
+
recordStat("image_meta", bytesSaved, visionTokensSavedByText(meta.width, meta.height, Buffer.byteLength(text, "utf8"), loadConfig().image_shrink.vision_tier));
|
|
15473
15852
|
}
|
|
15474
15853
|
function fenceOcrText(text) {
|
|
15475
|
-
|
|
15476
|
-
|
|
15477
|
-
const matches2 = scanForInjectionPatterns(text);
|
|
15478
|
-
if (matches2.length > 0) recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
|
|
15479
|
-
}
|
|
15480
|
-
} catch {
|
|
15481
|
-
}
|
|
15854
|
+
if (!injectionFencingEnabled()) return text;
|
|
15855
|
+
scanAndRecord(text);
|
|
15482
15856
|
return fenceUntrustedOcrText(text);
|
|
15483
15857
|
}
|
|
15484
15858
|
async function cmdImageText(file, opts = {}) {
|
|
@@ -15505,8 +15879,8 @@ ${fenceOcrText(result.text)}` : `${lines.join("\n")}
|
|
|
15505
15879
|
}
|
|
15506
15880
|
out(text);
|
|
15507
15881
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15508
|
-
const bytesSaved =
|
|
15509
|
-
recordStat("image_text", bytesSaved,
|
|
15882
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15883
|
+
recordStat("image_text", bytesSaved, visionTokensSavedByText(null, null, Buffer.byteLength(text, "utf8"), loadConfig().image_shrink.vision_tier));
|
|
15510
15884
|
}
|
|
15511
15885
|
function cmdVideoChapters(file) {
|
|
15512
15886
|
const { chapters, subtitleStreams } = extractVideoChapters(file);
|
|
@@ -15531,7 +15905,7 @@ function cmdVideoChapters(file) {
|
|
|
15531
15905
|
const text = lines.join("\n");
|
|
15532
15906
|
out(text);
|
|
15533
15907
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15534
|
-
const bytesSaved =
|
|
15908
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15535
15909
|
recordStat("video_chapters", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15536
15910
|
}
|
|
15537
15911
|
function formatVideoTimestamp(totalSeconds) {
|
|
@@ -15558,25 +15932,24 @@ function cmdSharepointResolve(url) {
|
|
|
15558
15932
|
}
|
|
15559
15933
|
function recordXlsxStat(kind, file, emitted) {
|
|
15560
15934
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15561
|
-
const bytesSaved =
|
|
15935
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(emitted, "utf8"));
|
|
15562
15936
|
recordStat(kind, bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15563
15937
|
}
|
|
15564
15938
|
async function cmdXlsxSheets(file, opts = {}) {
|
|
15565
15939
|
const sheets = await listSheets(file);
|
|
15566
|
-
const
|
|
15567
|
-
const text = opts.json === true ? JSON.stringify(fencedSheets.map((s) => ({ name: s.name, ref: s.ref, rows: s.rows, cols: s.cols })), null, 2) : fencedSheets.map((s) => `${s.name} ${s.ref} (${s.rows} rows x ${s.cols} cols)`).join("\n");
|
|
15940
|
+
const text = opts.json === true ? JSON.stringify(sheets.map((s) => ({ name: fenceFileFieldIfMatched(s.name), ref: s.ref, rows: s.rows, cols: s.cols })), null, 2) : fenceFileText(sheets.map((s) => `${s.name} ${s.ref} (${s.rows} rows x ${s.cols} cols)`).join("\n"));
|
|
15568
15941
|
out(text);
|
|
15569
15942
|
recordXlsxStat("xlsx_sheets", file, text);
|
|
15570
15943
|
}
|
|
15571
15944
|
async function cmdXlsxHead(file, opts) {
|
|
15572
15945
|
const rows = opts.rows !== void 0 ? requireNonNegativeInt("--rows", opts.rows) : 20;
|
|
15573
|
-
const text =
|
|
15946
|
+
const text = fenceFileText(await headSheet(file, opts.sheet, rows));
|
|
15574
15947
|
out(text);
|
|
15575
15948
|
recordXlsxStat("xlsx_head", file, text);
|
|
15576
15949
|
}
|
|
15577
15950
|
async function cmdXlsxRange(file, opts) {
|
|
15578
15951
|
const result = await rangeSheet(file, opts.sheet, opts.range, opts.formulas === true);
|
|
15579
|
-
const text =
|
|
15952
|
+
const text = fenceFileText(formatXlsxRange(result));
|
|
15580
15953
|
out(text);
|
|
15581
15954
|
recordXlsxStat("xlsx_range", file, text);
|
|
15582
15955
|
}
|
|
@@ -15588,32 +15961,33 @@ async function cmdXlsxQuery(file, opts) {
|
|
|
15588
15961
|
...wheres !== void 0 ? { wheres } : {},
|
|
15589
15962
|
...opts.head !== void 0 ? { head: requireNonNegativeInt("--head", opts.head) } : {}
|
|
15590
15963
|
});
|
|
15591
|
-
const text =
|
|
15964
|
+
const text = fenceFileText(formatCsvTable(result, (opts.where ?? []).map((w) => `--where ${w}`)));
|
|
15592
15965
|
out(text);
|
|
15593
15966
|
recordXlsxStat("xlsx_query", file, text);
|
|
15594
15967
|
}
|
|
15595
15968
|
function recordDocStat(kind, file, emitted) {
|
|
15596
15969
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15597
|
-
const bytesSaved =
|
|
15970
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(emitted, "utf8"));
|
|
15598
15971
|
recordStat(kind, bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15599
15972
|
}
|
|
15600
15973
|
async function cmdPptxOutline(file, opts) {
|
|
15601
15974
|
const slides = await pptxOutline(file);
|
|
15602
|
-
const
|
|
15603
|
-
|
|
15975
|
+
const text = opts.json === true ? JSON.stringify(slides.map((s) => ({ ...s, title: fenceFileFieldIfMatched(s.title) })), null, 2) : fenceFileText(
|
|
15976
|
+
slides.map((s) => `${s.slide}. ${s.title || "(untitled)"} [${s.bodyChars} body chars${s.hasNotes ? ", has notes" : ""}]`).join("\n")
|
|
15977
|
+
);
|
|
15604
15978
|
out(text);
|
|
15605
15979
|
recordDocStat("pptx_outline", file, text);
|
|
15606
15980
|
}
|
|
15607
15981
|
async function cmdPptxSlide(file, opts) {
|
|
15608
15982
|
const n = requireNonNegativeInt("--slide", opts.slide);
|
|
15609
|
-
const text =
|
|
15983
|
+
const text = fenceFileText(await pptxSlideText(file, n, opts.notes === true));
|
|
15610
15984
|
out(text);
|
|
15611
15985
|
recordDocStat("pptx_slide", file, text);
|
|
15612
15986
|
}
|
|
15613
15987
|
async function cmdPptxNotes(file, opts) {
|
|
15614
15988
|
const n = opts.slide !== void 0 ? requireNonNegativeInt("--slide", opts.slide) : void 0;
|
|
15615
15989
|
const text = await pptxNotesText(file, n);
|
|
15616
|
-
const printed = text.length > 0 ?
|
|
15990
|
+
const printed = text.length > 0 ? fenceFileText(text) : "no speaker notes found";
|
|
15617
15991
|
out(printed);
|
|
15618
15992
|
recordDocStat("pptx_notes", file, printed);
|
|
15619
15993
|
}
|
|
@@ -15623,7 +15997,7 @@ async function cmdPptxText(file, opts) {
|
|
|
15623
15997
|
out("no matches");
|
|
15624
15998
|
return;
|
|
15625
15999
|
}
|
|
15626
|
-
const text =
|
|
16000
|
+
const text = fenceFileText(matches2.map((m) => `Slide ${m.slide}: ...${m.snippet}...`).join("\n"));
|
|
15627
16001
|
out(text);
|
|
15628
16002
|
recordDocStat("pptx_text", file, text);
|
|
15629
16003
|
}
|
|
@@ -15637,8 +16011,7 @@ async function cmdDocxOutline(file, opts) {
|
|
|
15637
16011
|
}
|
|
15638
16012
|
return;
|
|
15639
16013
|
}
|
|
15640
|
-
const
|
|
15641
|
-
const text = opts.json === true ? JSON.stringify(fencedHeadings, null, 2) : fencedHeadings.map((h) => `${" ".repeat(h.level - 1)}${h.text}`).join("\n");
|
|
16014
|
+
const text = opts.json === true ? JSON.stringify(headings.map((h) => ({ ...h, text: fenceFileFieldIfMatched(h.text) })), null, 2) : fenceFileText(headings.map((h) => `${" ".repeat(h.level - 1)}${h.text}`).join("\n"));
|
|
15642
16015
|
out(text);
|
|
15643
16016
|
recordDocStat("docx_outline", file, text);
|
|
15644
16017
|
}
|
|
@@ -15752,6 +16125,18 @@ function cmdConflicts(targetPath, opts) {
|
|
|
15752
16125
|
async function cmdScreenshot(url, destPath, opts) {
|
|
15753
16126
|
out(await runScreenshot(url, destPath, opts));
|
|
15754
16127
|
}
|
|
16128
|
+
function readStdinPaths() {
|
|
16129
|
+
if (process.stdin.isTTY) {
|
|
16130
|
+
throw new CliError("--stdin requires piped input, e.g. `git diff --name-only | token-goat affected --stdin`");
|
|
16131
|
+
}
|
|
16132
|
+
let raw;
|
|
16133
|
+
try {
|
|
16134
|
+
raw = fs28.readFileSync(0, "utf8");
|
|
16135
|
+
} catch (e) {
|
|
16136
|
+
throw new CliError(`could not read the file list from stdin: ${extractErrorMessage(e)}`);
|
|
16137
|
+
}
|
|
16138
|
+
return raw.split("\n").map((line) => line.trim()).filter((line) => line !== "");
|
|
16139
|
+
}
|
|
15755
16140
|
function runExit(fn) {
|
|
15756
16141
|
try {
|
|
15757
16142
|
process.exitCode = fn();
|
|
@@ -15782,7 +16167,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
|
|
|
15782
16167
|
}
|
|
15783
16168
|
async function cmdCompress(opts) {
|
|
15784
16169
|
try {
|
|
15785
|
-
const bashRunner = await import("./token-goat-chunk-
|
|
16170
|
+
const bashRunner = await import("./token-goat-chunk-4KD2N46D.mjs");
|
|
15786
16171
|
if (opts.compress === false) {
|
|
15787
16172
|
process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
|
|
15788
16173
|
return;
|
|
@@ -15880,8 +16265,8 @@ async function cmdSkillCompact(name, opts) {
|
|
|
15880
16265
|
}
|
|
15881
16266
|
throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
|
|
15882
16267
|
}
|
|
15883
|
-
cacheName = name ??
|
|
15884
|
-
sourcePath =
|
|
16268
|
+
cacheName = name ?? path29.basename(path29.dirname(path29.resolve(opts.path)));
|
|
16269
|
+
sourcePath = path29.resolve(opts.path);
|
|
15885
16270
|
} else {
|
|
15886
16271
|
if (name === void 0 || !name.trim()) {
|
|
15887
16272
|
throw new CliError("skill-compact requires a <name> or --path <file>");
|
|
@@ -16022,8 +16407,8 @@ async function cmdSkillDiff(name) {
|
|
|
16022
16407
|
}
|
|
16023
16408
|
const newer = versions[0];
|
|
16024
16409
|
const older = versions[1];
|
|
16025
|
-
const newerBody = await fs28.promises.readFile(
|
|
16026
|
-
const olderBody = await fs28.promises.readFile(
|
|
16410
|
+
const newerBody = await fs28.promises.readFile(path29.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
|
|
16411
|
+
const olderBody = await fs28.promises.readFile(path29.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
|
|
16027
16412
|
if (newerBody === null || olderBody === null) {
|
|
16028
16413
|
out(`a cached version of '${name}' was evicted while diffing -- try again`);
|
|
16029
16414
|
return;
|
|
@@ -16075,7 +16460,7 @@ function atomicWriteBuffer(dest, data) {
|
|
|
16075
16460
|
if (e.code !== "ENOENT") throw e;
|
|
16076
16461
|
}
|
|
16077
16462
|
const rnd = randomBytes(4).toString("hex");
|
|
16078
|
-
const tmp =
|
|
16463
|
+
const tmp = path29.join(path29.dirname(path29.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
|
|
16079
16464
|
try {
|
|
16080
16465
|
fs28.writeFileSync(tmp, data, { mode: 384 });
|
|
16081
16466
|
try {
|
|
@@ -16113,9 +16498,9 @@ function mapFsError(e, src, dest, srcLabel = "source") {
|
|
|
16113
16498
|
const fe = e;
|
|
16114
16499
|
if (fe.code === "ENOENT") {
|
|
16115
16500
|
const errPath = fe.path ?? "";
|
|
16116
|
-
const isSource = src !== void 0 &&
|
|
16501
|
+
const isSource = src !== void 0 && path29.resolve(errPath) === path29.resolve(src);
|
|
16117
16502
|
if (isSource) throw new CliError(`${/\bfile$/i.test(srcLabel) ? srcLabel : `${srcLabel} file`} not found: ${src}`);
|
|
16118
|
-
const destDir = dest ?
|
|
16503
|
+
const destDir = dest ? path29.dirname(path29.resolve(dest)) : path29.dirname(path29.resolve(errPath || "."));
|
|
16119
16504
|
throw new CliError(`destination directory does not exist: ${destDir}`);
|
|
16120
16505
|
}
|
|
16121
16506
|
if (fe.code === "ENOTDIR") {
|
|
@@ -16126,7 +16511,7 @@ function mapFsError(e, src, dest, srcLabel = "source") {
|
|
|
16126
16511
|
}
|
|
16127
16512
|
if (fe.code === "EISDIR") {
|
|
16128
16513
|
const errPath = fe.path ?? "";
|
|
16129
|
-
const isSource = src !== void 0 && (errPath === "" ||
|
|
16514
|
+
const isSource = src !== void 0 && (errPath === "" || path29.resolve(errPath) === path29.resolve(src));
|
|
16130
16515
|
if (isSource) throw new CliError(`source is a directory, not a file: ${src}`);
|
|
16131
16516
|
throw new CliError(`destination is a directory, not a file: ${dest ?? (errPath || "(unknown)")}`);
|
|
16132
16517
|
}
|
|
@@ -16195,7 +16580,7 @@ function validateWritablePath(dest, label) {
|
|
|
16195
16580
|
throw new CliError(`${label} path contains a null byte`);
|
|
16196
16581
|
}
|
|
16197
16582
|
if (isWindows()) {
|
|
16198
|
-
const base =
|
|
16583
|
+
const base = path29.basename(dest);
|
|
16199
16584
|
const stem = base.replace(/\.[^.]*$/, "").toUpperCase();
|
|
16200
16585
|
if (WIN_RESERVED.has(stem)) {
|
|
16201
16586
|
throw new CliError(`${label} '${base}' is a reserved Windows device name`);
|
|
@@ -16336,7 +16721,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
16336
16721
|
throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
|
|
16337
16722
|
}
|
|
16338
16723
|
const maxBytes = maxMB * 1024 * 1024;
|
|
16339
|
-
return new Promise((
|
|
16724
|
+
return new Promise((resolve14, reject) => {
|
|
16340
16725
|
const chunks = [];
|
|
16341
16726
|
let totalBytes = 0;
|
|
16342
16727
|
let settled = false;
|
|
@@ -16360,7 +16745,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
16360
16745
|
try {
|
|
16361
16746
|
atomicWriteBuffer(dest, Buffer.concat(chunks));
|
|
16362
16747
|
enqueueDirtyPathSafe(dest);
|
|
16363
|
-
|
|
16748
|
+
resolve14();
|
|
16364
16749
|
} catch (e) {
|
|
16365
16750
|
try {
|
|
16366
16751
|
mapFsError(e, void 0, dest);
|
|
@@ -16710,19 +17095,9 @@ ${content}`;
|
|
|
16710
17095
|
const sections = await getDocSections(fileId, { fresh: false });
|
|
16711
17096
|
emitted = formatSections(sections);
|
|
16712
17097
|
}
|
|
16713
|
-
|
|
16714
|
-
try {
|
|
16715
|
-
if (loadConfig().injection.enabled) {
|
|
16716
|
-
const matches2 = scanForInjectionPatterns(emitted);
|
|
16717
|
-
if (matches2.length > 0) {
|
|
16718
|
-
recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
|
|
16719
|
-
toEmit = fenceUntrustedContent(emitted, matches2, UNTRUSTED_WEB_TAG);
|
|
16720
|
-
}
|
|
16721
|
-
}
|
|
16722
|
-
} catch {
|
|
16723
|
-
}
|
|
17098
|
+
const toEmit = fenceUntrusted(emitted, UNTRUSTED_WEB_TAG);
|
|
16724
17099
|
out(toEmit);
|
|
16725
|
-
const bytesSaved =
|
|
17100
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(toEmit, "utf8"));
|
|
16726
17101
|
recordStat("gdrive_sections", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
16727
17102
|
}
|
|
16728
17103
|
function expandGlobs(root, patterns, globFnOverride) {
|
|
@@ -16732,17 +17107,17 @@ function expandGlobs(root, patterns, globFnOverride) {
|
|
|
16732
17107
|
if (globFn !== void 0 && (p.includes("*") || p.includes("?") || p.includes("{"))) {
|
|
16733
17108
|
try {
|
|
16734
17109
|
const hits = globFn(p, { cwd: root });
|
|
16735
|
-
for (const h of hits) out2.push(
|
|
17110
|
+
for (const h of hits) out2.push(path29.isAbsolute(h) ? h : path29.join(root, h));
|
|
16736
17111
|
continue;
|
|
16737
17112
|
} catch {
|
|
16738
17113
|
}
|
|
16739
17114
|
}
|
|
16740
|
-
out2.push(
|
|
17115
|
+
out2.push(path29.isAbsolute(p) ? p : path29.join(root, p));
|
|
16741
17116
|
}
|
|
16742
17117
|
return out2;
|
|
16743
17118
|
}
|
|
16744
17119
|
function readIgnoreFile(root) {
|
|
16745
|
-
const ignorePath =
|
|
17120
|
+
const ignorePath = path29.join(root, ".tokengoatignore");
|
|
16746
17121
|
let raw;
|
|
16747
17122
|
try {
|
|
16748
17123
|
raw = fs28.readFileSync(ignorePath, "utf8");
|
|
@@ -16803,15 +17178,17 @@ function cmdTokens(patterns, opts) {
|
|
|
16803
17178
|
const result = estimateBudget(root, expandGlobs(root, patterns ?? []));
|
|
16804
17179
|
let entries = [...result.entries];
|
|
16805
17180
|
if (opts.asc === true) entries.reverse();
|
|
17181
|
+
const eligibleCount = entries.length;
|
|
16806
17182
|
if (opts.top !== void 0) entries = entries.slice(0, requireNonNegativeInt("--top", opts.top));
|
|
17183
|
+
const truncated = entries.length < eligibleCount;
|
|
16807
17184
|
if (opts.json === true) {
|
|
16808
|
-
out(JSON.stringify({ entries, total_tokens: result.total_tokens, total_lines: result.total_lines }, null, 2));
|
|
17185
|
+
out(JSON.stringify({ entries, truncated, totalCount: eligibleCount, total_tokens: result.total_tokens, total_lines: result.total_lines }, null, 2));
|
|
16809
17186
|
return;
|
|
16810
17187
|
}
|
|
16811
17188
|
if (opts.tree === true) {
|
|
16812
17189
|
const dirs = /* @__PURE__ */ new Map();
|
|
16813
17190
|
for (const e of entries) {
|
|
16814
|
-
const dir =
|
|
17191
|
+
const dir = path29.dirname(e.rel_path);
|
|
16815
17192
|
if (!dirs.has(dir)) dirs.set(dir, []);
|
|
16816
17193
|
dirs.get(dir).push(e);
|
|
16817
17194
|
}
|
|
@@ -16821,7 +17198,7 @@ function cmdTokens(patterns, opts) {
|
|
|
16821
17198
|
const pct2 = result.total_tokens > 0 ? Math.round(dirTokens / result.total_tokens * 100) : 0;
|
|
16822
17199
|
lines2.push(`${dir}/ (${dirTokens} tokens, ${pct2}%)`);
|
|
16823
17200
|
for (const e of dirEntries) {
|
|
16824
|
-
lines2.push(` ${
|
|
17201
|
+
lines2.push(` ${path29.basename(e.rel_path).padEnd(30)} ${String(e.tokens).padStart(8)} tokens`);
|
|
16825
17202
|
}
|
|
16826
17203
|
}
|
|
16827
17204
|
out(lines2.join("\n"));
|
|
@@ -16839,6 +17216,9 @@ function cmdTokens(patterns, opts) {
|
|
|
16839
17216
|
for (const e of entries) {
|
|
16840
17217
|
lines.push(`${e.rel_path.padEnd(colW)} ${String(e.tokens).padStart(8)} ${String(e.lines).padStart(6)}`);
|
|
16841
17218
|
}
|
|
17219
|
+
if (truncated) {
|
|
17220
|
+
lines.push(`...and ${eligibleCount - entries.length} more (raise --top to see them).`);
|
|
17221
|
+
}
|
|
16842
17222
|
out(lines.join("\n"));
|
|
16843
17223
|
}
|
|
16844
17224
|
function cmdBudget(patterns, opts) {
|
|
@@ -16869,6 +17249,63 @@ function cmdFailures(src, opts) {
|
|
|
16869
17249
|
saveFailureSnapshot(project.hash, key, { signatures, runner: result.runner, storedAt: Date.now() });
|
|
16870
17250
|
out(opts.json === true ? formatFailureDeltaJson(delta, result.runner) : formatFailureDeltaText(delta, result.runner));
|
|
16871
17251
|
}
|
|
17252
|
+
var originalHelpInformation = null;
|
|
17253
|
+
function generateCompactHelp() {
|
|
17254
|
+
const lines = [
|
|
17255
|
+
"Usage: token-goat [options] [command]",
|
|
17256
|
+
"",
|
|
17257
|
+
"Surgical token-reduction companion for AI coding agents",
|
|
17258
|
+
"",
|
|
17259
|
+
"Options:",
|
|
17260
|
+
" -v, --version print the token-goat version",
|
|
17261
|
+
" --cwd <path> run as if invoked from this directory",
|
|
17262
|
+
" -h, --help display help for command",
|
|
17263
|
+
"",
|
|
17264
|
+
"Reads: symbol, read, brief, section, semantic, skeleton, outline, refs, scope,",
|
|
17265
|
+
" similar, context-for, call-chain, impact, callers, dead, test-for",
|
|
17266
|
+
"",
|
|
17267
|
+
"Analysis: arch, affected, blame, deps, types, coverage-gaps, changed, diff, log,",
|
|
17268
|
+
" exports, imports, find, grep",
|
|
17269
|
+
"",
|
|
17270
|
+
"File Formats: pdf-meta, pdf-outline, pdf-extract, pdf-locate, xlsx-sheets,",
|
|
17271
|
+
" xlsx-head, xlsx-query, xlsx-range, yaml-outline, yaml-query, json-outline,",
|
|
17272
|
+
" json-query, xml-outline, xml-query, docx-outline, docx-text, pptx-outline,",
|
|
17273
|
+
" pptx-slide, pptx-text, pptx-notes",
|
|
17274
|
+
"",
|
|
17275
|
+
"Index & Search: index, map, reconcile, doctor, commands, ask, pack, tokens,",
|
|
17276
|
+
" budget, failures, todo, trace, logfold, lockdeps, dep-docs, recent, hot,",
|
|
17277
|
+
" snapshot-snapshot, baseline, cost, coverage-report-gaps, clean-cache,",
|
|
17278
|
+
" cache-audit, reclaim-index, project",
|
|
17279
|
+
"",
|
|
17280
|
+
"Session: stats, waste, session-outline, session-audit, session-slice,",
|
|
17281
|
+
" session-summary, context-stats, bootstrap-audit, memory, hint-stats,",
|
|
17282
|
+
" mcp-audit",
|
|
17283
|
+
"",
|
|
17284
|
+
"Skills: skill-body, skill-compact, skill-list, skill-size, skill-history,",
|
|
17285
|
+
" skill-diff, skill-section",
|
|
17286
|
+
"",
|
|
17287
|
+
"Compression: compress-text, retrieve, handoff-create, handoff-resolve,",
|
|
17288
|
+
" compress, recall, bash-output, web-output, mcp-output, compress-text,",
|
|
17289
|
+
" compress",
|
|
17290
|
+
"",
|
|
17291
|
+
"Config & Integration: install, uninstall, mcp-serve, mcp-status, hook,",
|
|
17292
|
+
" bridges-status, worker, statusline, version, config, config-get, help",
|
|
17293
|
+
"",
|
|
17294
|
+
"Data: note, note-add, note-get, note-list, sqlite-query, sqlite-schema,",
|
|
17295
|
+
" csv-profile, csv-query",
|
|
17296
|
+
"",
|
|
17297
|
+
"Utils: ignores, bash-history, web-history, openapi-op, openapi-outline,",
|
|
17298
|
+
" gdrive-sections, screenshot, opencode-*, fetch-image, image-meta,",
|
|
17299
|
+
" image-text, start, stop, resume, replace, insert-section, pr-slice,",
|
|
17300
|
+
" sharepoint-resolve, transcript, transcript-outline, video-chapters,",
|
|
17301
|
+
" write-file, zip-list, zip-read, mcp-history, compact-doc, compact-hint,",
|
|
17302
|
+
" conflicts, prune-cache",
|
|
17303
|
+
"",
|
|
17304
|
+
"Tip: look up one command instead of this whole list: 'token-goat help <command>', or 'token-goat commands --grep <pattern>' anchored ('^read$' ~700B; bare 'read' matches descriptions too, ~7KB).",
|
|
17305
|
+
""
|
|
17306
|
+
];
|
|
17307
|
+
return lines.join("\n");
|
|
17308
|
+
}
|
|
16872
17309
|
function buildProgram() {
|
|
16873
17310
|
const program2 = new Command();
|
|
16874
17311
|
program2.name("token-goat").description("Surgical token-reduction companion for AI coding agents").version(VERSION, "-v, --version", "print the token-goat version").option("--cwd <path>", "run as if invoked from this directory (overrides the real working directory)");
|
|
@@ -17047,6 +17484,15 @@ function buildProgram() {
|
|
|
17047
17484
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
17048
17485
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
17049
17486
|
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("--methodology", "explain local savings estimates and their billing limits").option("--window-days <days>", "days to include (0 = all time)", "30").option("--home-dir <path>", "home directory (for testing)").action(guard(cmdStats));
|
|
17487
|
+
program2.command("reconcile").description("sweep this project for files that changed while token-goat was not watching, and queue them for reindexing").option("--dry-run", "report the drift without queueing anything").option("--budget-ms <ms>", `wall-clock budget for the sweep (default ${DEFAULT_RECONCILE_BUDGET_MS})`).option("-j, --json", "output as JSON").action(
|
|
17488
|
+
(opts) => runExit(
|
|
17489
|
+
() => runReconcile({
|
|
17490
|
+
...opts.dryRun === true ? { dryRun: true } : {},
|
|
17491
|
+
...opts.budgetMs !== void 0 ? { budgetMs: requireNonNegativeInt("--budget-ms", opts.budgetMs) } : {},
|
|
17492
|
+
...opts.json === true ? { json: true } : {}
|
|
17493
|
+
})
|
|
17494
|
+
)
|
|
17495
|
+
);
|
|
17050
17496
|
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));
|
|
17051
17497
|
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));
|
|
17052
17498
|
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));
|
|
@@ -17197,10 +17643,22 @@ function buildProgram() {
|
|
|
17197
17643
|
})
|
|
17198
17644
|
)
|
|
17199
17645
|
);
|
|
17200
|
-
program2.command("arch").description("internal import graph analysis: hubs, entry points, cycles").option("--top <n>", "limit hubs
|
|
17646
|
+
program2.command("arch").description("internal import graph analysis: hubs, entry points, cycles, and with --modules the groups of files that mostly import each other").option("--top <n>", "limit hubs, entry points, modules and cross-module pairs to top N (default 10)").option("--modules", "also group files into modules by who imports whom, and report which modules reach into which (--json carries the full member list of each)").option("-j, --json", "output as JSON").action(
|
|
17201
17647
|
(opts) => runExit(
|
|
17202
17648
|
() => runArch({
|
|
17203
17649
|
...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
|
|
17650
|
+
...opts.json === true ? { json: true } : {},
|
|
17651
|
+
...opts.modules === true ? { modules: true } : {}
|
|
17652
|
+
})
|
|
17653
|
+
)
|
|
17654
|
+
);
|
|
17655
|
+
program2.command("affected [files...]").description("test files that transitively import the given changed files (for narrowing a CI run)").option("--stdin", "read the changed-file list from stdin, one path per line").option("--depth <n>", `max reverse-import hops (default ${DEFAULT_AFFECTED_DEPTH})`).option("--filter <regex>", "regex deciding which reached files count as tests (default: the built-in test-file heuristic)").option("-q, --quiet", "print bare paths only, for piping into a test runner").option("-j, --json", "output as JSON").action(
|
|
17656
|
+
(files, opts) => runExit(
|
|
17657
|
+
() => runAffected({
|
|
17658
|
+
files: opts.stdin === true ? [...files, ...readStdinPaths()] : files,
|
|
17659
|
+
...opts.depth !== void 0 ? { depth: requireNonNegativeInt("--depth", opts.depth) } : {},
|
|
17660
|
+
...opts.filter !== void 0 ? { filter: opts.filter } : {},
|
|
17661
|
+
...opts.quiet === true ? { quiet: true } : {},
|
|
17204
17662
|
...opts.json === true ? { json: true } : {}
|
|
17205
17663
|
})
|
|
17206
17664
|
)
|
|
@@ -17425,8 +17883,24 @@ function buildProgram() {
|
|
|
17425
17883
|
out(VERSION);
|
|
17426
17884
|
})
|
|
17427
17885
|
);
|
|
17428
|
-
|
|
17429
|
-
|
|
17886
|
+
program2.command("help [command]").description("show help for a command").option("--full", "show full original help instead of compact summary").action(
|
|
17887
|
+
guard((cmd, opts) => {
|
|
17888
|
+
const options = opts;
|
|
17889
|
+
if (options?.["full"] && !cmd) {
|
|
17890
|
+
out(originalHelpInformation ? originalHelpInformation() : generateCompactHelp());
|
|
17891
|
+
return;
|
|
17892
|
+
}
|
|
17893
|
+
if (cmd) {
|
|
17894
|
+
const argv = [process.argv[0] ?? "node", process.argv[1] ?? "token-goat", cmd, "--help"];
|
|
17895
|
+
program2.parse(argv);
|
|
17896
|
+
} else {
|
|
17897
|
+
out(generateCompactHelp());
|
|
17898
|
+
}
|
|
17899
|
+
})
|
|
17900
|
+
);
|
|
17901
|
+
program2.helpOption("-h, --help", "display help for command");
|
|
17902
|
+
originalHelpInformation = program2.helpInformation.bind(program2);
|
|
17903
|
+
program2.helpInformation = () => generateCompactHelp();
|
|
17430
17904
|
return program2;
|
|
17431
17905
|
}
|
|
17432
17906
|
function applyExitOverride(command) {
|