token-goat 2.9.17 → 2.9.19
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 +2 -0
- package/dist/{token-goat-chunk-HRUGS6CO.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
- package/dist/{token-goat-chunk-W2GUUQAQ.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
- package/dist/{token-goat-chunk-5WB7GDH4.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
- package/dist/{token-goat-chunk-C26ZBGBC.mjs → token-goat-chunk-7DGTVBWP.mjs} +58 -44
- package/dist/{token-goat-chunk-GPCCYQSS.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
- package/dist/{token-goat-chunk-66224KCZ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
- package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
- package/dist/{token-goat-chunk-YFGEUIEO.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
- package/dist/{token-goat-chunk-CPHVXKNU.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
- package/dist/{token-goat-chunk-73GXYYZR.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
- package/dist/{token-goat-chunk-EKGVCAES.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
- package/dist/{token-goat-chunk-CICJFVIH.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
- package/dist/{token-goat-chunk-THBLW56G.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
- package/dist/{token-goat-chunk-PKNOHVWP.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -350
- package/dist/{token-goat-chunk-RFFUCQK7.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
- package/dist/{token-goat-chunk-RB24DBBS.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
- package/dist/{token-goat-chunk-2M33Q3O4.mjs → token-goat-chunk-HPMMLV37.mjs} +369 -120
- package/dist/{token-goat-chunk-BK3RPPTK.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
- package/dist/{token-goat-chunk-MWSVUKVV.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
- package/dist/{token-goat-chunk-KZPO4DWC.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
- package/dist/{token-goat-chunk-G75WIVFM.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
- package/dist/{token-goat-chunk-2XA5ZW4T.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
- package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
- package/dist/{token-goat-chunk-SWRKVA3J.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
- package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
- package/dist/{token-goat-chunk-CKIJWTRQ.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
- package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
- package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
- package/dist/{token-goat-chunk-IDHWKNKZ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
- package/dist/{token-goat-chunk-26YUO7MP.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
- package/dist/{token-goat-chunk-2VASS5VR.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
- package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
- package/dist/{token-goat-chunk-7VQMOBPQ.mjs → token-goat-chunk-WKO5TVNA.mjs} +948 -618
- package/dist/{token-goat-chunk-UBZCON6N.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
- package/dist/token-goat-hook.mjs +12 -12
- package/dist/token-goat.core.mjs +21 -20
- package/docs/cli.md +4 -3
- package/docs/security.md +1 -1
- package/package.json +1 -1
- package/dist/token-goat-chunk-7EGKXBWT.mjs +0 -173
- package/dist/token-goat-chunk-GLOM5W5C.mjs +0 -93
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
compareSemver
|
|
5
|
+
} from "./token-goat-chunk-GYC34EGE.mjs";
|
|
3
6
|
import {
|
|
4
7
|
ALL_SYMBOLS_IN_FILE_LIMIT,
|
|
5
8
|
AMBIGUOUS_HEADING_LIMIT,
|
|
@@ -20,8 +23,10 @@ import {
|
|
|
20
23
|
isAvailable as isAvailable2,
|
|
21
24
|
load,
|
|
22
25
|
loadError,
|
|
26
|
+
namedSpecsMergeable,
|
|
23
27
|
queryJson,
|
|
24
28
|
rankSimilarNames,
|
|
29
|
+
readSpecsMergeable,
|
|
25
30
|
resolveBody,
|
|
26
31
|
resolveHandoff,
|
|
27
32
|
resolveSymbolMatch,
|
|
@@ -91,24 +96,31 @@ import {
|
|
|
91
96
|
symbolNamesInFile,
|
|
92
97
|
upsertNote,
|
|
93
98
|
warnIfFilesStale
|
|
94
|
-
} from "./token-goat-chunk-
|
|
99
|
+
} from "./token-goat-chunk-PIAESKVU.mjs";
|
|
95
100
|
import {
|
|
96
101
|
DEFAULT_RECONCILE_BUDGET_MS,
|
|
97
102
|
HOOK_EVENTS,
|
|
103
|
+
MCP_TOOL_PATTERN,
|
|
98
104
|
WEB_OUTPUT_SUBDIR,
|
|
99
105
|
buildCommandManifest,
|
|
106
|
+
buildWasteReport,
|
|
100
107
|
checkSymbolBodySize,
|
|
101
108
|
cmdDescribe,
|
|
102
109
|
cmdSessionSchema,
|
|
110
|
+
extractResultText,
|
|
103
111
|
filterCommandManifest,
|
|
112
|
+
findLatestTranscript,
|
|
104
113
|
formatCommandManifest,
|
|
105
114
|
getWebOutput,
|
|
106
115
|
getWebOutputByUrlFromDisk,
|
|
107
116
|
getWebOutputRaw,
|
|
117
|
+
projectTranscriptsDir,
|
|
108
118
|
readStdinJson,
|
|
119
|
+
runRecallCommand,
|
|
109
120
|
runReconcile,
|
|
121
|
+
safeStringify,
|
|
110
122
|
storeWebOutput
|
|
111
|
-
} from "./token-goat-chunk-
|
|
123
|
+
} from "./token-goat-chunk-2GTB5RBZ.mjs";
|
|
112
124
|
import {
|
|
113
125
|
AGENT_SALT_MARKER,
|
|
114
126
|
CONTEXT_AUTOCOMPACT_TOKENS,
|
|
@@ -121,7 +133,6 @@ import {
|
|
|
121
133
|
SKIP_DIRS,
|
|
122
134
|
WORKER_HEARTBEAT_STALE_MS,
|
|
123
135
|
WorkerAlreadyRunningError,
|
|
124
|
-
accumulateResidentLine,
|
|
125
136
|
applyIndexingPriority,
|
|
126
137
|
auditClaudeMd,
|
|
127
138
|
buildExtractiveCompact,
|
|
@@ -133,6 +144,7 @@ import {
|
|
|
133
144
|
buildStats,
|
|
134
145
|
bundledCliPath,
|
|
135
146
|
capJsonRows,
|
|
147
|
+
claudeConfigDir,
|
|
136
148
|
cmdReclaimIndex,
|
|
137
149
|
compactDoc,
|
|
138
150
|
compactPathFor,
|
|
@@ -143,7 +155,6 @@ import {
|
|
|
143
155
|
copilotCliScriptPath,
|
|
144
156
|
copilotCliUserRoot,
|
|
145
157
|
copilotHooksFilePaths,
|
|
146
|
-
createResidentContextStats,
|
|
147
158
|
detectWalkMode,
|
|
148
159
|
dirtyQueuePathFor,
|
|
149
160
|
drainHeartbeatPathFor,
|
|
@@ -163,6 +174,7 @@ import {
|
|
|
163
174
|
findMemoryMd,
|
|
164
175
|
findOrphanedChunkPaths,
|
|
165
176
|
findSystemTempFiles,
|
|
177
|
+
findTopIndexedProjects,
|
|
166
178
|
formatAge,
|
|
167
179
|
formatBytes,
|
|
168
180
|
formatMemSuggestions,
|
|
@@ -189,9 +201,11 @@ import {
|
|
|
189
201
|
isCompactFresh,
|
|
190
202
|
isCopilotCliInstalled,
|
|
191
203
|
isHintCategory,
|
|
204
|
+
isIgnoredIndexPath,
|
|
192
205
|
isManagedServer,
|
|
193
206
|
isPrivateIpv4Octets,
|
|
194
207
|
isPrivateIpv6Groups,
|
|
208
|
+
isResidueServersJson,
|
|
195
209
|
isSuppressionCategory,
|
|
196
210
|
isVisualStudioInstalled,
|
|
197
211
|
isWorkerRunning,
|
|
@@ -208,6 +222,7 @@ import {
|
|
|
208
222
|
pruneOrphanedChunks,
|
|
209
223
|
pruneSkillOutputs,
|
|
210
224
|
pruneSystemTempFiles,
|
|
225
|
+
pruneUnembeddableChunks,
|
|
211
226
|
readCompactBody,
|
|
212
227
|
readCopilotHooksOwners,
|
|
213
228
|
readCopilotMcpTools,
|
|
@@ -219,6 +234,7 @@ import {
|
|
|
219
234
|
runDetachedWorkerDaemon,
|
|
220
235
|
scanAndRecord,
|
|
221
236
|
serversOf,
|
|
237
|
+
sessionsDir,
|
|
222
238
|
setTokenGoatServer,
|
|
223
239
|
shrinkImage,
|
|
224
240
|
skillDescriptionLine,
|
|
@@ -227,7 +243,6 @@ import {
|
|
|
227
243
|
stopWorker,
|
|
228
244
|
storeCompact,
|
|
229
245
|
storeOutput,
|
|
230
|
-
summarizeResidentContext,
|
|
231
246
|
syncVisualStudioProjectGuidance,
|
|
232
247
|
toolMatcherFor,
|
|
233
248
|
trimToBudget,
|
|
@@ -244,59 +259,59 @@ import {
|
|
|
244
259
|
visualStudioUserMcpPath,
|
|
245
260
|
walkProject,
|
|
246
261
|
writeCompact
|
|
247
|
-
} from "./token-goat-chunk-
|
|
262
|
+
} from "./token-goat-chunk-FQYMZASI.mjs";
|
|
248
263
|
import {
|
|
249
264
|
BASH_OUTPUT_SUBDIR,
|
|
250
265
|
CompressedOutput,
|
|
251
266
|
FILTERS,
|
|
252
|
-
RECALL_DEFAULT_LIMIT,
|
|
253
267
|
TOOL_FILTERS,
|
|
254
268
|
ToolFilter,
|
|
255
269
|
clipLongMatchLine,
|
|
256
270
|
combineStreams,
|
|
257
|
-
commandHash,
|
|
258
271
|
deliverCompressed,
|
|
259
272
|
detectFromCommand,
|
|
260
273
|
getBashOutput,
|
|
261
|
-
isRecallCacheType
|
|
262
|
-
|
|
263
|
-
normalizeCommandForCacheKey,
|
|
264
|
-
searchRecall
|
|
265
|
-
} from "./token-goat-chunk-C26ZBGBC.mjs";
|
|
274
|
+
isRecallCacheType
|
|
275
|
+
} from "./token-goat-chunk-7DGTVBWP.mjs";
|
|
266
276
|
import {
|
|
267
277
|
C,
|
|
268
278
|
DEFAULT_MAX_AGE_MS,
|
|
269
279
|
DEFAULT_MAX_COUNT,
|
|
280
|
+
HOOK_STATS_RETENTION_DAYS,
|
|
270
281
|
RESET,
|
|
271
282
|
_useRichStats,
|
|
272
283
|
colorStdout,
|
|
273
284
|
compileCustomPatterns,
|
|
274
285
|
fg,
|
|
275
286
|
formatLocalTimestamp,
|
|
287
|
+
getGlobalDb,
|
|
276
288
|
listBlobs,
|
|
277
289
|
loadBlob,
|
|
278
290
|
pruneBlobs,
|
|
291
|
+
pruneStalePatternCoveredUnmappedTools,
|
|
279
292
|
readUnmappedTools,
|
|
280
293
|
recordStat,
|
|
281
294
|
redactSecrets,
|
|
282
295
|
renderShortStats,
|
|
283
296
|
renderStats,
|
|
284
297
|
savedTokensFromBytes,
|
|
298
|
+
statsHasDurationColumn,
|
|
299
|
+
statsHasHarnessColumn,
|
|
285
300
|
stripAnsi,
|
|
286
301
|
summarize
|
|
287
|
-
} from "./token-goat-chunk-
|
|
302
|
+
} from "./token-goat-chunk-S7RFIX4Y.mjs";
|
|
288
303
|
import {
|
|
289
304
|
docxOutline,
|
|
290
305
|
docxTables,
|
|
291
306
|
docxText,
|
|
292
307
|
formatDocxTables
|
|
293
|
-
} from "./token-goat-chunk-
|
|
308
|
+
} from "./token-goat-chunk-DHIXE6GV.mjs";
|
|
294
309
|
import {
|
|
295
310
|
pptxNotesText,
|
|
296
311
|
pptxOutline,
|
|
297
312
|
pptxSlideText,
|
|
298
313
|
pptxTextGrep
|
|
299
|
-
} from "./token-goat-chunk-
|
|
314
|
+
} from "./token-goat-chunk-3W5P673V.mjs";
|
|
300
315
|
import {
|
|
301
316
|
formatXlsxColumns,
|
|
302
317
|
formatXlsxRange,
|
|
@@ -305,13 +320,13 @@ import {
|
|
|
305
320
|
querySheet,
|
|
306
321
|
rangeSheet,
|
|
307
322
|
xlsxColumns
|
|
308
|
-
} from "./token-goat-chunk-
|
|
323
|
+
} from "./token-goat-chunk-FJZ5MZK7.mjs";
|
|
309
324
|
import {
|
|
310
325
|
formatCsvTable,
|
|
311
326
|
parseWhereSpecs
|
|
312
|
-
} from "./token-goat-chunk-
|
|
327
|
+
} from "./token-goat-chunk-KWM6KBPA.mjs";
|
|
313
328
|
import {
|
|
314
|
-
|
|
329
|
+
deleteFileEmbeddings,
|
|
315
330
|
embeddingBackendLoadError,
|
|
316
331
|
embeddingsDepsAvailable,
|
|
317
332
|
ensureEmbeddingProvenance,
|
|
@@ -329,14 +344,16 @@ import {
|
|
|
329
344
|
loadRegexExtractors,
|
|
330
345
|
missingTreeSitterGrammarPackages,
|
|
331
346
|
modelFilesPresent,
|
|
347
|
+
parserFingerprintForLanguage,
|
|
332
348
|
readSection,
|
|
333
349
|
treeSitterCoreAvailable,
|
|
334
350
|
treeSitterCoreLoadError
|
|
335
|
-
} from "./token-goat-chunk-
|
|
351
|
+
} from "./token-goat-chunk-EP7TCWHM.mjs";
|
|
336
352
|
import {
|
|
337
353
|
getFileEntry,
|
|
354
|
+
getProjectFileEntries,
|
|
338
355
|
querySymbols
|
|
339
|
-
} from "./token-goat-chunk-
|
|
356
|
+
} from "./token-goat-chunk-IBTFGZBX.mjs";
|
|
340
357
|
import {
|
|
341
358
|
TREE_SITTER_LANGUAGES,
|
|
342
359
|
detectLanguage,
|
|
@@ -347,7 +364,7 @@ import {
|
|
|
347
364
|
matchGroovySlashy,
|
|
348
365
|
nonTreeSitterLanguageCount,
|
|
349
366
|
redactIfDotenv
|
|
350
|
-
} from "./token-goat-chunk-
|
|
367
|
+
} from "./token-goat-chunk-JNIFSVWM.mjs";
|
|
351
368
|
import {
|
|
352
369
|
CONFIG_KEY_ENV_OVERRIDES,
|
|
353
370
|
PROJECT_LOCKED_KEYS,
|
|
@@ -376,7 +393,7 @@ import {
|
|
|
376
393
|
stringify,
|
|
377
394
|
validateEnumField,
|
|
378
395
|
validateNumericField
|
|
379
|
-
} from "./token-goat-chunk-
|
|
396
|
+
} from "./token-goat-chunk-SOKAPOOC.mjs";
|
|
380
397
|
import {
|
|
381
398
|
ENV_KEYS,
|
|
382
399
|
LOCK_WAIT_MS_HARDENED,
|
|
@@ -402,6 +419,7 @@ import {
|
|
|
402
419
|
foldPath,
|
|
403
420
|
globalDbPath,
|
|
404
421
|
hookCommandFor,
|
|
422
|
+
hookExecPartsFor,
|
|
405
423
|
installSingleFilePlugin,
|
|
406
424
|
isTestFile,
|
|
407
425
|
isUnderBlockedRoot,
|
|
@@ -414,6 +432,7 @@ import {
|
|
|
414
432
|
recordCreatedConfig,
|
|
415
433
|
redactUrlQuery,
|
|
416
434
|
removeCreatedBackups,
|
|
435
|
+
removeEmptyDirInScope,
|
|
417
436
|
removeFileInScope,
|
|
418
437
|
requireNonNegativeStrictInt,
|
|
419
438
|
requirePositiveStrictInt,
|
|
@@ -436,7 +455,7 @@ import {
|
|
|
436
455
|
withRetryOnLock,
|
|
437
456
|
writeIfDifferent,
|
|
438
457
|
writeJsonSettings
|
|
439
|
-
} from "./token-goat-chunk-
|
|
458
|
+
} from "./token-goat-chunk-ZGQXRYKE.mjs";
|
|
440
459
|
import {
|
|
441
460
|
UNTRUSTED_FILE_TAG,
|
|
442
461
|
UNTRUSTED_TOOL_TAG,
|
|
@@ -450,7 +469,7 @@ import {
|
|
|
450
469
|
normalizePath,
|
|
451
470
|
resolveIndexPath,
|
|
452
471
|
toDisplayPath
|
|
453
|
-
} from "./token-goat-chunk-
|
|
472
|
+
} from "./token-goat-chunk-NMTKNYGF.mjs";
|
|
454
473
|
import {
|
|
455
474
|
compileGuardedRegex
|
|
456
475
|
} from "./token-goat-chunk-GMOUBOX4.mjs";
|
|
@@ -3584,9 +3603,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3584
3603
|
* @param {string} [path]
|
|
3585
3604
|
* @return {(string|null|Command)}
|
|
3586
3605
|
*/
|
|
3587
|
-
executableDir(
|
|
3588
|
-
if (
|
|
3589
|
-
this._executableDir =
|
|
3606
|
+
executableDir(path43) {
|
|
3607
|
+
if (path43 === void 0) return this._executableDir;
|
|
3608
|
+
this._executableDir = path43;
|
|
3590
3609
|
return this;
|
|
3591
3610
|
}
|
|
3592
3611
|
/**
|
|
@@ -3886,9 +3905,8 @@ function attemptedCommandName(argv) {
|
|
|
3886
3905
|
}
|
|
3887
3906
|
|
|
3888
3907
|
// src/cli.ts
|
|
3889
|
-
import * as
|
|
3890
|
-
import * as
|
|
3891
|
-
import { homedir as homedir20 } from "os";
|
|
3908
|
+
import * as fs46 from "fs";
|
|
3909
|
+
import * as path42 from "path";
|
|
3892
3910
|
|
|
3893
3911
|
// src/walk_index.ts
|
|
3894
3912
|
init_define_import_meta_env();
|
|
@@ -3941,8 +3959,8 @@ function collectWalkIndexFiles(root, opts = {}) {
|
|
|
3941
3959
|
|
|
3942
3960
|
// src/install.ts
|
|
3943
3961
|
init_define_import_meta_env();
|
|
3962
|
+
import { spawnSync } from "node:child_process";
|
|
3944
3963
|
import * as fs3 from "node:fs";
|
|
3945
|
-
import * as os2 from "node:os";
|
|
3946
3964
|
import * as path3 from "node:path";
|
|
3947
3965
|
|
|
3948
3966
|
// src/bridges/claudecode.ts
|
|
@@ -3974,15 +3992,18 @@ var SHIM_TRY_IN_PROCESS = `// Attempts the in-process hook call: import()s dist/
|
|
|
3974
3992
|
// loaded) and calls its exported relayInProcess() directly, avoiding a second node
|
|
3975
3993
|
// process spawn entirely. Returns undefined (triggering the spawnSync fallback below)
|
|
3976
3994
|
// when entryPath is absent, the sibling file doesn't exist (an older install predating
|
|
3977
|
-
// this file), or anything else goes wrong -- this must never throw.
|
|
3978
|
-
|
|
3995
|
+
// this file), or anything else goes wrong -- this must never throw. harnessWaitMs, when
|
|
3996
|
+
// the caller has one (Claude Code's async-detach marker time -- see SHIM_ASYNC_DETACH),
|
|
3997
|
+
// is forwarded straight through: this runs in the same process as the caller, so its
|
|
3998
|
+
// performance.now() reading shares the same origin and needs no conversion.
|
|
3999
|
+
async function tryInProcess(entryPath, eventName, input, harnessWaitMs) {
|
|
3979
4000
|
if (!entryPath) return undefined
|
|
3980
4001
|
try {
|
|
3981
4002
|
const hookLibPath = path.join(path.dirname(entryPath), 'token-goat-hook.mjs')
|
|
3982
4003
|
if (!require('node:fs').existsSync(hookLibPath)) return undefined
|
|
3983
4004
|
const mod = await import(pathToFileURL(hookLibPath).href)
|
|
3984
4005
|
const payload = JSON.parse(input)
|
|
3985
|
-
return await mod.relayInProcess(eventName, payload)
|
|
4006
|
+
return await mod.relayInProcess(eventName, payload, harnessWaitMs)
|
|
3986
4007
|
} catch {
|
|
3987
4008
|
return undefined
|
|
3988
4009
|
}
|
|
@@ -3996,6 +4017,52 @@ var SHIM_SPAWN_LADDER = ` const res = entryPath
|
|
|
3996
4017
|
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
3997
4018
|
})`;
|
|
3998
4019
|
var SHIM_MAX_BUFFER_CONST = `const SHIM_MAX_BUFFER_BYTES = 32 * 1024 * 1024`;
|
|
4020
|
+
var SHIM_ASYNC_DETACH = `const ASYNC_DETACH_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit'])
|
|
4021
|
+
const ASYNC_DETACH_SKIP_EXT_RE = /\\.(md|mdx|markdown|rst)$/i
|
|
4022
|
+
const ASYNC_DETACH_BASH_MAX_BYTES = 200
|
|
4023
|
+
// Same key order as OUTPUT_FIRST_TOOL_RESPONSE_KEYS in hooks_common.ts.
|
|
4024
|
+
const ASYNC_DETACH_BASH_TEXT_KEYS = ['output', 'content', 'text', 'body', 'stdout', 'stderr']
|
|
4025
|
+
function bashResultByteLength(resp) {
|
|
4026
|
+
if (typeof resp === 'string') return Buffer.byteLength(resp, 'utf8')
|
|
4027
|
+
if (resp && typeof resp === 'object') {
|
|
4028
|
+
if (typeof resp['persistedOutputSize'] === 'number') return resp['persistedOutputSize']
|
|
4029
|
+
for (const key of ASYNC_DETACH_BASH_TEXT_KEYS) {
|
|
4030
|
+
if (typeof resp[key] === 'string' && resp[key] !== '') return Buffer.byteLength(resp[key], 'utf8')
|
|
4031
|
+
}
|
|
4032
|
+
return Buffer.byteLength(JSON.stringify(resp), 'utf8')
|
|
4033
|
+
}
|
|
4034
|
+
return 0
|
|
4035
|
+
}
|
|
4036
|
+
function isAsyncDetachEligible(eventName, input) {
|
|
4037
|
+
if (eventName === 'subagent_stop') return true
|
|
4038
|
+
if (eventName !== 'post_tool_use') return false
|
|
4039
|
+
try {
|
|
4040
|
+
const payload = JSON.parse(input)
|
|
4041
|
+
const toolName = payload['tool_name']
|
|
4042
|
+
if (ASYNC_DETACH_TOOLS.has(toolName)) {
|
|
4043
|
+
const toolInput = payload['tool_input'] || {}
|
|
4044
|
+
const filePath =
|
|
4045
|
+
typeof toolInput['file_path'] === 'string'
|
|
4046
|
+
? toolInput['file_path']
|
|
4047
|
+
: typeof toolInput['notebook_path'] === 'string'
|
|
4048
|
+
? toolInput['notebook_path']
|
|
4049
|
+
: ''
|
|
4050
|
+
return !ASYNC_DETACH_SKIP_EXT_RE.test(filePath)
|
|
4051
|
+
}
|
|
4052
|
+
if (toolName === 'Bash') return bashResultByteLength(payload['tool_response']) < ASYNC_DETACH_BASH_MAX_BYTES
|
|
4053
|
+
return false
|
|
4054
|
+
} catch {
|
|
4055
|
+
return false
|
|
4056
|
+
}
|
|
4057
|
+
}`;
|
|
4058
|
+
var SHIM_OWN_COMMAND_BYPASS = `const TG_CD_PREFIX_RE = /^(?:cd\\s+(?:"[^"]*"|'[^']*'|\\S+)[ \\t]*(?:&&|;|\\r?\\n)\\s*)+/
|
|
4059
|
+
const TG_OWN_COMMAND_RE = /^(?:[A-Za-z_][A-Za-z0-9_]*=\\S+\\s+)*(?:token-goat\\b|node(?:\\.exe)?\\s+["']?(?:\\S*[\\\\/])?token-goat(?:\\.mjs)?["']?\\b)/i
|
|
4060
|
+
function isOwnTokenGoatCommand(cmd) {
|
|
4061
|
+
if (typeof cmd !== 'string' || cmd.trim() === '') return false
|
|
4062
|
+
const body = (cmd.replace(TG_CD_PREFIX_RE, '') || cmd).trim()
|
|
4063
|
+
if (/[;&|\`]|\\$\\(/.test(body)) return false
|
|
4064
|
+
return TG_OWN_COMMAND_RE.test(body)
|
|
4065
|
+
}`;
|
|
3999
4066
|
|
|
4000
4067
|
// src/bridges/claudecode.ts
|
|
4001
4068
|
var CLAUDECODE_HOOK_SCRIPT = `#!/usr/bin/env node
|
|
@@ -4025,6 +4092,10 @@ ${SHIM_TRY_IN_PROCESS}
|
|
|
4025
4092
|
|
|
4026
4093
|
${SHIM_MAX_BUFFER_CONST}
|
|
4027
4094
|
|
|
4095
|
+
${SHIM_ASYNC_DETACH}
|
|
4096
|
+
|
|
4097
|
+
${SHIM_OWN_COMMAND_BYPASS}
|
|
4098
|
+
|
|
4028
4099
|
async function main() {
|
|
4029
4100
|
const eventName = process.argv[2] || ''
|
|
4030
4101
|
if (!VALID_HOOK_EVENTS.has(eventName)) {
|
|
@@ -4038,6 +4109,32 @@ async function main() {
|
|
|
4038
4109
|
process.stdout.write('{}')
|
|
4039
4110
|
return
|
|
4040
4111
|
}
|
|
4112
|
+
// A pre_tool_use Bash call that is token-goat's own CLI has nothing for the pipeline below to
|
|
4113
|
+
// say, so this skips the round trip entirely rather than backgrounding it -- see
|
|
4114
|
+
// isOwnTokenGoatCommand's docstring in shim_common.ts for the measured emission rate this
|
|
4115
|
+
// forfeits.
|
|
4116
|
+
if (eventName === 'pre_tool_use') {
|
|
4117
|
+
try {
|
|
4118
|
+
const payload = JSON.parse(input)
|
|
4119
|
+
if (payload['tool_name'] === 'Bash' && isOwnTokenGoatCommand(payload['tool_input'] && payload['tool_input']['command'])) {
|
|
4120
|
+
process.stdout.write('{}')
|
|
4121
|
+
return
|
|
4122
|
+
}
|
|
4123
|
+
} catch {
|
|
4124
|
+
// Fall through to normal handling on unparseable stdin.
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
// Print this before the in-process/spawn round trip below, not after: the harness detaches on
|
|
4128
|
+
// the first stdout line it sees, and the whole point is not waiting out that round trip for a
|
|
4129
|
+
// call that was always going to answer '{}'. asyncDetachAtMs, taken at this exact print, is the
|
|
4130
|
+
// last instant this process's elapsed time and the harness's own wait are the same number --
|
|
4131
|
+
// record it now so the finally block in relayInProcess (relay.ts) can report what Claude Code
|
|
4132
|
+
// actually waited on instead of this call's full lifetime.
|
|
4133
|
+
let asyncDetachAtMs
|
|
4134
|
+
if (isAsyncDetachEligible(eventName, input)) {
|
|
4135
|
+
asyncDetachAtMs = performance.now()
|
|
4136
|
+
process.stdout.write('{"async":true}\\n')
|
|
4137
|
+
}
|
|
4041
4138
|
// process.argv[3], when present, is the absolute path to the running token-goat CLI
|
|
4042
4139
|
// entry (mirrors the same argv[3]-baked-entry convention used by the Codex/Copilot
|
|
4043
4140
|
// CLI shims). Try the in-process hook lib first (tryInProcess above), which avoids
|
|
@@ -4048,8 +4145,15 @@ async function main() {
|
|
|
4048
4145
|
// timeout/killSignal on both spawnSync fallbacks means token-goat degrades to its own
|
|
4049
4146
|
// fail-open '{}' rather than being force-killed by Claude Code's own hook timeout.
|
|
4050
4147
|
const entryPath = process.argv[3]
|
|
4051
|
-
let stdout = await tryInProcess(entryPath, eventName, input)
|
|
4148
|
+
let stdout = await tryInProcess(entryPath, eventName, input, asyncDetachAtMs)
|
|
4052
4149
|
if (stdout === undefined) {
|
|
4150
|
+
// Reaching here with asyncDetachAtMs set means tryInProcess failed after
|
|
4151
|
+
// isAsyncDetachEligible had already parsed this same input successfully, so the cause is an
|
|
4152
|
+
// install missing its dist/token-goat-hook.mjs sibling or a broken import -- never a bad
|
|
4153
|
+
// payload. asyncDetachAtMs is this process's own elapsed time and has no meaning in the
|
|
4154
|
+
// spawned child's clock, so it is deliberately not threaded across that boundary; the child
|
|
4155
|
+
// falls back to timing its own lifetime, the same already-approximate number every hook got
|
|
4156
|
+
// on this fallback path before async-detach existed.
|
|
4053
4157
|
${SHIM_SPAWN_LADDER}
|
|
4054
4158
|
: spawnSync('token-goat hook ' + eventName, {
|
|
4055
4159
|
input,
|
|
@@ -4091,18 +4195,56 @@ function anchoredMarkerPattern(marker) {
|
|
|
4091
4195
|
return new RegExp(`(?<![a-zA-Z0-9_-])${escaped}(?![a-zA-Z0-9_-])`);
|
|
4092
4196
|
}
|
|
4093
4197
|
var HOOK_MARKER_PATTERNS = [SHIM_COMMAND_MARKER, COMMAND_MARKER, ...LEGACY_COMMAND_MARKERS].map(anchoredMarkerPattern);
|
|
4094
|
-
function isTokenGoatHookCommand(command) {
|
|
4095
|
-
|
|
4198
|
+
function isTokenGoatHookCommand(command, args) {
|
|
4199
|
+
const haystack = args !== void 0 && args.length > 0 ? `${command} ${args.join(" ")}` : command;
|
|
4200
|
+
return HOOK_MARKER_PATTERNS.some((pattern) => pattern.test(haystack));
|
|
4201
|
+
}
|
|
4202
|
+
var CLAUDE_EXEC_FORM_MIN_VERSION = "2.1.139";
|
|
4203
|
+
var _claudeExecFormHooksSupported;
|
|
4204
|
+
function claudeExecFormHooksSupported() {
|
|
4205
|
+
const override = process.env["TOKEN_GOAT_CLAUDE_EXEC_FORM_HOOKS"];
|
|
4206
|
+
if (override === "1") return true;
|
|
4207
|
+
if (override === "0") return false;
|
|
4208
|
+
if (_claudeExecFormHooksSupported !== void 0) return _claudeExecFormHooksSupported;
|
|
4209
|
+
_claudeExecFormHooksSupported = probeClaudeExecFormHooksSupported();
|
|
4210
|
+
return _claudeExecFormHooksSupported;
|
|
4211
|
+
}
|
|
4212
|
+
function probeClaudeExecFormHooksSupported() {
|
|
4213
|
+
const resolved = resolveOnPath("claude");
|
|
4214
|
+
if (resolved === null) return false;
|
|
4215
|
+
try {
|
|
4216
|
+
const isBatch = /\.(?:cmd|bat)$/i.test(resolved);
|
|
4217
|
+
const comspec = path3.join(process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows", "System32", "cmd.exe");
|
|
4218
|
+
const result = isBatch ? spawnSync(fs3.existsSync(comspec) ? comspec : "cmd.exe", ["/d", "/s", "/c", resolved, "--version"], { encoding: "utf-8", timeout: 5e3, windowsHide: true }) : spawnSync(resolved, ["--version"], { encoding: "utf-8", timeout: 5e3, windowsHide: true });
|
|
4219
|
+
if (result.status !== 0) return false;
|
|
4220
|
+
return execFormSupportedForVersionOutput(result.stdout ?? "");
|
|
4221
|
+
} catch {
|
|
4222
|
+
return false;
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
function execFormSupportedForVersionOutput(stdout) {
|
|
4226
|
+
const m = /(\d+\.\d+\.\d+)/.exec(stdout);
|
|
4227
|
+
if (!m) return false;
|
|
4228
|
+
return compareSemver(m[1], CLAUDE_EXEC_FORM_MIN_VERSION) >= 0;
|
|
4229
|
+
}
|
|
4230
|
+
function expectedHookEntryFor(scriptPath, event) {
|
|
4231
|
+
return claudeExecFormHooksSupported() ? hookExecPartsFor(scriptPath, event) : { command: hookCommandFor(scriptPath, event) };
|
|
4232
|
+
}
|
|
4233
|
+
function hookEntryMatches(command, args, expected) {
|
|
4234
|
+
if (command !== expected.command) return false;
|
|
4235
|
+
const a = args ?? [];
|
|
4236
|
+
const b = expected.args ?? [];
|
|
4237
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
4096
4238
|
}
|
|
4097
4239
|
function claudeHookScriptPath() {
|
|
4098
|
-
return path3.join(
|
|
4240
|
+
return path3.join(claudeConfigDir(), "hooks", "token-goat-shim.js");
|
|
4099
4241
|
}
|
|
4100
4242
|
function anyScopeReferencesShim(scriptPath, currentScope, alreadyStripped) {
|
|
4101
4243
|
const referencesIn = (map) => {
|
|
4102
4244
|
for (const groups of Object.values(map ?? {})) {
|
|
4103
4245
|
for (const group of groups) {
|
|
4104
4246
|
for (const h of group.hooks ?? []) {
|
|
4105
|
-
if (h.command.includes(scriptPath)) return true;
|
|
4247
|
+
if (h.command.includes(scriptPath) || (h.args ?? []).some((a) => a.includes(scriptPath))) return true;
|
|
4106
4248
|
}
|
|
4107
4249
|
}
|
|
4108
4250
|
}
|
|
@@ -4116,8 +4258,7 @@ function anyScopeReferencesShim(scriptPath, currentScope, alreadyStripped) {
|
|
|
4116
4258
|
return false;
|
|
4117
4259
|
}
|
|
4118
4260
|
function settingsPath(scope) {
|
|
4119
|
-
const
|
|
4120
|
-
const base = path3.join(root, ".claude");
|
|
4261
|
+
const base = scope === "user" ? claudeConfigDir() : path3.join(normalizeDarwinSystemAlias(process.cwd()), ".claude");
|
|
4121
4262
|
return path3.join(base, "settings.json");
|
|
4122
4263
|
}
|
|
4123
4264
|
var SettingsParseError = class extends Error {
|
|
@@ -4154,7 +4295,7 @@ function groupHasTokenGoat(groups, predicate) {
|
|
|
4154
4295
|
if (groups === void 0) return false;
|
|
4155
4296
|
for (const group of groups) {
|
|
4156
4297
|
for (const h of group.hooks ?? []) {
|
|
4157
|
-
if (predicate(h.command)) return true;
|
|
4298
|
+
if (predicate(h.command, h.args)) return true;
|
|
4158
4299
|
}
|
|
4159
4300
|
}
|
|
4160
4301
|
return false;
|
|
@@ -4176,13 +4317,13 @@ function installHooksScoped(scope) {
|
|
|
4176
4317
|
});
|
|
4177
4318
|
let settingsChanged = false;
|
|
4178
4319
|
for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
|
|
4179
|
-
const
|
|
4320
|
+
const expected = expectedHookEntryFor(scriptPath, eventArg);
|
|
4180
4321
|
const existingGroups = hooks[eventKey] ?? [];
|
|
4181
4322
|
const groups = [];
|
|
4182
4323
|
let strippedStale = false;
|
|
4183
4324
|
for (const group of existingGroups) {
|
|
4184
4325
|
const keptHooks = (group.hooks ?? []).filter((h) => {
|
|
4185
|
-
const isStale = isTokenGoatHookCommand(h.command) && h.command
|
|
4326
|
+
const isStale = isTokenGoatHookCommand(h.command, h.args) && !hookEntryMatches(h.command, h.args, expected);
|
|
4186
4327
|
if (isStale) strippedStale = true;
|
|
4187
4328
|
return !isStale;
|
|
4188
4329
|
});
|
|
@@ -4192,7 +4333,7 @@ function installHooksScoped(scope) {
|
|
|
4192
4333
|
groups.push(group);
|
|
4193
4334
|
}
|
|
4194
4335
|
}
|
|
4195
|
-
const isOurs = (command) => command
|
|
4336
|
+
const isOurs = (command, args) => hookEntryMatches(command, args, expected);
|
|
4196
4337
|
if (groupHasTokenGoat(groups, isOurs)) {
|
|
4197
4338
|
const narrowed = toolMatcherFor(eventArg);
|
|
4198
4339
|
let renarrowed = false;
|
|
@@ -4201,7 +4342,7 @@ function installHooksScoped(scope) {
|
|
|
4201
4342
|
const group = groups[i];
|
|
4202
4343
|
if (group === void 0) continue;
|
|
4203
4344
|
const ownHooks = group.hooks ?? [];
|
|
4204
|
-
const isOwnGroup = ownHooks.length > 0 && ownHooks.every((h) => isOurs(h.command));
|
|
4345
|
+
const isOwnGroup = ownHooks.length > 0 && ownHooks.every((h) => isOurs(h.command, h.args));
|
|
4205
4346
|
if (isOwnGroup && group.matcher !== narrowed) {
|
|
4206
4347
|
groups[i] = { ...group, matcher: narrowed };
|
|
4207
4348
|
renarrowed = true;
|
|
@@ -4215,7 +4356,7 @@ function installHooksScoped(scope) {
|
|
|
4215
4356
|
continue;
|
|
4216
4357
|
}
|
|
4217
4358
|
const matcher = toolMatcherFor(eventArg) ?? "";
|
|
4218
|
-
groups.push({ matcher, hooks: [{ type: "command", command:
|
|
4359
|
+
groups.push({ matcher, hooks: [{ type: "command", command: expected.command, ...expected.args !== void 0 ? { args: expected.args } : {} }] });
|
|
4219
4360
|
hooks[eventKey] = groups;
|
|
4220
4361
|
settingsChanged = true;
|
|
4221
4362
|
}
|
|
@@ -4259,15 +4400,15 @@ function isInstalled(scope = "user") {
|
|
|
4259
4400
|
const scriptPath = claudeHookScriptPath();
|
|
4260
4401
|
if (!fs3.existsSync(scriptPath)) return false;
|
|
4261
4402
|
for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
|
|
4262
|
-
const
|
|
4263
|
-
if (!groupHasTokenGoat(hooks[eventKey], (c) => c
|
|
4403
|
+
const expected = expectedHookEntryFor(scriptPath, eventArg);
|
|
4404
|
+
if (!groupHasTokenGoat(hooks[eventKey], (c, a) => hookEntryMatches(c, a, expected))) return false;
|
|
4264
4405
|
}
|
|
4265
4406
|
return true;
|
|
4266
4407
|
}
|
|
4267
4408
|
var CLAUDE_MD_BEGIN = "<!-- token-goat-begin -->";
|
|
4268
4409
|
var CLAUDE_MD_END = "<!-- token-goat-end -->";
|
|
4269
4410
|
function claudeMdPath() {
|
|
4270
|
-
return path3.join(
|
|
4411
|
+
return path3.join(claudeConfigDir(), "CLAUDE.md");
|
|
4271
4412
|
}
|
|
4272
4413
|
function buildClaudeMdBlock() {
|
|
4273
4414
|
return buildGuidanceBlock({
|
|
@@ -4292,7 +4433,7 @@ function uninstallClaudeMd() {
|
|
|
4292
4433
|
return stripClaudeMdBlock(claudeMdPath());
|
|
4293
4434
|
}
|
|
4294
4435
|
function findStrayClaudeMdBlocks(searchRoot) {
|
|
4295
|
-
const root = searchRoot ??
|
|
4436
|
+
const root = searchRoot ?? claudeConfigDir();
|
|
4296
4437
|
const canonical = path3.resolve(claudeMdPath());
|
|
4297
4438
|
const found = [];
|
|
4298
4439
|
if (!fs3.existsSync(root)) return found;
|
|
@@ -4354,7 +4495,7 @@ ${buildGuidanceBody("Claude Code's own Read, Grep, and Glob preference rules", {
|
|
|
4354
4495
|
`;
|
|
4355
4496
|
}
|
|
4356
4497
|
function skillDir() {
|
|
4357
|
-
return path3.join(
|
|
4498
|
+
return path3.join(claudeConfigDir(), "skills", "token-goat");
|
|
4358
4499
|
}
|
|
4359
4500
|
function skillPath() {
|
|
4360
4501
|
return path3.join(skillDir(), "SKILL.md");
|
|
@@ -4386,7 +4527,7 @@ function uninstallSkill() {
|
|
|
4386
4527
|
init_define_import_meta_env();
|
|
4387
4528
|
import * as crypto from "node:crypto";
|
|
4388
4529
|
import * as fs4 from "node:fs";
|
|
4389
|
-
import * as
|
|
4530
|
+
import * as os2 from "node:os";
|
|
4390
4531
|
import * as path4 from "node:path";
|
|
4391
4532
|
|
|
4392
4533
|
// src/bridges/codex.ts
|
|
@@ -4541,13 +4682,13 @@ var CODEX_GLOBAL_EVENT_ARG = {
|
|
|
4541
4682
|
var CodexConfigParseError = class extends Error {
|
|
4542
4683
|
};
|
|
4543
4684
|
function codexConfigPath() {
|
|
4544
|
-
return path4.join(
|
|
4685
|
+
return path4.join(os2.homedir(), ".codex", "config.toml");
|
|
4545
4686
|
}
|
|
4546
4687
|
function codexAgentsPath() {
|
|
4547
|
-
return path4.join(
|
|
4688
|
+
return path4.join(os2.homedir(), ".codex", "AGENTS.md");
|
|
4548
4689
|
}
|
|
4549
4690
|
function codexHookScriptPath() {
|
|
4550
|
-
return path4.join(
|
|
4691
|
+
return path4.join(os2.homedir(), ".codex", "hooks", "token-goat-shim.js");
|
|
4551
4692
|
}
|
|
4552
4693
|
function readCodexConfig(p, opts = {}) {
|
|
4553
4694
|
let raw;
|
|
@@ -4823,7 +4964,7 @@ function stripAgentsBlock(p) {
|
|
|
4823
4964
|
// src/bridges/gemini_install.ts
|
|
4824
4965
|
init_define_import_meta_env();
|
|
4825
4966
|
import * as fs5 from "node:fs";
|
|
4826
|
-
import * as
|
|
4967
|
+
import * as os3 from "node:os";
|
|
4827
4968
|
import * as path5 from "node:path";
|
|
4828
4969
|
var GEMINI_LEGACY_COMMAND_MARKER = "token-goat hook";
|
|
4829
4970
|
var GEMINI_HOOK_EVENTS = ["BeforeTool", "AfterTool", "PreCompress"];
|
|
@@ -4845,7 +4986,7 @@ function registeredInternalTools(eventName) {
|
|
|
4845
4986
|
var GeminiSettingsParseError = class extends Error {
|
|
4846
4987
|
};
|
|
4847
4988
|
function geminiSettingsPath() {
|
|
4848
|
-
return path5.join(
|
|
4989
|
+
return path5.join(os3.homedir(), ".gemini", "settings.json");
|
|
4849
4990
|
}
|
|
4850
4991
|
function readGeminiSettings(p, opts = {}) {
|
|
4851
4992
|
let raw;
|
|
@@ -4976,7 +5117,7 @@ function isGeminiInstalled() {
|
|
|
4976
5117
|
// src/bridges/qwen_install.ts
|
|
4977
5118
|
init_define_import_meta_env();
|
|
4978
5119
|
import * as fs6 from "node:fs";
|
|
4979
|
-
import * as
|
|
5120
|
+
import * as os4 from "node:os";
|
|
4980
5121
|
import * as path6 from "node:path";
|
|
4981
5122
|
var QWEN_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "PreCompact", "UserPromptSubmit", "SubagentStop"];
|
|
4982
5123
|
var QWEN_EVENT_ARG = {
|
|
@@ -4989,7 +5130,7 @@ var QWEN_EVENT_ARG = {
|
|
|
4989
5130
|
var QwenSettingsParseError = class extends Error {
|
|
4990
5131
|
};
|
|
4991
5132
|
function qwenSettingsPath() {
|
|
4992
|
-
return path6.join(
|
|
5133
|
+
return path6.join(os4.homedir(), ".qwen", "settings.json");
|
|
4993
5134
|
}
|
|
4994
5135
|
function readQwenSettings(p, opts = {}) {
|
|
4995
5136
|
let raw;
|
|
@@ -5093,7 +5234,7 @@ function isQwenInstalled() {
|
|
|
5093
5234
|
// src/bridges/kimi_install.ts
|
|
5094
5235
|
init_define_import_meta_env();
|
|
5095
5236
|
import * as fs7 from "node:fs";
|
|
5096
|
-
import * as
|
|
5237
|
+
import * as os5 from "node:os";
|
|
5097
5238
|
import * as path7 from "node:path";
|
|
5098
5239
|
|
|
5099
5240
|
// src/bridges/kimi.ts
|
|
@@ -5202,7 +5343,7 @@ var KimiConfigParseError = class extends Error {
|
|
|
5202
5343
|
function kimiHome() {
|
|
5203
5344
|
const override = process.env["KIMI_CODE_HOME"];
|
|
5204
5345
|
if (override !== void 0 && override.trim() !== "") return path7.resolve(override);
|
|
5205
|
-
return path7.join(
|
|
5346
|
+
return path7.join(os5.homedir(), ".kimi-code");
|
|
5206
5347
|
}
|
|
5207
5348
|
function kimiConfigPath() {
|
|
5208
5349
|
return path7.join(kimiHome(), "config.toml");
|
|
@@ -5356,7 +5497,7 @@ function isKimiInstalled() {
|
|
|
5356
5497
|
// src/bridges/pi_install.ts
|
|
5357
5498
|
init_define_import_meta_env();
|
|
5358
5499
|
import * as fs8 from "node:fs";
|
|
5359
|
-
import * as
|
|
5500
|
+
import * as os6 from "node:os";
|
|
5360
5501
|
import * as path8 from "node:path";
|
|
5361
5502
|
|
|
5362
5503
|
// src/bridges/pi.ts
|
|
@@ -5722,7 +5863,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5722
5863
|
|
|
5723
5864
|
// src/bridges/pi_install.ts
|
|
5724
5865
|
function piGlobalExtensionPath() {
|
|
5725
|
-
return path8.join(
|
|
5866
|
+
return path8.join(os6.homedir(), ".pi", "agent", "extensions", "token-goat.ts");
|
|
5726
5867
|
}
|
|
5727
5868
|
function piLocalExtensionPath() {
|
|
5728
5869
|
return path8.join(process.cwd(), ".pi", "extensions", "token-goat.ts");
|
|
@@ -5758,7 +5899,7 @@ function isPiInstalled(opts = {}) {
|
|
|
5758
5899
|
// src/bridges/opencode_install.ts
|
|
5759
5900
|
init_define_import_meta_env();
|
|
5760
5901
|
import * as fs9 from "node:fs";
|
|
5761
|
-
import * as
|
|
5902
|
+
import * as os7 from "node:os";
|
|
5762
5903
|
import * as path9 from "node:path";
|
|
5763
5904
|
|
|
5764
5905
|
// src/bridges/opencode.ts
|
|
@@ -6032,11 +6173,11 @@ function opencodeGlobalConfigDir() {
|
|
|
6032
6173
|
if (process.platform === "win32") {
|
|
6033
6174
|
const appData = process.env["APPDATA"];
|
|
6034
6175
|
if (appData !== void 0 && appData.trim() !== "") return appData;
|
|
6035
|
-
return path9.join(
|
|
6176
|
+
return path9.join(os7.homedir(), "AppData", "Roaming");
|
|
6036
6177
|
}
|
|
6037
6178
|
const xdgConfigHome = process.env["XDG_CONFIG_HOME"];
|
|
6038
6179
|
if (xdgConfigHome !== void 0 && xdgConfigHome.trim() !== "") return xdgConfigHome;
|
|
6039
|
-
return path9.join(
|
|
6180
|
+
return path9.join(os7.homedir(), ".config");
|
|
6040
6181
|
}
|
|
6041
6182
|
function opencodePluginPath() {
|
|
6042
6183
|
return path9.join(opencodeGlobalConfigDir(), "opencode", "plugins", "token-goat.ts");
|
|
@@ -6059,7 +6200,7 @@ function isOpencodeInstalled() {
|
|
|
6059
6200
|
// src/bridges/openclaw_install.ts
|
|
6060
6201
|
init_define_import_meta_env();
|
|
6061
6202
|
import * as fs10 from "node:fs";
|
|
6062
|
-
import * as
|
|
6203
|
+
import * as os8 from "node:os";
|
|
6063
6204
|
import * as path10 from "node:path";
|
|
6064
6205
|
|
|
6065
6206
|
// src/bridges/openclaw.ts
|
|
@@ -6221,7 +6362,7 @@ var OpenclawConfigParseError = class extends Error {
|
|
|
6221
6362
|
};
|
|
6222
6363
|
var OPENCLAW_PLUGIN_ID = "token-goat";
|
|
6223
6364
|
function openclawHomeDir() {
|
|
6224
|
-
return path10.join(
|
|
6365
|
+
return path10.join(os8.homedir(), ".openclaw");
|
|
6225
6366
|
}
|
|
6226
6367
|
function openclawConfigPath() {
|
|
6227
6368
|
return path10.join(openclawHomeDir(), "openclaw.json");
|
|
@@ -6373,7 +6514,7 @@ function isOpenclawInstalled() {
|
|
|
6373
6514
|
// src/bridges/grok_install.ts
|
|
6374
6515
|
init_define_import_meta_env();
|
|
6375
6516
|
import * as fs11 from "node:fs";
|
|
6376
|
-
import * as
|
|
6517
|
+
import * as os9 from "node:os";
|
|
6377
6518
|
import * as path11 from "node:path";
|
|
6378
6519
|
|
|
6379
6520
|
// src/bridges/grok.ts
|
|
@@ -6485,7 +6626,7 @@ var GROK_EVENT_ARG = {
|
|
|
6485
6626
|
SubagentStop: "subagent_stop"
|
|
6486
6627
|
};
|
|
6487
6628
|
function grokHooksDir() {
|
|
6488
|
-
return path11.join(
|
|
6629
|
+
return path11.join(os9.homedir(), ".grok", "hooks");
|
|
6489
6630
|
}
|
|
6490
6631
|
function grokConfigPath() {
|
|
6491
6632
|
return path11.join(grokHooksDir(), "token-goat.json");
|
|
@@ -6535,7 +6676,7 @@ function isGrokInstalled() {
|
|
|
6535
6676
|
// src/bridges/vscode_install.ts
|
|
6536
6677
|
init_define_import_meta_env();
|
|
6537
6678
|
import * as fs12 from "node:fs";
|
|
6538
|
-
import * as
|
|
6679
|
+
import * as os10 from "node:os";
|
|
6539
6680
|
import * as path12 from "node:path";
|
|
6540
6681
|
var VSCODE_GUIDANCE_BEGIN = "<!-- token-goat-vscode-begin -->";
|
|
6541
6682
|
var VSCODE_GUIDANCE_END = "<!-- token-goat-vscode-end -->";
|
|
@@ -6544,13 +6685,13 @@ var END = VSCODE_GUIDANCE_END;
|
|
|
6544
6685
|
function vscodeUserConfigDir() {
|
|
6545
6686
|
if (process.platform === "win32") {
|
|
6546
6687
|
const appData = process.env["APPDATA"];
|
|
6547
|
-
const base = appData !== void 0 && appData.trim() !== "" ? appData : path12.join(
|
|
6688
|
+
const base = appData !== void 0 && appData.trim() !== "" ? appData : path12.join(os10.homedir(), "AppData", "Roaming");
|
|
6548
6689
|
return path12.join(base, "Code", "User");
|
|
6549
6690
|
}
|
|
6550
6691
|
if (process.platform === "darwin") {
|
|
6551
|
-
return path12.join(
|
|
6692
|
+
return path12.join(os10.homedir(), "Library", "Application Support", "Code", "User");
|
|
6552
6693
|
}
|
|
6553
|
-
return path12.join(
|
|
6694
|
+
return path12.join(os10.homedir(), ".config", "Code", "User");
|
|
6554
6695
|
}
|
|
6555
6696
|
function vscodeUserMcpPath() {
|
|
6556
6697
|
return path12.join(vscodeUserConfigDir(), "mcp.json");
|
|
@@ -6565,11 +6706,11 @@ function otherScopeMcpPath(opts) {
|
|
|
6565
6706
|
return opts.project === true ? vscodeUserMcpPath() : vscodeProjectMcpPath(opts.projectRoot);
|
|
6566
6707
|
}
|
|
6567
6708
|
function vscodeInstructionsPath(opts = {}) {
|
|
6568
|
-
return opts.project === true ? path12.join(path12.resolve(opts.projectRoot ?? process.cwd()), ".github", "copilot-instructions.md") : path12.join(
|
|
6709
|
+
return opts.project === true ? path12.join(path12.resolve(opts.projectRoot ?? process.cwd()), ".github", "copilot-instructions.md") : path12.join(os10.homedir(), ".copilot", "instructions", "token-goat.instructions.md");
|
|
6569
6710
|
}
|
|
6570
6711
|
var USER_INSTRUCTIONS_FRONTMATTER = "---\ndescription: 'token-goat: when to use its MCP tools and CLI instead of reading whole files'\napplyTo: '**'\n---\n";
|
|
6571
6712
|
function vscodeHooksDir(opts = {}) {
|
|
6572
|
-
return opts.project === true ? path12.join(path12.resolve(opts.projectRoot ?? process.cwd()), ".github", "hooks") : path12.join(
|
|
6713
|
+
return opts.project === true ? path12.join(path12.resolve(opts.projectRoot ?? process.cwd()), ".github", "hooks") : path12.join(os10.homedir(), ".copilot", "hooks");
|
|
6573
6714
|
}
|
|
6574
6715
|
function vscodeScopeFromFlags(flags) {
|
|
6575
6716
|
return { project: flags.user !== true };
|
|
@@ -6715,8 +6856,12 @@ function uninstallVscodeScoped(opts) {
|
|
|
6715
6856
|
}
|
|
6716
6857
|
if (servers && isManagedServer(servers["token-goat"])) {
|
|
6717
6858
|
const next = dropEmptyServers(updateConfig(config.text, void 0));
|
|
6718
|
-
if (/^\s*\{\s*\}\s*$/.test(next) && takeCreatedConfig(mcpPath))
|
|
6719
|
-
|
|
6859
|
+
if (/^\s*\{\s*\}\s*$/.test(next) && takeCreatedConfig(mcpPath)) {
|
|
6860
|
+
removeFileInScope(mcpPath);
|
|
6861
|
+
if (opts.project === true) {
|
|
6862
|
+
removeEmptyDirInScope(path12.dirname(mcpPath));
|
|
6863
|
+
}
|
|
6864
|
+
} else {
|
|
6720
6865
|
backupFile(mcpPath);
|
|
6721
6866
|
atomicWriteText(mcpPath, next);
|
|
6722
6867
|
}
|
|
@@ -6733,16 +6878,31 @@ function uninstallVscodeScoped(opts) {
|
|
|
6733
6878
|
if (releaseCopilotHooksFile(vscodeHooksDir(opts), "vscode", opts.keepBackups === true)) removed = true;
|
|
6734
6879
|
return removed;
|
|
6735
6880
|
}
|
|
6881
|
+
function cleanupDeprecatedVscodeProjectMcp(projectRoot) {
|
|
6882
|
+
const root = path12.resolve(projectRoot ?? process.cwd());
|
|
6883
|
+
return withInstallScope(root, () => {
|
|
6884
|
+
const projectMcp = path12.join(root, ".vscode", "mcp.json");
|
|
6885
|
+
if (!fs12.existsSync(projectMcp)) return false;
|
|
6886
|
+
const text = fs12.readFileSync(projectMcp, "utf8");
|
|
6887
|
+
if (isResidueServersJson(text) && takeCreatedConfig(projectMcp)) {
|
|
6888
|
+
removeFileInScope(projectMcp);
|
|
6889
|
+
removeEmptyDirInScope(path12.dirname(projectMcp));
|
|
6890
|
+
removeCreatedBackups(projectMcp);
|
|
6891
|
+
return true;
|
|
6892
|
+
}
|
|
6893
|
+
return false;
|
|
6894
|
+
});
|
|
6895
|
+
}
|
|
6736
6896
|
|
|
6737
6897
|
// src/bridges/cursor_install.ts
|
|
6738
6898
|
init_define_import_meta_env();
|
|
6739
6899
|
import * as fs13 from "node:fs";
|
|
6740
|
-
import * as
|
|
6900
|
+
import * as os11 from "node:os";
|
|
6741
6901
|
import * as path13 from "node:path";
|
|
6742
6902
|
var MCP_SERVERS_KEY = "mcpServers";
|
|
6743
6903
|
var TOKEN_GOAT_ENTRY_KEY = "token-goat";
|
|
6744
6904
|
function cursorUserConfigDir() {
|
|
6745
|
-
return path13.join(
|
|
6905
|
+
return path13.join(os11.homedir(), ".cursor");
|
|
6746
6906
|
}
|
|
6747
6907
|
function cursorMcpPath(opts = {}) {
|
|
6748
6908
|
if (opts.project === true) {
|
|
@@ -6817,7 +6977,7 @@ function cursorManagedEntry(mcpPath) {
|
|
|
6817
6977
|
// src/bridges/zed_install.ts
|
|
6818
6978
|
init_define_import_meta_env();
|
|
6819
6979
|
import * as fs14 from "node:fs";
|
|
6820
|
-
import * as
|
|
6980
|
+
import * as os12 from "node:os";
|
|
6821
6981
|
import * as path14 from "node:path";
|
|
6822
6982
|
var CONTEXT_SERVERS_KEY = "context_servers";
|
|
6823
6983
|
var TOKEN_GOAT_ENTRY_KEY2 = "token-goat";
|
|
@@ -6825,11 +6985,11 @@ var ZED_TIMEOUT_MS = 3e4;
|
|
|
6825
6985
|
function zedConfigDir() {
|
|
6826
6986
|
if (process.platform === "win32") {
|
|
6827
6987
|
const appData = process.env["APPDATA"];
|
|
6828
|
-
const base2 = appData !== void 0 && appData.trim() !== "" ? appData : path14.join(
|
|
6988
|
+
const base2 = appData !== void 0 && appData.trim() !== "" ? appData : path14.join(os12.homedir(), "AppData", "Roaming");
|
|
6829
6989
|
return path14.join(base2, "Zed");
|
|
6830
6990
|
}
|
|
6831
6991
|
const xdgConfig = process.env["XDG_CONFIG_HOME"];
|
|
6832
|
-
const base = xdgConfig !== void 0 && xdgConfig.trim() !== "" ? xdgConfig : path14.join(
|
|
6992
|
+
const base = xdgConfig !== void 0 && xdgConfig.trim() !== "" ? xdgConfig : path14.join(os12.homedir(), ".config");
|
|
6833
6993
|
return path14.join(base, "zed");
|
|
6834
6994
|
}
|
|
6835
6995
|
function zedSettingsPath() {
|
|
@@ -6919,13 +7079,88 @@ function zedManagedEntry(settingsPath2) {
|
|
|
6919
7079
|
init_define_import_meta_env();
|
|
6920
7080
|
import * as fs18 from "fs";
|
|
6921
7081
|
import * as path17 from "path";
|
|
6922
|
-
import { spawnSync as
|
|
7082
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
7083
|
+
|
|
7084
|
+
// src/hook_latency.ts
|
|
7085
|
+
init_define_import_meta_env();
|
|
7086
|
+
function percentile(sorted, p) {
|
|
7087
|
+
if (sorted.length === 0) return 0;
|
|
7088
|
+
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p / 100 * sorted.length) - 1));
|
|
7089
|
+
return sorted[idx];
|
|
7090
|
+
}
|
|
7091
|
+
function formatAgeSeconds(seconds) {
|
|
7092
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
7093
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
7094
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
|
7095
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
7096
|
+
}
|
|
7097
|
+
function hookLatencyBreakdown(testDb, homeDir) {
|
|
7098
|
+
try {
|
|
7099
|
+
const db = testDb ?? getGlobalDb(homeDir);
|
|
7100
|
+
if (!statsHasDurationColumn(db)) return [];
|
|
7101
|
+
const harnessExpr = statsHasHarnessColumn(db) ? "COALESCE(harness, '')" : "''";
|
|
7102
|
+
const rows = db.prepare(
|
|
7103
|
+
`SELECT kind, ${harnessExpr} AS harness, duration_ms, ts FROM stats
|
|
7104
|
+
WHERE kind LIKE 'hook:%' AND duration_ms IS NOT NULL ORDER BY ts DESC`
|
|
7105
|
+
).all();
|
|
7106
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7107
|
+
for (const row of rows) {
|
|
7108
|
+
const event = row.kind.slice("hook:".length);
|
|
7109
|
+
const key = `${event}::${row.harness}`;
|
|
7110
|
+
let g = grouped.get(key);
|
|
7111
|
+
if (g === void 0) {
|
|
7112
|
+
g = { event, harness: row.harness, durations: [], newest_ts: row.ts };
|
|
7113
|
+
grouped.set(key, g);
|
|
7114
|
+
}
|
|
7115
|
+
g.durations.push(row.duration_ms);
|
|
7116
|
+
if (row.ts > g.newest_ts) g.newest_ts = row.ts;
|
|
7117
|
+
}
|
|
7118
|
+
return [...grouped.values()].map((g) => {
|
|
7119
|
+
const sorted = [...g.durations].sort((a, b) => a - b);
|
|
7120
|
+
return {
|
|
7121
|
+
event: g.event,
|
|
7122
|
+
harness: g.harness,
|
|
7123
|
+
count: sorted.length,
|
|
7124
|
+
median_ms: percentile(sorted, 50),
|
|
7125
|
+
p95_ms: percentile(sorted, 95),
|
|
7126
|
+
max_ms: sorted[sorted.length - 1],
|
|
7127
|
+
newest_ts: g.newest_ts
|
|
7128
|
+
};
|
|
7129
|
+
}).sort((a, b) => b.p95_ms - a.p95_ms);
|
|
7130
|
+
} catch {
|
|
7131
|
+
return [];
|
|
7132
|
+
}
|
|
7133
|
+
}
|
|
7134
|
+
function renderHookLatencyStats(testDb, homeDir) {
|
|
7135
|
+
const rows = hookLatencyBreakdown(testDb, homeDir);
|
|
7136
|
+
if (rows.length === 0) {
|
|
7137
|
+
console.log(`No hook latency recorded in the last ${HOOK_STATS_RETENTION_DAYS} day(s) (or this database predates the duration_ms column).`);
|
|
7138
|
+
return;
|
|
7139
|
+
}
|
|
7140
|
+
const nowTs = Math.floor(Date.now() / 1e3);
|
|
7141
|
+
console.log(`Hook latency, last ${HOOK_STATS_RETENTION_DAYS} day(s) (worst p95 first):`);
|
|
7142
|
+
for (const r of rows) {
|
|
7143
|
+
const age = formatAgeSeconds(Math.max(0, nowTs - r.newest_ts));
|
|
7144
|
+
console.log(
|
|
7145
|
+
` ${r.event.padEnd(22)} ${(r.harness || "unrecorded").padEnd(12)} n=${String(r.count).padEnd(5)} median ${String(r.median_ms).padStart(5)}ms p95 ${String(r.p95_ms).padStart(5)}ms max ${String(r.max_ms).padStart(6)}ms last seen ${age}`
|
|
7146
|
+
);
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
6923
7149
|
|
|
6924
7150
|
// src/cli_doctor_process.ts
|
|
6925
7151
|
init_define_import_meta_env();
|
|
6926
|
-
import { spawnSync } from "node:child_process";
|
|
7152
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
6927
7153
|
import * as fs15 from "node:fs";
|
|
6928
7154
|
import * as path15 from "node:path";
|
|
7155
|
+
var INTERPRETER_BASENAMES = /* @__PURE__ */ new Set(["node.exe", "python.exe", "python3.exe", "pwsh.exe", "powershell.exe", "cmd.exe"]);
|
|
7156
|
+
function describeProcess(commandLine) {
|
|
7157
|
+
for (const m of commandLine.matchAll(/[^\s"]+\.(?:js|mjs|cjs|ts|py|exe)\b/gi)) {
|
|
7158
|
+
const basename11 = m[0].replace(/\\/g, "/").split("/").pop() ?? m[0];
|
|
7159
|
+
if (!INTERPRETER_BASENAMES.has(basename11.toLowerCase())) return basename11;
|
|
7160
|
+
}
|
|
7161
|
+
const trimmed = commandLine.trim();
|
|
7162
|
+
return trimmed.length > 0 ? trimmed.length > 60 ? trimmed.slice(0, 60) + "\u2026" : trimmed : "(no command line)";
|
|
7163
|
+
}
|
|
6929
7164
|
function checkMcpProcessHealth(processes) {
|
|
6930
7165
|
if (processes === null) {
|
|
6931
7166
|
return {
|
|
@@ -6947,13 +7182,13 @@ function checkMcpProcessHealth(processes) {
|
|
|
6947
7182
|
if (chromeLaunchers.length > 1) details.push(`${chromeLaunchers.length} Chrome DevTools MCP launchers (PIDs: ${chromeLaunchers.map((p) => p.processId).join(", ")})`);
|
|
6948
7183
|
if (playwrightLaunchers.length > 1) details.push(`${playwrightLaunchers.length} Playwright MCP launchers (PIDs: ${playwrightLaunchers.map((p) => p.processId).join(", ")})`);
|
|
6949
7184
|
if (orphanedNodeProcesses.length > 0) {
|
|
6950
|
-
const
|
|
6951
|
-
details.push(`${orphanedNodeProcesses.length} orphaned Node process${orphanedNodeProcesses.length === 1 ? "" : "es"}
|
|
7185
|
+
const described = orphanedNodeProcesses.map((p) => `${p.processId} (${describeProcess(p.commandLine)})`).join(", ");
|
|
7186
|
+
details.push(`${orphanedNodeProcesses.length} orphaned Node process${orphanedNodeProcesses.length === 1 ? "" : "es"}: ${described}`);
|
|
6952
7187
|
}
|
|
6953
7188
|
return {
|
|
6954
7189
|
name: "MCP process health",
|
|
6955
7190
|
status: "warn",
|
|
6956
|
-
message: `${details.join("; ")} detected.
|
|
7191
|
+
message: `${details.join("; ")} detected. None of these are token-goat's own processes -- they are host-managed Node/MCP processes left by closed terminal or CLI sessions; confirm what each one is (the name shown above is a starting point) before stopping it with Stop-Process -Id <PID>.`
|
|
6957
7192
|
};
|
|
6958
7193
|
}
|
|
6959
7194
|
return { name: "MCP process health", status: "ok", message: "no duplicate MCP launchers or orphaned Node processes detected" };
|
|
@@ -6962,7 +7197,7 @@ function runProcessListCommand() {
|
|
|
6962
7197
|
const command = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine | ConvertTo-Json -Compress";
|
|
6963
7198
|
const systemRoot = process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows";
|
|
6964
7199
|
const shell = path15.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
6965
|
-
const result =
|
|
7200
|
+
const result = spawnSync2(fs15.existsSync(shell) ? shell : "powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
|
|
6966
7201
|
encoding: "utf8",
|
|
6967
7202
|
timeout: 2e4,
|
|
6968
7203
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -7000,11 +7235,11 @@ function checkWorkerRunning(dataDir2) {
|
|
|
7000
7235
|
// src/cli_doctor_platforms.ts
|
|
7001
7236
|
init_define_import_meta_env();
|
|
7002
7237
|
import * as fs16 from "node:fs";
|
|
7003
|
-
import * as
|
|
7238
|
+
import * as os13 from "node:os";
|
|
7004
7239
|
import * as path16 from "node:path";
|
|
7005
7240
|
function globalMcpConfigPath() {
|
|
7006
7241
|
const copilotHome = process.env["COPILOT_HOME"];
|
|
7007
|
-
const root = copilotHome !== void 0 && copilotHome.trim() !== "" ? path16.resolve(copilotHome) : path16.join(
|
|
7242
|
+
const root = copilotHome !== void 0 && copilotHome.trim() !== "" ? path16.resolve(copilotHome) : path16.join(os13.homedir(), ".copilot");
|
|
7008
7243
|
return path16.join(root, "mcp-config.json");
|
|
7009
7244
|
}
|
|
7010
7245
|
function checkGlobalMcpConfig(configPath2 = globalMcpConfigPath()) {
|
|
@@ -7161,6 +7396,28 @@ function checkStrayClaudeMdBlocks(searchRoot) {
|
|
|
7161
7396
|
message: `${strays.length} stray cop${strays.length === 1 ? "y" : "ies"} outside CLAUDE.md (never refreshed by install, never removed by uninstall, will go stale): ${strays.join(", ")}`
|
|
7162
7397
|
};
|
|
7163
7398
|
}
|
|
7399
|
+
function checkVscodeProjectMcp(projectRoot = process.cwd()) {
|
|
7400
|
+
const mcpPath = path16.join(path16.resolve(projectRoot), ".vscode", "mcp.json");
|
|
7401
|
+
if (!fs16.existsSync(mcpPath)) return null;
|
|
7402
|
+
if (hasManagedServer(mcpPath, "VS Code")) return null;
|
|
7403
|
+
try {
|
|
7404
|
+
const content = fs16.readFileSync(mcpPath, "utf8");
|
|
7405
|
+
if (isResidueServersJson(content)) {
|
|
7406
|
+
return {
|
|
7407
|
+
name: "VS Code project MCP",
|
|
7408
|
+
status: "warn",
|
|
7409
|
+
message: `empty residue file at ${displaySafeText(mcpPath)} triggers Copilot CLI deprecation warnings; delete it or run 'token-goat uninstall --vscode'`
|
|
7410
|
+
};
|
|
7411
|
+
}
|
|
7412
|
+
return {
|
|
7413
|
+
name: "VS Code project MCP",
|
|
7414
|
+
status: "warn",
|
|
7415
|
+
message: `${displaySafeText(mcpPath)} is deprecated by Copilot CLI v1.0.84+; migrate configured servers to .mcp.json or .github/mcp.json (https://gh.io/copilotcli-mcpmigrate)`
|
|
7416
|
+
};
|
|
7417
|
+
} catch {
|
|
7418
|
+
return null;
|
|
7419
|
+
}
|
|
7420
|
+
}
|
|
7164
7421
|
|
|
7165
7422
|
// src/cli_doctor_security.ts
|
|
7166
7423
|
init_define_import_meta_env();
|
|
@@ -7286,6 +7543,56 @@ function checkSecurityPosture(cfg, dataDirPath) {
|
|
|
7286
7543
|
|
|
7287
7544
|
// src/cli_doctor.ts
|
|
7288
7545
|
var DB_SIZE_WARN_BYTES = 1024 * 1024 * 1024;
|
|
7546
|
+
function freelistBytes(header) {
|
|
7547
|
+
if (header.length < 40) return 0;
|
|
7548
|
+
const rawPageSize = header.readUInt16BE(16);
|
|
7549
|
+
return (rawPageSize === 1 ? 65536 : rawPageSize) * header.readUInt32BE(36);
|
|
7550
|
+
}
|
|
7551
|
+
var CATEGORY_COLUMNS = [
|
|
7552
|
+
{ name: "symbol bodies", table: "symbols", column: "body", command: "'token-goat reclaim-index --rebuild' drops and re-derives them under the current parser's size cap" },
|
|
7553
|
+
{ name: "refs", table: "refs", column: "context", command: "'token-goat reclaim-index --rebuild' drops and re-derives them" },
|
|
7554
|
+
{ name: "chunk text", table: "chunks", column: "text", command: "'token-goat reclaim-index --rebuild' drops and re-derives them" },
|
|
7555
|
+
{ name: "stats detail", table: "stats", column: "detail", command: "ages out on its own (180-day retention) -- no manual command needed" }
|
|
7556
|
+
];
|
|
7557
|
+
var VECTOR_BYTES_PER_ROW = 384 * 4;
|
|
7558
|
+
function dbCategoryBreakdown(dbPath) {
|
|
7559
|
+
const db = getDb(dbPath);
|
|
7560
|
+
const shares = [];
|
|
7561
|
+
for (const cat of CATEGORY_COLUMNS) {
|
|
7562
|
+
try {
|
|
7563
|
+
const row = db.prepare(`SELECT COALESCE(SUM(LENGTH(${cat.column})), 0) as bytes FROM ${cat.table}`).get();
|
|
7564
|
+
if (row.bytes > 0) shares.push({ name: cat.name, bytes: row.bytes, command: cat.command });
|
|
7565
|
+
} catch {
|
|
7566
|
+
}
|
|
7567
|
+
}
|
|
7568
|
+
try {
|
|
7569
|
+
const row = db.prepare("SELECT COUNT(*) as n FROM chunk_vectors").get();
|
|
7570
|
+
if (row.n > 0) {
|
|
7571
|
+
shares.push({
|
|
7572
|
+
name: "embedding vectors",
|
|
7573
|
+
bytes: row.n * VECTOR_BYTES_PER_ROW,
|
|
7574
|
+
command: "'token-goat reclaim-index --rebuild' drops them (set indexing.embeddings_enabled = false first so they don't regrow)"
|
|
7575
|
+
});
|
|
7576
|
+
}
|
|
7577
|
+
} catch {
|
|
7578
|
+
}
|
|
7579
|
+
return shares.sort((a, b) => b.bytes - a.bytes);
|
|
7580
|
+
}
|
|
7581
|
+
function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories = [], topConsumers = []) {
|
|
7582
|
+
const mb = (bytes) => Math.round(bytes / (1024 * 1024));
|
|
7583
|
+
const advice = [];
|
|
7584
|
+
if (freeBytes >= sizeBytes / 10) advice.push(`'token-goat reclaim-index' returns the ${mb(freeBytes)} MB of it that is free pages`);
|
|
7585
|
+
if (tempRows > 0) advice.push(`'token-goat project prune' removes ${countNoun(tempRows, "scratch file")} indexed under the OS temp dir`);
|
|
7586
|
+
const head = `global.db is ${mb(sizeBytes)} MB at ${displaySafeText(dbPath)} (larger than recommended). `;
|
|
7587
|
+
const totalCategoryBytes = categories.reduce((sum, c) => sum + c.bytes, 0);
|
|
7588
|
+
const breakdown = totalCategoryBytes > 0 ? ` Where it went: ${categories.slice(0, 3).map((c) => `${c.name} ${mb(c.bytes)} MB (${Math.round(c.bytes / totalCategoryBytes * 100)}%) -- ${c.command}`).join("; ")}.` : "";
|
|
7589
|
+
let base = advice.length > 0 ? `${head}${advice.join("; ")}.${breakdown}` : `${head}Only ${mb(freeBytes)} MB of it is free pages and none of it is temp-dir scratch, so it is live index data that neither 'reclaim-index' nor 'project prune' will shrink.${breakdown}`;
|
|
7590
|
+
if (topConsumers.length > 0) {
|
|
7591
|
+
const list = topConsumers.map((c) => `${path17.basename(c.root) || c.root} (${countNoun(c.fileCount, "file")})`).join(", ");
|
|
7592
|
+
base += ` Top index consumers: ${list}.`;
|
|
7593
|
+
}
|
|
7594
|
+
return base;
|
|
7595
|
+
}
|
|
7289
7596
|
function checkDbExists(dataDir2) {
|
|
7290
7597
|
const dbPath = path17.join(dataDir2, "global.db");
|
|
7291
7598
|
if (!fs18.existsSync(dbPath)) {
|
|
@@ -7298,12 +7605,14 @@ function checkDbExists(dataDir2) {
|
|
|
7298
7605
|
const sizeBytes = fs18.statSync(dbPath).size;
|
|
7299
7606
|
const SQLITE_HEADER = "SQLite format 3\0";
|
|
7300
7607
|
let header = "";
|
|
7608
|
+
let headerBytes = Buffer.alloc(0);
|
|
7301
7609
|
try {
|
|
7302
7610
|
const fd = fs18.openSync(dbPath, "r");
|
|
7303
7611
|
try {
|
|
7304
|
-
const buf = Buffer.alloc(
|
|
7612
|
+
const buf = Buffer.alloc(100);
|
|
7305
7613
|
const bytesRead = fs18.readSync(fd, buf, 0, buf.length, 0);
|
|
7306
|
-
|
|
7614
|
+
headerBytes = buf.subarray(0, bytesRead);
|
|
7615
|
+
header = buf.toString("latin1", 0, Math.min(bytesRead, SQLITE_HEADER.length));
|
|
7307
7616
|
} finally {
|
|
7308
7617
|
fs18.closeSync(fd);
|
|
7309
7618
|
}
|
|
@@ -7317,12 +7626,18 @@ function checkDbExists(dataDir2) {
|
|
|
7317
7626
|
};
|
|
7318
7627
|
}
|
|
7319
7628
|
if (sizeBytes > DB_SIZE_WARN_BYTES) {
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7629
|
+
let tempRows = 0;
|
|
7630
|
+
try {
|
|
7631
|
+
tempRows = findSystemTempFiles(dbPath).length;
|
|
7632
|
+
} catch {
|
|
7633
|
+
}
|
|
7634
|
+
let categories = [];
|
|
7635
|
+
try {
|
|
7636
|
+
categories = dbCategoryBreakdown(dbPath);
|
|
7637
|
+
} catch {
|
|
7638
|
+
}
|
|
7639
|
+
const topConsumers = findTopIndexedProjects(dbPath, 3);
|
|
7640
|
+
return { name: "Database", status: "warn", message: oversizeDbMessage(dbPath, sizeBytes, freelistBytes(headerBytes), tempRows, categories, topConsumers) };
|
|
7326
7641
|
}
|
|
7327
7642
|
return {
|
|
7328
7643
|
name: "Database",
|
|
@@ -7405,7 +7720,7 @@ function checkParserFreshness(dbPath, rootDir) {
|
|
|
7405
7720
|
return { name: "Parser freshness", status: "ok", message: "no database yet" };
|
|
7406
7721
|
}
|
|
7407
7722
|
try {
|
|
7408
|
-
const { indexedFiles, currentFiles } = getParserFreshness(dbPath,
|
|
7723
|
+
const { indexedFiles, currentFiles } = getParserFreshness(dbPath, parserFingerprintForLanguage, rootDir);
|
|
7409
7724
|
if (indexedFiles === 0) {
|
|
7410
7725
|
return { name: "Parser freshness", status: "ok", message: "no indexed files yet" };
|
|
7411
7726
|
}
|
|
@@ -7468,7 +7783,7 @@ function checkInstall() {
|
|
|
7468
7783
|
if (resolved !== null) {
|
|
7469
7784
|
const isBatch = /\.(?:cmd|bat)$/i.test(resolved);
|
|
7470
7785
|
const comspec = path17.join(process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows", "System32", "cmd.exe");
|
|
7471
|
-
const result = isBatch ?
|
|
7786
|
+
const result = isBatch ? spawnSync3(fs18.existsSync(comspec) ? comspec : "cmd.exe", ["/d", "/s", "/c", resolved, "--version"], { encoding: "utf-8", timeout: 15e3, windowsHide: true }) : spawnSync3(resolved, ["--version"], { encoding: "utf-8", timeout: 15e3, windowsHide: true });
|
|
7472
7787
|
if (result.status === 0) return { name: "Installation", status: "ok", message: (result.stdout ?? "").trim() };
|
|
7473
7788
|
}
|
|
7474
7789
|
return { name: "Installation", status: "fail", message: `token-goat command not found; run: npm install -g ${PACKAGE_NAME}` };
|
|
@@ -7617,7 +7932,7 @@ function checkDiskSpace(dataDir2) {
|
|
|
7617
7932
|
}
|
|
7618
7933
|
if (process.platform !== "win32") {
|
|
7619
7934
|
try {
|
|
7620
|
-
const result =
|
|
7935
|
+
const result = spawnSync3("df", ["-Pk", dataDir2], { encoding: "utf-8" });
|
|
7621
7936
|
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
7622
7937
|
if (result.error === void 0 && result.status === 0 && stdout) {
|
|
7623
7938
|
const lines = stdout.trim().split("\n");
|
|
@@ -7673,7 +7988,7 @@ function checkCopilotCli(configPath2, scriptPath) {
|
|
|
7673
7988
|
toolName: "view",
|
|
7674
7989
|
toolArgs: { path: "doctor-check.txt" }
|
|
7675
7990
|
});
|
|
7676
|
-
const res =
|
|
7991
|
+
const res = spawnSync3(preToolUseCommand, {
|
|
7677
7992
|
input: synthetic,
|
|
7678
7993
|
encoding: "utf-8",
|
|
7679
7994
|
shell: true,
|
|
@@ -7708,6 +8023,7 @@ function checkCopilotCli(configPath2, scriptPath) {
|
|
|
7708
8023
|
var COMPACTION_CHANNEL_WINDOW = 5;
|
|
7709
8024
|
var COMPACTION_STATS_BATCH_SIZE = 50;
|
|
7710
8025
|
var COMPACTION_STATS_SCAN_CEILING = 2e3;
|
|
8026
|
+
var COMPACTION_MANIFEST_FIX_VERSION = "2.9.18";
|
|
7711
8027
|
function checkCompactionChannel(dbPath) {
|
|
7712
8028
|
const name = "Compaction channel";
|
|
7713
8029
|
if (!fs18.existsSync(dbPath)) {
|
|
@@ -7722,8 +8038,9 @@ function checkCompactionChannel(dbPath) {
|
|
|
7722
8038
|
const conclusive = [];
|
|
7723
8039
|
let offset = 0;
|
|
7724
8040
|
let scanned = 0;
|
|
8041
|
+
let preFixConclusive = 0;
|
|
7725
8042
|
for (; ; ) {
|
|
7726
|
-
const batch = db.prepare("SELECT detail FROM stats WHERE kind = 'compact_summary' ORDER BY rowid DESC LIMIT ? OFFSET ?").all(COMPACTION_STATS_BATCH_SIZE, offset);
|
|
8043
|
+
const batch = db.prepare("SELECT detail, tg_version FROM stats WHERE kind = 'compact_summary' ORDER BY rowid DESC LIMIT ? OFFSET ?").all(COMPACTION_STATS_BATCH_SIZE, offset);
|
|
7727
8044
|
if (batch.length === 0) break;
|
|
7728
8045
|
offset += batch.length;
|
|
7729
8046
|
scanned += batch.length;
|
|
@@ -7732,6 +8049,10 @@ function checkCompactionChannel(dbPath) {
|
|
|
7732
8049
|
if (m === null) continue;
|
|
7733
8050
|
const sampled = Number(m[2]);
|
|
7734
8051
|
if (sampled === 0) continue;
|
|
8052
|
+
if (row.tg_version === null || compareSemver(row.tg_version, COMPACTION_MANIFEST_FIX_VERSION) < 0) {
|
|
8053
|
+
preFixConclusive++;
|
|
8054
|
+
continue;
|
|
8055
|
+
}
|
|
7735
8056
|
conclusive.push({ survived: Number(m[1]), sampled });
|
|
7736
8057
|
if (conclusive.length >= COMPACTION_CHANNEL_WINDOW) break;
|
|
7737
8058
|
}
|
|
@@ -7739,6 +8060,9 @@ function checkCompactionChannel(dbPath) {
|
|
|
7739
8060
|
if (scanned >= COMPACTION_STATS_SCAN_CEILING) break;
|
|
7740
8061
|
}
|
|
7741
8062
|
if (conclusive.length === 0) {
|
|
8063
|
+
if (preFixConclusive > 0) {
|
|
8064
|
+
return { name, status: "ok", message: `no post-fix compaction evidence yet -- ${preFixConclusive} compaction(s) found predate ${COMPACTION_MANIFEST_FIX_VERSION}'s manifest-survival fix and are not counted` };
|
|
8065
|
+
}
|
|
7742
8066
|
return { name, status: "ok", message: "no compaction has been measured yet" };
|
|
7743
8067
|
}
|
|
7744
8068
|
const dead = conclusive.filter((c) => c.survived === 0).length;
|
|
@@ -7756,20 +8080,58 @@ function checkCompactionChannel(dbPath) {
|
|
|
7756
8080
|
return { name, status: "warn", message: `could not read compaction stats: ${extractErrorMessage(e)}` };
|
|
7757
8081
|
}
|
|
7758
8082
|
}
|
|
8083
|
+
function checkHookLatency(dbPath) {
|
|
8084
|
+
const name = "Hook latency";
|
|
8085
|
+
if (!fs18.existsSync(dbPath)) {
|
|
8086
|
+
return { name, status: "ok", message: "no database yet" };
|
|
8087
|
+
}
|
|
8088
|
+
try {
|
|
8089
|
+
const db = getDb(dbPath);
|
|
8090
|
+
const present = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'stats'").get();
|
|
8091
|
+
if (present === void 0) {
|
|
8092
|
+
return { name, status: "ok", message: "no hooks measured yet" };
|
|
8093
|
+
}
|
|
8094
|
+
const rows = hookLatencyBreakdown(db);
|
|
8095
|
+
if (rows.length === 0) {
|
|
8096
|
+
return { name, status: "ok", message: "no hook latency recorded yet (or this database predates the duration_ms column)" };
|
|
8097
|
+
}
|
|
8098
|
+
const worst = rows[0];
|
|
8099
|
+
const totalCount = rows.reduce((n, r) => n + r.count, 0);
|
|
8100
|
+
const worstLabel = `${worst.event} (${worst.harness || "unrecorded harness"})`;
|
|
8101
|
+
const budgetMs = loadConfig().hooks.latency_budget_ms;
|
|
8102
|
+
if (worst.p95_ms > budgetMs) {
|
|
8103
|
+
return {
|
|
8104
|
+
name,
|
|
8105
|
+
status: "warn",
|
|
8106
|
+
message: `${worstLabel} is running a p95 of ${worst.p95_ms}ms across ${totalCount} recent hook(s); see 'token-goat stats --hooks' for the full breakdown`
|
|
8107
|
+
};
|
|
8108
|
+
}
|
|
8109
|
+
return { name, status: "ok", message: `worst p95 ${worst.p95_ms}ms (${worstLabel}) across ${totalCount} recent hook(s)` };
|
|
8110
|
+
} catch (e) {
|
|
8111
|
+
return { name, status: "warn", message: `could not read hook latency stats: ${extractErrorMessage(e)}` };
|
|
8112
|
+
}
|
|
8113
|
+
}
|
|
7759
8114
|
var UNMAPPED_TOOL_SAMPLE = 5;
|
|
7760
|
-
|
|
8115
|
+
var UNMAPPED_TOOL_MAX_AGE_DAYS = 7;
|
|
8116
|
+
function checkUnmappedTools(dbPath, options) {
|
|
7761
8117
|
const name = "Tool names";
|
|
7762
8118
|
if (!fs18.existsSync(dbPath)) {
|
|
7763
8119
|
return { name, status: "ok", message: "no database yet" };
|
|
7764
8120
|
}
|
|
7765
8121
|
try {
|
|
8122
|
+
pruneStalePatternCoveredUnmappedTools(getDb(dbPath), [MCP_TOOL_PATTERN]);
|
|
7766
8123
|
const rows = readUnmappedTools(dbPath);
|
|
7767
8124
|
if (rows.length === 0) {
|
|
7768
8125
|
return { name, status: "ok", message: "every tool name seen so far reached a handler that wanted it" };
|
|
7769
8126
|
}
|
|
7770
|
-
const
|
|
7771
|
-
|
|
7772
|
-
|
|
8127
|
+
const maxAgeDays = options?.maxAgeDays ?? UNMAPPED_TOOL_MAX_AGE_DAYS;
|
|
8128
|
+
const nowSecs = options?.nowSecs ?? Math.floor(Date.now() / 1e3);
|
|
8129
|
+
const maxAgeSecs = maxAgeDays * 86400;
|
|
8130
|
+
const nearMisses = rows.filter((r) => {
|
|
8131
|
+
if (r.near_miss === null || r.near_miss === void 0 || r.near_miss === r.tool_name) return false;
|
|
8132
|
+
if (r.last_seen > 0 && nowSecs - r.last_seen > maxAgeSecs) return false;
|
|
8133
|
+
return true;
|
|
8134
|
+
});
|
|
7773
8135
|
if (nearMisses.length > 0) {
|
|
7774
8136
|
const shown2 = nearMisses.slice(0, UNMAPPED_TOOL_SAMPLE).map((r) => `${r.harness} sent "${r.tool_name}" where "${r.near_miss}" is handled (${r.event_name}, ${r.hits}x)`);
|
|
7775
8137
|
const more2 = nearMisses.length > UNMAPPED_TOOL_SAMPLE ? ` (+${nearMisses.length - UNMAPPED_TOOL_SAMPLE} more)` : "";
|
|
@@ -7779,12 +8141,15 @@ function checkUnmappedTools(dbPath) {
|
|
|
7779
8141
|
message: `${shown2.join("; ")}${more2} -- these differ only by case or separators, so that bridge's tool-rename step is very likely not being applied and every handler behind those names is inert`
|
|
7780
8142
|
};
|
|
7781
8143
|
}
|
|
7782
|
-
const
|
|
7783
|
-
const
|
|
8144
|
+
const byName = /* @__PURE__ */ new Map();
|
|
8145
|
+
for (const r of rows) byName.set(r.tool_name, Math.max(byName.get(r.tool_name) ?? 0, r.hits));
|
|
8146
|
+
const names = [...byName].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
8147
|
+
const shown = names.slice(0, UNMAPPED_TOOL_SAMPLE).map(([tool, hits]) => `${tool} (${hits}x)`);
|
|
8148
|
+
const more = names.length > UNMAPPED_TOOL_SAMPLE ? `, +${names.length - UNMAPPED_TOOL_SAMPLE} more` : "";
|
|
7784
8149
|
return {
|
|
7785
8150
|
name,
|
|
7786
8151
|
status: "ok",
|
|
7787
|
-
message: `${
|
|
8152
|
+
message: `${names.length} tool name(s) seen with no handler, none resembling one token-goat handles: ${shown.join(", ")}${more}`
|
|
7788
8153
|
};
|
|
7789
8154
|
} catch (e) {
|
|
7790
8155
|
return { name, status: "warn", message: `could not read the tool-name histogram: ${extractErrorMessage(e)}` };
|
|
@@ -7803,6 +8168,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
|
|
|
7803
8168
|
results.push(checkSymbolCount(path17.join(actualDataDir, "global.db"), rootDir));
|
|
7804
8169
|
results.push(checkDirtyQueueHealth(actualDataDir));
|
|
7805
8170
|
results.push(checkCompactionChannel(path17.join(actualDataDir, "global.db")));
|
|
8171
|
+
results.push(checkHookLatency(path17.join(actualDataDir, "global.db")));
|
|
7806
8172
|
results.push(checkUnmappedTools(path17.join(actualDataDir, "global.db")));
|
|
7807
8173
|
const actualConfigPath = configPath2 || configPath();
|
|
7808
8174
|
results.push(checkConfigValid(actualConfigPath));
|
|
@@ -7822,6 +8188,8 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
|
|
|
7822
8188
|
if (vscodeHooksResult) results.push(vscodeHooksResult);
|
|
7823
8189
|
const vscodeScopeResult = checkVscodeUserScopeHooks(vscodeHooksInstalled(), vscodeHooksInstalled({ project: true }));
|
|
7824
8190
|
if (vscodeScopeResult) results.push(vscodeScopeResult);
|
|
8191
|
+
const vscodeProjectMcpResult = checkVscodeProjectMcp(rootDir);
|
|
8192
|
+
if (vscodeProjectMcpResult) results.push(vscodeProjectMcpResult);
|
|
7825
8193
|
const visualStudioResult = checkVisualStudio([visualStudioUserMcpPath(), visualStudioProjectMcpPath()], [visualStudioSolutionVscodeMcpPath()]);
|
|
7826
8194
|
if (visualStudioResult) results.push(visualStudioResult);
|
|
7827
8195
|
const zedResult = checkZed(zedSettingsPath());
|
|
@@ -7878,6 +8246,15 @@ async function runDoctorRepair(opts) {
|
|
|
7878
8246
|
errors.push(`Failed to download embedding model: ${extractErrorMessage(e)}`);
|
|
7879
8247
|
}
|
|
7880
8248
|
}
|
|
8249
|
+
const rootDir = path17.resolve(opts?.rootDir ?? process.cwd());
|
|
8250
|
+
const projectMcp = path17.join(rootDir, ".vscode", "mcp.json");
|
|
8251
|
+
try {
|
|
8252
|
+
if (cleanupDeprecatedVscodeProjectMcp(rootDir)) {
|
|
8253
|
+
repairs.push(`Removed empty deprecated VS Code MCP residue file at ${displaySafeText(projectMcp)}`);
|
|
8254
|
+
}
|
|
8255
|
+
} catch (e) {
|
|
8256
|
+
errors.push(`Failed to clean up ${displaySafeText(projectMcp)}: ${extractErrorMessage(e)}`);
|
|
8257
|
+
}
|
|
7881
8258
|
return { repairs, errors };
|
|
7882
8259
|
}
|
|
7883
8260
|
function printDoctorResults(results) {
|
|
@@ -8039,7 +8416,7 @@ async function runDoctorAndExit(opts) {
|
|
|
8039
8416
|
}
|
|
8040
8417
|
}
|
|
8041
8418
|
try {
|
|
8042
|
-
const { checkUpdateStatus } = await import("./token-goat-chunk-
|
|
8419
|
+
const { checkUpdateStatus } = await import("./token-goat-chunk-VJQBYSXG.mjs");
|
|
8043
8420
|
const update = await checkUpdateStatus(1500);
|
|
8044
8421
|
if (update.updateAvailable && update.latest) {
|
|
8045
8422
|
console.log(`
|
|
@@ -8131,6 +8508,7 @@ function generateCompactHelp() {
|
|
|
8131
8508
|
"Options:",
|
|
8132
8509
|
" -v, --version print the token-goat version",
|
|
8133
8510
|
" --cwd <path> run as if invoked from this directory",
|
|
8511
|
+
" --notice <text> print this line before the command's own output",
|
|
8134
8512
|
" -h, --help display help for command",
|
|
8135
8513
|
"",
|
|
8136
8514
|
"Reads: symbol, read, brief, section, semantic, skeleton, outline, refs, scope,",
|
|
@@ -8145,7 +8523,7 @@ function generateCompactHelp() {
|
|
|
8145
8523
|
" html-lint, docx-outline, docx-tables, docx-text, pptx-outline, pptx-slide, pptx-text,",
|
|
8146
8524
|
" pptx-notes",
|
|
8147
8525
|
"",
|
|
8148
|
-
"Index & Search: index, map, reconcile, doctor, commands, ask, pack, tokens,",
|
|
8526
|
+
"Index & Search: index, map, reconcile, doctor, commands, ask, answer, pack, tokens,",
|
|
8149
8527
|
" budget, failures, todo, trace, logfold, lockdeps, dep-docs, recent, hot,",
|
|
8150
8528
|
" snapshot-snapshot, baseline, cost, coverage-report-gaps, clean-cache,",
|
|
8151
8529
|
" cache-audit, reclaim-index, project",
|
|
@@ -8187,7 +8565,7 @@ init_define_import_meta_env();
|
|
|
8187
8565
|
// src/config_commands.ts
|
|
8188
8566
|
init_define_import_meta_env();
|
|
8189
8567
|
import * as fs20 from "node:fs";
|
|
8190
|
-
import * as
|
|
8568
|
+
import * as os14 from "node:os";
|
|
8191
8569
|
import * as path18 from "node:path";
|
|
8192
8570
|
|
|
8193
8571
|
// src/webfetch.ts
|
|
@@ -8196,7 +8574,7 @@ import { existsSync as existsSync14, readdirSync as readdirSync3, statSync as st
|
|
|
8196
8574
|
import * as http from "http";
|
|
8197
8575
|
import * as https from "https";
|
|
8198
8576
|
import { isIPv4, isIPv6 } from "net";
|
|
8199
|
-
import { resolve as
|
|
8577
|
+
import { resolve as resolve7, join as join16 } from "path";
|
|
8200
8578
|
import { URL as URL2 } from "url";
|
|
8201
8579
|
import { promisify } from "util";
|
|
8202
8580
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -8337,7 +8715,7 @@ function cleanupStaleDownloads() {
|
|
|
8337
8715
|
const files = readdirSync3(cacheDir);
|
|
8338
8716
|
for (const file of files) {
|
|
8339
8717
|
if (file.endsWith(".tmp")) {
|
|
8340
|
-
const filePath =
|
|
8718
|
+
const filePath = resolve7(cacheDir, file);
|
|
8341
8719
|
try {
|
|
8342
8720
|
const stat = statSync3(filePath);
|
|
8343
8721
|
if (Date.now() - stat.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -9053,7 +9431,7 @@ async function cmdFetchImage(opts) {
|
|
|
9053
9431
|
throw new Error(`fetch failed: ${opts.url}`, { cause: e });
|
|
9054
9432
|
}
|
|
9055
9433
|
const buf = fetched.body;
|
|
9056
|
-
const outPath = opts.out ?? path18.join(
|
|
9434
|
+
const outPath = opts.out ?? path18.join(os14.tmpdir(), `tg-fetch-${Date.now()}${extensionForContentType(fetched.contentType)}`);
|
|
9057
9435
|
const originalBytes = buf.length;
|
|
9058
9436
|
let shrunkBytes;
|
|
9059
9437
|
let outData;
|
|
@@ -12171,7 +12549,7 @@ import * as fs26 from "node:fs";
|
|
|
12171
12549
|
// src/sharepoint_resolve.ts
|
|
12172
12550
|
init_define_import_meta_env();
|
|
12173
12551
|
import * as fs24 from "node:fs";
|
|
12174
|
-
import * as
|
|
12552
|
+
import * as os15 from "node:os";
|
|
12175
12553
|
import * as path23 from "node:path";
|
|
12176
12554
|
var TENANT_HOST_RE = /^([a-z0-9-]+?)(-my)?\.sharepoint\.com$/i;
|
|
12177
12555
|
function parseShareUrl(url) {
|
|
@@ -12244,7 +12622,7 @@ function tryLibraryPaths(root, libSegments, triedPaths) {
|
|
|
12244
12622
|
}
|
|
12245
12623
|
return null;
|
|
12246
12624
|
}
|
|
12247
|
-
function resolveLocalPath(parsed, env = process.env, home =
|
|
12625
|
+
function resolveLocalPath(parsed, env = process.env, home = os15.homedir()) {
|
|
12248
12626
|
const roots = candidateRoots(env, home);
|
|
12249
12627
|
const libSegments = parsed.libraryPath.split(/[/\\]+/).filter((s) => s.length > 0 && s !== "." && s !== "..");
|
|
12250
12628
|
const triedPaths = [];
|
|
@@ -12404,12 +12782,12 @@ function formatCues(cues) {
|
|
|
12404
12782
|
|
|
12405
12783
|
// src/video_chapters.ts
|
|
12406
12784
|
init_define_import_meta_env();
|
|
12407
|
-
import { spawnSync as
|
|
12785
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12408
12786
|
var _ffprobeAvailable;
|
|
12409
12787
|
function isFfprobeAvailable() {
|
|
12410
12788
|
if (_ffprobeAvailable !== void 0) return _ffprobeAvailable;
|
|
12411
12789
|
try {
|
|
12412
|
-
const res =
|
|
12790
|
+
const res = spawnSync4("ffprobe", ["-version"], { stdio: "ignore" });
|
|
12413
12791
|
_ffprobeAvailable = res.status === 0;
|
|
12414
12792
|
} catch {
|
|
12415
12793
|
_ffprobeAvailable = false;
|
|
@@ -12420,7 +12798,7 @@ function extractVideoChapters(file) {
|
|
|
12420
12798
|
if (!isFfprobeAvailable()) {
|
|
12421
12799
|
throw new Error("ffprobe not found on PATH; video-chapters requires ffmpeg (install ffmpeg and ensure ffprobe is on PATH)");
|
|
12422
12800
|
}
|
|
12423
|
-
const res =
|
|
12801
|
+
const res = spawnSync4(
|
|
12424
12802
|
"ffprobe",
|
|
12425
12803
|
["-v", "error", "-print_format", "json", "-show_chapters", "-show_streams", file],
|
|
12426
12804
|
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
|
|
@@ -13610,15 +13988,248 @@ function registerFormatCommands(program2, guard) {
|
|
|
13610
13988
|
// src/cli_cmd_analysis.ts
|
|
13611
13989
|
init_define_import_meta_env();
|
|
13612
13990
|
|
|
13991
|
+
// src/answer_router.ts
|
|
13992
|
+
init_define_import_meta_env();
|
|
13993
|
+
function emit2(text) {
|
|
13994
|
+
const payload = colorStdout() ? text : stripAnsi(text);
|
|
13995
|
+
process.stdout.write(ensureNewline(payload));
|
|
13996
|
+
}
|
|
13997
|
+
function emitErr2(text) {
|
|
13998
|
+
process.stderr.write(ensureNewline(text));
|
|
13999
|
+
}
|
|
14000
|
+
function refusal(why, suggestion) {
|
|
14001
|
+
return `cannot answer deterministically: ${why}; try: ${suggestion}`;
|
|
14002
|
+
}
|
|
14003
|
+
var JUDGEMENT_PATTERNS = [
|
|
14004
|
+
/\bwhy\b/i,
|
|
14005
|
+
/\bshould (?:i|we|it|this|that)\b/i,
|
|
14006
|
+
/^(?:is|are|does|do|did|can|could|would|will|has|have)\b/i,
|
|
14007
|
+
/^(?:explain|describe|summari[sz]e|review|assess|evaluate|compare|analy[sz]e)\b/i,
|
|
14008
|
+
/^how\b/i,
|
|
14009
|
+
/\bwhat(?:'s| is)? (?:the )?(?:bug|issue|problem|point|difference)\b/i,
|
|
14010
|
+
/\bwhat(?:'s| is) wrong\b/i,
|
|
14011
|
+
/\bat runtime\b/i,
|
|
14012
|
+
/\bwhat value\b/i,
|
|
14013
|
+
/\breturns? when\b/i,
|
|
14014
|
+
/\b(?:correct|incorrect|buggy|broken|safe|unsafe|better|worse|best)\b/i
|
|
14015
|
+
];
|
|
14016
|
+
var INTENT_RULES = [
|
|
14017
|
+
{ intent: "callers", re: /^who calls (.+)$/i },
|
|
14018
|
+
{ intent: "callers", re: /^what calls (.+)$/i },
|
|
14019
|
+
{ intent: "callers", re: /^callers of (.+)$/i },
|
|
14020
|
+
{ intent: "callers", re: /^call ?-?sites of (.+)$/i },
|
|
14021
|
+
{ intent: "callers", re: /^(?:all )?(.+) call ?-?sites$/i },
|
|
14022
|
+
{ intent: "tests", re: /^wh(?:at|ich) tests? (?:covers?|exercises?|tests?|touch(?:es)?) (.+)$/i },
|
|
14023
|
+
{ intent: "tests", re: /^tests? for (.+)$/i },
|
|
14024
|
+
{ intent: "tests", re: /^test coverage (?:for|of) (.+)$/i },
|
|
14025
|
+
{ intent: "exports", re: /^what does (.+) export$/i },
|
|
14026
|
+
{ intent: "exports", re: /^exports of (.+)$/i },
|
|
14027
|
+
{ intent: "exports", re: /^(.+) exports$/i },
|
|
14028
|
+
{ intent: "imports", re: /^what does (.+) import$/i },
|
|
14029
|
+
{ intent: "imports", re: /^imports of (.+)$/i },
|
|
14030
|
+
// The bare form is the commoner of the pair in the captured corpus (34 hits vs 15 for `X exports`). It is only safe alongside file-only subject resolution for this intent: with a symbol fallback, the corpus lines `Add imports` and `Update imports` resolve `Add`/`Update` to whatever same-named symbol sorts first.
|
|
14031
|
+
{ intent: "imports", re: /^(.+) imports$/i },
|
|
14032
|
+
{ intent: "impact", re: /^what breaks if (.+) changes?$/i },
|
|
14033
|
+
{ intent: "impact", re: /^what breaks if (?:i|we|you) chang(?:e|ed) (.+)$/i },
|
|
14034
|
+
{ intent: "impact", re: /^what breaks when (?:i|we|you) chang(?:e|ed) (.+)$/i },
|
|
14035
|
+
{ intent: "impact", re: /^what breaks when (.+) chang(?:es|ed)$/i },
|
|
14036
|
+
{ intent: "impact", re: /^what depends on (.+)$/i },
|
|
14037
|
+
{ intent: "impact", re: /^what(?:'s| is) impacted by (?:changing )?(.+)$/i },
|
|
14038
|
+
{ intent: "impact", re: /^blast radius of (.+)$/i },
|
|
14039
|
+
{ intent: "impact", re: /^impact of (?:changing )?(.+)$/i },
|
|
14040
|
+
{ intent: "where", re: /^where is (.+) (?:defined|declared)$/i },
|
|
14041
|
+
{ intent: "where", re: /^where is (.+)$/i },
|
|
14042
|
+
{ intent: "where", re: /^where does (.+) live$/i }
|
|
14043
|
+
];
|
|
14044
|
+
function normalizeQuestion(question) {
|
|
14045
|
+
const collapsed = question.replace(/\s+/g, " ").trim().replace(/[?\s]+$/, "");
|
|
14046
|
+
return collapsed.replace(/^(?:check|show|list|find|get|print|inspect|read|locate|verify|measure|view|trace|identify) (?:the |a |an )?/i, "");
|
|
14047
|
+
}
|
|
14048
|
+
function isJudgementQuestion(rawQuestion) {
|
|
14049
|
+
const question = normalizeQuestion(rawQuestion);
|
|
14050
|
+
return JUDGEMENT_PATTERNS.some((p) => p.test(question));
|
|
14051
|
+
}
|
|
14052
|
+
function classify(rawQuestion) {
|
|
14053
|
+
const question = normalizeQuestion(rawQuestion);
|
|
14054
|
+
if (isJudgementQuestion(question)) return null;
|
|
14055
|
+
for (const rule of INTENT_RULES) {
|
|
14056
|
+
const m = rule.re.exec(question);
|
|
14057
|
+
if (m && m[1] !== void 0) {
|
|
14058
|
+
const subject = normalizeSubject(m[1]);
|
|
14059
|
+
if (subject.length > 0) return { intent: rule.intent, subject };
|
|
14060
|
+
}
|
|
14061
|
+
}
|
|
14062
|
+
return null;
|
|
14063
|
+
}
|
|
14064
|
+
function normalizeSubject(raw) {
|
|
14065
|
+
let s = raw.trim();
|
|
14066
|
+
for (; ; ) {
|
|
14067
|
+
const next = s.replace(/^[`'"]+/, "").replace(/[`'".,;:!]+$/, "").trim();
|
|
14068
|
+
if (next === s) break;
|
|
14069
|
+
s = next;
|
|
14070
|
+
}
|
|
14071
|
+
s = s.replace(/^(?:the|a|an) /i, "");
|
|
14072
|
+
return s.trim();
|
|
14073
|
+
}
|
|
14074
|
+
var SYMBOL_SCAN_PAGE = 200;
|
|
14075
|
+
function resolveSymbolHit(subject, rootDir) {
|
|
14076
|
+
for (let offset = 0; ; offset += SYMBOL_SCAN_PAGE) {
|
|
14077
|
+
const rows = querySymbols({ name: subject, rootDir, limit: SYMBOL_SCAN_PAGE, offset });
|
|
14078
|
+
if (rows.length === 0) return null;
|
|
14079
|
+
const hit = rows.find((r) => !isIgnoredIndexPath(r.filePath));
|
|
14080
|
+
if (hit) return { name: hit.name, file: hit.filePath };
|
|
14081
|
+
if (rows.length < SYMBOL_SCAN_PAGE) return null;
|
|
14082
|
+
}
|
|
14083
|
+
}
|
|
14084
|
+
function resolveFileHit(subject, rootDir) {
|
|
14085
|
+
const entry = getFileEntry(resolveIndexPath(subject));
|
|
14086
|
+
if (entry && !isIgnoredIndexPath(entry.filePath)) return { kind: "file", path: entry.filePath };
|
|
14087
|
+
if (subject.includes("/") || subject.includes("\\")) return null;
|
|
14088
|
+
const want = foldPath(subject);
|
|
14089
|
+
const matches2 = [];
|
|
14090
|
+
for (const [folded, indexed] of getProjectFileEntries(rootDir)) {
|
|
14091
|
+
if (isIgnoredIndexPath(folded)) continue;
|
|
14092
|
+
const base = folded.slice(Math.max(folded.lastIndexOf("/"), folded.lastIndexOf("\\")) + 1);
|
|
14093
|
+
const dot = base.lastIndexOf(".");
|
|
14094
|
+
if (base === want || dot > 0 && base.slice(0, dot) === want) matches2.push(indexed.filePath);
|
|
14095
|
+
}
|
|
14096
|
+
const paths = [...new Set(matches2)].sort();
|
|
14097
|
+
if (paths.length === 0) return null;
|
|
14098
|
+
if (paths.length === 1 && paths[0] !== void 0) return { kind: "file", path: paths[0] };
|
|
14099
|
+
return { kind: "ambiguous", candidates: paths };
|
|
14100
|
+
}
|
|
14101
|
+
function resolveSubject(subject, mode = "symbol-first") {
|
|
14102
|
+
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
14103
|
+
const sep5 = subject.lastIndexOf("::");
|
|
14104
|
+
if (sep5 > 0 && sep5 + 2 < subject.length) {
|
|
14105
|
+
const file2 = resolveIndexPath(subject.slice(0, sep5));
|
|
14106
|
+
const hit2 = querySymbols({ filePath: file2, name: subject.slice(sep5 + 2), rootDir, limit: 1 })[0];
|
|
14107
|
+
if (hit2 && !isIgnoredIndexPath(hit2.filePath)) return { kind: "symbol", name: hit2.name, file: hit2.filePath };
|
|
14108
|
+
return null;
|
|
14109
|
+
}
|
|
14110
|
+
if (/\s/.test(subject)) return null;
|
|
14111
|
+
if (mode === "symbol-first") {
|
|
14112
|
+
const hit2 = resolveSymbolHit(subject, rootDir);
|
|
14113
|
+
if (hit2) return { kind: "symbol", name: hit2.name, file: hit2.file };
|
|
14114
|
+
return resolveFileHit(subject, rootDir);
|
|
14115
|
+
}
|
|
14116
|
+
const file = resolveFileHit(subject, rootDir);
|
|
14117
|
+
if (file) return file;
|
|
14118
|
+
const hit = resolveSymbolHit(subject, rootDir);
|
|
14119
|
+
if (!hit) return null;
|
|
14120
|
+
return mode === "file-first" ? { kind: "symbol", name: hit.name, file: hit.file } : { kind: "symbol-only", name: hit.name, file: hit.file };
|
|
14121
|
+
}
|
|
14122
|
+
var FILE_INTENTS = /* @__PURE__ */ new Set(["tests", "exports", "imports"]);
|
|
14123
|
+
var ANSWER_DELEGATE_LIMIT = 20;
|
|
14124
|
+
function subjectModeFor(intent) {
|
|
14125
|
+
if (intent === "exports" || intent === "imports") return "file-only";
|
|
14126
|
+
return intent === "tests" ? "file-first" : "symbol-first";
|
|
14127
|
+
}
|
|
14128
|
+
function runAnswer(opts) {
|
|
14129
|
+
const question = opts.question.trim();
|
|
14130
|
+
if (question.length === 0) {
|
|
14131
|
+
emitErr2(refusal("the question is empty", 'token-goat answer "who calls <symbol>"'));
|
|
14132
|
+
return 1;
|
|
14133
|
+
}
|
|
14134
|
+
if (isJudgementQuestion(question)) {
|
|
14135
|
+
emitErr2(
|
|
14136
|
+
refusal(
|
|
14137
|
+
"that asks for judgement, intent, or runtime behaviour, which the index cannot answer -- it would need the code read and reasoned over",
|
|
14138
|
+
`token-goat semantic "${question}"`
|
|
14139
|
+
)
|
|
14140
|
+
);
|
|
14141
|
+
return 1;
|
|
14142
|
+
}
|
|
14143
|
+
const cls = classify(question);
|
|
14144
|
+
if (cls === null) {
|
|
14145
|
+
emitErr2(
|
|
14146
|
+
refusal(
|
|
14147
|
+
"no intent matched -- this router only answers where/who-calls/what-tests-cover/what-exports/what-imports/what-breaks questions about a named symbol or file",
|
|
14148
|
+
`token-goat semantic "${question}"`
|
|
14149
|
+
)
|
|
14150
|
+
);
|
|
14151
|
+
return 1;
|
|
14152
|
+
}
|
|
14153
|
+
const resolved = resolveSubject(cls.subject, subjectModeFor(cls.intent));
|
|
14154
|
+
if (resolved === null) {
|
|
14155
|
+
emitErr2(
|
|
14156
|
+
refusal(
|
|
14157
|
+
`'${cls.subject}' is not an indexed symbol or file`,
|
|
14158
|
+
`token-goat semantic "${question}"`
|
|
14159
|
+
)
|
|
14160
|
+
);
|
|
14161
|
+
return 1;
|
|
14162
|
+
}
|
|
14163
|
+
const rootDir = resolveProjectRoot({ project: process.cwd() });
|
|
14164
|
+
if (resolved.kind === "ambiguous") {
|
|
14165
|
+
const shown = resolved.candidates.slice(0, 5).map((c) => toDisplayPath(rootDir, c));
|
|
14166
|
+
const more = resolved.candidates.length - shown.length;
|
|
14167
|
+
const first = shown[0] ?? "";
|
|
14168
|
+
const next = FILE_INTENTS.has(cls.intent) ? `token-goat answer "${cls.intent === "tests" ? "tests for" : cls.intent === "imports" ? "imports of" : "exports of"} ${first}"` : `token-goat outline ${first}`;
|
|
14169
|
+
emitErr2(
|
|
14170
|
+
refusal(
|
|
14171
|
+
`'${cls.subject}' names ${resolved.candidates.length} files in this project (${shown.join(", ")}${more > 0 ? `, +${more} more` : ""})`,
|
|
14172
|
+
next
|
|
14173
|
+
)
|
|
14174
|
+
);
|
|
14175
|
+
return 1;
|
|
14176
|
+
}
|
|
14177
|
+
if (resolved.kind === "symbol-only") {
|
|
14178
|
+
emitErr2(
|
|
14179
|
+
refusal(
|
|
14180
|
+
`'${cls.subject}' is a symbol; exports/imports are file-level`,
|
|
14181
|
+
`token-goat ${cls.intent} ${toDisplayPath(rootDir, resolved.file)}`
|
|
14182
|
+
)
|
|
14183
|
+
);
|
|
14184
|
+
return 1;
|
|
14185
|
+
}
|
|
14186
|
+
if (FILE_INTENTS.has(cls.intent)) {
|
|
14187
|
+
const file = resolved.kind === "symbol" ? resolved.file : resolved.path;
|
|
14188
|
+
const display = toDisplayPath(rootDir, file);
|
|
14189
|
+
if (cls.intent === "tests") {
|
|
14190
|
+
emit2(`via: token-goat test-for ${display}`);
|
|
14191
|
+
return runTestFor({ file });
|
|
14192
|
+
}
|
|
14193
|
+
if (cls.intent === "exports") {
|
|
14194
|
+
emit2(`via: token-goat exports ${display}`);
|
|
14195
|
+
return runExports({ file });
|
|
14196
|
+
}
|
|
14197
|
+
emit2(`via: token-goat imports ${display}`);
|
|
14198
|
+
return runImports({ file });
|
|
14199
|
+
}
|
|
14200
|
+
if (resolved.kind === "file") {
|
|
14201
|
+
const display = toDisplayPath(rootDir, resolved.path);
|
|
14202
|
+
emitErr2(
|
|
14203
|
+
refusal(
|
|
14204
|
+
`'${cls.subject}' is a file, and ${cls.intent === "where" ? "where" : cls.intent} needs a symbol`,
|
|
14205
|
+
`token-goat outline ${display}`
|
|
14206
|
+
)
|
|
14207
|
+
);
|
|
14208
|
+
return 1;
|
|
14209
|
+
}
|
|
14210
|
+
if (cls.intent === "callers") {
|
|
14211
|
+
emit2(`via: token-goat callers ${displaySafeText(resolved.name)} --limit ${ANSWER_DELEGATE_LIMIT}`);
|
|
14212
|
+
return runCallers({ symbol: resolved.name, limit: ANSWER_DELEGATE_LIMIT });
|
|
14213
|
+
}
|
|
14214
|
+
if (cls.intent === "impact") {
|
|
14215
|
+
emit2(`via: token-goat impact ${displaySafeText(resolved.name)} --top ${ANSWER_DELEGATE_LIMIT}`);
|
|
14216
|
+
return runImpact({ symbol: resolved.name, top: ANSWER_DELEGATE_LIMIT });
|
|
14217
|
+
}
|
|
14218
|
+
emit2(`via: token-goat symbol ${displaySafeText(resolved.name)} -p --exclude-vendored`);
|
|
14219
|
+
const r = runSymbol({ name: resolved.name, projectRoot: rootDir, limit: ANSWER_DELEGATE_LIMIT, excludeVendored: true });
|
|
14220
|
+
if (r.text.length > 0) emit2(r.text);
|
|
14221
|
+
return r.code;
|
|
14222
|
+
}
|
|
14223
|
+
|
|
13613
14224
|
// src/affected.ts
|
|
13614
14225
|
init_define_import_meta_env();
|
|
13615
14226
|
import * as path25 from "node:path";
|
|
13616
14227
|
var DEFAULT_AFFECTED_DEPTH = 5;
|
|
13617
|
-
function
|
|
14228
|
+
function emit3(text) {
|
|
13618
14229
|
const payload = colorStdout() ? text : stripAnsi(text);
|
|
13619
14230
|
process.stdout.write(ensureNewline(payload));
|
|
13620
14231
|
}
|
|
13621
|
-
function
|
|
14232
|
+
function emitErr3(text) {
|
|
13622
14233
|
process.stderr.write(ensureNewline(text));
|
|
13623
14234
|
}
|
|
13624
14235
|
function guardedFilter(filter2) {
|
|
@@ -13675,43 +14286,43 @@ function computeAffected(opts) {
|
|
|
13675
14286
|
}
|
|
13676
14287
|
function runAffected(opts) {
|
|
13677
14288
|
if (opts.depth !== void 0 && opts.depth <= 0) {
|
|
13678
|
-
|
|
14289
|
+
emitErr3(`--depth must be a positive number, got: ${opts.depth}`);
|
|
13679
14290
|
return 1;
|
|
13680
14291
|
}
|
|
13681
14292
|
if (opts.files.length === 0) {
|
|
13682
|
-
|
|
14293
|
+
emitErr3("No changed files given. Pass paths as arguments, or pipe them in with --stdin.");
|
|
13683
14294
|
return 1;
|
|
13684
14295
|
}
|
|
13685
14296
|
if (opts.filter !== void 0) {
|
|
13686
14297
|
const guarded = compileGuardedRegex(opts.filter);
|
|
13687
14298
|
if (!guarded.ok) {
|
|
13688
|
-
|
|
14299
|
+
emitErr3(`--filter is not usable as a regular expression: ${guarded.reason}`);
|
|
13689
14300
|
return 1;
|
|
13690
14301
|
}
|
|
13691
14302
|
}
|
|
13692
14303
|
const result = computeAffected(opts);
|
|
13693
14304
|
if (opts.json === true) {
|
|
13694
|
-
|
|
14305
|
+
emit3(displaySafeJson(result));
|
|
13695
14306
|
return 0;
|
|
13696
14307
|
}
|
|
13697
14308
|
if (opts.quiet === true) {
|
|
13698
|
-
for (const f of result.testFiles)
|
|
14309
|
+
for (const f of result.testFiles) emit3(f);
|
|
13699
14310
|
} else if (result.testFiles.length === 0) {
|
|
13700
14311
|
const traced = opts.files.length - result.unknownSeeds.length;
|
|
13701
14312
|
if (traced === 0) {
|
|
13702
|
-
|
|
14313
|
+
emit3(`Nothing could be traced: ${result.unknownSeeds.length === 1 ? "the file given is not tracked" : "none of the files given are tracked"} in this project.`);
|
|
13703
14314
|
} else {
|
|
13704
|
-
|
|
14315
|
+
emit3(`No test files import ${countNoun(traced, "changed file")} (within ${countNoun(result.depth, "hop")}).`);
|
|
13705
14316
|
}
|
|
13706
14317
|
} else {
|
|
13707
|
-
|
|
13708
|
-
for (const f of result.testFiles)
|
|
14318
|
+
emit3(`${countNoun(result.testFiles.length, "affected test file")} (${countNoun(result.reachedCount, "file")} reached within ${countNoun(result.depth, "hop")}):`);
|
|
14319
|
+
for (const f of result.testFiles) emit3(` ${f}`);
|
|
13709
14320
|
}
|
|
13710
14321
|
if (result.unknownSeeds.length > 0) {
|
|
13711
|
-
|
|
14322
|
+
emitErr3(`Not tracked in this project, so nothing was traced from ${result.unknownSeeds.length === 1 ? "it" : "them"}: ${result.unknownSeeds.join(", ")}`);
|
|
13712
14323
|
}
|
|
13713
14324
|
if (result.depthLimited) {
|
|
13714
|
-
|
|
14325
|
+
emitErr3(`Stopped at --depth ${result.depth} with ${countNoun(result.unexploredAtFrontier, "file")} still unexplored; more tests may be affected (raise --depth).`);
|
|
13715
14326
|
}
|
|
13716
14327
|
return 0;
|
|
13717
14328
|
}
|
|
@@ -14844,17 +15455,17 @@ function cmdNote(action, key, value, opts) {
|
|
|
14844
15455
|
throw new Error(`Unknown note action: ${action}. Use: set, get, unset, list, clear`);
|
|
14845
15456
|
}
|
|
14846
15457
|
function loadAllSessionReadCounts() {
|
|
14847
|
-
const
|
|
15458
|
+
const sessionsDir2 = path29.join(tokenGoatHome(), "sessions");
|
|
14848
15459
|
const totals = /* @__PURE__ */ new Map();
|
|
14849
15460
|
let entries;
|
|
14850
15461
|
try {
|
|
14851
|
-
entries = fs32.readdirSync(
|
|
15462
|
+
entries = fs32.readdirSync(sessionsDir2, { withFileTypes: true });
|
|
14852
15463
|
} catch {
|
|
14853
15464
|
return totals;
|
|
14854
15465
|
}
|
|
14855
15466
|
for (const entry of entries) {
|
|
14856
15467
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
14857
|
-
const filePath = path29.join(
|
|
15468
|
+
const filePath = path29.join(sessionsDir2, entry.name);
|
|
14858
15469
|
let raw;
|
|
14859
15470
|
try {
|
|
14860
15471
|
raw = JSON.parse(fs32.readFileSync(filePath, "utf8"));
|
|
@@ -15457,6 +16068,7 @@ function registerAnalysisCommands(program2, guard) {
|
|
|
15457
16068
|
})
|
|
15458
16069
|
)
|
|
15459
16070
|
);
|
|
16071
|
+
program2.command("answer <question>").description("answer a plain-English question from the index alone, or refuse with a reason (no model call, no inference)").action((question) => runExit(() => runAnswer({ question })));
|
|
15460
16072
|
program2.command("pack [patterns...]").description("bundle matched files into a single LLM-ready output (Markdown, XML, or plain text)").option("--format <style>", "output style: md (default), xml, or text", "md").option("--line-numbers", "prefix each line with its line number").option("--instruction-file <path>", "append a task prompt from a file").option("--output <path>", "write output to a file instead of stdout").option("--no-ignore", "bypass .tokengoatignore patterns").option("--strip-comments", "remove language-appropriate comments before packing").option("--scan-secrets", "scan for credentials; exit 2 if any are found").option("--budget <n>", "exit 3 if the estimated token count exceeds n").action(guard(cmdPack));
|
|
15461
16073
|
program2.command("tokens [patterns...]").description("per-file token footprint table, sorted largest-first").option("--tree", "group by directory with subtotals and percentage of total").option("--top <n>", "limit to the N biggest files").option("--asc", "reverse order (ascending)").option("-j, --json", "output as JSON").action(guard(cmdTokens));
|
|
15462
16074
|
program2.command("budget <patterns...>").description("estimate the total token cost of a file set").option("--context <n>", "context window in thousands of tokens (shows % fill)").option("-j, --json", "output as JSON").action(guard(cmdBudget));
|
|
@@ -15623,12 +16235,12 @@ function renderCapabilities(caps) {
|
|
|
15623
16235
|
|
|
15624
16236
|
// src/cli_session.ts
|
|
15625
16237
|
init_define_import_meta_env();
|
|
15626
|
-
import * as
|
|
16238
|
+
import * as fs41 from "node:fs";
|
|
15627
16239
|
|
|
15628
16240
|
// src/session_audit.ts
|
|
15629
16241
|
init_define_import_meta_env();
|
|
15630
16242
|
import * as fs34 from "node:fs";
|
|
15631
|
-
import * as
|
|
16243
|
+
import * as os16 from "node:os";
|
|
15632
16244
|
import * as path31 from "node:path";
|
|
15633
16245
|
import * as readline from "node:readline";
|
|
15634
16246
|
function emptyMeasured() {
|
|
@@ -15735,10 +16347,10 @@ var SHELL_READER_TOKEN_RE = /\b(?:sed|grep|cat|head|tail|awk|rg|less|nl|python3?
|
|
|
15735
16347
|
function escapeRegExp2(s) {
|
|
15736
16348
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
15737
16349
|
}
|
|
15738
|
-
function shellReadMatch(command,
|
|
15739
|
-
if (
|
|
15740
|
-
if (!command.includes(
|
|
15741
|
-
const escaped = escapeRegExp2(
|
|
16350
|
+
function shellReadMatch(command, basename11) {
|
|
16351
|
+
if (basename11 === "" || !SHELL_READER_TOKEN_RE.test(command)) return "none";
|
|
16352
|
+
if (!command.includes(basename11)) return "none";
|
|
16353
|
+
const escaped = escapeRegExp2(basename11);
|
|
15742
16354
|
const strict = new RegExp(`(?:^|["'\`/\\\\\\s])${escaped}(?:$|["'\`/\\\\\\s,;:)])`);
|
|
15743
16355
|
return strict.test(command) ? "match" : "ambiguous";
|
|
15744
16356
|
}
|
|
@@ -15826,11 +16438,11 @@ function listCorpusTranscripts(corpusDir) {
|
|
|
15826
16438
|
return found.sort();
|
|
15827
16439
|
}
|
|
15828
16440
|
function defaultCorpusDir() {
|
|
15829
|
-
const claudeDir = path31.join(
|
|
16441
|
+
const claudeDir = path31.join(claudeConfigDir(), "projects");
|
|
15830
16442
|
if (fs34.existsSync(claudeDir) && listCorpusTranscripts(claudeDir).length > 0) {
|
|
15831
16443
|
return claudeDir;
|
|
15832
16444
|
}
|
|
15833
|
-
const home =
|
|
16445
|
+
const home = os16.homedir();
|
|
15834
16446
|
const appData = process.env["APPDATA"] || path31.join(home, "AppData", "Roaming");
|
|
15835
16447
|
const candidates = [
|
|
15836
16448
|
claudeDir,
|
|
@@ -16027,13 +16639,13 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
|
|
|
16027
16639
|
if (name === "Read" || name === "Skill") {
|
|
16028
16640
|
const denyTemplate = DENY_TEMPLATES.find((t) => t.tool === name && t.re.test(text));
|
|
16029
16641
|
if (denyTemplate !== void 0) {
|
|
16030
|
-
const
|
|
16031
|
-
const lastSlash =
|
|
16642
|
+
const path43 = readPathById.get(id) ?? "";
|
|
16643
|
+
const lastSlash = path43.lastIndexOf("/");
|
|
16032
16644
|
openDenies.push({
|
|
16033
16645
|
kind: denyTemplate.kind,
|
|
16034
16646
|
withheldBytes: parseWithheldBytes(denyTemplate.kind, text),
|
|
16035
|
-
path:
|
|
16036
|
-
basename: lastSlash === -1 ?
|
|
16647
|
+
path: path43,
|
|
16648
|
+
basename: lastSlash === -1 ? path43 : path43.slice(lastSlash + 1),
|
|
16037
16649
|
callIndexAtDeny: perCall.length,
|
|
16038
16650
|
compactEpochAtOpen: compactEpoch,
|
|
16039
16651
|
compactEpochAtWindow: null,
|
|
@@ -16505,267 +17117,17 @@ function formatSessionAudit(s) {
|
|
|
16505
17117
|
|
|
16506
17118
|
// src/session_read.ts
|
|
16507
17119
|
init_define_import_meta_env();
|
|
16508
|
-
import * as fs36 from "node:fs";
|
|
16509
|
-
import * as path33 from "node:path";
|
|
16510
|
-
import * as readline2 from "node:readline";
|
|
16511
|
-
|
|
16512
|
-
// src/waste.ts
|
|
16513
|
-
init_define_import_meta_env();
|
|
16514
17120
|
import * as fs35 from "node:fs";
|
|
16515
|
-
import * as os18 from "node:os";
|
|
16516
17121
|
import * as path32 from "node:path";
|
|
16517
|
-
|
|
16518
|
-
const rootStr = path32.resolve(projectRoot);
|
|
16519
|
-
const slug = rootStr.replace(/[^A-Za-z0-9]/g, "-");
|
|
16520
|
-
return path32.join(os18.homedir(), ".claude", "projects", slug);
|
|
16521
|
-
}
|
|
16522
|
-
function findLatestTranscript(projectRoot) {
|
|
16523
|
-
const dir = projectTranscriptsDir(projectRoot);
|
|
16524
|
-
let entries;
|
|
16525
|
-
try {
|
|
16526
|
-
entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
16527
|
-
} catch {
|
|
16528
|
-
return null;
|
|
16529
|
-
}
|
|
16530
|
-
let best = null;
|
|
16531
|
-
for (const name of entries) {
|
|
16532
|
-
const full = path32.join(dir, name);
|
|
16533
|
-
let stat;
|
|
16534
|
-
try {
|
|
16535
|
-
stat = fs35.statSync(full);
|
|
16536
|
-
} catch {
|
|
16537
|
-
continue;
|
|
16538
|
-
}
|
|
16539
|
-
if (best === null || stat.mtimeMs > best.mtimeMs) best = { file: full, mtimeMs: stat.mtimeMs };
|
|
16540
|
-
}
|
|
16541
|
-
return best?.file ?? null;
|
|
16542
|
-
}
|
|
16543
|
-
function safeStringify(value) {
|
|
16544
|
-
try {
|
|
16545
|
-
return JSON.stringify(value) ?? "";
|
|
16546
|
-
} catch {
|
|
16547
|
-
return "";
|
|
16548
|
-
}
|
|
16549
|
-
}
|
|
16550
|
-
var FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "NotebookEdit", "MultiEdit"]);
|
|
16551
|
-
function extractFilePath(name, input) {
|
|
16552
|
-
if (!FILE_PATH_TOOLS.has(name)) return null;
|
|
16553
|
-
if (input === null || typeof input !== "object") return null;
|
|
16554
|
-
const o = input;
|
|
16555
|
-
const fp = o["file_path"];
|
|
16556
|
-
if (typeof fp === "string") return fp;
|
|
16557
|
-
const notebookPath = o["notebook_path"];
|
|
16558
|
-
return typeof notebookPath === "string" ? notebookPath : null;
|
|
16559
|
-
}
|
|
16560
|
-
function extractCommand(name, input) {
|
|
16561
|
-
if (name !== "Bash") return null;
|
|
16562
|
-
if (input === null || typeof input !== "object") return null;
|
|
16563
|
-
const cmd = input["command"];
|
|
16564
|
-
return typeof cmd === "string" ? cmd : null;
|
|
16565
|
-
}
|
|
16566
|
-
function summarizeInput(name, input, filePath, command) {
|
|
16567
|
-
if (filePath !== null) return filePath;
|
|
16568
|
-
if (command !== null) return command.length > 100 ? `${command.slice(0, 100)}\u2026` : command;
|
|
16569
|
-
if (input === null || typeof input !== "object") return "";
|
|
16570
|
-
const o = input;
|
|
16571
|
-
for (const key of ["pattern", "query", "path", "description", "prompt"]) {
|
|
16572
|
-
const v = o[key];
|
|
16573
|
-
if (typeof v === "string") return v.length > 100 ? `${v.slice(0, 100)}\u2026` : v;
|
|
16574
|
-
}
|
|
16575
|
-
const s = safeStringify(input);
|
|
16576
|
-
return s.length > 100 ? `${s.slice(0, 100)}\u2026` : s;
|
|
16577
|
-
}
|
|
16578
|
-
function extractResultText(content) {
|
|
16579
|
-
if (typeof content === "string") return content;
|
|
16580
|
-
if (Array.isArray(content)) {
|
|
16581
|
-
return content.map((b) => {
|
|
16582
|
-
if (b !== null && typeof b === "object" && b["type"] === "text") {
|
|
16583
|
-
const t = b["text"];
|
|
16584
|
-
return typeof t === "string" ? t : "";
|
|
16585
|
-
}
|
|
16586
|
-
return "";
|
|
16587
|
-
}).join("\n");
|
|
16588
|
-
}
|
|
16589
|
-
return "";
|
|
16590
|
-
}
|
|
16591
|
-
function parseTranscript2(transcriptPath) {
|
|
16592
|
-
const raw = fs35.readFileSync(transcriptPath, "utf-8");
|
|
16593
|
-
const calls = [];
|
|
16594
|
-
const resultTextById = /* @__PURE__ */ new Map();
|
|
16595
|
-
const assistantTurns = [];
|
|
16596
|
-
const resident = createResidentContextStats();
|
|
16597
|
-
let seq = 0;
|
|
16598
|
-
for (const line of raw.split("\n")) {
|
|
16599
|
-
const trimmed = line.trim();
|
|
16600
|
-
if (trimmed === "") continue;
|
|
16601
|
-
let obj;
|
|
16602
|
-
try {
|
|
16603
|
-
obj = JSON.parse(trimmed);
|
|
16604
|
-
} catch {
|
|
16605
|
-
continue;
|
|
16606
|
-
}
|
|
16607
|
-
if (obj === null || typeof obj !== "object") continue;
|
|
16608
|
-
const o = obj;
|
|
16609
|
-
accumulateResidentLine(resident, o, trimmed.length);
|
|
16610
|
-
const message = o["message"];
|
|
16611
|
-
if (message === null || typeof message !== "object") continue;
|
|
16612
|
-
const messageObj = message;
|
|
16613
|
-
const content = messageObj["content"];
|
|
16614
|
-
if (!Array.isArray(content)) continue;
|
|
16615
|
-
const role = typeof messageObj["role"] === "string" ? messageObj["role"] : null;
|
|
16616
|
-
const cwd = typeof o["cwd"] === "string" ? o["cwd"] : null;
|
|
16617
|
-
let turnTextTokens = 0;
|
|
16618
|
-
let sawTextBlock = false;
|
|
16619
|
-
for (const block of content) {
|
|
16620
|
-
if (block === null || typeof block !== "object") continue;
|
|
16621
|
-
const b = block;
|
|
16622
|
-
if (b["type"] === "tool_use" && typeof b["id"] === "string" && typeof b["name"] === "string") {
|
|
16623
|
-
const name = b["name"];
|
|
16624
|
-
const input = b["input"];
|
|
16625
|
-
const filePath = extractFilePath(name, input);
|
|
16626
|
-
const command = extractCommand(name, input);
|
|
16627
|
-
calls.push({
|
|
16628
|
-
seq: seq++,
|
|
16629
|
-
id: b["id"],
|
|
16630
|
-
name,
|
|
16631
|
-
input,
|
|
16632
|
-
filePath,
|
|
16633
|
-
command,
|
|
16634
|
-
summary: summarizeInput(name, input, filePath, command),
|
|
16635
|
-
cwd
|
|
16636
|
-
});
|
|
16637
|
-
} else if (b["type"] === "tool_result" && typeof b["tool_use_id"] === "string") {
|
|
16638
|
-
const id = b["tool_use_id"];
|
|
16639
|
-
if (!resultTextById.has(id)) {
|
|
16640
|
-
resultTextById.set(id, extractResultText(b["content"]));
|
|
16641
|
-
}
|
|
16642
|
-
} else if (role === "assistant" && b["type"] === "text" && typeof b["text"] === "string") {
|
|
16643
|
-
sawTextBlock = true;
|
|
16644
|
-
turnTextTokens += estimateTokens(b["text"]);
|
|
16645
|
-
}
|
|
16646
|
-
}
|
|
16647
|
-
if (role === "assistant" && sawTextBlock) assistantTurns.push(turnTextTokens);
|
|
16648
|
-
}
|
|
16649
|
-
return { calls, resultTextById, assistantTurns, resident };
|
|
16650
|
-
}
|
|
16651
|
-
function costPerCall(parsed) {
|
|
16652
|
-
return parsed.calls.map((c) => ({
|
|
16653
|
-
...c,
|
|
16654
|
-
tokens: estimateTokens(parsed.resultTextById.get(c.id) ?? "")
|
|
16655
|
-
}));
|
|
16656
|
-
}
|
|
16657
|
-
function aggregateSortedDesc(pairs) {
|
|
16658
|
-
return [...pairs].map(([key, tokens]) => ({ key, tokens })).sort((a, b) => b.tokens - a.tokens);
|
|
16659
|
-
}
|
|
16660
|
-
function tokensByTool(costs) {
|
|
16661
|
-
const m = /* @__PURE__ */ new Map();
|
|
16662
|
-
for (const c of costs) m.set(c.name, (m.get(c.name) ?? 0) + c.tokens);
|
|
16663
|
-
return aggregateSortedDesc(m);
|
|
16664
|
-
}
|
|
16665
|
-
function tokensByFile(costs) {
|
|
16666
|
-
const m = /* @__PURE__ */ new Map();
|
|
16667
|
-
for (const c of costs) {
|
|
16668
|
-
if (c.filePath === null) continue;
|
|
16669
|
-
m.set(c.filePath, (m.get(c.filePath) ?? 0) + c.tokens);
|
|
16670
|
-
}
|
|
16671
|
-
return aggregateSortedDesc(m);
|
|
16672
|
-
}
|
|
16673
|
-
function topExpensiveCalls(costs, n) {
|
|
16674
|
-
return [...costs].sort((a, b) => b.tokens - a.tokens).slice(0, n);
|
|
16675
|
-
}
|
|
16676
|
-
function neverTouchedAgain(costs) {
|
|
16677
|
-
const readsByFile = /* @__PURE__ */ new Map();
|
|
16678
|
-
for (const c of costs) {
|
|
16679
|
-
if (c.name !== "Read" || c.filePath === null) continue;
|
|
16680
|
-
const arr = readsByFile.get(c.filePath) ?? [];
|
|
16681
|
-
arr.push(c);
|
|
16682
|
-
readsByFile.set(c.filePath, arr);
|
|
16683
|
-
}
|
|
16684
|
-
const out2 = [];
|
|
16685
|
-
for (const [filePath, reads] of readsByFile) {
|
|
16686
|
-
const firstSeq = Math.min(...reads.map((r) => r.seq));
|
|
16687
|
-
const touchedLater = costs.some((c) => {
|
|
16688
|
-
if (c.seq <= firstSeq) return false;
|
|
16689
|
-
if (c.filePath === filePath) return true;
|
|
16690
|
-
return safeStringify(c.input).includes(filePath);
|
|
16691
|
-
});
|
|
16692
|
-
if (!touchedLater) {
|
|
16693
|
-
out2.push({ filePath, tokens: reads.reduce((n, r) => n + r.tokens, 0) });
|
|
16694
|
-
}
|
|
16695
|
-
}
|
|
16696
|
-
return out2.sort((a, b) => b.tokens - a.tokens);
|
|
16697
|
-
}
|
|
16698
|
-
async function repeatedUncompressedBashCommands(costs) {
|
|
16699
|
-
const groups = /* @__PURE__ */ new Map();
|
|
16700
|
-
for (const c of costs) {
|
|
16701
|
-
if (c.name !== "Bash" || c.command === null) continue;
|
|
16702
|
-
const norm = normalizeCommandForCacheKey(c.command);
|
|
16703
|
-
const arr = groups.get(norm) ?? [];
|
|
16704
|
-
arr.push(c);
|
|
16705
|
-
groups.set(norm, arr);
|
|
16706
|
-
}
|
|
16707
|
-
const out2 = [];
|
|
16708
|
-
for (const [norm, calls] of groups) {
|
|
16709
|
-
if (calls.length < 2) continue;
|
|
16710
|
-
let cached = false;
|
|
16711
|
-
for (const c of calls) {
|
|
16712
|
-
try {
|
|
16713
|
-
const hash = await commandHash(c.command, c.cwd);
|
|
16714
|
-
if (getBashOutput(hash) !== null) {
|
|
16715
|
-
cached = true;
|
|
16716
|
-
break;
|
|
16717
|
-
}
|
|
16718
|
-
} catch {
|
|
16719
|
-
}
|
|
16720
|
-
}
|
|
16721
|
-
if (cached) continue;
|
|
16722
|
-
const totalTokens = calls.reduce((n, c) => n + c.tokens, 0);
|
|
16723
|
-
out2.push({
|
|
16724
|
-
normalized: norm,
|
|
16725
|
-
count: calls.length,
|
|
16726
|
-
totalTokens,
|
|
16727
|
-
avgTokens: Math.round(totalTokens / calls.length)
|
|
16728
|
-
});
|
|
16729
|
-
}
|
|
16730
|
-
return out2.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
16731
|
-
}
|
|
16732
|
-
function assistantOutputCost(assistantTurns) {
|
|
16733
|
-
const turnCount = assistantTurns.length;
|
|
16734
|
-
let generatedTokens = 0;
|
|
16735
|
-
let resendCeilingTokens = 0;
|
|
16736
|
-
for (let i = 0; i < turnCount; i++) {
|
|
16737
|
-
const tokens = assistantTurns[i] ?? 0;
|
|
16738
|
-
generatedTokens += tokens;
|
|
16739
|
-
resendCeilingTokens += tokens * (turnCount - 1 - i);
|
|
16740
|
-
}
|
|
16741
|
-
return { turnCount, generatedTokens, resendCeilingTokens };
|
|
16742
|
-
}
|
|
16743
|
-
async function buildWasteReport(transcriptPath, opts = {}) {
|
|
16744
|
-
const parsed = parseTranscript2(transcriptPath);
|
|
16745
|
-
const costs = costPerCall(parsed);
|
|
16746
|
-
const topN = opts.topN ?? 10;
|
|
16747
|
-
return {
|
|
16748
|
-
transcriptPath,
|
|
16749
|
-
totalTokens: costs.reduce((n, c) => n + c.tokens, 0),
|
|
16750
|
-
tokensByTool: tokensByTool(costs),
|
|
16751
|
-
tokensByFile: tokensByFile(costs),
|
|
16752
|
-
topCalls: topExpensiveCalls(costs, topN).map((c) => ({ seq: c.seq, name: c.name, summary: c.summary, tokens: c.tokens })),
|
|
16753
|
-
neverTouchedAgain: neverTouchedAgain(costs),
|
|
16754
|
-
repeatedUncompressedBash: await repeatedUncompressedBashCommands(costs),
|
|
16755
|
-
assistantOutput: assistantOutputCost(parsed.assistantTurns),
|
|
16756
|
-
residentContext: summarizeResidentContext(parsed.resident)
|
|
16757
|
-
};
|
|
16758
|
-
}
|
|
16759
|
-
|
|
16760
|
-
// src/session_read.ts
|
|
17122
|
+
import * as readline2 from "node:readline";
|
|
16761
17123
|
function resolveSessionTranscript(arg, opts = {}) {
|
|
16762
17124
|
if (arg !== void 0 && arg !== "") {
|
|
16763
|
-
if (
|
|
17125
|
+
if (fs35.existsSync(arg) && fs35.statSync(arg).isFile()) return arg;
|
|
16764
17126
|
const projectRoot2 = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
16765
17127
|
const dir = projectTranscriptsDir(projectRoot2);
|
|
16766
17128
|
const byId = arg.endsWith(".jsonl") ? arg : `${arg}.jsonl`;
|
|
16767
|
-
const candidate =
|
|
16768
|
-
if (
|
|
17129
|
+
const candidate = path32.join(dir, byId);
|
|
17130
|
+
if (fs35.existsSync(candidate) && fs35.statSync(candidate).isFile()) return candidate;
|
|
16769
17131
|
return null;
|
|
16770
17132
|
}
|
|
16771
17133
|
const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
@@ -16823,7 +17185,7 @@ function toolCallsForBlocks(blocks) {
|
|
|
16823
17185
|
return blocks.filter((b) => b.type === "tool_use" && b.name !== void 0).map((b) => b.name);
|
|
16824
17186
|
}
|
|
16825
17187
|
async function* streamTurns(transcriptPath) {
|
|
16826
|
-
const input =
|
|
17188
|
+
const input = fs35.createReadStream(transcriptPath, { encoding: "utf8" });
|
|
16827
17189
|
const rl = readline2.createInterface({ input, crlfDelay: Infinity });
|
|
16828
17190
|
try {
|
|
16829
17191
|
let lineNumber = 0;
|
|
@@ -16922,9 +17284,9 @@ function formatSessionSlice(turns) {
|
|
|
16922
17284
|
|
|
16923
17285
|
// src/cli_bootstrap_audit.ts
|
|
16924
17286
|
init_define_import_meta_env();
|
|
16925
|
-
import * as
|
|
16926
|
-
import * as
|
|
16927
|
-
import * as
|
|
17287
|
+
import * as fs36 from "node:fs";
|
|
17288
|
+
import * as os17 from "node:os";
|
|
17289
|
+
import * as path33 from "node:path";
|
|
16928
17290
|
import * as readline3 from "node:readline";
|
|
16929
17291
|
var FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["description", "tools", "allowed-tools", "allowed_tools"]);
|
|
16930
17292
|
function parseBudget(name, value) {
|
|
@@ -16933,7 +17295,7 @@ function parseBudget(name, value) {
|
|
|
16933
17295
|
return value;
|
|
16934
17296
|
}
|
|
16935
17297
|
async function readFrontmatter(filePath) {
|
|
16936
|
-
const stream =
|
|
17298
|
+
const stream = fs36.createReadStream(filePath, { encoding: "utf8" });
|
|
16937
17299
|
const lines = readline3.createInterface({ input: stream, crlfDelay: Infinity });
|
|
16938
17300
|
let first = true;
|
|
16939
17301
|
let inFrontmatter = false;
|
|
@@ -16979,44 +17341,44 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
16979
17341
|
let logicalRoot;
|
|
16980
17342
|
let rootIsLink;
|
|
16981
17343
|
try {
|
|
16982
|
-
rootIsLink = (await
|
|
16983
|
-
logicalRoot = await
|
|
17344
|
+
rootIsLink = (await fs36.promises.lstat(root)).isSymbolicLink();
|
|
17345
|
+
logicalRoot = await fs36.promises.realpath(root);
|
|
16984
17346
|
} catch (error) {
|
|
16985
17347
|
if (error.code === "ENOENT") return entries;
|
|
16986
|
-
diagnostics.push({ path:
|
|
17348
|
+
diagnostics.push({ path: path33.resolve(root), reason: "unreadable root" });
|
|
16987
17349
|
return entries;
|
|
16988
17350
|
}
|
|
16989
17351
|
if (rootIsLink && !followLinks) {
|
|
16990
|
-
diagnostics.push({ path:
|
|
17352
|
+
diagnostics.push({ path: path33.resolve(root), reason: "external root link skipped (use --follow-links)" });
|
|
16991
17353
|
return entries;
|
|
16992
17354
|
}
|
|
16993
17355
|
const canonicalKey = (candidate) => process.platform === "win32" ? candidate.toLocaleLowerCase() : candidate;
|
|
16994
17356
|
const isContained = (candidate, allowedRoot) => {
|
|
16995
|
-
const relative3 =
|
|
16996
|
-
return relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${
|
|
17357
|
+
const relative3 = path33.relative(canonicalKey(allowedRoot), canonicalKey(candidate));
|
|
17358
|
+
return relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${path33.sep}`) && !path33.isAbsolute(relative3);
|
|
16997
17359
|
};
|
|
16998
17360
|
async function visit(candidate, allowedRoot, topLevel = false) {
|
|
16999
17361
|
let real;
|
|
17000
17362
|
let candidateIsLink;
|
|
17001
17363
|
try {
|
|
17002
|
-
candidateIsLink = (await
|
|
17003
|
-
real = await
|
|
17364
|
+
candidateIsLink = (await fs36.promises.lstat(candidate)).isSymbolicLink();
|
|
17365
|
+
real = await fs36.promises.realpath(candidate);
|
|
17004
17366
|
} catch {
|
|
17005
|
-
diagnostics.push({ path:
|
|
17367
|
+
diagnostics.push({ path: path33.resolve(candidate), reason: "broken or unreadable link" });
|
|
17006
17368
|
return;
|
|
17007
17369
|
}
|
|
17008
17370
|
const withinAllowedRoot = allowedRoot === void 0 ? isContained(real, logicalRoot) : isContained(real, allowedRoot);
|
|
17009
17371
|
const canFollowExternal = followLinks && topLevel;
|
|
17010
17372
|
if (!withinAllowedRoot && !canFollowExternal) {
|
|
17011
|
-
diagnostics.push({ path:
|
|
17373
|
+
diagnostics.push({ path: path33.resolve(candidate), reason: candidateIsLink ? "external link skipped (use --follow-links)" : "canonical target outside scan root" });
|
|
17012
17374
|
return;
|
|
17013
17375
|
}
|
|
17014
17376
|
const trustedRoot = !withinAllowedRoot && canFollowExternal ? real : allowedRoot ?? real;
|
|
17015
17377
|
let stat;
|
|
17016
17378
|
try {
|
|
17017
|
-
stat = await
|
|
17379
|
+
stat = await fs36.promises.stat(real);
|
|
17018
17380
|
} catch {
|
|
17019
|
-
diagnostics.push({ path:
|
|
17381
|
+
diagnostics.push({ path: path33.resolve(candidate), reason: "unreadable" });
|
|
17020
17382
|
return;
|
|
17021
17383
|
}
|
|
17022
17384
|
const canonical = process.platform === "win32" ? real.toLocaleLowerCase() : real;
|
|
@@ -17025,16 +17387,16 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
17025
17387
|
visitedDirs.add(canonical);
|
|
17026
17388
|
let children;
|
|
17027
17389
|
try {
|
|
17028
|
-
children = await
|
|
17390
|
+
children = await fs36.promises.readdir(real, { withFileTypes: true });
|
|
17029
17391
|
} catch {
|
|
17030
|
-
diagnostics.push({ path:
|
|
17392
|
+
diagnostics.push({ path: path33.resolve(candidate), reason: "unreadable directory" });
|
|
17031
17393
|
return;
|
|
17032
17394
|
}
|
|
17033
17395
|
children.sort((a, b) => a.name.localeCompare(b.name));
|
|
17034
|
-
for (const child of children) await visit(
|
|
17396
|
+
for (const child of children) await visit(path33.join(real, child.name), trustedRoot);
|
|
17035
17397
|
return;
|
|
17036
17398
|
}
|
|
17037
|
-
if (!stat.isFile() ||
|
|
17399
|
+
if (!stat.isFile() || path33.extname(real).toLowerCase() !== ".md") return;
|
|
17038
17400
|
if (seenFiles.has(canonical)) return;
|
|
17039
17401
|
seenFiles.add(canonical);
|
|
17040
17402
|
try {
|
|
@@ -17044,35 +17406,35 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
|
|
|
17044
17406
|
const toolsBytes = Buffer.byteLength(metadata.tools, "utf8");
|
|
17045
17407
|
entries.push({
|
|
17046
17408
|
kind,
|
|
17047
|
-
path:
|
|
17409
|
+
path: path33.resolve(real),
|
|
17048
17410
|
description_bytes: descriptionBytes,
|
|
17049
17411
|
tools_bytes: toolsBytes,
|
|
17050
17412
|
metadata_bytes: descriptionBytes + toolsBytes,
|
|
17051
17413
|
estimated_tokens: Math.floor((descriptionBytes + toolsBytes) / 4)
|
|
17052
17414
|
});
|
|
17053
17415
|
} catch {
|
|
17054
|
-
diagnostics.push({ path:
|
|
17416
|
+
diagnostics.push({ path: path33.resolve(candidate), reason: "unreadable file" });
|
|
17055
17417
|
}
|
|
17056
17418
|
}
|
|
17057
17419
|
try {
|
|
17058
|
-
const children = await
|
|
17420
|
+
const children = await fs36.promises.readdir(logicalRoot, { withFileTypes: true });
|
|
17059
17421
|
children.sort((a, b) => a.name.localeCompare(b.name));
|
|
17060
|
-
for (const child of children) await visit(
|
|
17422
|
+
for (const child of children) await visit(path33.join(logicalRoot, child.name), void 0, true);
|
|
17061
17423
|
} catch {
|
|
17062
|
-
diagnostics.push({ path:
|
|
17424
|
+
diagnostics.push({ path: path33.resolve(root), reason: "unreadable root" });
|
|
17063
17425
|
}
|
|
17064
17426
|
return entries;
|
|
17065
17427
|
}
|
|
17066
17428
|
async function buildBootstrapAudit(opts = {}) {
|
|
17067
17429
|
const project = resolveProjectRoot(opts.project === void 0 ? {} : { project: opts.project });
|
|
17068
|
-
const home =
|
|
17430
|
+
const home = path33.resolve(opts.home ?? os17.homedir());
|
|
17069
17431
|
const context = buildStats(project, home, opts.project === void 0 ? [] : [opts.project]);
|
|
17070
17432
|
const diagnostics = [];
|
|
17071
17433
|
const top = opts.top === void 0 ? 10 : parseBudget("--top", opts.top) ?? 10;
|
|
17072
17434
|
const visitedDirs = /* @__PURE__ */ new Set();
|
|
17073
17435
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
17074
|
-
const agents = await scanMetadataRoot(
|
|
17075
|
-
const skills = await scanMetadataRoot(
|
|
17436
|
+
const agents = await scanMetadataRoot(path33.join(claudeConfigDir(home), "agents"), "agent", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
|
|
17437
|
+
const skills = await scanMetadataRoot(path33.join(claudeConfigDir(home), "skills"), "skill", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
|
|
17076
17438
|
const rankedEntries = [...agents, ...skills].sort((a, b) => b.metadata_bytes - a.metadata_bytes || a.path.localeCompare(b.path));
|
|
17077
17439
|
const largestTotal = rankedEntries.length;
|
|
17078
17440
|
const largest = rankedEntries.slice(0, top);
|
|
@@ -17139,8 +17501,8 @@ async function runBootstrapAudit(opts = {}) {
|
|
|
17139
17501
|
|
|
17140
17502
|
// src/cli_memory.ts
|
|
17141
17503
|
init_define_import_meta_env();
|
|
17142
|
-
import * as
|
|
17143
|
-
import * as
|
|
17504
|
+
import * as fs37 from "node:fs";
|
|
17505
|
+
import * as path34 from "node:path";
|
|
17144
17506
|
function removeExactDupLines(text, report) {
|
|
17145
17507
|
if (report.exactDupLines.length === 0) return text;
|
|
17146
17508
|
const toRemove = new Set(report.exactDupLines.map(([, dupIdx]) => dupIdx));
|
|
@@ -17198,7 +17560,7 @@ function printReport(reports, clusters) {
|
|
|
17198
17560
|
} else {
|
|
17199
17561
|
for (const cluster of clusters) {
|
|
17200
17562
|
w(
|
|
17201
|
-
` [${cluster.method}, similarity ${cluster.similarity}, ~${cluster.tokens} tok] ${cluster.members.map((m) => displaySafeText(
|
|
17563
|
+
` [${cluster.method}, similarity ${cluster.similarity}, ~${cluster.tokens} tok] ${cluster.members.map((m) => displaySafeText(path34.basename(m))).join(", ")}
|
|
17202
17564
|
`
|
|
17203
17565
|
);
|
|
17204
17566
|
}
|
|
@@ -17213,7 +17575,7 @@ async function runMemoryCommand(opts = {}) {
|
|
|
17213
17575
|
const claudeMds = findClaudeMdFiles(projectRoot);
|
|
17214
17576
|
const reports = auditClaudeMd(claudeMds);
|
|
17215
17577
|
const memoryMdPath = findMemoryMd(projectRoot);
|
|
17216
|
-
const clusters = memoryMdPath !== null ? await findContentDuplicates(
|
|
17578
|
+
const clusters = memoryMdPath !== null ? await findContentDuplicates(path34.dirname(memoryMdPath)) : [];
|
|
17217
17579
|
printReport(reports, clusters);
|
|
17218
17580
|
if (opts.fix !== true) return;
|
|
17219
17581
|
const changes = [];
|
|
@@ -17221,7 +17583,7 @@ async function runMemoryCommand(opts = {}) {
|
|
|
17221
17583
|
if (report.exactDupLines.length === 0) continue;
|
|
17222
17584
|
let before;
|
|
17223
17585
|
try {
|
|
17224
|
-
before =
|
|
17586
|
+
before = fs37.readFileSync(report.path, "utf-8");
|
|
17225
17587
|
} catch {
|
|
17226
17588
|
continue;
|
|
17227
17589
|
}
|
|
@@ -17259,13 +17621,13 @@ async function runMemoryCommand(opts = {}) {
|
|
|
17259
17621
|
|
|
17260
17622
|
// src/cli_waste.ts
|
|
17261
17623
|
init_define_import_meta_env();
|
|
17262
|
-
import * as
|
|
17263
|
-
import * as
|
|
17624
|
+
import * as fs39 from "node:fs";
|
|
17625
|
+
import * as path36 from "node:path";
|
|
17264
17626
|
|
|
17265
17627
|
// src/copilot_waste.ts
|
|
17266
17628
|
init_define_import_meta_env();
|
|
17267
|
-
import * as
|
|
17268
|
-
import * as
|
|
17629
|
+
import * as fs38 from "node:fs";
|
|
17630
|
+
import * as path35 from "node:path";
|
|
17269
17631
|
var HOOK_RECORD_TYPES = /* @__PURE__ */ new Set(["hook.start", "hook.end"]);
|
|
17270
17632
|
function splitInjectedBlocks(transformed) {
|
|
17271
17633
|
const out2 = [];
|
|
@@ -17284,19 +17646,19 @@ function splitInjectedBlocks(transformed) {
|
|
|
17284
17646
|
return out2;
|
|
17285
17647
|
}
|
|
17286
17648
|
function findLatestCopilotSession() {
|
|
17287
|
-
const root =
|
|
17649
|
+
const root = path35.join(copilotCliUserRoot(), "session-state");
|
|
17288
17650
|
let entries;
|
|
17289
17651
|
try {
|
|
17290
|
-
entries =
|
|
17652
|
+
entries = fs38.readdirSync(root);
|
|
17291
17653
|
} catch {
|
|
17292
17654
|
return null;
|
|
17293
17655
|
}
|
|
17294
17656
|
let best = null;
|
|
17295
17657
|
let bestMtime = -Infinity;
|
|
17296
17658
|
for (const entry of entries) {
|
|
17297
|
-
const candidate =
|
|
17659
|
+
const candidate = path35.join(root, entry, "events.jsonl");
|
|
17298
17660
|
try {
|
|
17299
|
-
const st =
|
|
17661
|
+
const st = fs38.statSync(candidate);
|
|
17300
17662
|
if (!st.isFile()) continue;
|
|
17301
17663
|
if (st.mtimeMs > bestMtime) {
|
|
17302
17664
|
bestMtime = st.mtimeMs;
|
|
@@ -17312,10 +17674,10 @@ function readNumber(source, key) {
|
|
|
17312
17674
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
17313
17675
|
}
|
|
17314
17676
|
function buildCopilotWasteReport(eventsPath) {
|
|
17315
|
-
const raw =
|
|
17677
|
+
const raw = fs38.readFileSync(eventsPath, "utf-8");
|
|
17316
17678
|
const report = {
|
|
17317
17679
|
sessionPath: eventsPath,
|
|
17318
|
-
sessionId:
|
|
17680
|
+
sessionId: path35.basename(path35.dirname(eventsPath)),
|
|
17319
17681
|
turns: 0,
|
|
17320
17682
|
tokens: null,
|
|
17321
17683
|
blocks: [],
|
|
@@ -17594,8 +17956,8 @@ function printResidentContext(resident, w) {
|
|
|
17594
17956
|
}
|
|
17595
17957
|
async function runWasteCommand(opts = {}) {
|
|
17596
17958
|
if (opts.copilot === true) {
|
|
17597
|
-
const eventsPath = opts.transcript !== void 0 ?
|
|
17598
|
-
if (eventsPath === null || !
|
|
17959
|
+
const eventsPath = opts.transcript !== void 0 ? path36.resolve(opts.transcript) : findLatestCopilotSession();
|
|
17960
|
+
if (eventsPath === null || !fs39.existsSync(eventsPath)) {
|
|
17599
17961
|
const detail = eventsPath === null ? "no Copilot CLI session found under <copilot-home>/session-state" : `Copilot session event log not found: ${eventsPath}`;
|
|
17600
17962
|
if (opts.json === true) {
|
|
17601
17963
|
process.stdout.write(`${displaySafeJson({ error: detail }, 0)}
|
|
@@ -17618,7 +17980,7 @@ async function runWasteCommand(opts = {}) {
|
|
|
17618
17980
|
return;
|
|
17619
17981
|
}
|
|
17620
17982
|
const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
|
|
17621
|
-
const transcriptPath = opts.transcript !== void 0 ?
|
|
17983
|
+
const transcriptPath = opts.transcript !== void 0 ? path36.resolve(opts.transcript) : findLatestTranscript(projectRoot);
|
|
17622
17984
|
if (transcriptPath === null) {
|
|
17623
17985
|
if (opts.json === true) {
|
|
17624
17986
|
process.stdout.write(`${displaySafeJson({ error: "no session transcript found", project: projectRoot }, 0)}
|
|
@@ -17632,7 +17994,7 @@ async function runWasteCommand(opts = {}) {
|
|
|
17632
17994
|
process.exitCode = 1;
|
|
17633
17995
|
return;
|
|
17634
17996
|
}
|
|
17635
|
-
if (!
|
|
17997
|
+
if (!fs39.existsSync(transcriptPath)) {
|
|
17636
17998
|
process.stderr.write(`token-goat: transcript not found: ${transcriptPath}
|
|
17637
17999
|
`);
|
|
17638
18000
|
process.exitCode = 1;
|
|
@@ -17649,13 +18011,13 @@ async function runWasteCommand(opts = {}) {
|
|
|
17649
18011
|
|
|
17650
18012
|
// src/cli_mcp_audit.ts
|
|
17651
18013
|
init_define_import_meta_env();
|
|
17652
|
-
import * as
|
|
17653
|
-
import * as
|
|
17654
|
-
import * as
|
|
18014
|
+
import * as fs40 from "node:fs";
|
|
18015
|
+
import * as os18 from "node:os";
|
|
18016
|
+
import * as path37 from "node:path";
|
|
17655
18017
|
function readMcpJsonFile(configPath2) {
|
|
17656
18018
|
try {
|
|
17657
|
-
if (!
|
|
17658
|
-
const content =
|
|
18019
|
+
if (!fs40.existsSync(configPath2)) return null;
|
|
18020
|
+
const content = fs40.readFileSync(configPath2, "utf-8");
|
|
17659
18021
|
const parsed = JSON.parse(content);
|
|
17660
18022
|
const servers = parsed && typeof parsed === "object" ? parsed.mcpServers ?? ("servers" in parsed ? null : parsed) : null;
|
|
17661
18023
|
return servers && typeof servers === "object" ? servers : null;
|
|
@@ -17671,8 +18033,8 @@ function driveLetterCaseVariants(p) {
|
|
|
17671
18033
|
}
|
|
17672
18034
|
function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
|
|
17673
18035
|
try {
|
|
17674
|
-
if (!
|
|
17675
|
-
const parsed = JSON.parse(
|
|
18036
|
+
if (!fs40.existsSync(claudeJsonPath)) return null;
|
|
18037
|
+
const parsed = JSON.parse(fs40.readFileSync(claudeJsonPath, "utf-8"));
|
|
17676
18038
|
const projects = parsed && typeof parsed === "object" ? parsed.projects : null;
|
|
17677
18039
|
if (!projects || typeof projects !== "object") return null;
|
|
17678
18040
|
const slashForms = [projectRoot, projectRoot.replace(/\\/g, "/"), projectRoot.replace(/\//g, "\\")];
|
|
@@ -17690,9 +18052,9 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
|
|
|
17690
18052
|
}
|
|
17691
18053
|
}
|
|
17692
18054
|
function discoverMcpConfig(projectRoot, home) {
|
|
17693
|
-
const mcpJsonPath =
|
|
17694
|
-
const claudeJsonPath =
|
|
17695
|
-
const copilotJsonPath =
|
|
18055
|
+
const mcpJsonPath = path37.join(projectRoot, ".mcp.json");
|
|
18056
|
+
const claudeJsonPath = path37.join(home, ".claude.json");
|
|
18057
|
+
const copilotJsonPath = path37.join(home, ".copilot", "mcp-config.json");
|
|
17696
18058
|
const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
|
|
17697
18059
|
const fromMcpJson = readMcpJsonFile(mcpJsonPath);
|
|
17698
18060
|
if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
|
|
@@ -17723,7 +18085,7 @@ function analyzeMcpCache() {
|
|
|
17723
18085
|
}
|
|
17724
18086
|
return serverMetrics;
|
|
17725
18087
|
}
|
|
17726
|
-
function buildMcpAuditReport(projectRoot, home =
|
|
18088
|
+
function buildMcpAuditReport(projectRoot, home = os18.homedir()) {
|
|
17727
18089
|
const discovery = discoverMcpConfig(projectRoot, home);
|
|
17728
18090
|
const config = discovery.servers;
|
|
17729
18091
|
const cacheMetrics = analyzeMcpCache();
|
|
@@ -17809,69 +18171,6 @@ async function runMcpAuditCommand(opts = {}) {
|
|
|
17809
18171
|
printReport3(report);
|
|
17810
18172
|
}
|
|
17811
18173
|
|
|
17812
|
-
// src/cli_recall.ts
|
|
17813
|
-
init_define_import_meta_env();
|
|
17814
|
-
var RECALL_COMMAND = {
|
|
17815
|
-
bash: "bash-output",
|
|
17816
|
-
web: "web-output",
|
|
17817
|
-
mcp: "mcp-output"
|
|
17818
|
-
};
|
|
17819
|
-
function fenceTagForCacheType(cacheType) {
|
|
17820
|
-
return cacheType === "web" ? UNTRUSTED_WEB_TAG : UNTRUSTED_TOOL_TAG;
|
|
17821
|
-
}
|
|
17822
|
-
function fenceRecallListing(text, hits) {
|
|
17823
|
-
const tags = new Set(hits.map((hit) => fenceTagForCacheType(hit.cacheType)));
|
|
17824
|
-
const tag = tags.size === 1 ? [...tags][0] ?? UNTRUSTED_TOOL_TAG : UNTRUSTED_TOOL_TAG;
|
|
17825
|
-
return fenceUntrusted(text, tag);
|
|
17826
|
-
}
|
|
17827
|
-
function fenceSnippetIfMatched(hit) {
|
|
17828
|
-
const matches2 = scanAndRecord(hit.snippet);
|
|
17829
|
-
if (matches2.length === 0) return hit.snippet;
|
|
17830
|
-
return fenceUntrustedContent(hit.snippet, matches2, fenceTagForCacheType(hit.cacheType));
|
|
17831
|
-
}
|
|
17832
|
-
function printHits(query, hits) {
|
|
17833
|
-
const w = (text) => {
|
|
17834
|
-
process.stdout.write(text);
|
|
17835
|
-
};
|
|
17836
|
-
if (hits.length === 0) {
|
|
17837
|
-
w(query === void 0 ? "No cache entries yet.\n" : `No cache entries match: ${query}
|
|
17838
|
-
`);
|
|
17839
|
-
return;
|
|
17840
|
-
}
|
|
17841
|
-
const body = hits.map((hit) => {
|
|
17842
|
-
const label = hit.label.length > 80 ? hit.label.slice(0, 77) + "..." : hit.label;
|
|
17843
|
-
const pointer = hit.blobPresent ? ` (token-goat ${RECALL_COMMAND[hit.cacheType]} ${hit.id})` : " (blob expired -- content below is all that remains)";
|
|
17844
|
-
return `[${pad(hit.cacheType, 4)}] ${hit.id}
|
|
17845
|
-
${pointer}
|
|
17846
|
-
${label}
|
|
17847
|
-
${hit.snippet}
|
|
17848
|
-
`;
|
|
17849
|
-
}).join("\n");
|
|
17850
|
-
w(`${fenceRecallListing(body, hits)}
|
|
17851
|
-
`);
|
|
17852
|
-
}
|
|
17853
|
-
function runRecallCommand(query, opts = {}) {
|
|
17854
|
-
const browse = query === void 0 || query.trim() === "";
|
|
17855
|
-
const scope = {
|
|
17856
|
-
...opts.type !== void 0 ? { type: opts.type } : {},
|
|
17857
|
-
...opts.limit !== void 0 ? { limit: opts.limit } : {}
|
|
17858
|
-
};
|
|
17859
|
-
const effectiveLimit = opts.limit ?? RECALL_DEFAULT_LIMIT;
|
|
17860
|
-
const fetched = browse ? listRecentRecall({ ...scope, limit: effectiveLimit + 1 }) : searchRecall(query, { ...scope, limit: effectiveLimit + 1 });
|
|
17861
|
-
const hits = fetched.slice(0, effectiveLimit);
|
|
17862
|
-
if (fetched.length > hits.length) {
|
|
17863
|
-
process.stderr.write(`Showing ${hits.length} entries; more are available (raise --limit to see them).
|
|
17864
|
-
`);
|
|
17865
|
-
}
|
|
17866
|
-
if (opts.json === true) {
|
|
17867
|
-
const fenced = hits.map((hit) => ({ ...hit, snippet: fenceSnippetIfMatched(hit) }));
|
|
17868
|
-
process.stdout.write(`${displaySafeJson(fenced, 0)}
|
|
17869
|
-
`);
|
|
17870
|
-
return;
|
|
17871
|
-
}
|
|
17872
|
-
printHits(browse ? void 0 : query, hits);
|
|
17873
|
-
}
|
|
17874
|
-
|
|
17875
18174
|
// src/cli_statusline.ts
|
|
17876
18175
|
init_define_import_meta_env();
|
|
17877
18176
|
var STDIN_TIMEOUT_MS = 1500;
|
|
@@ -17967,14 +18266,20 @@ function efficacyCell(row) {
|
|
|
17967
18266
|
const pct3 = row.efficacyPct === null ? "n/a" : `${row.efficacyPct}%`;
|
|
17968
18267
|
return isSuppressionCategory(row.category) ? `${pct3} *` : pct3;
|
|
17969
18268
|
}
|
|
18269
|
+
function emittedCell(row) {
|
|
18270
|
+
return row.unobservable > 0 ? `${row.emitted} ~${row.unobservable}` : String(row.emitted);
|
|
18271
|
+
}
|
|
18272
|
+
function detectedCell(row) {
|
|
18273
|
+
return row.detected === 0 ? "-" : String(row.detected);
|
|
18274
|
+
}
|
|
17970
18275
|
function printSummary(rows) {
|
|
17971
18276
|
const w = (text) => {
|
|
17972
18277
|
process.stdout.write(text);
|
|
17973
18278
|
};
|
|
17974
|
-
w(pad("category", 22) + pad("emitted", 9) + pad("acted-on", 10) + pad("efficacy", 12) + pad("suppressed", 17) + pad("manual+", 9) + pad("manual-", 9) + "spent\n");
|
|
18279
|
+
w(pad("category", 22) + pad("emitted", 9) + pad("undisplayed", 13) + pad("acted-on", 10) + pad("efficacy", 12) + pad("suppressed", 17) + pad("manual+", 9) + pad("manual-", 9) + "spent\n");
|
|
17975
18280
|
for (const row of rows) {
|
|
17976
18281
|
w(
|
|
17977
|
-
pad(row.category, 22) + pad(
|
|
18282
|
+
pad(row.category, 22) + pad(emittedCell(row), 9) + pad(detectedCell(row), 13) + pad(String(row.actedOn), 10) + pad(efficacyCell(row), 12) + pad(suppressedCell(row), 17) + pad(String(row.manualEffective), 9) + pad(String(row.manualIneffective), 9) + formatSpentCell(row) + "\n"
|
|
17978
18283
|
);
|
|
17979
18284
|
}
|
|
17980
18285
|
}
|
|
@@ -18011,7 +18316,7 @@ function runHintStatsCommand(opts = {}) {
|
|
|
18011
18316
|
`);
|
|
18012
18317
|
return;
|
|
18013
18318
|
}
|
|
18014
|
-
if (rows.every((r) => r.emitted === 0 && r.actedOn === 0)) {
|
|
18319
|
+
if (rows.every((r) => r.emitted === 0 && r.actedOn === 0 && r.unobservable === 0 && r.detected === 0)) {
|
|
18015
18320
|
process.stdout.write("No hint emissions recorded yet \u2014 the zeros below are absence of data, not measured ineffectiveness.\n");
|
|
18016
18321
|
}
|
|
18017
18322
|
printSummary(rows);
|
|
@@ -18020,6 +18325,16 @@ function runHintStatsCommand(opts = {}) {
|
|
|
18020
18325
|
'\n* Scored on an absence: these hints ask the agent NOT to do something, so a window that expires with no re-read counts as compliance. Their percentage is not comparable with the unmarked rows, which only count when the agent runs the command the hint named. A high starred figure means "the warned-against read was not seen", not "this hint persuaded anyone".\n'
|
|
18021
18326
|
);
|
|
18022
18327
|
}
|
|
18328
|
+
if (rows.some((r) => r.detected > 0)) {
|
|
18329
|
+
process.stdout.write(
|
|
18330
|
+
"\nundisplayed: detections that never reached the agent -- auto-suppressed, or declined by the hint's own net-benefit gate because the substitute it would have proposed was not smaller than the read it was redirecting. They cost nothing and are scored nowhere. A large figure against a small emitted count means the trigger is alive and the mute is doing the work; a `-` means it has gone quiet on its own.\n"
|
|
18331
|
+
);
|
|
18332
|
+
}
|
|
18333
|
+
if (rows.some((r) => r.unobservable > 0)) {
|
|
18334
|
+
process.stdout.write(
|
|
18335
|
+
"\n~N: N further emissions in that category carried no correlator -- no pointer a later command could have matched -- so no verdict about them was ever observed. They are not in the emitted or acted-on counts and not in the efficacy figure, which is scored only on the rows that could have gone either way. They ARE in the spend column: an unobservable hint costs the agent its bytes all the same.\n"
|
|
18336
|
+
);
|
|
18337
|
+
}
|
|
18023
18338
|
const permanent = rows.filter((r) => r.suppressionPermanent);
|
|
18024
18339
|
if (permanent.length > 0) {
|
|
18025
18340
|
const names = permanent.map((r) => r.category).join(", ");
|
|
@@ -18085,7 +18400,7 @@ ${formatSessionOutline(turns)}`;
|
|
|
18085
18400
|
}
|
|
18086
18401
|
function sessionTranscriptSize(transcriptPath) {
|
|
18087
18402
|
try {
|
|
18088
|
-
return
|
|
18403
|
+
return fs41.statSync(transcriptPath).size;
|
|
18089
18404
|
} catch {
|
|
18090
18405
|
return 0;
|
|
18091
18406
|
}
|
|
@@ -18145,14 +18460,14 @@ function cmdHintStats(opts = {}) {
|
|
|
18145
18460
|
|
|
18146
18461
|
// src/cli_skills.ts
|
|
18147
18462
|
init_define_import_meta_env();
|
|
18148
|
-
import * as
|
|
18149
|
-
import * as
|
|
18463
|
+
import * as fs42 from "node:fs";
|
|
18464
|
+
import * as path38 from "node:path";
|
|
18150
18465
|
async function cmdSkillBody(name, opts) {
|
|
18151
18466
|
const filePath = await getSkillFilePath(name);
|
|
18152
18467
|
if (filePath === null) {
|
|
18153
18468
|
throw new CliError(`skill '${name}' not found`);
|
|
18154
18469
|
}
|
|
18155
|
-
const body = decodeSource(
|
|
18470
|
+
const body = decodeSource(fs42.readFileSync(filePath));
|
|
18156
18471
|
if (opts.compact === true) {
|
|
18157
18472
|
const emitted = extractCompactFromMarker(body) ?? body;
|
|
18158
18473
|
out(emitted);
|
|
@@ -18179,7 +18494,7 @@ async function cmdSkillCompact(name, opts) {
|
|
|
18179
18494
|
}
|
|
18180
18495
|
let body2;
|
|
18181
18496
|
try {
|
|
18182
|
-
body2 = decodeSource(
|
|
18497
|
+
body2 = decodeSource(fs42.readFileSync(filePath));
|
|
18183
18498
|
} catch {
|
|
18184
18499
|
unresolvable++;
|
|
18185
18500
|
continue;
|
|
@@ -18213,19 +18528,19 @@ async function cmdSkillCompact(name, opts) {
|
|
|
18213
18528
|
if (!opts.path.trim()) {
|
|
18214
18529
|
throw new CliError("--path cannot be empty");
|
|
18215
18530
|
}
|
|
18216
|
-
if (!
|
|
18531
|
+
if (!fs42.existsSync(opts.path)) {
|
|
18217
18532
|
throw new CliError(`skill file not found: ${opts.path}`);
|
|
18218
18533
|
}
|
|
18219
18534
|
try {
|
|
18220
|
-
body =
|
|
18535
|
+
body = fs42.readFileSync(opts.path, "utf-8");
|
|
18221
18536
|
} catch (e) {
|
|
18222
18537
|
if (e.code === "ENOENT") {
|
|
18223
18538
|
throw new CliError(`skill file not found: ${opts.path}`);
|
|
18224
18539
|
}
|
|
18225
18540
|
throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
|
|
18226
18541
|
}
|
|
18227
|
-
cacheName = name ??
|
|
18228
|
-
sourcePath =
|
|
18542
|
+
cacheName = name ?? path38.basename(path38.dirname(path38.resolve(opts.path)));
|
|
18543
|
+
sourcePath = path38.resolve(opts.path);
|
|
18229
18544
|
} else {
|
|
18230
18545
|
if (name === void 0 || !name.trim()) {
|
|
18231
18546
|
throw new CliError("skill-compact requires a <name> or --path <file>");
|
|
@@ -18234,7 +18549,7 @@ async function cmdSkillCompact(name, opts) {
|
|
|
18234
18549
|
if (filePath === null) {
|
|
18235
18550
|
throw new CliError(`skill '${name}' not found`);
|
|
18236
18551
|
}
|
|
18237
|
-
body =
|
|
18552
|
+
body = fs42.readFileSync(filePath, "utf-8");
|
|
18238
18553
|
cacheName = name;
|
|
18239
18554
|
sourcePath = filePath;
|
|
18240
18555
|
}
|
|
@@ -18366,8 +18681,8 @@ async function cmdSkillDiff(name) {
|
|
|
18366
18681
|
}
|
|
18367
18682
|
const newer = versions[0];
|
|
18368
18683
|
const older = versions[1];
|
|
18369
|
-
const newerBody = await
|
|
18370
|
-
const olderBody = await
|
|
18684
|
+
const newerBody = await fs42.promises.readFile(path38.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
|
|
18685
|
+
const olderBody = await fs42.promises.readFile(path38.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
|
|
18371
18686
|
if (newerBody === null || olderBody === null) {
|
|
18372
18687
|
out(`a cached version of '${name}' was evicted while diffing -- try again`);
|
|
18373
18688
|
return;
|
|
@@ -18396,7 +18711,7 @@ async function cmdSkillSection(nameHeading, headingArg) {
|
|
|
18396
18711
|
if (!filePath) {
|
|
18397
18712
|
throw new CliError(`skill '${skillName}' not found`);
|
|
18398
18713
|
}
|
|
18399
|
-
const body = decodeSource(
|
|
18714
|
+
const body = decodeSource(fs42.readFileSync(filePath));
|
|
18400
18715
|
const extracted = extractNamedSection(body, heading);
|
|
18401
18716
|
if (!extracted) {
|
|
18402
18717
|
const allHeadings = listSections(filePath);
|
|
@@ -18501,8 +18816,8 @@ async function buildResumePacket(sessionId) {
|
|
|
18501
18816
|
|
|
18502
18817
|
// src/cli_stats.ts
|
|
18503
18818
|
init_define_import_meta_env();
|
|
18504
|
-
import * as
|
|
18505
|
-
import * as
|
|
18819
|
+
import * as fs43 from "node:fs";
|
|
18820
|
+
import * as path39 from "node:path";
|
|
18506
18821
|
function writeRaw(text) {
|
|
18507
18822
|
const payload = colorStdout() ? text : stripAnsi(text);
|
|
18508
18823
|
process.stdout.write(ensureNewline(payload));
|
|
@@ -18514,8 +18829,8 @@ function formatTopFiles(ranked) {
|
|
|
18514
18829
|
if (ranked.length === 0) return "";
|
|
18515
18830
|
const lines = ["Top files this session:"];
|
|
18516
18831
|
for (const { path: filePath, count } of ranked) {
|
|
18517
|
-
const
|
|
18518
|
-
lines.push(` ${count.toString().padStart(3)}x ${
|
|
18832
|
+
const basename11 = path39.basename(filePath);
|
|
18833
|
+
lines.push(` ${count.toString().padStart(3)}x ${basename11} (${filePath})`);
|
|
18519
18834
|
}
|
|
18520
18835
|
return lines.join("\n");
|
|
18521
18836
|
}
|
|
@@ -18531,12 +18846,12 @@ function renderTopSessionFiles(topN = 5) {
|
|
|
18531
18846
|
}
|
|
18532
18847
|
function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
|
|
18533
18848
|
try {
|
|
18534
|
-
const
|
|
18535
|
-
if (!
|
|
18536
|
-
const files =
|
|
18849
|
+
const dir = overrideSessionsDir ?? sessionsDir();
|
|
18850
|
+
if (!fs43.existsSync(dir)) return "";
|
|
18851
|
+
const files = fs43.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, mtime: fs43.statSync(path39.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).slice(0, 3);
|
|
18537
18852
|
for (const { name } of files) {
|
|
18538
18853
|
try {
|
|
18539
|
-
const raw =
|
|
18854
|
+
const raw = fs43.readFileSync(path39.join(dir, name), "utf-8");
|
|
18540
18855
|
const data = JSON.parse(raw);
|
|
18541
18856
|
const filesList = data["files"];
|
|
18542
18857
|
if (!Array.isArray(filesList)) continue;
|
|
@@ -18600,6 +18915,10 @@ function runStats(opts = {}) {
|
|
|
18600
18915
|
renderMethodology(opts.json === true);
|
|
18601
18916
|
return;
|
|
18602
18917
|
}
|
|
18918
|
+
if (opts.hooks === true) {
|
|
18919
|
+
renderHookLatencyStats(void 0, opts.homeDir);
|
|
18920
|
+
return;
|
|
18921
|
+
}
|
|
18603
18922
|
const window = opts.windowDays ?? 30;
|
|
18604
18923
|
const summary = summarize(window, void 0, opts.homeDir);
|
|
18605
18924
|
if (opts.json === true) {
|
|
@@ -18624,13 +18943,13 @@ function runStats(opts = {}) {
|
|
|
18624
18943
|
}
|
|
18625
18944
|
|
|
18626
18945
|
// src/cache_session_commands.ts
|
|
18627
|
-
function
|
|
18946
|
+
function emitErr4(text) {
|
|
18628
18947
|
process.stderr.write(ensureNewline(text));
|
|
18629
18948
|
}
|
|
18630
18949
|
function capAndNote(rows, limit) {
|
|
18631
18950
|
const shown = rows.slice(0, limit);
|
|
18632
18951
|
if (shown.length < rows.length) {
|
|
18633
|
-
|
|
18952
|
+
emitErr4(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
|
|
18634
18953
|
}
|
|
18635
18954
|
return shown;
|
|
18636
18955
|
}
|
|
@@ -18640,11 +18959,11 @@ function parseLimitOpt(cmdName, limitStr, dflt = 30) {
|
|
|
18640
18959
|
try {
|
|
18641
18960
|
n = requireNonNegativeStrictInt("--limit", limitStr);
|
|
18642
18961
|
} catch (e) {
|
|
18643
|
-
|
|
18962
|
+
emitErr4(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
|
|
18644
18963
|
throw new Error(`invalid --limit: ${limitStr}`, { cause: e });
|
|
18645
18964
|
}
|
|
18646
18965
|
if (n === 0) {
|
|
18647
|
-
|
|
18966
|
+
emitErr4(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
|
|
18648
18967
|
throw new Error(`invalid --limit: ${limitStr}`);
|
|
18649
18968
|
}
|
|
18650
18969
|
return n;
|
|
@@ -18676,9 +18995,10 @@ function cmdBashHistory(opts) {
|
|
|
18676
18995
|
const allItems = blobs.map(({ id, mtime, value }) => {
|
|
18677
18996
|
if (typeof value !== "object" || value === null) return null;
|
|
18678
18997
|
const v = value;
|
|
18998
|
+
if (typeof v["command"] !== "string") return null;
|
|
18679
18999
|
return {
|
|
18680
19000
|
id,
|
|
18681
|
-
command:
|
|
19001
|
+
command: v["command"],
|
|
18682
19002
|
storedAt: typeof v["storedAt"] === "number" ? v["storedAt"] : mtime,
|
|
18683
19003
|
exitCode: typeof v["exitCode"] === "number" ? v["exitCode"] : -1,
|
|
18684
19004
|
sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
|
|
@@ -19415,25 +19735,25 @@ function installVerificationNotice(harness) {
|
|
|
19415
19735
|
|
|
19416
19736
|
// src/purge.ts
|
|
19417
19737
|
init_define_import_meta_env();
|
|
19418
|
-
import * as
|
|
19419
|
-
import * as
|
|
19738
|
+
import * as fs44 from "node:fs";
|
|
19739
|
+
import * as path40 from "node:path";
|
|
19420
19740
|
function directorySize(root) {
|
|
19421
19741
|
let total = 0;
|
|
19422
19742
|
let entries;
|
|
19423
19743
|
try {
|
|
19424
|
-
entries =
|
|
19744
|
+
entries = fs44.readdirSync(root, { withFileTypes: true });
|
|
19425
19745
|
} catch {
|
|
19426
19746
|
return 0;
|
|
19427
19747
|
}
|
|
19428
19748
|
for (const entry of entries) {
|
|
19429
|
-
const full =
|
|
19749
|
+
const full = path40.join(root, entry.name);
|
|
19430
19750
|
if (entry.isDirectory()) {
|
|
19431
19751
|
total += directorySize(full);
|
|
19432
19752
|
continue;
|
|
19433
19753
|
}
|
|
19434
19754
|
if (!entry.isFile()) continue;
|
|
19435
19755
|
try {
|
|
19436
|
-
total +=
|
|
19756
|
+
total += fs44.statSync(full).size;
|
|
19437
19757
|
} catch {
|
|
19438
19758
|
}
|
|
19439
19759
|
}
|
|
@@ -19452,7 +19772,7 @@ function purgeDataDirectories() {
|
|
|
19452
19772
|
}
|
|
19453
19773
|
const bytes = directorySize(root);
|
|
19454
19774
|
try {
|
|
19455
|
-
|
|
19775
|
+
fs44.rmSync(root, { recursive: true, force: true });
|
|
19456
19776
|
result.removed.push({ path: root, bytes });
|
|
19457
19777
|
} catch (e) {
|
|
19458
19778
|
result.failed.push({ path: root, reason: e instanceof Error ? e.message : String(e) });
|
|
@@ -19599,8 +19919,8 @@ async function getSectionContent(fileId, heading, opts = {}) {
|
|
|
19599
19919
|
|
|
19600
19920
|
// src/cli_bench.ts
|
|
19601
19921
|
init_define_import_meta_env();
|
|
19602
|
-
import * as
|
|
19603
|
-
import * as
|
|
19922
|
+
import * as fs45 from "node:fs";
|
|
19923
|
+
import * as path41 from "node:path";
|
|
19604
19924
|
var ControlFilter = class extends ToolFilter {
|
|
19605
19925
|
constructor(name, transform) {
|
|
19606
19926
|
super();
|
|
@@ -19621,13 +19941,13 @@ var CONTROL_FILTERS = {
|
|
|
19621
19941
|
};
|
|
19622
19942
|
var MAX_BENCH_FILE_BYTES = 5 * 1024 * 1024;
|
|
19623
19943
|
function readCaseFile(id, filePath, what) {
|
|
19624
|
-
const bytes =
|
|
19944
|
+
const bytes = fs45.statSync(filePath).size;
|
|
19625
19945
|
if (bytes > MAX_BENCH_FILE_BYTES) {
|
|
19626
19946
|
throw new Error(
|
|
19627
19947
|
`bench case ${id}: ${what} is ${bytes} bytes, over the ${MAX_BENCH_FILE_BYTES}-byte limit for a corpus file (${filePath})`
|
|
19628
19948
|
);
|
|
19629
19949
|
}
|
|
19630
|
-
return
|
|
19950
|
+
return fs45.readFileSync(filePath, "utf8");
|
|
19631
19951
|
}
|
|
19632
19952
|
function parseCase(id, metaPath, outputPath) {
|
|
19633
19953
|
const metaText = readCaseFile(id, metaPath, "metadata");
|
|
@@ -19662,7 +19982,7 @@ function parseCase(id, metaPath, outputPath) {
|
|
|
19662
19982
|
function loadCorpus(dir) {
|
|
19663
19983
|
let entries;
|
|
19664
19984
|
try {
|
|
19665
|
-
entries =
|
|
19985
|
+
entries = fs45.readdirSync(dir);
|
|
19666
19986
|
} catch {
|
|
19667
19987
|
throw new Error(`bench corpus not found: ${dir}`);
|
|
19668
19988
|
}
|
|
@@ -19670,9 +19990,9 @@ function loadCorpus(dir) {
|
|
|
19670
19990
|
for (const entry of entries.sort()) {
|
|
19671
19991
|
if (!entry.endsWith(".json")) continue;
|
|
19672
19992
|
const id = entry.slice(0, -".json".length);
|
|
19673
|
-
const outputPath =
|
|
19674
|
-
if (!
|
|
19675
|
-
cases.push(parseCase(id,
|
|
19993
|
+
const outputPath = path41.join(dir, `${id}.txt`);
|
|
19994
|
+
if (!fs45.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
|
|
19995
|
+
cases.push(parseCase(id, path41.join(dir, entry), outputPath));
|
|
19676
19996
|
}
|
|
19677
19997
|
if (cases.length === 0) throw new Error(`bench corpus is empty: ${dir}`);
|
|
19678
19998
|
return cases;
|
|
@@ -19792,10 +20112,10 @@ function appendTsv(file, report, commit) {
|
|
|
19792
20112
|
report.mustKeepTotal,
|
|
19793
20113
|
report.coveredFilters
|
|
19794
20114
|
].join(" ");
|
|
19795
|
-
const dir =
|
|
19796
|
-
|
|
19797
|
-
const header =
|
|
19798
|
-
|
|
20115
|
+
const dir = path41.dirname(path41.resolve(file));
|
|
20116
|
+
fs45.mkdirSync(dir, { recursive: true });
|
|
20117
|
+
const header = fs45.existsSync(file) ? "" : TSV_HEADER;
|
|
20118
|
+
fs45.appendFileSync(file, header + row + "\n", "utf8");
|
|
19799
20119
|
}
|
|
19800
20120
|
function currentCommit(cwd) {
|
|
19801
20121
|
const opts = cwd === void 0 ? {} : { cwd };
|
|
@@ -19835,10 +20155,10 @@ function readBoundedText(text, file) {
|
|
|
19835
20155
|
if (text === void 0 && file === void 0) throw new CliError("provide text or --file");
|
|
19836
20156
|
let value;
|
|
19837
20157
|
if (file !== void 0) {
|
|
19838
|
-
if (
|
|
20158
|
+
if (fs46.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
|
|
19839
20159
|
throw new CliError(`file exceeds the ${CONTENT_MAX_INPUT_CHARS}-byte safety limit`);
|
|
19840
20160
|
}
|
|
19841
|
-
value =
|
|
20161
|
+
value = fs46.readFileSync(file, "utf8");
|
|
19842
20162
|
} else {
|
|
19843
20163
|
if (text === void 0) throw new CliError("provide text or --file");
|
|
19844
20164
|
value = text;
|
|
@@ -19948,6 +20268,7 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
19948
20268
|
const ixCfg = loadConfig().indexing;
|
|
19949
20269
|
if ((loadConfig().indexing?.embeddings_enabled ?? true) && embeddingsDepsAvailable(getDb(dbPath))) {
|
|
19950
20270
|
ensureEmbeddingProvenance(getDb(dbPath));
|
|
20271
|
+
pruneUnembeddableChunks(getDb(dbPath), ixCfg.max_chunks_per_file, deleteFileEmbeddings);
|
|
19951
20272
|
}
|
|
19952
20273
|
let indexed = 0;
|
|
19953
20274
|
let failed = 0;
|
|
@@ -19984,20 +20305,23 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
19984
20305
|
continue;
|
|
19985
20306
|
}
|
|
19986
20307
|
const sha = fingerprintFile(key);
|
|
19987
|
-
if (sha === null && !
|
|
20308
|
+
if (sha === null && !fs46.existsSync(key)) continue;
|
|
19988
20309
|
const entry = sha !== null ? getFileEntry(key, dbPath) : null;
|
|
19989
20310
|
const spellingStale = entry !== null && indexedPathSpellingIsStale(entry.filePath, key);
|
|
19990
|
-
const parseUnchanged = !force && !spellingStale && sha !== null && entry?.sha === sha && entry.parserSha ===
|
|
20311
|
+
const parseUnchanged = !force && !spellingStale && sha !== null && entry?.sha === sha && entry.parserSha === parserFingerprintForLanguage(entry.language);
|
|
19991
20312
|
const embeddingsEnabled = loadConfig().indexing?.embeddings_enabled ?? true;
|
|
19992
20313
|
const depsAvailable = embeddingsEnabled && embeddingsDepsAvailable(getDb(dbPath));
|
|
19993
|
-
const
|
|
19994
|
-
|
|
20314
|
+
const embedFreshFor = (embedSha) => !force && !spellingStale && sha !== null && isEmbedFresh(
|
|
20315
|
+
embedSha,
|
|
19995
20316
|
sha,
|
|
19996
20317
|
embeddingsEnabled,
|
|
19997
20318
|
depsAvailable,
|
|
19998
20319
|
// See isEmbedFresh: an `oversize:` marker stays fresh only while indexing.large_file_symbol_only_kb is still what it was stamped under, so raising the threshold re-embeds the files it just admitted instead of leaving them permanently skipped. 0 matches no marker (config floors this key at 1), the safe direction for a partially-mocked config.
|
|
19999
|
-
loadConfig().indexing?.large_file_symbol_only_kb ?? 0
|
|
20320
|
+
loadConfig().indexing?.large_file_symbol_only_kb ?? 0,
|
|
20321
|
+
// Same reasoning and same 0 fallback for the chunk-count marker.
|
|
20322
|
+
loadConfig().indexing?.max_chunks_per_file ?? 0
|
|
20000
20323
|
);
|
|
20324
|
+
const embedUnchanged = embedFreshFor(entry?.embedSha);
|
|
20001
20325
|
if (parseUnchanged && embedUnchanged) {
|
|
20002
20326
|
skipped += 1;
|
|
20003
20327
|
continue;
|
|
@@ -20017,9 +20341,10 @@ async function cmdIndex(pathArg, opts = {}) {
|
|
|
20017
20341
|
}
|
|
20018
20342
|
continue;
|
|
20019
20343
|
}
|
|
20020
|
-
if (!
|
|
20344
|
+
if (!fs46.existsSync(key)) continue;
|
|
20021
20345
|
}
|
|
20022
|
-
|
|
20346
|
+
const embedFresh = parseUnchanged ? embedUnchanged : embedFreshFor(getFileEntry(key, dbPath)?.embedSha);
|
|
20347
|
+
if (!embedFresh) {
|
|
20023
20348
|
paintProgress("embedding");
|
|
20024
20349
|
await indexFileEmbeddings(key, dbPath, sha ?? void 0);
|
|
20025
20350
|
}
|
|
@@ -20077,7 +20402,7 @@ async function cmdMcpServe() {
|
|
|
20077
20402
|
let StdioServerTransport;
|
|
20078
20403
|
try {
|
|
20079
20404
|
;
|
|
20080
|
-
({ createMcpServer } = await import("./token-goat-chunk-
|
|
20405
|
+
({ createMcpServer } = await import("./token-goat-chunk-DA6TIDLI.mjs"));
|
|
20081
20406
|
({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
|
|
20082
20407
|
} catch (err2) {
|
|
20083
20408
|
process.stderr.write(
|
|
@@ -20102,11 +20427,11 @@ async function cmdHook(event, opts) {
|
|
|
20102
20427
|
if (typeof opts.harness === "string" && opts.harness.length > 0) {
|
|
20103
20428
|
process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
|
|
20104
20429
|
}
|
|
20105
|
-
const { relay } = await import("./token-goat-chunk-
|
|
20430
|
+
const { relay } = await import("./token-goat-chunk-NYFYIRG3.mjs");
|
|
20106
20431
|
await relay(event);
|
|
20107
20432
|
}
|
|
20108
20433
|
function projectHooksCommitNote(pathFiles, hooksConfigPath) {
|
|
20109
|
-
const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(
|
|
20434
|
+
const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path42.join(path42.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
|
|
20110
20435
|
return `Note: ${pathFiles.map((p) => displaySafePath(p)).join(", ")} ${pathFiles.length === 1 ? "holds" : "hold"} absolute paths to node and token-goat on this machine${shim}, so do not commit them: list them in .git/info/exclude or .gitignore.`;
|
|
20111
20436
|
}
|
|
20112
20437
|
function visualStudioManualSteps(scope) {
|
|
@@ -20139,7 +20464,7 @@ async function cmdInstall(opts) {
|
|
|
20139
20464
|
if (opts.project === true && opts.user === true) {
|
|
20140
20465
|
throw new Error("install takes either -p/--project or --user, not both.");
|
|
20141
20466
|
}
|
|
20142
|
-
await import("./token-goat-chunk-
|
|
20467
|
+
await import("./token-goat-chunk-NYFYIRG3.mjs");
|
|
20143
20468
|
const scope = opts.project === true ? "project" : "user";
|
|
20144
20469
|
if (wantsClaudeCodeBase(opts)) {
|
|
20145
20470
|
const result = installHooks(scope);
|
|
@@ -20285,16 +20610,16 @@ async function cmdInstall(opts) {
|
|
|
20285
20610
|
);
|
|
20286
20611
|
}
|
|
20287
20612
|
try {
|
|
20288
|
-
const skillDir2 =
|
|
20289
|
-
if (
|
|
20290
|
-
const entries =
|
|
20613
|
+
const skillDir2 = path42.join(claudeConfigDir(), "skills");
|
|
20614
|
+
if (fs46.existsSync(skillDir2)) {
|
|
20615
|
+
const entries = fs46.readdirSync(skillDir2, { withFileTypes: true });
|
|
20291
20616
|
const skillNames = [];
|
|
20292
20617
|
const sessionId = getSessionId();
|
|
20293
20618
|
for (const entry of entries) {
|
|
20294
20619
|
if (!entry.isDirectory()) continue;
|
|
20295
|
-
const skillFile =
|
|
20296
|
-
if (
|
|
20297
|
-
const body =
|
|
20620
|
+
const skillFile = path42.join(skillDir2, entry.name, "SKILL.md");
|
|
20621
|
+
if (fs46.existsSync(skillFile)) {
|
|
20622
|
+
const body = fs46.readFileSync(skillFile, "utf-8");
|
|
20298
20623
|
const compact = extractCompactFromMarker(body);
|
|
20299
20624
|
if (compact === null) continue;
|
|
20300
20625
|
const sourceSha = contentHash(body);
|
|
@@ -20305,9 +20630,9 @@ async function cmdInstall(opts) {
|
|
|
20305
20630
|
if (skillNames.length > 0) {
|
|
20306
20631
|
const dir = skillOutputsDir();
|
|
20307
20632
|
ensureDirSync(dir);
|
|
20308
|
-
const pregenPath =
|
|
20633
|
+
const pregenPath = path42.join(dir, "pregen.json");
|
|
20309
20634
|
const pregenData = { ts: Date.now(), names: skillNames };
|
|
20310
|
-
await
|
|
20635
|
+
await fs46.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
|
|
20311
20636
|
out(`Pre-generated ${skillNames.length} skill compacts.`);
|
|
20312
20637
|
}
|
|
20313
20638
|
}
|
|
@@ -20453,7 +20778,8 @@ function cmdStats(opts = {}) {
|
|
|
20453
20778
|
windowDays,
|
|
20454
20779
|
full: opts.full === true,
|
|
20455
20780
|
short: opts.short === true,
|
|
20456
|
-
methodology: opts.methodology === true
|
|
20781
|
+
methodology: opts.methodology === true,
|
|
20782
|
+
hooks: opts.hooks === true
|
|
20457
20783
|
};
|
|
20458
20784
|
if (opts.homeDir !== void 0) {
|
|
20459
20785
|
statsOpts.homeDir = opts.homeDir;
|
|
@@ -20486,7 +20812,7 @@ async function cmdDoctor(opts) {
|
|
|
20486
20812
|
}
|
|
20487
20813
|
}
|
|
20488
20814
|
function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTag = UNTRUSTED_WEB_TAG) {
|
|
20489
|
-
const
|
|
20815
|
+
const emit4 = (text) => {
|
|
20490
20816
|
if (!fenceByProvenance || text === "") {
|
|
20491
20817
|
out(text);
|
|
20492
20818
|
return text;
|
|
@@ -20525,7 +20851,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
|
|
|
20525
20851
|
}
|
|
20526
20852
|
const rawLines = content.split(/\r?\n/);
|
|
20527
20853
|
if (opts.full === true) {
|
|
20528
|
-
return
|
|
20854
|
+
return emit4(rawLines.join("\n"));
|
|
20529
20855
|
}
|
|
20530
20856
|
const lines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
|
|
20531
20857
|
const headN = opts.head !== void 0 ? requireNonNegativeInt2("--head", opts.head) : 30;
|
|
@@ -20548,7 +20874,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
|
|
|
20548
20874
|
result = lines.slice(Math.max(0, lines.length - tailN));
|
|
20549
20875
|
noteLineCap("last", "tail", result.length, lines.length);
|
|
20550
20876
|
}
|
|
20551
|
-
return
|
|
20877
|
+
return emit4(result.join("\n"));
|
|
20552
20878
|
}
|
|
20553
20879
|
function cmdBashOutput(id, opts) {
|
|
20554
20880
|
const parseVerifyThreshold = (optVal) => {
|
|
@@ -20569,7 +20895,7 @@ function cmdBashOutput(id, opts) {
|
|
|
20569
20895
|
}
|
|
20570
20896
|
let content;
|
|
20571
20897
|
try {
|
|
20572
|
-
const st =
|
|
20898
|
+
const st = fs46.statSync(opts.file);
|
|
20573
20899
|
if (st.isFIFO() || st.isSocket()) {
|
|
20574
20900
|
throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
|
|
20575
20901
|
}
|
|
@@ -20584,7 +20910,7 @@ function cmdBashOutput(id, opts) {
|
|
|
20584
20910
|
`);
|
|
20585
20911
|
}
|
|
20586
20912
|
}
|
|
20587
|
-
content = redactIfDotenv(opts.file, decodeSource(
|
|
20913
|
+
content = redactIfDotenv(opts.file, decodeSource(fs46.readFileSync(opts.file)));
|
|
20588
20914
|
} catch (e) {
|
|
20589
20915
|
if (e instanceof CliError) throw e;
|
|
20590
20916
|
throw new CliError(`cannot read file: ${opts.file}`);
|
|
@@ -20648,15 +20974,15 @@ function cmdMcpOutput(id, opts) {
|
|
|
20648
20974
|
if (opts.file.includes("\0")) {
|
|
20649
20975
|
throw new CliError("--file path contains a null byte");
|
|
20650
20976
|
}
|
|
20651
|
-
if (!
|
|
20977
|
+
if (!fs46.existsSync(opts.file)) {
|
|
20652
20978
|
throw new CliError(`file not found: ${opts.file}`);
|
|
20653
20979
|
}
|
|
20654
20980
|
try {
|
|
20655
|
-
const st =
|
|
20981
|
+
const st = fs46.statSync(opts.file);
|
|
20656
20982
|
if (st.isFIFO() || st.isSocket()) {
|
|
20657
20983
|
throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
|
|
20658
20984
|
}
|
|
20659
|
-
content = decodeSource(
|
|
20985
|
+
content = decodeSource(fs46.readFileSync(opts.file));
|
|
20660
20986
|
} catch (e) {
|
|
20661
20987
|
if (e instanceof CliError) throw e;
|
|
20662
20988
|
throw new CliError(`cannot read file: ${opts.file}`);
|
|
@@ -20729,13 +21055,13 @@ async function cmdCompress(commandArgs, opts = {}) {
|
|
|
20729
21055
|
process.exitCode = 1;
|
|
20730
21056
|
return;
|
|
20731
21057
|
}
|
|
20732
|
-
const bashRunner = await import("./token-goat-chunk-
|
|
21058
|
+
const bashRunner = await import("./token-goat-chunk-P46RTOAQ.mjs");
|
|
20733
21059
|
if (opts.compress === false) {
|
|
20734
21060
|
process.exitCode = bashRunner.runRaw(command, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
|
|
20735
21061
|
return;
|
|
20736
21062
|
}
|
|
20737
21063
|
const maxTokens = opts.maxTokens !== void 0 ? requireNonNegativeInt2("--max-tokens", opts.maxTokens) : 0;
|
|
20738
|
-
process.exitCode = bashRunner.run(command, {
|
|
21064
|
+
process.exitCode = await bashRunner.run(command, {
|
|
20739
21065
|
filterName: opts.filter,
|
|
20740
21066
|
timeout: parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS),
|
|
20741
21067
|
maxTokens,
|
|
@@ -20779,9 +21105,10 @@ ${content}`;
|
|
|
20779
21105
|
var originalHelpInformation = null;
|
|
20780
21106
|
function buildProgram() {
|
|
20781
21107
|
const program2 = new Command();
|
|
20782
|
-
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)");
|
|
21108
|
+
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)").option("--notice <text>", "print this line to stdout before the command's own output");
|
|
20783
21109
|
program2.hook("preAction", (thisCommand) => {
|
|
20784
|
-
const
|
|
21110
|
+
const opts = thisCommand.opts();
|
|
21111
|
+
const cwdOverride = opts.cwd;
|
|
20785
21112
|
if (cwdOverride !== void 0) {
|
|
20786
21113
|
try {
|
|
20787
21114
|
process.chdir(cwdOverride);
|
|
@@ -20789,6 +21116,7 @@ function buildProgram() {
|
|
|
20789
21116
|
throw new Error(`--cwd ${cwdOverride}: ${extractErrorMessage(e)}`, { cause: e });
|
|
20790
21117
|
}
|
|
20791
21118
|
}
|
|
21119
|
+
if (opts.notice !== void 0) out(opts.notice);
|
|
20792
21120
|
});
|
|
20793
21121
|
const guard = (fn) => async (...args) => {
|
|
20794
21122
|
process.exitCode = void 0;
|
|
@@ -20818,7 +21146,7 @@ function buildProgram() {
|
|
|
20818
21146
|
process.exitCode = 1;
|
|
20819
21147
|
}
|
|
20820
21148
|
};
|
|
20821
|
-
program2.command("symbol [name] [more...]").description("search for a symbol by name, or project-wide by --grep name pattern").option("-l, --limit <n>", "max results").option("-f, --file <path>", "restrict to one file").option("-k, --kind <kind>", "restrict to one kind (function, class, ...)").option("-p, --project [path]", "scope search to one project root instead of the global index (defaults to cwd)").option("-j, --json", "output as JSON").option("--grep <pattern>", "only show symbols whose name matches this regex (literal substring if it is not valid regex); cannot be combined with <name>").option("--exclude-tests", "hide symbols defined in a test file (opt-in; default output is unchanged)").option("--stats", "add per-result reference count and doc-coverage flag (project-wide count per NAME, not per definition site -- same-named symbols across files share a count)").action((name, more, opts) => {
|
|
21149
|
+
program2.command("symbol [name] [more...]").description("search for a symbol by name, or project-wide by --grep name pattern").option("-l, --limit <n>", "max results").option("-f, --file <path>", "restrict to one file").option("-k, --kind <kind>", "restrict to one kind (function, class, ...)").option("-p, --project [path]", "scope search to one project root instead of the global index (defaults to cwd)").option("-j, --json", "output as JSON").option("--grep <pattern>", "only show symbols whose name matches this regex (literal substring if it is not valid regex); cannot be combined with <name>").option("--exclude-tests", "hide symbols defined in a test file (opt-in; default output is unchanged)").option("--exclude-vendored", "hide symbols defined under a dependency or build directory the indexer skips (node_modules, dist, site-packages, ...) (opt-in; default output is unchanged)").option("--stats", "add per-result reference count and doc-coverage flag (project-wide count per NAME, not per definition site -- same-named symbols across files share a count)").action((name, more, opts) => {
|
|
20822
21150
|
let projectRoot;
|
|
20823
21151
|
if (opts.project === true) {
|
|
20824
21152
|
projectRoot = resolveProjectRoot({ project: process.cwd() });
|
|
@@ -20839,6 +21167,7 @@ function buildProgram() {
|
|
|
20839
21167
|
...opts.json === true ? { json: true } : {},
|
|
20840
21168
|
...opts.grep !== void 0 ? { grep: opts.grep } : {},
|
|
20841
21169
|
...opts.excludeTests === true ? { excludeTests: true } : {},
|
|
21170
|
+
...opts.excludeVendored === true ? { excludeVendored: true } : {},
|
|
20842
21171
|
...opts.stats === true ? { stats: true } : {}
|
|
20843
21172
|
}),
|
|
20844
21173
|
// `symbol` searches one name; there is no comma form that would search several.
|
|
@@ -20847,7 +21176,7 @@ function buildProgram() {
|
|
|
20847
21176
|
);
|
|
20848
21177
|
});
|
|
20849
21178
|
program2.command("read <spec> [more...]").description(
|
|
20850
|
-
"read one symbol's full body (spec: file::symbol; disambiguate a name shared by several classes with file::Parent.symbol; a trailing @LINE anchor -- file::symbol@LINE, or combined as file::Parent.symbol@LINE -- picks out a specific candidate by its exact starting line, for the case a Parent qualifier can't reach (e.g. a top-level definition); comma-separated file::a,b for a merged multi-symbol view, or a::x,b::y to merge symbols across several files)"
|
|
21179
|
+
"read one symbol's full body (spec: file::symbol; disambiguate a name shared by several classes with file::Parent.symbol; a trailing @LINE anchor -- file::symbol@LINE, or combined as file::Parent.symbol@LINE -- picks out a specific candidate by its exact starting line, for the case a Parent qualifier can't reach (e.g. a top-level definition); comma-separated file::a,b for a merged multi-symbol view, or a::x,b::y to merge symbols across several files; file:LINE or file:START-END resolves a line number to the region enclosing it -- the symbol, the file preamble, or the gap between two symbols -- while file@START-END serves those raw lines)"
|
|
20851
21180
|
).option("-j, --json", "output as JSON").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
|
|
20852
21181
|
(spec, more, opts) => runExitText(
|
|
20853
21182
|
() => noteExtraFileArgs(
|
|
@@ -20860,7 +21189,8 @@ function buildProgram() {
|
|
|
20860
21189
|
...opts.forceRefresh === true ? { forceRefresh: true } : {},
|
|
20861
21190
|
...opts.stats === true ? { stats: true } : {}
|
|
20862
21191
|
}),
|
|
20863
|
-
|
|
21192
|
+
// Whether the comma form the note names would actually run depends on the specs themselves, not on the command: `read` merges `::` specs and nothing else.
|
|
21193
|
+
{ noun: "spec", mergeable: readSpecsMergeable([spec, ...more]) }
|
|
20864
21194
|
)
|
|
20865
21195
|
)
|
|
20866
21196
|
);
|
|
@@ -20868,7 +21198,7 @@ function buildProgram() {
|
|
|
20868
21198
|
"symbol body + callers + containing doc section in one call (spec: file::symbol; also accepts the file::symbol@LINE anchor form documented under `read`; comma-separated file::a,b for a merged multi-symbol view; cross-file a.ts::x,b.ts::y is also supported)"
|
|
20869
21199
|
).option("-j, --json", "output as JSON").option("--limit <n>", "max callers to show (default: 20)").option("-C, --context <n>", "lines of call-site source to show before and after each caller (default 0)").option("--exclude-tests", "hide callers whose call site lives in a test file (opt-in; default output is unchanged)").option("--grep <pattern>", "only show callers whose enclosing symbol name matches this regex (literal substring if it is not valid regex)").action(
|
|
20870
21200
|
(spec, more, opts) => runExit(() => {
|
|
20871
|
-
emitExtraFileArgsNote("brief", spec, more, { noun: "spec" });
|
|
21201
|
+
emitExtraFileArgsNote("brief", spec, more, { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) });
|
|
20872
21202
|
return runBrief({
|
|
20873
21203
|
spec,
|
|
20874
21204
|
...opts.json === true ? { json: true } : {},
|
|
@@ -20886,7 +21216,7 @@ function buildProgram() {
|
|
|
20886
21216
|
emitExtraFileArgsNote("section --list", spec, more, { mergeable: false });
|
|
20887
21217
|
return runListSections({ file: spec, ...opts.json === true ? { json: true } : {}, ...opts.grep !== void 0 ? { grep: opts.grep } : {} });
|
|
20888
21218
|
}) : runExitText(
|
|
20889
|
-
() => noteExtraFileArgs("section", spec, more, () => runSection({ spec, ...opts.json === true ? { json: true } : {} }), { noun: "spec" })
|
|
21219
|
+
() => noteExtraFileArgs("section", spec, more, () => runSection({ spec, ...opts.json === true ? { json: true } : {} }), { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) })
|
|
20890
21220
|
)
|
|
20891
21221
|
);
|
|
20892
21222
|
program2.command("semantic [query]").description("semantic search (falls back to full-text search)").option("-l, --limit <n>", "max results").option("-j, --json", "output as JSON").option("--grep <pattern>", "filter to hits whose file path matches this regex (literal substring if it is not valid regex); matched against the path as rendered").option("--exclude-tests", "hide hits whose file is a test file (opt-in; default output is unchanged)").option("--preflight", "run semantic embedding preflight check and exit").option("--warm", "warm up the embedding model session in memory").action(guard(cmdSemantic));
|
|
@@ -20924,7 +21254,7 @@ function buildProgram() {
|
|
|
20924
21254
|
"for a high-fanout symbol, group references by file (count only) and show only the top N files by reference count instead of a per-line dump"
|
|
20925
21255
|
).option("-C, --context <n>", "lines of call-site source to show before and after each reference (default 0)").option("-j, --json", "output as JSON").option("--exclude-tests", "hide references whose call site lives in a test file (opt-in; default output is unchanged)").option("--grep <pattern>", "filter to references whose call-site file path matches this regex (falls back to a literal substring match when the pattern does not compile); matched against the path as rendered, so ^src/ matches what you see in every form -- drops test/vendored hits from a wide-fanout symbol").action(
|
|
20926
21256
|
(spec, more, opts) => runExit(() => {
|
|
20927
|
-
emitExtraFileArgsNote("refs", spec, more, { noun: "spec" });
|
|
21257
|
+
emitExtraFileArgsNote("refs", spec, more, { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) });
|
|
20928
21258
|
return runRefs({
|
|
20929
21259
|
spec,
|
|
20930
21260
|
...opts.callers === true ? { callers: true } : {},
|
|
@@ -20945,7 +21275,7 @@ function buildProgram() {
|
|
|
20945
21275
|
program2.command("bench").description("replay a corpus of captured command output through the compressors; prints the byte savings ratio and a fidelity check, and exits 1 when a must-keep line was dropped").option("--corpus <dir>", "corpus directory (default: tests/fixtures/bench, relative to the working directory)").option("--tsv <path>", "append one result row, keyed by the current commit, creating the file and its header if absent").option("--validate", "score the corpus with control filters instead: proves the ratio has a floor and the fidelity guard can actually fail").option("-j, --json", "output as JSON").action(
|
|
20946
21276
|
(opts) => runExitText(
|
|
20947
21277
|
() => runBenchCommand({
|
|
20948
|
-
corpus: opts.corpus ??
|
|
21278
|
+
corpus: opts.corpus ?? path42.join("tests", "fixtures", "bench"),
|
|
20949
21279
|
...opts.tsv !== void 0 ? { tsv: opts.tsv } : {},
|
|
20950
21280
|
...opts.validate === true ? { validate: true } : {},
|
|
20951
21281
|
...opts.json === true ? { json: true } : {}
|
|
@@ -20964,7 +21294,7 @@ function buildProgram() {
|
|
|
20964
21294
|
worker.command("start").description("start the background indexer").action(guard(cmdWorkerStart));
|
|
20965
21295
|
worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
|
|
20966
21296
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
20967
|
-
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));
|
|
21297
|
+
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("--hooks", "show the per-event/per-harness hook latency breakdown (median/p95/slowest/last-seen)").option("--window-days <days>", "days to include (0 = all time)", "30").option("--home-dir <path>", "home directory (for testing)").action(guard(cmdStats));
|
|
20968
21298
|
program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").option("--json", "emit check results as JSON instead of text").option("--repair", "automatically repair fixable issues (permissive settings, missing semantics models)").option("--fix", "alias for --repair").action(guard(cmdDoctor));
|
|
20969
21299
|
program2.command("bash-output [id]").description("retrieve cached bash output by ID or file").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the output").option("--full", "print the entire cached entry with no head/tail elision").option("--file <path>", "read from raw output file instead of cache").option("--transcript", "parse the --file as a JSONL agent transcript: keep assistant text blocks in order before filtering").option("--verify-last-write [seconds]", "verify the file or cache entry was written within [seconds] (default: 60s) to catch stale/no-op terminal output").option("--strict", "exit non-zero when --verify-last-write detects stale output").action(guard(cmdBashOutput));
|
|
20970
21300
|
program2.command("web-output [id]").description("retrieve a cached WebFetch response body by ID").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the response").option("--full", "print the entire cached entry with no head/tail elision").option("--raw", "return the body as actually fetched, before extractCleanText cleaning, instead of the default cleaned text").action(guard(cmdWebOutput));
|
|
@@ -20979,7 +21309,7 @@ function buildProgram() {
|
|
|
20979
21309
|
program2.command("compress [command...]").alias("bash").alias("run").description("run a shell command (under a POSIX shell / bash) and emit a compressed view of its output").allowUnknownOption(true).option("-c, --cmd <command>", "the shell command to run, as one string (use / for paths across platforms)").option("--cmd-b64 <payload>", "the shell command as a base64-encoded string (preserves quotes, backslashes, and symbols across platforms)").option("-f, --filter <name>", "filter name (auto-detected from the command when omitted)").option("--timeout <seconds>", "wall-clock timeout in seconds (0 = built-in default)").option("--no-compress", "stream output raw without compression (debug the wrapper)").option("--profile <name>", "compression profile: aggressive | balanced | minimal").option("--max-tokens <n>", "post-compress token cap (0 = no cap)").option("-q, --quiet-success", "on exit code 0, emit only [tg: ok] summary and store full output for recall via bash-output").option("--native", "use native platform shell (e.g. cmd.exe on Windows) instead of bash, preserving Windows path backslashes").action(cmdCompress);
|
|
20980
21310
|
program2.command("upgrade").alias("update").description("check for updates and upgrade token-goat to the latest version").option("--check", "check whether an update is available without installing").option("-j, --json", "emit version check status as JSON").action(
|
|
20981
21311
|
(opts) => guard(async () => {
|
|
20982
|
-
const { cmdUpgrade } = await import("./token-goat-chunk-
|
|
21312
|
+
const { cmdUpgrade } = await import("./token-goat-chunk-VJQBYSXG.mjs");
|
|
20983
21313
|
await cmdUpgrade(opts, () => cmdInstall({}));
|
|
20984
21314
|
})()
|
|
20985
21315
|
);
|