token-goat 2.8.6 → 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 +11 -3
- package/dist/{token-goat-chunk-L2XHDICZ.mjs → token-goat-chunk-4KD2N46D.mjs} +28 -27
- package/dist/{token-goat-chunk-GUNYAGOZ.mjs → token-goat-chunk-4ZDRTNGV.mjs} +9942 -95
- package/dist/{token-goat-chunk-LJEETTER.mjs → token-goat-chunk-AUM22UQ2.mjs} +5 -5
- package/dist/{token-goat-chunk-ZIPBLIUZ.mjs → token-goat-chunk-CD6U5OD6.mjs} +28 -4
- package/dist/{token-goat-chunk-DK4VLLYB.mjs → token-goat-chunk-FDXINYK4.mjs} +2031 -3665
- package/dist/token-goat-chunk-LHMC5ENL.mjs +79 -0
- package/dist/{token-goat-chunk-TELKICYU.mjs → token-goat-chunk-MR7MNSDR.mjs} +155 -519
- package/dist/{token-goat-chunk-MA5237JN.mjs → token-goat-chunk-RRNRPCLX.mjs} +150 -56
- package/dist/{token-goat-chunk-U7M2LTGP.mjs → token-goat-chunk-SQSB5P5P.mjs} +690 -282
- package/dist/{token-goat-chunk-XAWIELXH.mjs → token-goat-chunk-SXSWQMNP.mjs} +405 -150
- package/dist/{token-goat-chunk-SHN4UTL4.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-2JZ66BBE.mjs +0 -7157
|
@@ -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,43 +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
|
-
RECALL_DEFAULT_LIMIT,
|
|
95
94
|
WEB_OUTPUT_SUBDIR,
|
|
96
95
|
accumulateResidentLine,
|
|
97
96
|
buildCommandManifest,
|
|
98
97
|
checkSymbolBodySize,
|
|
99
|
-
commandHash,
|
|
100
98
|
createResidentContextStats,
|
|
101
99
|
filterCommandManifest,
|
|
102
100
|
formatBytes,
|
|
103
101
|
formatCommandManifest,
|
|
104
102
|
formatTokenEstimate,
|
|
105
|
-
getBashOutput,
|
|
106
103
|
getWebOutput,
|
|
107
104
|
getWebOutputByUrlFromDisk,
|
|
108
105
|
getWebOutputRaw,
|
|
109
|
-
isRecallCacheType,
|
|
110
|
-
listRecentRecall,
|
|
111
|
-
normalizeCommandForCacheKey,
|
|
112
106
|
readCopilotMcpTools,
|
|
113
107
|
readStdinJson,
|
|
114
|
-
|
|
108
|
+
runReconcile,
|
|
115
109
|
storeWebOutput,
|
|
116
110
|
summarizeResidentContext
|
|
117
|
-
} from "./token-goat-chunk-
|
|
111
|
+
} from "./token-goat-chunk-MR7MNSDR.mjs";
|
|
118
112
|
import {
|
|
119
113
|
AGENT_SALT_MARKER,
|
|
114
|
+
BASH_OUTPUT_SUBDIR,
|
|
120
115
|
CONTEXT_AUTOCOMPACT_TOKENS,
|
|
121
116
|
DEFAULT_MAX_AGE_MS,
|
|
122
117
|
DEFAULT_MAX_COUNT,
|
|
123
|
-
MATERIALIZE_SHRUNK_IMAGE_JS,
|
|
124
118
|
MAX_FILES_SCANNED,
|
|
125
119
|
PARSER_FINGERPRINT,
|
|
120
|
+
RECALL_DEFAULT_LIMIT,
|
|
126
121
|
SESSIONS_SUBDIR,
|
|
127
122
|
SKILLS_OUTPUT_SUBDIR,
|
|
128
123
|
SKIP_DIRS,
|
|
@@ -131,7 +126,6 @@ import {
|
|
|
131
126
|
UNTRUSTED_WEB_TAG,
|
|
132
127
|
WORKER_HEARTBEAT_STALE_MS,
|
|
133
128
|
WorkerAlreadyRunningError,
|
|
134
|
-
anchoredMarkerPattern,
|
|
135
129
|
applyIndexingPriority,
|
|
136
130
|
auditClaudeMd,
|
|
137
131
|
buildExtractiveCompact,
|
|
@@ -140,14 +134,11 @@ import {
|
|
|
140
134
|
buildProjectMap,
|
|
141
135
|
buildStats,
|
|
142
136
|
capJsonRows,
|
|
137
|
+
commandHash,
|
|
143
138
|
compactDoc,
|
|
144
139
|
compactPathFor,
|
|
145
140
|
confirmAndApply,
|
|
146
141
|
contentHash,
|
|
147
|
-
copilotCliConfigPath,
|
|
148
|
-
copilotCliMcpToolsDir,
|
|
149
|
-
copilotCliScriptPath,
|
|
150
|
-
copilotCliUserRoot,
|
|
151
142
|
detectWalkMode,
|
|
152
143
|
dirtyQueuePathFor,
|
|
153
144
|
docxOutline,
|
|
@@ -171,13 +162,13 @@ import {
|
|
|
171
162
|
findMemSuggestionCandidates,
|
|
172
163
|
findMemoryMd,
|
|
173
164
|
findOrphanedChunkPaths,
|
|
174
|
-
findStrayClaudeMdBlocks,
|
|
175
165
|
findSystemTempFiles,
|
|
176
166
|
formatAge,
|
|
177
167
|
formatCsvTable,
|
|
178
168
|
formatMemSuggestions,
|
|
179
169
|
formatProjectMap,
|
|
180
170
|
formatXlsxRange,
|
|
171
|
+
getBashOutput,
|
|
181
172
|
getContextPressure,
|
|
182
173
|
getDirtyPathsFor,
|
|
183
174
|
getEmbeddingCoverage,
|
|
@@ -188,35 +179,24 @@ import {
|
|
|
188
179
|
getSessionFiles,
|
|
189
180
|
getSessionId,
|
|
190
181
|
getSkillFilePath,
|
|
191
|
-
|
|
182
|
+
getTrackedFiles,
|
|
192
183
|
headSheet,
|
|
193
184
|
incrementSkillHit,
|
|
194
185
|
indexFileEmbeddings,
|
|
195
186
|
indexFileSync,
|
|
196
187
|
indexedPathSpellingIsStale,
|
|
197
188
|
injectionFencingEnabled,
|
|
198
|
-
installClaudeMd,
|
|
199
|
-
installCodex,
|
|
200
|
-
installCopilotCli,
|
|
201
|
-
installGrok,
|
|
202
|
-
installHooks,
|
|
203
|
-
installKimi,
|
|
204
|
-
installSkill,
|
|
205
|
-
installVscode,
|
|
206
189
|
isAvailable,
|
|
207
|
-
isCodexInstalled,
|
|
208
190
|
isCompactFresh,
|
|
209
|
-
isCopilotCliInstalled,
|
|
210
191
|
isEmbedFresh,
|
|
211
192
|
isEmbeddableDocument,
|
|
212
|
-
isGrokInstalled,
|
|
213
193
|
isHintCategory,
|
|
214
|
-
isInstalled,
|
|
215
|
-
isKimiInstalled,
|
|
216
194
|
isParseSkipEligible,
|
|
195
|
+
isRecallCacheType,
|
|
217
196
|
isWorkerRunning,
|
|
218
197
|
listBlobs,
|
|
219
198
|
listOutputs,
|
|
199
|
+
listRecentRecall,
|
|
220
200
|
listSheets,
|
|
221
201
|
listSkills,
|
|
222
202
|
loadBlob,
|
|
@@ -224,7 +204,7 @@ import {
|
|
|
224
204
|
mapLookupBytesSaved,
|
|
225
205
|
markCategoryEffective,
|
|
226
206
|
markCategoryIneffective,
|
|
227
|
-
|
|
207
|
+
normalizeCommandForCacheKey,
|
|
228
208
|
parseWhereSpecs,
|
|
229
209
|
pptxNotesText,
|
|
230
210
|
pptxOutline,
|
|
@@ -245,6 +225,7 @@ import {
|
|
|
245
225
|
runContextStats,
|
|
246
226
|
runDetachedWorkerDaemon,
|
|
247
227
|
scanAndRecord,
|
|
228
|
+
searchRecall,
|
|
248
229
|
shrinkImage,
|
|
249
230
|
skillOutputsDir,
|
|
250
231
|
startDetachedWorker,
|
|
@@ -253,33 +234,32 @@ import {
|
|
|
253
234
|
storeOutput,
|
|
254
235
|
tokenGoatHome,
|
|
255
236
|
trimToBudget,
|
|
256
|
-
uninstallClaudeMd,
|
|
257
|
-
uninstallCodex,
|
|
258
|
-
uninstallCopilotCli,
|
|
259
|
-
uninstallGrok,
|
|
260
|
-
uninstallHooks,
|
|
261
|
-
uninstallKimi,
|
|
262
|
-
uninstallSkill,
|
|
263
|
-
uninstallVscode,
|
|
264
237
|
urlPolicyDenialReason,
|
|
265
|
-
|
|
238
|
+
visionTokensSavedByText,
|
|
266
239
|
walkProject,
|
|
267
240
|
writeCompact
|
|
268
|
-
} from "./token-goat-chunk-
|
|
241
|
+
} from "./token-goat-chunk-FDXINYK4.mjs";
|
|
269
242
|
import {
|
|
270
243
|
C,
|
|
271
244
|
CONFIG_KEY_ENV_OVERRIDES,
|
|
272
245
|
ENV_KEYS,
|
|
273
246
|
FILTERS,
|
|
274
247
|
LOCK_WAIT_MS_HARDENED,
|
|
248
|
+
MATERIALIZE_SHRUNK_IMAGE_JS,
|
|
275
249
|
RESET,
|
|
276
250
|
VERSION,
|
|
277
251
|
_useRichStats,
|
|
252
|
+
anchoredMarkerPattern,
|
|
278
253
|
atomicWriteBytes,
|
|
279
254
|
atomicWriteText,
|
|
280
255
|
canonicalize,
|
|
256
|
+
cappedSourceBytesSaved,
|
|
281
257
|
colorStdout,
|
|
282
258
|
configPath,
|
|
259
|
+
copilotCliConfigPath,
|
|
260
|
+
copilotCliMcpToolsDir,
|
|
261
|
+
copilotCliScriptPath,
|
|
262
|
+
copilotCliUserRoot,
|
|
283
263
|
countNoun,
|
|
284
264
|
dataDir,
|
|
285
265
|
decodeSource,
|
|
@@ -294,6 +274,7 @@ import {
|
|
|
294
274
|
fg,
|
|
295
275
|
fileIsAbsent,
|
|
296
276
|
findProject,
|
|
277
|
+
findStrayClaudeMdBlocks,
|
|
297
278
|
fingerprintContent,
|
|
298
279
|
fingerprintFile,
|
|
299
280
|
foldPath,
|
|
@@ -304,9 +285,24 @@ import {
|
|
|
304
285
|
getLastProjectConfigParseError,
|
|
305
286
|
getProjectConfigInfo,
|
|
306
287
|
globalDbPath,
|
|
288
|
+
groupHasTokenGoat,
|
|
289
|
+
installClaudeMd,
|
|
290
|
+
installCodex,
|
|
291
|
+
installCopilotCli,
|
|
292
|
+
installGrok,
|
|
293
|
+
installHooks,
|
|
294
|
+
installKimi,
|
|
307
295
|
installSingleFilePlugin,
|
|
296
|
+
installSkill,
|
|
297
|
+
installVscode,
|
|
308
298
|
invalidateConfigCache,
|
|
299
|
+
isCodexInstalled,
|
|
300
|
+
isCopilotCliInstalled,
|
|
309
301
|
isDotenvPath,
|
|
302
|
+
isGrokInstalled,
|
|
303
|
+
isInstalled,
|
|
304
|
+
isKimiInstalled,
|
|
305
|
+
isTestFile,
|
|
310
306
|
isUnderBlockedRoot,
|
|
311
307
|
isWindows,
|
|
312
308
|
lastProjectConfigLockedKeys,
|
|
@@ -314,6 +310,7 @@ import {
|
|
|
314
310
|
loadPersistedConfig,
|
|
315
311
|
normalizeDarwinSystemAlias,
|
|
316
312
|
normalizePath,
|
|
313
|
+
otherScopeHasManagedServer,
|
|
317
314
|
pad,
|
|
318
315
|
parse,
|
|
319
316
|
pushAll,
|
|
@@ -341,14 +338,23 @@ import {
|
|
|
341
338
|
summarize,
|
|
342
339
|
toDisplayPath,
|
|
343
340
|
toKB,
|
|
341
|
+
uninstallClaudeMd,
|
|
342
|
+
uninstallCodex,
|
|
343
|
+
uninstallCopilotCli,
|
|
344
|
+
uninstallGrok,
|
|
345
|
+
uninstallHooks,
|
|
346
|
+
uninstallKimi,
|
|
344
347
|
uninstallSingleFilePlugin,
|
|
348
|
+
uninstallSkill,
|
|
349
|
+
uninstallVscode,
|
|
345
350
|
validateEnumField,
|
|
346
351
|
validateNumericField,
|
|
352
|
+
vscodeDecoderConfigured,
|
|
347
353
|
withExtension,
|
|
348
354
|
withFileLock,
|
|
349
355
|
withRetryOnLock,
|
|
350
356
|
writeJsonSettings
|
|
351
|
-
} from "./token-goat-chunk-
|
|
357
|
+
} from "./token-goat-chunk-4ZDRTNGV.mjs";
|
|
352
358
|
import {
|
|
353
359
|
__export
|
|
354
360
|
} from "./token-goat-chunk-AEX54RUZ.mjs";
|
|
@@ -3467,9 +3473,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3467
3473
|
* @param {string} [path]
|
|
3468
3474
|
* @return {(string|null|Command)}
|
|
3469
3475
|
*/
|
|
3470
|
-
executableDir(
|
|
3471
|
-
if (
|
|
3472
|
-
this._executableDir =
|
|
3476
|
+
executableDir(path30) {
|
|
3477
|
+
if (path30 === void 0) return this._executableDir;
|
|
3478
|
+
this._executableDir = path30;
|
|
3473
3479
|
return this;
|
|
3474
3480
|
}
|
|
3475
3481
|
/**
|
|
@@ -3769,7 +3775,7 @@ function attemptedCommandName(argv) {
|
|
|
3769
3775
|
|
|
3770
3776
|
// src/cli.ts
|
|
3771
3777
|
import * as fs28 from "fs";
|
|
3772
|
-
import * as
|
|
3778
|
+
import * as path29 from "path";
|
|
3773
3779
|
import { homedir as homedir13 } from "os";
|
|
3774
3780
|
|
|
3775
3781
|
// src/walk_index.ts
|
|
@@ -5657,9 +5663,110 @@ function formatCues(cues) {
|
|
|
5657
5663
|
return cues.map((cue) => `[${formatTimestamp(cue.startSeconds)}]${cue.speaker !== null ? ` ${cue.speaker}:` : ""} ${cue.text}`).join("\n");
|
|
5658
5664
|
}
|
|
5659
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
|
+
|
|
5660
5767
|
// src/purge.ts
|
|
5661
5768
|
import * as fs10 from "node:fs";
|
|
5662
|
-
import * as
|
|
5769
|
+
import * as path10 from "node:path";
|
|
5663
5770
|
function directorySize(root) {
|
|
5664
5771
|
let total = 0;
|
|
5665
5772
|
let entries;
|
|
@@ -5669,7 +5776,7 @@ function directorySize(root) {
|
|
|
5669
5776
|
return 0;
|
|
5670
5777
|
}
|
|
5671
5778
|
for (const entry of entries) {
|
|
5672
|
-
const full =
|
|
5779
|
+
const full = path10.join(root, entry.name);
|
|
5673
5780
|
if (entry.isDirectory()) {
|
|
5674
5781
|
total += directorySize(full);
|
|
5675
5782
|
continue;
|
|
@@ -5712,7 +5819,7 @@ function formatBytes2(bytes) {
|
|
|
5712
5819
|
|
|
5713
5820
|
// src/cli_stats.ts
|
|
5714
5821
|
import * as fs11 from "node:fs";
|
|
5715
|
-
import * as
|
|
5822
|
+
import * as path11 from "node:path";
|
|
5716
5823
|
function writeRaw(text) {
|
|
5717
5824
|
const payload = colorStdout() ? text : stripAnsi(text);
|
|
5718
5825
|
process.stdout.write(ensureNewline(payload));
|
|
@@ -5724,7 +5831,7 @@ function formatTopFiles(ranked) {
|
|
|
5724
5831
|
if (ranked.length === 0) return "";
|
|
5725
5832
|
const lines = ["Top files this session:"];
|
|
5726
5833
|
for (const { path: filePath, count } of ranked) {
|
|
5727
|
-
const basename8 =
|
|
5834
|
+
const basename8 = path11.basename(filePath);
|
|
5728
5835
|
lines.push(` ${count.toString().padStart(3)}x ${basename8} (${filePath})`);
|
|
5729
5836
|
}
|
|
5730
5837
|
return lines.join("\n");
|
|
@@ -5741,12 +5848,12 @@ function renderTopSessionFiles(topN = 5) {
|
|
|
5741
5848
|
}
|
|
5742
5849
|
function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
|
|
5743
5850
|
try {
|
|
5744
|
-
const sessionsDir = overrideSessionsDir ??
|
|
5851
|
+
const sessionsDir = overrideSessionsDir ?? path11.join(dataDir(), "sessions");
|
|
5745
5852
|
if (!fs11.existsSync(sessionsDir)) return "";
|
|
5746
|
-
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);
|
|
5747
5854
|
for (const { name } of files) {
|
|
5748
5855
|
try {
|
|
5749
|
-
const raw = fs11.readFileSync(
|
|
5856
|
+
const raw = fs11.readFileSync(path11.join(sessionsDir, name), "utf-8");
|
|
5750
5857
|
const data = JSON.parse(raw);
|
|
5751
5858
|
const filesList = data["files"];
|
|
5752
5859
|
if (!Array.isArray(filesList)) continue;
|
|
@@ -5766,8 +5873,8 @@ function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
|
|
|
5766
5873
|
var METHODOLOGY = {
|
|
5767
5874
|
estimate_scope: "Local estimate of content avoided or reduced by token-goat.",
|
|
5768
5875
|
billing: "They are not GitHub Copilot usage, provider-reported token consumption, or billing data.",
|
|
5769
|
-
byte_derived_formula: "Most read, hook, and command entries go through savedTokensFromBytes in src/stats.ts, which is Math.round(bytes_saved / 4).",
|
|
5770
|
-
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.",
|
|
5771
5878
|
advisory_events: "Zero-byte, zero-token advisory events show that guidance fired, not that an agent followed it.",
|
|
5772
5879
|
audit: "Use stats --full or stats --json for source and command breakdowns; reconcile billing with provider-exported usage data."
|
|
5773
5880
|
};
|
|
@@ -5835,12 +5942,12 @@ function runStats(opts = {}) {
|
|
|
5835
5942
|
// src/cli_doctor.ts
|
|
5836
5943
|
import * as fs12 from "fs";
|
|
5837
5944
|
import * as os8 from "os";
|
|
5838
|
-
import * as
|
|
5945
|
+
import * as path12 from "path";
|
|
5839
5946
|
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
5840
5947
|
function globalMcpConfigPath() {
|
|
5841
5948
|
const copilotHome = process.env["COPILOT_HOME"];
|
|
5842
|
-
const root = copilotHome !== void 0 && copilotHome.trim() !== "" ?
|
|
5843
|
-
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");
|
|
5844
5951
|
}
|
|
5845
5952
|
function checkGlobalMcpConfig(configPath2 = globalMcpConfigPath()) {
|
|
5846
5953
|
if (!fs12.existsSync(configPath2)) {
|
|
@@ -5957,7 +6064,7 @@ function checkWorkerRunning(dataDir2) {
|
|
|
5957
6064
|
}
|
|
5958
6065
|
var DB_SIZE_WARN_BYTES = 1024 * 1024 * 1024;
|
|
5959
6066
|
function checkDbExists(dataDir2) {
|
|
5960
|
-
const dbPath =
|
|
6067
|
+
const dbPath = path12.join(dataDir2, "global.db");
|
|
5961
6068
|
if (!fs12.existsSync(dbPath)) {
|
|
5962
6069
|
return {
|
|
5963
6070
|
name: "Database",
|
|
@@ -6428,15 +6535,15 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
|
|
|
6428
6535
|
results.push(checkStrayClaudeMdBlocks());
|
|
6429
6536
|
results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
|
|
6430
6537
|
results.push(checkDbExists(actualDataDir));
|
|
6431
|
-
results.push(checkSymbolBodySize(
|
|
6432
|
-
results.push(checkSymbolCount(
|
|
6538
|
+
results.push(checkSymbolBodySize(path12.join(actualDataDir, "global.db")));
|
|
6539
|
+
results.push(checkSymbolCount(path12.join(actualDataDir, "global.db"), rootDir));
|
|
6433
6540
|
results.push(checkDirtyQueueHealth(actualDataDir));
|
|
6434
|
-
results.push(checkCompactionChannel(
|
|
6435
|
-
results.push(checkUnmappedTools(
|
|
6541
|
+
results.push(checkCompactionChannel(path12.join(actualDataDir, "global.db")));
|
|
6542
|
+
results.push(checkUnmappedTools(path12.join(actualDataDir, "global.db")));
|
|
6436
6543
|
const actualConfigPath = configPath2 || configPath();
|
|
6437
6544
|
results.push(checkConfigValid(actualConfigPath));
|
|
6438
6545
|
results.push(checkEmbeddings(loadConfig(rootDir)));
|
|
6439
|
-
results.push(checkEmbeddingCoverage(
|
|
6546
|
+
results.push(checkEmbeddingCoverage(path12.join(actualDataDir, "global.db"), rootDir));
|
|
6440
6547
|
for (const result of checkSecurityPosture(loadConfig(rootDir), actualDataDir)) results.push(result);
|
|
6441
6548
|
results.push(checkDiskSpace(actualDataDir));
|
|
6442
6549
|
const copilotResult = checkCopilotCli(copilotCliConfigPath(), copilotCliScriptPath());
|
|
@@ -6477,7 +6584,7 @@ async function runDoctorAndExit(opts) {
|
|
|
6477
6584
|
console.log();
|
|
6478
6585
|
try {
|
|
6479
6586
|
const dir = skillOutputsDir();
|
|
6480
|
-
const pregenPath =
|
|
6587
|
+
const pregenPath = path12.join(dir, "pregen.json");
|
|
6481
6588
|
if (fs12.existsSync(pregenPath)) {
|
|
6482
6589
|
const content = JSON.parse(fs12.readFileSync(pregenPath, "utf-8"));
|
|
6483
6590
|
const pregenNames = new Set(content.names || []);
|
|
@@ -6487,7 +6594,7 @@ async function runDoctorAndExit(opts) {
|
|
|
6487
6594
|
for (const entry of entries) {
|
|
6488
6595
|
if (!entry.isFile() || !entry.name.endsWith(".meta")) continue;
|
|
6489
6596
|
try {
|
|
6490
|
-
const meta = JSON.parse(fs12.readFileSync(
|
|
6597
|
+
const meta = JSON.parse(fs12.readFileSync(path12.join(dir, entry.name), "utf-8"));
|
|
6491
6598
|
if (meta.skillName && !pregenNames.has(meta.skillName)) {
|
|
6492
6599
|
skillsSeen.add(meta.skillName);
|
|
6493
6600
|
}
|
|
@@ -6514,7 +6621,7 @@ import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as sta
|
|
|
6514
6621
|
import * as http from "http";
|
|
6515
6622
|
import * as https from "https";
|
|
6516
6623
|
import { isIPv4, isIPv6 } from "net";
|
|
6517
|
-
import { resolve as
|
|
6624
|
+
import { resolve as resolve5, join as join10 } from "path";
|
|
6518
6625
|
import { URL as URL2 } from "url";
|
|
6519
6626
|
import { promisify } from "util";
|
|
6520
6627
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -6703,7 +6810,7 @@ function cleanupStaleDownloads() {
|
|
|
6703
6810
|
const files = readdirSync5(cacheDir);
|
|
6704
6811
|
for (const file of files) {
|
|
6705
6812
|
if (file.endsWith(".tmp")) {
|
|
6706
|
-
const filePath =
|
|
6813
|
+
const filePath = resolve5(cacheDir, file);
|
|
6707
6814
|
try {
|
|
6708
6815
|
const stat = statSync4(filePath);
|
|
6709
6816
|
if (Date.now() - stat.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -6969,7 +7076,7 @@ async function getSectionContent(fileId, heading, opts = {}) {
|
|
|
6969
7076
|
|
|
6970
7077
|
// src/pack.ts
|
|
6971
7078
|
import * as fs13 from "node:fs";
|
|
6972
|
-
import * as
|
|
7079
|
+
import * as path14 from "node:path";
|
|
6973
7080
|
|
|
6974
7081
|
// node_modules/minimatch/dist/esm/index.js
|
|
6975
7082
|
var esm_exports = {};
|
|
@@ -8093,11 +8200,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
8093
8200
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
8094
8201
|
};
|
|
8095
8202
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
8096
|
-
var
|
|
8203
|
+
var path13 = {
|
|
8097
8204
|
win32: { sep: "\\" },
|
|
8098
8205
|
posix: { sep: "/" }
|
|
8099
8206
|
};
|
|
8100
|
-
var sep2 = defaultPlatform === "win32" ?
|
|
8207
|
+
var sep2 = defaultPlatform === "win32" ? path13.win32.sep : path13.posix.sep;
|
|
8101
8208
|
minimatch.sep = sep2;
|
|
8102
8209
|
var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
|
|
8103
8210
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -8882,7 +8989,7 @@ var LANG_MAP = {
|
|
|
8882
8989
|
".exs": "elixir"
|
|
8883
8990
|
};
|
|
8884
8991
|
function getLang(filePath) {
|
|
8885
|
-
const ext2 =
|
|
8992
|
+
const ext2 = path14.extname(filePath).toLowerCase();
|
|
8886
8993
|
return LANG_MAP[ext2] ?? "";
|
|
8887
8994
|
}
|
|
8888
8995
|
function matches(rel, patterns) {
|
|
@@ -8892,8 +8999,8 @@ function matches(rel, patterns) {
|
|
|
8892
8999
|
return patterns.some((pat) => mm.minimatch(norm, pat) || mm.minimatch(base, pat));
|
|
8893
9000
|
}
|
|
8894
9001
|
function isPathWithinRoot(rootReal, resolvedPath) {
|
|
8895
|
-
const rel =
|
|
8896
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(".." +
|
|
9002
|
+
const rel = path14.relative(rootReal, resolvedPath);
|
|
9003
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(".." + path14.sep) && !path14.isAbsolute(rel);
|
|
8897
9004
|
}
|
|
8898
9005
|
function openWithinRoot(rootReal, p) {
|
|
8899
9006
|
let fd;
|
|
@@ -8933,11 +9040,11 @@ function openWithinRoot(rootReal, p) {
|
|
|
8933
9040
|
}
|
|
8934
9041
|
function* resolveOpenCandidates(projectRoot, patterns, rootReal, ignorePatterns, seen, skipped) {
|
|
8935
9042
|
for (const pattern of patterns) {
|
|
8936
|
-
const p =
|
|
9043
|
+
const p = path14.isAbsolute(pattern) ? pattern : path14.join(projectRoot, pattern);
|
|
8937
9044
|
if (seen.has(p)) continue;
|
|
8938
9045
|
let rel;
|
|
8939
9046
|
try {
|
|
8940
|
-
rel =
|
|
9047
|
+
rel = path14.relative(projectRoot, p).replace(/\\/g, "/");
|
|
8941
9048
|
} catch {
|
|
8942
9049
|
skipped.push(`${p} (outside project root)`);
|
|
8943
9050
|
continue;
|
|
@@ -9011,7 +9118,7 @@ function stripBlockComments(content, pattern, lineStates) {
|
|
|
9011
9118
|
);
|
|
9012
9119
|
}
|
|
9013
9120
|
function stripComments(content, filePath) {
|
|
9014
|
-
const ext2 =
|
|
9121
|
+
const ext2 = path14.extname(filePath).toLowerCase();
|
|
9015
9122
|
const lineStates = computeLineStartQuoteStates(content);
|
|
9016
9123
|
if (ext2 === ".py") {
|
|
9017
9124
|
return stripLineComments(content, PY_LINE_COMMENT_RE, lineStates);
|
|
@@ -9049,7 +9156,7 @@ var SAFE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp
|
|
|
9049
9156
|
function scanSecrets(files) {
|
|
9050
9157
|
const hits = [];
|
|
9051
9158
|
for (const pf of files) {
|
|
9052
|
-
if (SAFE_EXTS.has(
|
|
9159
|
+
if (SAFE_EXTS.has(path14.extname(pf.path).toLowerCase())) {
|
|
9053
9160
|
continue;
|
|
9054
9161
|
}
|
|
9055
9162
|
for (const [lineno, line] of pf.content.split("\n").entries()) {
|
|
@@ -9072,7 +9179,7 @@ function scanSecrets(files) {
|
|
|
9072
9179
|
function collectFiles(projectRoot, patterns, opts = {}) {
|
|
9073
9180
|
const result = { files: [], skipped: [], total_lines: 0, total_tokens: 0 };
|
|
9074
9181
|
const seen = /* @__PURE__ */ new Set();
|
|
9075
|
-
const rootResolved =
|
|
9182
|
+
const rootResolved = path14.resolve(projectRoot);
|
|
9076
9183
|
let rootReal;
|
|
9077
9184
|
try {
|
|
9078
9185
|
rootReal = fs13.realpathSync(rootResolved);
|
|
@@ -9247,7 +9354,7 @@ function formatPack(result, style, opts = {}) {
|
|
|
9247
9354
|
function estimateBudget(projectRoot, patterns, opts = {}) {
|
|
9248
9355
|
const result = { entries: [], skipped: [], total_lines: 0, total_tokens: 0 };
|
|
9249
9356
|
const seen = /* @__PURE__ */ new Set();
|
|
9250
|
-
const rootResolved =
|
|
9357
|
+
const rootResolved = path14.resolve(projectRoot);
|
|
9251
9358
|
let rootReal;
|
|
9252
9359
|
try {
|
|
9253
9360
|
rootReal = fs13.realpathSync(rootResolved);
|
|
@@ -9744,7 +9851,7 @@ function formatFailureDeltaJson(delta, runner) {
|
|
|
9744
9851
|
|
|
9745
9852
|
// src/failures_state.ts
|
|
9746
9853
|
import * as fs14 from "node:fs";
|
|
9747
|
-
import * as
|
|
9854
|
+
import * as path15 from "node:path";
|
|
9748
9855
|
var KEY_RE = /^[A-Za-z0-9_-]{1,80}$/;
|
|
9749
9856
|
var DEFAULT_FAILURES_STATE_KEY = "default";
|
|
9750
9857
|
function validateKey(key) {
|
|
@@ -9756,7 +9863,7 @@ function validateKey(key) {
|
|
|
9756
9863
|
}
|
|
9757
9864
|
function failuresStatePath(projectHash, key) {
|
|
9758
9865
|
validateKey(key);
|
|
9759
|
-
return
|
|
9866
|
+
return path15.join(dataDir(), "projects", `${projectHash}_failures_${key}.json`);
|
|
9760
9867
|
}
|
|
9761
9868
|
function loadFailureSnapshot(projectHash, key) {
|
|
9762
9869
|
const p = failuresStatePath(projectHash, key);
|
|
@@ -9777,7 +9884,7 @@ function loadFailureSnapshot(projectHash, key) {
|
|
|
9777
9884
|
}
|
|
9778
9885
|
function saveFailureSnapshot(projectHash, key, snapshot) {
|
|
9779
9886
|
const p = failuresStatePath(projectHash, key);
|
|
9780
|
-
const dir =
|
|
9887
|
+
const dir = path15.dirname(p);
|
|
9781
9888
|
ensureDirSync(dir);
|
|
9782
9889
|
const content = JSON.stringify(snapshot);
|
|
9783
9890
|
const doSave = () => {
|
|
@@ -9789,18 +9896,18 @@ function saveFailureSnapshot(projectHash, key, snapshot) {
|
|
|
9789
9896
|
|
|
9790
9897
|
// src/text_commands.ts
|
|
9791
9898
|
import * as fs16 from "fs";
|
|
9792
|
-
import * as
|
|
9899
|
+
import * as path17 from "path";
|
|
9793
9900
|
|
|
9794
9901
|
// src/project_memory.ts
|
|
9795
9902
|
import * as fs15 from "node:fs";
|
|
9796
|
-
import * as
|
|
9903
|
+
import * as path16 from "node:path";
|
|
9797
9904
|
var MAX_ENTRIES = 30;
|
|
9798
9905
|
var KEY_RE2 = /^[A-Za-z0-9_-]{1,80}$/;
|
|
9799
9906
|
function ordinal(a, b) {
|
|
9800
9907
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
9801
9908
|
}
|
|
9802
9909
|
function memoryPath(projectHash) {
|
|
9803
|
-
return
|
|
9910
|
+
return path16.join(dataDir(), "projects", `${projectHash}_memory.toml`);
|
|
9804
9911
|
}
|
|
9805
9912
|
function validateKey2(key) {
|
|
9806
9913
|
if (!KEY_RE2.test(key)) {
|
|
@@ -9859,7 +9966,7 @@ function save(filePath, entries) {
|
|
|
9859
9966
|
lines.push(`${k} = "${escaped}"`);
|
|
9860
9967
|
}
|
|
9861
9968
|
const content = lines.length > 0 ? lines.join("\n") + "\n" : "";
|
|
9862
|
-
const dir =
|
|
9969
|
+
const dir = path16.dirname(filePath);
|
|
9863
9970
|
ensureDirSync(dir);
|
|
9864
9971
|
atomicWriteText(filePath, content);
|
|
9865
9972
|
}
|
|
@@ -9869,7 +9976,7 @@ function loadEntries(projectHash) {
|
|
|
9869
9976
|
function setEntry(projectHash, key, value) {
|
|
9870
9977
|
validateKey2(key);
|
|
9871
9978
|
const p = memoryPath(projectHash);
|
|
9872
|
-
const dir =
|
|
9979
|
+
const dir = path16.dirname(p);
|
|
9873
9980
|
ensureDirSync(dir);
|
|
9874
9981
|
const doSet = () => {
|
|
9875
9982
|
const entries = loadRaw(p);
|
|
@@ -9967,7 +10074,7 @@ function collectTodoFiles(patterns) {
|
|
|
9967
10074
|
if (patterns.length > 0) {
|
|
9968
10075
|
const results = [];
|
|
9969
10076
|
for (const p of patterns) {
|
|
9970
|
-
const abs =
|
|
10077
|
+
const abs = path17.resolve(p);
|
|
9971
10078
|
try {
|
|
9972
10079
|
const stat = fs16.statSync(abs);
|
|
9973
10080
|
if (stat.isDirectory()) {
|
|
@@ -10238,7 +10345,7 @@ function isProjectFrame(framePath, cwd) {
|
|
|
10238
10345
|
if (framePath.includes("/rustc/")) return false;
|
|
10239
10346
|
if (framePath.includes(".cargo/registry") || framePath.includes(".cargo\\registry")) return false;
|
|
10240
10347
|
if (/^<.+>$/.test(framePath)) return false;
|
|
10241
|
-
if (!
|
|
10348
|
+
if (!path17.isAbsolute(framePath) && !framePath.startsWith("..")) return true;
|
|
10242
10349
|
return false;
|
|
10243
10350
|
}
|
|
10244
10351
|
function resolveFrameSymbol(frame, projectRoot) {
|
|
@@ -10451,17 +10558,17 @@ function findLockfile(startPath) {
|
|
|
10451
10558
|
if (stat !== void 0 && stat.isFile()) {
|
|
10452
10559
|
return { file: startPath, others: [] };
|
|
10453
10560
|
}
|
|
10454
|
-
const dir = stat !== void 0 && stat.isDirectory() ? startPath :
|
|
10561
|
+
const dir = stat !== void 0 && stat.isDirectory() ? startPath : path17.dirname(startPath);
|
|
10455
10562
|
const found = [];
|
|
10456
10563
|
for (const name of LOCK_PRIORITY) {
|
|
10457
|
-
const candidate =
|
|
10564
|
+
const candidate = path17.join(dir, name);
|
|
10458
10565
|
if (fs16.existsSync(candidate)) found.push(candidate);
|
|
10459
10566
|
}
|
|
10460
10567
|
try {
|
|
10461
10568
|
const entries = fs16.readdirSync(dir);
|
|
10462
10569
|
for (const e of entries) {
|
|
10463
10570
|
if (/^requirements.*\.txt$/.test(e)) {
|
|
10464
|
-
const p =
|
|
10571
|
+
const p = path17.join(dir, e);
|
|
10465
10572
|
if (!found.includes(p)) found.push(p);
|
|
10466
10573
|
}
|
|
10467
10574
|
}
|
|
@@ -10658,7 +10765,7 @@ function parsePnpmLock(content) {
|
|
|
10658
10765
|
return deps;
|
|
10659
10766
|
}
|
|
10660
10767
|
function parseLockFile(filePath) {
|
|
10661
|
-
const base =
|
|
10768
|
+
const base = path17.basename(filePath);
|
|
10662
10769
|
const content = fs16.readFileSync(filePath, "utf8");
|
|
10663
10770
|
if (base === "package-lock.json") return { format: "npm", deps: parsePackageLockJson(content) };
|
|
10664
10771
|
if (base === "yarn.lock") return { format: "yarn", deps: parseYarnLock(content) };
|
|
@@ -10774,7 +10881,7 @@ Depended on by direct/top-level deps (${dependedOnBy.length}):
|
|
|
10774
10881
|
`);
|
|
10775
10882
|
}
|
|
10776
10883
|
function cmdLockdeps(filePath, opts) {
|
|
10777
|
-
const target = filePath !== void 0 ?
|
|
10884
|
+
const target = filePath !== void 0 ? path17.resolve(filePath) : process.cwd();
|
|
10778
10885
|
const found = findLockfile(target);
|
|
10779
10886
|
if (found === null) {
|
|
10780
10887
|
throw new Error("No lockfile found. Expected one of: " + LOCK_PRIORITY.join(", ") + ", requirements*.txt");
|
|
@@ -10862,7 +10969,7 @@ function cmdNote(action, key, value, opts) {
|
|
|
10862
10969
|
throw new Error(`Unknown note action: ${action}. Use: set, get, unset, list, clear`);
|
|
10863
10970
|
}
|
|
10864
10971
|
function loadAllSessionReadCounts() {
|
|
10865
|
-
const sessionsDir =
|
|
10972
|
+
const sessionsDir = path17.join(tokenGoatHome(), "sessions");
|
|
10866
10973
|
const totals = /* @__PURE__ */ new Map();
|
|
10867
10974
|
let entries;
|
|
10868
10975
|
try {
|
|
@@ -10872,7 +10979,7 @@ function loadAllSessionReadCounts() {
|
|
|
10872
10979
|
}
|
|
10873
10980
|
for (const entry of entries) {
|
|
10874
10981
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
10875
|
-
const filePath =
|
|
10982
|
+
const filePath = path17.join(sessionsDir, entry.name);
|
|
10876
10983
|
let raw;
|
|
10877
10984
|
try {
|
|
10878
10985
|
raw = JSON.parse(fs16.readFileSync(filePath, "utf8"));
|
|
@@ -11008,7 +11115,7 @@ function cmdIgnores(opts) {
|
|
|
11008
11115
|
}
|
|
11009
11116
|
function countPackIgnorePatterns(root) {
|
|
11010
11117
|
try {
|
|
11011
|
-
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;
|
|
11012
11119
|
} catch {
|
|
11013
11120
|
return 0;
|
|
11014
11121
|
}
|
|
@@ -11017,7 +11124,7 @@ function countPackIgnorePatterns(root) {
|
|
|
11017
11124
|
// src/dep_docs.ts
|
|
11018
11125
|
import { createRequire } from "node:module";
|
|
11019
11126
|
import * as fs17 from "node:fs";
|
|
11020
|
-
import * as
|
|
11127
|
+
import * as path18 from "node:path";
|
|
11021
11128
|
var _require = createRequire(import.meta.url);
|
|
11022
11129
|
var _ts = null;
|
|
11023
11130
|
var _tsLoadAttempted = false;
|
|
@@ -11068,7 +11175,7 @@ function listInstalledPackageNames(nodeModulesDir) {
|
|
|
11068
11175
|
if (entry.name.startsWith("@")) {
|
|
11069
11176
|
let scoped;
|
|
11070
11177
|
try {
|
|
11071
|
-
scoped = fs17.readdirSync(
|
|
11178
|
+
scoped = fs17.readdirSync(path18.join(nodeModulesDir, entry.name), { withFileTypes: true });
|
|
11072
11179
|
} catch {
|
|
11073
11180
|
continue;
|
|
11074
11181
|
}
|
|
@@ -11085,7 +11192,7 @@ var README_NAME_RE = /^readme(\.(md|markdown|txt))?$/i;
|
|
|
11085
11192
|
function findReadmeFile(pkgDir) {
|
|
11086
11193
|
const direct = findReadmeIn(pkgDir);
|
|
11087
11194
|
if (direct !== null) return direct;
|
|
11088
|
-
return findReadmeIn(
|
|
11195
|
+
return findReadmeIn(path18.join(pkgDir, "docs"));
|
|
11089
11196
|
}
|
|
11090
11197
|
function findReadmeIn(dir) {
|
|
11091
11198
|
let entries;
|
|
@@ -11102,7 +11209,7 @@ function findReadmeIn(dir) {
|
|
|
11102
11209
|
return 1;
|
|
11103
11210
|
};
|
|
11104
11211
|
matches2.sort((a, b) => rank(a.name) - rank(b.name));
|
|
11105
|
-
return
|
|
11212
|
+
return path18.join(dir, matches2[0].name);
|
|
11106
11213
|
}
|
|
11107
11214
|
function typesPackageDirName(pkgName) {
|
|
11108
11215
|
if (pkgName.startsWith("@")) {
|
|
@@ -11129,30 +11236,30 @@ function resolveTypesLocation(pkgDir, pkgJson, nodeModulesDir, pkgName) {
|
|
|
11129
11236
|
if (declared !== void 0) {
|
|
11130
11237
|
const candidates = [declared, declared.endsWith(".d.ts") ? declared : `${declared}.d.ts`, declared.replace(/\.[cm]?[jt]s$/, ".d.ts")];
|
|
11131
11238
|
for (const c of candidates) {
|
|
11132
|
-
const p =
|
|
11239
|
+
const p = path18.join(pkgDir, c);
|
|
11133
11240
|
if (fileExists(p)) return { path: p, source: "bundled" };
|
|
11134
11241
|
}
|
|
11135
11242
|
}
|
|
11136
11243
|
const main = typeof pkgJson["main"] === "string" ? pkgJson["main"] : "index.js";
|
|
11137
|
-
const mainDts =
|
|
11244
|
+
const mainDts = path18.join(pkgDir, main.replace(/\.[cm]?js$/, ".d.ts"));
|
|
11138
11245
|
if (fileExists(mainDts)) return { path: mainDts, source: "bundled" };
|
|
11139
|
-
const indexDts =
|
|
11246
|
+
const indexDts = path18.join(pkgDir, "index.d.ts");
|
|
11140
11247
|
if (fileExists(indexDts)) return { path: indexDts, source: "bundled" };
|
|
11141
11248
|
const typesDirName = typesPackageDirName(pkgName);
|
|
11142
|
-
const typesPkgDir =
|
|
11143
|
-
const typesPkgJsonRaw = readFileTextOrNull(
|
|
11249
|
+
const typesPkgDir = path18.join(nodeModulesDir, "@types", typesDirName);
|
|
11250
|
+
const typesPkgJsonRaw = readFileTextOrNull(path18.join(typesPkgDir, "package.json"));
|
|
11144
11251
|
if (typesPkgJsonRaw !== null) {
|
|
11145
11252
|
try {
|
|
11146
11253
|
const typesPkgJson = JSON.parse(typesPkgJsonRaw);
|
|
11147
11254
|
const entry = (typeof typesPkgJson["types"] === "string" ? typesPkgJson["types"] : void 0) ?? (typeof typesPkgJson["main"] === "string" ? typesPkgJson["main"] : void 0) ?? "index.d.ts";
|
|
11148
11255
|
const entryCandidates = [entry, entry.endsWith(".d.ts") ? entry : `${entry}.d.ts`, entry.replace(/\.[cm]?[jt]s$/, ".d.ts")];
|
|
11149
11256
|
for (const c of entryCandidates) {
|
|
11150
|
-
const entryPath =
|
|
11257
|
+
const entryPath = path18.join(typesPkgDir, c);
|
|
11151
11258
|
if (fileExists(entryPath)) return { path: entryPath, source: "@types" };
|
|
11152
11259
|
}
|
|
11153
11260
|
} catch {
|
|
11154
11261
|
}
|
|
11155
|
-
const fallback =
|
|
11262
|
+
const fallback = path18.join(typesPkgDir, "index.d.ts");
|
|
11156
11263
|
if (fileExists(fallback)) return { path: fallback, source: "@types" };
|
|
11157
11264
|
}
|
|
11158
11265
|
return null;
|
|
@@ -11219,9 +11326,9 @@ function recordDepDocsStat(fullSourceBytes, emittedText, packageName) {
|
|
|
11219
11326
|
}
|
|
11220
11327
|
function runDepDocs(opts) {
|
|
11221
11328
|
const root = resolveProjectRoot({ project: opts.projectRoot ?? process.cwd() });
|
|
11222
|
-
const nodeModulesDir =
|
|
11223
|
-
const pkgDir =
|
|
11224
|
-
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");
|
|
11225
11332
|
if (!dirExists(pkgDir) || !fileExists(pkgJsonPath)) {
|
|
11226
11333
|
const known = listInstalledPackageNames(nodeModulesDir);
|
|
11227
11334
|
const suggestions = suggestPackageNames(opts.packageName, known);
|
|
@@ -11384,13 +11491,13 @@ async function buildResumePacket(sessionId) {
|
|
|
11384
11491
|
}
|
|
11385
11492
|
|
|
11386
11493
|
// src/cache_session_commands.ts
|
|
11387
|
-
function
|
|
11494
|
+
function emitErr2(text) {
|
|
11388
11495
|
process.stderr.write(ensureNewline(text));
|
|
11389
11496
|
}
|
|
11390
11497
|
function capAndNote(rows, limit) {
|
|
11391
11498
|
const shown = rows.slice(0, limit);
|
|
11392
11499
|
if (shown.length < rows.length) {
|
|
11393
|
-
|
|
11500
|
+
emitErr2(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
|
|
11394
11501
|
}
|
|
11395
11502
|
return shown;
|
|
11396
11503
|
}
|
|
@@ -11400,11 +11507,11 @@ function parseLimitOpt(cmdName, limitStr, dflt = 30) {
|
|
|
11400
11507
|
try {
|
|
11401
11508
|
n = requireNonNegativeStrictInt("--limit", limitStr);
|
|
11402
11509
|
} catch (e) {
|
|
11403
|
-
|
|
11510
|
+
emitErr2(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
|
|
11404
11511
|
throw new Error(`invalid --limit: ${limitStr}`, { cause: e });
|
|
11405
11512
|
}
|
|
11406
11513
|
if (n === 0) {
|
|
11407
|
-
|
|
11514
|
+
emitErr2(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
|
|
11408
11515
|
throw new Error(`invalid --limit: ${limitStr}`);
|
|
11409
11516
|
}
|
|
11410
11517
|
return n;
|
|
@@ -11738,7 +11845,7 @@ function cmdBaseline(opts) {
|
|
|
11738
11845
|
|
|
11739
11846
|
// src/index_reclaim.ts
|
|
11740
11847
|
import * as fs18 from "node:fs";
|
|
11741
|
-
import * as
|
|
11848
|
+
import * as path19 from "node:path";
|
|
11742
11849
|
var DERIVED_TABLES = ["chunk_vectors", "chunks", "refs", "symbols", "files"];
|
|
11743
11850
|
function indexSizeBytes(dbPath) {
|
|
11744
11851
|
let total = 0;
|
|
@@ -11822,7 +11929,7 @@ function mb(bytes) {
|
|
|
11822
11929
|
}
|
|
11823
11930
|
function cmdReclaimIndex(opts) {
|
|
11824
11931
|
const dbPath = opts.dbPath ?? globalDbPath();
|
|
11825
|
-
if (opts.force !== true && isWorkerRunning(
|
|
11932
|
+
if (opts.force !== true && isWorkerRunning(path19.dirname(dbPath))) {
|
|
11826
11933
|
throw new Error(
|
|
11827
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)."
|
|
11828
11935
|
);
|
|
@@ -11856,7 +11963,7 @@ function cmdReclaimIndex(opts) {
|
|
|
11856
11963
|
}
|
|
11857
11964
|
if (result.checkpointBusy) {
|
|
11858
11965
|
process.stdout.write(
|
|
11859
|
-
` 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
|
|
11860
11967
|
`
|
|
11861
11968
|
);
|
|
11862
11969
|
}
|
|
@@ -11865,16 +11972,16 @@ function cmdReclaimIndex(opts) {
|
|
|
11865
11972
|
// src/config_commands.ts
|
|
11866
11973
|
import * as fs19 from "node:fs";
|
|
11867
11974
|
import * as os9 from "node:os";
|
|
11868
|
-
import * as
|
|
11869
|
-
function
|
|
11975
|
+
import * as path20 from "node:path";
|
|
11976
|
+
function emit2(text) {
|
|
11870
11977
|
const payload = colorStdout() ? text : stripAnsi(text);
|
|
11871
11978
|
process.stdout.write(ensureNewline(payload));
|
|
11872
11979
|
}
|
|
11873
|
-
function
|
|
11980
|
+
function emitErr3(text) {
|
|
11874
11981
|
process.stderr.write(ensureNewline(text));
|
|
11875
11982
|
}
|
|
11876
11983
|
function saveConfigSafe(cfg) {
|
|
11877
|
-
ensureDirSync(
|
|
11984
|
+
ensureDirSync(path20.dirname(configPath()));
|
|
11878
11985
|
saveConfig(cfg);
|
|
11879
11986
|
}
|
|
11880
11987
|
function levenshtein(a, b, cap = 3) {
|
|
@@ -12036,16 +12143,16 @@ function cmdConfig(opts) {
|
|
|
12036
12143
|
if (state.layer !== "global" && state.layer !== "project-unparsed") sources[k] = layerJson(state);
|
|
12037
12144
|
}
|
|
12038
12145
|
if (Object.keys(sources).length > 0) payload["_sources"] = sources;
|
|
12039
|
-
|
|
12146
|
+
emit2(JSON.stringify(payload, null, 2));
|
|
12040
12147
|
return;
|
|
12041
12148
|
}
|
|
12042
12149
|
for (const [k, v] of pairs) {
|
|
12043
12150
|
const state = resolveConfigKeyLayer(k, v, cfg, projectInfo);
|
|
12044
|
-
|
|
12151
|
+
emit2(`${k} = ${JSON.stringify(v)}${state.layer === "project-unparsed" ? "" : layerAnnotation(state)}`);
|
|
12045
12152
|
}
|
|
12046
12153
|
if (projectInfo !== null) {
|
|
12047
|
-
|
|
12048
|
-
|
|
12154
|
+
emit2("");
|
|
12155
|
+
emit2(
|
|
12049
12156
|
projectInfo.parseError !== null ? `# project override ${projectInfo.path} failed to parse (${projectInfo.parseError}); ignored` : `# project override: ${projectInfo.path}`
|
|
12050
12157
|
);
|
|
12051
12158
|
}
|
|
@@ -12063,11 +12170,11 @@ function cmdConfig(opts) {
|
|
|
12063
12170
|
}
|
|
12064
12171
|
const getState = resolveConfigKeyLayer(opts.key, result.value, cfg, getProjectConfigInfo());
|
|
12065
12172
|
if (opts.json === true) {
|
|
12066
|
-
|
|
12173
|
+
emit2(JSON.stringify({ key: opts.key, value: result.value, ...layerJson(getState) }, null, 2));
|
|
12067
12174
|
return;
|
|
12068
12175
|
}
|
|
12069
12176
|
const rendered = typeof result.value === "string" ? result.value : JSON.stringify(result.value);
|
|
12070
|
-
|
|
12177
|
+
emit2(`${rendered}${layerAnnotation(getState)}`);
|
|
12071
12178
|
return;
|
|
12072
12179
|
}
|
|
12073
12180
|
if (action === "set") {
|
|
@@ -12086,7 +12193,7 @@ function cmdConfig(opts) {
|
|
|
12086
12193
|
if (parseErrAtLoad !== null) {
|
|
12087
12194
|
try {
|
|
12088
12195
|
fs19.copyFileSync(configPath(), `${configPath()}.bak`);
|
|
12089
|
-
|
|
12196
|
+
emitErr3(`config set: warning: config.toml failed to parse (${parseErrAtLoad}); backed up the original to config.toml.bak and rewriting it from defaults`);
|
|
12090
12197
|
} catch {
|
|
12091
12198
|
}
|
|
12092
12199
|
}
|
|
@@ -12121,8 +12228,8 @@ function cmdConfig(opts) {
|
|
|
12121
12228
|
saveConfigSafe(cfg);
|
|
12122
12229
|
return coercedValue;
|
|
12123
12230
|
};
|
|
12124
|
-
ensureDirSync(
|
|
12125
|
-
const lockPath =
|
|
12231
|
+
ensureDirSync(path20.dirname(configPath()));
|
|
12232
|
+
const lockPath = path20.join(path20.dirname(configPath()), ".config.lock");
|
|
12126
12233
|
const lockResult = withFileLock(lockPath, applySet, { waitMs: LOCK_WAIT_MS_HARDENED });
|
|
12127
12234
|
const coerced = lockResult === void 0 ? applySet() : lockResult;
|
|
12128
12235
|
invalidateConfigCache();
|
|
@@ -12131,19 +12238,19 @@ function cmdConfig(opts) {
|
|
|
12131
12238
|
const setState = resolveConfigKeyLayer(key, setEffective.found ? setEffective.value : void 0, effectiveCfg, getProjectConfigInfo());
|
|
12132
12239
|
const shadowed = setEffective.found && setEffective.value !== coerced;
|
|
12133
12240
|
if (shadowed && setState.layer === "env") {
|
|
12134
|
-
|
|
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`);
|
|
12135
12242
|
} else if (shadowed && setState.layer === "env-invalid") {
|
|
12136
|
-
|
|
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`);
|
|
12137
12244
|
} else if (setState.layer === "project") {
|
|
12138
|
-
|
|
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`);
|
|
12139
12246
|
} else if (setState.layer === "project-invalid") {
|
|
12140
|
-
|
|
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`);
|
|
12141
12248
|
}
|
|
12142
12249
|
if (opts.json === true) {
|
|
12143
|
-
|
|
12250
|
+
emit2(JSON.stringify({ key, value: coerced }, null, 2));
|
|
12144
12251
|
return;
|
|
12145
12252
|
}
|
|
12146
|
-
|
|
12253
|
+
emit2(`${key} = ${JSON.stringify(coerced)}`);
|
|
12147
12254
|
return;
|
|
12148
12255
|
}
|
|
12149
12256
|
if (action === "validate") {
|
|
@@ -12211,19 +12318,19 @@ function cmdConfig(opts) {
|
|
|
12211
12318
|
}
|
|
12212
12319
|
if (findings.length > 0) process.exitCode = 1;
|
|
12213
12320
|
if (opts.json === true) {
|
|
12214
|
-
|
|
12321
|
+
emit2(JSON.stringify({ findings, ok: findings.length === 0 }, null, 2));
|
|
12215
12322
|
return;
|
|
12216
12323
|
}
|
|
12217
12324
|
if (findings.length === 0) {
|
|
12218
|
-
|
|
12325
|
+
emit2("config validate: no issues found");
|
|
12219
12326
|
return;
|
|
12220
12327
|
}
|
|
12221
12328
|
for (const f of findings) {
|
|
12222
12329
|
const isNearMiss = f.kind === "unknown_section" || f.kind === "unknown_key";
|
|
12223
12330
|
const hint = f.suggestion === void 0 ? "" : isNearMiss ? ` (did you mean: ${f.suggestion}?)` : ` (${f.suggestion})`;
|
|
12224
|
-
|
|
12331
|
+
emit2(`[${f.kind}] ${f.key}${hint}`);
|
|
12225
12332
|
}
|
|
12226
|
-
|
|
12333
|
+
emit2(`config validate: ${findings.length} issue(s) found`);
|
|
12227
12334
|
return;
|
|
12228
12335
|
}
|
|
12229
12336
|
throw new Error(`config: unknown action '${action}'. Use list, get, set, or validate.`);
|
|
@@ -12235,33 +12342,33 @@ function cmdProject(opts) {
|
|
|
12235
12342
|
const active = findProject(process.cwd());
|
|
12236
12343
|
const blocked = cfg.worker.blocked_roots;
|
|
12237
12344
|
if (opts.json === true) {
|
|
12238
|
-
|
|
12345
|
+
emit2(JSON.stringify({ active: active ? { root: active.root, hash: active.hash, marker: active.marker } : null, blocked_roots: blocked }, null, 2));
|
|
12239
12346
|
return;
|
|
12240
12347
|
}
|
|
12241
12348
|
if (active) {
|
|
12242
|
-
|
|
12243
|
-
|
|
12349
|
+
emit2(`Active project: ${active.root}`);
|
|
12350
|
+
emit2(` marker: ${active.marker}`);
|
|
12244
12351
|
} else {
|
|
12245
|
-
|
|
12352
|
+
emit2("Active project: (none \u2014 not inside a recognized project root)");
|
|
12246
12353
|
}
|
|
12247
12354
|
if (blocked.length === 0) {
|
|
12248
|
-
|
|
12355
|
+
emit2("Blocked roots: (none)");
|
|
12249
12356
|
} else {
|
|
12250
|
-
|
|
12251
|
-
for (const r of blocked)
|
|
12357
|
+
emit2("Blocked roots:");
|
|
12358
|
+
for (const r of blocked) emit2(` ${r}`);
|
|
12252
12359
|
}
|
|
12253
12360
|
return;
|
|
12254
12361
|
}
|
|
12255
12362
|
if (action === "exclude") {
|
|
12256
12363
|
if (!opts.pathArg) {
|
|
12257
|
-
|
|
12364
|
+
emitErr3("project exclude requires a path argument");
|
|
12258
12365
|
throw new Error("missing path");
|
|
12259
12366
|
}
|
|
12260
|
-
const target =
|
|
12367
|
+
const target = path20.resolve(opts.pathArg);
|
|
12261
12368
|
const cfg = loadPersistedConfig();
|
|
12262
12369
|
const targetFolded = foldPath(normalizePath(target));
|
|
12263
12370
|
if (cfg.worker.blocked_roots.some((r) => foldPath(normalizePath(r)) === targetFolded)) {
|
|
12264
|
-
|
|
12371
|
+
emit2(`Already excluded: ${target}`);
|
|
12265
12372
|
return;
|
|
12266
12373
|
}
|
|
12267
12374
|
cfg.worker.blocked_roots = [...cfg.worker.blocked_roots, target];
|
|
@@ -12269,12 +12376,12 @@ function cmdProject(opts) {
|
|
|
12269
12376
|
invalidateConfigCache();
|
|
12270
12377
|
const purged = pruneBlockedRoot(target);
|
|
12271
12378
|
if (opts.json === true) {
|
|
12272
|
-
|
|
12379
|
+
emit2(JSON.stringify({ excluded: target, purgedFromIndex: purged.length, blocked_roots: cfg.worker.blocked_roots }, null, 2));
|
|
12273
12380
|
return;
|
|
12274
12381
|
}
|
|
12275
|
-
|
|
12382
|
+
emit2(`Excluded: ${target}`);
|
|
12276
12383
|
if (purged.length > 0) {
|
|
12277
|
-
|
|
12384
|
+
emit2(`Removed ${purged.length} already-indexed file${purged.length === 1 ? "" : "s"} under it from the index.`);
|
|
12278
12385
|
}
|
|
12279
12386
|
return;
|
|
12280
12387
|
}
|
|
@@ -12294,26 +12401,26 @@ function cmdProject(opts) {
|
|
|
12294
12401
|
const orphanChunkPaths = findOrphanedChunkPaths();
|
|
12295
12402
|
if (opts.dryRun === true) {
|
|
12296
12403
|
if (opts.json === true) {
|
|
12297
|
-
|
|
12404
|
+
emit2(JSON.stringify({ dryRun: true, wouldPrune: removed, stale, wouldPruneTempFiles: staleTempFiles.length, staleTempFiles, wouldPruneOrphanChunkFiles: orphanChunkPaths.length, orphanChunkPaths, blocked_roots: before }, null, 2));
|
|
12298
12405
|
return;
|
|
12299
12406
|
}
|
|
12300
12407
|
if (removed === 0) {
|
|
12301
|
-
|
|
12408
|
+
emit2("Would prune 0 stale root(s). Nothing to do.");
|
|
12302
12409
|
} else {
|
|
12303
|
-
|
|
12304
|
-
for (const r of stale)
|
|
12410
|
+
emit2(`Would prune ${removed} stale root(s):`);
|
|
12411
|
+
for (const r of stale) emit2(` ${r}`);
|
|
12305
12412
|
}
|
|
12306
12413
|
if (staleTempFiles.length === 0) {
|
|
12307
|
-
|
|
12414
|
+
emit2("Would prune 0 stale indexed temp-dir file(s). Nothing to do.");
|
|
12308
12415
|
} else {
|
|
12309
|
-
|
|
12310
|
-
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}`);
|
|
12311
12418
|
}
|
|
12312
12419
|
if (orphanChunkPaths.length === 0) {
|
|
12313
|
-
|
|
12420
|
+
emit2("Would prune 0 orphaned embedding chunk file(s). Nothing to do.");
|
|
12314
12421
|
} else {
|
|
12315
|
-
|
|
12316
|
-
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}`);
|
|
12317
12424
|
}
|
|
12318
12425
|
return;
|
|
12319
12426
|
}
|
|
@@ -12323,37 +12430,37 @@ function cmdProject(opts) {
|
|
|
12323
12430
|
const prunedTempFiles = pruneSystemTempFiles();
|
|
12324
12431
|
const prunedOrphanChunks = pruneOrphanedChunks();
|
|
12325
12432
|
if (opts.json === true) {
|
|
12326
|
-
|
|
12433
|
+
emit2(JSON.stringify({ pruned: removed, blocked_roots: after, prunedTempFiles: prunedTempFiles.length, prunedOrphanChunkFiles: prunedOrphanChunks.length }, null, 2));
|
|
12327
12434
|
return;
|
|
12328
12435
|
}
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
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).`);
|
|
12332
12439
|
return;
|
|
12333
12440
|
}
|
|
12334
|
-
|
|
12441
|
+
emitErr3(`project: unknown action '${action}'. Use list, exclude, or prune.`);
|
|
12335
12442
|
throw new Error(`unknown project action: ${action}`);
|
|
12336
12443
|
}
|
|
12337
12444
|
function recordCompactDocStat(fullSourceBytes, emittedText, detail) {
|
|
12338
|
-
const bytesSaved =
|
|
12445
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(emittedText, "utf8"));
|
|
12339
12446
|
recordStat("compact_doc", bytesSaved, savedTokensFromBytes(bytesSaved), void 0, detail);
|
|
12340
12447
|
}
|
|
12341
12448
|
function cmdCompactDoc(opts) {
|
|
12342
|
-
const resolved =
|
|
12449
|
+
const resolved = path20.resolve(opts.filePath);
|
|
12343
12450
|
if (opts.heading !== void 0) {
|
|
12344
12451
|
const result = compactDoc(resolved, opts.heading);
|
|
12345
12452
|
if (result === null) {
|
|
12346
|
-
|
|
12453
|
+
emitErr3(`compact-doc: could not read or compact '${resolved}'`);
|
|
12347
12454
|
throw new Error(`could not compact: ${resolved}`);
|
|
12348
12455
|
}
|
|
12349
12456
|
const legacyFullBytes = fs19.statSync(resolved).size;
|
|
12350
12457
|
if (opts.json === true) {
|
|
12351
12458
|
const jsonText = JSON.stringify({ path: resolved, compact: result }, null, 2);
|
|
12352
|
-
|
|
12459
|
+
emit2(jsonText);
|
|
12353
12460
|
recordCompactDocStat(legacyFullBytes, jsonText, resolved);
|
|
12354
12461
|
return;
|
|
12355
12462
|
}
|
|
12356
|
-
|
|
12463
|
+
emit2(result);
|
|
12357
12464
|
recordCompactDocStat(legacyFullBytes, result, resolved);
|
|
12358
12465
|
return;
|
|
12359
12466
|
}
|
|
@@ -12362,7 +12469,7 @@ function cmdCompactDoc(opts) {
|
|
|
12362
12469
|
try {
|
|
12363
12470
|
sentences = requirePositiveStrictInt("--sentences", opts.sentences);
|
|
12364
12471
|
} catch (e) {
|
|
12365
|
-
|
|
12472
|
+
emitErr3(`compact-doc: --sentences must be a positive number, got: "${opts.sentences}"`);
|
|
12366
12473
|
throw new Error(`invalid --sentences: ${opts.sentences}`, { cause: e });
|
|
12367
12474
|
}
|
|
12368
12475
|
}
|
|
@@ -12375,7 +12482,7 @@ function cmdCompactDoc(opts) {
|
|
|
12375
12482
|
try {
|
|
12376
12483
|
sourceText = fs19.readFileSync(resolved, "utf-8");
|
|
12377
12484
|
} catch {
|
|
12378
|
-
|
|
12485
|
+
emitErr3(`compact-doc: could not read or compact '${resolved}'`);
|
|
12379
12486
|
throw new Error(`could not compact: ${resolved}`);
|
|
12380
12487
|
}
|
|
12381
12488
|
body = buildExtractiveCompact(sourceText, sentences);
|
|
@@ -12384,7 +12491,7 @@ function cmdCompactDoc(opts) {
|
|
|
12384
12491
|
} else {
|
|
12385
12492
|
const existing = readCompactBody(compactPath);
|
|
12386
12493
|
if (existing === null) {
|
|
12387
|
-
|
|
12494
|
+
emitErr3(`compact-doc: could not read or compact '${resolved}'`);
|
|
12388
12495
|
throw new Error(`could not compact: ${resolved}`);
|
|
12389
12496
|
}
|
|
12390
12497
|
body = existing;
|
|
@@ -12392,16 +12499,16 @@ function cmdCompactDoc(opts) {
|
|
|
12392
12499
|
const extractiveFullBytes = fs19.statSync(resolved).size;
|
|
12393
12500
|
if (opts.json === true) {
|
|
12394
12501
|
const jsonText = JSON.stringify({ path: resolved, compactPath, rebuilt, compact: body }, null, 2);
|
|
12395
|
-
|
|
12502
|
+
emit2(jsonText);
|
|
12396
12503
|
recordCompactDocStat(extractiveFullBytes, jsonText, resolved);
|
|
12397
12504
|
return;
|
|
12398
12505
|
}
|
|
12399
12506
|
if (opts.show === true) {
|
|
12400
|
-
|
|
12507
|
+
emit2(body);
|
|
12401
12508
|
recordCompactDocStat(extractiveFullBytes, body, resolved);
|
|
12402
12509
|
return;
|
|
12403
12510
|
}
|
|
12404
|
-
|
|
12511
|
+
emit2(
|
|
12405
12512
|
`Compact sidecar ${rebuilt ? "built" : "already fresh"} at ${compactPath} (source: ${resolved}). Use --show to print it, --force to rebuild.`
|
|
12406
12513
|
);
|
|
12407
12514
|
}
|
|
@@ -12442,11 +12549,11 @@ async function cmdFetchImage(opts) {
|
|
|
12442
12549
|
try {
|
|
12443
12550
|
fetched = await fetchBuffer(opts.url);
|
|
12444
12551
|
} catch (e) {
|
|
12445
|
-
|
|
12552
|
+
emitErr3(`fetch-image: network error \u2014 ${extractErrorMessage(e)}`);
|
|
12446
12553
|
throw new Error(`fetch failed: ${opts.url}`, { cause: e });
|
|
12447
12554
|
}
|
|
12448
12555
|
const buf = fetched.body;
|
|
12449
|
-
const outPath = opts.out ??
|
|
12556
|
+
const outPath = opts.out ?? path20.join(os9.tmpdir(), `tg-fetch-${Date.now()}${extensionForContentType(fetched.contentType)}`);
|
|
12450
12557
|
const originalBytes = buf.length;
|
|
12451
12558
|
let shrunkBytes;
|
|
12452
12559
|
let outData;
|
|
@@ -12469,11 +12576,11 @@ async function cmdFetchImage(opts) {
|
|
|
12469
12576
|
}
|
|
12470
12577
|
atomicWriteBytes(finalPath, outData);
|
|
12471
12578
|
if (opts.json === true) {
|
|
12472
|
-
|
|
12579
|
+
emit2(JSON.stringify({ url: opts.url, out: finalPath, originalBytes, shrunkBytes, wasShrunk }, null, 2));
|
|
12473
12580
|
return;
|
|
12474
12581
|
}
|
|
12475
12582
|
const savings = wasShrunk ? ` (saved ${originalBytes - shrunkBytes} bytes)` : " (not shrunk \u2014 already small or unsupported format)";
|
|
12476
|
-
|
|
12583
|
+
emit2(`Fetched ${originalBytes} bytes \u2192 ${finalPath}${savings}`);
|
|
12477
12584
|
}
|
|
12478
12585
|
function cmdHistory(opts) {
|
|
12479
12586
|
let limit = 30;
|
|
@@ -12481,11 +12588,11 @@ function cmdHistory(opts) {
|
|
|
12481
12588
|
try {
|
|
12482
12589
|
limit = requireNonNegativeStrictInt("--limit", opts.limit);
|
|
12483
12590
|
} catch (e) {
|
|
12484
|
-
|
|
12591
|
+
emitErr3(`history: --limit must be a non-negative number, got: "${opts.limit}"`);
|
|
12485
12592
|
throw new Error(`invalid --limit: ${opts.limit}`, { cause: e });
|
|
12486
12593
|
}
|
|
12487
12594
|
if (limit === 0) {
|
|
12488
|
-
|
|
12595
|
+
emitErr3(`history: --limit must be a positive number, got: "${opts.limit}"`);
|
|
12489
12596
|
throw new Error(`invalid --limit: ${opts.limit}`);
|
|
12490
12597
|
}
|
|
12491
12598
|
}
|
|
@@ -12520,21 +12627,21 @@ function cmdHistory(opts) {
|
|
|
12520
12627
|
return;
|
|
12521
12628
|
}
|
|
12522
12629
|
if (items.length === 0) {
|
|
12523
|
-
|
|
12630
|
+
emit2("No history entries found (no cached bash outputs or web fetches).");
|
|
12524
12631
|
return;
|
|
12525
12632
|
}
|
|
12526
12633
|
const pad2 = (s, n) => s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
12527
|
-
|
|
12634
|
+
emit2(`${pad2("type", 5)} ${pad2("id", 18)} summary`);
|
|
12528
12635
|
for (const item of items) {
|
|
12529
12636
|
const preview = item.summary.length > 80 ? item.summary.slice(0, 77) + "..." : item.summary;
|
|
12530
|
-
|
|
12637
|
+
emit2(`${pad2(item.type, 5)} ${pad2(item.id, 18)} ${preview}`);
|
|
12531
12638
|
}
|
|
12532
12639
|
}
|
|
12533
12640
|
|
|
12534
12641
|
// src/cli_bootstrap_audit.ts
|
|
12535
12642
|
import * as fs20 from "node:fs";
|
|
12536
12643
|
import * as os10 from "node:os";
|
|
12537
|
-
import * as
|
|
12644
|
+
import * as path21 from "node:path";
|
|
12538
12645
|
import * as readline from "node:readline";
|
|
12539
12646
|
var FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["description", "tools", "allowed-tools", "allowed_tools"]);
|
|
12540
12647
|
function parseBudget(name, value) {
|
|
@@ -12593,17 +12700,17 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12593
12700
|
logicalRoot = await fs20.promises.realpath(root);
|
|
12594
12701
|
} catch (error) {
|
|
12595
12702
|
if (error.code === "ENOENT") return entries;
|
|
12596
|
-
diagnostics.push({ path:
|
|
12703
|
+
diagnostics.push({ path: path21.resolve(root), reason: "unreadable root" });
|
|
12597
12704
|
return entries;
|
|
12598
12705
|
}
|
|
12599
12706
|
if (rootIsLink && !followLinks) {
|
|
12600
|
-
diagnostics.push({ path:
|
|
12707
|
+
diagnostics.push({ path: path21.resolve(root), reason: "external root link skipped (use --follow-links)" });
|
|
12601
12708
|
return entries;
|
|
12602
12709
|
}
|
|
12603
12710
|
const canonicalKey = (candidate) => process.platform === "win32" ? candidate.toLocaleLowerCase() : candidate;
|
|
12604
12711
|
const isContained = (candidate, allowedRoot) => {
|
|
12605
|
-
const relative3 =
|
|
12606
|
-
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);
|
|
12607
12714
|
};
|
|
12608
12715
|
async function visit(candidate, allowedRoot, topLevel = false) {
|
|
12609
12716
|
let real;
|
|
@@ -12612,13 +12719,13 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12612
12719
|
candidateIsLink = (await fs20.promises.lstat(candidate)).isSymbolicLink();
|
|
12613
12720
|
real = await fs20.promises.realpath(candidate);
|
|
12614
12721
|
} catch {
|
|
12615
|
-
diagnostics.push({ path:
|
|
12722
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "broken or unreadable link" });
|
|
12616
12723
|
return;
|
|
12617
12724
|
}
|
|
12618
12725
|
const withinAllowedRoot = allowedRoot === void 0 ? isContained(real, logicalRoot) : isContained(real, allowedRoot);
|
|
12619
12726
|
const canFollowExternal = followLinks && topLevel;
|
|
12620
12727
|
if (!withinAllowedRoot && !canFollowExternal) {
|
|
12621
|
-
diagnostics.push({ path:
|
|
12728
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: candidateIsLink ? "external link skipped (use --follow-links)" : "canonical target outside scan root" });
|
|
12622
12729
|
return;
|
|
12623
12730
|
}
|
|
12624
12731
|
const trustedRoot = !withinAllowedRoot && canFollowExternal ? real : allowedRoot ?? real;
|
|
@@ -12626,7 +12733,7 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12626
12733
|
try {
|
|
12627
12734
|
stat = await fs20.promises.stat(real);
|
|
12628
12735
|
} catch {
|
|
12629
|
-
diagnostics.push({ path:
|
|
12736
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "unreadable" });
|
|
12630
12737
|
return;
|
|
12631
12738
|
}
|
|
12632
12739
|
const canonical = process.platform === "win32" ? real.toLocaleLowerCase() : real;
|
|
@@ -12637,14 +12744,14 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12637
12744
|
try {
|
|
12638
12745
|
children = await fs20.promises.readdir(real, { withFileTypes: true });
|
|
12639
12746
|
} catch {
|
|
12640
|
-
diagnostics.push({ path:
|
|
12747
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "unreadable directory" });
|
|
12641
12748
|
return;
|
|
12642
12749
|
}
|
|
12643
12750
|
children.sort((a, b) => a.name.localeCompare(b.name));
|
|
12644
|
-
for (const child of children) await visit(
|
|
12751
|
+
for (const child of children) await visit(path21.join(real, child.name), trustedRoot);
|
|
12645
12752
|
return;
|
|
12646
12753
|
}
|
|
12647
|
-
if (!stat.isFile() ||
|
|
12754
|
+
if (!stat.isFile() || path21.extname(real).toLowerCase() !== ".md") return;
|
|
12648
12755
|
if (seenFiles.has(canonical)) return;
|
|
12649
12756
|
seenFiles.add(canonical);
|
|
12650
12757
|
try {
|
|
@@ -12654,35 +12761,35 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
12654
12761
|
const toolsBytes = Buffer.byteLength(metadata.tools, "utf8");
|
|
12655
12762
|
entries.push({
|
|
12656
12763
|
kind,
|
|
12657
|
-
path:
|
|
12764
|
+
path: path21.resolve(real),
|
|
12658
12765
|
description_bytes: descriptionBytes,
|
|
12659
12766
|
tools_bytes: toolsBytes,
|
|
12660
12767
|
metadata_bytes: descriptionBytes + toolsBytes,
|
|
12661
12768
|
estimated_tokens: Math.floor((descriptionBytes + toolsBytes) / 4)
|
|
12662
12769
|
});
|
|
12663
12770
|
} catch {
|
|
12664
|
-
diagnostics.push({ path:
|
|
12771
|
+
diagnostics.push({ path: path21.resolve(candidate), reason: "unreadable file" });
|
|
12665
12772
|
}
|
|
12666
12773
|
}
|
|
12667
12774
|
try {
|
|
12668
12775
|
const children = await fs20.promises.readdir(logicalRoot, { withFileTypes: true });
|
|
12669
12776
|
children.sort((a, b) => a.name.localeCompare(b.name));
|
|
12670
|
-
for (const child of children) await visit(
|
|
12777
|
+
for (const child of children) await visit(path21.join(logicalRoot, child.name), void 0, true);
|
|
12671
12778
|
} catch {
|
|
12672
|
-
diagnostics.push({ path:
|
|
12779
|
+
diagnostics.push({ path: path21.resolve(root), reason: "unreadable root" });
|
|
12673
12780
|
}
|
|
12674
12781
|
return entries;
|
|
12675
12782
|
}
|
|
12676
12783
|
async function buildBootstrapAudit(opts = {}) {
|
|
12677
12784
|
const project = resolveProjectRoot(opts.project === void 0 ? {} : { project: opts.project });
|
|
12678
|
-
const home =
|
|
12785
|
+
const home = path21.resolve(opts.home ?? os10.homedir());
|
|
12679
12786
|
const context = buildStats(project, home, opts.project === void 0 ? [] : [opts.project]);
|
|
12680
12787
|
const diagnostics = [];
|
|
12681
12788
|
const top = opts.top === void 0 ? 10 : parseBudget("--top", opts.top) ?? 10;
|
|
12682
12789
|
const visitedDirs = /* @__PURE__ */ new Set();
|
|
12683
12790
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
12684
|
-
const agents = await scanMetadataRoot(
|
|
12685
|
-
const skills = await scanMetadataRoot(
|
|
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);
|
|
12686
12793
|
const rankedEntries = [...agents, ...skills].sort((a, b) => b.metadata_bytes - a.metadata_bytes || a.path.localeCompare(b.path));
|
|
12687
12794
|
const largestTotal = rankedEntries.length;
|
|
12688
12795
|
const largest = rankedEntries.slice(0, top);
|
|
@@ -12749,7 +12856,7 @@ async function runBootstrapAudit(opts = {}) {
|
|
|
12749
12856
|
|
|
12750
12857
|
// src/cli_memory.ts
|
|
12751
12858
|
import * as fs21 from "node:fs";
|
|
12752
|
-
import * as
|
|
12859
|
+
import * as path22 from "node:path";
|
|
12753
12860
|
function removeExactDupLines(text, report) {
|
|
12754
12861
|
if (report.exactDupLines.length === 0) return text;
|
|
12755
12862
|
const toRemove = new Set(report.exactDupLines.map(([, dupIdx]) => dupIdx));
|
|
@@ -12807,7 +12914,7 @@ function printReport(reports, clusters) {
|
|
|
12807
12914
|
} else {
|
|
12808
12915
|
for (const cluster of clusters) {
|
|
12809
12916
|
w(
|
|
12810
|
-
` [${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(", ")}
|
|
12811
12918
|
`
|
|
12812
12919
|
);
|
|
12813
12920
|
}
|
|
@@ -12822,7 +12929,7 @@ async function runMemoryCommand(opts = {}) {
|
|
|
12822
12929
|
const claudeMds = findClaudeMdFiles(projectRoot);
|
|
12823
12930
|
const reports = auditClaudeMd(claudeMds);
|
|
12824
12931
|
const memoryMdPath = findMemoryMd(projectRoot);
|
|
12825
|
-
const clusters = memoryMdPath !== null ? await findContentDuplicates(
|
|
12932
|
+
const clusters = memoryMdPath !== null ? await findContentDuplicates(path22.dirname(memoryMdPath)) : [];
|
|
12826
12933
|
printReport(reports, clusters);
|
|
12827
12934
|
if (opts.fix !== true) return;
|
|
12828
12935
|
const changes = [];
|
|
@@ -12868,16 +12975,16 @@ async function runMemoryCommand(opts = {}) {
|
|
|
12868
12975
|
|
|
12869
12976
|
// src/cli_waste.ts
|
|
12870
12977
|
import * as fs24 from "node:fs";
|
|
12871
|
-
import * as
|
|
12978
|
+
import * as path25 from "node:path";
|
|
12872
12979
|
|
|
12873
12980
|
// src/waste.ts
|
|
12874
12981
|
import * as fs22 from "node:fs";
|
|
12875
12982
|
import * as os11 from "node:os";
|
|
12876
|
-
import * as
|
|
12983
|
+
import * as path23 from "node:path";
|
|
12877
12984
|
function projectTranscriptsDir(projectRoot) {
|
|
12878
|
-
const rootStr =
|
|
12985
|
+
const rootStr = path23.resolve(projectRoot);
|
|
12879
12986
|
const slug = rootStr.replace(/[^A-Za-z0-9]/g, "-");
|
|
12880
|
-
return
|
|
12987
|
+
return path23.join(os11.homedir(), ".claude", "projects", slug);
|
|
12881
12988
|
}
|
|
12882
12989
|
function findLatestTranscript(projectRoot) {
|
|
12883
12990
|
const dir = projectTranscriptsDir(projectRoot);
|
|
@@ -12889,7 +12996,7 @@ function findLatestTranscript(projectRoot) {
|
|
|
12889
12996
|
}
|
|
12890
12997
|
let best = null;
|
|
12891
12998
|
for (const name of entries) {
|
|
12892
|
-
const full =
|
|
12999
|
+
const full = path23.join(dir, name);
|
|
12893
13000
|
let stat;
|
|
12894
13001
|
try {
|
|
12895
13002
|
stat = fs22.statSync(full);
|
|
@@ -13119,7 +13226,7 @@ async function buildWasteReport(transcriptPath, opts = {}) {
|
|
|
13119
13226
|
|
|
13120
13227
|
// src/copilot_waste.ts
|
|
13121
13228
|
import * as fs23 from "node:fs";
|
|
13122
|
-
import * as
|
|
13229
|
+
import * as path24 from "node:path";
|
|
13123
13230
|
var HOOK_RECORD_TYPES = /* @__PURE__ */ new Set(["hook.start", "hook.end"]);
|
|
13124
13231
|
function splitInjectedBlocks(transformed) {
|
|
13125
13232
|
const out2 = [];
|
|
@@ -13138,7 +13245,7 @@ function splitInjectedBlocks(transformed) {
|
|
|
13138
13245
|
return out2;
|
|
13139
13246
|
}
|
|
13140
13247
|
function findLatestCopilotSession() {
|
|
13141
|
-
const root =
|
|
13248
|
+
const root = path24.join(copilotCliUserRoot(), "session-state");
|
|
13142
13249
|
let entries;
|
|
13143
13250
|
try {
|
|
13144
13251
|
entries = fs23.readdirSync(root);
|
|
@@ -13148,7 +13255,7 @@ function findLatestCopilotSession() {
|
|
|
13148
13255
|
let best = null;
|
|
13149
13256
|
let bestMtime = -Infinity;
|
|
13150
13257
|
for (const entry of entries) {
|
|
13151
|
-
const candidate =
|
|
13258
|
+
const candidate = path24.join(root, entry, "events.jsonl");
|
|
13152
13259
|
try {
|
|
13153
13260
|
const st = fs23.statSync(candidate);
|
|
13154
13261
|
if (!st.isFile()) continue;
|
|
@@ -13169,7 +13276,7 @@ function buildCopilotWasteReport(eventsPath) {
|
|
|
13169
13276
|
const raw = fs23.readFileSync(eventsPath, "utf-8");
|
|
13170
13277
|
const report = {
|
|
13171
13278
|
sessionPath: eventsPath,
|
|
13172
|
-
sessionId:
|
|
13279
|
+
sessionId: path24.basename(path24.dirname(eventsPath)),
|
|
13173
13280
|
turns: 0,
|
|
13174
13281
|
tokens: null,
|
|
13175
13282
|
blocks: [],
|
|
@@ -13448,7 +13555,7 @@ function printResidentContext(resident, w) {
|
|
|
13448
13555
|
}
|
|
13449
13556
|
async function runWasteCommand(opts = {}) {
|
|
13450
13557
|
if (opts.copilot === true) {
|
|
13451
|
-
const eventsPath = opts.transcript !== void 0 ?
|
|
13558
|
+
const eventsPath = opts.transcript !== void 0 ? path25.resolve(opts.transcript) : findLatestCopilotSession();
|
|
13452
13559
|
if (eventsPath === null || !fs24.existsSync(eventsPath)) {
|
|
13453
13560
|
const detail = eventsPath === null ? "no Copilot CLI session found under <copilot-home>/session-state" : `Copilot session event log not found: ${eventsPath}`;
|
|
13454
13561
|
if (opts.json === true) {
|
|
@@ -13472,7 +13579,7 @@ async function runWasteCommand(opts = {}) {
|
|
|
13472
13579
|
return;
|
|
13473
13580
|
}
|
|
13474
13581
|
const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
13475
|
-
const transcriptPath = opts.transcript !== void 0 ?
|
|
13582
|
+
const transcriptPath = opts.transcript !== void 0 ? path25.resolve(opts.transcript) : findLatestTranscript(projectRoot);
|
|
13476
13583
|
if (transcriptPath === null) {
|
|
13477
13584
|
if (opts.json === true) {
|
|
13478
13585
|
process.stdout.write(`${JSON.stringify({ error: "no session transcript found", project: projectRoot })}
|
|
@@ -13503,7 +13610,7 @@ async function runWasteCommand(opts = {}) {
|
|
|
13503
13610
|
|
|
13504
13611
|
// src/session_read.ts
|
|
13505
13612
|
import * as fs25 from "node:fs";
|
|
13506
|
-
import * as
|
|
13613
|
+
import * as path26 from "node:path";
|
|
13507
13614
|
import * as readline2 from "node:readline";
|
|
13508
13615
|
function resolveSessionTranscript(arg, opts = {}) {
|
|
13509
13616
|
if (arg !== void 0 && arg !== "") {
|
|
@@ -13511,7 +13618,7 @@ function resolveSessionTranscript(arg, opts = {}) {
|
|
|
13511
13618
|
const projectRoot2 = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
13512
13619
|
const dir = projectTranscriptsDir(projectRoot2);
|
|
13513
13620
|
const byId = arg.endsWith(".jsonl") ? arg : `${arg}.jsonl`;
|
|
13514
|
-
const candidate =
|
|
13621
|
+
const candidate = path26.join(dir, byId);
|
|
13515
13622
|
if (fs25.existsSync(candidate) && fs25.statSync(candidate).isFile()) return candidate;
|
|
13516
13623
|
return null;
|
|
13517
13624
|
}
|
|
@@ -13670,7 +13777,7 @@ function formatSessionSlice(turns) {
|
|
|
13670
13777
|
// src/session_audit.ts
|
|
13671
13778
|
import * as fs26 from "node:fs";
|
|
13672
13779
|
import * as os12 from "node:os";
|
|
13673
|
-
import * as
|
|
13780
|
+
import * as path27 from "node:path";
|
|
13674
13781
|
import * as readline3 from "node:readline";
|
|
13675
13782
|
function emptyMeasured() {
|
|
13676
13783
|
return { apiCalls: 0, inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };
|
|
@@ -13717,6 +13824,64 @@ var CACHE_READ_MULTIPLIER = 0.1;
|
|
|
13717
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)/;
|
|
13718
13825
|
var READ_DIVERT_MAX_BYTES = 2500;
|
|
13719
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
|
+
}
|
|
13720
13885
|
var BASH_FILTER_MARKER_RE = /\[token-goat[:\]]/;
|
|
13721
13886
|
var BASH_SMALL_RESULT_MAX_BYTES = 1024;
|
|
13722
13887
|
function commandHead(raw) {
|
|
@@ -13771,7 +13936,7 @@ function listCorpusTranscripts(corpusDir) {
|
|
|
13771
13936
|
const found = [];
|
|
13772
13937
|
const walk = (dir, entries) => {
|
|
13773
13938
|
for (const entry of entries) {
|
|
13774
|
-
const full =
|
|
13939
|
+
const full = path27.join(dir, entry.name);
|
|
13775
13940
|
if (entry.name.endsWith(".jsonl")) {
|
|
13776
13941
|
found.push(full);
|
|
13777
13942
|
} else if (entry.isDirectory()) {
|
|
@@ -13789,9 +13954,9 @@ function listCorpusTranscripts(corpusDir) {
|
|
|
13789
13954
|
return found.sort();
|
|
13790
13955
|
}
|
|
13791
13956
|
function defaultCorpusDir() {
|
|
13792
|
-
return
|
|
13957
|
+
return path27.join(os12.homedir(), ".claude", "projects");
|
|
13793
13958
|
}
|
|
13794
|
-
async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap) {
|
|
13959
|
+
async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows) {
|
|
13795
13960
|
const isLane = filePath.split(/[\\/]/).includes("subagents");
|
|
13796
13961
|
let laneFirstPrefix = null;
|
|
13797
13962
|
let laneBriefBytes = -1;
|
|
@@ -13799,8 +13964,12 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13799
13964
|
const bashHeadById = /* @__PURE__ */ new Map();
|
|
13800
13965
|
let pendingBashReread = [];
|
|
13801
13966
|
const readCallById = /* @__PURE__ */ new Map();
|
|
13967
|
+
const readPathById = /* @__PURE__ */ new Map();
|
|
13968
|
+
const editPathById = /* @__PURE__ */ new Map();
|
|
13969
|
+
const editErrorById = /* @__PURE__ */ new Map();
|
|
13802
13970
|
const readPathEpoch = /* @__PURE__ */ new Map();
|
|
13803
13971
|
let compactEpoch = 0;
|
|
13972
|
+
const openDenies = [];
|
|
13804
13973
|
let sawTokenGoatHook = false;
|
|
13805
13974
|
let fileRepeats = 0;
|
|
13806
13975
|
let fileRepeatsFullNoRange = 0;
|
|
@@ -13895,13 +14064,38 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13895
14064
|
toolNameById.set(block["id"], name);
|
|
13896
14065
|
toolRollup(toolMap, name).calls += 1;
|
|
13897
14066
|
const input = block["input"] !== null && typeof block["input"] === "object" ? block["input"] : {};
|
|
14067
|
+
let readNorm;
|
|
14068
|
+
let editNorm;
|
|
13898
14069
|
if (name === "Bash" && typeof input["command"] === "string") {
|
|
13899
14070
|
bashHeadById.set(block["id"], commandHead(input["command"]));
|
|
13900
14071
|
} else if (name === "Read" && typeof input["file_path"] === "string") {
|
|
13901
|
-
|
|
13902
|
-
const priorEpoch = readPathEpoch.get(
|
|
14072
|
+
readNorm = normalizeReadPath(input["file_path"]);
|
|
14073
|
+
const priorEpoch = readPathEpoch.get(readNorm);
|
|
13903
14074
|
readCallById.set(block["id"], { wasSeenBefore: priorEpoch !== void 0, hasRange: input["offset"] !== void 0 || input["limit"] !== void 0, afterCompaction: priorEpoch !== void 0 && compactEpoch > priorEpoch });
|
|
13904
|
-
|
|
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
|
+
}
|
|
13905
14099
|
}
|
|
13906
14100
|
}
|
|
13907
14101
|
let inputBytes;
|
|
@@ -13930,8 +14124,27 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
13930
14124
|
if (!toolNameById.has(id)) roll.calls += 1;
|
|
13931
14125
|
roll.resultBytes += bytes;
|
|
13932
14126
|
roll.resultEstTokens += estimateTokensFromLength(bytes);
|
|
14127
|
+
if (name === "Edit" && block["is_error"] === true) {
|
|
14128
|
+
s.editErrorBaseline.totalErrors += 1;
|
|
14129
|
+
editErrorById.set(id, true);
|
|
14130
|
+
}
|
|
13933
14131
|
if (name === "Read") {
|
|
13934
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
|
+
}
|
|
13935
14148
|
if (bytes < READ_DIVERT_MAX_BYTES && READ_DIVERT_MARKER_RE.test(text)) {
|
|
13936
14149
|
s.readInterception.divertedByMarker += 1;
|
|
13937
14150
|
s.readInterception.divertedBytes += bytes;
|
|
@@ -14068,6 +14281,42 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
14068
14281
|
}
|
|
14069
14282
|
flushLane(0);
|
|
14070
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
|
+
}
|
|
14071
14320
|
if (sawTokenGoatHook) {
|
|
14072
14321
|
s.readInterception.repeatInHookedSessions += fileRepeats;
|
|
14073
14322
|
s.readInterception.repeatFullNoRangeInHookedSessions += fileRepeatsFullNoRange;
|
|
@@ -14117,8 +14366,47 @@ function hookRollup(hookMap, origin, event) {
|
|
|
14117
14366
|
}
|
|
14118
14367
|
return roll;
|
|
14119
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
|
+
}
|
|
14120
14408
|
async function auditSessionCorpus(opts = {}) {
|
|
14121
|
-
const corpusDir =
|
|
14409
|
+
const corpusDir = path27.resolve(opts.dir ?? defaultCorpusDir());
|
|
14122
14410
|
if (!fs26.existsSync(corpusDir)) {
|
|
14123
14411
|
throw new Error(`session corpus directory not found: ${corpusDir}`);
|
|
14124
14412
|
}
|
|
@@ -14156,6 +14444,8 @@ async function auditSessionCorpus(opts = {}) {
|
|
|
14156
14444
|
laneAgentTypes: [],
|
|
14157
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 },
|
|
14158
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 },
|
|
14159
14449
|
lineTypes: {},
|
|
14160
14450
|
omissionMarkers: { fires: 0, linesOmitted: 0 }
|
|
14161
14451
|
};
|
|
@@ -14164,9 +14454,10 @@ async function auditSessionCorpus(opts = {}) {
|
|
|
14164
14454
|
const hookMap = /* @__PURE__ */ new Map();
|
|
14165
14455
|
const laneObservations = [];
|
|
14166
14456
|
const bashHeadMap = /* @__PURE__ */ new Map();
|
|
14457
|
+
const denyRows = [];
|
|
14167
14458
|
for (const file of files) {
|
|
14168
14459
|
try {
|
|
14169
|
-
await auditOneFile(file, summary, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap);
|
|
14460
|
+
await auditOneFile(file, summary, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows);
|
|
14170
14461
|
summary.filesScanned += 1;
|
|
14171
14462
|
} catch {
|
|
14172
14463
|
summary.filesFailed += 1;
|
|
@@ -14203,6 +14494,8 @@ async function auditSessionCorpus(opts = {}) {
|
|
|
14203
14494
|
}
|
|
14204
14495
|
summary.attachmentKinds = [...attachmentMap.values()].sort((a, b) => b.billedEquivTokens - a.billedEquivTokens || b.injections - a.injections || a.kind.localeCompare(b.kind));
|
|
14205
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;
|
|
14206
14499
|
summary.runtimeMs = Date.now() - started;
|
|
14207
14500
|
return summary;
|
|
14208
14501
|
}
|
|
@@ -14288,6 +14581,15 @@ function formatSessionAudit(s) {
|
|
|
14288
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)}`);
|
|
14289
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)})`);
|
|
14290
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("");
|
|
14291
14593
|
lines.push("## Bash filter fire-rate (token-goat in-band markers inside Bash tool results)");
|
|
14292
14594
|
const bi = s.bashInterception;
|
|
14293
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)`);
|
|
@@ -14306,7 +14608,7 @@ function formatSessionAudit(s) {
|
|
|
14306
14608
|
// src/cli_mcp_audit.ts
|
|
14307
14609
|
import * as fs27 from "node:fs";
|
|
14308
14610
|
import * as os13 from "node:os";
|
|
14309
|
-
import * as
|
|
14611
|
+
import * as path28 from "node:path";
|
|
14310
14612
|
function readMcpJsonFile(configPath2) {
|
|
14311
14613
|
try {
|
|
14312
14614
|
if (!fs27.existsSync(configPath2)) return null;
|
|
@@ -14345,8 +14647,8 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
|
|
|
14345
14647
|
}
|
|
14346
14648
|
}
|
|
14347
14649
|
function discoverMcpConfig(projectRoot, home) {
|
|
14348
|
-
const mcpJsonPath =
|
|
14349
|
-
const claudeJsonPath =
|
|
14650
|
+
const mcpJsonPath = path28.join(projectRoot, ".mcp.json");
|
|
14651
|
+
const claudeJsonPath = path28.join(home, ".claude.json");
|
|
14350
14652
|
const sourcesChecked = [mcpJsonPath, claudeJsonPath];
|
|
14351
14653
|
const fromMcpJson = readMcpJsonFile(mcpJsonPath);
|
|
14352
14654
|
if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
|
|
@@ -14906,7 +15208,7 @@ async function cmdMcpServe() {
|
|
|
14906
15208
|
let StdioServerTransport;
|
|
14907
15209
|
try {
|
|
14908
15210
|
;
|
|
14909
|
-
({ createMcpServer } = await import("./token-goat-chunk-
|
|
15211
|
+
({ createMcpServer } = await import("./token-goat-chunk-CD6U5OD6.mjs"));
|
|
14910
15212
|
({ StdioServerTransport } = await import("./token-goat-chunk-324QOJYZ.mjs"));
|
|
14911
15213
|
} catch (err2) {
|
|
14912
15214
|
process.stderr.write(
|
|
@@ -14919,8 +15221,8 @@ async function cmdMcpServe() {
|
|
|
14919
15221
|
const server = await createMcpServer();
|
|
14920
15222
|
const transport = new StdioServerTransport();
|
|
14921
15223
|
await server.connect(transport);
|
|
14922
|
-
await new Promise((
|
|
14923
|
-
server.onclose =
|
|
15224
|
+
await new Promise((resolve14) => {
|
|
15225
|
+
server.onclose = resolve14;
|
|
14924
15226
|
});
|
|
14925
15227
|
}
|
|
14926
15228
|
function printBridgeVerificationNotice(harness) {
|
|
@@ -14931,7 +15233,7 @@ async function cmdHook(event, opts) {
|
|
|
14931
15233
|
if (typeof opts.harness === "string" && opts.harness.length > 0) {
|
|
14932
15234
|
process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
|
|
14933
15235
|
}
|
|
14934
|
-
const { relay } = await import("./token-goat-chunk-
|
|
15236
|
+
const { relay } = await import("./token-goat-chunk-AUM22UQ2.mjs");
|
|
14935
15237
|
await relay(event);
|
|
14936
15238
|
}
|
|
14937
15239
|
async function cmdInstall(opts) {
|
|
@@ -15044,14 +15346,14 @@ async function cmdInstall(opts) {
|
|
|
15044
15346
|
);
|
|
15045
15347
|
}
|
|
15046
15348
|
try {
|
|
15047
|
-
const skillDir =
|
|
15349
|
+
const skillDir = path29.join(homedir13(), ".claude", "skills");
|
|
15048
15350
|
if (fs28.existsSync(skillDir)) {
|
|
15049
15351
|
const entries = fs28.readdirSync(skillDir, { withFileTypes: true });
|
|
15050
15352
|
const skillNames = [];
|
|
15051
15353
|
const sessionId = getSessionId();
|
|
15052
15354
|
for (const entry of entries) {
|
|
15053
15355
|
if (!entry.isDirectory()) continue;
|
|
15054
|
-
const skillFile =
|
|
15356
|
+
const skillFile = path29.join(skillDir, entry.name, "SKILL.md");
|
|
15055
15357
|
if (fs28.existsSync(skillFile)) {
|
|
15056
15358
|
const body = fs28.readFileSync(skillFile, "utf-8");
|
|
15057
15359
|
const compact = extractCompactFromMarker(body);
|
|
@@ -15064,7 +15366,7 @@ async function cmdInstall(opts) {
|
|
|
15064
15366
|
if (skillNames.length > 0) {
|
|
15065
15367
|
const dir = skillOutputsDir();
|
|
15066
15368
|
await fs28.promises.mkdir(dir, { recursive: true });
|
|
15067
|
-
const pregenPath =
|
|
15369
|
+
const pregenPath = path29.join(dir, "pregen.json");
|
|
15068
15370
|
const pregenData = { ts: Date.now(), names: skillNames };
|
|
15069
15371
|
await fs28.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
|
|
15070
15372
|
out(`Pre-generated ${skillNames.length} skill compacts.`);
|
|
@@ -15265,7 +15567,7 @@ async function cmdSessionOutline(sessionIdOrPath, opts = {}) {
|
|
|
15265
15567
|
${formatSessionOutline(turns)}`;
|
|
15266
15568
|
out(text);
|
|
15267
15569
|
const fullSourceBytes = sessionTranscriptSize(transcriptPath);
|
|
15268
|
-
const bytesSaved =
|
|
15570
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15269
15571
|
recordStat("session_outline", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15270
15572
|
}
|
|
15271
15573
|
function sessionTranscriptSize(transcriptPath) {
|
|
@@ -15287,7 +15589,7 @@ async function cmdSessionSlice(sessionIdOrPath, opts) {
|
|
|
15287
15589
|
const text = opts.json === true ? JSON.stringify({ transcriptPath, turns }) : formatSessionSlice(turns);
|
|
15288
15590
|
out(text);
|
|
15289
15591
|
const fullSourceBytes = sessionTranscriptSize(transcriptPath);
|
|
15290
|
-
const bytesSaved =
|
|
15592
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15291
15593
|
recordStat("session_slice", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15292
15594
|
}
|
|
15293
15595
|
function cmdMcpAudit(opts = {}) {
|
|
@@ -15328,7 +15630,7 @@ function cmdHintStats(opts = {}) {
|
|
|
15328
15630
|
});
|
|
15329
15631
|
}
|
|
15330
15632
|
function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTag = UNTRUSTED_WEB_TAG) {
|
|
15331
|
-
const
|
|
15633
|
+
const emit3 = (text) => {
|
|
15332
15634
|
if (!fenceByProvenance || text === "") {
|
|
15333
15635
|
out(text);
|
|
15334
15636
|
return text;
|
|
@@ -15365,7 +15667,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
|
|
|
15365
15667
|
}
|
|
15366
15668
|
const rawLines = content.split(/\r?\n/);
|
|
15367
15669
|
if (opts.full === true) {
|
|
15368
|
-
return
|
|
15670
|
+
return emit3(rawLines.join("\n"));
|
|
15369
15671
|
}
|
|
15370
15672
|
const lines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
|
|
15371
15673
|
const headN = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 30;
|
|
@@ -15388,7 +15690,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
|
|
|
15388
15690
|
result = lines.slice(Math.max(0, lines.length - tailN));
|
|
15389
15691
|
noteLineCap("last", "tail", result.length, lines.length);
|
|
15390
15692
|
}
|
|
15391
|
-
return
|
|
15693
|
+
return emit3(result.join("\n"));
|
|
15392
15694
|
}
|
|
15393
15695
|
function fenceFileText(text) {
|
|
15394
15696
|
return fenceUntrusted(text, UNTRUSTED_FILE_TAG);
|
|
@@ -15463,7 +15765,7 @@ async function cmdPdfExtract(file, opts) {
|
|
|
15463
15765
|
const text = await runPdfExtractText(file, opts.pages, opts.layout === true);
|
|
15464
15766
|
const printed = _applyFiltersAndPrint(text, opts, true, UNTRUSTED_FILE_TAG);
|
|
15465
15767
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15466
|
-
const bytesSaved =
|
|
15768
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(printed, "utf8"));
|
|
15467
15769
|
recordStat("pdf_extract", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15468
15770
|
}
|
|
15469
15771
|
async function cmdPdfLocate(file, pattern, opts) {
|
|
@@ -15493,7 +15795,7 @@ ${countNoun(matches2.length, "match", "matches")} across ${countNoun(pages.lengt
|
|
|
15493
15795
|
out(printed);
|
|
15494
15796
|
}
|
|
15495
15797
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15496
|
-
const bytesSaved =
|
|
15798
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(printed, "utf8"));
|
|
15497
15799
|
recordStat("pdf_locate", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15498
15800
|
}
|
|
15499
15801
|
async function cmdPdfOutline(file, opts) {
|
|
@@ -15509,7 +15811,7 @@ async function cmdPdfOutline(file, opts) {
|
|
|
15509
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"));
|
|
15510
15812
|
out(text);
|
|
15511
15813
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15512
|
-
const bytesSaved =
|
|
15814
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15513
15815
|
recordStat("pdf_outline", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15514
15816
|
}
|
|
15515
15817
|
async function cmdPdfMeta(file, opts = {}) {
|
|
@@ -15523,7 +15825,7 @@ async function cmdPdfMeta(file, opts = {}) {
|
|
|
15523
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");
|
|
15524
15826
|
out(text);
|
|
15525
15827
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15526
|
-
const bytesSaved =
|
|
15828
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15527
15829
|
recordStat("pdf_meta", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15528
15830
|
}
|
|
15529
15831
|
async function cmdImageMeta(file, opts = {}) {
|
|
@@ -15545,8 +15847,8 @@ ${msg}`;
|
|
|
15545
15847
|
const text = opts.json === true ? JSON.stringify(meta, null, 2) : lines.join("\n");
|
|
15546
15848
|
out(text);
|
|
15547
15849
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15548
|
-
const bytesSaved =
|
|
15549
|
-
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));
|
|
15550
15852
|
}
|
|
15551
15853
|
function fenceOcrText(text) {
|
|
15552
15854
|
if (!injectionFencingEnabled()) return text;
|
|
@@ -15577,8 +15879,8 @@ ${fenceOcrText(result.text)}` : `${lines.join("\n")}
|
|
|
15577
15879
|
}
|
|
15578
15880
|
out(text);
|
|
15579
15881
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15580
|
-
const bytesSaved =
|
|
15581
|
-
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));
|
|
15582
15884
|
}
|
|
15583
15885
|
function cmdVideoChapters(file) {
|
|
15584
15886
|
const { chapters, subtitleStreams } = extractVideoChapters(file);
|
|
@@ -15603,7 +15905,7 @@ function cmdVideoChapters(file) {
|
|
|
15603
15905
|
const text = lines.join("\n");
|
|
15604
15906
|
out(text);
|
|
15605
15907
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15606
|
-
const bytesSaved =
|
|
15908
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(text, "utf8"));
|
|
15607
15909
|
recordStat("video_chapters", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15608
15910
|
}
|
|
15609
15911
|
function formatVideoTimestamp(totalSeconds) {
|
|
@@ -15630,7 +15932,7 @@ function cmdSharepointResolve(url) {
|
|
|
15630
15932
|
}
|
|
15631
15933
|
function recordXlsxStat(kind, file, emitted) {
|
|
15632
15934
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15633
|
-
const bytesSaved =
|
|
15935
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(emitted, "utf8"));
|
|
15634
15936
|
recordStat(kind, bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15635
15937
|
}
|
|
15636
15938
|
async function cmdXlsxSheets(file, opts = {}) {
|
|
@@ -15665,7 +15967,7 @@ async function cmdXlsxQuery(file, opts) {
|
|
|
15665
15967
|
}
|
|
15666
15968
|
function recordDocStat(kind, file, emitted) {
|
|
15667
15969
|
const fullSourceBytes = fileSizeOrZero(file);
|
|
15668
|
-
const bytesSaved =
|
|
15970
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(emitted, "utf8"));
|
|
15669
15971
|
recordStat(kind, bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
15670
15972
|
}
|
|
15671
15973
|
async function cmdPptxOutline(file, opts) {
|
|
@@ -15823,6 +16125,18 @@ function cmdConflicts(targetPath, opts) {
|
|
|
15823
16125
|
async function cmdScreenshot(url, destPath, opts) {
|
|
15824
16126
|
out(await runScreenshot(url, destPath, opts));
|
|
15825
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
|
+
}
|
|
15826
16140
|
function runExit(fn) {
|
|
15827
16141
|
try {
|
|
15828
16142
|
process.exitCode = fn();
|
|
@@ -15853,7 +16167,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
|
|
|
15853
16167
|
}
|
|
15854
16168
|
async function cmdCompress(opts) {
|
|
15855
16169
|
try {
|
|
15856
|
-
const bashRunner = await import("./token-goat-chunk-
|
|
16170
|
+
const bashRunner = await import("./token-goat-chunk-4KD2N46D.mjs");
|
|
15857
16171
|
if (opts.compress === false) {
|
|
15858
16172
|
process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
|
|
15859
16173
|
return;
|
|
@@ -15951,8 +16265,8 @@ async function cmdSkillCompact(name, opts) {
|
|
|
15951
16265
|
}
|
|
15952
16266
|
throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
|
|
15953
16267
|
}
|
|
15954
|
-
cacheName = name ??
|
|
15955
|
-
sourcePath =
|
|
16268
|
+
cacheName = name ?? path29.basename(path29.dirname(path29.resolve(opts.path)));
|
|
16269
|
+
sourcePath = path29.resolve(opts.path);
|
|
15956
16270
|
} else {
|
|
15957
16271
|
if (name === void 0 || !name.trim()) {
|
|
15958
16272
|
throw new CliError("skill-compact requires a <name> or --path <file>");
|
|
@@ -16093,8 +16407,8 @@ async function cmdSkillDiff(name) {
|
|
|
16093
16407
|
}
|
|
16094
16408
|
const newer = versions[0];
|
|
16095
16409
|
const older = versions[1];
|
|
16096
|
-
const newerBody = await fs28.promises.readFile(
|
|
16097
|
-
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);
|
|
16098
16412
|
if (newerBody === null || olderBody === null) {
|
|
16099
16413
|
out(`a cached version of '${name}' was evicted while diffing -- try again`);
|
|
16100
16414
|
return;
|
|
@@ -16146,7 +16460,7 @@ function atomicWriteBuffer(dest, data) {
|
|
|
16146
16460
|
if (e.code !== "ENOENT") throw e;
|
|
16147
16461
|
}
|
|
16148
16462
|
const rnd = randomBytes(4).toString("hex");
|
|
16149
|
-
const tmp =
|
|
16463
|
+
const tmp = path29.join(path29.dirname(path29.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
|
|
16150
16464
|
try {
|
|
16151
16465
|
fs28.writeFileSync(tmp, data, { mode: 384 });
|
|
16152
16466
|
try {
|
|
@@ -16184,9 +16498,9 @@ function mapFsError(e, src, dest, srcLabel = "source") {
|
|
|
16184
16498
|
const fe = e;
|
|
16185
16499
|
if (fe.code === "ENOENT") {
|
|
16186
16500
|
const errPath = fe.path ?? "";
|
|
16187
|
-
const isSource = src !== void 0 &&
|
|
16501
|
+
const isSource = src !== void 0 && path29.resolve(errPath) === path29.resolve(src);
|
|
16188
16502
|
if (isSource) throw new CliError(`${/\bfile$/i.test(srcLabel) ? srcLabel : `${srcLabel} file`} not found: ${src}`);
|
|
16189
|
-
const destDir = dest ?
|
|
16503
|
+
const destDir = dest ? path29.dirname(path29.resolve(dest)) : path29.dirname(path29.resolve(errPath || "."));
|
|
16190
16504
|
throw new CliError(`destination directory does not exist: ${destDir}`);
|
|
16191
16505
|
}
|
|
16192
16506
|
if (fe.code === "ENOTDIR") {
|
|
@@ -16197,7 +16511,7 @@ function mapFsError(e, src, dest, srcLabel = "source") {
|
|
|
16197
16511
|
}
|
|
16198
16512
|
if (fe.code === "EISDIR") {
|
|
16199
16513
|
const errPath = fe.path ?? "";
|
|
16200
|
-
const isSource = src !== void 0 && (errPath === "" ||
|
|
16514
|
+
const isSource = src !== void 0 && (errPath === "" || path29.resolve(errPath) === path29.resolve(src));
|
|
16201
16515
|
if (isSource) throw new CliError(`source is a directory, not a file: ${src}`);
|
|
16202
16516
|
throw new CliError(`destination is a directory, not a file: ${dest ?? (errPath || "(unknown)")}`);
|
|
16203
16517
|
}
|
|
@@ -16266,7 +16580,7 @@ function validateWritablePath(dest, label) {
|
|
|
16266
16580
|
throw new CliError(`${label} path contains a null byte`);
|
|
16267
16581
|
}
|
|
16268
16582
|
if (isWindows()) {
|
|
16269
|
-
const base =
|
|
16583
|
+
const base = path29.basename(dest);
|
|
16270
16584
|
const stem = base.replace(/\.[^.]*$/, "").toUpperCase();
|
|
16271
16585
|
if (WIN_RESERVED.has(stem)) {
|
|
16272
16586
|
throw new CliError(`${label} '${base}' is a reserved Windows device name`);
|
|
@@ -16407,7 +16721,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
16407
16721
|
throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
|
|
16408
16722
|
}
|
|
16409
16723
|
const maxBytes = maxMB * 1024 * 1024;
|
|
16410
|
-
return new Promise((
|
|
16724
|
+
return new Promise((resolve14, reject) => {
|
|
16411
16725
|
const chunks = [];
|
|
16412
16726
|
let totalBytes = 0;
|
|
16413
16727
|
let settled = false;
|
|
@@ -16431,7 +16745,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
16431
16745
|
try {
|
|
16432
16746
|
atomicWriteBuffer(dest, Buffer.concat(chunks));
|
|
16433
16747
|
enqueueDirtyPathSafe(dest);
|
|
16434
|
-
|
|
16748
|
+
resolve14();
|
|
16435
16749
|
} catch (e) {
|
|
16436
16750
|
try {
|
|
16437
16751
|
mapFsError(e, void 0, dest);
|
|
@@ -16783,7 +17097,7 @@ ${content}`;
|
|
|
16783
17097
|
}
|
|
16784
17098
|
const toEmit = fenceUntrusted(emitted, UNTRUSTED_WEB_TAG);
|
|
16785
17099
|
out(toEmit);
|
|
16786
|
-
const bytesSaved =
|
|
17100
|
+
const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(toEmit, "utf8"));
|
|
16787
17101
|
recordStat("gdrive_sections", bytesSaved, savedTokensFromBytes(bytesSaved));
|
|
16788
17102
|
}
|
|
16789
17103
|
function expandGlobs(root, patterns, globFnOverride) {
|
|
@@ -16793,17 +17107,17 @@ function expandGlobs(root, patterns, globFnOverride) {
|
|
|
16793
17107
|
if (globFn !== void 0 && (p.includes("*") || p.includes("?") || p.includes("{"))) {
|
|
16794
17108
|
try {
|
|
16795
17109
|
const hits = globFn(p, { cwd: root });
|
|
16796
|
-
for (const h of hits) out2.push(
|
|
17110
|
+
for (const h of hits) out2.push(path29.isAbsolute(h) ? h : path29.join(root, h));
|
|
16797
17111
|
continue;
|
|
16798
17112
|
} catch {
|
|
16799
17113
|
}
|
|
16800
17114
|
}
|
|
16801
|
-
out2.push(
|
|
17115
|
+
out2.push(path29.isAbsolute(p) ? p : path29.join(root, p));
|
|
16802
17116
|
}
|
|
16803
17117
|
return out2;
|
|
16804
17118
|
}
|
|
16805
17119
|
function readIgnoreFile(root) {
|
|
16806
|
-
const ignorePath =
|
|
17120
|
+
const ignorePath = path29.join(root, ".tokengoatignore");
|
|
16807
17121
|
let raw;
|
|
16808
17122
|
try {
|
|
16809
17123
|
raw = fs28.readFileSync(ignorePath, "utf8");
|
|
@@ -16874,7 +17188,7 @@ function cmdTokens(patterns, opts) {
|
|
|
16874
17188
|
if (opts.tree === true) {
|
|
16875
17189
|
const dirs = /* @__PURE__ */ new Map();
|
|
16876
17190
|
for (const e of entries) {
|
|
16877
|
-
const dir =
|
|
17191
|
+
const dir = path29.dirname(e.rel_path);
|
|
16878
17192
|
if (!dirs.has(dir)) dirs.set(dir, []);
|
|
16879
17193
|
dirs.get(dir).push(e);
|
|
16880
17194
|
}
|
|
@@ -16884,7 +17198,7 @@ function cmdTokens(patterns, opts) {
|
|
|
16884
17198
|
const pct2 = result.total_tokens > 0 ? Math.round(dirTokens / result.total_tokens * 100) : 0;
|
|
16885
17199
|
lines2.push(`${dir}/ (${dirTokens} tokens, ${pct2}%)`);
|
|
16886
17200
|
for (const e of dirEntries) {
|
|
16887
|
-
lines2.push(` ${
|
|
17201
|
+
lines2.push(` ${path29.basename(e.rel_path).padEnd(30)} ${String(e.tokens).padStart(8)} tokens`);
|
|
16888
17202
|
}
|
|
16889
17203
|
}
|
|
16890
17204
|
out(lines2.join("\n"));
|
|
@@ -16935,6 +17249,63 @@ function cmdFailures(src, opts) {
|
|
|
16935
17249
|
saveFailureSnapshot(project.hash, key, { signatures, runner: result.runner, storedAt: Date.now() });
|
|
16936
17250
|
out(opts.json === true ? formatFailureDeltaJson(delta, result.runner) : formatFailureDeltaText(delta, result.runner));
|
|
16937
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
|
+
}
|
|
16938
17309
|
function buildProgram() {
|
|
16939
17310
|
const program2 = new Command();
|
|
16940
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)");
|
|
@@ -17113,6 +17484,15 @@ function buildProgram() {
|
|
|
17113
17484
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
17114
17485
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
17115
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
|
+
);
|
|
17116
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));
|
|
17117
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));
|
|
17118
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));
|
|
@@ -17263,10 +17643,22 @@ function buildProgram() {
|
|
|
17263
17643
|
})
|
|
17264
17644
|
)
|
|
17265
17645
|
);
|
|
17266
|
-
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(
|
|
17267
17647
|
(opts) => runExit(
|
|
17268
17648
|
() => runArch({
|
|
17269
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 } : {},
|
|
17270
17662
|
...opts.json === true ? { json: true } : {}
|
|
17271
17663
|
})
|
|
17272
17664
|
)
|
|
@@ -17491,8 +17883,24 @@ function buildProgram() {
|
|
|
17491
17883
|
out(VERSION);
|
|
17492
17884
|
})
|
|
17493
17885
|
);
|
|
17494
|
-
|
|
17495
|
-
|
|
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();
|
|
17496
17904
|
return program2;
|
|
17497
17905
|
}
|
|
17498
17906
|
function applyExitOverride(command) {
|