token-goat 2.9.19 → 2.9.21

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 (50) hide show
  1. package/README.md +3 -1
  2. package/dist/{token-goat-chunk-JNIFSVWM.mjs → token-goat-chunk-2ZHD3XCD.mjs} +5 -1
  3. package/dist/{token-goat-chunk-P46RTOAQ.mjs → token-goat-chunk-4IIU3GNS.mjs} +10 -7
  4. package/dist/{token-goat-chunk-2GTB5RBZ.mjs → token-goat-chunk-4V4OZHHV.mjs} +11 -271
  5. package/dist/token-goat-chunk-64OEF3UN.mjs +128 -0
  6. package/dist/{token-goat-chunk-AVRD7SBO.mjs → token-goat-chunk-6AQLZMCO.mjs} +270 -29
  7. package/dist/{token-goat-chunk-LACH3WKS.mjs → token-goat-chunk-BIEDTF6V.mjs} +3 -3
  8. package/dist/{token-goat-chunk-PIAESKVU.mjs → token-goat-chunk-BKHJRFDE.mjs} +568 -429
  9. package/dist/token-goat-chunk-CAHN4QU7.mjs +26 -0
  10. package/dist/{token-goat-chunk-MU6I4IDI.mjs → token-goat-chunk-CMAJGP3E.mjs} +11 -7
  11. package/dist/token-goat-chunk-CMHBTXMY.mjs +41 -0
  12. package/dist/{token-goat-chunk-HPMMLV37.mjs → token-goat-chunk-CZ5ZFJSN.mjs} +256 -341
  13. package/dist/{token-goat-chunk-7DGTVBWP.mjs → token-goat-chunk-DDP64XR7.mjs} +159 -1863
  14. package/dist/{token-goat-chunk-FX5DHKGF.mjs → token-goat-chunk-DISVHOW6.mjs} +4 -4
  15. package/dist/{token-goat-chunk-L2UOJBV4.mjs → token-goat-chunk-FZN77SZD.mjs} +27 -19
  16. package/dist/{token-goat-chunk-DA6TIDLI.mjs → token-goat-chunk-GO7OWONJ.mjs} +18 -13
  17. package/dist/{token-goat-chunk-EP7TCWHM.mjs → token-goat-chunk-IAMIQLCV.mjs} +83 -66
  18. package/dist/{token-goat-chunk-NJKNAC37.mjs → token-goat-chunk-IJUHQX2M.mjs} +1 -1
  19. package/dist/token-goat-chunk-KQI24O4B.mjs +292 -0
  20. package/dist/{token-goat-chunk-GYC34EGE.mjs → token-goat-chunk-KRPJIXCT.mjs} +2 -2
  21. package/dist/token-goat-chunk-KVAXTA2W.mjs +198 -0
  22. package/dist/{token-goat-chunk-FJZ5MZK7.mjs → token-goat-chunk-LK3YOKDF.mjs} +2 -2
  23. package/dist/token-goat-chunk-M43UJVYQ.mjs +1816 -0
  24. package/dist/{token-goat-chunk-SOKAPOOC.mjs → token-goat-chunk-M6CDVASA.mjs} +46 -47
  25. package/dist/token-goat-chunk-MECDAPXB.mjs +1989 -0
  26. package/dist/token-goat-chunk-MEKVJZUN.mjs +324 -0
  27. package/dist/token-goat-chunk-MQACN7VG.mjs +1379 -0
  28. package/dist/{token-goat-chunk-3W5P673V.mjs → token-goat-chunk-MZQORUX5.mjs} +1 -1
  29. package/dist/{token-goat-chunk-DHIXE6GV.mjs → token-goat-chunk-ORRDWKLA.mjs} +1 -1
  30. package/dist/{token-goat-chunk-F24NE3FO.mjs → token-goat-chunk-PCR43MCM.mjs} +2 -2
  31. package/dist/token-goat-chunk-PDYQA3AP.mjs +248 -0
  32. package/dist/{token-goat-chunk-S7RFIX4Y.mjs → token-goat-chunk-PHDNWISV.mjs} +45 -14
  33. package/dist/token-goat-chunk-PPYZS7TI.mjs +304 -0
  34. package/dist/{token-goat-chunk-BTKDZW7F.mjs → token-goat-chunk-QTIRVK3M.mjs} +1 -1
  35. package/dist/{token-goat-chunk-62VJ47WN.mjs → token-goat-chunk-QUM3UNWS.mjs} +5 -5
  36. package/dist/{token-goat-chunk-KWM6KBPA.mjs → token-goat-chunk-SHV6QQAS.mjs} +1 -1
  37. package/dist/{token-goat-chunk-FQYMZASI.mjs → token-goat-chunk-SMUYPBF5.mjs} +796 -4464
  38. package/dist/{token-goat-chunk-ZGQXRYKE.mjs → token-goat-chunk-TGBSMHPM.mjs} +1 -1
  39. package/dist/{token-goat-chunk-EXLLLIC3.mjs → token-goat-chunk-VXKT3W4C.mjs} +3 -3
  40. package/dist/{token-goat-chunk-WKO5TVNA.mjs → token-goat-chunk-XJUPKJF5.mjs} +544 -576
  41. package/dist/{token-goat-chunk-IBTFGZBX.mjs → token-goat-chunk-XQSSPX3X.mjs} +2 -2
  42. package/dist/{token-goat-chunk-VJQBYSXG.mjs → token-goat-chunk-ZJWPJL7O.mjs} +3 -3
  43. package/dist/token-goat-hook.mjs +17 -11
  44. package/dist/token-goat.core.mjs +27 -19
  45. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  46. package/docs/cli.md +13 -12
  47. package/docs/install.md +1 -1
  48. package/docs/security.md +2 -2
  49. package/package.json +3 -2
  50. package/dist/token-goat-chunk-NYFYIRG3.mjs +0 -35
@@ -2,7 +2,16 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  compareSemver
5
- } from "./token-goat-chunk-GYC34EGE.mjs";
5
+ } from "./token-goat-chunk-KRPJIXCT.mjs";
6
+ import {
7
+ buildCopilotWasteReport,
8
+ findLatestCopilotSession,
9
+ findProjectSession,
10
+ isCopilotTranscript
11
+ } from "./token-goat-chunk-PPYZS7TI.mjs";
12
+ import {
13
+ buildResumePacket
14
+ } from "./token-goat-chunk-64OEF3UN.mjs";
6
15
  import {
7
16
  ALL_SYMBOLS_IN_FILE_LIMIT,
8
17
  AMBIGUOUS_HEADING_LIMIT,
@@ -13,6 +22,8 @@ import {
13
22
  computeFileFingerprint,
14
23
  createHandoff,
15
24
  didYouMean,
25
+ emit,
26
+ emitErr,
16
27
  enclosingSymbol,
17
28
  extraFileArgsNote,
18
29
  extractTranscriptText,
@@ -96,183 +107,195 @@ import {
96
107
  symbolNamesInFile,
97
108
  upsertNote,
98
109
  warnIfFilesStale
99
- } from "./token-goat-chunk-PIAESKVU.mjs";
110
+ } from "./token-goat-chunk-BKHJRFDE.mjs";
100
111
  import {
101
112
  DEFAULT_RECONCILE_BUDGET_MS,
102
113
  HOOK_EVENTS,
103
114
  MCP_TOOL_PATTERN,
104
115
  WEB_OUTPUT_SUBDIR,
105
116
  buildCommandManifest,
106
- buildWasteReport,
107
117
  checkSymbolBodySize,
108
118
  cmdDescribe,
109
119
  cmdSessionSchema,
110
- extractResultText,
111
120
  filterCommandManifest,
112
- findLatestTranscript,
113
121
  formatCommandManifest,
114
122
  getWebOutput,
115
123
  getWebOutputByUrlFromDisk,
116
124
  getWebOutputRaw,
117
- projectTranscriptsDir,
118
125
  readStdinJson,
119
126
  runRecallCommand,
120
127
  runReconcile,
121
- safeStringify,
122
128
  storeWebOutput
123
- } from "./token-goat-chunk-2GTB5RBZ.mjs";
129
+ } from "./token-goat-chunk-4V4OZHHV.mjs";
130
+ import {
131
+ buildWasteReport,
132
+ extractResultText,
133
+ findLatestTranscript,
134
+ projectTranscriptsDir,
135
+ safeStringify
136
+ } from "./token-goat-chunk-KQI24O4B.mjs";
137
+ import {
138
+ buildManifest
139
+ } from "./token-goat-chunk-PDYQA3AP.mjs";
124
140
  import {
125
- AGENT_SALT_MARKER,
126
- CONTEXT_AUTOCOMPACT_TOKENS,
127
141
  GEMINI_TOOL_NAME_MAP,
128
- HOOKS_SCRIPT_FILE,
129
- MATERIALIZE_SHRUNK_IMAGE_JS,
130
142
  MAX_FILES_SCANNED,
131
- SESSIONS_SUBDIR,
132
- SKILLS_OUTPUT_SUBDIR,
133
143
  SKIP_DIRS,
134
144
  WORKER_HEARTBEAT_STALE_MS,
135
145
  WorkerAlreadyRunningError,
136
146
  applyIndexingPriority,
137
147
  auditClaudeMd,
138
148
  buildExtractiveCompact,
139
- buildGuidanceBlock,
140
- buildGuidanceBody,
141
149
  buildLineDiff,
142
- buildManifestWithCount,
143
150
  buildProjectMap,
144
151
  buildStats,
145
- bundledCliPath,
146
- capJsonRows,
147
- claudeConfigDir,
148
152
  cmdReclaimIndex,
149
153
  compactDoc,
150
154
  compactPathFor,
151
155
  confirmAndApply,
152
- contentHash,
153
- copilotCliConfigPath,
154
- copilotCliMcpToolsDir,
155
- copilotCliScriptPath,
156
- copilotCliUserRoot,
157
- copilotHooksFilePaths,
158
156
  detectWalkMode,
159
157
  dirtyQueuePathFor,
160
158
  drainHeartbeatPathFor,
161
- dropEmptyServers,
162
159
  emptyIndexMessage,
163
160
  enqueueDirtyPathSafe,
164
- estimateTokens,
165
- estimateTokensFromLength,
166
- extractChecklistSection,
167
- extractCompactFromMarker,
168
- extractNamedSection,
169
161
  fenceUntrusted,
170
162
  findClaudeMdFiles,
171
163
  findContentDuplicates,
172
- findLatestSessionId,
173
164
  findMemSuggestionCandidates,
174
165
  findMemoryMd,
175
- findOrphanedChunkPaths,
176
166
  findSystemTempFiles,
177
167
  findTopIndexedProjects,
178
- formatAge,
179
- formatBytes,
180
168
  formatMemSuggestions,
181
169
  formatProjectMap,
182
170
  formatSymbolLocation,
183
- formatTokenEstimate,
184
- getContextPressure,
185
171
  getDirtyPathsFor,
186
172
  getEmbeddingCoverage,
187
173
  getHintStatsSummary,
188
174
  getHintStatsTotals,
189
175
  getParserFreshness,
190
176
  getProjectIndexCounts,
191
- getSessionFiles,
192
- getSessionId,
193
- getSkillFilePath,
194
177
  getTrackedFiles,
195
- hasManagedServer,
196
- incrementSkillHit,
178
+ indexSizeBytes,
197
179
  injectionFencingEnabled,
198
- installCopilotCli,
199
- installCopilotHooksFile,
200
- installVisualStudio,
201
180
  isCompactFresh,
202
- isCopilotCliInstalled,
203
181
  isHintCategory,
204
182
  isIgnoredIndexPath,
205
- isManagedServer,
206
183
  isPrivateIpv4Octets,
207
184
  isPrivateIpv6Groups,
208
- isResidueServersJson,
209
185
  isSuppressionCategory,
210
- isVisualStudioInstalled,
211
186
  isWorkerRunning,
212
- jsonc,
213
- listOutputs,
214
- listSkills,
215
- loadSessionCache,
216
- managedServer,
217
187
  mapLookupBytesSaved,
218
188
  markCategoryEffective,
219
189
  markCategoryIneffective,
220
190
  pruneBlockedRoot,
221
191
  pruneDeletedFiles,
222
- pruneOrphanedChunks,
223
- pruneSkillOutputs,
224
192
  pruneSystemTempFiles,
225
193
  pruneUnembeddableChunks,
226
194
  readCompactBody,
227
- readCopilotHooksOwners,
228
- readCopilotMcpTools,
229
- readServersJson,
230
- releaseCopilotHooksFile,
195
+ reclaimIndex,
196
+ recordKnownRootThrottled,
231
197
  removeFileFromIndex,
232
198
  resetHintStats,
233
199
  runContextStats,
234
200
  runDetachedWorkerDaemon,
235
201
  scanAndRecord,
236
- serversOf,
237
- sessionsDir,
238
- setTokenGoatServer,
239
202
  shrinkImage,
240
- skillDescriptionLine,
241
- skillOutputsDir,
242
203
  startDetachedWorker,
243
204
  stopWorker,
244
- storeCompact,
245
- storeOutput,
246
- syncVisualStudioProjectGuidance,
205
+ sweepKnownRoots,
247
206
  toolMatcherFor,
248
- trimToBudget,
249
- uninstallCopilotCli,
250
- uninstallVisualStudio,
251
207
  urlPolicyDenialReason,
252
208
  visionTokensSavedByText,
209
+ walkProject,
210
+ writeCompact
211
+ } from "./token-goat-chunk-SMUYPBF5.mjs";
212
+ import {
213
+ CompressedOutput,
214
+ TOOL_FILTERS,
215
+ ToolFilter,
216
+ deliverCompressed,
217
+ detectFromCommand
218
+ } from "./token-goat-chunk-DDP64XR7.mjs";
219
+ import {
220
+ HOOKS_SCRIPT_FILE,
221
+ MATERIALIZE_SHRUNK_IMAGE_JS,
222
+ buildGuidanceBlock,
223
+ buildGuidanceBody,
224
+ bundledCliPath,
225
+ copilotCliConfigPath,
226
+ copilotCliMcpToolsDir,
227
+ copilotCliScriptPath,
228
+ copilotHooksFilePaths,
229
+ dropEmptyServers,
230
+ hasManagedServer,
231
+ installCopilotCli,
232
+ installCopilotHooksFile,
233
+ installVisualStudio,
234
+ isCopilotCliInstalled,
235
+ isManagedServer,
236
+ isResidueServersJson,
237
+ isVisualStudioInstalled,
238
+ jsonc,
239
+ managedServer,
240
+ readCopilotHooksOwners,
241
+ readServersJson,
242
+ releaseCopilotHooksFile,
243
+ serversOf,
244
+ setTokenGoatServer,
245
+ skillDescriptionLine,
246
+ syncVisualStudioProjectGuidance,
247
+ uninstallCopilotCli,
248
+ uninstallVisualStudio,
253
249
  visualStudioDuplicateNote,
254
250
  visualStudioManagedEntry,
255
251
  visualStudioMcpStatus,
256
252
  visualStudioOtherScopeHasManagedServer,
257
253
  visualStudioProjectMcpPath,
258
254
  visualStudioSolutionVscodeMcpPath,
259
- visualStudioUserMcpPath,
260
- walkProject,
261
- writeCompact
262
- } from "./token-goat-chunk-FQYMZASI.mjs";
255
+ visualStudioUserMcpPath
256
+ } from "./token-goat-chunk-MQACN7VG.mjs";
257
+ import {
258
+ AGENT_SALT_MARKER,
259
+ CONTEXT_AUTOCOMPACT_TOKENS,
260
+ SESSIONS_SUBDIR,
261
+ SKILLS_OUTPUT_SUBDIR,
262
+ contentHash,
263
+ eventCount,
264
+ extractCompactFromMarker,
265
+ extractNamedSection,
266
+ findLatestSessionId,
267
+ formatAge,
268
+ getContextPressure,
269
+ getSessionFiles,
270
+ getSessionId,
271
+ getSkillFilePath,
272
+ incrementSkillHit,
273
+ listOutputs,
274
+ listSkills,
275
+ loadSessionCache,
276
+ loadSessionState,
277
+ pruneSkillOutputs,
278
+ sessionsDir,
279
+ skillOutputsDir,
280
+ storeCompact,
281
+ storeOutput
282
+ } from "./token-goat-chunk-MECDAPXB.mjs";
283
+ import {
284
+ capJsonRows,
285
+ claudeConfigDir,
286
+ estimateTokens,
287
+ estimateTokensFromLength,
288
+ formatBytes,
289
+ formatTokenEstimate,
290
+ trimToBudget
291
+ } from "./token-goat-chunk-MEKVJZUN.mjs";
263
292
  import {
264
293
  BASH_OUTPUT_SUBDIR,
265
- CompressedOutput,
266
- FILTERS,
267
- TOOL_FILTERS,
268
- ToolFilter,
269
294
  clipLongMatchLine,
270
295
  combineStreams,
271
- deliverCompressed,
272
- detectFromCommand,
273
296
  getBashOutput,
274
297
  isRecallCacheType
275
- } from "./token-goat-chunk-7DGTVBWP.mjs";
298
+ } from "./token-goat-chunk-M43UJVYQ.mjs";
276
299
  import {
277
300
  C,
278
301
  DEFAULT_MAX_AGE_MS,
@@ -286,7 +309,6 @@ import {
286
309
  formatLocalTimestamp,
287
310
  getGlobalDb,
288
311
  listBlobs,
289
- loadBlob,
290
312
  pruneBlobs,
291
313
  pruneStalePatternCoveredUnmappedTools,
292
314
  readUnmappedTools,
@@ -297,21 +319,21 @@ import {
297
319
  savedTokensFromBytes,
298
320
  statsHasDurationColumn,
299
321
  statsHasHarnessColumn,
300
- stripAnsi,
322
+ stripAnsiEscapes,
301
323
  summarize
302
- } from "./token-goat-chunk-S7RFIX4Y.mjs";
324
+ } from "./token-goat-chunk-PHDNWISV.mjs";
303
325
  import {
304
326
  docxOutline,
305
327
  docxTables,
306
328
  docxText,
307
329
  formatDocxTables
308
- } from "./token-goat-chunk-DHIXE6GV.mjs";
330
+ } from "./token-goat-chunk-ORRDWKLA.mjs";
309
331
  import {
310
332
  pptxNotesText,
311
333
  pptxOutline,
312
334
  pptxSlideText,
313
335
  pptxTextGrep
314
- } from "./token-goat-chunk-3W5P673V.mjs";
336
+ } from "./token-goat-chunk-MZQORUX5.mjs";
315
337
  import {
316
338
  formatXlsxColumns,
317
339
  formatXlsxRange,
@@ -320,12 +342,13 @@ import {
320
342
  querySheet,
321
343
  rangeSheet,
322
344
  xlsxColumns
323
- } from "./token-goat-chunk-FJZ5MZK7.mjs";
345
+ } from "./token-goat-chunk-LK3YOKDF.mjs";
324
346
  import {
325
347
  formatCsvTable,
326
348
  parseWhereSpecs
327
- } from "./token-goat-chunk-KWM6KBPA.mjs";
349
+ } from "./token-goat-chunk-SHV6QQAS.mjs";
328
350
  import {
351
+ assetEmbedSha,
329
352
  deleteFileEmbeddings,
330
353
  embeddingBackendLoadError,
331
354
  embeddingsDepsAvailable,
@@ -342,18 +365,19 @@ import {
342
365
  isTreeSitterAvailable,
343
366
  listSections,
344
367
  loadRegexExtractors,
368
+ maxChunksEmbedSha,
345
369
  missingTreeSitterGrammarPackages,
346
370
  modelFilesPresent,
347
371
  parserFingerprintForLanguage,
348
372
  readSection,
349
373
  treeSitterCoreAvailable,
350
374
  treeSitterCoreLoadError
351
- } from "./token-goat-chunk-EP7TCWHM.mjs";
375
+ } from "./token-goat-chunk-IAMIQLCV.mjs";
352
376
  import {
353
377
  getFileEntry,
354
378
  getProjectFileEntries,
355
379
  querySymbols
356
- } from "./token-goat-chunk-IBTFGZBX.mjs";
380
+ } from "./token-goat-chunk-XQSSPX3X.mjs";
357
381
  import {
358
382
  TREE_SITTER_LANGUAGES,
359
383
  detectLanguage,
@@ -364,7 +388,7 @@ import {
364
388
  matchGroovySlashy,
365
389
  nonTreeSitterLanguageCount,
366
390
  redactIfDotenv
367
- } from "./token-goat-chunk-JNIFSVWM.mjs";
391
+ } from "./token-goat-chunk-2ZHD3XCD.mjs";
368
392
  import {
369
393
  CONFIG_KEY_ENV_OVERRIDES,
370
394
  PROJECT_LOCKED_KEYS,
@@ -393,7 +417,7 @@ import {
393
417
  stringify,
394
418
  validateEnumField,
395
419
  validateNumericField
396
- } from "./token-goat-chunk-SOKAPOOC.mjs";
420
+ } from "./token-goat-chunk-M6CDVASA.mjs";
397
421
  import {
398
422
  ENV_KEYS,
399
423
  LOCK_WAIT_MS_HARDENED,
@@ -438,7 +462,6 @@ import {
438
462
  requirePositiveStrictInt,
439
463
  resolveOnPath,
440
464
  runGit,
441
- safeSlice,
442
465
  sleepSync,
443
466
  stripDelimitedBlock,
444
467
  stripLower,
@@ -455,7 +478,7 @@ import {
455
478
  withRetryOnLock,
456
479
  writeIfDifferent,
457
480
  writeJsonSettings
458
- } from "./token-goat-chunk-ZGQXRYKE.mjs";
481
+ } from "./token-goat-chunk-TGBSMHPM.mjs";
459
482
  import {
460
483
  UNTRUSTED_FILE_TAG,
461
484
  UNTRUSTED_TOOL_TAG,
@@ -3603,9 +3626,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3603
3626
  * @param {string} [path]
3604
3627
  * @return {(string|null|Command)}
3605
3628
  */
3606
- executableDir(path43) {
3607
- if (path43 === void 0) return this._executableDir;
3608
- this._executableDir = path43;
3629
+ executableDir(path42) {
3630
+ if (path42 === void 0) return this._executableDir;
3631
+ this._executableDir = path42;
3609
3632
  return this;
3610
3633
  }
3611
3634
  /**
@@ -3905,8 +3928,8 @@ function attemptedCommandName(argv) {
3905
3928
  }
3906
3929
 
3907
3930
  // src/cli.ts
3908
- import * as fs46 from "fs";
3909
- import * as path42 from "path";
3931
+ import * as fs45 from "fs";
3932
+ import * as path41 from "path";
3910
3933
 
3911
3934
  // src/walk_index.ts
3912
3935
  init_define_import_meta_env();
@@ -4177,6 +4200,12 @@ main().catch(() => {
4177
4200
  })
4178
4201
  `;
4179
4202
 
4203
+ // src/canonical_skill.ts
4204
+ init_define_import_meta_env();
4205
+ import { gunzipSync } from "node:zlib";
4206
+ var CANONICAL_SKILL_GZIP_BASE64 = "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";
4207
+ var CANONICAL_SKILL_MD = gunzipSync(Buffer.from(CANONICAL_SKILL_GZIP_BASE64, "base64")).toString("utf8");
4208
+
4180
4209
  // src/install.ts
4181
4210
  var HOOK_EVENT_MAP = [
4182
4211
  ["PreToolUse", "pre_tool_use"],
@@ -4476,23 +4505,8 @@ function findStrayClaudeMdBlocks(searchRoot) {
4476
4505
  walk(root, 0);
4477
4506
  return found.sort();
4478
4507
  }
4479
- function skillMdFrontmatter(gdrive) {
4480
- return `---
4481
- name: token-goat
4482
- ${skillDescriptionLine(gdrive)}
4483
- allowed-tools:
4484
- - Bash
4485
- - Read
4486
- - Grep
4487
- - Glob
4488
- ---`;
4489
- }
4490
4508
  function skillMdContent() {
4491
- const gdrive = loadConfig().gdrive.enabled;
4492
- return `${skillMdFrontmatter(gdrive)}
4493
-
4494
- ${buildGuidanceBody("Claude Code's own Read, Grep, and Glob preference rules", { gdrive })}
4495
- `;
4509
+ return CANONICAL_SKILL_MD;
4496
4510
  }
4497
4511
  function skillDir() {
4498
4512
  return path3.join(claudeConfigDir(), "skills", "token-goat");
@@ -4539,6 +4553,9 @@ ${SHIM_REQUIRES}
4539
4553
 
4540
4554
  ${SHIM_VALID_HOOK_EVENTS}
4541
4555
 
4556
+ // Codex sets no env var of its own identifying a hook subprocess; see this module's docblock.
4557
+ process.env.TOKEN_GOAT_HARNESS_OVERRIDE = 'codex'
4558
+
4542
4559
  // Keep in sync with CLAUDE_CODE_EVENT_NAMES in src/hook_registry.ts -- the
4543
4560
  // live/wired 'token-goat hook <event>' path (src/relay.ts) always emits this
4544
4561
  // PascalCase spelling for hookSpecificOutput.hookEventName, never the raw
@@ -4667,7 +4684,7 @@ function groupHasTokenGoat2(groups, matcher, predicate) {
4667
4684
 
4668
4685
  // src/bridges/codex_install.ts
4669
4686
  var CODEX_COMMAND_MARKER = "token-goat-shim";
4670
- var CODEX_MATCHERS = ["view_image|shell|bash", "apply_patch", "web_search"];
4687
+ var CODEX_MATCHERS = ["view_image|Bash|exec|shell|bash", "apply_patch", "web_search"];
4671
4688
  var CODEX_HOOK_EVENTS = ["PreToolUse", "PostToolUse"];
4672
4689
  var CODEX_EVENT_ARG = {
4673
4690
  PreToolUse: "pre_tool_use",
@@ -4950,7 +4967,7 @@ function buildAgentsBlock() {
4950
4967
  return buildGuidanceBlock({
4951
4968
  beginMarker: AGENTS_BEGIN,
4952
4969
  endMarker: AGENTS_END,
4953
- fallbackToolClause: "Codex's native `shell`, `apply_patch`, and `view_image` tools (shell commands like `cat`/`type` run inside `shell`)",
4970
+ fallbackToolClause: "Codex's native `exec`, `apply_patch`, and `view_image` tools (shell commands like `cat`/`type` run inside `exec`)",
4954
4971
  gdrive: loadConfig().gdrive.enabled
4955
4972
  });
4956
4973
  }
@@ -5142,10 +5159,10 @@ function readQwenSettings(p, opts = {}) {
5142
5159
  let parsed;
5143
5160
  try {
5144
5161
  parsed = JSON.parse(raw);
5145
- } catch {
5162
+ } catch (e) {
5146
5163
  if (opts.strict === true) {
5147
5164
  throw new QwenSettingsParseError(
5148
- `Qwen Code settings file '${p}' exists but contains invalid JSON. Fix or back up the file before running install.`
5165
+ `Qwen Code settings file '${p}' exists but contains invalid JSON. Fix or back up the file before running install. (${extractErrorMessage(e)})`
5149
5166
  );
5150
5167
  }
5151
5168
  return {};
@@ -7155,8 +7172,8 @@ import * as path15 from "node:path";
7155
7172
  var INTERPRETER_BASENAMES = /* @__PURE__ */ new Set(["node.exe", "python.exe", "python3.exe", "pwsh.exe", "powershell.exe", "cmd.exe"]);
7156
7173
  function describeProcess(commandLine) {
7157
7174
  for (const m of commandLine.matchAll(/[^\s"]+\.(?:js|mjs|cjs|ts|py|exe)\b/gi)) {
7158
- const basename11 = m[0].replace(/\\/g, "/").split("/").pop() ?? m[0];
7159
- if (!INTERPRETER_BASENAMES.has(basename11.toLowerCase())) return basename11;
7175
+ const basename10 = m[0].replace(/\\/g, "/").split("/").pop() ?? m[0];
7176
+ if (!INTERPRETER_BASENAMES.has(basename10.toLowerCase())) return basename10;
7160
7177
  }
7161
7178
  const trimmed = commandLine.trim();
7162
7179
  return trimmed.length > 0 ? trimmed.length > 60 ? trimmed.slice(0, 60) + "\u2026" : trimmed : "(no command line)";
@@ -7542,7 +7559,7 @@ function checkSecurityPosture(cfg, dataDirPath) {
7542
7559
  }
7543
7560
 
7544
7561
  // src/cli_doctor.ts
7545
- var DB_SIZE_WARN_BYTES = 1024 * 1024 * 1024;
7562
+ var DB_SIZE_WARN_BYTES = 1500 * 1024 * 1024;
7546
7563
  function freelistBytes(header) {
7547
7564
  if (header.length < 40) return 0;
7548
7565
  const rawPageSize = header.readUInt16BE(16);
@@ -7578,12 +7595,15 @@ function dbCategoryBreakdown(dbPath) {
7578
7595
  }
7579
7596
  return shares.sort((a, b) => b.bytes - a.bytes);
7580
7597
  }
7581
- function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories = [], topConsumers = []) {
7598
+ function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories = [], topConsumers = [], thresholdMb = 1500, autoReclaimEmbeddings = false) {
7582
7599
  const mb = (bytes) => Math.round(bytes / (1024 * 1024));
7583
7600
  const advice = [];
7584
7601
  if (freeBytes >= sizeBytes / 10) advice.push(`'token-goat reclaim-index' returns the ${mb(freeBytes)} MB of it that is free pages`);
7585
7602
  if (tempRows > 0) advice.push(`'token-goat project prune' removes ${countNoun(tempRows, "scratch file")} indexed under the OS temp dir`);
7586
- const head = `global.db is ${mb(sizeBytes)} MB at ${displaySafeText(dbPath)} (larger than recommended). `;
7603
+ if (autoReclaimEmbeddings) {
7604
+ advice.push(`'token-goat doctor --repair' will automatically reclaim embedding vectors and compact global.db`);
7605
+ }
7606
+ const head = `global.db is ${mb(sizeBytes)} MB at ${displaySafeText(dbPath)} (larger than threshold of ${thresholdMb} MB). `;
7587
7607
  const totalCategoryBytes = categories.reduce((sum, c) => sum + c.bytes, 0);
7588
7608
  const breakdown = totalCategoryBytes > 0 ? ` Where it went: ${categories.slice(0, 3).map((c) => `${c.name} ${mb(c.bytes)} MB (${Math.round(c.bytes / totalCategoryBytes * 100)}%) -- ${c.command}`).join("; ")}.` : "";
7589
7609
  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}`;
@@ -7593,7 +7613,7 @@ function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories =
7593
7613
  }
7594
7614
  return base;
7595
7615
  }
7596
- function checkDbExists(dataDir2) {
7616
+ function checkDbExists(dataDir2, maxDbSizeMb) {
7597
7617
  const dbPath = path17.join(dataDir2, "global.db");
7598
7618
  if (!fs18.existsSync(dbPath)) {
7599
7619
  return {
@@ -7602,7 +7622,7 @@ function checkDbExists(dataDir2) {
7602
7622
  message: `global.db not found at ${dbPath}`
7603
7623
  };
7604
7624
  }
7605
- const sizeBytes = fs18.statSync(dbPath).size;
7625
+ const sizeBytes = indexSizeBytes(dbPath);
7606
7626
  const SQLITE_HEADER = "SQLite format 3\0";
7607
7627
  let header = "";
7608
7628
  let headerBytes = Buffer.alloc(0);
@@ -7625,7 +7645,10 @@ function checkDbExists(dataDir2) {
7625
7645
  message: `global.db at ${dbPath} is not a valid SQLite file (${sizeBytes} bytes) \u2014 likely truncated or corrupt`
7626
7646
  };
7627
7647
  }
7628
- if (sizeBytes > DB_SIZE_WARN_BYTES) {
7648
+ const cfg = loadConfig();
7649
+ const thresholdMb = maxDbSizeMb ?? cfg.indexing.max_db_size_mb ?? 1500;
7650
+ const warnBytes = thresholdMb * 1024 * 1024;
7651
+ if (sizeBytes > warnBytes) {
7629
7652
  let tempRows = 0;
7630
7653
  try {
7631
7654
  tempRows = findSystemTempFiles(dbPath).length;
@@ -7637,7 +7660,20 @@ function checkDbExists(dataDir2) {
7637
7660
  } catch {
7638
7661
  }
7639
7662
  const topConsumers = findTopIndexedProjects(dbPath, 3);
7640
- return { name: "Database", status: "warn", message: oversizeDbMessage(dbPath, sizeBytes, freelistBytes(headerBytes), tempRows, categories, topConsumers) };
7663
+ return {
7664
+ name: "Database",
7665
+ status: "warn",
7666
+ message: oversizeDbMessage(
7667
+ dbPath,
7668
+ sizeBytes,
7669
+ freelistBytes(headerBytes),
7670
+ tempRows,
7671
+ categories,
7672
+ topConsumers,
7673
+ thresholdMb,
7674
+ cfg.indexing.auto_reclaim_embeddings
7675
+ )
7676
+ };
7641
7677
  }
7642
7678
  return {
7643
7679
  name: "Database",
@@ -8163,7 +8199,8 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
8163
8199
  results.push(checkTreeSitter());
8164
8200
  results.push(checkStrayClaudeMdBlocks());
8165
8201
  results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
8166
- results.push(checkDbExists(actualDataDir));
8202
+ const cfg = loadConfig(rootDir);
8203
+ results.push(checkDbExists(actualDataDir, cfg.indexing.max_db_size_mb));
8167
8204
  results.push(checkSymbolBodySize(path17.join(actualDataDir, "global.db")));
8168
8205
  results.push(checkSymbolCount(path17.join(actualDataDir, "global.db"), rootDir));
8169
8206
  results.push(checkDirtyQueueHealth(actualDataDir));
@@ -8255,6 +8292,22 @@ async function runDoctorRepair(opts) {
8255
8292
  } catch (e) {
8256
8293
  errors.push(`Failed to clean up ${displaySafeText(projectMcp)}: ${extractErrorMessage(e)}`);
8257
8294
  }
8295
+ const actualDataDir = opts?.dataDir ?? dataDir();
8296
+ const dbPath = path17.join(actualDataDir, "global.db");
8297
+ if (cfg.indexing.auto_reclaim_embeddings && fs18.existsSync(dbPath)) {
8298
+ try {
8299
+ const sizeBytes = indexSizeBytes(dbPath);
8300
+ const thresholdMb = cfg.indexing.max_db_size_mb ?? 1500;
8301
+ const maxBytes = thresholdMb * 1024 * 1024;
8302
+ if (sizeBytes > maxBytes) {
8303
+ const res = reclaimIndex(dbPath, { embeddingsOnly: true });
8304
+ const savedMb = Math.round((res.beforeBytes - res.afterBytes) / (1024 * 1024));
8305
+ repairs.push(`Auto-reclaimed embeddings and compacted global.db (${savedMb > 0 ? `reclaimed ${savedMb} MB` : "compacted"})`);
8306
+ }
8307
+ } catch (e) {
8308
+ errors.push(`Failed to auto-reclaim embeddings from global.db: ${extractErrorMessage(e)}`);
8309
+ }
8310
+ }
8258
8311
  return { repairs, errors };
8259
8312
  }
8260
8313
  function printDoctorResults(results) {
@@ -8416,7 +8469,7 @@ async function runDoctorAndExit(opts) {
8416
8469
  }
8417
8470
  }
8418
8471
  try {
8419
- const { checkUpdateStatus } = await import("./token-goat-chunk-VJQBYSXG.mjs");
8472
+ const { checkUpdateStatus } = await import("./token-goat-chunk-ZJWPJL7O.mjs");
8420
8473
  const update = await checkUpdateStatus(1500);
8421
8474
  if (update.updateAvailable && update.latest) {
8422
8475
  console.log(`
@@ -8851,13 +8904,6 @@ function performHttpFetch(targetUrl, opts) {
8851
8904
  }
8852
8905
 
8853
8906
  // src/config_commands.ts
8854
- function emit(text) {
8855
- const payload = colorStdout() ? text : stripAnsi(text);
8856
- process.stdout.write(ensureNewline(payload));
8857
- }
8858
- function emitErr(text) {
8859
- process.stderr.write(ensureNewline(text));
8860
- }
8861
8907
  function saveConfigSafe(cfg) {
8862
8908
  ensureDirSync(path18.dirname(configPath()));
8863
8909
  saveConfig(cfg);
@@ -9276,10 +9322,11 @@ function cmdProject(opts) {
9276
9322
  const removed = before.length - after.length;
9277
9323
  const stale = before.filter((r) => !after.includes(r));
9278
9324
  const staleTempFiles = findSystemTempFiles();
9279
- const orphanChunkPaths = findOrphanedChunkPaths();
9325
+ const sweep = sweepKnownRoots(void 0, { dryRun: opts.dryRun === true });
9326
+ const orphanChunkPaths = sweep.prunedOrphanChunkPaths;
9280
9327
  if (opts.dryRun === true) {
9281
9328
  if (opts.json === true) {
9282
- emit(displaySafeJson({ dryRun: true, wouldPrune: removed, stale, wouldPruneTempFiles: staleTempFiles.length, staleTempFiles, wouldPruneOrphanChunkFiles: orphanChunkPaths.length, orphanChunkPaths, blocked_roots: before }));
9329
+ emit(displaySafeJson({ dryRun: true, wouldPrune: removed, stale, wouldPruneTempFiles: staleTempFiles.length, staleTempFiles, wouldPruneOrphanChunkFiles: orphanChunkPaths.length, orphanChunkPaths, wouldPruneDeadFileRows: sweep.prunedRows, deadRowRoots: sweep.prunedRoots, flaggedRoots: sweep.flaggedRoots, blocked_roots: before }));
9283
9330
  return;
9284
9331
  }
9285
9332
  if (removed === 0) {
@@ -9300,20 +9347,31 @@ function cmdProject(opts) {
9300
9347
  emit(`Would prune orphaned embedding chunks for ${orphanChunkPaths.length} file(s):`);
9301
9348
  for (const p of orphanChunkPaths) emit(` ${p}`);
9302
9349
  }
9350
+ if (sweep.prunedRows === 0) {
9351
+ emit("Would prune 0 dead file row(s) under known roots. Nothing to do.");
9352
+ } else {
9353
+ emit(`Would prune ${sweep.prunedRows} dead file row(s) under ${sweep.prunedRoots.length} known root(s):`);
9354
+ for (const r of sweep.prunedRoots) emit(` ${r}`);
9355
+ }
9356
+ for (const r of sweep.flaggedRoots) emit(` skipped (too many rows would go at once, root may be partly offline): ${r}`);
9303
9357
  return;
9304
9358
  }
9305
9359
  cfg.worker.blocked_roots = after;
9306
9360
  saveConfigSafe(cfg);
9307
9361
  invalidateConfigCache();
9308
9362
  const prunedTempFiles = pruneSystemTempFiles();
9309
- const prunedOrphanChunks = pruneOrphanedChunks();
9363
+ const prunedOrphanChunks = sweep.prunedOrphanChunkPaths;
9364
+ const prunedOrphanVectors = sweep.prunedOrphanVectors;
9310
9365
  if (opts.json === true) {
9311
- emit(displaySafeJson({ pruned: removed, blocked_roots: after, prunedTempFiles: prunedTempFiles.length, prunedOrphanChunkFiles: prunedOrphanChunks.length }));
9366
+ emit(displaySafeJson({ pruned: removed, blocked_roots: after, prunedTempFiles: prunedTempFiles.length, prunedOrphanChunkFiles: prunedOrphanChunks.length, prunedOrphanVectors, prunedDeadFileRows: sweep.prunedRows, deadRowRoots: sweep.prunedRoots, flaggedRoots: sweep.flaggedRoots }));
9312
9367
  return;
9313
9368
  }
9314
9369
  emit(`Pruned ${removed} stale root(s). Remaining: ${after.length}`);
9315
9370
  emit(`Pruned ${prunedTempFiles.length} stale indexed temp-dir file(s).`);
9316
9371
  emit(`Pruned orphaned embedding chunks for ${prunedOrphanChunks.length} file(s).`);
9372
+ emit(`Pruned ${prunedOrphanVectors} orphaned embedding vector(s).`);
9373
+ emit(`Pruned ${sweep.prunedRows} dead file row(s) under ${sweep.prunedRoots.length} known root(s).`);
9374
+ for (const r of sweep.flaggedRoots) emit(` skipped (too many rows would go at once, root may be partly offline): ${r}`);
9317
9375
  return;
9318
9376
  }
9319
9377
  emitErr(`project: unknown action '${action}'. Use list, exclude, or prune.`);
@@ -12211,7 +12269,7 @@ function extractGeneric(lines) {
12211
12269
  return result;
12212
12270
  }
12213
12271
  function extractFailures(text, options) {
12214
- const cleaned = stripAnsi(text);
12272
+ const cleaned = stripAnsiEscapes(text);
12215
12273
  const lines = cleaned.split("\n");
12216
12274
  const detected = options?.runner ?? detectRunner(cleaned);
12217
12275
  if (detected === "pytest") {
@@ -12386,7 +12444,8 @@ function cmdConflicts(targetPath, opts) {
12386
12444
  process.exitCode = runConflicts({
12387
12445
  ...targetPath !== void 0 ? { path: targetPath } : {},
12388
12446
  ...opts.json === true ? { json: true } : {},
12389
- ...opts.summary === true ? { summary: true } : {}
12447
+ ...opts.summary === true ? { summary: true } : {},
12448
+ ...opts.context !== void 0 ? { context: parseInt(opts.context, 10) } : {}
12390
12449
  });
12391
12450
  }
12392
12451
  async function cmdScreenshot(url, destPath, opts) {
@@ -12434,7 +12493,7 @@ function cmdPack(patterns, opts) {
12434
12493
  }
12435
12494
  const result = expandedList.length > 0 ? collectFiles(root, expandedList, collectOpts) : collectFromStdin(root, collectOpts);
12436
12495
  if (opts.budget !== void 0) {
12437
- const budgetN = requireInt2("--budget", opts.budget);
12496
+ const budgetN = requireInt("--budget", opts.budget);
12438
12497
  if (result.total_tokens > budgetN) {
12439
12498
  err(`token-goat: pack: token count ${result.total_tokens} exceeds budget ${budgetN}`);
12440
12499
  process.exitCode = 3;
@@ -12471,7 +12530,7 @@ function cmdTokens(patterns, opts) {
12471
12530
  let entries = [...result.entries];
12472
12531
  if (opts.asc === true) entries.reverse();
12473
12532
  const eligibleCount = entries.length;
12474
- if (opts.top !== void 0) entries = entries.slice(0, requireNonNegativeInt2("--top", opts.top));
12533
+ if (opts.top !== void 0) entries = entries.slice(0, requireNonNegativeInt("--top", opts.top));
12475
12534
  const truncated = entries.length < eligibleCount;
12476
12535
  if (opts.json === true) {
12477
12536
  out(displaySafeJson({ entries, truncated, totalCount: eligibleCount, total_tokens: result.total_tokens, total_lines: result.total_lines }));
@@ -12519,7 +12578,7 @@ function cmdBudget(patterns, opts) {
12519
12578
  if (opts.json === true) {
12520
12579
  out(displaySafeJson(result));
12521
12580
  } else {
12522
- const contextK = opts.context !== void 0 ? requirePositiveInt2("--context", opts.context) : Math.round(loadConfig().context.model_window_tokens / 1e3);
12581
+ const contextK = opts.context !== void 0 ? requirePositiveInt("--context", opts.context) : Math.round(loadConfig().context.model_window_tokens / 1e3);
12523
12582
  out(formatBudgetText(result, contextK));
12524
12583
  }
12525
12584
  }
@@ -12866,8 +12925,8 @@ async function cmdPdfLocate(file, pattern, opts) {
12866
12925
  const locateOpts = {
12867
12926
  ignoreCase: opts.ignoreCase === true
12868
12927
  };
12869
- if (opts.maxMatches !== void 0) locateOpts.maxMatches = requirePositiveInt2("--max-matches", opts.maxMatches);
12870
- if (opts.context !== void 0) locateOpts.context = requirePositiveInt2("--context", opts.context);
12928
+ if (opts.maxMatches !== void 0) locateOpts.maxMatches = requirePositiveInt("--max-matches", opts.maxMatches);
12929
+ if (opts.context !== void 0) locateOpts.context = requirePositiveInt("--context", opts.context);
12871
12930
  if (opts.pages !== void 0) locateOpts.pages = opts.pages;
12872
12931
  const { matches: matches2, truncated } = await runPdfLocate(file, pattern, locateOpts);
12873
12932
  const pages = matches2.map((m) => m.page);
@@ -12977,14 +13036,14 @@ async function cmdXlsxSheets(file, opts = {}) {
12977
13036
  recordXlsxStat("xlsx_sheets", file, text);
12978
13037
  }
12979
13038
  async function cmdXlsxHead(file, opts) {
12980
- const rows = opts.rows !== void 0 ? requireNonNegativeInt2("--rows", opts.rows) : 20;
13039
+ const rows = opts.rows !== void 0 ? requireNonNegativeInt("--rows", opts.rows) : 20;
12981
13040
  const columns = opts.columns ? opts.columns.split(",").map((c) => c.trim()).filter(Boolean) : void 0;
12982
13041
  const text = fenceFileText(await headSheet(file, opts.sheet, rows, columns));
12983
13042
  out(text);
12984
13043
  recordXlsxStat("xlsx_head", file, text);
12985
13044
  }
12986
13045
  async function cmdXlsxColumns(file, opts = {}) {
12987
- const maxRows = opts.head !== void 0 ? requireNonNegativeInt2("--head", opts.head) : 100;
13046
+ const maxRows = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 100;
12988
13047
  const result = await xlsxColumns(file, opts.sheet, maxRows);
12989
13048
  if (opts.json === true) {
12990
13049
  const text = displaySafeJson({
@@ -13012,7 +13071,7 @@ async function cmdXlsxQuery(file, opts) {
13012
13071
  const result = await querySheet(file, opts.sheet, {
13013
13072
  ...columns !== void 0 ? { columns } : {},
13014
13073
  ...wheres !== void 0 ? { wheres } : {},
13015
- ...opts.head !== void 0 ? { head: requireNonNegativeInt2("--head", opts.head) } : {}
13074
+ ...opts.head !== void 0 ? { head: requireNonNegativeInt("--head", opts.head) } : {}
13016
13075
  });
13017
13076
  if (opts.json === true) {
13018
13077
  const rowsJson = result.rows.map((r) => Object.fromEntries(result.header.map((h, i) => [h, fenceFileFieldIfMatched(r[i] ?? "")])));
@@ -13044,13 +13103,13 @@ async function cmdPptxOutline(file, opts) {
13044
13103
  recordDocStat("pptx_outline", file, text);
13045
13104
  }
13046
13105
  async function cmdPptxSlide(file, opts) {
13047
- const n = requireNonNegativeInt2("--slide", opts.slide);
13106
+ const n = requireNonNegativeInt("--slide", opts.slide);
13048
13107
  const text = fenceFileText(await pptxSlideText(file, n, opts.notes === true));
13049
13108
  out(text);
13050
13109
  recordDocStat("pptx_slide", file, text);
13051
13110
  }
13052
13111
  async function cmdPptxNotes(file, opts) {
13053
- const n = opts.slide !== void 0 ? requireNonNegativeInt2("--slide", opts.slide) : void 0;
13112
+ const n = opts.slide !== void 0 ? requireNonNegativeInt("--slide", opts.slide) : void 0;
13054
13113
  const text = await pptxNotesText(file, n);
13055
13114
  const printed = text.length > 0 ? fenceFileText(text) : "no speaker notes found";
13056
13115
  out(printed);
@@ -13179,7 +13238,7 @@ function cmdXmlOutline(file, opts) {
13179
13238
  process.exitCode = runXmlOutline({
13180
13239
  file,
13181
13240
  ...opts.json === true ? { json: true } : {},
13182
- ...depthStr !== void 0 ? { maxDepth: requireNonNegativeInt2("--max-depth", depthStr) } : {}
13241
+ ...depthStr !== void 0 ? { maxDepth: requireNonNegativeInt("--max-depth", depthStr) } : {}
13183
13242
  });
13184
13243
  }
13185
13244
  function cmdXmlQuery(file, pathOrOpts, optsMaybe) {
@@ -13990,13 +14049,6 @@ init_define_import_meta_env();
13990
14049
 
13991
14050
  // src/answer_router.ts
13992
14051
  init_define_import_meta_env();
13993
- function emit2(text) {
13994
- const payload = colorStdout() ? text : stripAnsi(text);
13995
- process.stdout.write(ensureNewline(payload));
13996
- }
13997
- function emitErr2(text) {
13998
- process.stderr.write(ensureNewline(text));
13999
- }
14000
14052
  function refusal(why, suggestion) {
14001
14053
  return `cannot answer deterministically: ${why}; try: ${suggestion}`;
14002
14054
  }
@@ -14128,11 +14180,11 @@ function subjectModeFor(intent) {
14128
14180
  function runAnswer(opts) {
14129
14181
  const question = opts.question.trim();
14130
14182
  if (question.length === 0) {
14131
- emitErr2(refusal("the question is empty", 'token-goat answer "who calls <symbol>"'));
14183
+ emitErr(refusal("the question is empty", 'token-goat answer "who calls <symbol>"'));
14132
14184
  return 1;
14133
14185
  }
14134
14186
  if (isJudgementQuestion(question)) {
14135
- emitErr2(
14187
+ emitErr(
14136
14188
  refusal(
14137
14189
  "that asks for judgement, intent, or runtime behaviour, which the index cannot answer -- it would need the code read and reasoned over",
14138
14190
  `token-goat semantic "${question}"`
@@ -14142,7 +14194,7 @@ function runAnswer(opts) {
14142
14194
  }
14143
14195
  const cls = classify(question);
14144
14196
  if (cls === null) {
14145
- emitErr2(
14197
+ emitErr(
14146
14198
  refusal(
14147
14199
  "no intent matched -- this router only answers where/who-calls/what-tests-cover/what-exports/what-imports/what-breaks questions about a named symbol or file",
14148
14200
  `token-goat semantic "${question}"`
@@ -14152,7 +14204,7 @@ function runAnswer(opts) {
14152
14204
  }
14153
14205
  const resolved = resolveSubject(cls.subject, subjectModeFor(cls.intent));
14154
14206
  if (resolved === null) {
14155
- emitErr2(
14207
+ emitErr(
14156
14208
  refusal(
14157
14209
  `'${cls.subject}' is not an indexed symbol or file`,
14158
14210
  `token-goat semantic "${question}"`
@@ -14166,7 +14218,7 @@ function runAnswer(opts) {
14166
14218
  const more = resolved.candidates.length - shown.length;
14167
14219
  const first = shown[0] ?? "";
14168
14220
  const next = FILE_INTENTS.has(cls.intent) ? `token-goat answer "${cls.intent === "tests" ? "tests for" : cls.intent === "imports" ? "imports of" : "exports of"} ${first}"` : `token-goat outline ${first}`;
14169
- emitErr2(
14221
+ emitErr(
14170
14222
  refusal(
14171
14223
  `'${cls.subject}' names ${resolved.candidates.length} files in this project (${shown.join(", ")}${more > 0 ? `, +${more} more` : ""})`,
14172
14224
  next
@@ -14175,7 +14227,7 @@ function runAnswer(opts) {
14175
14227
  return 1;
14176
14228
  }
14177
14229
  if (resolved.kind === "symbol-only") {
14178
- emitErr2(
14230
+ emitErr(
14179
14231
  refusal(
14180
14232
  `'${cls.subject}' is a symbol; exports/imports are file-level`,
14181
14233
  `token-goat ${cls.intent} ${toDisplayPath(rootDir, resolved.file)}`
@@ -14187,19 +14239,19 @@ function runAnswer(opts) {
14187
14239
  const file = resolved.kind === "symbol" ? resolved.file : resolved.path;
14188
14240
  const display = toDisplayPath(rootDir, file);
14189
14241
  if (cls.intent === "tests") {
14190
- emit2(`via: token-goat test-for ${display}`);
14242
+ emit(`via: token-goat test-for ${display}`);
14191
14243
  return runTestFor({ file });
14192
14244
  }
14193
14245
  if (cls.intent === "exports") {
14194
- emit2(`via: token-goat exports ${display}`);
14246
+ emit(`via: token-goat exports ${display}`);
14195
14247
  return runExports({ file });
14196
14248
  }
14197
- emit2(`via: token-goat imports ${display}`);
14249
+ emit(`via: token-goat imports ${display}`);
14198
14250
  return runImports({ file });
14199
14251
  }
14200
14252
  if (resolved.kind === "file") {
14201
14253
  const display = toDisplayPath(rootDir, resolved.path);
14202
- emitErr2(
14254
+ emitErr(
14203
14255
  refusal(
14204
14256
  `'${cls.subject}' is a file, and ${cls.intent === "where" ? "where" : cls.intent} needs a symbol`,
14205
14257
  `token-goat outline ${display}`
@@ -14208,16 +14260,16 @@ function runAnswer(opts) {
14208
14260
  return 1;
14209
14261
  }
14210
14262
  if (cls.intent === "callers") {
14211
- emit2(`via: token-goat callers ${displaySafeText(resolved.name)} --limit ${ANSWER_DELEGATE_LIMIT}`);
14263
+ emit(`via: token-goat callers ${displaySafeText(resolved.name)} --limit ${ANSWER_DELEGATE_LIMIT}`);
14212
14264
  return runCallers({ symbol: resolved.name, limit: ANSWER_DELEGATE_LIMIT });
14213
14265
  }
14214
14266
  if (cls.intent === "impact") {
14215
- emit2(`via: token-goat impact ${displaySafeText(resolved.name)} --top ${ANSWER_DELEGATE_LIMIT}`);
14267
+ emit(`via: token-goat impact ${displaySafeText(resolved.name)} --top ${ANSWER_DELEGATE_LIMIT}`);
14216
14268
  return runImpact({ symbol: resolved.name, top: ANSWER_DELEGATE_LIMIT });
14217
14269
  }
14218
- emit2(`via: token-goat symbol ${displaySafeText(resolved.name)} -p --exclude-vendored`);
14270
+ emit(`via: token-goat symbol ${displaySafeText(resolved.name)} -p --exclude-vendored`);
14219
14271
  const r = runSymbol({ name: resolved.name, projectRoot: rootDir, limit: ANSWER_DELEGATE_LIMIT, excludeVendored: true });
14220
- if (r.text.length > 0) emit2(r.text);
14272
+ if (r.text.length > 0) emit(r.text);
14221
14273
  return r.code;
14222
14274
  }
14223
14275
 
@@ -14225,13 +14277,6 @@ function runAnswer(opts) {
14225
14277
  init_define_import_meta_env();
14226
14278
  import * as path25 from "node:path";
14227
14279
  var DEFAULT_AFFECTED_DEPTH = 5;
14228
- function emit3(text) {
14229
- const payload = colorStdout() ? text : stripAnsi(text);
14230
- process.stdout.write(ensureNewline(payload));
14231
- }
14232
- function emitErr3(text) {
14233
- process.stderr.write(ensureNewline(text));
14234
- }
14235
14280
  function guardedFilter(filter2) {
14236
14281
  const guarded = compileGuardedRegex(filter2);
14237
14282
  if (!guarded.ok) throw new Error(`--filter is not usable as a regular expression: ${guarded.reason}`);
@@ -14286,43 +14331,43 @@ function computeAffected(opts) {
14286
14331
  }
14287
14332
  function runAffected(opts) {
14288
14333
  if (opts.depth !== void 0 && opts.depth <= 0) {
14289
- emitErr3(`--depth must be a positive number, got: ${opts.depth}`);
14334
+ emitErr(`--depth must be a positive number, got: ${opts.depth}`);
14290
14335
  return 1;
14291
14336
  }
14292
14337
  if (opts.files.length === 0) {
14293
- emitErr3("No changed files given. Pass paths as arguments, or pipe them in with --stdin.");
14338
+ emitErr("No changed files given. Pass paths as arguments, or pipe them in with --stdin.");
14294
14339
  return 1;
14295
14340
  }
14296
14341
  if (opts.filter !== void 0) {
14297
14342
  const guarded = compileGuardedRegex(opts.filter);
14298
14343
  if (!guarded.ok) {
14299
- emitErr3(`--filter is not usable as a regular expression: ${guarded.reason}`);
14344
+ emitErr(`--filter is not usable as a regular expression: ${guarded.reason}`);
14300
14345
  return 1;
14301
14346
  }
14302
14347
  }
14303
14348
  const result = computeAffected(opts);
14304
14349
  if (opts.json === true) {
14305
- emit3(displaySafeJson(result));
14350
+ emit(displaySafeJson(result));
14306
14351
  return 0;
14307
14352
  }
14308
14353
  if (opts.quiet === true) {
14309
- for (const f of result.testFiles) emit3(f);
14354
+ for (const f of result.testFiles) emit(f);
14310
14355
  } else if (result.testFiles.length === 0) {
14311
14356
  const traced = opts.files.length - result.unknownSeeds.length;
14312
14357
  if (traced === 0) {
14313
- emit3(`Nothing could be traced: ${result.unknownSeeds.length === 1 ? "the file given is not tracked" : "none of the files given are tracked"} in this project.`);
14358
+ emit(`Nothing could be traced: ${result.unknownSeeds.length === 1 ? "the file given is not tracked" : "none of the files given are tracked"} in this project.`);
14314
14359
  } else {
14315
- emit3(`No test files import ${countNoun(traced, "changed file")} (within ${countNoun(result.depth, "hop")}).`);
14360
+ emit(`No test files import ${countNoun(traced, "changed file")} (within ${countNoun(result.depth, "hop")}).`);
14316
14361
  }
14317
14362
  } else {
14318
- emit3(`${countNoun(result.testFiles.length, "affected test file")} (${countNoun(result.reachedCount, "file")} reached within ${countNoun(result.depth, "hop")}):`);
14319
- for (const f of result.testFiles) emit3(` ${f}`);
14363
+ emit(`${countNoun(result.testFiles.length, "affected test file")} (${countNoun(result.reachedCount, "file")} reached within ${countNoun(result.depth, "hop")}):`);
14364
+ for (const f of result.testFiles) emit(` ${f}`);
14320
14365
  }
14321
14366
  if (result.unknownSeeds.length > 0) {
14322
- emitErr3(`Not tracked in this project, so nothing was traced from ${result.unknownSeeds.length === 1 ? "it" : "them"}: ${result.unknownSeeds.join(", ")}`);
14367
+ emitErr(`Not tracked in this project, so nothing was traced from ${result.unknownSeeds.length === 1 ? "it" : "them"}: ${result.unknownSeeds.join(", ")}`);
14323
14368
  }
14324
14369
  if (result.depthLimited) {
14325
- emitErr3(`Stopped at --depth ${result.depth} with ${countNoun(result.unexploredAtFrontier, "file")} still unexplored; more tests may be affected (raise --depth).`);
14370
+ emitErr(`Stopped at --depth ${result.depth} with ${countNoun(result.unexploredAtFrontier, "file")} still unexplored; more tests may be affected (raise --depth).`);
14326
14371
  }
14327
14372
  return 0;
14328
14373
  }
@@ -14924,6 +14969,69 @@ function cmdTrace(src, opts) {
14924
14969
  // src/text_logfold.ts
14925
14970
  init_define_import_meta_env();
14926
14971
  import * as fs31 from "fs";
14972
+
14973
+ // src/filters.ts
14974
+ init_define_import_meta_env();
14975
+ var DROP = () => null;
14976
+ var FILTERS = [
14977
+ {
14978
+ // git fetch/clone progress: "remote: Counting objects: 73% (8/11)", "Receiving objects: 100% (11/11), done.", "Resolving deltas: ...".
14979
+ name: "git-progress",
14980
+ pattern: /^(remote:\s+)?(Counting objects|Compressing objects|Receiving objects|Resolving deltas|Unpacking objects|Enumerating objects|Writing objects):/i,
14981
+ replacer: DROP
14982
+ },
14983
+ {
14984
+ // npm install summary chatter: "added 142 packages in 3s", "changed 5 packages", "audited 200 packages in 1s".
14985
+ name: "npm-summary",
14986
+ pattern: /^\s*(added|removed|changed|audited)\s+\d+\s+packages?\b/i,
14987
+ replacer: DROP
14988
+ },
14989
+ {
14990
+ // npm/yarn deprecation + funding noise that adds nothing actionable.
14991
+ name: "npm-funding",
14992
+ pattern: /^\s*\d+\s+packages? are looking for funding\b/i,
14993
+ replacer: DROP
14994
+ },
14995
+ {
14996
+ // pip download progress: "Downloading foo-1.2.3-py3-none-any.whl (1.2 MB)" and the " |████████| 1.2 MB 5.0 MB/s" meter lines.
14997
+ name: "pip-download",
14998
+ pattern: /^\s*(Downloading|Collecting|Using cached|Requirement already satisfied)\b/i,
14999
+ replacer: DROP
15000
+ },
15001
+ {
15002
+ // A progress meter line built from Unicode box-drawing/block glyphs, e.g. "████████░░░░ 60%". Restricted to those glyphs (not ASCII #/=/-) so an ASCII final state like "Building [####] 100%" is preserved as real output.
15003
+ name: "progress-bar",
15004
+ pattern: /[█▉▊▋▌▍▎▏▓▒░]{3,}/,
15005
+ replacer: DROP
15006
+ },
15007
+ {
15008
+ // Docker layer pull progress: "abc123: Pulling fs layer", "abc123: Download complete", "abc123: Pull complete".
15009
+ name: "docker-pull",
15010
+ pattern: /^[0-9a-f]{6,}:\s+(Pulling|Waiting|Downloading|Verifying|Extracting|Download complete|Pull complete|Already exists)\b/i,
15011
+ replacer: DROP
15012
+ },
15013
+ {
15014
+ // cargo build chatter: " Compiling foo v0.1.0", " Downloading crates ...".
15015
+ name: "cargo-progress",
15016
+ pattern: /^\s*(Compiling|Downloading|Downloaded|Updating|Fetching)\s+\S/,
15017
+ replacer: DROP
15018
+ },
15019
+ {
15020
+ // Webpack/bundler asset rows are voluminous; keep the build result lines (errors, warnings, "compiled") by only dropping plain asset size rows.
15021
+ name: "webpack-asset",
15022
+ pattern: /^\s*(asset|chunk)\s+\S+\s+[\d.]+\s*(KiB|MiB|bytes)\b/i,
15023
+ replacer: DROP
15024
+ },
15025
+ {
15026
+ // A binary-content marker line: any line carrying a NUL byte is treated as non-text and collapsed to a single readable marker.
15027
+ name: "binary-content",
15028
+ // eslint-disable-next-line no-control-regex -- intentionally matches the NUL control byte that signals binary content
15029
+ pattern: /\x00/,
15030
+ replacer: () => "[binary content elided by token-goat]"
15031
+ }
15032
+ ];
15033
+
15034
+ // src/text_logfold.ts
14927
15035
  var VOLATILE_SUBS = [
14928
15036
  { re: /\b\d{1,2}:\d{2}:\d{2}(?:\.\d+)?\b/g, placeholder: "[HH:MM:SS]" },
14929
15037
  { re: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, placeholder: "[UUID]" },
@@ -16103,7 +16211,7 @@ function registerAnalysisCommands(program2, guard) {
16103
16211
  ).option("--file <path>", "narrow to one source file's gaps (matched exact or as a path suffix against the report's own file keys)").option("--json", "emit the gap report as JSON instead of text").action(guard(cmdCoverageReportGaps));
16104
16212
  program2.command("conflicts [path]").description(
16105
16213
  "unresolved git merge-conflict markers (<<<<<<< / ||||||| / ======= / >>>>>>>, two-way or diff3 three-way) instead of a raw Read or grep\n\npath may be a single file, a directory (scanned recursively), or omitted entirely (scans the whole project from the current directory); only files with at least one conflict region or malformed-marker warning are reported"
16106
- ).option("--summary", "line ranges and ours/base/theirs labels only, omitting the conflict content").option("--json", "emit the results as JSON instead of text").action(guard(cmdConflicts));
16214
+ ).option("-C, --context <n>", "lines of surrounding context to include before and after each conflict region (default: 3)").option("--summary", "line ranges and ours/base/theirs labels only, omitting the conflict content").option("--json", "emit the results as JSON instead of text").action(guard(cmdConflicts));
16107
16215
  program2.command("history").description("show recent session history: bash commands and web fetches (current-session or recent cache)").option("--limit <n>", "max entries to show (default: 30)").option("-j, --json", "output as JSON").action((opts) => guard(() => cmdHistory(opts))());
16108
16216
  program2.command("changed [ref]").description("list files or symbols changed since a git ref").option("--since <ref>", "git ref to compare against (default: HEAD~5)").option("--symbol", "list symbols instead of files").option("-j, --json", "output as JSON").option("--grep <pattern>", "filter to changed files whose path matches this regex (falls back to a literal substring match when the pattern does not compile); applies to file paths even in --symbol mode").option("--exclude-tests", "hide changed files that live in a test file (opt-in; default output is unchanged); applies to file paths even in --symbol mode").action(
16109
16217
  (ref, opts) => runExit(
@@ -16235,7 +16343,7 @@ function renderCapabilities(caps) {
16235
16343
 
16236
16344
  // src/cli_session.ts
16237
16345
  init_define_import_meta_env();
16238
- import * as fs41 from "node:fs";
16346
+ import * as fs40 from "node:fs";
16239
16347
 
16240
16348
  // src/session_audit.ts
16241
16349
  init_define_import_meta_env();
@@ -16243,6 +16351,23 @@ import * as fs34 from "node:fs";
16243
16351
  import * as os16 from "node:os";
16244
16352
  import * as path31 from "node:path";
16245
16353
  import * as readline from "node:readline";
16354
+ var MODEL_VISIBLE_CATEGORIES = ["toolResults", "assistantText", "assistantThinking", "toolUseInputs", "attachments", "userTurns", "harnessMeta", "system"];
16355
+ function computeCalibration(estimated, measured) {
16356
+ let modelVisibleBytes = 0;
16357
+ let estimatedTokens = 0;
16358
+ for (const key of MODEL_VISIBLE_CATEGORIES) {
16359
+ modelVisibleBytes += estimated[key].bytes;
16360
+ estimatedTokens += estimated[key].estTokens;
16361
+ }
16362
+ const measuredFirstWriteTokens = measured.inputTokens + measured.cacheCreationTokens;
16363
+ return {
16364
+ modelVisibleBytes,
16365
+ estimatedTokens,
16366
+ measuredFirstWriteTokens,
16367
+ relativeError: measuredFirstWriteTokens === 0 ? 0 : (estimatedTokens - measuredFirstWriteTokens) / measuredFirstWriteTokens,
16368
+ bytesPerMeasuredToken: measuredFirstWriteTokens === 0 ? 0 : modelVisibleBytes / measuredFirstWriteTokens
16369
+ };
16370
+ }
16246
16371
  function emptyMeasured() {
16247
16372
  return { apiCalls: 0, inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };
16248
16373
  }
@@ -16317,8 +16442,12 @@ var DENY_TEMPLATES = [
16317
16442
  { kind: "doc_unchanged_deny", re: /is unchanged since last read\. Use `token-goat (?:section|read)/, tool: "Read" },
16318
16443
  { kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?Use `token-goat (?:section|read)/, tool: "Read" },
16319
16444
  { kind: "read_served_deny", re: /was already served in this session, byte for byte/, tool: "Read" },
16445
+ // Its own kind rather than folded into read_served_deny above, whose stat name this branch shares: the wording is disjoint (a span, not a byte-for-byte whole serve) and so is the follow-up it invites, since the model still needs part of that span and has to choose a narrower call rather than recall the whole result.
16446
+ { kind: "range_reread_deny", re: /Lines \d+\.\.\d+ of [\s\S]*?was already read this session/, tool: "Read" },
16320
16447
  { kind: "markdown_already_read_deny", re: /Markdown file already read this session\. Use `token-goat section/, tool: "Read" },
16321
16448
  { kind: "read_count_deny", re: /(?:Read|Tried to read) this file \d+ times already/, tool: "Read" },
16449
+ // Shares read_count_deny's stat name and matches none of its wording: this branch fires on the SHAPE of the reads (consecutive line windows walking one file) rather than on their count, and says so.
16450
+ { kind: "sequential_paging_deny", re: /Sequential line-range paging detected on /, tool: "Read" },
16322
16451
  { kind: "generic_reread_deny", re: /was already read this session \(\d+ read/, tool: "Read" },
16323
16452
  { kind: "large_file_deny", re: /is very large \(\d+(?:\.\d+)?KB\)\./, tool: "Read" },
16324
16453
  { kind: "file_type_handler_deny", re: /too large to preview \(exceeds the in-hook scan cap\)|cannot be read as text\.|Read cannot return spreadsheet content|Read cannot return slide content|Read cannot return document content|Use Read with offset and limit parameters to read specific line ranges/, tool: "Read" },
@@ -16347,10 +16476,10 @@ var SHELL_READER_TOKEN_RE = /\b(?:sed|grep|cat|head|tail|awk|rg|less|nl|python3?
16347
16476
  function escapeRegExp2(s) {
16348
16477
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16349
16478
  }
16350
- function shellReadMatch(command, basename11) {
16351
- if (basename11 === "" || !SHELL_READER_TOKEN_RE.test(command)) return "none";
16352
- if (!command.includes(basename11)) return "none";
16353
- const escaped = escapeRegExp2(basename11);
16479
+ function shellReadMatch(command, basename10) {
16480
+ if (basename10 === "" || !SHELL_READER_TOKEN_RE.test(command)) return "none";
16481
+ if (!command.includes(basename10)) return "none";
16482
+ const escaped = escapeRegExp2(basename10);
16354
16483
  const strict = new RegExp(`(?:^|["'\`/\\\\\\s])${escaped}(?:$|["'\`/\\\\\\s,;:)])`);
16355
16484
  return strict.test(command) ? "match" : "ambiguous";
16356
16485
  }
@@ -16390,7 +16519,7 @@ function deepStringBytes(value) {
16390
16519
  }
16391
16520
  return 0;
16392
16521
  }
16393
- var OMISSION_MARKER_RE = /(?:--- (\d+) lines omitted ---|\[token-goat: \+?(\d+) more [a-z ]*lines omitted\]|--- patch: (\d+) lines omitted by token-goat ---|\.\.\. (\d+) lines omitted by token-goat \.\.\.)/g;
16522
+ var OMISSION_MARKER_RE = /(?:--- (\d+) lines omitted ---|\[token-goat: \+?(\d+) (?:more )?[a-z ]*lines omitted\]|--- patch: (\d+) lines omitted by token-goat ---|\.\.\. (\d+) lines omitted by token-goat \.\.\.)/g;
16394
16523
  function toolResultText(content) {
16395
16524
  if (typeof content === "string") return content;
16396
16525
  if (Array.isArray(content)) {
@@ -16588,7 +16717,7 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16588
16717
  s.editErrorBaseline.totalEdits += 1;
16589
16718
  }
16590
16719
  if (openDenies.length > 0) {
16591
- const bashCommand = name === "Bash" && typeof input["command"] === "string" ? input["command"] : void 0;
16720
+ const bashCommand = name === "Bash" && typeof input["command"] === "string" ? input["command"].toLowerCase() : void 0;
16592
16721
  for (const o of openDenies) {
16593
16722
  if (o.toolCalls.length >= 10) continue;
16594
16723
  const isReadSamePath = readNorm !== void 0 && o.path !== "" && readNorm === o.path;
@@ -16639,13 +16768,13 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16639
16768
  if (name === "Read" || name === "Skill") {
16640
16769
  const denyTemplate = DENY_TEMPLATES.find((t) => t.tool === name && t.re.test(text));
16641
16770
  if (denyTemplate !== void 0) {
16642
- const path43 = readPathById.get(id) ?? "";
16643
- const lastSlash = path43.lastIndexOf("/");
16771
+ const path42 = readPathById.get(id) ?? "";
16772
+ const lastSlash = path42.lastIndexOf("/");
16644
16773
  openDenies.push({
16645
16774
  kind: denyTemplate.kind,
16646
16775
  withheldBytes: parseWithheldBytes(denyTemplate.kind, text),
16647
- path: path43,
16648
- basename: lastSlash === -1 ? path43 : path43.slice(lastSlash + 1),
16776
+ path: path42,
16777
+ basename: lastSlash === -1 ? path42 : path42.slice(lastSlash + 1),
16649
16778
  callIndexAtDeny: perCall.length,
16650
16779
  compactEpochAtOpen: compactEpoch,
16651
16780
  compactEpochAtWindow: null,
@@ -16946,6 +17075,7 @@ async function auditSessionCorpus(opts = {}) {
16946
17075
  system: emptyCategory(),
16947
17076
  otherLocal: emptyCategory()
16948
17077
  },
17078
+ calibration: { modelVisibleBytes: 0, estimatedTokens: 0, measuredFirstWriteTokens: 0, relativeError: 0, bytesPerMeasuredToken: 0 },
16949
17079
  tools: [],
16950
17080
  attachmentKinds: [],
16951
17081
  hookOutputs: [],
@@ -17006,6 +17136,7 @@ async function auditSessionCorpus(opts = {}) {
17006
17136
  summary.hookOutputs = [...hookMap.values()].sort((a, b) => b.contextBytes - a.contextBytes || b.fires - a.fires || a.origin.localeCompare(b.origin) || a.event.localeCompare(b.event));
17007
17137
  summary.denyOutcomes = aggregateDenyOutcomes(denyRows);
17008
17138
  summary.editErrorBaseline.rate = summary.editErrorBaseline.totalEdits === 0 ? 0 : summary.editErrorBaseline.totalErrors / summary.editErrorBaseline.totalEdits;
17139
+ summary.calibration = computeCalibration(summary.estimated, summary.measured);
17009
17140
  summary.runtimeMs = Date.now() - started;
17010
17141
  return summary;
17011
17142
  }
@@ -17046,11 +17177,20 @@ function formatSessionAudit(s) {
17046
17177
  ["system lines", e.system],
17047
17178
  ["local bookkeeping (never sent)", e.otherLocal]
17048
17179
  ];
17049
- const modelVisibleBytes = rows.slice(0, 8).reduce((acc, [, c]) => acc + c.bytes, 0);
17180
+ const cal = s.calibration;
17181
+ const modelVisibleBytes = cal.modelVisibleBytes;
17050
17182
  for (const [label, cat] of rows.sort((a, b) => b[1].bytes - a[1].bytes)) {
17051
17183
  lines.push(`${label.padEnd(31)} count ${fmt(cat.count).padStart(11)} bytes ${fmt(cat.bytes).padStart(15)} est-tokens ${fmt(cat.estTokens).padStart(13)} ${label === "local bookkeeping (never sent)" ? "(excluded from share)" : pct(cat.bytes, modelVisibleBytes)}`);
17052
17184
  }
17053
17185
  lines.push("");
17186
+ lines.push("## Estimator calibration (the estimate above against the billed ledger above it)");
17187
+ lines.push(`Model-visible bytes: ${fmt(cal.modelVisibleBytes).padStart(18)}`);
17188
+ lines.push(`Estimated tokens (chars/3): ${fmt(cal.estimatedTokens).padStart(18)}`);
17189
+ lines.push(`Measured first-write tokens: ${fmt(cal.measuredFirstWriteTokens).padStart(18)} (input + cache-write; NEVER input alone -- a cached session bills nearly every first write as a cache write)`);
17190
+ lines.push(`Estimator error: ${(cal.relativeError >= 0 ? "+" : "") + (cal.relativeError * 100).toFixed(1) + "%"} ${cal.relativeError >= 0 ? "over" : "under"} measured`);
17191
+ lines.push(`Implied bytes per token: ${cal.bytesPerMeasuredToken.toFixed(2)} (against the 3.0 the estimate divides by)`);
17192
+ lines.push("Read the error as a bound, not a correction factor. Two effects inflate the measured side and neither is separable here: a prefix whose cache entry expires is written again and billed twice while the estimate counts it once, and the system prompt and tool schemas are sent on every call but appear in the transcript only in part. Both push the measured figure up, so an estimate below it is expected and only the magnitude carries information.");
17193
+ lines.push("");
17054
17194
  lines.push("## Tool results by tool (estimated content size; calls = tool_use invocations)");
17055
17195
  for (const t of s.tools.slice(0, 25)) {
17056
17196
  lines.push(`${displaySafeText(t.name).padEnd(42)} calls ${fmt(t.calls).padStart(9)} bytes ${fmt(t.resultBytes).padStart(15)} est-tokens ${fmt(t.resultEstTokens).padStart(12)}`);
@@ -17621,142 +17761,8 @@ async function runMemoryCommand(opts = {}) {
17621
17761
 
17622
17762
  // src/cli_waste.ts
17623
17763
  init_define_import_meta_env();
17624
- import * as fs39 from "node:fs";
17625
- import * as path36 from "node:path";
17626
-
17627
- // src/copilot_waste.ts
17628
- init_define_import_meta_env();
17629
17764
  import * as fs38 from "node:fs";
17630
17765
  import * as path35 from "node:path";
17631
- var HOOK_RECORD_TYPES = /* @__PURE__ */ new Set(["hook.start", "hook.end"]);
17632
- function splitInjectedBlocks(transformed) {
17633
- const out2 = [];
17634
- const blockRe = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
17635
- let m;
17636
- while ((m = blockRe.exec(transformed)) !== null) {
17637
- const tag = (m[1] ?? "").toLowerCase();
17638
- const body = m[2] ?? "";
17639
- let kind = tag;
17640
- if (tag === "system_reminder") {
17641
- const inner = /^\s*<([a-z_][a-z0-9_]*)>/i.exec(body);
17642
- kind = inner !== null ? `reminder:${(inner[1] ?? "").toLowerCase()}` : "reminder:text";
17643
- }
17644
- out2.push({ kind, body: m[0] });
17645
- }
17646
- return out2;
17647
- }
17648
- function findLatestCopilotSession() {
17649
- const root = path35.join(copilotCliUserRoot(), "session-state");
17650
- let entries;
17651
- try {
17652
- entries = fs38.readdirSync(root);
17653
- } catch {
17654
- return null;
17655
- }
17656
- let best = null;
17657
- let bestMtime = -Infinity;
17658
- for (const entry of entries) {
17659
- const candidate = path35.join(root, entry, "events.jsonl");
17660
- try {
17661
- const st = fs38.statSync(candidate);
17662
- if (!st.isFile()) continue;
17663
- if (st.mtimeMs > bestMtime) {
17664
- bestMtime = st.mtimeMs;
17665
- best = candidate;
17666
- }
17667
- } catch {
17668
- }
17669
- }
17670
- return best;
17671
- }
17672
- function readNumber(source, key) {
17673
- const value = source[key];
17674
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
17675
- }
17676
- function buildCopilotWasteReport(eventsPath) {
17677
- const raw = fs38.readFileSync(eventsPath, "utf-8");
17678
- const report = {
17679
- sessionPath: eventsPath,
17680
- sessionId: path35.basename(path35.dirname(eventsPath)),
17681
- turns: 0,
17682
- tokens: null,
17683
- blocks: [],
17684
- compactions: [],
17685
- hookRecordBytes: 0,
17686
- totalEventBytes: Buffer.byteLength(raw, "utf-8"),
17687
- // Read through the real resolution chain rather than passed in. Copilot's
17688
- // own COPILOT_CACHE_HOME override is what tests point at a fixture, so the
17689
- // shipping path is the tested path and there is no seam here that only a
17690
- // test ever supplies.
17691
- mcpTools: readCopilotMcpTools()
17692
- };
17693
- const classes = /* @__PURE__ */ new Map();
17694
- const seen = /* @__PURE__ */ new Map();
17695
- for (const line of raw.split("\n")) {
17696
- const trimmed = line.trim();
17697
- if (trimmed === "") continue;
17698
- let event;
17699
- try {
17700
- event = JSON.parse(trimmed);
17701
- } catch {
17702
- continue;
17703
- }
17704
- const type = typeof event["type"] === "string" ? event["type"] : "";
17705
- const data = event["data"] ?? {};
17706
- if (HOOK_RECORD_TYPES.has(type)) {
17707
- report.hookRecordBytes += Buffer.byteLength(trimmed, "utf-8");
17708
- continue;
17709
- }
17710
- if (type === "session.shutdown") {
17711
- report.tokens = {
17712
- systemTokens: readNumber(data, "systemTokens"),
17713
- toolDefinitionsTokens: readNumber(data, "toolDefinitionsTokens"),
17714
- conversationTokens: readNumber(data, "conversationTokens"),
17715
- currentTokens: readNumber(data, "currentTokens")
17716
- };
17717
- continue;
17718
- }
17719
- if (type === "session.compaction_complete") {
17720
- const summary = typeof data["summaryContent"] === "string" ? data["summaryContent"] : "";
17721
- report.compactions.push({
17722
- trigger: typeof data["trigger"] === "string" ? data["trigger"] : "unknown",
17723
- summaryBytes: Buffer.byteLength(summary, "utf-8"),
17724
- preTokens: readNumber(data, "preCompactionTokens"),
17725
- postTokens: readNumber(data, "postCompactionTokens")
17726
- });
17727
- continue;
17728
- }
17729
- if (type !== "user.message") continue;
17730
- const transformed = typeof data["transformedContent"] === "string" ? data["transformedContent"] : "";
17731
- if (transformed === "") continue;
17732
- report.turns += 1;
17733
- for (const block of splitInjectedBlocks(transformed)) {
17734
- const bytes = Buffer.byteLength(block.body, "utf-8");
17735
- let cls = classes.get(block.kind);
17736
- if (cls === void 0) {
17737
- cls = { kind: block.kind, count: 0, bytes: 0, repeatBytes: 0, repeatCount: 0 };
17738
- classes.set(block.kind, cls);
17739
- }
17740
- cls.count += 1;
17741
- cls.bytes += bytes;
17742
- let bucket = seen.get(block.kind);
17743
- if (bucket === void 0) {
17744
- bucket = /* @__PURE__ */ new Set();
17745
- seen.set(block.kind, bucket);
17746
- }
17747
- if (bucket.has(block.body)) {
17748
- cls.repeatBytes += bytes;
17749
- cls.repeatCount += 1;
17750
- } else {
17751
- bucket.add(block.body);
17752
- }
17753
- }
17754
- }
17755
- report.blocks = [...classes.values()].sort((a, b) => b.bytes - a.bytes);
17756
- return report;
17757
- }
17758
-
17759
- // src/cli_waste.ts
17760
17766
  function printCopilotReport(report) {
17761
17767
  const w = (text) => {
17762
17768
  process.stdout.write(text);
@@ -17955,9 +17961,50 @@ function printResidentContext(resident, w) {
17955
17961
  w(resident.compactionCount === 0 ? "\n" : ", each re-injecting the fixed preamble (CLAUDE.md, memory, skill and agent listings)\n");
17956
17962
  }
17957
17963
  async function runWasteCommand(opts = {}) {
17964
+ const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
17965
+ if (opts.transcript !== void 0) {
17966
+ const resolvedPath = path35.resolve(opts.transcript);
17967
+ if (!fs38.existsSync(resolvedPath)) {
17968
+ if (opts.copilot === true) {
17969
+ const detail = `Copilot session event log not found: ${resolvedPath}`;
17970
+ if (opts.json === true) {
17971
+ process.stdout.write(`${displaySafeJson({ error: detail }, 0)}
17972
+ `);
17973
+ } else {
17974
+ process.stdout.write("\n# token-goat waste (Copilot CLI)\n");
17975
+ process.stdout.write(`${detail}
17976
+ `);
17977
+ }
17978
+ process.exitCode = 1;
17979
+ return;
17980
+ }
17981
+ process.stderr.write(`token-goat: transcript not found: ${resolvedPath}
17982
+ `);
17983
+ process.exitCode = 1;
17984
+ return;
17985
+ }
17986
+ if (opts.copilot === true || isCopilotTranscript(resolvedPath)) {
17987
+ const copilotReport = buildCopilotWasteReport(resolvedPath);
17988
+ if (opts.json === true) {
17989
+ process.stdout.write(`${displaySafeJson(copilotReport, 0)}
17990
+ `);
17991
+ return;
17992
+ }
17993
+ printCopilotReport(copilotReport);
17994
+ return;
17995
+ }
17996
+ const report2 = await buildWasteReport(resolvedPath, opts.top !== void 0 ? { topN: opts.top } : {});
17997
+ if (opts.json === true) {
17998
+ process.stdout.write(`${displaySafeJson(report2, 0)}
17999
+ `);
18000
+ return;
18001
+ }
18002
+ printReport2(report2);
18003
+ return;
18004
+ }
17958
18005
  if (opts.copilot === true) {
17959
- const eventsPath = opts.transcript !== void 0 ? path36.resolve(opts.transcript) : findLatestCopilotSession();
17960
- if (eventsPath === null || !fs39.existsSync(eventsPath)) {
18006
+ const eventsPath = opts.project !== void 0 ? findLatestCopilotSession({ projectRoot }) : findLatestCopilotSession({ projectRoot }) ?? findLatestCopilotSession();
18007
+ if (eventsPath === null || !fs38.existsSync(eventsPath)) {
17961
18008
  const detail = eventsPath === null ? "no Copilot CLI session found under <copilot-home>/session-state" : `Copilot session event log not found: ${eventsPath}`;
17962
18009
  if (opts.json === true) {
17963
18010
  process.stdout.write(`${displaySafeJson({ error: detail }, 0)}
@@ -17979,9 +18026,8 @@ async function runWasteCommand(opts = {}) {
17979
18026
  printCopilotReport(copilotReport);
17980
18027
  return;
17981
18028
  }
17982
- const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
17983
- const transcriptPath = opts.transcript !== void 0 ? path36.resolve(opts.transcript) : findLatestTranscript(projectRoot);
17984
- if (transcriptPath === null) {
18029
+ const detected = findProjectSession(projectRoot);
18030
+ if (detected === null) {
17985
18031
  if (opts.json === true) {
17986
18032
  process.stdout.write(`${displaySafeJson({ error: "no session transcript found", project: projectRoot }, 0)}
17987
18033
  `);
@@ -17994,13 +18040,17 @@ async function runWasteCommand(opts = {}) {
17994
18040
  process.exitCode = 1;
17995
18041
  return;
17996
18042
  }
17997
- if (!fs39.existsSync(transcriptPath)) {
17998
- process.stderr.write(`token-goat: transcript not found: ${transcriptPath}
18043
+ if (detected.kind === "copilot") {
18044
+ const copilotReport = buildCopilotWasteReport(detected.path);
18045
+ if (opts.json === true) {
18046
+ process.stdout.write(`${displaySafeJson(copilotReport, 0)}
17999
18047
  `);
18000
- process.exitCode = 1;
18048
+ return;
18049
+ }
18050
+ printCopilotReport(copilotReport);
18001
18051
  return;
18002
18052
  }
18003
- const report = await buildWasteReport(transcriptPath, opts.top !== void 0 ? { topN: opts.top } : {});
18053
+ const report = await buildWasteReport(detected.path, opts.top !== void 0 ? { topN: opts.top } : {});
18004
18054
  if (opts.json === true) {
18005
18055
  process.stdout.write(`${displaySafeJson(report, 0)}
18006
18056
  `);
@@ -18011,13 +18061,13 @@ async function runWasteCommand(opts = {}) {
18011
18061
 
18012
18062
  // src/cli_mcp_audit.ts
18013
18063
  init_define_import_meta_env();
18014
- import * as fs40 from "node:fs";
18064
+ import * as fs39 from "node:fs";
18015
18065
  import * as os18 from "node:os";
18016
- import * as path37 from "node:path";
18066
+ import * as path36 from "node:path";
18017
18067
  function readMcpJsonFile(configPath2) {
18018
18068
  try {
18019
- if (!fs40.existsSync(configPath2)) return null;
18020
- const content = fs40.readFileSync(configPath2, "utf-8");
18069
+ if (!fs39.existsSync(configPath2)) return null;
18070
+ const content = fs39.readFileSync(configPath2, "utf-8");
18021
18071
  const parsed = JSON.parse(content);
18022
18072
  const servers = parsed && typeof parsed === "object" ? parsed.mcpServers ?? ("servers" in parsed ? null : parsed) : null;
18023
18073
  return servers && typeof servers === "object" ? servers : null;
@@ -18033,8 +18083,8 @@ function driveLetterCaseVariants(p) {
18033
18083
  }
18034
18084
  function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
18035
18085
  try {
18036
- if (!fs40.existsSync(claudeJsonPath)) return null;
18037
- const parsed = JSON.parse(fs40.readFileSync(claudeJsonPath, "utf-8"));
18086
+ if (!fs39.existsSync(claudeJsonPath)) return null;
18087
+ const parsed = JSON.parse(fs39.readFileSync(claudeJsonPath, "utf-8"));
18038
18088
  const projects = parsed && typeof parsed === "object" ? parsed.projects : null;
18039
18089
  if (!projects || typeof projects !== "object") return null;
18040
18090
  const slashForms = [projectRoot, projectRoot.replace(/\\/g, "/"), projectRoot.replace(/\//g, "\\")];
@@ -18052,9 +18102,9 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
18052
18102
  }
18053
18103
  }
18054
18104
  function discoverMcpConfig(projectRoot, home) {
18055
- const mcpJsonPath = path37.join(projectRoot, ".mcp.json");
18056
- const claudeJsonPath = path37.join(home, ".claude.json");
18057
- const copilotJsonPath = path37.join(home, ".copilot", "mcp-config.json");
18105
+ const mcpJsonPath = path36.join(projectRoot, ".mcp.json");
18106
+ const claudeJsonPath = path36.join(home, ".claude.json");
18107
+ const copilotJsonPath = path36.join(home, ".copilot", "mcp-config.json");
18058
18108
  const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
18059
18109
  const fromMcpJson = readMcpJsonFile(mcpJsonPath);
18060
18110
  if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
@@ -18247,7 +18297,7 @@ async function runStatuslineCommand(opts = {}) {
18247
18297
  return;
18248
18298
  }
18249
18299
  const line = renderStatusline(data);
18250
- process.stdout.write(`${colorStdout() ? line : stripAnsi(line)}
18300
+ process.stdout.write(`${colorStdout() ? line : stripAnsiEscapes(line)}
18251
18301
  `);
18252
18302
  }
18253
18303
 
@@ -18276,7 +18326,7 @@ function printSummary(rows) {
18276
18326
  const w = (text) => {
18277
18327
  process.stdout.write(text);
18278
18328
  };
18279
- w(pad("category", 22) + pad("emitted", 9) + pad("undisplayed", 13) + pad("acted-on", 10) + pad("efficacy", 12) + pad("suppressed", 17) + pad("manual+", 9) + pad("manual-", 9) + "spent\n");
18329
+ w(pad("category", 22) + pad("emitted", 9) + pad("undisplayed", 13) + pad("acted-on", 10) + pad("efficacy", 12) + pad("suppressed", 17) + pad("manual+", 9) + pad("manual-", 9) + "spent-bytes\n");
18280
18330
  for (const row of rows) {
18281
18331
  w(
18282
18332
  pad(row.category, 22) + pad(emittedCell(row), 9) + pad(detectedCell(row), 13) + pad(String(row.actedOn), 10) + pad(efficacyCell(row), 12) + pad(suppressedCell(row), 17) + pad(String(row.manualEffective), 9) + pad(String(row.manualIneffective), 9) + formatSpentCell(row) + "\n"
@@ -18288,7 +18338,7 @@ function printTotals(totals) {
18288
18338
  const legacyNote = totals.legacyEmissions > 0 ? ` (excludes ${totals.legacyEmissions} legacy emission(s) recorded before spend tracking)` : "";
18289
18339
  process.stdout.write(
18290
18340
  `
18291
- TOTAL saved=${totals.savedBytes} (all-time, every hint kind) spent=${spent} (hint_emissions ledger only)${legacyNote}
18341
+ TOTAL saved-bytes=${totals.savedBytes} (all-time, every hint kind) spent-bytes=${spent} (hint_emissions ledger only)${legacyNote}
18292
18342
  `
18293
18343
  );
18294
18344
  }
@@ -18370,10 +18420,18 @@ function cmdWaste(opts = {}) {
18370
18420
  ...opts.project !== void 0 ? { project: opts.project } : {},
18371
18421
  ...opts.transcript !== void 0 ? { transcript: opts.transcript } : {},
18372
18422
  ...opts.json === true ? { json: true } : {},
18373
- ...opts.top !== void 0 ? { top: requireNonNegativeInt2("--top", opts.top) } : {},
18423
+ ...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
18374
18424
  ...opts.copilot === true ? { copilot: true } : {}
18375
18425
  });
18376
18426
  }
18427
+ async function cmdAudit(opts = {}) {
18428
+ const { runAuditCommand } = await import("./token-goat-chunk-KVAXTA2W.mjs");
18429
+ return runAuditCommand({
18430
+ ...opts.project !== void 0 ? { project: opts.project } : {},
18431
+ ...opts.transcript !== void 0 ? { transcript: opts.transcript } : {},
18432
+ ...opts.json === true ? { json: true } : {}
18433
+ });
18434
+ }
18377
18435
  async function cmdSessionAudit(opts = {}) {
18378
18436
  let summary;
18379
18437
  try {
@@ -18400,7 +18458,7 @@ ${formatSessionOutline(turns)}`;
18400
18458
  }
18401
18459
  function sessionTranscriptSize(transcriptPath) {
18402
18460
  try {
18403
- return fs41.statSync(transcriptPath).size;
18461
+ return fs40.statSync(transcriptPath).size;
18404
18462
  } catch {
18405
18463
  return 0;
18406
18464
  }
@@ -18436,7 +18494,7 @@ function cmdRecall(query, opts = {}) {
18436
18494
  }
18437
18495
  runRecallCommand(query, {
18438
18496
  ...type !== void 0 ? { type } : {},
18439
- ...opts.limit !== void 0 ? { limit: requireNonNegativeInt2("--limit", opts.limit) } : {},
18497
+ ...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
18440
18498
  ...opts.json === true ? { json: true } : {}
18441
18499
  });
18442
18500
  }
@@ -18460,14 +18518,14 @@ function cmdHintStats(opts = {}) {
18460
18518
 
18461
18519
  // src/cli_skills.ts
18462
18520
  init_define_import_meta_env();
18463
- import * as fs42 from "node:fs";
18464
- import * as path38 from "node:path";
18521
+ import * as fs41 from "node:fs";
18522
+ import * as path37 from "node:path";
18465
18523
  async function cmdSkillBody(name, opts) {
18466
18524
  const filePath = await getSkillFilePath(name);
18467
18525
  if (filePath === null) {
18468
18526
  throw new CliError(`skill '${name}' not found`);
18469
18527
  }
18470
- const body = decodeSource(fs42.readFileSync(filePath));
18528
+ const body = decodeSource(fs41.readFileSync(filePath));
18471
18529
  if (opts.compact === true) {
18472
18530
  const emitted = extractCompactFromMarker(body) ?? body;
18473
18531
  out(emitted);
@@ -18494,7 +18552,7 @@ async function cmdSkillCompact(name, opts) {
18494
18552
  }
18495
18553
  let body2;
18496
18554
  try {
18497
- body2 = decodeSource(fs42.readFileSync(filePath));
18555
+ body2 = decodeSource(fs41.readFileSync(filePath));
18498
18556
  } catch {
18499
18557
  unresolvable++;
18500
18558
  continue;
@@ -18528,19 +18586,19 @@ async function cmdSkillCompact(name, opts) {
18528
18586
  if (!opts.path.trim()) {
18529
18587
  throw new CliError("--path cannot be empty");
18530
18588
  }
18531
- if (!fs42.existsSync(opts.path)) {
18589
+ if (!fs41.existsSync(opts.path)) {
18532
18590
  throw new CliError(`skill file not found: ${opts.path}`);
18533
18591
  }
18534
18592
  try {
18535
- body = fs42.readFileSync(opts.path, "utf-8");
18593
+ body = fs41.readFileSync(opts.path, "utf-8");
18536
18594
  } catch (e) {
18537
18595
  if (e.code === "ENOENT") {
18538
18596
  throw new CliError(`skill file not found: ${opts.path}`);
18539
18597
  }
18540
18598
  throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
18541
18599
  }
18542
- cacheName = name ?? path38.basename(path38.dirname(path38.resolve(opts.path)));
18543
- sourcePath = path38.resolve(opts.path);
18600
+ cacheName = name ?? path37.basename(path37.dirname(path37.resolve(opts.path)));
18601
+ sourcePath = path37.resolve(opts.path);
18544
18602
  } else {
18545
18603
  if (name === void 0 || !name.trim()) {
18546
18604
  throw new CliError("skill-compact requires a <name> or --path <file>");
@@ -18549,7 +18607,7 @@ async function cmdSkillCompact(name, opts) {
18549
18607
  if (filePath === null) {
18550
18608
  throw new CliError(`skill '${name}' not found`);
18551
18609
  }
18552
- body = fs42.readFileSync(filePath, "utf-8");
18610
+ body = fs41.readFileSync(filePath, "utf-8");
18553
18611
  cacheName = name;
18554
18612
  sourcePath = filePath;
18555
18613
  }
@@ -18681,8 +18739,8 @@ async function cmdSkillDiff(name) {
18681
18739
  }
18682
18740
  const newer = versions[0];
18683
18741
  const older = versions[1];
18684
- const newerBody = await fs42.promises.readFile(path38.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
18685
- const olderBody = await fs42.promises.readFile(path38.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
18742
+ const newerBody = await fs41.promises.readFile(path37.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
18743
+ const olderBody = await fs41.promises.readFile(path37.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
18686
18744
  if (newerBody === null || olderBody === null) {
18687
18745
  out(`a cached version of '${name}' was evicted while diffing -- try again`);
18688
18746
  return;
@@ -18711,7 +18769,7 @@ async function cmdSkillSection(nameHeading, headingArg) {
18711
18769
  if (!filePath) {
18712
18770
  throw new CliError(`skill '${skillName}' not found`);
18713
18771
  }
18714
- const body = decodeSource(fs42.readFileSync(filePath));
18772
+ const body = decodeSource(fs41.readFileSync(filePath));
18715
18773
  const extracted = extractNamedSection(body, heading);
18716
18774
  if (!extracted) {
18717
18775
  const allHeadings = listSections(filePath);
@@ -18732,94 +18790,12 @@ async function cmdSkillSection(nameHeading, headingArg) {
18732
18790
  // src/cache_session_commands.ts
18733
18791
  init_define_import_meta_env();
18734
18792
 
18735
- // src/resume.ts
18736
- init_define_import_meta_env();
18737
- import { readFile } from "node:fs/promises";
18738
- var MAX_RESUME_TOKENS = 2e3;
18739
- var MAX_RESUME_CHARS = MAX_RESUME_TOKENS * 4;
18740
- var SKILL_MAX_COUNT = 3;
18741
- var SKILL_MAX_CHARS_EACH = 400;
18742
- async function buildSkillsSection(sessionId) {
18743
- const skills = await listSkills(sessionId);
18744
- if (skills.length === 0) return [];
18745
- const skillLines = ["## Skills"];
18746
- for (const skill of skills.slice(0, SKILL_MAX_COUNT)) {
18747
- let checklist = null;
18748
- try {
18749
- const filePath = await getSkillFilePath(skill.name);
18750
- if (filePath !== null) {
18751
- const body = await readFile(filePath, "utf-8");
18752
- checklist = extractChecklistSection(body);
18753
- }
18754
- } catch {
18755
- checklist = null;
18756
- }
18757
- if (checklist !== null) {
18758
- const trimmed = checklist.length > SKILL_MAX_CHARS_EACH ? checklist.slice(0, SKILL_MAX_CHARS_EACH).trimEnd() + "\u2026" : checklist;
18759
- skillLines.push(`**${skill.name}**:`);
18760
- skillLines.push(trimmed);
18761
- } else {
18762
- skillLines.push(`**${skill.name}** \u2014 \`token-goat skill-body ${skill.name} --section DoD\``);
18763
- }
18764
- }
18765
- skillLines.push("");
18766
- return skillLines;
18767
- }
18768
- async function buildResumePacket(sessionId) {
18769
- const blob = loadBlob(SESSIONS_SUBDIR, sessionId);
18770
- if (blob === null || typeof blob !== "object") return null;
18771
- const raw = blob;
18772
- const filesArr = Array.isArray(raw["files"]) ? raw["files"] : [];
18773
- const lines = [`# Resume packet \u2014 session ${sessionId}`, ""];
18774
- try {
18775
- lines.push(...await buildSkillsSection(sessionId));
18776
- } catch {
18777
- }
18778
- const editedPaths = filesArr.filter((f) => f["wasEdited"] === true).map((f) => typeof f["path"] === "string" ? f["path"] : "").filter(Boolean).slice(0, 10);
18779
- if (editedPaths.length > 0) {
18780
- lines.push("## Edited files");
18781
- for (const p of editedPaths) lines.push(`- ${p}`);
18782
- lines.push("");
18783
- }
18784
- const topRead = [...filesArr].filter((f) => f["wasEdited"] !== true).sort((a, b) => (typeof b["readCount"] === "number" ? b["readCount"] : 0) - (typeof a["readCount"] === "number" ? a["readCount"] : 0)).slice(0, 8).map((f) => typeof f["path"] === "string" ? f["path"] : "").filter(Boolean);
18785
- if (topRead.length > 0) {
18786
- lines.push("## Top files read");
18787
- for (const p of topRead) lines.push(`- ${p}`);
18788
- lines.push("");
18789
- }
18790
- const bashOutputs = Array.isArray(raw["bashOutputs"]) ? raw["bashOutputs"] : [];
18791
- const recentBash = bashOutputs.slice(-2);
18792
- if (recentBash.length > 0) {
18793
- lines.push("## Recent bash commands");
18794
- for (const entry of recentBash) {
18795
- if (Array.isArray(entry) && typeof entry[1] === "string") {
18796
- const bashEntry = getBashOutput(entry[1]);
18797
- if (bashEntry !== null) lines.push(`- ${displaySafeText(bashEntry.command)}`);
18798
- }
18799
- }
18800
- lines.push("");
18801
- }
18802
- try {
18803
- const projectRoot = resolveProjectRoot();
18804
- const result = runGit(["diff", "--stat"], { cwd: projectRoot });
18805
- if (result.exitCode === 0 && result.stdout.trim().length > 0) {
18806
- lines.push("## Uncommitted changes (git diff --stat)");
18807
- lines.push(result.stdout.trim());
18808
- lines.push("");
18809
- }
18810
- } catch {
18811
- }
18812
- const text = lines.join("\n");
18813
- if (text.length <= MAX_RESUME_CHARS) return text;
18814
- return safeSlice(text, MAX_RESUME_CHARS) + "\n... (truncated to cap)\n";
18815
- }
18816
-
18817
18793
  // src/cli_stats.ts
18818
18794
  init_define_import_meta_env();
18819
- import * as fs43 from "node:fs";
18820
- import * as path39 from "node:path";
18795
+ import * as fs42 from "node:fs";
18796
+ import * as path38 from "node:path";
18821
18797
  function writeRaw(text) {
18822
- const payload = colorStdout() ? text : stripAnsi(text);
18798
+ const payload = colorStdout() ? text : stripAnsiEscapes(text);
18823
18799
  process.stdout.write(ensureNewline(payload));
18824
18800
  }
18825
18801
  function rankByReadCount(entries, topN) {
@@ -18829,8 +18805,8 @@ function formatTopFiles(ranked) {
18829
18805
  if (ranked.length === 0) return "";
18830
18806
  const lines = ["Top files this session:"];
18831
18807
  for (const { path: filePath, count } of ranked) {
18832
- const basename11 = path39.basename(filePath);
18833
- lines.push(` ${count.toString().padStart(3)}x ${basename11} (${filePath})`);
18808
+ const basename10 = path38.basename(filePath);
18809
+ lines.push(` ${count.toString().padStart(3)}x ${basename10} (${filePath})`);
18834
18810
  }
18835
18811
  return lines.join("\n");
18836
18812
  }
@@ -18847,11 +18823,11 @@ function renderTopSessionFiles(topN = 5) {
18847
18823
  function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
18848
18824
  try {
18849
18825
  const dir = overrideSessionsDir ?? sessionsDir();
18850
- if (!fs43.existsSync(dir)) return "";
18851
- const files = fs43.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, mtime: fs43.statSync(path39.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).slice(0, 3);
18826
+ if (!fs42.existsSync(dir)) return "";
18827
+ const files = fs42.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, mtime: fs42.statSync(path38.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).slice(0, 3);
18852
18828
  for (const { name } of files) {
18853
18829
  try {
18854
- const raw = fs43.readFileSync(path39.join(dir, name), "utf-8");
18830
+ const raw = fs42.readFileSync(path38.join(dir, name), "utf-8");
18855
18831
  const data = JSON.parse(raw);
18856
18832
  const filesList = data["files"];
18857
18833
  if (!Array.isArray(filesList)) continue;
@@ -18943,13 +18919,10 @@ function runStats(opts = {}) {
18943
18919
  }
18944
18920
 
18945
18921
  // src/cache_session_commands.ts
18946
- function emitErr4(text) {
18947
- process.stderr.write(ensureNewline(text));
18948
- }
18949
18922
  function capAndNote(rows, limit) {
18950
18923
  const shown = rows.slice(0, limit);
18951
18924
  if (shown.length < rows.length) {
18952
- emitErr4(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
18925
+ emitErr(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
18953
18926
  }
18954
18927
  return shown;
18955
18928
  }
@@ -18959,11 +18932,11 @@ function parseLimitOpt(cmdName, limitStr, dflt = 30) {
18959
18932
  try {
18960
18933
  n = requireNonNegativeStrictInt("--limit", limitStr);
18961
18934
  } catch (e) {
18962
- emitErr4(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18935
+ emitErr(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18963
18936
  throw new Error(`invalid --limit: ${limitStr}`, { cause: e });
18964
18937
  }
18965
18938
  if (n === 0) {
18966
- emitErr4(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18939
+ emitErr(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18967
18940
  throw new Error(`invalid --limit: ${limitStr}`);
18968
18941
  }
18969
18942
  return n;
@@ -19201,11 +19174,17 @@ function cmdCompactHint(opts) {
19201
19174
  const cache = sessionId !== null ? loadSessionCache(sessionId) : null;
19202
19175
  const transcriptPath = sessionId !== null ? resolveSessionTranscript(sessionId) : null;
19203
19176
  const pressure = getContextPressure(cache ?? void 0, transcriptPath ?? void 0);
19204
- const [manifest, eventCount] = sessionId !== null ? buildManifestWithCount(sessionId) : ["", 0];
19177
+ let manifest = "";
19178
+ let events = 0;
19179
+ if (sessionId !== null && cache !== null) {
19180
+ loadSessionState(sessionId);
19181
+ manifest = buildManifest(sessionId, process.cwd(), transcriptPath ?? void 0);
19182
+ events = eventCount(cache);
19183
+ }
19205
19184
  const manifestTokens = estimateTokens(manifest);
19206
19185
  const pct3 = (pressure.fillFraction * 100).toFixed(1);
19207
19186
  if (opts.json === true) {
19208
- const out2 = { tier: pressure.tier, fillFraction: pressure.fillFraction, pct: Number(pct3), manifestTokens, eventCount };
19187
+ const out2 = { tier: pressure.tier, fillFraction: pressure.fillFraction, pct: Number(pct3), manifestTokens, eventCount: events };
19209
19188
  if (sessionId !== null) out2["sessionId"] = sessionId;
19210
19189
  if (opts.trigger !== void 0) out2["trigger"] = opts.trigger;
19211
19190
  process.stdout.write(displaySafeJson(out2) + "\n");
@@ -19215,7 +19194,7 @@ function cmdCompactHint(opts) {
19215
19194
  `);
19216
19195
  if (sessionId !== null) process.stdout.write(`Session: ${sessionId}
19217
19196
  `);
19218
- process.stdout.write(`Manifest: ${manifestTokens} tokens, ${countNoun(eventCount, "event")}
19197
+ process.stdout.write(`Manifest: ${manifestTokens} tokens, ${countNoun(events, "event")}
19219
19198
  `);
19220
19199
  if (opts.trigger === "auto") {
19221
19200
  const remaining = Math.max(0, Math.round((1 - pressure.fillFraction) * CONTEXT_AUTOCOMPACT_TOKENS));
@@ -19318,6 +19297,7 @@ function registerSessionCommands(program2, guard) {
19318
19297
  program2.command("bootstrap-audit").description("audit Claude Code startup-context contributors without reading prompt bodies").option("--project <path>", "project root to analyze").option("--home <path>", "home directory override (for CI/testing)").option("--follow-links", "follow external symlink/junction roots and direct children").option("-j, --json", "output as JSON").option("--top <n>", "largest metadata entries to show (default 10)", "10").option("--warn-tokens <n>", "warn when total estimated startup tokens exceed n").option("--fail-tokens <n>", "fail when total estimated startup tokens exceed n").option("--warn-bytes <n>", "warn when agent/skill metadata bytes exceed n").option("--fail-bytes <n>", "fail when agent/skill metadata bytes exceed n").action(guard(cmdBootstrapAudit));
19319
19298
  program2.command("memory").description("analyze CLAUDE.md files for duplicate/overlapping content (--fix to apply safe mechanical fixes)").option("--project <path>", "project root to analyze").option("--analyze", "report-only analysis (default)").option("--fix", "remove exact-duplicate lines (confirm-gated; shows a diff before writing)").option("--yes", "apply --fix changes without prompting (non-interactive)").action(guard(cmdMemory));
19320
19299
  program2.command("waste").description("session spend-ledger: token cost per tool/file from the current Claude Code session transcript, plus waste signals").option("--project <path>", "project root to analyze").option("--transcript <path>", "explicit transcript JSONL path (default: most-recently-modified transcript for this project)").option("--top <n>", "number of top expensive tool calls to show (default: 10)").option("--copilot", "analyze a Copilot CLI session event log instead, reporting Copilot's own token split").option("--json", "output JSON").action(guard(cmdWaste));
19300
+ program2.command("audit").description("session retrospective: analyze the current session transcript (Copilot CLI or Claude Code) and emit a Maintainer Feedback Card").option("--project <path>", "project root to analyze").option("--transcript <path>", "explicit transcript path (default: active or newest session for this project)").option("-j, --json", "output as JSON").action(guard(cmdAudit));
19321
19301
  program2.command("session-outline [session-id-or-path]").description("turn-by-turn structure (role, preview, tool calls, approx size) of a Claude Code session JSONL transcript, instead of a raw Read; defaults to the current project's most recent session").option("--project <path>", "project root to resolve the session transcript against").option("--json", "output JSON").action(guard(cmdSessionOutline));
19322
19302
  program2.command("session-slice [session-id-or-path]").description("full content of one turn range from a Claude Code session JSONL transcript (see session-outline for turn numbers), instead of a raw Read").requiredOption("--range <spec>", "turn range, e.g. 5-9 or 12 (see session-outline for turn numbers)").option("--project <path>", "project root to resolve the session transcript against").option("--json", "output JSON").action(guard(cmdSessionSlice));
19323
19303
  program2.command("session-audit").alias("audit-session").description("corpus-wide token attribution across every local Claude Code session transcript: measured billed usage, estimated content size by source and by tool, and billed cost by session position (aggregate counts only, never transcript content)").option("--dir <path>", "transcript corpus root to scan (default: ~/.claude/projects)").option("--json", "output JSON").action(guard(cmdSessionAudit));
@@ -19432,16 +19412,15 @@ var BRIDGE_CAPABILITY_MATRIX = [
19432
19412
  harness: "codex",
19433
19413
  label: "Codex CLI",
19434
19414
  sourceFile: "src/bridges/codex_install.ts (CODEX_HOOK_EVENTS, CODEX_GLOBAL_HOOK_EVENTS)",
19435
- verification: "sourced",
19436
- verificationNote: "Wire format pinned to Codex 0.137.0+'s declared hookSpecificOutput schema (src/bridges/codex.ts); no run against a Codex binary is recorded here.",
19415
+ verification: "dogfooded",
19416
+ verificationNote: `codex-cli 0.155.0, 2026-09-22: observed running the installed config.toml hooks and delivering real payloads to the shim -- PreToolUse/PostToolUse with tool_name "Bash" and tool_input.command, and PreCompact with trigger "auto" from a forced auto-compaction. A pre-tool deny was honored (the model dropped Get-Content and ran token-goat outline instead) and post-tool additionalContext reached the model; the pre-compact response did not, which is why codex is in PRE_COMPACT_CONTEXT_DROPPED. Two conditions gate the tool events and both fail silently. config.toml must carry the matching [hooks.state] trusted_hash the installer writes, and the matcher must name the tool as it appears on the hook wire -- "Bash" for shell execution, since Codex normalizes its own exec tool into Claude Code's vocabulary before matching, while apply_patch stays native. A matcher naming the internal spelling produced a run with eight pre_compact rows and no pre_tool_use or post_tool_use row at all, and that also stranded every queued compaction manifest. Verified end to end after the fix: a forced auto-compaction run recorded eight compactions and twelve manifest deliveries in its own rollout transcript.`,
19437
19417
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "user_prompt_submit", "subagent_stop"]),
19438
19418
  reasons: [
19439
19419
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
19440
- { events: ["post_compact"], reason: NO_POST_COMPACT_EVENT_REASON },
19441
19420
  { events: ["notification", "stop"], reason: NO_SERVER_HANDLER_REASON },
19442
19421
  {
19443
- events: ["session_start"],
19444
- reason: "Codex CLI's hook config schema (CODEX_EVENT_ARG) has no session-start-equivalent event to map onto token-goat's session_start -- unlike Claude Code's real SessionStart hook"
19422
+ events: ["post_compact", "session_start"],
19423
+ reason: "Codex CLI 0.155.0 does declare PostCompact and SessionStart -- both appear in the hook-event enum next to PreToolUse and PreCompact, so the earlier claim that it had no session-start equivalent and no post-compact event was wrong. They stay unwired for the reason the rest of this table applies: neither channel has been measured. The post-compact input has no summary field anywhere in the binary's payload key list (session_id, turn_id, agent_id, agent_type, transcript_path, hook_event_name, model, permission_mode, trigger, tool_name, tool_input, tool_use_id), so postCompactHandler would have nothing to count, and whether SessionStart forwards additionalContext has not been run"
19445
19424
  }
19446
19425
  ]
19447
19426
  },
@@ -19582,7 +19561,7 @@ var BRIDGE_CAPABILITY_MATRIX = [
19582
19561
  reasons: [
19583
19562
  {
19584
19563
  events: ["pre_compact", "post_tool_use_failure"],
19585
- reason: "VS Code reads the shared Copilot hooks file but maps only sessionStart, sessionEnd, userPromptSubmitted, preToolUse, postToolUse, agentStop, subagentStop and errorOccurred out of it, so the file's preCompact and postToolUseFailure entries never fire there"
19564
+ reason: "VS Code reads the shared Copilot hooks file but maps only sessionStart, sessionEnd, userPromptSubmitted, preToolUse, postToolUse, agentStop, subagentStop and errorOccurred out of it, so the file's preCompact and postToolUseFailure entries never fire there. Re-read in the 1.138.0 bundle, where the github-copilot branch of that table still lists exactly those eight: the compaction manifest is unavailable on this harness rather than merely unwired, and unlike Copilot CLI and Codex there is no queueing fallback, because the trigger itself never arrives"
19586
19565
  },
19587
19566
  { events: ["post_compact"], reason: NO_POST_COMPACT_EVENT_REASON },
19588
19567
  { events: ["notification", "stop"], reason: NO_SERVER_HANDLER_REASON }
@@ -19735,25 +19714,25 @@ function installVerificationNotice(harness) {
19735
19714
 
19736
19715
  // src/purge.ts
19737
19716
  init_define_import_meta_env();
19738
- import * as fs44 from "node:fs";
19739
- import * as path40 from "node:path";
19717
+ import * as fs43 from "node:fs";
19718
+ import * as path39 from "node:path";
19740
19719
  function directorySize(root) {
19741
19720
  let total = 0;
19742
19721
  let entries;
19743
19722
  try {
19744
- entries = fs44.readdirSync(root, { withFileTypes: true });
19723
+ entries = fs43.readdirSync(root, { withFileTypes: true });
19745
19724
  } catch {
19746
19725
  return 0;
19747
19726
  }
19748
19727
  for (const entry of entries) {
19749
- const full = path40.join(root, entry.name);
19728
+ const full = path39.join(root, entry.name);
19750
19729
  if (entry.isDirectory()) {
19751
19730
  total += directorySize(full);
19752
19731
  continue;
19753
19732
  }
19754
19733
  if (!entry.isFile()) continue;
19755
19734
  try {
19756
- total += fs44.statSync(full).size;
19735
+ total += fs43.statSync(full).size;
19757
19736
  } catch {
19758
19737
  }
19759
19738
  }
@@ -19772,7 +19751,7 @@ function purgeDataDirectories() {
19772
19751
  }
19773
19752
  const bytes = directorySize(root);
19774
19753
  try {
19775
- fs44.rmSync(root, { recursive: true, force: true });
19754
+ fs43.rmSync(root, { recursive: true, force: true });
19776
19755
  result.removed.push({ path: root, bytes });
19777
19756
  } catch (e) {
19778
19757
  result.failed.push({ path: root, reason: e instanceof Error ? e.message : String(e) });
@@ -19919,8 +19898,8 @@ async function getSectionContent(fileId, heading, opts = {}) {
19919
19898
 
19920
19899
  // src/cli_bench.ts
19921
19900
  init_define_import_meta_env();
19922
- import * as fs45 from "node:fs";
19923
- import * as path41 from "node:path";
19901
+ import * as fs44 from "node:fs";
19902
+ import * as path40 from "node:path";
19924
19903
  var ControlFilter = class extends ToolFilter {
19925
19904
  constructor(name, transform) {
19926
19905
  super();
@@ -19941,13 +19920,13 @@ var CONTROL_FILTERS = {
19941
19920
  };
19942
19921
  var MAX_BENCH_FILE_BYTES = 5 * 1024 * 1024;
19943
19922
  function readCaseFile(id, filePath, what) {
19944
- const bytes = fs45.statSync(filePath).size;
19923
+ const bytes = fs44.statSync(filePath).size;
19945
19924
  if (bytes > MAX_BENCH_FILE_BYTES) {
19946
19925
  throw new Error(
19947
19926
  `bench case ${id}: ${what} is ${bytes} bytes, over the ${MAX_BENCH_FILE_BYTES}-byte limit for a corpus file (${filePath})`
19948
19927
  );
19949
19928
  }
19950
- return fs45.readFileSync(filePath, "utf8");
19929
+ return fs44.readFileSync(filePath, "utf8");
19951
19930
  }
19952
19931
  function parseCase(id, metaPath, outputPath) {
19953
19932
  const metaText = readCaseFile(id, metaPath, "metadata");
@@ -19982,7 +19961,7 @@ function parseCase(id, metaPath, outputPath) {
19982
19961
  function loadCorpus(dir) {
19983
19962
  let entries;
19984
19963
  try {
19985
- entries = fs45.readdirSync(dir);
19964
+ entries = fs44.readdirSync(dir);
19986
19965
  } catch {
19987
19966
  throw new Error(`bench corpus not found: ${dir}`);
19988
19967
  }
@@ -19990,9 +19969,9 @@ function loadCorpus(dir) {
19990
19969
  for (const entry of entries.sort()) {
19991
19970
  if (!entry.endsWith(".json")) continue;
19992
19971
  const id = entry.slice(0, -".json".length);
19993
- const outputPath = path41.join(dir, `${id}.txt`);
19994
- if (!fs45.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
19995
- cases.push(parseCase(id, path41.join(dir, entry), outputPath));
19972
+ const outputPath = path40.join(dir, `${id}.txt`);
19973
+ if (!fs44.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
19974
+ cases.push(parseCase(id, path40.join(dir, entry), outputPath));
19996
19975
  }
19997
19976
  if (cases.length === 0) throw new Error(`bench corpus is empty: ${dir}`);
19998
19977
  return cases;
@@ -20112,10 +20091,10 @@ function appendTsv(file, report, commit) {
20112
20091
  report.mustKeepTotal,
20113
20092
  report.coveredFilters
20114
20093
  ].join(" ");
20115
- const dir = path41.dirname(path41.resolve(file));
20116
- fs45.mkdirSync(dir, { recursive: true });
20117
- const header = fs45.existsSync(file) ? "" : TSV_HEADER;
20118
- fs45.appendFileSync(file, header + row + "\n", "utf8");
20094
+ const dir = path40.dirname(path40.resolve(file));
20095
+ fs44.mkdirSync(dir, { recursive: true });
20096
+ const header = fs44.existsSync(file) ? "" : TSV_HEADER;
20097
+ fs44.appendFileSync(file, header + row + "\n", "utf8");
20119
20098
  }
20120
20099
  function currentCommit(cwd) {
20121
20100
  const opts = cwd === void 0 ? {} : { cwd };
@@ -20144,7 +20123,7 @@ function runBenchCommand(opts) {
20144
20123
  var CliError = class extends Error {
20145
20124
  };
20146
20125
  function out(text) {
20147
- const payload = colorStdout() ? text : stripAnsi(text);
20126
+ const payload = colorStdout() ? text : stripAnsiEscapes(text);
20148
20127
  process.stdout.write(ensureNewline(payload));
20149
20128
  }
20150
20129
  function err(text) {
@@ -20155,10 +20134,10 @@ function readBoundedText(text, file) {
20155
20134
  if (text === void 0 && file === void 0) throw new CliError("provide text or --file");
20156
20135
  let value;
20157
20136
  if (file !== void 0) {
20158
- if (fs46.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
20137
+ if (fs45.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
20159
20138
  throw new CliError(`file exceeds the ${CONTENT_MAX_INPUT_CHARS}-byte safety limit`);
20160
20139
  }
20161
- value = fs46.readFileSync(file, "utf8");
20140
+ value = fs45.readFileSync(file, "utf8");
20162
20141
  } else {
20163
20142
  if (text === void 0) throw new CliError("provide text or --file");
20164
20143
  value = text;
@@ -20203,35 +20182,11 @@ function cmdHandoffResolve(name, opts) {
20203
20182
  if (result === null) throw new CliError(`no local handoff named "${name}" in this project`);
20204
20183
  out(typeof result === "string" ? result : formatCompression(result, false, "payload withheld: inlining it would cost more tokens than the original text; pass --full to get the original text back outright"));
20205
20184
  }
20206
- function requireInt2(flag, raw) {
20207
- if (!/^-?\d+$/.test(raw)) {
20208
- throw new CliError(`${flag} must be a number, got: "${raw}"`);
20209
- }
20210
- const n = Number.parseInt(raw, 10);
20211
- if (!Number.isFinite(n)) {
20212
- throw new CliError(`${flag} must be a number, got: "${raw}"`);
20213
- }
20214
- return n;
20215
- }
20216
- function requireNonNegativeInt2(flag, raw) {
20217
- const n = requireInt2(flag, raw);
20218
- if (n < 0) {
20219
- throw new CliError(`${flag} must be a non-negative number, got: "${raw}"`);
20220
- }
20221
- return n;
20222
- }
20223
- function requirePositiveInt2(flag, raw) {
20224
- const n = requireInt2(flag, raw);
20225
- if (n <= 0) {
20226
- throw new CliError(`${flag} must be a positive number, got: "${raw}"`);
20227
- }
20228
- return n;
20229
- }
20230
20185
  async function cmdSemantic(query, opts) {
20231
20186
  if (!query && !opts.preflight && !opts.warm) {
20232
20187
  throw new CliError("missing required argument: query");
20233
20188
  }
20234
- const limit = opts.limit !== void 0 ? requireNonNegativeInt2("--limit", opts.limit) : 20;
20189
+ const limit = opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20;
20235
20190
  const { text, code } = await runSemantic(query ?? "", {
20236
20191
  limit,
20237
20192
  ...opts.json === true ? { json: true } : {},
@@ -20268,7 +20223,7 @@ async function cmdIndex(pathArg, opts = {}) {
20268
20223
  const ixCfg = loadConfig().indexing;
20269
20224
  if ((loadConfig().indexing?.embeddings_enabled ?? true) && embeddingsDepsAvailable(getDb(dbPath))) {
20270
20225
  ensureEmbeddingProvenance(getDb(dbPath));
20271
- pruneUnembeddableChunks(getDb(dbPath), ixCfg.max_chunks_per_file, deleteFileEmbeddings);
20226
+ pruneUnembeddableChunks(getDb(dbPath), ixCfg.max_chunks_per_file, deleteFileEmbeddings, { asset: assetEmbedSha, maxChunks: maxChunksEmbedSha });
20272
20227
  }
20273
20228
  let indexed = 0;
20274
20229
  let failed = 0;
@@ -20305,7 +20260,8 @@ async function cmdIndex(pathArg, opts = {}) {
20305
20260
  continue;
20306
20261
  }
20307
20262
  const sha = fingerprintFile(key);
20308
- if (sha === null && !fs46.existsSync(key)) continue;
20263
+ if (sha === null && !fs45.existsSync(key)) continue;
20264
+ recordKnownRootThrottled(key, dataDir(), dbPath);
20309
20265
  const entry = sha !== null ? getFileEntry(key, dbPath) : null;
20310
20266
  const spellingStale = entry !== null && indexedPathSpellingIsStale(entry.filePath, key);
20311
20267
  const parseUnchanged = !force && !spellingStale && sha !== null && entry?.sha === sha && entry.parserSha === parserFingerprintForLanguage(entry.language);
@@ -20341,7 +20297,7 @@ async function cmdIndex(pathArg, opts = {}) {
20341
20297
  }
20342
20298
  continue;
20343
20299
  }
20344
- if (!fs46.existsSync(key)) continue;
20300
+ if (!fs45.existsSync(key)) continue;
20345
20301
  }
20346
20302
  const embedFresh = parseUnchanged ? embedUnchanged : embedFreshFor(getFileEntry(key, dbPath)?.embedSha);
20347
20303
  if (!embedFresh) {
@@ -20402,7 +20358,7 @@ async function cmdMcpServe() {
20402
20358
  let StdioServerTransport;
20403
20359
  try {
20404
20360
  ;
20405
- ({ createMcpServer } = await import("./token-goat-chunk-DA6TIDLI.mjs"));
20361
+ ({ createMcpServer } = await import("./token-goat-chunk-GO7OWONJ.mjs"));
20406
20362
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
20407
20363
  } catch (err2) {
20408
20364
  process.stderr.write(
@@ -20427,11 +20383,11 @@ async function cmdHook(event, opts) {
20427
20383
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
20428
20384
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
20429
20385
  }
20430
- const { relay } = await import("./token-goat-chunk-NYFYIRG3.mjs");
20386
+ const { relay } = await import("./token-goat-chunk-CMHBTXMY.mjs");
20431
20387
  await relay(event);
20432
20388
  }
20433
20389
  function projectHooksCommitNote(pathFiles, hooksConfigPath) {
20434
- const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path42.join(path42.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
20390
+ const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path41.join(path41.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
20435
20391
  return `Note: ${pathFiles.map((p) => displaySafePath(p)).join(", ")} ${pathFiles.length === 1 ? "holds" : "hold"} absolute paths to node and token-goat on this machine${shim}, so do not commit them: list them in .git/info/exclude or .gitignore.`;
20436
20392
  }
20437
20393
  function visualStudioManualSteps(scope) {
@@ -20464,7 +20420,7 @@ async function cmdInstall(opts) {
20464
20420
  if (opts.project === true && opts.user === true) {
20465
20421
  throw new Error("install takes either -p/--project or --user, not both.");
20466
20422
  }
20467
- await import("./token-goat-chunk-NYFYIRG3.mjs");
20423
+ await import("./token-goat-chunk-CMHBTXMY.mjs");
20468
20424
  const scope = opts.project === true ? "project" : "user";
20469
20425
  if (wantsClaudeCodeBase(opts)) {
20470
20426
  const result = installHooks(scope);
@@ -20610,16 +20566,16 @@ async function cmdInstall(opts) {
20610
20566
  );
20611
20567
  }
20612
20568
  try {
20613
- const skillDir2 = path42.join(claudeConfigDir(), "skills");
20614
- if (fs46.existsSync(skillDir2)) {
20615
- const entries = fs46.readdirSync(skillDir2, { withFileTypes: true });
20569
+ const skillDir2 = path41.join(claudeConfigDir(), "skills");
20570
+ if (fs45.existsSync(skillDir2)) {
20571
+ const entries = fs45.readdirSync(skillDir2, { withFileTypes: true });
20616
20572
  const skillNames = [];
20617
20573
  const sessionId = getSessionId();
20618
20574
  for (const entry of entries) {
20619
20575
  if (!entry.isDirectory()) continue;
20620
- const skillFile = path42.join(skillDir2, entry.name, "SKILL.md");
20621
- if (fs46.existsSync(skillFile)) {
20622
- const body = fs46.readFileSync(skillFile, "utf-8");
20576
+ const skillFile = path41.join(skillDir2, entry.name, "SKILL.md");
20577
+ if (fs45.existsSync(skillFile)) {
20578
+ const body = fs45.readFileSync(skillFile, "utf-8");
20623
20579
  const compact = extractCompactFromMarker(body);
20624
20580
  if (compact === null) continue;
20625
20581
  const sourceSha = contentHash(body);
@@ -20630,9 +20586,9 @@ async function cmdInstall(opts) {
20630
20586
  if (skillNames.length > 0) {
20631
20587
  const dir = skillOutputsDir();
20632
20588
  ensureDirSync(dir);
20633
- const pregenPath = path42.join(dir, "pregen.json");
20589
+ const pregenPath = path41.join(dir, "pregen.json");
20634
20590
  const pregenData = { ts: Date.now(), names: skillNames };
20635
- await fs46.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
20591
+ await fs45.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
20636
20592
  out(`Pre-generated ${skillNames.length} skill compacts.`);
20637
20593
  }
20638
20594
  }
@@ -20772,7 +20728,7 @@ function cmdWorkerStatus() {
20772
20728
  out(isWorkerRunning() ? "Worker is running." : "Worker is not running.");
20773
20729
  }
20774
20730
  function cmdStats(opts = {}) {
20775
- const windowDays = opts.windowDays !== void 0 ? requireNonNegativeInt2("--window-days", opts.windowDays) : 30;
20731
+ const windowDays = opts.windowDays !== void 0 ? requireNonNegativeInt("--window-days", opts.windowDays) : 30;
20776
20732
  const statsOpts = {
20777
20733
  json: opts.json === true,
20778
20734
  windowDays,
@@ -20812,7 +20768,7 @@ async function cmdDoctor(opts) {
20812
20768
  }
20813
20769
  }
20814
20770
  function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTag = UNTRUSTED_WEB_TAG) {
20815
- const emit4 = (text) => {
20771
+ const emit2 = (text) => {
20816
20772
  if (!fenceByProvenance || text === "") {
20817
20773
  out(text);
20818
20774
  return text;
@@ -20843,7 +20799,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
20843
20799
  }
20844
20800
  }
20845
20801
  if (opts.grep !== void 0 && opts.maxMatches !== void 0) {
20846
- const cap = requireNonNegativeInt2("--max-matches", opts.maxMatches);
20802
+ const cap = requireNonNegativeInt("--max-matches", opts.maxMatches);
20847
20803
  const matched = content === "" ? [] : content.split(/\r?\n/);
20848
20804
  if (matched.length > cap) {
20849
20805
  content = [...matched.slice(0, cap), "[token-goat: showing first " + cap + " of " + matched.length + " matching lines; raise --max-matches for more]"].join("\n");
@@ -20851,11 +20807,11 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
20851
20807
  }
20852
20808
  const rawLines = content.split(/\r?\n/);
20853
20809
  if (opts.full === true) {
20854
- return emit4(rawLines.join("\n"));
20810
+ return emit2(rawLines.join("\n"));
20855
20811
  }
20856
20812
  const lines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
20857
- const headN = opts.head !== void 0 ? requireNonNegativeInt2("--head", opts.head) : 30;
20858
- const tailN = opts.tail !== void 0 ? requireNonNegativeInt2("--tail", opts.tail) : 80;
20813
+ const headN = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 30;
20814
+ const tailN = opts.tail !== void 0 ? requireNonNegativeInt("--tail", opts.tail) : 80;
20859
20815
  const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 + 1 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
20860
20816
  const noteLineCap = (which, flag, shown, total) => {
20861
20817
  if (shown >= total) return;
@@ -20874,7 +20830,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
20874
20830
  result = lines.slice(Math.max(0, lines.length - tailN));
20875
20831
  noteLineCap("last", "tail", result.length, lines.length);
20876
20832
  }
20877
- return emit4(result.join("\n"));
20833
+ return emit2(result.join("\n"));
20878
20834
  }
20879
20835
  function cmdBashOutput(id, opts) {
20880
20836
  const parseVerifyThreshold = (optVal) => {
@@ -20895,7 +20851,7 @@ function cmdBashOutput(id, opts) {
20895
20851
  }
20896
20852
  let content;
20897
20853
  try {
20898
- const st = fs46.statSync(opts.file);
20854
+ const st = fs45.statSync(opts.file);
20899
20855
  if (st.isFIFO() || st.isSocket()) {
20900
20856
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
20901
20857
  }
@@ -20910,7 +20866,7 @@ function cmdBashOutput(id, opts) {
20910
20866
  `);
20911
20867
  }
20912
20868
  }
20913
- content = redactIfDotenv(opts.file, decodeSource(fs46.readFileSync(opts.file)));
20869
+ content = redactIfDotenv(opts.file, decodeSource(fs45.readFileSync(opts.file)));
20914
20870
  } catch (e) {
20915
20871
  if (e instanceof CliError) throw e;
20916
20872
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -20974,15 +20930,15 @@ function cmdMcpOutput(id, opts) {
20974
20930
  if (opts.file.includes("\0")) {
20975
20931
  throw new CliError("--file path contains a null byte");
20976
20932
  }
20977
- if (!fs46.existsSync(opts.file)) {
20933
+ if (!fs45.existsSync(opts.file)) {
20978
20934
  throw new CliError(`file not found: ${opts.file}`);
20979
20935
  }
20980
20936
  try {
20981
- const st = fs46.statSync(opts.file);
20937
+ const st = fs45.statSync(opts.file);
20982
20938
  if (st.isFIFO() || st.isSocket()) {
20983
20939
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
20984
20940
  }
20985
- content = decodeSource(fs46.readFileSync(opts.file));
20941
+ content = decodeSource(fs45.readFileSync(opts.file));
20986
20942
  } catch (e) {
20987
20943
  if (e instanceof CliError) throw e;
20988
20944
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -21003,7 +20959,7 @@ function cmdMcpOutput(id, opts) {
21003
20959
  const data = extractJsonFromMcpOutput(content);
21004
20960
  let head;
21005
20961
  if (opts.head !== void 0) {
21006
- head = requireNonNegativeInt2("--head", opts.head);
20962
+ head = requireNonNegativeInt("--head", opts.head);
21007
20963
  }
21008
20964
  const queryResult = queryJson(data, opts.jsonQuery);
21009
20965
  const { head: _omittedHead, ...restOpts } = opts;
@@ -21055,12 +21011,12 @@ async function cmdCompress(commandArgs, opts = {}) {
21055
21011
  process.exitCode = 1;
21056
21012
  return;
21057
21013
  }
21058
- const bashRunner = await import("./token-goat-chunk-P46RTOAQ.mjs");
21014
+ const bashRunner = await import("./token-goat-chunk-4IIU3GNS.mjs");
21059
21015
  if (opts.compress === false) {
21060
21016
  process.exitCode = bashRunner.runRaw(command, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
21061
21017
  return;
21062
21018
  }
21063
- const maxTokens = opts.maxTokens !== void 0 ? requireNonNegativeInt2("--max-tokens", opts.maxTokens) : 0;
21019
+ const maxTokens = opts.maxTokens !== void 0 ? requireNonNegativeInt("--max-tokens", opts.maxTokens) : 0;
21064
21020
  process.exitCode = await bashRunner.run(command, {
21065
21021
  filterName: opts.filter,
21066
21022
  timeout: parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS),
@@ -21160,7 +21116,7 @@ function buildProgram() {
21160
21116
  more,
21161
21117
  () => runSymbol({
21162
21118
  ...name !== void 0 ? { name } : {},
21163
- limit: opts.limit !== void 0 ? requireNonNegativeInt2("--limit", opts.limit) : 20,
21119
+ limit: opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20,
21164
21120
  ...opts.file !== void 0 ? { file: opts.file } : {},
21165
21121
  ...opts.kind !== void 0 ? { kind: opts.kind } : {},
21166
21122
  ...projectRoot !== void 0 ? { projectRoot } : {},
@@ -21202,8 +21158,8 @@ function buildProgram() {
21202
21158
  return runBrief({
21203
21159
  spec,
21204
21160
  ...opts.json === true ? { json: true } : {},
21205
- ...opts.limit !== void 0 ? { limit: requireNonNegativeInt2("--limit", opts.limit) } : {},
21206
- ...opts.context !== void 0 ? { context: requireNonNegativeInt2("--context", opts.context) } : {},
21161
+ ...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
21162
+ ...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
21207
21163
  ...opts.excludeTests === true ? { excludeTests: true } : {},
21208
21164
  ...opts.grep !== void 0 ? { grep: opts.grep } : {}
21209
21165
  });
@@ -21211,14 +21167,26 @@ function buildProgram() {
21211
21167
  );
21212
21168
  program2.command("section <spec> [more...]").description(
21213
21169
  'read one section from a file (spec: file::heading, or file::<unambiguous heading prefix> \u2014 e.g. "Lesson 16" resolves a longer unique heading; comma-separated file::A,B for a merged multi-heading view), or list all sections with --list'
21214
- ).option("-j, --json", "output as JSON").option("--list", "list all section headings in the file instead of reading one").option("--grep <pattern>", "with --list, filter headings to this regex (literal substring if it is not valid regex)").action(
21215
- (spec, more, opts) => opts.list === true ? runExit(() => {
21170
+ ).option("-j, --json", "output as JSON").option("--list", "list all section headings in the file instead of reading one").option("--grep <pattern>", "with --list, filter headings to this regex (literal substring if it is not valid regex)").option("--max-lines <n>", "limit returned section content to at most N lines from the top").option("--head <n>", "alias for --max-lines").action((spec, more, opts) => {
21171
+ const maxLinesRaw = opts.maxLines ?? opts.head;
21172
+ const maxLines = maxLinesRaw !== void 0 ? requireNonNegativeInt("--max-lines", maxLinesRaw) : void 0;
21173
+ return opts.list === true ? runExit(() => {
21216
21174
  emitExtraFileArgsNote("section --list", spec, more, { mergeable: false });
21217
21175
  return runListSections({ file: spec, ...opts.json === true ? { json: true } : {}, ...opts.grep !== void 0 ? { grep: opts.grep } : {} });
21218
21176
  }) : runExitText(
21219
- () => noteExtraFileArgs("section", spec, more, () => runSection({ spec, ...opts.json === true ? { json: true } : {} }), { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) })
21220
- )
21221
- );
21177
+ () => noteExtraFileArgs(
21178
+ "section",
21179
+ spec,
21180
+ more,
21181
+ () => runSection({
21182
+ spec,
21183
+ ...opts.json === true ? { json: true } : {},
21184
+ ...maxLines !== void 0 ? { maxLines } : {}
21185
+ }),
21186
+ { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) }
21187
+ )
21188
+ );
21189
+ });
21222
21190
  program2.command("semantic [query]").description("semantic search (falls back to full-text search)").option("-l, --limit <n>", "max results").option("-j, --json", "output as JSON").option("--grep <pattern>", "filter to hits whose file path matches this regex (literal substring if it is not valid regex); matched against the path as rendered").option("--exclude-tests", "hide hits whose file is a test file (opt-in; default output is unchanged)").option("--preflight", "run semantic embedding preflight check and exit").option("--warm", "warm up the embedding model session in memory").action(guard(cmdSemantic));
21223
21191
  const registerSymbolListing = (name, description, run2) => {
21224
21192
  program2.command(`${name} <file> [more...]`).description(description).option("-j, --json", "output as JSON").option("--min-lines <n>", "only show symbols at least N lines long").option("--grep <pattern>", "only show symbols whose name matches this regex (literal substring if it is not valid regex)").option("--force-refresh", "reparse file from disk before querying (ignore stale index)").option("--stats", "add per-symbol reference count and doc-coverage flag").action(
@@ -21230,7 +21198,7 @@ function buildProgram() {
21230
21198
  () => run2({
21231
21199
  file,
21232
21200
  ...opts.json === true ? { json: true } : {},
21233
- ...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt2("--min-lines", opts.minLines) } : {},
21201
+ ...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
21234
21202
  ...opts.grep !== void 0 ? { grep: opts.grep } : {},
21235
21203
  ...opts.forceRefresh === true ? { forceRefresh: true } : {},
21236
21204
  ...opts.stats === true ? { stats: true } : {}
@@ -21259,9 +21227,9 @@ function buildProgram() {
21259
21227
  spec,
21260
21228
  ...opts.callers === true ? { callers: true } : {},
21261
21229
  ...opts.json === true ? { json: true } : {},
21262
- ...opts.limit !== void 0 ? { limit: requireNonNegativeInt2("--limit", opts.limit) } : {},
21263
- ...opts.top !== void 0 ? { top: requireNonNegativeInt2("--top", opts.top) } : {},
21264
- ...opts.context !== void 0 ? { context: requireNonNegativeInt2("--context", opts.context) } : {},
21230
+ ...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
21231
+ ...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
21232
+ ...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
21265
21233
  ...opts.excludeTests === true ? { excludeTests: true } : {},
21266
21234
  ...opts.grep !== void 0 ? { grep: opts.grep } : {}
21267
21235
  });
@@ -21275,7 +21243,7 @@ function buildProgram() {
21275
21243
  program2.command("bench").description("replay a corpus of captured command output through the compressors; prints the byte savings ratio and a fidelity check, and exits 1 when a must-keep line was dropped").option("--corpus <dir>", "corpus directory (default: tests/fixtures/bench, relative to the working directory)").option("--tsv <path>", "append one result row, keyed by the current commit, creating the file and its header if absent").option("--validate", "score the corpus with control filters instead: proves the ratio has a floor and the fidelity guard can actually fail").option("-j, --json", "output as JSON").action(
21276
21244
  (opts) => runExitText(
21277
21245
  () => runBenchCommand({
21278
- corpus: opts.corpus ?? path42.join("tests", "fixtures", "bench"),
21246
+ corpus: opts.corpus ?? path41.join("tests", "fixtures", "bench"),
21279
21247
  ...opts.tsv !== void 0 ? { tsv: opts.tsv } : {},
21280
21248
  ...opts.validate === true ? { validate: true } : {},
21281
21249
  ...opts.json === true ? { json: true } : {}
@@ -21309,7 +21277,7 @@ function buildProgram() {
21309
21277
  program2.command("compress [command...]").alias("bash").alias("run").description("run a shell command (under a POSIX shell / bash) and emit a compressed view of its output").allowUnknownOption(true).option("-c, --cmd <command>", "the shell command to run, as one string (use / for paths across platforms)").option("--cmd-b64 <payload>", "the shell command as a base64-encoded string (preserves quotes, backslashes, and symbols across platforms)").option("-f, --filter <name>", "filter name (auto-detected from the command when omitted)").option("--timeout <seconds>", "wall-clock timeout in seconds (0 = built-in default)").option("--no-compress", "stream output raw without compression (debug the wrapper)").option("--profile <name>", "compression profile: aggressive | balanced | minimal").option("--max-tokens <n>", "post-compress token cap (0 = no cap)").option("-q, --quiet-success", "on exit code 0, emit only [tg: ok] summary and store full output for recall via bash-output").option("--native", "use native platform shell (e.g. cmd.exe on Windows) instead of bash, preserving Windows path backslashes").action(cmdCompress);
21310
21278
  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(
21311
21279
  (opts) => guard(async () => {
21312
- const { cmdUpgrade } = await import("./token-goat-chunk-VJQBYSXG.mjs");
21280
+ const { cmdUpgrade } = await import("./token-goat-chunk-ZJWPJL7O.mjs");
21313
21281
  await cmdUpgrade(opts, () => cmdInstall({}));
21314
21282
  })()
21315
21283
  );
@@ -21379,6 +21347,9 @@ async function run(argv = process.argv) {
21379
21347
  }
21380
21348
 
21381
21349
  export {
21350
+ requireInt,
21351
+ requireNonNegativeInt,
21352
+ requirePositiveInt,
21382
21353
  expandGlobs,
21383
21354
  fenceFileText,
21384
21355
  fenceFileFieldIfMatched,
@@ -21438,9 +21409,6 @@ export {
21438
21409
  CliError,
21439
21410
  out,
21440
21411
  err,
21441
- requireInt2 as requireInt,
21442
- requireNonNegativeInt2 as requireNonNegativeInt,
21443
- requirePositiveInt2 as requirePositiveInt,
21444
21412
  cmdIndex,
21445
21413
  visualStudioManualSteps,
21446
21414
  leftoverIntegrations,