token-goat 2.9.28 → 2.9.29

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.
Files changed (77) hide show
  1. package/README.md +2 -0
  2. package/dist/{token-goat-chunk-OZRSREKR.mjs → token-goat-chunk-2AWQ2R5X.mjs} +2 -2
  3. package/dist/{token-goat-chunk-5NNYIUGV.mjs → token-goat-chunk-2NNUA2LR.mjs} +4 -4
  4. package/dist/{token-goat-chunk-4RN64ZND.mjs → token-goat-chunk-2WOUYWLM.mjs} +2 -1
  5. package/dist/{token-goat-chunk-HUDC5GS5.mjs → token-goat-chunk-435UQTKD.mjs} +5 -3
  6. package/dist/{token-goat-chunk-6DQNNFJ6.mjs → token-goat-chunk-44BM7DDQ.mjs} +3 -3
  7. package/dist/{token-goat-chunk-4JMWPWCS.mjs → token-goat-chunk-52NSDWYY.mjs} +2 -2
  8. package/dist/{token-goat-chunk-IHKZ3QNA.mjs → token-goat-chunk-67BNIKKO.mjs} +19 -16
  9. package/dist/{token-goat-chunk-XBESNWEX.mjs → token-goat-chunk-6AK46ZT7.mjs} +2 -3
  10. package/dist/{token-goat-chunk-M56BWPBN.mjs → token-goat-chunk-7CPMDFGB.mjs} +166 -40
  11. package/dist/token-goat-chunk-ASLWOQ3I.mjs +14 -0
  12. package/dist/{token-goat-chunk-QM76S6YS.mjs → token-goat-chunk-BGYOZ6DO.mjs} +6 -4
  13. package/dist/{token-goat-chunk-JXK2HQXS.mjs → token-goat-chunk-C5YPNZKQ.mjs} +3 -3
  14. package/dist/{token-goat-chunk-MVSWBNMD.mjs → token-goat-chunk-CIQ2FTY3.mjs} +19 -14
  15. package/dist/{token-goat-chunk-BJBRGPBX.mjs → token-goat-chunk-D4ETHJD6.mjs} +6 -4
  16. package/dist/{token-goat-chunk-VE27HPIF.mjs → token-goat-chunk-DNRPXHYH.mjs} +21 -17
  17. package/dist/{token-goat-chunk-YWSP6IU7.mjs → token-goat-chunk-DNSPXQXQ.mjs} +2 -2
  18. package/dist/token-goat-chunk-DPNLUJWB.mjs +220 -0
  19. package/dist/{token-goat-chunk-I7CB3X4K.mjs → token-goat-chunk-E4ZOJIZK.mjs} +10 -7
  20. package/dist/{token-goat-chunk-IZXKVT4A.mjs → token-goat-chunk-E5TK5DNO.mjs} +11 -4
  21. package/dist/{token-goat-chunk-MMLMALWK.mjs → token-goat-chunk-EUSDXRXU.mjs} +60 -12
  22. package/dist/{token-goat-chunk-7R2XYKC5.mjs → token-goat-chunk-FTP4U4QA.mjs} +12 -9
  23. package/dist/{token-goat-chunk-LEG3JNJ6.mjs → token-goat-chunk-G23DWHV4.mjs} +17 -14
  24. package/dist/{token-goat-chunk-L7SDVGBT.mjs → token-goat-chunk-G7IWXCFF.mjs} +49 -23
  25. package/dist/{token-goat-chunk-NILVK7XM.mjs → token-goat-chunk-GLXRTTLE.mjs} +7 -5
  26. package/dist/{token-goat-chunk-6CCQDAYY.mjs → token-goat-chunk-H5J3C56Q.mjs} +35 -16
  27. package/dist/{token-goat-chunk-Q4S3UQGO.mjs → token-goat-chunk-HNFZZHFW.mjs} +2 -2
  28. package/dist/{token-goat-chunk-CBMY2FGI.mjs → token-goat-chunk-HUYUHODZ.mjs} +572 -230
  29. package/dist/token-goat-chunk-IVE6U7KV.mjs +53 -0
  30. package/dist/{token-goat-chunk-F7QZRIWM.mjs → token-goat-chunk-IVWI2OCK.mjs} +8 -30
  31. package/dist/{token-goat-chunk-GMOUBOX4.mjs → token-goat-chunk-JTKCQMQA.mjs} +37 -5
  32. package/dist/{token-goat-chunk-6SGLIPQB.mjs → token-goat-chunk-KOPIR3KZ.mjs} +12 -9
  33. package/dist/token-goat-chunk-L7H3U27L.mjs +35 -0
  34. package/dist/{token-goat-chunk-FSTWAOW6.mjs → token-goat-chunk-LZNBQTDK.mjs} +6 -4
  35. package/dist/token-goat-chunk-MELOKFDJ.mjs +199 -0
  36. package/dist/{token-goat-chunk-VWZKZPSR.mjs → token-goat-chunk-NMBWLRHW.mjs} +76 -39
  37. package/dist/{token-goat-chunk-HXNAC4ZF.mjs → token-goat-chunk-O6I2XU4P.mjs} +12 -10
  38. package/dist/{token-goat-chunk-MAMQPC43.mjs → token-goat-chunk-ONXH6NZ5.mjs} +1 -1
  39. package/dist/token-goat-chunk-ORNFMIER.mjs +295 -0
  40. package/dist/{token-goat-chunk-66LODAUT.mjs → token-goat-chunk-PGTH2U4C.mjs} +1 -1
  41. package/dist/{token-goat-chunk-SJEKA7Y2.mjs → token-goat-chunk-PKRWMHVP.mjs} +76 -64
  42. package/dist/{token-goat-chunk-DA43OE7L.mjs → token-goat-chunk-PQ53BSCF.mjs} +1 -1
  43. package/dist/{token-goat-chunk-RSS3TLXU.mjs → token-goat-chunk-PYSUADX4.mjs} +2 -2
  44. package/dist/{token-goat-chunk-EL7M26WP.mjs → token-goat-chunk-QO3LXVVR.mjs} +185 -104
  45. package/dist/{token-goat-chunk-XI2VFAV6.mjs → token-goat-chunk-RLP6J7WR.mjs} +6 -6
  46. package/dist/token-goat-chunk-RQGGMPCR.mjs +205 -0
  47. package/dist/{token-goat-chunk-6Z5ULNE5.mjs → token-goat-chunk-RTMVLHWX.mjs} +165 -60
  48. package/dist/{token-goat-chunk-E3K3BTEQ.mjs → token-goat-chunk-S4VSXURL.mjs} +8 -6
  49. package/dist/{token-goat-chunk-LKSXAMJB.mjs → token-goat-chunk-SE6E4BKJ.mjs} +1 -1
  50. package/dist/token-goat-chunk-TCQANXYC.mjs +351 -0
  51. package/dist/{token-goat-chunk-GGLEE35R.mjs → token-goat-chunk-TDUT2CL6.mjs} +7 -7
  52. package/dist/{token-goat-chunk-CC2PYJM7.mjs → token-goat-chunk-TGOXNAHW.mjs} +54 -112
  53. package/dist/{token-goat-chunk-XKY4VKJ4.mjs → token-goat-chunk-UA5BKWWQ.mjs} +1 -1
  54. package/dist/{token-goat-chunk-UBUKHVYW.mjs → token-goat-chunk-UXSXQQIY.mjs} +1 -1
  55. package/dist/{token-goat-chunk-D5ICEPSB.mjs → token-goat-chunk-VMFWNHGR.mjs} +79 -68
  56. package/dist/token-goat-chunk-VQ6F6T7X.mjs +36 -0
  57. package/dist/{token-goat-chunk-4STDIGYC.mjs → token-goat-chunk-VZQEWRVH.mjs} +6 -4
  58. package/dist/{token-goat-chunk-OSUFN2FV.mjs → token-goat-chunk-XB5KKOCG.mjs} +5 -25
  59. package/dist/{token-goat-chunk-L45ZQRT7.mjs → token-goat-chunk-XYWPOSCN.mjs} +57 -615
  60. package/dist/{token-goat-chunk-4JJBFT7V.mjs → token-goat-chunk-YAGLESS3.mjs} +8 -5
  61. package/dist/token-goat-chunk-YKSRAWUU.mjs +33 -0
  62. package/dist/{token-goat-chunk-F2ARFHLJ.mjs → token-goat-chunk-YOOVFPTP.mjs} +2 -2
  63. package/dist/{token-goat-chunk-EEIDFMEM.mjs → token-goat-chunk-YZAAZU2S.mjs} +12 -3
  64. package/dist/token-goat-chunk-Z6KBMOBI.mjs +53 -0
  65. package/dist/{token-goat-chunk-RJH64KQH.mjs → token-goat-chunk-ZER6EAXZ.mjs} +3 -3
  66. package/dist/token-goat-hook-client.cjs +475 -0
  67. package/dist/token-goat-hook-client.mjs +26 -0
  68. package/dist/token-goat-hook.mjs +31 -27
  69. package/dist/token-goat.core.mjs +9 -44
  70. package/docs/C4_RUNTIME_ARCHITECTURE.md +2 -2
  71. package/docs/cli.md +3 -2
  72. package/docs/install.md +16 -14
  73. package/package.json +1 -1
  74. package/dist/token-goat-chunk-7MAFDRVR.mjs +0 -47
  75. package/dist/token-goat-chunk-BCN2YQC6.mjs +0 -32
  76. package/dist/token-goat-chunk-D7GHQKQD.mjs +0 -30
  77. package/dist/token-goat-chunk-IFORDBGC.mjs +0 -192
@@ -9,7 +9,7 @@ import {
9
9
  compressText,
10
10
  computeFileFingerprint,
11
11
  createHandoff,
12
- didYouMean,
12
+ didYouMeanLines,
13
13
  emit,
14
14
  emitErr,
15
15
  enclosingSymbol,
@@ -93,13 +93,16 @@ import {
93
93
  runYamlQuery,
94
94
  runZipList,
95
95
  runZipRead,
96
+ statThroughHandle,
96
97
  symbolNamesInFile,
97
98
  upsertNote,
98
99
  warnIfFilesStale
99
- } from "./token-goat-chunk-VWZKZPSR.mjs";
100
+ } from "./token-goat-chunk-NMBWLRHW.mjs";
101
+ import "./token-goat-chunk-VZQEWRVH.mjs";
100
102
  import {
101
103
  compareSemver
102
- } from "./token-goat-chunk-5NNYIUGV.mjs";
104
+ } from "./token-goat-chunk-2NNUA2LR.mjs";
105
+ import "./token-goat-chunk-PQ53BSCF.mjs";
103
106
  import {
104
107
  MCP_DISABLE_NOTE,
105
108
  buildCopilotWasteReport,
@@ -107,10 +110,10 @@ import {
107
110
  findProjectSession,
108
111
  isCopilotTranscript,
109
112
  unusedMcpServers
110
- } from "./token-goat-chunk-JXK2HQXS.mjs";
113
+ } from "./token-goat-chunk-C5YPNZKQ.mjs";
111
114
  import {
112
115
  buildResumePacket
113
- } from "./token-goat-chunk-XI2VFAV6.mjs";
116
+ } from "./token-goat-chunk-RLP6J7WR.mjs";
114
117
  import {
115
118
  DEFAULT_RECONCILE_BUDGET_MS,
116
119
  HOOK_EVENTS,
@@ -129,7 +132,7 @@ import {
129
132
  runRecallCommand,
130
133
  runReconcile,
131
134
  storeWebOutput
132
- } from "./token-goat-chunk-VE27HPIF.mjs";
135
+ } from "./token-goat-chunk-DNRPXHYH.mjs";
133
136
  import {
134
137
  HINT_CATEGORIES,
135
138
  MAX_FILES_SCANNED,
@@ -146,6 +149,7 @@ import {
146
149
  compactDoc,
147
150
  compactPathFor,
148
151
  confirmAndApply,
152
+ dataDirWriteRefusal,
149
153
  detectWalkMode,
150
154
  dirtyQueuePathFor,
151
155
  drainHeartbeatPathFor,
@@ -163,6 +167,7 @@ import {
163
167
  formatSymbolLocation,
164
168
  getDirtyPathsFor,
165
169
  getEmbeddingCoverage,
170
+ getHintSpendTotals,
166
171
  getHintStatsSummary,
167
172
  getHintStatsTotals,
168
173
  getParserFreshness,
@@ -201,28 +206,32 @@ import {
201
206
  visionTokensSavedByText,
202
207
  walkProject,
203
208
  writeCompact
204
- } from "./token-goat-chunk-6Z5ULNE5.mjs";
209
+ } from "./token-goat-chunk-RTMVLHWX.mjs";
210
+ import "./token-goat-chunk-L7H3U27L.mjs";
211
+ import "./token-goat-chunk-RRNZMM3A.mjs";
205
212
  import {
206
213
  GEMINI_TOOL_NAME_MAP
207
- } from "./token-goat-chunk-IZXKVT4A.mjs";
214
+ } from "./token-goat-chunk-E5TK5DNO.mjs";
208
215
  import {
209
216
  CompressedOutput,
210
217
  TOOL_FILTERS,
211
218
  ToolFilter,
212
219
  deliverCompressed,
213
220
  detectFromCommand
214
- } from "./token-goat-chunk-RJH64KQH.mjs";
221
+ } from "./token-goat-chunk-ZER6EAXZ.mjs";
215
222
  import {
216
223
  buildWasteReport,
217
224
  extractResultText,
218
225
  findLatestTranscript,
219
226
  projectTranscriptsDir,
220
227
  safeStringify
221
- } from "./token-goat-chunk-6DQNNFJ6.mjs";
228
+ } from "./token-goat-chunk-44BM7DDQ.mjs";
222
229
  import {
223
230
  COPILOT_CLI_HOOK_SCRIPT,
224
231
  HOOKS_SCRIPT_FILE,
232
+ LEGACY_HOOKS_SCRIPT_FILE,
225
233
  MATERIALIZE_SHRUNK_IMAGE_JS,
234
+ SHIM_TRY_SERVER,
226
235
  buildGuidanceBlock,
227
236
  buildGuidanceBody,
228
237
  bundledCliPath,
@@ -257,14 +266,14 @@ import {
257
266
  visualStudioProjectMcpPath,
258
267
  visualStudioSolutionVscodeMcpPath,
259
268
  visualStudioUserMcpPath
260
- } from "./token-goat-chunk-MMLMALWK.mjs";
269
+ } from "./token-goat-chunk-EUSDXRXU.mjs";
261
270
  import {
262
271
  buildManifest,
263
272
  clearAll,
264
273
  loadEntries,
265
274
  setEntry,
266
275
  unsetEntry
267
- } from "./token-goat-chunk-L7SDVGBT.mjs";
276
+ } from "./token-goat-chunk-G7IWXCFF.mjs";
268
277
  import {
269
278
  AGENT_SALT_MARKER,
270
279
  CONTEXT_AUTOCOMPACT_TOKENS,
@@ -288,37 +297,37 @@ import {
288
297
  skillOutputsDir,
289
298
  storeCompact,
290
299
  storeOutput
291
- } from "./token-goat-chunk-6CCQDAYY.mjs";
300
+ } from "./token-goat-chunk-H5J3C56Q.mjs";
292
301
  import {
293
302
  getSessionFiles,
294
303
  getSessionId
295
- } from "./token-goat-chunk-GGLEE35R.mjs";
304
+ } from "./token-goat-chunk-TDUT2CL6.mjs";
296
305
  import {
297
306
  claudeConfigDir,
298
307
  formatBytes,
299
308
  formatTokenEstimate
300
- } from "./token-goat-chunk-MAMQPC43.mjs";
309
+ } from "./token-goat-chunk-ONXH6NZ5.mjs";
301
310
  import {
302
311
  BASH_OUTPUT_SUBDIR,
303
312
  getBashOutput,
304
313
  isRecallCacheType
305
- } from "./token-goat-chunk-HXNAC4ZF.mjs";
314
+ } from "./token-goat-chunk-O6I2XU4P.mjs";
306
315
  import {
307
316
  DEFAULT_MAX_AGE_MS,
308
317
  DEFAULT_MAX_COUNT,
309
318
  listBlobs,
310
319
  pruneBlobs
311
- } from "./token-goat-chunk-BJBRGPBX.mjs";
320
+ } from "./token-goat-chunk-D4ETHJD6.mjs";
312
321
  import {
313
322
  capJsonRows,
314
323
  estimateTokens,
315
324
  estimateTokensFromLength,
316
325
  trimToBudget
317
- } from "./token-goat-chunk-4JMWPWCS.mjs";
326
+ } from "./token-goat-chunk-52NSDWYY.mjs";
318
327
  import {
319
328
  clipLongMatchLine,
320
329
  combineStreams
321
- } from "./token-goat-chunk-4RN64ZND.mjs";
330
+ } from "./token-goat-chunk-2WOUYWLM.mjs";
322
331
  import {
323
332
  C,
324
333
  HOOK_STATS_RETENTION_DAYS,
@@ -340,19 +349,28 @@ import {
340
349
  statsHasHarnessColumn,
341
350
  stripAnsiEscapes,
342
351
  summarize
343
- } from "./token-goat-chunk-MVSWBNMD.mjs";
352
+ } from "./token-goat-chunk-CIQ2FTY3.mjs";
353
+ import {
354
+ queryServers,
355
+ serverStatuses
356
+ } from "./token-goat-chunk-RQGGMPCR.mjs";
357
+ import {
358
+ readMarker,
359
+ touchMarker
360
+ } from "./token-goat-chunk-DPNLUJWB.mjs";
361
+ import "./token-goat-chunk-SE6E4BKJ.mjs";
344
362
  import {
345
363
  docxOutline,
346
364
  docxTables,
347
365
  docxText,
348
366
  formatDocxTables
349
- } from "./token-goat-chunk-XKY4VKJ4.mjs";
367
+ } from "./token-goat-chunk-UA5BKWWQ.mjs";
350
368
  import {
351
369
  pptxNotesText,
352
370
  pptxOutline,
353
371
  pptxSlideText,
354
372
  pptxTextGrep
355
- } from "./token-goat-chunk-Q4S3UQGO.mjs";
373
+ } from "./token-goat-chunk-HNFZZHFW.mjs";
356
374
  import {
357
375
  formatXlsxColumns,
358
376
  formatXlsxRange,
@@ -361,11 +379,16 @@ import {
361
379
  querySheet,
362
380
  rangeSheet,
363
381
  xlsxColumns
364
- } from "./token-goat-chunk-RSS3TLXU.mjs";
382
+ } from "./token-goat-chunk-PYSUADX4.mjs";
365
383
  import {
366
384
  formatCsvTable,
367
385
  parseWhereSpecs
368
- } from "./token-goat-chunk-YWSP6IU7.mjs";
386
+ } from "./token-goat-chunk-DNSPXQXQ.mjs";
387
+ import "./token-goat-chunk-3BTK54F3.mjs";
388
+ import "./token-goat-chunk-UXSXQQIY.mjs";
389
+ import "./token-goat-chunk-XTQAOTSO.mjs";
390
+ import "./token-goat-chunk-AH6QILZM.mjs";
391
+ import "./token-goat-chunk-PGTH2U4C.mjs";
369
392
  import {
370
393
  assetEmbedSha,
371
394
  deleteFileEmbeddings,
@@ -387,18 +410,21 @@ import {
387
410
  maxChunksEmbedSha,
388
411
  missingTreeSitterGrammarPackages,
389
412
  modelFilesPresent,
413
+ oversizeEmbedSha,
390
414
  parserFingerprintForLanguage,
391
415
  readSection,
392
416
  treeSitterCoreAvailable,
393
417
  treeSitterCoreLoadError
394
- } from "./token-goat-chunk-D5ICEPSB.mjs";
418
+ } from "./token-goat-chunk-VMFWNHGR.mjs";
395
419
  import {
396
420
  getFileEntry,
397
421
  getProjectFileEntries,
422
+ projectScopeClause,
398
423
  querySymbols
399
- } from "./token-goat-chunk-HUDC5GS5.mjs";
424
+ } from "./token-goat-chunk-435UQTKD.mjs";
400
425
  import {
401
426
  TREE_SITTER_LANGUAGES,
427
+ allowReadOnlyIndex,
402
428
  detectLanguage,
403
429
  detectLanguageOfFile,
404
430
  fenceFor,
@@ -407,14 +433,13 @@ import {
407
433
  matchGroovySlashy,
408
434
  nonTreeSitterLanguageCount,
409
435
  redactIfDotenv
410
- } from "./token-goat-chunk-M56BWPBN.mjs";
436
+ } from "./token-goat-chunk-7CPMDFGB.mjs";
411
437
  import {
412
438
  CONFIG_KEY_ENV_OVERRIDES,
413
439
  PROJECT_LOCKED_KEYS,
414
440
  PROJECT_LOCKED_SECTIONS,
415
441
  SUPPORTED_OCR_LANG_CODES,
416
442
  defaultConfig,
417
- envBool,
418
443
  fileIsAbsent,
419
444
  findProject,
420
445
  fingerprintContent,
@@ -423,8 +448,10 @@ import {
423
448
  getLastConfigParseError,
424
449
  getLastProjectConfigParseError,
425
450
  getProjectConfigInfo,
451
+ hookServerEnabled,
426
452
  invalidateConfigCache,
427
453
  isSupportedOcrLang,
454
+ isUnderSystemTemp,
428
455
  lastProjectConfigLockedKeys,
429
456
  loadConfig,
430
457
  loadPersistedConfig,
@@ -436,22 +463,21 @@ import {
436
463
  stringify,
437
464
  validateEnumField,
438
465
  validateNumericField
439
- } from "./token-goat-chunk-CC2PYJM7.mjs";
466
+ } from "./token-goat-chunk-TGOXNAHW.mjs";
467
+ import "./token-goat-chunk-XB5KKOCG.mjs";
468
+ import "./token-goat-chunk-YZAAZU2S.mjs";
469
+ import {
470
+ envBool
471
+ } from "./token-goat-chunk-IVE6U7KV.mjs";
440
472
  import {
441
- ENV_KEYS,
442
473
  LOCK_WAIT_MS_HARDENED,
443
- PACKAGE_NAME,
444
- VERSION,
445
474
  assertProjectScopeTarget,
446
475
  assertWriteInScope,
447
476
  atomicWriteBytes,
448
477
  atomicWriteText,
449
478
  backupFile,
450
- canonicalize,
451
479
  cappedSourceBytesSaved,
452
- configPath,
453
480
  countNoun,
454
- dataDir,
455
481
  decodeSource,
456
482
  detectSourceEncoding,
457
483
  encodeSource,
@@ -459,19 +485,16 @@ import {
459
485
  ensureNewline,
460
486
  escapeRegExp,
461
487
  extractErrorMessage,
462
- foldPath,
463
- globalDbPath,
488
+ hasCreatedConfig,
464
489
  hookCommandFor,
465
490
  hookExecPartsFor,
466
491
  installSingleFilePlugin,
467
492
  isTestFile,
468
493
  isUnderBlockedRoot,
469
- isWindows,
470
494
  pad,
471
495
  projectPathIsConsultable,
472
496
  projectScopeRoot,
473
497
  pushAll,
474
- quoteShellPath,
475
498
  recordCreatedConfig,
476
499
  redactUrlQuery,
477
500
  removeCreatedBackups,
@@ -479,16 +502,13 @@ import {
479
502
  removeFileInScope,
480
503
  requireNonNegativeStrictInt,
481
504
  requirePositiveStrictInt,
482
- resolveOnPath,
483
505
  runGit,
484
- sleepSync,
485
506
  stripDelimitedBlock,
486
507
  stripLower,
487
508
  stripOwnHooksFromMap,
488
509
  stripStaleGroupHooks,
489
510
  takeCreatedConfig,
490
511
  toKB,
491
- tokenGoatHome,
492
512
  uninstallSingleFilePlugin,
493
513
  upsertDelimitedBlock,
494
514
  withExtension,
@@ -497,7 +517,15 @@ import {
497
517
  withRetryOnLock,
498
518
  writeIfDifferent,
499
519
  writeJsonSettings
500
- } from "./token-goat-chunk-L45ZQRT7.mjs";
520
+ } from "./token-goat-chunk-XYWPOSCN.mjs";
521
+ import {
522
+ canonicalize,
523
+ foldPath,
524
+ isWindows,
525
+ quoteShellPath,
526
+ resolveOnPath,
527
+ sleepSync
528
+ } from "./token-goat-chunk-TCQANXYC.mjs";
501
529
  import {
502
530
  UNTRUSTED_FILE_TAG,
503
531
  UNTRUSTED_TOOL_TAG,
@@ -512,9 +540,19 @@ import {
512
540
  resolveIndexPath,
513
541
  toDisplayPath
514
542
  } from "./token-goat-chunk-NMTKNYGF.mjs";
543
+ import {
544
+ ENV_KEYS,
545
+ PACKAGE_NAME,
546
+ VERSION,
547
+ configPath,
548
+ dataDir,
549
+ globalDbPath,
550
+ tokenGoatHome
551
+ } from "./token-goat-chunk-MELOKFDJ.mjs";
552
+ import "./token-goat-chunk-Y4AFKTHK.mjs";
515
553
  import {
516
554
  compileGuardedRegex
517
- } from "./token-goat-chunk-GMOUBOX4.mjs";
555
+ } from "./token-goat-chunk-JTKCQMQA.mjs";
518
556
  import {
519
557
  __export,
520
558
  init_define_import_meta_env
@@ -4010,6 +4048,18 @@ init_define_import_meta_env();
4010
4048
 
4011
4049
  // src/bridges/shim_common.ts
4012
4050
  init_define_import_meta_env();
4051
+ var SHIM_FILE = "token-goat-shim.cjs";
4052
+ var LEGACY_SHIM_FILE = "token-goat-shim.js";
4053
+ function legacyShimForwarder(noOp) {
4054
+ const answer = noOp === "" ? "" : `
4055
+ try { process.stdout.write('${noOp}') } catch {}`;
4056
+ return `#!/usr/bin/env node
4057
+ // Forwards to token-goat-shim.cjs for hook commands wired before the shim was renamed.
4058
+ import('./token-goat-shim.cjs').catch(() => {${answer}
4059
+ process.exitCode = 0
4060
+ });
4061
+ `;
4062
+ }
4013
4063
  var SHIM_REQUIRES = `const { spawnSync } = require('node:child_process')
4014
4064
  const path = require('node:path')
4015
4065
  const { pathToFileURL } = require('node:url')`;
@@ -4041,10 +4091,12 @@ var SHIM_TRY_IN_PROCESS = `// Attempts the in-process hook call: import()s dist/
4041
4091
  async function tryInProcess(entryPath, eventName, input, harnessWaitMs) {
4042
4092
  if (!entryPath) return undefined
4043
4093
  try {
4094
+ const payload = JSON.parse(input)
4095
+ const served = await tryServer(entryPath, eventName, input, harnessWaitMs)
4096
+ if (served !== undefined) return served
4044
4097
  const hookLibPath = path.join(path.dirname(entryPath), 'token-goat-hook.mjs')
4045
4098
  if (!require('node:fs').existsSync(hookLibPath)) return undefined
4046
4099
  const mod = await import(pathToFileURL(hookLibPath).href)
4047
- const payload = JSON.parse(input)
4048
4100
  return await mod.relayInProcess(eventName, payload, harnessWaitMs)
4049
4101
  } catch {
4050
4102
  return undefined
@@ -4130,6 +4182,8 @@ ${SHIM_REQUIRES}
4130
4182
 
4131
4183
  ${SHIM_VALID_HOOK_EVENTS}
4132
4184
 
4185
+ ${SHIM_TRY_SERVER}
4186
+
4133
4187
  ${SHIM_TRY_IN_PROCESS}
4134
4188
 
4135
4189
  ${SHIM_MAX_BUFFER_CONST}
@@ -4280,21 +4334,46 @@ function execFormSupportedForVersionOutput(stdout) {
4280
4334
  function expectedHookEntryFor(scriptPath, event) {
4281
4335
  return claudeExecFormHooksSupported() ? hookExecPartsFor(scriptPath, event) : { command: hookCommandFor(scriptPath, event) };
4282
4336
  }
4337
+ function realPathOrNull(p) {
4338
+ if (!path3.isAbsolute(p)) return null;
4339
+ try {
4340
+ return fs3.realpathSync.native(p);
4341
+ } catch {
4342
+ return null;
4343
+ }
4344
+ }
4345
+ function sameHookWord(a, b) {
4346
+ if (a === b) return true;
4347
+ const realA = realPathOrNull(a);
4348
+ return realA !== null && realA === realPathOrNull(b);
4349
+ }
4350
+ function stringFormHookWords(command) {
4351
+ const words = [];
4352
+ for (const m of command.matchAll(/"((?:\\.|[^"\\])*)"|(\S+)/g)) {
4353
+ const quoted = m[1];
4354
+ words.push(quoted === void 0 ? m[2] : process.platform === "win32" ? quoted : quoted.replace(/\\(.)/g, "$1"));
4355
+ }
4356
+ return words;
4357
+ }
4283
4358
  function hookEntryMatches(command, args, expected) {
4284
- if (command !== expected.command) return false;
4285
- const a = args ?? [];
4286
- const b = expected.args ?? [];
4287
- return a.length === b.length && a.every((v, i) => v === b[i]);
4359
+ const execForm = expected.args !== void 0;
4360
+ if (!execForm && args !== void 0 && args.length > 0) return false;
4361
+ const a = execForm ? [command, ...args ?? []] : stringFormHookWords(command);
4362
+ const b = execForm ? [expected.command, ...expected.args] : stringFormHookWords(expected.command);
4363
+ return a.length === b.length && a.every((v, i) => sameHookWord(v, b[i]));
4288
4364
  }
4289
4365
  function claudeHookScriptPath() {
4290
- return path3.join(claudeConfigDir(), "hooks", "token-goat-shim.js");
4366
+ return path3.join(claudeConfigDir(), "hooks", SHIM_FILE);
4367
+ }
4368
+ function claudeLegacyHookScriptPath() {
4369
+ return path3.join(claudeConfigDir(), "hooks", LEGACY_SHIM_FILE);
4291
4370
  }
4292
- function anyScopeReferencesShim(scriptPath, currentScope, alreadyStripped) {
4371
+ function anyScopeReferencesShim(scriptPaths, currentScope, alreadyStripped) {
4293
4372
  const referencesIn = (map) => {
4294
4373
  for (const groups of Object.values(map ?? {})) {
4295
4374
  for (const group of groups) {
4296
4375
  for (const h of group.hooks ?? []) {
4297
- if (h.command.includes(scriptPath) || (h.args ?? []).some((a) => a.includes(scriptPath))) return true;
4376
+ if (scriptPaths.some((p) => h.command.includes(p) || (h.args ?? []).some((a) => a.includes(p)))) return true;
4298
4377
  }
4299
4378
  }
4300
4379
  }
@@ -4363,7 +4442,9 @@ function installHooksScoped(scope) {
4363
4442
  const scriptPath = claudeHookScriptPath();
4364
4443
  const scriptChanged = withInstallScope(void 0, () => {
4365
4444
  ensureDirSync(path3.dirname(scriptPath));
4366
- return writeIfDifferent(scriptPath, CLAUDECODE_HOOK_SCRIPT);
4445
+ const forwarderChanged = writeIfDifferent(claudeLegacyHookScriptPath(), legacyShimForwarder("{}"));
4446
+ const scriptChanged2 = writeIfDifferent(scriptPath, CLAUDECODE_HOOK_SCRIPT);
4447
+ return scriptChanged2 || forwarderChanged;
4367
4448
  });
4368
4449
  let settingsChanged = false;
4369
4450
  for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
@@ -4430,8 +4511,8 @@ function uninstallHooksScoped(scope) {
4430
4511
  const removed = stripOwnHooksFromMap(hooks, isTokenGoatHookCommand);
4431
4512
  const scriptPath = claudeHookScriptPath();
4432
4513
  let removedScript = false;
4433
- if (!anyScopeReferencesShim(scriptPath, scope, hooks)) {
4434
- removedScript = withInstallScope(void 0, () => removeFileInScope(scriptPath));
4514
+ if (!anyScopeReferencesShim([scriptPath, claudeLegacyHookScriptPath()], scope, hooks)) {
4515
+ removedScript = withInstallScope(void 0, () => [scriptPath, claudeLegacyHookScriptPath()].map((p2) => removeFileInScope(p2)).some(Boolean));
4435
4516
  }
4436
4517
  if (!removed) return removedScript;
4437
4518
  if (Object.keys(hooks).length === 0) {
@@ -4610,6 +4691,8 @@ function stripTg(value) {
4610
4691
  return value
4611
4692
  }
4612
4693
 
4694
+ ${SHIM_TRY_SERVER}
4695
+
4613
4696
  ${SHIM_TRY_IN_PROCESS}
4614
4697
 
4615
4698
  ${SHIM_MAX_BUFFER_CONST}
@@ -4729,7 +4812,10 @@ function codexAgentsPath() {
4729
4812
  return path4.join(os2.homedir(), ".codex", "AGENTS.md");
4730
4813
  }
4731
4814
  function codexHookScriptPath() {
4732
- return path4.join(os2.homedir(), ".codex", "hooks", "token-goat-shim.js");
4815
+ return path4.join(os2.homedir(), ".codex", "hooks", SHIM_FILE);
4816
+ }
4817
+ function codexLegacyHookScriptPath() {
4818
+ return path4.join(os2.homedir(), ".codex", "hooks", LEGACY_SHIM_FILE);
4733
4819
  }
4734
4820
  function readCodexConfig(p, opts = {}) {
4735
4821
  let raw;
@@ -4786,6 +4872,7 @@ function installCodex() {
4786
4872
  const scriptPath = codexHookScriptPath();
4787
4873
  ensureDirSync(path4.dirname(scriptPath));
4788
4874
  atomicWriteText(scriptPath, CODEX_HOOK_SCRIPT);
4875
+ atomicWriteText(codexLegacyHookScriptPath(), legacyShimForwarder("{}"));
4789
4876
  const config = readCodexConfig(configPath2, { strict: true });
4790
4877
  const hooks = config.hooks ?? {};
4791
4878
  let hooksChanged = false;
@@ -4939,10 +5026,12 @@ function uninstallCodex() {
4939
5026
  if (stripAgentsBlock(agentsPath)) {
4940
5027
  removedAny = true;
4941
5028
  }
4942
- try {
4943
- fs4.unlinkSync(scriptPath);
4944
- removedAny = true;
4945
- } catch {
5029
+ for (const p of [scriptPath, codexLegacyHookScriptPath()]) {
5030
+ try {
5031
+ fs4.unlinkSync(p);
5032
+ removedAny = true;
5033
+ } catch {
5034
+ }
4946
5035
  }
4947
5036
  removeCreatedBackups(configPath2);
4948
5037
  return removedAny;
@@ -5320,6 +5409,8 @@ function toKimi(parsed) {
5320
5409
  return ''
5321
5410
  }
5322
5411
 
5412
+ ${SHIM_TRY_SERVER}
5413
+
5323
5414
  ${SHIM_TRY_IN_PROCESS}
5324
5415
 
5325
5416
  ${SHIM_MAX_BUFFER_CONST}
@@ -5393,7 +5484,10 @@ function kimiAgentsPath() {
5393
5484
  return path7.join(kimiHome(), "AGENTS.md");
5394
5485
  }
5395
5486
  function kimiHookScriptPath() {
5396
- return path7.join(kimiHome(), "hooks", "token-goat-shim.js");
5487
+ return path7.join(kimiHome(), "hooks", SHIM_FILE);
5488
+ }
5489
+ function kimiLegacyHookScriptPath() {
5490
+ return path7.join(kimiHome(), "hooks", LEGACY_SHIM_FILE);
5397
5491
  }
5398
5492
  function kimiSkillDir() {
5399
5493
  return path7.join(kimiHome(), "skills", "token-goat");
@@ -5463,6 +5557,7 @@ function installKimi() {
5463
5557
  const scriptPath = kimiHookScriptPath();
5464
5558
  ensureDirSync(path7.dirname(scriptPath));
5465
5559
  atomicWriteText(scriptPath, KIMI_HOOK_SCRIPT);
5560
+ atomicWriteText(kimiLegacyHookScriptPath(), legacyShimForwarder(""));
5466
5561
  const config = readKimiConfig(configPath2, { strict: true });
5467
5562
  const existing = Array.isArray(config.hooks) ? config.hooks : [];
5468
5563
  const desired = KIMI_HOOK_EVENTS.map((event) => ({
@@ -5514,12 +5609,14 @@ function uninstallKimi() {
5514
5609
  }
5515
5610
  } catch {
5516
5611
  }
5517
- try {
5518
- if (fs7.existsSync(kimiHookScriptPath())) {
5519
- fs7.rmSync(kimiHookScriptPath(), { force: true });
5520
- removed = true;
5612
+ for (const p of [kimiHookScriptPath(), kimiLegacyHookScriptPath()]) {
5613
+ try {
5614
+ if (fs7.existsSync(p)) {
5615
+ fs7.rmSync(p, { force: true });
5616
+ removed = true;
5617
+ }
5618
+ } catch {
5521
5619
  }
5522
- } catch {
5523
5620
  }
5524
5621
  removeCreatedBackups(configPath2);
5525
5622
  return removed;
@@ -5613,7 +5710,7 @@ function resolveEntryPath(): string | undefined {
5613
5710
  // undefined (triggering the spawnSync fallback below) when entryPath is absent, the
5614
5711
  // sibling file doesn't exist (an install predating this file), or anything else goes
5615
5712
  // wrong -- this must never throw.
5616
- type RelayInProcessFn = (event: string, payload: unknown) => Promise<string>;
5713
+ type RelayInProcessFn = (event: string, payload: unknown, harnessWaitMs?: number, opts?: { elapsedMs?: () => number }) => Promise<string>;
5617
5714
  let cachedRelayInProcess: RelayInProcessFn | undefined;
5618
5715
  async function resolveRelayInProcess(): Promise<RelayInProcessFn | undefined> {
5619
5716
  if (cachedRelayInProcess) return cachedRelayInProcess;
@@ -5681,11 +5778,13 @@ function callHookViaSpawn(event: string, payload: Record<string, unknown>): Reco
5681
5778
  // its own fail-open null rather than being force-killed by pi's own hook timeout
5682
5779
  // budget, ~5000ms) when the in-process path is unavailable or throws.
5683
5780
  async function callHook(event: string, payload: Record<string, unknown>): Promise<Record<string, unknown> | null> {
5781
+ // Timed from here, not from process start: this host lives far longer than one call, so the hook library's default clock (process age) would record the host's uptime as this call's duration.
5782
+ const start = performance.now();
5684
5783
  const relay = await resolveRelayInProcess();
5685
5784
  if (relay) {
5686
5785
  try {
5687
5786
  process.env.TOKEN_GOAT_HARNESS_OVERRIDE = "pi";
5688
- const out = await relay(event, payload);
5787
+ const out = await relay(event, payload, undefined, { elapsedMs: () => performance.now() - start });
5689
5788
  const trimmed = out?.trim();
5690
5789
  if (!trimmed) return null;
5691
5790
  return JSON.parse(trimmed) as Record<string, unknown>;
@@ -6047,10 +6146,12 @@ function callHookViaSpawn(event, payload) {
6047
6146
  // its own fail-open null rather than being force-killed by the host's own hook timeout
6048
6147
  // budget, ~5000ms) when the in-process path is unavailable or throws.
6049
6148
  async function callHook(event, payload) {
6149
+ // Timed from here, not from process start: this host lives far longer than one call, so the hook library's default clock (process age) would record the host's uptime as this call's duration.
6150
+ const start = performance.now();
6050
6151
  const relay = await resolveRelayInProcess();
6051
6152
  if (relay) {
6052
6153
  try {
6053
- const out = await relay(event, payload);
6154
+ const out = await relay(event, payload, undefined, { elapsedMs: () => performance.now() - start });
6054
6155
  const trimmed = out ? out.trim() : "";
6055
6156
  if (!trimmed) return null;
6056
6157
  return JSON.parse(trimmed);
@@ -6580,6 +6681,8 @@ function allowResponse(eventName) {
6580
6681
  return eventName === 'pre_tool_use' ? '{"decision":"allow"}' : '{}'
6581
6682
  }
6582
6683
 
6684
+ ${SHIM_TRY_SERVER}
6685
+
6583
6686
  ${SHIM_TRY_IN_PROCESS}
6584
6687
 
6585
6688
  ${SHIM_MAX_BUFFER_CONST}
@@ -6673,7 +6776,10 @@ function grokConfigPath() {
6673
6776
  return path11.join(grokHooksDir(), "token-goat.json");
6674
6777
  }
6675
6778
  function grokHookScriptPath() {
6676
- return path11.join(grokHooksDir(), "token-goat-shim.js");
6779
+ return path11.join(grokHooksDir(), SHIM_FILE);
6780
+ }
6781
+ function grokLegacyHookScriptPath() {
6782
+ return path11.join(grokHooksDir(), LEGACY_SHIM_FILE);
6677
6783
  }
6678
6784
  function buildConfig(scriptPath) {
6679
6785
  const hooks = {};
@@ -6687,11 +6793,12 @@ function buildConfig(scriptPath) {
6687
6793
  function installGrok() {
6688
6794
  const configPath2 = grokConfigPath();
6689
6795
  const scriptPath = grokHookScriptPath();
6796
+ const forwarderChanged = writeIfDifferent(grokLegacyHookScriptPath(), legacyShimForwarder("{}"));
6690
6797
  const scriptChanged = writeIfDifferent(scriptPath, GROK_HOOK_SCRIPT);
6691
6798
  const desiredText = `${JSON.stringify(buildConfig(scriptPath), null, 2)}
6692
6799
  `;
6693
6800
  const configChanged = writeIfDifferent(configPath2, desiredText, true);
6694
- return { configPath: configPath2, hookScriptPath: scriptPath, alreadyInstalled: !scriptChanged && !configChanged };
6801
+ return { configPath: configPath2, hookScriptPath: scriptPath, alreadyInstalled: !scriptChanged && !forwarderChanged && !configChanged };
6695
6802
  }
6696
6803
  function uninstallGrok() {
6697
6804
  const configPath2 = grokConfigPath();
@@ -6702,10 +6809,12 @@ function uninstallGrok() {
6702
6809
  removedAny = true;
6703
6810
  } catch {
6704
6811
  }
6705
- try {
6706
- fs11.unlinkSync(scriptPath);
6707
- removedAny = true;
6708
- } catch {
6812
+ for (const p of [scriptPath, grokLegacyHookScriptPath()]) {
6813
+ try {
6814
+ fs11.unlinkSync(p);
6815
+ removedAny = true;
6816
+ } catch {
6817
+ }
6709
6818
  }
6710
6819
  removeCreatedBackups(configPath2);
6711
6820
  return removedAny;
@@ -7193,6 +7302,9 @@ init_define_import_meta_env();
7193
7302
  import { spawnSync as spawnSync2 } from "node:child_process";
7194
7303
  import * as fs15 from "node:fs";
7195
7304
  import * as path15 from "node:path";
7305
+ var ProcessListTimeout = class extends Error {
7306
+ };
7307
+ var PROCESS_LIST_TIMEOUT_MS = 2e4;
7196
7308
  var INTERPRETER_BASENAMES = /* @__PURE__ */ new Set(["node.exe", "python.exe", "python3.exe", "pwsh.exe", "powershell.exe", "cmd.exe"]);
7197
7309
  function describeProcess(commandLine) {
7198
7310
  for (const m of commandLine.matchAll(/[^\s"]+\.(?:js|mjs|cjs|ts|py|exe)\b/gi)) {
@@ -7203,11 +7315,14 @@ function describeProcess(commandLine) {
7203
7315
  return trimmed.length > 0 ? trimmed.length > 60 ? trimmed.slice(0, 60) + "\u2026" : trimmed : "(no command line)";
7204
7316
  }
7205
7317
  function checkMcpProcessHealth(processes) {
7206
- if (processes === null) {
7318
+ if (!Array.isArray(processes)) {
7319
+ const failure = processes;
7320
+ const query = `Get-CimInstance Win32_Process -Filter "Name='node.exe'" | Select-Object ProcessId,ParentProcessId,CommandLine`;
7321
+ const advice = failure.transient === true ? `Run doctor again once the machine is less busy, or list them yourself with: ${query}` : `List them yourself with: ${query}`;
7207
7322
  return {
7208
7323
  name: "MCP process health",
7209
7324
  status: "warn",
7210
- message: "could not read the process list (PowerShell did not answer), so duplicate MCP launchers and orphaned Node processes were not checked"
7325
+ message: `could not read the process list (${displaySafeText(failure.reason)}), so duplicate MCP launchers and orphaned Node processes were not checked. ${advice}`
7211
7326
  };
7212
7327
  }
7213
7328
  const byPid = new Set(processes.map((process3) => process3.processId));
@@ -7240,19 +7355,37 @@ function runProcessListCommand() {
7240
7355
  const shell = path15.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
7241
7356
  const result = spawnSync2(fs15.existsSync(shell) ? shell : "powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
7242
7357
  encoding: "utf8",
7243
- timeout: 2e4,
7358
+ timeout: PROCESS_LIST_TIMEOUT_MS,
7244
7359
  maxBuffer: 10 * 1024 * 1024,
7245
7360
  windowsHide: true
7246
7361
  });
7247
- if (result.error !== void 0) throw result.error;
7248
- return result.stdout ?? "";
7362
+ return processListOutput(result);
7363
+ }
7364
+ function processListOutput(result) {
7365
+ if (result.error !== void 0) {
7366
+ const code = result.error.code;
7367
+ if (code === "ETIMEDOUT") throw new ProcessListTimeout(`PowerShell did not finish within ${PROCESS_LIST_TIMEOUT_MS / 1e3}s`);
7368
+ if (code === "ENOENT") throw new Error("powershell.exe was not found");
7369
+ throw result.error;
7370
+ }
7371
+ const stdout = result.stdout ?? "";
7372
+ if (result.status !== 0 && stdout.trim() === "") {
7373
+ const firstLine2 = (result.stderr ?? "").split(/\r?\n/).map((line) => line.trim()).find((line) => line !== "");
7374
+ throw new Error(`PowerShell exited with code ${String(result.status)}${firstLine2 !== void 0 ? `: ${firstLine2}` : ""}`);
7375
+ }
7376
+ return stdout;
7249
7377
  }
7250
7378
  function readWindowsProcesses(runCommand = runProcessListCommand) {
7251
7379
  if (process.platform !== "win32") return [];
7252
7380
  try {
7253
7381
  const output = runCommand().trim();
7254
7382
  if (output === "") return [];
7255
- const parsed = JSON.parse(output);
7383
+ let parsed;
7384
+ try {
7385
+ parsed = JSON.parse(output);
7386
+ } catch {
7387
+ return { reason: "PowerShell printed something other than the JSON process list" };
7388
+ }
7256
7389
  const rows = Array.isArray(parsed) ? parsed : [parsed];
7257
7390
  return rows.flatMap((row) => {
7258
7391
  if (typeof row !== "object" || row === null) return [];
@@ -7265,8 +7398,9 @@ function readWindowsProcesses(runCommand = runProcessListCommand) {
7265
7398
  commandLine: typeof value["CommandLine"] === "string" ? value["CommandLine"] : ""
7266
7399
  }];
7267
7400
  });
7268
- } catch {
7269
- return null;
7401
+ } catch (e) {
7402
+ const reason = e instanceof Error ? e.message : String(e);
7403
+ return e instanceof ProcessListTimeout ? { reason, transient: true } : { reason };
7270
7404
  }
7271
7405
  }
7272
7406
  function checkWorkerRunning(dataDir2) {
@@ -7598,35 +7732,31 @@ function freelistBytes(header) {
7598
7732
  const rawPageSize = header.readUInt16BE(16);
7599
7733
  return (rawPageSize === 1 ? 65536 : rawPageSize) * header.readUInt32BE(36);
7600
7734
  }
7601
- var CATEGORY_COLUMNS = [
7602
- { 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" },
7603
- { name: "refs", table: "refs", column: "context", command: "'token-goat reclaim-index --rebuild' drops and re-derives them" },
7604
- { name: "chunk text", table: "chunks", column: "text", command: "'token-goat reclaim-index --rebuild' drops and re-derives them" },
7605
- { name: "stats detail", table: "stats", column: "detail", command: "ages out on its own (180-day retention) -- no manual command needed" }
7735
+ var STORAGE_GROUPS = [
7736
+ { name: "refs", owns: /^refs$/, command: "grows with the files indexed, so it shrinks only when files leave the index" },
7737
+ { name: "symbols and their search index", owns: /^symbols(_fts\w*)?$/, command: "grows with the files indexed, so it shrinks only when files leave the index" },
7738
+ {
7739
+ name: "embeddings",
7740
+ owns: /^(chunks|chunk_vectors\w*)$/,
7741
+ command: "these back 'semantic'; to drop them, set indexing.embeddings_enabled = false and indexing.auto_reclaim_embeddings = true, then run 'token-goat doctor --repair'"
7742
+ },
7743
+ { name: "usage stats", owns: /^(stats\w*|hint_\w+|unmapped_tools)$/, command: "ages out on its own (180-day retention)" },
7744
+ { name: "recall cache", owns: /^cache_recall\w*$/, command: "ages out on its own" }
7606
7745
  ];
7607
- var VECTOR_BYTES_PER_ROW = 384 * 4;
7746
+ var OWNER_BYTES_SQL = `SELECT COALESCE(m.tbl_name, s.name) AS owner, SUM(s.pgsize) AS bytes FROM dbstat AS s LEFT JOIN sqlite_master AS m ON m.name = s.name WHERE s.aggregate = 1 GROUP BY owner`;
7608
7747
  function dbCategoryBreakdown(dbPath) {
7609
- const db = getDb(dbPath);
7610
- const shares = [];
7611
- for (const cat of CATEGORY_COLUMNS) {
7612
- try {
7613
- const row = db.prepare(`SELECT COALESCE(SUM(LENGTH(${cat.column})), 0) as bytes FROM ${cat.table}`).get();
7614
- if (row.bytes > 0) shares.push({ name: cat.name, bytes: row.bytes, command: cat.command });
7615
- } catch {
7616
- }
7617
- }
7748
+ let rows;
7618
7749
  try {
7619
- const row = db.prepare("SELECT COUNT(*) as n FROM chunk_vectors").get();
7620
- if (row.n > 0) {
7621
- shares.push({
7622
- name: "embedding vectors",
7623
- bytes: row.n * VECTOR_BYTES_PER_ROW,
7624
- command: "'token-goat reclaim-index --rebuild' drops them (set indexing.embeddings_enabled = false first so they don't regrow)"
7625
- });
7626
- }
7750
+ rows = getDb(dbPath).prepare(OWNER_BYTES_SQL).all();
7627
7751
  } catch {
7752
+ return [];
7628
7753
  }
7629
- return shares.sort((a, b) => b.bytes - a.bytes);
7754
+ const shares = STORAGE_GROUPS.map((group) => ({
7755
+ name: group.name,
7756
+ bytes: rows.filter((r) => group.owns.test(r.owner)).reduce((sum, r) => sum + r.bytes, 0),
7757
+ command: group.command
7758
+ }));
7759
+ return shares.filter((share) => share.bytes > 0).sort((a, b) => b.bytes - a.bytes);
7630
7760
  }
7631
7761
  function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories = [], topConsumers = [], thresholdMb = 1500, autoReclaimEmbeddings = false) {
7632
7762
  const mb = (bytes) => Math.round(bytes / (1024 * 1024));
@@ -7637,15 +7767,18 @@ function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories =
7637
7767
  advice.push(`'token-goat doctor --repair' will automatically reclaim embedding vectors and compact global.db`);
7638
7768
  }
7639
7769
  const head = `global.db is ${mb(sizeBytes)} MB at ${displaySafeText(dbPath)} (larger than threshold of ${thresholdMb} MB). `;
7640
- const totalCategoryBytes = categories.reduce((sum, c) => sum + c.bytes, 0);
7770
+ const listed = categories.slice(0, 3);
7771
+ const totalCategoryBytes = listed.reduce((sum, c) => sum + c.bytes, 0);
7641
7772
  const unmeasuredBytes = sizeBytes - totalCategoryBytes;
7642
- const breakdown = totalCategoryBytes > 0 && sizeBytes > 0 ? ` Where it went: ${categories.slice(0, 3).map((c) => `${c.name} ${mb(c.bytes)} MB (${Math.round(c.bytes / sizeBytes * 100)}%) -- ${c.command}`).join("; ")}.${unmeasuredBytes >= sizeBytes / 20 ? ` The other ${mb(unmeasuredBytes)} MB is row overhead and indexes, which these commands do not measure.` : ""}` : "";
7773
+ const breakdown = totalCategoryBytes > 0 && sizeBytes > 0 ? ` Where it went: ${listed.map((c) => `${c.name} ${mb(c.bytes)} MB (${Math.round(c.bytes / sizeBytes * 100)}%) -- ${c.command}`).join("; ")}.${unmeasuredBytes >= sizeBytes / 20 ? ` The other ${mb(unmeasuredBytes)} MB is smaller tables and free pages.` : ""}` : "";
7643
7774
  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}`;
7644
- if (topConsumers.length > 0) {
7775
+ const [top] = topConsumers;
7776
+ if (top !== void 0) {
7645
7777
  const list = topConsumers.map((c) => `${path17.basename(c.root) || c.root} (${countNoun(c.fileCount, "file")})`).join(", ");
7646
7778
  base += ` Top index consumers: ${list}.`;
7779
+ base += ` To shrink it, take out a project you do not need surgical reads in: 'token-goat project exclude "${displaySafeText(top.root)}"' removes its rows, then 'token-goat reclaim-index' returns the space.`;
7647
7780
  }
7648
- return base;
7781
+ return `${base} If this size is expected, raise indexing.max_db_size_mb above ${Math.ceil(sizeBytes / (1024 * 1024))}.`;
7649
7782
  }
7650
7783
  function checkDbExists(dataDir2, maxDbSizeMb) {
7651
7784
  const dbPath = path17.join(dataDir2, "global.db");
@@ -7749,6 +7882,29 @@ function checkSymbolCount(dbPath, rootDir) {
7749
7882
  }
7750
7883
  }
7751
7884
  var EMBED_COVERAGE_WARN_FRACTION = 0.25;
7885
+ function unembeddedReasons(dbPath, rootDir, symbolOnlyKb, maxChunks) {
7886
+ const db = getDb(dbPath);
7887
+ const scope = rootDir === void 0 ? null : projectScopeClause("path");
7888
+ const rows = db.prepare(`SELECT path, sha, embed_sha FROM files${scope === null ? "" : ` WHERE ${scope.clause}`}`).all(...scope === null || rootDir === void 0 ? [] : scope.params(rootDir));
7889
+ let owed = 0;
7890
+ let overSizeCap = 0;
7891
+ for (const row of rows) {
7892
+ if (isUnderSystemTemp(row.path)) continue;
7893
+ if (row.embed_sha === oversizeEmbedSha(row.sha, symbolOnlyKb)) overSizeCap += 1;
7894
+ else if (!isEmbedFresh(row.embed_sha ?? void 0, row.sha, true, true, symbolOnlyKb, maxChunks)) owed += 1;
7895
+ }
7896
+ return { owed, overSizeCap };
7897
+ }
7898
+ function unembeddedReasonText(reasons, sizeKb) {
7899
+ let text = "";
7900
+ if (reasons.owed > 0) {
7901
+ text += ` ${countNoun(reasons.owed, "file")} ${reasons.owed === 1 ? "is" : "are"} still owed an embed: the worker embeds them while it is idle, or run 'token-goat index' in the project to embed them now.`;
7902
+ }
7903
+ if (reasons.overSizeCap > 0) {
7904
+ text += ` ${countNoun(reasons.overSizeCap, "file")} ${reasons.overSizeCap === 1 ? "is" : "are"} over indexing.large_file_symbol_only_kb (currently ${sizeKb} KB) and indexed for symbols only; raise it with 'token-goat config set indexing.large_file_symbol_only_kb <KB>' and the next 'token-goat index' embeds them.`;
7905
+ }
7906
+ return text;
7907
+ }
7752
7908
  function checkEmbeddingCoverage(dbPath, rootDir) {
7753
7909
  if (!fs18.existsSync(dbPath)) {
7754
7910
  return { name: "Embedding coverage", status: "ok", message: "no database yet" };
@@ -7768,7 +7924,7 @@ function checkEmbeddingCoverage(dbPath, rootDir) {
7768
7924
  return {
7769
7925
  name: "Embedding coverage",
7770
7926
  status: "warn",
7771
- message: `only ${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct3}%) have embeddings \u2014 'semantic' searches those files only, and reports finding nothing in the same words it uses after searching everything. Files over indexing.large_file_symbol_only_kb (currently ${sizeKb} KB) are indexed for symbols only and are the usual reason; raise it with 'token-goat config set indexing.large_file_symbol_only_kb <KB>' and re-embed with 'token-goat index --force' to widen coverage. Exact symbol lookups are unaffected`
7927
+ message: `only ${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct3}%) have embeddings, so 'semantic' searches those files only, and reports finding nothing in the same words it uses after searching everything.` + unembeddedReasonText(unembeddedReasons(dbPath, rootDir, sizeKb, cfg.indexing.max_chunks_per_file), sizeKb) + ` Exact symbol lookups are unaffected`
7772
7928
  };
7773
7929
  }
7774
7930
  return {
@@ -8022,16 +8178,33 @@ function checkDiskSpace(dataDir2) {
8022
8178
  }
8023
8179
  return { name: "Disk Space", status: "warn", message: "disk space check unavailable on this platform" };
8024
8180
  }
8025
- function checkCopilotCli(configPath2, scriptPath) {
8026
- if (!fs18.existsSync(configPath2) || !fs18.existsSync(scriptPath)) {
8181
+ function checkCopilotCli(configPath2, scriptPath, scope = "user") {
8182
+ if (!fs18.existsSync(configPath2)) {
8027
8183
  return null;
8028
8184
  }
8185
+ const name = scope === "project" ? "Copilot CLI (project)" : "Copilot CLI";
8186
+ const { install, harness } = copilotHooksRecovery(path17.dirname(configPath2), scope);
8187
+ if (scope === "project" && !hasCreatedConfig(configPath2)) {
8188
+ return {
8189
+ name,
8190
+ status: "warn",
8191
+ message: `hook config at ${configPath2} was not written by token-goat on this machine, so doctor does not run the command it names. If these hooks are yours, run "${install}" to rewrite them here.`
8192
+ };
8193
+ }
8194
+ if (!fs18.existsSync(scriptPath)) {
8195
+ if (!fs18.existsSync(path17.join(path17.dirname(scriptPath), LEGACY_HOOKS_SCRIPT_FILE))) return null;
8196
+ return {
8197
+ name,
8198
+ status: "warn",
8199
+ message: `hooks at ${path17.dirname(scriptPath)} still run ${LEGACY_HOOKS_SCRIPT_FILE} from an older token-goat build, which Node loads as an ES module under any package.json that says "type": "module" and then fails every tool call. Recovery: run "${install}", then fully restart ${harness}.`
8200
+ };
8201
+ }
8029
8202
  let config;
8030
8203
  try {
8031
8204
  config = JSON.parse(fs18.readFileSync(configPath2, "utf-8"));
8032
8205
  } catch (err2) {
8033
8206
  return {
8034
- name: "Copilot CLI",
8207
+ name,
8035
8208
  status: "fail",
8036
8209
  message: `hook config at ${configPath2} is not valid JSON: ${extractErrorMessage(err2, "unknown error")}`
8037
8210
  };
@@ -8039,17 +8212,17 @@ function checkCopilotCli(configPath2, scriptPath) {
8039
8212
  const preToolUseCommand = config.hooks?.["preToolUse"]?.[0]?.command;
8040
8213
  if (typeof preToolUseCommand !== "string" || preToolUseCommand === "") {
8041
8214
  return {
8042
- name: "Copilot CLI",
8215
+ name,
8043
8216
  status: "fail",
8044
- message: `hook config at ${configPath2} has no preToolUse entry; run: token-goat install --copilot`
8217
+ message: `hook config at ${configPath2} has no preToolUse entry; run: ${install}`
8045
8218
  };
8046
8219
  }
8047
8220
  const bakedExecPath = /^"([^"]+)"/.exec(preToolUseCommand)?.[1];
8048
8221
  if (bakedExecPath !== void 0 && !fs18.existsSync(bakedExecPath)) {
8049
8222
  return {
8050
- name: "Copilot CLI",
8223
+ name,
8051
8224
  status: "fail",
8052
- message: `hook points at a node binary that no longer exists (${bakedExecPath}) -- likely stale after an nvm/fnm/volta node upgrade. Recovery: run "token-goat install --copilot", then fully restart Copilot CLI (renaming/reinstalling the hook has no effect on an already-running session -- Copilot caches hook configs at startup).`
8225
+ message: `hook points at a node binary that no longer exists (${bakedExecPath}) -- likely stale after an nvm/fnm/volta node upgrade. Recovery: run "${install}", then fully restart ${harness} (renaming/reinstalling the hook has no effect on an already-running session -- hook configs are cached at startup).`
8053
8226
  };
8054
8227
  }
8055
8228
  const synthetic = JSON.stringify({
@@ -8067,31 +8240,38 @@ function checkCopilotCli(configPath2, scriptPath) {
8067
8240
  });
8068
8241
  if (res.error) {
8069
8242
  return {
8070
- name: "Copilot CLI",
8243
+ name,
8071
8244
  status: "fail",
8072
- message: `hook failed to launch: ${extractErrorMessage(res.error, "unknown error")}. Recovery: run "token-goat install --copilot", then fully restart Copilot CLI.`
8245
+ message: `hook failed to launch: ${extractErrorMessage(res.error, "unknown error")}. Recovery: run "${install}", then fully restart ${harness}.`
8073
8246
  };
8074
8247
  }
8075
8248
  if (res.status !== 0) {
8076
8249
  return {
8077
- name: "Copilot CLI",
8250
+ name,
8078
8251
  status: "fail",
8079
- message: `hook exited with status ${res.status} -- Copilot's preToolUse fails closed on a non-zero exit and denies every tool call for the rest of the session. Recovery: run "token-goat install --copilot", then fully restart Copilot CLI (a live session won't pick up the fix).`
8252
+ message: `hook exited with status ${res.status} -- Copilot's preToolUse fails closed on a non-zero exit and denies every tool call for the rest of the session. Recovery: run "${install}", then fully restart ${harness} (a live session won't pick up the fix).`
8080
8253
  };
8081
8254
  }
8082
8255
  try {
8083
8256
  JSON.parse(res.stdout ?? "");
8084
8257
  } catch {
8085
8258
  return {
8086
- name: "Copilot CLI",
8259
+ name,
8087
8260
  status: "fail",
8088
- message: 'hook did not return valid JSON -- Copilot treats this as a hook error and denies every tool call. Recovery: run "token-goat install --copilot", then fully restart Copilot CLI.'
8261
+ message: `hook did not return valid JSON -- Copilot treats this as a hook error and denies every tool call. Recovery: run "${install}", then fully restart ${harness}.`
8089
8262
  };
8090
8263
  }
8091
8264
  if (!shimIsCurrent(scriptPath, COPILOT_CLI_HOOK_SCRIPT)) {
8092
- return { name: "Copilot CLI", status: "warn", message: staleShimMessage(scriptPath, "Copilot", "token-goat install --copilot") + ", then fully restart Copilot CLI." };
8265
+ return { name, status: "warn", message: staleShimMessage(scriptPath, "Copilot", install) + `, then fully restart ${harness}.` };
8266
+ }
8267
+ return { name, status: "ok", message: "preToolUse hook invokes cleanly and returns valid JSON" };
8268
+ }
8269
+ function copilotHooksRecovery(hooksDir, scope) {
8270
+ const owners = readCopilotHooksOwners(hooksDir);
8271
+ if (!owners.has("copilot") && owners.has("vscode")) {
8272
+ return { install: scope === "project" ? "token-goat install --vscode" : "token-goat install --vscode --user", harness: "VS Code" };
8093
8273
  }
8094
- return { name: "Copilot CLI", status: "ok", message: "preToolUse hook invokes cleanly and returns valid JSON" };
8274
+ return { install: scope === "project" ? "token-goat install --copilot --local" : "token-goat install --copilot", harness: "Copilot CLI" };
8095
8275
  }
8096
8276
  function shimIsCurrent(scriptPath, expected) {
8097
8277
  return fs18.readFileSync(scriptPath, "utf-8").replace(/\r\n/g, "\n") === expected.replace(/\r\n/g, "\n");
@@ -8100,7 +8280,14 @@ function staleShimMessage(scriptPath, harness, reinstall) {
8100
8280
  return `hook shim at ${scriptPath} was written by an older token-goat build, so fixes to it since then are not reaching ${harness}. Recovery: run "${reinstall}"`;
8101
8281
  }
8102
8282
  function checkHookShim(name, scriptPath, expected, reinstall) {
8103
- if (!fs18.existsSync(scriptPath)) return null;
8283
+ if (!fs18.existsSync(scriptPath)) {
8284
+ if (!fs18.existsSync(path17.join(path17.dirname(scriptPath), LEGACY_SHIM_FILE))) return null;
8285
+ return {
8286
+ name,
8287
+ status: "warn",
8288
+ message: `hooks at ${path17.dirname(scriptPath)} still run ${LEGACY_SHIM_FILE} from an older token-goat build, which Node loads as an ES module under any package.json that says "type": "module" and then fails every tool call. Recovery: run "${reinstall}", then restart any running session.`
8289
+ };
8290
+ }
8104
8291
  if (shimIsCurrent(scriptPath, expected)) return { name, status: "ok", message: `hook shim at ${scriptPath} matches this build` };
8105
8292
  return { name, status: "warn", message: staleShimMessage(scriptPath, name, reinstall) + ", then restart any running session." };
8106
8293
  }
@@ -8239,6 +8426,28 @@ function checkUnmappedTools(dbPath, options) {
8239
8426
  return { name, status: "warn", message: `could not read the tool-name histogram: ${extractErrorMessage(e)}` };
8240
8427
  }
8241
8428
  }
8429
+ function checkWorker(dir) {
8430
+ if (checkWorkerRunning(dir)) return { name: "Worker", status: "ok", message: "running" };
8431
+ const refusal2 = fs18.existsSync(dir) ? dataDirWriteRefusal(dir) : void 0;
8432
+ if (refusal2 !== void 0) {
8433
+ return { name: "Worker", status: "fail", message: `not running, and cannot start: ${displaySafeText(dir)} cannot be written (${displaySafeText(extractErrorMessage(refusal2))}); make it writable, then run 'token-goat worker start'` };
8434
+ }
8435
+ return { name: "Worker", status: "warn", message: "not running" };
8436
+ }
8437
+ function checkHookServer(state) {
8438
+ const name = "Hook server";
8439
+ if (!state.enabled) return { name, status: "ok", message: "off (hooks.server or TOKEN_GOAT_HOOK_SERVER); every hook call starts its own process" };
8440
+ if (state.statuses.length > 0) return { name, status: "ok", message: `${state.statuses.length} running (${state.statuses.map((s) => `slot ${s.slot}, pid ${s.pid}, ${s.served} served`).join("; ")})` };
8441
+ if (state.failure !== void 0) return { name, status: "warn", message: `not running, and the last start failed: ${state.failure}. Run 'token-goat hook-server run' to see it in the foreground` };
8442
+ return { name, status: "ok", message: "not running; the next hook call starts one" };
8443
+ }
8444
+ async function runDoctorChecks(dataDir2, configPath2, rootDir, processes) {
8445
+ const results = runDoctor(dataDir2, configPath2, rootDir, processes);
8446
+ const enabled = hookServerEnabled();
8447
+ const dir = dataDir2 || dataDir();
8448
+ results.push(checkHookServer({ enabled, statuses: enabled ? await serverStatuses(dir) : [], failure: readMarker("failed", dir) }));
8449
+ return results;
8450
+ }
8242
8451
  function runDoctor(dataDir2, configPath2, rootDir, processes) {
8243
8452
  const results = [];
8244
8453
  const actualDataDir = dataDir2 || dataDir();
@@ -8246,7 +8455,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
8246
8455
  results.push(checkTsCompiler());
8247
8456
  results.push(checkTreeSitter());
8248
8457
  results.push(checkStrayClaudeMdBlocks());
8249
- results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
8458
+ results.push(checkWorker(actualDataDir));
8250
8459
  const cfg = loadConfig(rootDir);
8251
8460
  results.push(checkDbExists(actualDataDir, cfg.indexing.max_db_size_mb));
8252
8461
  results.push(checkSymbolBodySize(path17.join(actualDataDir, "global.db")));
@@ -8265,6 +8474,8 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
8265
8474
  results.push(checkDiskSpace(actualDataDir));
8266
8475
  const copilotResult = checkCopilotCli(copilotCliConfigPath(), copilotCliScriptPath());
8267
8476
  if (copilotResult) results.push(copilotResult);
8477
+ const copilotProjectResult = checkCopilotCli(copilotCliConfigPath({ local: true }), copilotCliScriptPath({ local: true }), "project");
8478
+ if (copilotProjectResult) results.push(copilotProjectResult);
8268
8479
  const claudeShimResult = checkHookShim("Claude Code", claudeHookScriptPath(), CLAUDECODE_HOOK_SCRIPT, isInstalled("user") || !isInstalled("project") ? "token-goat install" : "token-goat install --project");
8269
8480
  if (claudeShimResult) results.push(claudeShimResult);
8270
8481
  const claudeEventsResult = checkClaudeHookEvents({ user: missingHookEvents("user"), project: missingHookEvents("project") });
@@ -8485,7 +8696,7 @@ async function runDoctorAndExit(opts) {
8485
8696
  console.log();
8486
8697
  }
8487
8698
  }
8488
- const results = runDoctor(options.dataDir, options.configPath, options.rootDir, options.processes);
8699
+ const results = await runDoctorChecks(options.dataDir, options.configPath, options.rootDir, options.processes);
8489
8700
  printDoctorResults(results);
8490
8701
  if (options.context === true) {
8491
8702
  console.log("\n## Context footprint\n");
@@ -8523,7 +8734,7 @@ async function runDoctorAndExit(opts) {
8523
8734
  }
8524
8735
  }
8525
8736
  try {
8526
- const { checkUpdateStatus } = await import("./token-goat-chunk-4JJBFT7V.mjs");
8737
+ const { checkUpdateStatus } = await import("./token-goat-chunk-YAGLESS3.mjs");
8527
8738
  const update = await checkUpdateStatus(1500);
8528
8739
  if (update.updateAvailable && update.latest) {
8529
8740
  console.log(`
@@ -8646,7 +8857,7 @@ function generateCompactHelp() {
8646
8857
  " compress, recall, bash-output, web-output, mcp-output, bench",
8647
8858
  "",
8648
8859
  "Config & Integration: install, uninstall, upgrade, mcp-serve, mcp-status, hook,",
8649
- " bridges-status, worker, statusline, version, config, config-get,",
8860
+ " bridges-status, worker, hook-server, statusline, version, config, config-get,",
8650
8861
  " capabilities, help",
8651
8862
  "",
8652
8863
  "Data: note, note-add, note-get, note-list, describe, sqlite-query,",
@@ -11569,11 +11780,12 @@ function openWithinRoot(rootReal, p) {
11569
11780
  if (!isPathWithinRoot(rootReal, realPath)) return "outside-root";
11570
11781
  let realStat;
11571
11782
  try {
11572
- realStat = fs21.statSync(realPath);
11783
+ realStat = statThroughHandle(realPath);
11573
11784
  } catch {
11574
11785
  return "outside-root";
11575
11786
  }
11576
- if (realStat.dev !== stat.dev || realStat.ino !== stat.ino) return "outside-root";
11787
+ const fdStat = fs21.fstatSync(fd, { bigint: true });
11788
+ if (realStat.dev !== fdStat.dev || realStat.ino !== fdStat.ino) return "outside-root";
11577
11789
  ownershipTransferred = true;
11578
11790
  return { fd, stat };
11579
11791
  } finally {
@@ -13580,9 +13792,9 @@ function cmdNoteAdd(file, opts) {
13580
13792
  const messages = [`No symbol named '${opts.symbol}' is indexed in '${file}'`];
13581
13793
  const allNames = symbolNamesInFile(resolvedPath);
13582
13794
  const available = rankSimilarNames(allNames, opts.symbol);
13583
- if (available.length > 0) messages.push(didYouMean(available));
13795
+ if (available.length > 0) messages.push(...didYouMeanLines(available));
13584
13796
  else if (allNames.length > 0) messages.push(`Try: token-goat outline ${file}`);
13585
- throw new CliError(messages.join("\n"));
13797
+ throw new CliError(messages);
13586
13798
  }
13587
13799
  symbol = opts.symbol;
13588
13800
  fingerprint = fingerprintContent(match2.body);
@@ -13911,9 +14123,9 @@ function cmdInsertSection(file, opts) {
13911
14123
  const allHeadings = listSections(file);
13912
14124
  const messages = [`Section '${opts.after}' not found in '${file}'`];
13913
14125
  const available = filterSimilarHeadings(allHeadings, opts.after);
13914
- if (available.length > 0) messages.push(didYouMean(available));
14126
+ if (available.length > 0) messages.push(...didYouMeanLines(available));
13915
14127
  else if (allHeadings.length > 0) messages.push(`Try: token-goat outline ${file}`);
13916
- throw new CliError(messages.join("\n"));
14128
+ throw new CliError(messages);
13917
14129
  }
13918
14130
  if (result.occurrences !== void 0) {
13919
14131
  const lines = [
@@ -13925,7 +14137,7 @@ function cmdInsertSection(file, opts) {
13925
14137
  if (result.occurrences.length > AMBIGUOUS_HEADING_LIMIT) {
13926
14138
  lines.push(` (${result.occurrences.length - AMBIGUOUS_HEADING_LIMIT} more not shown)`);
13927
14139
  }
13928
- throw new CliError(lines.join("\n"));
14140
+ throw new CliError(lines);
13929
14141
  }
13930
14142
  let rawBytes;
13931
14143
  try {
@@ -16233,8 +16445,7 @@ function collectCapabilities(config = loadConfig()) {
16233
16445
  id: "at_rest.symbol_index",
16234
16446
  kind: "at-rest",
16235
16447
  what: "Stores the text of indexed source files in a local database, so a symbol can be returned without re-reading the file.",
16236
- // Always on: it is the product. Reported anyway, because a reviewer deciding about data at
16237
- // rest needs the always-on items more than the optional ones.
16448
+ // Always on: it is the product. Reported anyway, because a reviewer deciding about data at rest needs the always-on items more than the optional ones.
16238
16449
  enabled: true,
16239
16450
  controlledBy: "always on (this is what the tool does)",
16240
16451
  enforcedAt: "src/db.ts"
@@ -16246,15 +16457,24 @@ function collectCapabilities(config = loadConfig()) {
16246
16457
  enabled: true,
16247
16458
  controlledBy: "always on, 24-hour expiry",
16248
16459
  enforcedAt: "src/disk_cache.ts"
16460
+ },
16461
+ {
16462
+ id: "ipc.hook_server",
16463
+ kind: "local-ipc",
16464
+ what: "Keeps up to three hook servers running between hook calls, each on a named pipe (Windows) or Unix socket (elsewhere) that answers only a caller holding the key file in the data directory.",
16465
+ // Read the way the server reads it, which a project's config cannot change.
16466
+ enabled: hookServerEnabled(),
16467
+ controlledBy: "hooks.server (global config only), TOKEN_GOAT_HOOK_SERVER",
16468
+ enforcedAt: "src/hook_server.ts::runHookServer"
16249
16469
  }
16250
16470
  ];
16251
16471
  }
16252
16472
  function renderCapabilities(caps) {
16253
16473
  const lines = [];
16254
- for (const kind of ["egress", "at-rest"]) {
16474
+ for (const [kind, heading] of [["egress", "Can send data off this machine:"], ["at-rest", "Leaves data on this machine:"], ["local-ipc", "Listens for other processes on this machine:"]]) {
16255
16475
  const group = caps.filter((c) => c.kind === kind);
16256
16476
  if (group.length === 0) continue;
16257
- lines.push(kind === "egress" ? "Can send data off this machine:" : "Leaves data on this machine:");
16477
+ lines.push(heading);
16258
16478
  for (const c of group) {
16259
16479
  lines.push(` [${c.enabled ? "ON " : "OFF"}] ${displaySafeText(c.id)}`);
16260
16480
  lines.push(` ${c.what}`);
@@ -17044,7 +17264,7 @@ async function auditSessionCorpus(opts = {}) {
17044
17264
  windowDays,
17045
17265
  toolErrors: { byTool: [], byModel: [], unknownPrefixes: [] }
17046
17266
  };
17047
- const toolErrorAcc = (await import("./token-goat-chunk-LEG3JNJ6.mjs")).newToolErrorAccumulator();
17267
+ const toolErrorAcc = (await import("./token-goat-chunk-G23DWHV4.mjs")).newToolErrorAccumulator();
17048
17268
  const toolMap = /* @__PURE__ */ new Map();
17049
17269
  const attachmentMap = /* @__PURE__ */ new Map();
17050
17270
  const hookMap = /* @__PURE__ */ new Map();
@@ -18270,6 +18490,11 @@ async function runStatuslineCommand(opts = {}) {
18270
18490
 
18271
18491
  // src/cli_hint_stats.ts
18272
18492
  init_define_import_meta_env();
18493
+ var SESSION_JSON_SCOPE = {
18494
+ session: ["emitted", "actedOn", "efficacyPct", "unobservable", "detected", "bytesEmitted", "legacyEmissions", "totals.spentBytes", "totals.legacyEmissions"],
18495
+ allTime: ["suppressed", "suppressionPermanent", "manualEffective", "manualIneffective"],
18496
+ unavailable: ["totals.savedBytes"]
18497
+ };
18273
18498
  function formatSpentCell(row) {
18274
18499
  if (row.emitted === 0) return "n/a";
18275
18500
  if (row.bytesEmitted === null) return "n/a (legacy)";
@@ -18309,6 +18534,16 @@ TOTAL saved-bytes=${totals.savedBytes} (all-time, every hint kind) spent-byt
18309
18534
  `
18310
18535
  );
18311
18536
  }
18537
+ function printSessionTotals(totals) {
18538
+ const spent = totals.spentBytes === null ? "n/a" : String(totals.spentBytes);
18539
+ const legacyNote = totals.legacyEmissions > 0 ? ` (excludes ${totals.legacyEmissions} legacy emission(s) recorded before spend tracking)` : "";
18540
+ process.stdout.write(
18541
+ `
18542
+ TOTAL (this session) spent-bytes=${spent} (hint_emissions ledger only)${legacyNote}
18543
+ saved-bytes: not available per session, because the stats ledger it comes from records no session id. Run \`token-goat hint-stats\` without --session-id for the all-time figure.
18544
+ `
18545
+ );
18546
+ }
18312
18547
  function runHintStatsCommand(opts = {}) {
18313
18548
  if (opts.reset === true) {
18314
18549
  resetHintStats();
@@ -18327,13 +18562,24 @@ function runHintStatsCommand(opts = {}) {
18327
18562
  `);
18328
18563
  return;
18329
18564
  }
18330
- const rows = getHintStatsSummary();
18565
+ const session = opts.sessionId;
18566
+ const rows = getHintStatsSummary(session);
18331
18567
  if (opts.json === true) {
18332
- process.stdout.write(`${displaySafeJson(rows, 0)}
18568
+ const payload = session === void 0 ? rows : { session, scope: SESSION_JSON_SCOPE, rows, totals: { savedBytes: null, ...getHintSpendTotals(session) } };
18569
+ process.stdout.write(`${displaySafeJson(payload, 0)}
18333
18570
  `);
18334
18571
  return;
18335
18572
  }
18336
- if (rows.every((r) => r.emitted === 0 && r.actedOn === 0 && r.unobservable === 0 && r.detected === 0)) {
18573
+ const noEmissions = rows.every((r) => r.emitted === 0 && r.actedOn === 0 && r.unobservable === 0 && r.detected === 0);
18574
+ if (session !== void 0) {
18575
+ process.stdout.write(
18576
+ `Session: ${displaySafeText(session)}
18577
+ emitted, undisplayed, acted-on, efficacy and spent-bytes count this session only. suppressed, manual+ and manual- are all-time, across every session: suppression is decided on the whole history, and manual marks carry no session.
18578
+ `
18579
+ );
18580
+ if (noEmissions) process.stdout.write("No hint emissions were recorded for this session, so every per-session figure below is 0.\n");
18581
+ }
18582
+ if (session === void 0 && noEmissions) {
18337
18583
  process.stdout.write("No hint emissions recorded yet \u2014 the zeros below are absence of data, not measured ineffectiveness.\n");
18338
18584
  }
18339
18585
  printSummary(rows);
@@ -18359,7 +18605,8 @@ function runHintStatsCommand(opts = {}) {
18359
18605
  hints.backoff_thresholds is empty, so suppression here is permanent rather than a self-healing throttle: ${names} will not emit again on any occasion. Set backoff_thresholds to re-enable probe emissions, or run \`token-goat hint-stats --reset\` to clear the ledger.
18360
18606
  `);
18361
18607
  }
18362
- printTotals(getHintStatsTotals());
18608
+ if (session === void 0) printTotals(getHintStatsTotals());
18609
+ else printSessionTotals(getHintSpendTotals(session));
18363
18610
  }
18364
18611
 
18365
18612
  // src/cli_session.ts
@@ -18392,7 +18639,7 @@ function cmdWaste(opts = {}) {
18392
18639
  });
18393
18640
  }
18394
18641
  async function cmdAudit(opts = {}) {
18395
- const { runAuditCommand } = await import("./token-goat-chunk-IHKZ3QNA.mjs");
18642
+ const { runAuditCommand } = await import("./token-goat-chunk-67BNIKKO.mjs");
18396
18643
  return runAuditCommand({
18397
18644
  ...opts.project !== void 0 ? { project: opts.project } : {},
18398
18645
  ...opts.transcript !== void 0 ? { transcript: opts.transcript } : {},
@@ -18407,7 +18654,7 @@ async function cmdSessionAudit(opts = {}) {
18407
18654
  throw new CliError(err2 instanceof Error ? err2.message : String(err2));
18408
18655
  }
18409
18656
  if (opts.toolErrors === true) {
18410
- const { formatToolErrorCensus } = await import("./token-goat-chunk-LEG3JNJ6.mjs");
18657
+ const { formatToolErrorCensus } = await import("./token-goat-chunk-G23DWHV4.mjs");
18411
18658
  out(opts.json === true ? displaySafeJson({ filesScanned: summary.filesScanned, windowDays: summary.windowDays, ...summary.toolErrors }, 0) : formatToolErrorCensus(summary.toolErrors, summary));
18412
18659
  return;
18413
18660
  }
@@ -18474,6 +18721,13 @@ function cmdStatusline(opts = {}) {
18474
18721
  return runStatuslineCommand({ ...opts.json === true ? { json: true } : {} });
18475
18722
  }
18476
18723
  function cmdHintStats(opts = {}) {
18724
+ if (opts.sessionId !== void 0) {
18725
+ const mutating = [opts.reset === true ? "--reset" : null, opts.markEffective !== void 0 ? "--mark-effective" : null, opts.markIneffective !== void 0 ? "--mark-ineffective" : null].filter((f) => f !== null);
18726
+ if (mutating.length > 0) {
18727
+ throw new CliError(`--session-id cannot be combined with ${mutating.join(", ")}: those change cross-session state (every session's emissions and marks), not one session's view`);
18728
+ }
18729
+ }
18730
+ const session = opts.sessionId === void 0 ? void 0 : resolveHintStatsSession(opts.sessionId);
18477
18731
  if (opts.markEffective !== void 0 && !isHintCategory(opts.markEffective)) {
18478
18732
  throw new CliError(`--mark-effective must be one of: ${HINT_CATEGORIES.join(", ")} (got: ${opts.markEffective})`);
18479
18733
  }
@@ -18484,9 +18738,18 @@ function cmdHintStats(opts = {}) {
18484
18738
  ...opts.json === true ? { json: true } : {},
18485
18739
  ...opts.reset === true ? { reset: true } : {},
18486
18740
  ...opts.markEffective !== void 0 && isHintCategory(opts.markEffective) ? { markEffective: opts.markEffective } : {},
18487
- ...opts.markIneffective !== void 0 && isHintCategory(opts.markIneffective) ? { markIneffective: opts.markIneffective } : {}
18741
+ ...opts.markIneffective !== void 0 && isHintCategory(opts.markIneffective) ? { markIneffective: opts.markIneffective } : {},
18742
+ ...session !== void 0 ? { sessionId: session } : {}
18488
18743
  });
18489
18744
  }
18745
+ function resolveHintStatsSession(arg) {
18746
+ const trimmed = arg.trim();
18747
+ if (trimmed === "") throw new CliError("--session-id needs a session id, or `latest` for the most recent session");
18748
+ if (trimmed !== "latest") return trimmed;
18749
+ const latest = findLatestSessionId();
18750
+ if (latest === null) throw new CliError("--session-id latest: no session state found on disk; pass a session id explicitly");
18751
+ return latest;
18752
+ }
18490
18753
 
18491
18754
  // src/cli_skills.ts
18492
18755
  init_define_import_meta_env();
@@ -18751,10 +19014,10 @@ async function cmdSkillSection(nameHeading, headingArg) {
18751
19014
  }
18752
19015
  const messages = [`Section '${heading}' not found in skill '${skillName}'`];
18753
19016
  const available = filterSimilarHeadings(allHeadings, heading);
18754
- if (available.length > 0) messages.push(didYouMean(available));
19017
+ if (available.length > 0) messages.push(...didYouMeanLines(available));
18755
19018
  else if (allHeadings.length === 0) messages.push(`skill '${skillName}' has no headings`);
18756
19019
  else messages.push(`Try: token-goat outline ${filePath}`);
18757
- throw new CliError(messages.join("\n"));
19020
+ throw new CliError(messages);
18758
19021
  }
18759
19022
  out(extracted);
18760
19023
  }
@@ -18821,6 +19084,7 @@ var METHODOLOGY = {
18821
19084
  billing: "They are not GitHub Copilot usage, provider-reported token consumption, or billing data.",
18822
19085
  byte_derived_formula: "Most read, hook, and command entries go through savedTokensFromBytes in src/stats.ts, which is Math.round(bytes_saved / 4). Image entries are the exception: they are priced in visual tokens, not bytes.",
18823
19086
  filter_estimates: "Output compressors record their filter-calculated delta, measured against what the harness would actually have delivered rather than the full command output. Image entries are priced in visual tokens (28x28-pixel patches, capped at the configured vision tier), because an image is not billed by its byte size; where the pixel dimensions are unavailable the tier ceiling is used.",
19087
+ read_command_baselines: "A read command is credited the bytes of what it replaced, less what it printed. Naming a file (read, section, outline and the rest) replaces reading that file whole, capped at 100,000 bytes per file, about what one Read returns before it truncates. A search over files the caller did not name (symbol NAME, semantic) replaces a search plus a read of the one file wanted, so it is credited the largest matched file under the same cap, not every match. refs is credited the one line per reference a plain text search prints, and changed the diff it stands in for; both are shell output, so both are first cut to what the harness would have delivered of it.",
18824
19088
  advisory_events: "Zero-byte, zero-token advisory events show that guidance fired, not that an agent followed it.",
18825
19089
  audit: "Use stats --full or stats --json for source and command breakdowns; reconcile billing with provider-exported usage data."
18826
19090
  };
@@ -18836,6 +19100,7 @@ function renderMethodology(json = false) {
18836
19100
  `\`tokens saved\` is a ${METHODOLOGY.estimate_scope.toLowerCase()} ${METHODOLOGY.billing}`,
18837
19101
  "",
18838
19102
  `- ${METHODOLOGY.byte_derived_formula}`,
19103
+ `- ${METHODOLOGY.read_command_baselines}`,
18839
19104
  `- ${METHODOLOGY.filter_estimates}`,
18840
19105
  `- ${METHODOLOGY.advisory_events}`,
18841
19106
  `- ${METHODOLOGY.audit}`,
@@ -19251,7 +19516,7 @@ function registerSessionCommands(program2, guard) {
19251
19516
  })
19252
19517
  )
19253
19518
  );
19254
- program2.command("capabilities").description("report every capability that can send data off this machine, and its current state").option("-j, --json", "emit as JSON, for asserting on in a pipeline").action(
19519
+ program2.command("capabilities").description("report every capability that can send data off this machine, leave data on it, or listen for other processes on it, and its current state").option("-j, --json", "emit as JSON, for asserting on in a pipeline").action(
19255
19520
  guard((opts) => {
19256
19521
  const caps = collectCapabilities();
19257
19522
  console.log(opts.json ? displaySafeJson({ version: VERSION, capabilities: caps }) : renderCapabilities(caps));
@@ -19274,7 +19539,7 @@ function registerSessionCommands(program2, guard) {
19274
19539
  ));
19275
19540
  program2.command("mcp-audit").description("MCP server schema cost-vs-usage report: estimate per-server token cost from cached tool calls").option("--project <path>", "project root to analyze").option("--json", "output JSON").action(guard(cmdMcpAudit));
19276
19541
  program2.command("recall [query]").description("search across every cached bash-output, web-output, and mcp-output entry (full-text); with no query, list them newest-first").option("--type <type>", "filter to one cache type: bash, web, or mcp").option("--limit <n>", "max results to return (default: 10)").option("--json", "output JSON").action(guard(cmdRecall));
19277
- program2.command("hint-stats").description("per-category efficacy report for token-goat's discretionary hint hooks (emitted/acted-on/suppression)").option("--json", "output JSON").option("--reset", "clear all tracked emissions and manual marks").option("--mark-effective <category>", 'record a manual "effective" vote for a hint category').option("--mark-ineffective <category>", 'record a manual "ineffective" vote for a hint category').action(guard(cmdHintStats));
19542
+ program2.command("hint-stats").description("per-category efficacy report for token-goat's discretionary hint hooks (emitted/acted-on/suppression); all-time unless --session-id narrows it").option("--json", "output JSON").option("--session-id <id>", "scope emission counts, efficacy and spend to one session id (or `latest`); suppression and manual marks stay all-time, and saved-bytes has no per-session figure").option("--reset", "clear all tracked emissions and manual marks").option("--mark-effective <category>", 'record a manual "effective" vote for a hint category').option("--mark-ineffective <category>", 'record a manual "ineffective" vote for a hint category').action(guard(cmdHintStats));
19278
19543
  program2.command("statusline").description("render one line of terminal status text from a harness statusline payload on stdin (Claude Code statusLine.command)").option("--json", "emit the underlying data as JSON instead of a rendered line (debug)").action(guard(cmdStatusline));
19279
19544
  program2.command("skill-body <name>").description("retrieve a skill's cached body").option("-c, --compact", "print compact slice instead of full body").action(guard(cmdSkillBody));
19280
19545
  program2.command("skill-compact [name]").description("regenerate and cache compact slice for a skill").option("--path <file>", "read the skill body from this file instead of resolving by name").option("--all", "regenerate compacts for all skills, skipping fresh ones").action(guard(cmdSkillCompact));
@@ -19350,6 +19615,39 @@ function registerSessionCommands(program2, guard) {
19350
19615
  program2.command("describe [target] [table]").description("describe columns and structure of a session store table or SQLite database file instead of querying SELECT *").option("-j, --json", "output as JSON").action(guard(cmdDescribe));
19351
19616
  }
19352
19617
 
19618
+ // src/cli_hook_server.ts
19619
+ init_define_import_meta_env();
19620
+ async function cmdHookServerRun(opts) {
19621
+ const slot = Number(opts.slot ?? "0");
19622
+ const { runHookServer } = await import("./token-goat-chunk-ORNFMIER.mjs");
19623
+ try {
19624
+ await runHookServer(slot, (argv) => run(argv));
19625
+ } catch (e) {
19626
+ const message = e instanceof Error ? e.message : String(e);
19627
+ touchMarker("failed", `slot ${slot}: ${message}`);
19628
+ throw new CliError(message);
19629
+ }
19630
+ }
19631
+ function formatHookServerStatus(s, now = Date.now()) {
19632
+ return `slot ${s.slot}: pid ${s.pid}, v${s.version}, up ${formatAge(now - s.startedAt)}, served ${s.served}, errors ${s.errors}, idle ${formatAge(now - s.lastUsedAt)}`;
19633
+ }
19634
+ async function cmdHookServerStatus(opts) {
19635
+ const statuses = await serverStatuses();
19636
+ if (opts.json === true) {
19637
+ out(displaySafeJson(statuses));
19638
+ return;
19639
+ }
19640
+ if (statuses.length === 0) {
19641
+ out("No hook server is running. The next hook call starts one.");
19642
+ return;
19643
+ }
19644
+ for (const s of statuses) out(formatHookServerStatus(s));
19645
+ }
19646
+ async function cmdHookServerStop() {
19647
+ const stopped = await queryServers("stop");
19648
+ out(stopped.length === 0 ? "No hook server is running." : `Stopped ${stopped.length} hook server${stopped.length === 1 ? "" : "s"}.`);
19649
+ }
19650
+
19353
19651
  // src/bridges_status.ts
19354
19652
  init_define_import_meta_env();
19355
19653
  var NO_SERVER_HANDLER_REASON = "token-goat has no registered server-side handler for this event (zero registerHook('notification'|'stop', ...) call sites in src/) -- wiring it client-side would currently be a no-op";
@@ -20043,7 +20341,16 @@ function runBenchCommand(opts) {
20043
20341
 
20044
20342
  // src/cli.ts
20045
20343
  var CliError = class extends Error {
20344
+ lines;
20345
+ constructor(message) {
20346
+ super(typeof message === "string" ? message : message.join("\n"));
20347
+ this.lines = typeof message === "string" ? void 0 : message;
20348
+ }
20046
20349
  };
20350
+ function formatCommandError(e) {
20351
+ if (e instanceof CliError && e.lines !== void 0) return "token-goat: " + e.lines.map(displaySafeText).join("\n");
20352
+ return "token-goat: " + displaySafeText(extractErrorMessage(e));
20353
+ }
20047
20354
  function out(text) {
20048
20355
  const payload = colorStdout() ? text : stripAnsiEscapes(text);
20049
20356
  process.stdout.write(ensureNewline(payload));
@@ -20284,7 +20591,7 @@ async function cmdMcpServe() {
20284
20591
  let StdioServerTransport;
20285
20592
  try {
20286
20593
  ;
20287
- ({ createMcpServer } = await import("./token-goat-chunk-SJEKA7Y2.mjs"));
20594
+ ({ createMcpServer } = await import("./token-goat-chunk-PKRWMHVP.mjs"));
20288
20595
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
20289
20596
  } catch (err2) {
20290
20597
  process.stderr.write(
@@ -20309,7 +20616,7 @@ async function cmdHook(event, opts) {
20309
20616
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
20310
20617
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
20311
20618
  }
20312
- const { relay } = await import("./token-goat-chunk-7MAFDRVR.mjs");
20619
+ const { relay } = await import("./token-goat-chunk-Z6KBMOBI.mjs");
20313
20620
  await relay(event);
20314
20621
  }
20315
20622
  function projectHooksCommitNote(pathFiles, hooksConfigPath) {
@@ -20346,7 +20653,7 @@ async function cmdInstall(opts) {
20346
20653
  if (opts.project === true && opts.user === true) {
20347
20654
  throw new Error("install takes either -p/--project or --user, not both.");
20348
20655
  }
20349
- await import("./token-goat-chunk-7MAFDRVR.mjs");
20656
+ await import("./token-goat-chunk-Z6KBMOBI.mjs");
20350
20657
  const scope = opts.project === true ? "project" : "user";
20351
20658
  if (wantsClaudeCodeBase(opts)) {
20352
20659
  const result = installHooks(scope);
@@ -20528,7 +20835,7 @@ function cmdMcpStatus(opts) {
20528
20835
  const scope = opts.project === true ? { projectRoot: process.cwd() } : {};
20529
20836
  out(displaySafeJson(opts.vscode === true ? vscodeDecoderConfigured(scope) : visualStudioMcpStatus(scope), 0));
20530
20837
  }
20531
- function cmdUninstall(opts) {
20838
+ async function cmdUninstall(opts) {
20532
20839
  if (opts.project === true && opts.user === true) {
20533
20840
  throw new Error("uninstall takes either -p/--project or --user, not both.");
20534
20841
  }
@@ -20578,7 +20885,7 @@ function cmdUninstall(opts) {
20578
20885
  if (opts.hermes === true) {
20579
20886
  out("No separate Hermes integration to remove (it shares the Claude Code hook entries).");
20580
20887
  }
20581
- if (opts.purge === true) runPurge();
20888
+ if (opts.purge === true) await runPurge();
20582
20889
  }
20583
20890
  function leftoverIntegrations(opts) {
20584
20891
  const candidates = [
@@ -20620,7 +20927,8 @@ function leftoverIntegrations(opts) {
20620
20927
  }
20621
20928
  return found;
20622
20929
  }
20623
- function runPurge() {
20930
+ async function runPurge() {
20931
+ await queryServers("stop");
20624
20932
  if (isWorkerRunning()) {
20625
20933
  err('token-goat: the background worker is running, so --purge would delete files it is about to rewrite. Run "token-goat worker stop" first.');
20626
20934
  return;
@@ -20681,7 +20989,7 @@ async function cmdDoctor(opts) {
20681
20989
  doctorOpts.rootDir = project.root;
20682
20990
  }
20683
20991
  if (opts.json === true) {
20684
- const results = runDoctor(doctorOpts.dataDir, doctorOpts.configPath, doctorOpts.rootDir);
20992
+ const results = await runDoctorChecks(doctorOpts.dataDir, doctorOpts.configPath, doctorOpts.rootDir);
20685
20993
  out(displaySafeJson(results, 0));
20686
20994
  if (results.some((r) => r.status === "fail")) {
20687
20995
  throw new CliError("doctor checks failed");
@@ -20937,7 +21245,7 @@ async function cmdCompress(commandArgs, opts = {}) {
20937
21245
  process.exitCode = 1;
20938
21246
  return;
20939
21247
  }
20940
- const bashRunner = await import("./token-goat-chunk-BCN2YQC6.mjs");
21248
+ const bashRunner = await import("./token-goat-chunk-VQ6F6T7X.mjs");
20941
21249
  if (opts.compress === false) {
20942
21250
  process.exitCode = bashRunner.runRaw(
20943
21251
  command,
@@ -20959,7 +21267,7 @@ async function cmdCompress(commandArgs, opts = {}) {
20959
21267
  ...opts.shell !== void 0 ? { shellType: opts.shell } : {}
20960
21268
  });
20961
21269
  } catch (e) {
20962
- err(`token-goat: ${displaySafeText(extractErrorMessage(e))}`);
21270
+ err(formatCommandError(e));
20963
21271
  process.exitCode = 1;
20964
21272
  }
20965
21273
  }
@@ -20991,11 +21299,41 @@ ${content}`;
20991
21299
  const bytesSaved = cappedSourceBytesSaved(fullSourceBytes, Buffer.byteLength(toEmit, "utf8"));
20992
21300
  recordStat("gdrive_sections", bytesSaved, savedTokensFromBytes(bytesSaved));
20993
21301
  }
21302
+ var READ_ONLY_INDEX_COMMANDS = /* @__PURE__ */ new Set([
21303
+ "symbol",
21304
+ "read",
21305
+ "brief",
21306
+ "section",
21307
+ "semantic",
21308
+ "skeleton",
21309
+ "outline",
21310
+ "refs",
21311
+ "answer",
21312
+ "ask",
21313
+ "map",
21314
+ "exports",
21315
+ "imports",
21316
+ "find",
21317
+ "locate",
21318
+ "callers",
21319
+ "call-chain",
21320
+ "impact",
21321
+ "dead",
21322
+ "deps",
21323
+ "types",
21324
+ "scope",
21325
+ "similar",
21326
+ "context-for",
21327
+ "test-for"
21328
+ ]);
20994
21329
  var originalHelpInformation = null;
20995
21330
  function buildProgram() {
20996
21331
  const program2 = new Command();
20997
21332
  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");
20998
- program2.hook("preAction", (thisCommand) => {
21333
+ program2.hook("preAction", (thisCommand, actionCommand) => {
21334
+ if (actionCommand.parent === program2 && READ_ONLY_INDEX_COMMANDS.has(actionCommand.name())) {
21335
+ allowReadOnlyIndex(() => err("token-goat: the index database cannot be written here, so this run reads it without writing: no stats are recorded and changed files are not reindexed."));
21336
+ }
20999
21337
  const opts = thisCommand.opts();
21000
21338
  const cwdOverride = opts.cwd;
21001
21339
  if (cwdOverride !== void 0) {
@@ -21030,8 +21368,7 @@ function buildProgram() {
21030
21368
  process.exitCode = 0;
21031
21369
  }
21032
21370
  } catch (e) {
21033
- const msg = extractErrorMessage(e);
21034
- err(`token-goat: ${displaySafeText(msg)}`);
21371
+ err(formatCommandError(e));
21035
21372
  process.exitCode = 1;
21036
21373
  }
21037
21374
  };
@@ -21188,13 +21525,17 @@ function buildProgram() {
21188
21525
  program2.command("handoff-create <name> [text]").description("create a project-local named compressed handoff").option("--file <path>", "read handoff text from a local file instead of the argument").action(guard(cmdHandoffCreate));
21189
21526
  program2.command("handoff-resolve <name>").description("resolve a project-local handoff compactly, or return it in full").option("--full", "return the full handoff text").action(guard(cmdHandoffResolve));
21190
21527
  program2.command("hook <event>").description("hook relay entrypoint (reads JSON on stdin)").option("--harness <name>", "override harness detection for this invocation (sets TOKEN_GOAT_HARNESS_OVERRIDE)").action(guard(cmdHook));
21191
- program2.command("install").description("install hooks into Claude Code settings").option("-p, --project", "install into project scope instead of user scope").option("--user", "with --vscode, install into user scope instead of this project (every project at once, but nothing past the first folder of a multi-root workspace)").option("--codex", "also patch Codex CLI (~/.codex/config.toml, ~/.codex/AGENTS.md)").option("--gemini", "also patch Gemini CLI (~/.gemini/settings.json)").option("--qwen", "also patch Qwen Code (~/.qwen/settings.json)").option("--kimi", "also register a Kimi Code hook config, shim, instructions block and skill ($KIMI_CODE_HOME or ~/.kimi-code: config.toml, hooks/token-goat-shim.js, AGENTS.md, skills/token-goat/SKILL.md)").option("--pi", "also drop a pi (pi-coding-agent) extension (~/.pi/agent/extensions/token-goat.ts)").option("--opencode", "also drop an opencode plugin (~/.config/opencode/plugins/token-goat.ts, %APPDATA%\\opencode\\plugins\\token-goat.ts on Windows)").option("--hermes", "verify token-goat hooks are present for Hermes Agent (writes nothing new)").option("--openclaw", "also register an OpenClaw plugin (~/.openclaw/openclaw.json, ~/.openclaw/plugins/token-goat.ts)").option("--copilot", "also register a Copilot CLI hook config and routing block (~/.copilot/hooks/token-goat.json, ~/.copilot/hooks/token-goat-shim.js, ~/.copilot/copilot-instructions.md; with --local, <project>/.github/hooks/token-goat.json, <project>/.github/hooks/token-goat-shim.js, <project>/.github/copilot-instructions.md)").option("--grok", "also register a Grok CLI (xAI Grok Build) hook config (~/.grok/hooks/token-goat.json, ~/.grok/hooks/token-goat-shim.js)").option("--vscode", "also configure a VS Code MCP server (the workspace .vscode/mcp.json by default; --user for the user-profile mcp.json) and Copilot routing guidance").option("--visualstudio", "also configure a Visual Studio (2022 17.14+ / 2026) Copilot MCP server and routing guidance, no hooks (%USERPROFILE%\\.mcp.json and %USERPROFILE%\\copilot-instructions.md; -p/--project for <project>/.mcp.json and <project>/.github/copilot-instructions.md)").option("--zed", "also register token-goat as a Zed MCP context server (%APPDATA%\\Zed\\settings.json on Windows, ~/.config/zed/settings.json elsewhere, plus a generated shim script); Zed has no hooks API, so this is user scope only, no -p/--project support").option("--cursor", 'also register a Cursor MCP server (~/.cursor/mcp.json by default; -p/--project for <project>/.cursor/mcp.json); writes no Cursor hooks config -- Cursor already imports the Claude Code hooks "token-goat install" writes to ~/.claude/settings.json').option("--local", "with --pi, install the project-local extension (<project>/.pi/extensions/token-goat.ts) instead of the global one").action(guard(cmdInstall));
21192
- program2.command("uninstall").description("remove token-goat hooks from Claude Code settings").option("-p, --project", "uninstall from project scope instead of user scope").option("--user", "with --vscode, remove the user-scope install instead of this project one").option("--codex", "also strip the Codex CLI integration (~/.codex/config.toml, ~/.codex/AGENTS.md)").option("--gemini", "also strip the Gemini CLI integration (~/.gemini/settings.json)").option("--qwen", "also strip the Qwen Code integration (~/.qwen/settings.json)").option("--kimi", "also strip the Kimi Code integration (config.toml hooks, hooks/token-goat-shim.js, the AGENTS.md block and skills/token-goat under $KIMI_CODE_HOME or ~/.kimi-code)").option("--pi", "also remove the pi (pi-coding-agent) extension").option("--opencode", "also remove the opencode plugin").option("--hermes", "no-op verification flag for symmetry with install (removes no files)").option("--openclaw", "also remove the OpenClaw plugin and config entry").option("--copilot", "also remove the Copilot CLI hook config and shim script, and strip the token-goat block from ~/.copilot/copilot-instructions.md (or <project>/.github/copilot-instructions.md with --local)").option("--grok", "also remove the Grok CLI hook config and shim script").option("--vscode", "also remove the VS Code MCP server (project scope by default; --user for the user-profile one) and routing guidance").option("--visualstudio", "also remove the Visual Studio MCP server entry and routing guidance (user scope by default; -p/--project for the project one)").option("--zed", "also remove the Zed MCP context server entry and its generated shim script").option("--cursor", "also remove the Cursor MCP server entry (user scope by default; -p/--project for the project one)").option("--local", "with --pi, remove the project-local extension instead of the global one").option("--purge", "also delete the data directories (index, caches, session state, logs); refuses while the worker is running").action(guard(cmdUninstall));
21528
+ program2.command("install").description("install hooks into Claude Code settings, or with harness flags into those harnesses instead").option("-p, --project", "install into project scope instead of user scope").option("--user", "with --vscode, install into user scope instead of this project (every project at once, but nothing past the first folder of a multi-root workspace)").option("--codex", "patch Codex CLI (~/.codex/config.toml, ~/.codex/AGENTS.md)").option("--gemini", "patch Gemini CLI (~/.gemini/settings.json)").option("--qwen", "patch Qwen Code (~/.qwen/settings.json)").option("--kimi", "register a Kimi Code hook config, shim, instructions block and skill ($KIMI_CODE_HOME or ~/.kimi-code: config.toml, hooks/token-goat-shim.cjs, AGENTS.md, skills/token-goat/SKILL.md)").option("--pi", "drop a pi (pi-coding-agent) extension (~/.pi/agent/extensions/token-goat.ts)").option("--opencode", "drop an opencode plugin (~/.config/opencode/plugins/token-goat.ts, %APPDATA%\\opencode\\plugins\\token-goat.ts on Windows)").option("--hermes", "verify token-goat hooks are present for Hermes Agent (writes nothing new)").option("--openclaw", "register an OpenClaw plugin (~/.openclaw/openclaw.json, ~/.openclaw/plugins/token-goat.ts)").option("--copilot", "register a Copilot CLI hook config and routing block (~/.copilot/hooks/token-goat.json, ~/.copilot/hooks/token-goat-shim.cjs, ~/.copilot/copilot-instructions.md; with --local, <project>/.github/hooks/token-goat.json, <project>/.github/hooks/token-goat-shim.cjs, <project>/.github/copilot-instructions.md)").option("--grok", "register a Grok CLI (xAI Grok Build) hook config (~/.grok/hooks/token-goat.json, ~/.grok/hooks/token-goat-shim.cjs)").option("--vscode", "configure a VS Code MCP server (the workspace .vscode/mcp.json by default; --user for the user-profile mcp.json) and Copilot routing guidance").option("--visualstudio", "configure a Visual Studio (2022 17.14+ / 2026) Copilot MCP server and routing guidance, no hooks (%USERPROFILE%\\.mcp.json and %USERPROFILE%\\copilot-instructions.md; -p/--project for <project>/.mcp.json and <project>/.github/copilot-instructions.md)").option("--zed", "register token-goat as a Zed MCP context server (%APPDATA%\\Zed\\settings.json on Windows, ~/.config/zed/settings.json elsewhere, plus a generated shim script); Zed has no hooks API, so this is user scope only, no -p/--project support").option("--cursor", 'register a Cursor MCP server (~/.cursor/mcp.json by default; -p/--project for <project>/.cursor/mcp.json); writes no Cursor hooks config -- Cursor already imports the Claude Code hooks "token-goat install" writes to ~/.claude/settings.json').option("--local", "with --pi, install the project-local extension (<project>/.pi/extensions/token-goat.ts) instead of the global one").action(guard(cmdInstall));
21529
+ program2.command("uninstall").description("remove token-goat hooks from Claude Code settings, or with harness flags from those harnesses instead").option("-p, --project", "uninstall from project scope instead of user scope").option("--user", "with --vscode, remove the user-scope install instead of this project one").option("--codex", "strip the Codex CLI integration (~/.codex/config.toml, ~/.codex/AGENTS.md)").option("--gemini", "strip the Gemini CLI integration (~/.gemini/settings.json)").option("--qwen", "strip the Qwen Code integration (~/.qwen/settings.json)").option("--kimi", "strip the Kimi Code integration (config.toml hooks, hooks/token-goat-shim.cjs, the AGENTS.md block and skills/token-goat under $KIMI_CODE_HOME or ~/.kimi-code)").option("--pi", "remove the pi (pi-coding-agent) extension").option("--opencode", "remove the opencode plugin").option("--hermes", "no-op verification flag for symmetry with install (removes no files)").option("--openclaw", "remove the OpenClaw plugin and config entry").option("--copilot", "remove the Copilot CLI hook config and shim script, and strip the token-goat block from ~/.copilot/copilot-instructions.md (or <project>/.github/copilot-instructions.md with --local)").option("--grok", "remove the Grok CLI hook config and shim script").option("--vscode", "remove the VS Code MCP server (project scope by default; --user for the user-profile one) and routing guidance").option("--visualstudio", "remove the Visual Studio MCP server entry and routing guidance (user scope by default; -p/--project for the project one)").option("--zed", "remove the Zed MCP context server entry and its generated shim script").option("--cursor", "remove the Cursor MCP server entry (user scope by default; -p/--project for the project one)").option("--local", "with --pi, remove the project-local extension instead of the global one").option("--purge", "also delete the data directories (index, caches, session state, logs); refuses while the worker is running").action(guard(cmdUninstall));
21193
21530
  program2.command("mcp-status").description("check whether an MCP integration is already configured (used by the VS Code extension)").option("--vscode", "check VS Code mcp.json (user scope, plus workspace scope with --project)").option("--visualstudio", "check the Visual Studio %USERPROFILE%\\.mcp.json (plus the project .mcp.json with --project)").option("-p, --project", "also check the project file: .vscode/mcp.json for --vscode, .mcp.json for --visualstudio (relative to --cwd or the real cwd)").action(guard(cmdMcpStatus));
21194
21531
  const worker = program2.command("worker").description("background indexer lifecycle");
21195
21532
  worker.command("start").description("start the background indexer").action(guard(cmdWorkerStart));
21196
21533
  worker.command("stop").description("stop the background indexer").action(guard(cmdWorkerStop));
21197
21534
  worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
21535
+ const hookServer = program2.command("hook-server").description("resident processes that answer hook calls and read-only commands without starting Node for each one");
21536
+ hookServer.command("run", { hidden: true }).description("serve one slot (started automatically by the first hook call)").option("--slot <n>", "which slot to serve", "0").action(guard(cmdHookServerRun));
21537
+ hookServer.command("status").description("list the running hook servers").option("-j, --json", "output as JSON").action(guard(cmdHookServerStatus));
21538
+ hookServer.command("stop").description("stop every running hook server (the next hook call starts a fresh one)").action(guard(cmdHookServerStop));
21198
21539
  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));
21199
21540
  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));
21200
21541
  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));
@@ -21210,7 +21551,7 @@ function buildProgram() {
21210
21551
  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("--shell <type>", "shell interpreter to run under: bash | pwsh | powershell | native").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("--cap-hint-b64 <payload>", "base64 text printed after the output when --max-tokens cut it (the Bash hook sets it to the narrower read command)").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);
21211
21552
  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(
21212
21553
  (opts) => guard(async () => {
21213
- const { cmdUpgrade } = await import("./token-goat-chunk-4JJBFT7V.mjs");
21554
+ const { cmdUpgrade } = await import("./token-goat-chunk-YAGLESS3.mjs");
21214
21555
  await cmdUpgrade(opts, () => cmdInstall({}));
21215
21556
  })()
21216
21557
  );
@@ -21260,7 +21601,7 @@ async function run(argv = process.argv) {
21260
21601
  return;
21261
21602
  }
21262
21603
  if (argv[2] === "--batch-serve" && typeof argv[3] === "string") {
21263
- const { serveBatch } = await import("./token-goat-chunk-E3K3BTEQ.mjs");
21604
+ const { serveBatch } = await import("./token-goat-chunk-ASLWOQ3I.mjs");
21264
21605
  serveBatch(argv[3], (a) => run(a));
21265
21606
  return;
21266
21607
  }
@@ -21284,80 +21625,81 @@ async function run(argv = process.argv) {
21284
21625
  process.exitCode = 1;
21285
21626
  return;
21286
21627
  }
21287
- const msg = extractErrorMessage(e);
21288
- err(`token-goat: ${displaySafeText(msg)}`);
21628
+ err(formatCommandError(e));
21289
21629
  process.exitCode = 1;
21290
21630
  }
21291
21631
  }
21292
-
21293
21632
  export {
21294
- requireInt,
21295
- requireNonNegativeInt,
21296
- requirePositiveInt,
21297
- expandGlobs,
21298
- fenceFileText,
21299
- fenceFileFieldIfMatched,
21300
- fileSizeOrZero,
21301
- cmdPdfExtract,
21302
- cmdPdfLocate,
21303
- cmdPdfOutline,
21304
- cmdPdfMeta,
21305
- cmdVideoChapters,
21306
- cmdSharepointResolve,
21307
- cmdXlsxSheets,
21308
- cmdXlsxHead,
21309
- cmdXlsxColumns,
21310
- cmdXlsxRange,
21311
- cmdXlsxQuery,
21312
- cmdPptxOutline,
21313
- cmdPptxSlide,
21314
- cmdPptxNotes,
21315
- cmdPptxText,
21633
+ CliError,
21634
+ _applyFiltersAndPrint,
21635
+ applyExitOverride,
21636
+ buildProgram,
21637
+ cmdCsvProfile,
21638
+ cmdCsvQuery,
21639
+ cmdDescribe,
21316
21640
  cmdDocxOutline,
21317
21641
  cmdDocxTables,
21318
21642
  cmdDocxText,
21319
- cmdTranscriptOutline,
21320
- cmdTranscript,
21321
- cmdCsvQuery,
21322
- cmdCsvProfile,
21323
- cmdJsonOutline,
21324
- cmdJsonQuery,
21325
- cmdYamlOutline,
21326
- cmdYamlQuery,
21327
- cmdXmlOutline,
21328
- cmdXmlQuery,
21643
+ cmdHtmlLint,
21329
21644
  cmdHtmlOutline,
21330
21645
  cmdHtmlQuery,
21331
- cmdHtmlLint,
21332
- cmdOpenApiOutline,
21333
- cmdOpenApiOp,
21334
- cmdZipList,
21335
- cmdZipRead,
21336
- cmdNoteAdd,
21337
- cmdWriteFile,
21338
- cmdReplace,
21339
- cmdInsertSection,
21340
- fenceOcrText,
21341
21646
  cmdImageMeta,
21342
21647
  cmdImageText,
21343
- cmdSqliteTables,
21344
- cmdSqliteSchema,
21345
- cmdSqliteQuery,
21648
+ cmdIndex,
21649
+ cmdInsertSection,
21650
+ cmdJsonOutline,
21651
+ cmdJsonQuery,
21652
+ cmdNoteAdd,
21653
+ cmdOpenApiOp,
21654
+ cmdOpenApiOutline,
21655
+ cmdPdfExtract,
21656
+ cmdPdfLocate,
21657
+ cmdPdfMeta,
21658
+ cmdPdfOutline,
21659
+ cmdPptxNotes,
21660
+ cmdPptxOutline,
21661
+ cmdPptxSlide,
21662
+ cmdPptxText,
21663
+ cmdReplace,
21664
+ cmdSessionSchema,
21665
+ cmdSharepointResolve,
21346
21666
  cmdSkillBody,
21347
21667
  cmdSkillCompact,
21348
- cmdSkillList,
21349
- cmdSkillSize,
21350
- cmdSkillHistory,
21351
21668
  cmdSkillDiff,
21669
+ cmdSkillHistory,
21670
+ cmdSkillList,
21352
21671
  cmdSkillSection,
21353
- CliError,
21354
- out,
21672
+ cmdSkillSize,
21673
+ cmdSqliteQuery,
21674
+ cmdSqliteSchema,
21675
+ cmdSqliteTables,
21676
+ cmdTranscript,
21677
+ cmdTranscriptOutline,
21678
+ cmdVideoChapters,
21679
+ cmdWriteFile,
21680
+ cmdXlsxColumns,
21681
+ cmdXlsxHead,
21682
+ cmdXlsxQuery,
21683
+ cmdXlsxRange,
21684
+ cmdXlsxSheets,
21685
+ cmdXmlOutline,
21686
+ cmdXmlQuery,
21687
+ cmdYamlOutline,
21688
+ cmdYamlQuery,
21689
+ cmdZipList,
21690
+ cmdZipRead,
21355
21691
  err,
21356
- cmdIndex,
21357
- visualStudioManualSteps,
21692
+ expandGlobs,
21693
+ fenceFileFieldIfMatched,
21694
+ fenceFileText,
21695
+ fenceOcrText,
21696
+ fileSizeOrZero,
21697
+ formatCommandError,
21358
21698
  leftoverIntegrations,
21359
- _applyFiltersAndPrint,
21360
- buildProgram,
21361
- applyExitOverride,
21362
- run
21699
+ out,
21700
+ requireInt,
21701
+ requireNonNegativeInt,
21702
+ requirePositiveInt,
21703
+ run,
21704
+ visualStudioManualSteps
21363
21705
  };