token-goat 2.9.19 → 2.9.20

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-2CLWWRFR.mjs +26 -0
  3. package/dist/{token-goat-chunk-WKO5TVNA.mjs → token-goat-chunk-2Q3N5KUQ.mjs} +531 -571
  4. package/dist/{token-goat-chunk-DA6TIDLI.mjs → token-goat-chunk-3OGZOFLS.mjs} +18 -13
  5. package/dist/{token-goat-chunk-PIAESKVU.mjs → token-goat-chunk-3VTJC7KU.mjs} +568 -429
  6. package/dist/{token-goat-chunk-ZGQXRYKE.mjs → token-goat-chunk-6LJNM4ZZ.mjs} +1 -1
  7. package/dist/{token-goat-chunk-SOKAPOOC.mjs → token-goat-chunk-74ADIZZX.mjs} +46 -47
  8. package/dist/{token-goat-chunk-LACH3WKS.mjs → token-goat-chunk-ASTBLE2R.mjs} +3 -3
  9. package/dist/{token-goat-chunk-VJQBYSXG.mjs → token-goat-chunk-AU2K2QHR.mjs} +3 -3
  10. package/dist/token-goat-chunk-BQIC3AVL.mjs +292 -0
  11. package/dist/{token-goat-chunk-P46RTOAQ.mjs → token-goat-chunk-CBXLE54Q.mjs} +10 -7
  12. package/dist/{token-goat-chunk-3W5P673V.mjs → token-goat-chunk-DNAF26L4.mjs} +1 -1
  13. package/dist/{token-goat-chunk-FQYMZASI.mjs → token-goat-chunk-DPHJU3P7.mjs} +789 -4456
  14. package/dist/{token-goat-chunk-FX5DHKGF.mjs → token-goat-chunk-DW4VKU7D.mjs} +4 -4
  15. package/dist/token-goat-chunk-DYUBZZNP.mjs +198 -0
  16. package/dist/{token-goat-chunk-EP7TCWHM.mjs → token-goat-chunk-ERGHDBC6.mjs} +70 -65
  17. package/dist/token-goat-chunk-EUZKG7ZO.mjs +304 -0
  18. package/dist/{token-goat-chunk-AVRD7SBO.mjs → token-goat-chunk-FWYZVS3E.mjs} +270 -29
  19. package/dist/token-goat-chunk-G6SYQQO7.mjs +41 -0
  20. package/dist/{token-goat-chunk-62VJ47WN.mjs → token-goat-chunk-GIPMOGOI.mjs} +5 -5
  21. package/dist/{token-goat-chunk-L2UOJBV4.mjs → token-goat-chunk-H4VC5TR6.mjs} +27 -19
  22. package/dist/{token-goat-chunk-2GTB5RBZ.mjs → token-goat-chunk-I5PQRHFE.mjs} +11 -271
  23. package/dist/{token-goat-chunk-MU6I4IDI.mjs → token-goat-chunk-ITWYDOKP.mjs} +11 -7
  24. package/dist/{token-goat-chunk-IBTFGZBX.mjs → token-goat-chunk-K3ZNBN4U.mjs} +2 -2
  25. package/dist/{token-goat-chunk-S7RFIX4Y.mjs → token-goat-chunk-K5XPIUVB.mjs} +45 -14
  26. package/dist/token-goat-chunk-KT75DZK4.mjs +1989 -0
  27. package/dist/{token-goat-chunk-F24NE3FO.mjs → token-goat-chunk-MNJWG2ZO.mjs} +2 -2
  28. package/dist/{token-goat-chunk-NJKNAC37.mjs → token-goat-chunk-MUJV7BR3.mjs} +1 -1
  29. package/dist/{token-goat-chunk-FJZ5MZK7.mjs → token-goat-chunk-ODF7H7WS.mjs} +2 -2
  30. package/dist/{token-goat-chunk-GYC34EGE.mjs → token-goat-chunk-ONMU7BON.mjs} +2 -2
  31. package/dist/{token-goat-chunk-KWM6KBPA.mjs → token-goat-chunk-P4XBHZXU.mjs} +1 -1
  32. package/dist/{token-goat-chunk-7DGTVBWP.mjs → token-goat-chunk-PAVWB4L4.mjs} +159 -1863
  33. package/dist/{token-goat-chunk-DHIXE6GV.mjs → token-goat-chunk-PTW22MRC.mjs} +1 -1
  34. package/dist/token-goat-chunk-SL454EN5.mjs +1816 -0
  35. package/dist/token-goat-chunk-SP5QR2HN.mjs +1379 -0
  36. package/dist/{token-goat-chunk-BTKDZW7F.mjs → token-goat-chunk-T46UKRFM.mjs} +1 -1
  37. package/dist/{token-goat-chunk-JNIFSVWM.mjs → token-goat-chunk-T63VHOL3.mjs} +5 -1
  38. package/dist/token-goat-chunk-VSJGPEBB.mjs +248 -0
  39. package/dist/{token-goat-chunk-EXLLLIC3.mjs → token-goat-chunk-VYXGHGUY.mjs} +3 -3
  40. package/dist/token-goat-chunk-W6QYCWAT.mjs +128 -0
  41. package/dist/{token-goat-chunk-HPMMLV37.mjs → token-goat-chunk-Z4ALQADF.mjs} +256 -341
  42. package/dist/token-goat-chunk-ZHFM4DHJ.mjs +324 -0
  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-ONMU7BON.mjs";
6
+ import {
7
+ buildCopilotWasteReport,
8
+ findLatestCopilotSession,
9
+ findProjectSession,
10
+ isCopilotTranscript
11
+ } from "./token-goat-chunk-EUZKG7ZO.mjs";
12
+ import {
13
+ buildResumePacket
14
+ } from "./token-goat-chunk-W6QYCWAT.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,197 @@ import {
96
107
  symbolNamesInFile,
97
108
  upsertNote,
98
109
  warnIfFilesStale
99
- } from "./token-goat-chunk-PIAESKVU.mjs";
110
+ } from "./token-goat-chunk-3VTJC7KU.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-I5PQRHFE.mjs";
130
+ import {
131
+ buildWasteReport,
132
+ extractResultText,
133
+ findLatestTranscript,
134
+ projectTranscriptsDir,
135
+ safeStringify
136
+ } from "./token-goat-chunk-BQIC3AVL.mjs";
137
+ import {
138
+ buildManifest
139
+ } from "./token-goat-chunk-VSJGPEBB.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
166
  findOrphanedChunkPaths,
176
167
  findSystemTempFiles,
177
168
  findTopIndexedProjects,
178
- formatAge,
179
- formatBytes,
180
169
  formatMemSuggestions,
181
170
  formatProjectMap,
182
171
  formatSymbolLocation,
183
- formatTokenEstimate,
184
- getContextPressure,
185
172
  getDirtyPathsFor,
186
173
  getEmbeddingCoverage,
187
174
  getHintStatsSummary,
188
175
  getHintStatsTotals,
189
176
  getParserFreshness,
190
177
  getProjectIndexCounts,
191
- getSessionFiles,
192
- getSessionId,
193
- getSkillFilePath,
194
178
  getTrackedFiles,
195
- hasManagedServer,
196
- incrementSkillHit,
179
+ indexSizeBytes,
197
180
  injectionFencingEnabled,
198
- installCopilotCli,
199
- installCopilotHooksFile,
200
- installVisualStudio,
201
181
  isCompactFresh,
202
- isCopilotCliInstalled,
203
182
  isHintCategory,
204
183
  isIgnoredIndexPath,
205
- isManagedServer,
206
184
  isPrivateIpv4Octets,
207
185
  isPrivateIpv6Groups,
208
- isResidueServersJson,
209
186
  isSuppressionCategory,
210
- isVisualStudioInstalled,
211
187
  isWorkerRunning,
212
- jsonc,
213
- listOutputs,
214
- listSkills,
215
- loadSessionCache,
216
- managedServer,
217
188
  mapLookupBytesSaved,
218
189
  markCategoryEffective,
219
190
  markCategoryIneffective,
220
191
  pruneBlockedRoot,
221
192
  pruneDeletedFiles,
222
193
  pruneOrphanedChunks,
223
- pruneSkillOutputs,
194
+ pruneOrphanedVectors,
224
195
  pruneSystemTempFiles,
225
196
  pruneUnembeddableChunks,
226
197
  readCompactBody,
227
- readCopilotHooksOwners,
228
- readCopilotMcpTools,
229
- readServersJson,
230
- releaseCopilotHooksFile,
198
+ reclaimIndex,
199
+ recordKnownRootThrottled,
231
200
  removeFileFromIndex,
232
201
  resetHintStats,
233
202
  runContextStats,
234
203
  runDetachedWorkerDaemon,
235
204
  scanAndRecord,
236
- serversOf,
237
- sessionsDir,
238
- setTokenGoatServer,
239
205
  shrinkImage,
240
- skillDescriptionLine,
241
- skillOutputsDir,
242
206
  startDetachedWorker,
243
207
  stopWorker,
244
- storeCompact,
245
- storeOutput,
246
- syncVisualStudioProjectGuidance,
247
208
  toolMatcherFor,
248
- trimToBudget,
249
- uninstallCopilotCli,
250
- uninstallVisualStudio,
251
209
  urlPolicyDenialReason,
252
210
  visionTokensSavedByText,
211
+ walkProject,
212
+ writeCompact
213
+ } from "./token-goat-chunk-DPHJU3P7.mjs";
214
+ import {
215
+ CompressedOutput,
216
+ TOOL_FILTERS,
217
+ ToolFilter,
218
+ deliverCompressed,
219
+ detectFromCommand
220
+ } from "./token-goat-chunk-PAVWB4L4.mjs";
221
+ import {
222
+ HOOKS_SCRIPT_FILE,
223
+ MATERIALIZE_SHRUNK_IMAGE_JS,
224
+ buildGuidanceBlock,
225
+ buildGuidanceBody,
226
+ bundledCliPath,
227
+ copilotCliConfigPath,
228
+ copilotCliMcpToolsDir,
229
+ copilotCliScriptPath,
230
+ copilotHooksFilePaths,
231
+ dropEmptyServers,
232
+ hasManagedServer,
233
+ installCopilotCli,
234
+ installCopilotHooksFile,
235
+ installVisualStudio,
236
+ isCopilotCliInstalled,
237
+ isManagedServer,
238
+ isResidueServersJson,
239
+ isVisualStudioInstalled,
240
+ jsonc,
241
+ managedServer,
242
+ readCopilotHooksOwners,
243
+ readServersJson,
244
+ releaseCopilotHooksFile,
245
+ serversOf,
246
+ setTokenGoatServer,
247
+ skillDescriptionLine,
248
+ syncVisualStudioProjectGuidance,
249
+ uninstallCopilotCli,
250
+ uninstallVisualStudio,
253
251
  visualStudioDuplicateNote,
254
252
  visualStudioManagedEntry,
255
253
  visualStudioMcpStatus,
256
254
  visualStudioOtherScopeHasManagedServer,
257
255
  visualStudioProjectMcpPath,
258
256
  visualStudioSolutionVscodeMcpPath,
259
- visualStudioUserMcpPath,
260
- walkProject,
261
- writeCompact
262
- } from "./token-goat-chunk-FQYMZASI.mjs";
257
+ visualStudioUserMcpPath
258
+ } from "./token-goat-chunk-SP5QR2HN.mjs";
259
+ import {
260
+ AGENT_SALT_MARKER,
261
+ CONTEXT_AUTOCOMPACT_TOKENS,
262
+ SESSIONS_SUBDIR,
263
+ SKILLS_OUTPUT_SUBDIR,
264
+ contentHash,
265
+ eventCount,
266
+ extractCompactFromMarker,
267
+ extractNamedSection,
268
+ findLatestSessionId,
269
+ formatAge,
270
+ getContextPressure,
271
+ getSessionFiles,
272
+ getSessionId,
273
+ getSkillFilePath,
274
+ incrementSkillHit,
275
+ listOutputs,
276
+ listSkills,
277
+ loadSessionCache,
278
+ loadSessionState,
279
+ pruneSkillOutputs,
280
+ sessionsDir,
281
+ skillOutputsDir,
282
+ storeCompact,
283
+ storeOutput
284
+ } from "./token-goat-chunk-KT75DZK4.mjs";
285
+ import {
286
+ capJsonRows,
287
+ claudeConfigDir,
288
+ estimateTokens,
289
+ estimateTokensFromLength,
290
+ formatBytes,
291
+ formatTokenEstimate,
292
+ trimToBudget
293
+ } from "./token-goat-chunk-ZHFM4DHJ.mjs";
263
294
  import {
264
295
  BASH_OUTPUT_SUBDIR,
265
- CompressedOutput,
266
- FILTERS,
267
- TOOL_FILTERS,
268
- ToolFilter,
269
296
  clipLongMatchLine,
270
297
  combineStreams,
271
- deliverCompressed,
272
- detectFromCommand,
273
298
  getBashOutput,
274
299
  isRecallCacheType
275
- } from "./token-goat-chunk-7DGTVBWP.mjs";
300
+ } from "./token-goat-chunk-SL454EN5.mjs";
276
301
  import {
277
302
  C,
278
303
  DEFAULT_MAX_AGE_MS,
@@ -286,7 +311,6 @@ import {
286
311
  formatLocalTimestamp,
287
312
  getGlobalDb,
288
313
  listBlobs,
289
- loadBlob,
290
314
  pruneBlobs,
291
315
  pruneStalePatternCoveredUnmappedTools,
292
316
  readUnmappedTools,
@@ -297,21 +321,21 @@ import {
297
321
  savedTokensFromBytes,
298
322
  statsHasDurationColumn,
299
323
  statsHasHarnessColumn,
300
- stripAnsi,
324
+ stripAnsiEscapes,
301
325
  summarize
302
- } from "./token-goat-chunk-S7RFIX4Y.mjs";
326
+ } from "./token-goat-chunk-K5XPIUVB.mjs";
303
327
  import {
304
328
  docxOutline,
305
329
  docxTables,
306
330
  docxText,
307
331
  formatDocxTables
308
- } from "./token-goat-chunk-DHIXE6GV.mjs";
332
+ } from "./token-goat-chunk-PTW22MRC.mjs";
309
333
  import {
310
334
  pptxNotesText,
311
335
  pptxOutline,
312
336
  pptxSlideText,
313
337
  pptxTextGrep
314
- } from "./token-goat-chunk-3W5P673V.mjs";
338
+ } from "./token-goat-chunk-DNAF26L4.mjs";
315
339
  import {
316
340
  formatXlsxColumns,
317
341
  formatXlsxRange,
@@ -320,12 +344,13 @@ import {
320
344
  querySheet,
321
345
  rangeSheet,
322
346
  xlsxColumns
323
- } from "./token-goat-chunk-FJZ5MZK7.mjs";
347
+ } from "./token-goat-chunk-ODF7H7WS.mjs";
324
348
  import {
325
349
  formatCsvTable,
326
350
  parseWhereSpecs
327
- } from "./token-goat-chunk-KWM6KBPA.mjs";
351
+ } from "./token-goat-chunk-P4XBHZXU.mjs";
328
352
  import {
353
+ assetEmbedSha,
329
354
  deleteFileEmbeddings,
330
355
  embeddingBackendLoadError,
331
356
  embeddingsDepsAvailable,
@@ -342,18 +367,19 @@ import {
342
367
  isTreeSitterAvailable,
343
368
  listSections,
344
369
  loadRegexExtractors,
370
+ maxChunksEmbedSha,
345
371
  missingTreeSitterGrammarPackages,
346
372
  modelFilesPresent,
347
373
  parserFingerprintForLanguage,
348
374
  readSection,
349
375
  treeSitterCoreAvailable,
350
376
  treeSitterCoreLoadError
351
- } from "./token-goat-chunk-EP7TCWHM.mjs";
377
+ } from "./token-goat-chunk-ERGHDBC6.mjs";
352
378
  import {
353
379
  getFileEntry,
354
380
  getProjectFileEntries,
355
381
  querySymbols
356
- } from "./token-goat-chunk-IBTFGZBX.mjs";
382
+ } from "./token-goat-chunk-K3ZNBN4U.mjs";
357
383
  import {
358
384
  TREE_SITTER_LANGUAGES,
359
385
  detectLanguage,
@@ -364,7 +390,7 @@ import {
364
390
  matchGroovySlashy,
365
391
  nonTreeSitterLanguageCount,
366
392
  redactIfDotenv
367
- } from "./token-goat-chunk-JNIFSVWM.mjs";
393
+ } from "./token-goat-chunk-T63VHOL3.mjs";
368
394
  import {
369
395
  CONFIG_KEY_ENV_OVERRIDES,
370
396
  PROJECT_LOCKED_KEYS,
@@ -393,7 +419,7 @@ import {
393
419
  stringify,
394
420
  validateEnumField,
395
421
  validateNumericField
396
- } from "./token-goat-chunk-SOKAPOOC.mjs";
422
+ } from "./token-goat-chunk-74ADIZZX.mjs";
397
423
  import {
398
424
  ENV_KEYS,
399
425
  LOCK_WAIT_MS_HARDENED,
@@ -438,7 +464,6 @@ import {
438
464
  requirePositiveStrictInt,
439
465
  resolveOnPath,
440
466
  runGit,
441
- safeSlice,
442
467
  sleepSync,
443
468
  stripDelimitedBlock,
444
469
  stripLower,
@@ -455,7 +480,7 @@ import {
455
480
  withRetryOnLock,
456
481
  writeIfDifferent,
457
482
  writeJsonSettings
458
- } from "./token-goat-chunk-ZGQXRYKE.mjs";
483
+ } from "./token-goat-chunk-6LJNM4ZZ.mjs";
459
484
  import {
460
485
  UNTRUSTED_FILE_TAG,
461
486
  UNTRUSTED_TOOL_TAG,
@@ -3603,9 +3628,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3603
3628
  * @param {string} [path]
3604
3629
  * @return {(string|null|Command)}
3605
3630
  */
3606
- executableDir(path43) {
3607
- if (path43 === void 0) return this._executableDir;
3608
- this._executableDir = path43;
3631
+ executableDir(path42) {
3632
+ if (path42 === void 0) return this._executableDir;
3633
+ this._executableDir = path42;
3609
3634
  return this;
3610
3635
  }
3611
3636
  /**
@@ -3905,8 +3930,8 @@ function attemptedCommandName(argv) {
3905
3930
  }
3906
3931
 
3907
3932
  // src/cli.ts
3908
- import * as fs46 from "fs";
3909
- import * as path42 from "path";
3933
+ import * as fs45 from "fs";
3934
+ import * as path41 from "path";
3910
3935
 
3911
3936
  // src/walk_index.ts
3912
3937
  init_define_import_meta_env();
@@ -4177,6 +4202,12 @@ main().catch(() => {
4177
4202
  })
4178
4203
  `;
4179
4204
 
4205
+ // src/canonical_skill.ts
4206
+ init_define_import_meta_env();
4207
+ import { gunzipSync } from "node:zlib";
4208
+ var CANONICAL_SKILL_GZIP_BASE64 = "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";
4209
+ var CANONICAL_SKILL_MD = gunzipSync(Buffer.from(CANONICAL_SKILL_GZIP_BASE64, "base64")).toString("utf8");
4210
+
4180
4211
  // src/install.ts
4181
4212
  var HOOK_EVENT_MAP = [
4182
4213
  ["PreToolUse", "pre_tool_use"],
@@ -4476,23 +4507,8 @@ function findStrayClaudeMdBlocks(searchRoot) {
4476
4507
  walk(root, 0);
4477
4508
  return found.sort();
4478
4509
  }
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
4510
  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
- `;
4511
+ return CANONICAL_SKILL_MD;
4496
4512
  }
4497
4513
  function skillDir() {
4498
4514
  return path3.join(claudeConfigDir(), "skills", "token-goat");
@@ -4539,6 +4555,9 @@ ${SHIM_REQUIRES}
4539
4555
 
4540
4556
  ${SHIM_VALID_HOOK_EVENTS}
4541
4557
 
4558
+ // Codex sets no env var of its own identifying a hook subprocess; see this module's docblock.
4559
+ process.env.TOKEN_GOAT_HARNESS_OVERRIDE = 'codex'
4560
+
4542
4561
  // Keep in sync with CLAUDE_CODE_EVENT_NAMES in src/hook_registry.ts -- the
4543
4562
  // live/wired 'token-goat hook <event>' path (src/relay.ts) always emits this
4544
4563
  // PascalCase spelling for hookSpecificOutput.hookEventName, never the raw
@@ -4667,7 +4686,7 @@ function groupHasTokenGoat2(groups, matcher, predicate) {
4667
4686
 
4668
4687
  // src/bridges/codex_install.ts
4669
4688
  var CODEX_COMMAND_MARKER = "token-goat-shim";
4670
- var CODEX_MATCHERS = ["view_image|shell|bash", "apply_patch", "web_search"];
4689
+ var CODEX_MATCHERS = ["view_image|Bash|exec|shell|bash", "apply_patch", "web_search"];
4671
4690
  var CODEX_HOOK_EVENTS = ["PreToolUse", "PostToolUse"];
4672
4691
  var CODEX_EVENT_ARG = {
4673
4692
  PreToolUse: "pre_tool_use",
@@ -4950,7 +4969,7 @@ function buildAgentsBlock() {
4950
4969
  return buildGuidanceBlock({
4951
4970
  beginMarker: AGENTS_BEGIN,
4952
4971
  endMarker: AGENTS_END,
4953
- fallbackToolClause: "Codex's native `shell`, `apply_patch`, and `view_image` tools (shell commands like `cat`/`type` run inside `shell`)",
4972
+ fallbackToolClause: "Codex's native `exec`, `apply_patch`, and `view_image` tools (shell commands like `cat`/`type` run inside `exec`)",
4954
4973
  gdrive: loadConfig().gdrive.enabled
4955
4974
  });
4956
4975
  }
@@ -5142,10 +5161,10 @@ function readQwenSettings(p, opts = {}) {
5142
5161
  let parsed;
5143
5162
  try {
5144
5163
  parsed = JSON.parse(raw);
5145
- } catch {
5164
+ } catch (e) {
5146
5165
  if (opts.strict === true) {
5147
5166
  throw new QwenSettingsParseError(
5148
- `Qwen Code settings file '${p}' exists but contains invalid JSON. Fix or back up the file before running install.`
5167
+ `Qwen Code settings file '${p}' exists but contains invalid JSON. Fix or back up the file before running install. (${extractErrorMessage(e)})`
5149
5168
  );
5150
5169
  }
5151
5170
  return {};
@@ -7155,8 +7174,8 @@ import * as path15 from "node:path";
7155
7174
  var INTERPRETER_BASENAMES = /* @__PURE__ */ new Set(["node.exe", "python.exe", "python3.exe", "pwsh.exe", "powershell.exe", "cmd.exe"]);
7156
7175
  function describeProcess(commandLine) {
7157
7176
  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;
7177
+ const basename10 = m[0].replace(/\\/g, "/").split("/").pop() ?? m[0];
7178
+ if (!INTERPRETER_BASENAMES.has(basename10.toLowerCase())) return basename10;
7160
7179
  }
7161
7180
  const trimmed = commandLine.trim();
7162
7181
  return trimmed.length > 0 ? trimmed.length > 60 ? trimmed.slice(0, 60) + "\u2026" : trimmed : "(no command line)";
@@ -7542,7 +7561,7 @@ function checkSecurityPosture(cfg, dataDirPath) {
7542
7561
  }
7543
7562
 
7544
7563
  // src/cli_doctor.ts
7545
- var DB_SIZE_WARN_BYTES = 1024 * 1024 * 1024;
7564
+ var DB_SIZE_WARN_BYTES = 1500 * 1024 * 1024;
7546
7565
  function freelistBytes(header) {
7547
7566
  if (header.length < 40) return 0;
7548
7567
  const rawPageSize = header.readUInt16BE(16);
@@ -7578,12 +7597,15 @@ function dbCategoryBreakdown(dbPath) {
7578
7597
  }
7579
7598
  return shares.sort((a, b) => b.bytes - a.bytes);
7580
7599
  }
7581
- function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories = [], topConsumers = []) {
7600
+ function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories = [], topConsumers = [], thresholdMb = 1500, autoReclaimEmbeddings = false) {
7582
7601
  const mb = (bytes) => Math.round(bytes / (1024 * 1024));
7583
7602
  const advice = [];
7584
7603
  if (freeBytes >= sizeBytes / 10) advice.push(`'token-goat reclaim-index' returns the ${mb(freeBytes)} MB of it that is free pages`);
7585
7604
  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). `;
7605
+ if (autoReclaimEmbeddings) {
7606
+ advice.push(`'token-goat doctor --repair' will automatically reclaim embedding vectors and compact global.db`);
7607
+ }
7608
+ const head = `global.db is ${mb(sizeBytes)} MB at ${displaySafeText(dbPath)} (larger than threshold of ${thresholdMb} MB). `;
7587
7609
  const totalCategoryBytes = categories.reduce((sum, c) => sum + c.bytes, 0);
7588
7610
  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
7611
  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 +7615,7 @@ function oversizeDbMessage(dbPath, sizeBytes, freeBytes, tempRows, categories =
7593
7615
  }
7594
7616
  return base;
7595
7617
  }
7596
- function checkDbExists(dataDir2) {
7618
+ function checkDbExists(dataDir2, maxDbSizeMb) {
7597
7619
  const dbPath = path17.join(dataDir2, "global.db");
7598
7620
  if (!fs18.existsSync(dbPath)) {
7599
7621
  return {
@@ -7602,7 +7624,7 @@ function checkDbExists(dataDir2) {
7602
7624
  message: `global.db not found at ${dbPath}`
7603
7625
  };
7604
7626
  }
7605
- const sizeBytes = fs18.statSync(dbPath).size;
7627
+ const sizeBytes = indexSizeBytes(dbPath);
7606
7628
  const SQLITE_HEADER = "SQLite format 3\0";
7607
7629
  let header = "";
7608
7630
  let headerBytes = Buffer.alloc(0);
@@ -7625,7 +7647,10 @@ function checkDbExists(dataDir2) {
7625
7647
  message: `global.db at ${dbPath} is not a valid SQLite file (${sizeBytes} bytes) \u2014 likely truncated or corrupt`
7626
7648
  };
7627
7649
  }
7628
- if (sizeBytes > DB_SIZE_WARN_BYTES) {
7650
+ const cfg = loadConfig();
7651
+ const thresholdMb = maxDbSizeMb ?? cfg.indexing.max_db_size_mb ?? 1500;
7652
+ const warnBytes = thresholdMb * 1024 * 1024;
7653
+ if (sizeBytes > warnBytes) {
7629
7654
  let tempRows = 0;
7630
7655
  try {
7631
7656
  tempRows = findSystemTempFiles(dbPath).length;
@@ -7637,7 +7662,20 @@ function checkDbExists(dataDir2) {
7637
7662
  } catch {
7638
7663
  }
7639
7664
  const topConsumers = findTopIndexedProjects(dbPath, 3);
7640
- return { name: "Database", status: "warn", message: oversizeDbMessage(dbPath, sizeBytes, freelistBytes(headerBytes), tempRows, categories, topConsumers) };
7665
+ return {
7666
+ name: "Database",
7667
+ status: "warn",
7668
+ message: oversizeDbMessage(
7669
+ dbPath,
7670
+ sizeBytes,
7671
+ freelistBytes(headerBytes),
7672
+ tempRows,
7673
+ categories,
7674
+ topConsumers,
7675
+ thresholdMb,
7676
+ cfg.indexing.auto_reclaim_embeddings
7677
+ )
7678
+ };
7641
7679
  }
7642
7680
  return {
7643
7681
  name: "Database",
@@ -8163,7 +8201,8 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
8163
8201
  results.push(checkTreeSitter());
8164
8202
  results.push(checkStrayClaudeMdBlocks());
8165
8203
  results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
8166
- results.push(checkDbExists(actualDataDir));
8204
+ const cfg = loadConfig(rootDir);
8205
+ results.push(checkDbExists(actualDataDir, cfg.indexing.max_db_size_mb));
8167
8206
  results.push(checkSymbolBodySize(path17.join(actualDataDir, "global.db")));
8168
8207
  results.push(checkSymbolCount(path17.join(actualDataDir, "global.db"), rootDir));
8169
8208
  results.push(checkDirtyQueueHealth(actualDataDir));
@@ -8255,6 +8294,22 @@ async function runDoctorRepair(opts) {
8255
8294
  } catch (e) {
8256
8295
  errors.push(`Failed to clean up ${displaySafeText(projectMcp)}: ${extractErrorMessage(e)}`);
8257
8296
  }
8297
+ const actualDataDir = opts?.dataDir ?? dataDir();
8298
+ const dbPath = path17.join(actualDataDir, "global.db");
8299
+ if (cfg.indexing.auto_reclaim_embeddings && fs18.existsSync(dbPath)) {
8300
+ try {
8301
+ const sizeBytes = indexSizeBytes(dbPath);
8302
+ const thresholdMb = cfg.indexing.max_db_size_mb ?? 1500;
8303
+ const maxBytes = thresholdMb * 1024 * 1024;
8304
+ if (sizeBytes > maxBytes) {
8305
+ const res = reclaimIndex(dbPath, { embeddingsOnly: true });
8306
+ const savedMb = Math.round((res.beforeBytes - res.afterBytes) / (1024 * 1024));
8307
+ repairs.push(`Auto-reclaimed embeddings and compacted global.db (${savedMb > 0 ? `reclaimed ${savedMb} MB` : "compacted"})`);
8308
+ }
8309
+ } catch (e) {
8310
+ errors.push(`Failed to auto-reclaim embeddings from global.db: ${extractErrorMessage(e)}`);
8311
+ }
8312
+ }
8258
8313
  return { repairs, errors };
8259
8314
  }
8260
8315
  function printDoctorResults(results) {
@@ -8416,7 +8471,7 @@ async function runDoctorAndExit(opts) {
8416
8471
  }
8417
8472
  }
8418
8473
  try {
8419
- const { checkUpdateStatus } = await import("./token-goat-chunk-VJQBYSXG.mjs");
8474
+ const { checkUpdateStatus } = await import("./token-goat-chunk-AU2K2QHR.mjs");
8420
8475
  const update = await checkUpdateStatus(1500);
8421
8476
  if (update.updateAvailable && update.latest) {
8422
8477
  console.log(`
@@ -8851,13 +8906,6 @@ function performHttpFetch(targetUrl, opts) {
8851
8906
  }
8852
8907
 
8853
8908
  // 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
8909
  function saveConfigSafe(cfg) {
8862
8910
  ensureDirSync(path18.dirname(configPath()));
8863
8911
  saveConfig(cfg);
@@ -9307,13 +9355,15 @@ function cmdProject(opts) {
9307
9355
  invalidateConfigCache();
9308
9356
  const prunedTempFiles = pruneSystemTempFiles();
9309
9357
  const prunedOrphanChunks = pruneOrphanedChunks();
9358
+ const prunedOrphanVectors = pruneOrphanedVectors();
9310
9359
  if (opts.json === true) {
9311
- emit(displaySafeJson({ pruned: removed, blocked_roots: after, prunedTempFiles: prunedTempFiles.length, prunedOrphanChunkFiles: prunedOrphanChunks.length }));
9360
+ emit(displaySafeJson({ pruned: removed, blocked_roots: after, prunedTempFiles: prunedTempFiles.length, prunedOrphanChunkFiles: prunedOrphanChunks.length, prunedOrphanVectors }));
9312
9361
  return;
9313
9362
  }
9314
9363
  emit(`Pruned ${removed} stale root(s). Remaining: ${after.length}`);
9315
9364
  emit(`Pruned ${prunedTempFiles.length} stale indexed temp-dir file(s).`);
9316
9365
  emit(`Pruned orphaned embedding chunks for ${prunedOrphanChunks.length} file(s).`);
9366
+ emit(`Pruned ${prunedOrphanVectors} orphaned embedding vector(s).`);
9317
9367
  return;
9318
9368
  }
9319
9369
  emitErr(`project: unknown action '${action}'. Use list, exclude, or prune.`);
@@ -12211,7 +12261,7 @@ function extractGeneric(lines) {
12211
12261
  return result;
12212
12262
  }
12213
12263
  function extractFailures(text, options) {
12214
- const cleaned = stripAnsi(text);
12264
+ const cleaned = stripAnsiEscapes(text);
12215
12265
  const lines = cleaned.split("\n");
12216
12266
  const detected = options?.runner ?? detectRunner(cleaned);
12217
12267
  if (detected === "pytest") {
@@ -12386,7 +12436,8 @@ function cmdConflicts(targetPath, opts) {
12386
12436
  process.exitCode = runConflicts({
12387
12437
  ...targetPath !== void 0 ? { path: targetPath } : {},
12388
12438
  ...opts.json === true ? { json: true } : {},
12389
- ...opts.summary === true ? { summary: true } : {}
12439
+ ...opts.summary === true ? { summary: true } : {},
12440
+ ...opts.context !== void 0 ? { context: parseInt(opts.context, 10) } : {}
12390
12441
  });
12391
12442
  }
12392
12443
  async function cmdScreenshot(url, destPath, opts) {
@@ -12434,7 +12485,7 @@ function cmdPack(patterns, opts) {
12434
12485
  }
12435
12486
  const result = expandedList.length > 0 ? collectFiles(root, expandedList, collectOpts) : collectFromStdin(root, collectOpts);
12436
12487
  if (opts.budget !== void 0) {
12437
- const budgetN = requireInt2("--budget", opts.budget);
12488
+ const budgetN = requireInt("--budget", opts.budget);
12438
12489
  if (result.total_tokens > budgetN) {
12439
12490
  err(`token-goat: pack: token count ${result.total_tokens} exceeds budget ${budgetN}`);
12440
12491
  process.exitCode = 3;
@@ -12471,7 +12522,7 @@ function cmdTokens(patterns, opts) {
12471
12522
  let entries = [...result.entries];
12472
12523
  if (opts.asc === true) entries.reverse();
12473
12524
  const eligibleCount = entries.length;
12474
- if (opts.top !== void 0) entries = entries.slice(0, requireNonNegativeInt2("--top", opts.top));
12525
+ if (opts.top !== void 0) entries = entries.slice(0, requireNonNegativeInt("--top", opts.top));
12475
12526
  const truncated = entries.length < eligibleCount;
12476
12527
  if (opts.json === true) {
12477
12528
  out(displaySafeJson({ entries, truncated, totalCount: eligibleCount, total_tokens: result.total_tokens, total_lines: result.total_lines }));
@@ -12519,7 +12570,7 @@ function cmdBudget(patterns, opts) {
12519
12570
  if (opts.json === true) {
12520
12571
  out(displaySafeJson(result));
12521
12572
  } else {
12522
- const contextK = opts.context !== void 0 ? requirePositiveInt2("--context", opts.context) : Math.round(loadConfig().context.model_window_tokens / 1e3);
12573
+ const contextK = opts.context !== void 0 ? requirePositiveInt("--context", opts.context) : Math.round(loadConfig().context.model_window_tokens / 1e3);
12523
12574
  out(formatBudgetText(result, contextK));
12524
12575
  }
12525
12576
  }
@@ -12866,8 +12917,8 @@ async function cmdPdfLocate(file, pattern, opts) {
12866
12917
  const locateOpts = {
12867
12918
  ignoreCase: opts.ignoreCase === true
12868
12919
  };
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);
12920
+ if (opts.maxMatches !== void 0) locateOpts.maxMatches = requirePositiveInt("--max-matches", opts.maxMatches);
12921
+ if (opts.context !== void 0) locateOpts.context = requirePositiveInt("--context", opts.context);
12871
12922
  if (opts.pages !== void 0) locateOpts.pages = opts.pages;
12872
12923
  const { matches: matches2, truncated } = await runPdfLocate(file, pattern, locateOpts);
12873
12924
  const pages = matches2.map((m) => m.page);
@@ -12977,14 +13028,14 @@ async function cmdXlsxSheets(file, opts = {}) {
12977
13028
  recordXlsxStat("xlsx_sheets", file, text);
12978
13029
  }
12979
13030
  async function cmdXlsxHead(file, opts) {
12980
- const rows = opts.rows !== void 0 ? requireNonNegativeInt2("--rows", opts.rows) : 20;
13031
+ const rows = opts.rows !== void 0 ? requireNonNegativeInt("--rows", opts.rows) : 20;
12981
13032
  const columns = opts.columns ? opts.columns.split(",").map((c) => c.trim()).filter(Boolean) : void 0;
12982
13033
  const text = fenceFileText(await headSheet(file, opts.sheet, rows, columns));
12983
13034
  out(text);
12984
13035
  recordXlsxStat("xlsx_head", file, text);
12985
13036
  }
12986
13037
  async function cmdXlsxColumns(file, opts = {}) {
12987
- const maxRows = opts.head !== void 0 ? requireNonNegativeInt2("--head", opts.head) : 100;
13038
+ const maxRows = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 100;
12988
13039
  const result = await xlsxColumns(file, opts.sheet, maxRows);
12989
13040
  if (opts.json === true) {
12990
13041
  const text = displaySafeJson({
@@ -13012,7 +13063,7 @@ async function cmdXlsxQuery(file, opts) {
13012
13063
  const result = await querySheet(file, opts.sheet, {
13013
13064
  ...columns !== void 0 ? { columns } : {},
13014
13065
  ...wheres !== void 0 ? { wheres } : {},
13015
- ...opts.head !== void 0 ? { head: requireNonNegativeInt2("--head", opts.head) } : {}
13066
+ ...opts.head !== void 0 ? { head: requireNonNegativeInt("--head", opts.head) } : {}
13016
13067
  });
13017
13068
  if (opts.json === true) {
13018
13069
  const rowsJson = result.rows.map((r) => Object.fromEntries(result.header.map((h, i) => [h, fenceFileFieldIfMatched(r[i] ?? "")])));
@@ -13044,13 +13095,13 @@ async function cmdPptxOutline(file, opts) {
13044
13095
  recordDocStat("pptx_outline", file, text);
13045
13096
  }
13046
13097
  async function cmdPptxSlide(file, opts) {
13047
- const n = requireNonNegativeInt2("--slide", opts.slide);
13098
+ const n = requireNonNegativeInt("--slide", opts.slide);
13048
13099
  const text = fenceFileText(await pptxSlideText(file, n, opts.notes === true));
13049
13100
  out(text);
13050
13101
  recordDocStat("pptx_slide", file, text);
13051
13102
  }
13052
13103
  async function cmdPptxNotes(file, opts) {
13053
- const n = opts.slide !== void 0 ? requireNonNegativeInt2("--slide", opts.slide) : void 0;
13104
+ const n = opts.slide !== void 0 ? requireNonNegativeInt("--slide", opts.slide) : void 0;
13054
13105
  const text = await pptxNotesText(file, n);
13055
13106
  const printed = text.length > 0 ? fenceFileText(text) : "no speaker notes found";
13056
13107
  out(printed);
@@ -13179,7 +13230,7 @@ function cmdXmlOutline(file, opts) {
13179
13230
  process.exitCode = runXmlOutline({
13180
13231
  file,
13181
13232
  ...opts.json === true ? { json: true } : {},
13182
- ...depthStr !== void 0 ? { maxDepth: requireNonNegativeInt2("--max-depth", depthStr) } : {}
13233
+ ...depthStr !== void 0 ? { maxDepth: requireNonNegativeInt("--max-depth", depthStr) } : {}
13183
13234
  });
13184
13235
  }
13185
13236
  function cmdXmlQuery(file, pathOrOpts, optsMaybe) {
@@ -13990,13 +14041,6 @@ init_define_import_meta_env();
13990
14041
 
13991
14042
  // src/answer_router.ts
13992
14043
  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
14044
  function refusal(why, suggestion) {
14001
14045
  return `cannot answer deterministically: ${why}; try: ${suggestion}`;
14002
14046
  }
@@ -14128,11 +14172,11 @@ function subjectModeFor(intent) {
14128
14172
  function runAnswer(opts) {
14129
14173
  const question = opts.question.trim();
14130
14174
  if (question.length === 0) {
14131
- emitErr2(refusal("the question is empty", 'token-goat answer "who calls <symbol>"'));
14175
+ emitErr(refusal("the question is empty", 'token-goat answer "who calls <symbol>"'));
14132
14176
  return 1;
14133
14177
  }
14134
14178
  if (isJudgementQuestion(question)) {
14135
- emitErr2(
14179
+ emitErr(
14136
14180
  refusal(
14137
14181
  "that asks for judgement, intent, or runtime behaviour, which the index cannot answer -- it would need the code read and reasoned over",
14138
14182
  `token-goat semantic "${question}"`
@@ -14142,7 +14186,7 @@ function runAnswer(opts) {
14142
14186
  }
14143
14187
  const cls = classify(question);
14144
14188
  if (cls === null) {
14145
- emitErr2(
14189
+ emitErr(
14146
14190
  refusal(
14147
14191
  "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
14192
  `token-goat semantic "${question}"`
@@ -14152,7 +14196,7 @@ function runAnswer(opts) {
14152
14196
  }
14153
14197
  const resolved = resolveSubject(cls.subject, subjectModeFor(cls.intent));
14154
14198
  if (resolved === null) {
14155
- emitErr2(
14199
+ emitErr(
14156
14200
  refusal(
14157
14201
  `'${cls.subject}' is not an indexed symbol or file`,
14158
14202
  `token-goat semantic "${question}"`
@@ -14166,7 +14210,7 @@ function runAnswer(opts) {
14166
14210
  const more = resolved.candidates.length - shown.length;
14167
14211
  const first = shown[0] ?? "";
14168
14212
  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(
14213
+ emitErr(
14170
14214
  refusal(
14171
14215
  `'${cls.subject}' names ${resolved.candidates.length} files in this project (${shown.join(", ")}${more > 0 ? `, +${more} more` : ""})`,
14172
14216
  next
@@ -14175,7 +14219,7 @@ function runAnswer(opts) {
14175
14219
  return 1;
14176
14220
  }
14177
14221
  if (resolved.kind === "symbol-only") {
14178
- emitErr2(
14222
+ emitErr(
14179
14223
  refusal(
14180
14224
  `'${cls.subject}' is a symbol; exports/imports are file-level`,
14181
14225
  `token-goat ${cls.intent} ${toDisplayPath(rootDir, resolved.file)}`
@@ -14187,19 +14231,19 @@ function runAnswer(opts) {
14187
14231
  const file = resolved.kind === "symbol" ? resolved.file : resolved.path;
14188
14232
  const display = toDisplayPath(rootDir, file);
14189
14233
  if (cls.intent === "tests") {
14190
- emit2(`via: token-goat test-for ${display}`);
14234
+ emit(`via: token-goat test-for ${display}`);
14191
14235
  return runTestFor({ file });
14192
14236
  }
14193
14237
  if (cls.intent === "exports") {
14194
- emit2(`via: token-goat exports ${display}`);
14238
+ emit(`via: token-goat exports ${display}`);
14195
14239
  return runExports({ file });
14196
14240
  }
14197
- emit2(`via: token-goat imports ${display}`);
14241
+ emit(`via: token-goat imports ${display}`);
14198
14242
  return runImports({ file });
14199
14243
  }
14200
14244
  if (resolved.kind === "file") {
14201
14245
  const display = toDisplayPath(rootDir, resolved.path);
14202
- emitErr2(
14246
+ emitErr(
14203
14247
  refusal(
14204
14248
  `'${cls.subject}' is a file, and ${cls.intent === "where" ? "where" : cls.intent} needs a symbol`,
14205
14249
  `token-goat outline ${display}`
@@ -14208,16 +14252,16 @@ function runAnswer(opts) {
14208
14252
  return 1;
14209
14253
  }
14210
14254
  if (cls.intent === "callers") {
14211
- emit2(`via: token-goat callers ${displaySafeText(resolved.name)} --limit ${ANSWER_DELEGATE_LIMIT}`);
14255
+ emit(`via: token-goat callers ${displaySafeText(resolved.name)} --limit ${ANSWER_DELEGATE_LIMIT}`);
14212
14256
  return runCallers({ symbol: resolved.name, limit: ANSWER_DELEGATE_LIMIT });
14213
14257
  }
14214
14258
  if (cls.intent === "impact") {
14215
- emit2(`via: token-goat impact ${displaySafeText(resolved.name)} --top ${ANSWER_DELEGATE_LIMIT}`);
14259
+ emit(`via: token-goat impact ${displaySafeText(resolved.name)} --top ${ANSWER_DELEGATE_LIMIT}`);
14216
14260
  return runImpact({ symbol: resolved.name, top: ANSWER_DELEGATE_LIMIT });
14217
14261
  }
14218
- emit2(`via: token-goat symbol ${displaySafeText(resolved.name)} -p --exclude-vendored`);
14262
+ emit(`via: token-goat symbol ${displaySafeText(resolved.name)} -p --exclude-vendored`);
14219
14263
  const r = runSymbol({ name: resolved.name, projectRoot: rootDir, limit: ANSWER_DELEGATE_LIMIT, excludeVendored: true });
14220
- if (r.text.length > 0) emit2(r.text);
14264
+ if (r.text.length > 0) emit(r.text);
14221
14265
  return r.code;
14222
14266
  }
14223
14267
 
@@ -14225,13 +14269,6 @@ function runAnswer(opts) {
14225
14269
  init_define_import_meta_env();
14226
14270
  import * as path25 from "node:path";
14227
14271
  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
14272
  function guardedFilter(filter2) {
14236
14273
  const guarded = compileGuardedRegex(filter2);
14237
14274
  if (!guarded.ok) throw new Error(`--filter is not usable as a regular expression: ${guarded.reason}`);
@@ -14286,43 +14323,43 @@ function computeAffected(opts) {
14286
14323
  }
14287
14324
  function runAffected(opts) {
14288
14325
  if (opts.depth !== void 0 && opts.depth <= 0) {
14289
- emitErr3(`--depth must be a positive number, got: ${opts.depth}`);
14326
+ emitErr(`--depth must be a positive number, got: ${opts.depth}`);
14290
14327
  return 1;
14291
14328
  }
14292
14329
  if (opts.files.length === 0) {
14293
- emitErr3("No changed files given. Pass paths as arguments, or pipe them in with --stdin.");
14330
+ emitErr("No changed files given. Pass paths as arguments, or pipe them in with --stdin.");
14294
14331
  return 1;
14295
14332
  }
14296
14333
  if (opts.filter !== void 0) {
14297
14334
  const guarded = compileGuardedRegex(opts.filter);
14298
14335
  if (!guarded.ok) {
14299
- emitErr3(`--filter is not usable as a regular expression: ${guarded.reason}`);
14336
+ emitErr(`--filter is not usable as a regular expression: ${guarded.reason}`);
14300
14337
  return 1;
14301
14338
  }
14302
14339
  }
14303
14340
  const result = computeAffected(opts);
14304
14341
  if (opts.json === true) {
14305
- emit3(displaySafeJson(result));
14342
+ emit(displaySafeJson(result));
14306
14343
  return 0;
14307
14344
  }
14308
14345
  if (opts.quiet === true) {
14309
- for (const f of result.testFiles) emit3(f);
14346
+ for (const f of result.testFiles) emit(f);
14310
14347
  } else if (result.testFiles.length === 0) {
14311
14348
  const traced = opts.files.length - result.unknownSeeds.length;
14312
14349
  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.`);
14350
+ 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
14351
  } else {
14315
- emit3(`No test files import ${countNoun(traced, "changed file")} (within ${countNoun(result.depth, "hop")}).`);
14352
+ emit(`No test files import ${countNoun(traced, "changed file")} (within ${countNoun(result.depth, "hop")}).`);
14316
14353
  }
14317
14354
  } 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}`);
14355
+ emit(`${countNoun(result.testFiles.length, "affected test file")} (${countNoun(result.reachedCount, "file")} reached within ${countNoun(result.depth, "hop")}):`);
14356
+ for (const f of result.testFiles) emit(` ${f}`);
14320
14357
  }
14321
14358
  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(", ")}`);
14359
+ emitErr(`Not tracked in this project, so nothing was traced from ${result.unknownSeeds.length === 1 ? "it" : "them"}: ${result.unknownSeeds.join(", ")}`);
14323
14360
  }
14324
14361
  if (result.depthLimited) {
14325
- emitErr3(`Stopped at --depth ${result.depth} with ${countNoun(result.unexploredAtFrontier, "file")} still unexplored; more tests may be affected (raise --depth).`);
14362
+ emitErr(`Stopped at --depth ${result.depth} with ${countNoun(result.unexploredAtFrontier, "file")} still unexplored; more tests may be affected (raise --depth).`);
14326
14363
  }
14327
14364
  return 0;
14328
14365
  }
@@ -14924,6 +14961,69 @@ function cmdTrace(src, opts) {
14924
14961
  // src/text_logfold.ts
14925
14962
  init_define_import_meta_env();
14926
14963
  import * as fs31 from "fs";
14964
+
14965
+ // src/filters.ts
14966
+ init_define_import_meta_env();
14967
+ var DROP = () => null;
14968
+ var FILTERS = [
14969
+ {
14970
+ // git fetch/clone progress: "remote: Counting objects: 73% (8/11)", "Receiving objects: 100% (11/11), done.", "Resolving deltas: ...".
14971
+ name: "git-progress",
14972
+ pattern: /^(remote:\s+)?(Counting objects|Compressing objects|Receiving objects|Resolving deltas|Unpacking objects|Enumerating objects|Writing objects):/i,
14973
+ replacer: DROP
14974
+ },
14975
+ {
14976
+ // npm install summary chatter: "added 142 packages in 3s", "changed 5 packages", "audited 200 packages in 1s".
14977
+ name: "npm-summary",
14978
+ pattern: /^\s*(added|removed|changed|audited)\s+\d+\s+packages?\b/i,
14979
+ replacer: DROP
14980
+ },
14981
+ {
14982
+ // npm/yarn deprecation + funding noise that adds nothing actionable.
14983
+ name: "npm-funding",
14984
+ pattern: /^\s*\d+\s+packages? are looking for funding\b/i,
14985
+ replacer: DROP
14986
+ },
14987
+ {
14988
+ // 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.
14989
+ name: "pip-download",
14990
+ pattern: /^\s*(Downloading|Collecting|Using cached|Requirement already satisfied)\b/i,
14991
+ replacer: DROP
14992
+ },
14993
+ {
14994
+ // 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.
14995
+ name: "progress-bar",
14996
+ pattern: /[█▉▊▋▌▍▎▏▓▒░]{3,}/,
14997
+ replacer: DROP
14998
+ },
14999
+ {
15000
+ // Docker layer pull progress: "abc123: Pulling fs layer", "abc123: Download complete", "abc123: Pull complete".
15001
+ name: "docker-pull",
15002
+ pattern: /^[0-9a-f]{6,}:\s+(Pulling|Waiting|Downloading|Verifying|Extracting|Download complete|Pull complete|Already exists)\b/i,
15003
+ replacer: DROP
15004
+ },
15005
+ {
15006
+ // cargo build chatter: " Compiling foo v0.1.0", " Downloading crates ...".
15007
+ name: "cargo-progress",
15008
+ pattern: /^\s*(Compiling|Downloading|Downloaded|Updating|Fetching)\s+\S/,
15009
+ replacer: DROP
15010
+ },
15011
+ {
15012
+ // Webpack/bundler asset rows are voluminous; keep the build result lines (errors, warnings, "compiled") by only dropping plain asset size rows.
15013
+ name: "webpack-asset",
15014
+ pattern: /^\s*(asset|chunk)\s+\S+\s+[\d.]+\s*(KiB|MiB|bytes)\b/i,
15015
+ replacer: DROP
15016
+ },
15017
+ {
15018
+ // A binary-content marker line: any line carrying a NUL byte is treated as non-text and collapsed to a single readable marker.
15019
+ name: "binary-content",
15020
+ // eslint-disable-next-line no-control-regex -- intentionally matches the NUL control byte that signals binary content
15021
+ pattern: /\x00/,
15022
+ replacer: () => "[binary content elided by token-goat]"
15023
+ }
15024
+ ];
15025
+
15026
+ // src/text_logfold.ts
14927
15027
  var VOLATILE_SUBS = [
14928
15028
  { re: /\b\d{1,2}:\d{2}:\d{2}(?:\.\d+)?\b/g, placeholder: "[HH:MM:SS]" },
14929
15029
  { 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 +16203,7 @@ function registerAnalysisCommands(program2, guard) {
16103
16203
  ).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
16204
  program2.command("conflicts [path]").description(
16105
16205
  "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));
16206
+ ).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
16207
  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
16208
  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
16209
  (ref, opts) => runExit(
@@ -16235,7 +16335,7 @@ function renderCapabilities(caps) {
16235
16335
 
16236
16336
  // src/cli_session.ts
16237
16337
  init_define_import_meta_env();
16238
- import * as fs41 from "node:fs";
16338
+ import * as fs40 from "node:fs";
16239
16339
 
16240
16340
  // src/session_audit.ts
16241
16341
  init_define_import_meta_env();
@@ -16243,6 +16343,23 @@ import * as fs34 from "node:fs";
16243
16343
  import * as os16 from "node:os";
16244
16344
  import * as path31 from "node:path";
16245
16345
  import * as readline from "node:readline";
16346
+ var MODEL_VISIBLE_CATEGORIES = ["toolResults", "assistantText", "assistantThinking", "toolUseInputs", "attachments", "userTurns", "harnessMeta", "system"];
16347
+ function computeCalibration(estimated, measured) {
16348
+ let modelVisibleBytes = 0;
16349
+ let estimatedTokens = 0;
16350
+ for (const key of MODEL_VISIBLE_CATEGORIES) {
16351
+ modelVisibleBytes += estimated[key].bytes;
16352
+ estimatedTokens += estimated[key].estTokens;
16353
+ }
16354
+ const measuredFirstWriteTokens = measured.inputTokens + measured.cacheCreationTokens;
16355
+ return {
16356
+ modelVisibleBytes,
16357
+ estimatedTokens,
16358
+ measuredFirstWriteTokens,
16359
+ relativeError: measuredFirstWriteTokens === 0 ? 0 : (estimatedTokens - measuredFirstWriteTokens) / measuredFirstWriteTokens,
16360
+ bytesPerMeasuredToken: measuredFirstWriteTokens === 0 ? 0 : modelVisibleBytes / measuredFirstWriteTokens
16361
+ };
16362
+ }
16246
16363
  function emptyMeasured() {
16247
16364
  return { apiCalls: 0, inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };
16248
16365
  }
@@ -16317,8 +16434,12 @@ var DENY_TEMPLATES = [
16317
16434
  { kind: "doc_unchanged_deny", re: /is unchanged since last read\. Use `token-goat (?:section|read)/, tool: "Read" },
16318
16435
  { kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?Use `token-goat (?:section|read)/, tool: "Read" },
16319
16436
  { kind: "read_served_deny", re: /was already served in this session, byte for byte/, tool: "Read" },
16437
+ // 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.
16438
+ { kind: "range_reread_deny", re: /Lines \d+\.\.\d+ of [\s\S]*?was already read this session/, tool: "Read" },
16320
16439
  { kind: "markdown_already_read_deny", re: /Markdown file already read this session\. Use `token-goat section/, tool: "Read" },
16321
16440
  { kind: "read_count_deny", re: /(?:Read|Tried to read) this file \d+ times already/, tool: "Read" },
16441
+ // 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.
16442
+ { kind: "sequential_paging_deny", re: /Sequential line-range paging detected on /, tool: "Read" },
16322
16443
  { kind: "generic_reread_deny", re: /was already read this session \(\d+ read/, tool: "Read" },
16323
16444
  { kind: "large_file_deny", re: /is very large \(\d+(?:\.\d+)?KB\)\./, tool: "Read" },
16324
16445
  { 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 +16468,10 @@ var SHELL_READER_TOKEN_RE = /\b(?:sed|grep|cat|head|tail|awk|rg|less|nl|python3?
16347
16468
  function escapeRegExp2(s) {
16348
16469
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16349
16470
  }
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);
16471
+ function shellReadMatch(command, basename10) {
16472
+ if (basename10 === "" || !SHELL_READER_TOKEN_RE.test(command)) return "none";
16473
+ if (!command.includes(basename10)) return "none";
16474
+ const escaped = escapeRegExp2(basename10);
16354
16475
  const strict = new RegExp(`(?:^|["'\`/\\\\\\s])${escaped}(?:$|["'\`/\\\\\\s,;:)])`);
16355
16476
  return strict.test(command) ? "match" : "ambiguous";
16356
16477
  }
@@ -16390,7 +16511,7 @@ function deepStringBytes(value) {
16390
16511
  }
16391
16512
  return 0;
16392
16513
  }
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;
16514
+ 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
16515
  function toolResultText(content) {
16395
16516
  if (typeof content === "string") return content;
16396
16517
  if (Array.isArray(content)) {
@@ -16588,7 +16709,7 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16588
16709
  s.editErrorBaseline.totalEdits += 1;
16589
16710
  }
16590
16711
  if (openDenies.length > 0) {
16591
- const bashCommand = name === "Bash" && typeof input["command"] === "string" ? input["command"] : void 0;
16712
+ const bashCommand = name === "Bash" && typeof input["command"] === "string" ? input["command"].toLowerCase() : void 0;
16592
16713
  for (const o of openDenies) {
16593
16714
  if (o.toolCalls.length >= 10) continue;
16594
16715
  const isReadSamePath = readNorm !== void 0 && o.path !== "" && readNorm === o.path;
@@ -16639,13 +16760,13 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16639
16760
  if (name === "Read" || name === "Skill") {
16640
16761
  const denyTemplate = DENY_TEMPLATES.find((t) => t.tool === name && t.re.test(text));
16641
16762
  if (denyTemplate !== void 0) {
16642
- const path43 = readPathById.get(id) ?? "";
16643
- const lastSlash = path43.lastIndexOf("/");
16763
+ const path42 = readPathById.get(id) ?? "";
16764
+ const lastSlash = path42.lastIndexOf("/");
16644
16765
  openDenies.push({
16645
16766
  kind: denyTemplate.kind,
16646
16767
  withheldBytes: parseWithheldBytes(denyTemplate.kind, text),
16647
- path: path43,
16648
- basename: lastSlash === -1 ? path43 : path43.slice(lastSlash + 1),
16768
+ path: path42,
16769
+ basename: lastSlash === -1 ? path42 : path42.slice(lastSlash + 1),
16649
16770
  callIndexAtDeny: perCall.length,
16650
16771
  compactEpochAtOpen: compactEpoch,
16651
16772
  compactEpochAtWindow: null,
@@ -16946,6 +17067,7 @@ async function auditSessionCorpus(opts = {}) {
16946
17067
  system: emptyCategory(),
16947
17068
  otherLocal: emptyCategory()
16948
17069
  },
17070
+ calibration: { modelVisibleBytes: 0, estimatedTokens: 0, measuredFirstWriteTokens: 0, relativeError: 0, bytesPerMeasuredToken: 0 },
16949
17071
  tools: [],
16950
17072
  attachmentKinds: [],
16951
17073
  hookOutputs: [],
@@ -17006,6 +17128,7 @@ async function auditSessionCorpus(opts = {}) {
17006
17128
  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
17129
  summary.denyOutcomes = aggregateDenyOutcomes(denyRows);
17008
17130
  summary.editErrorBaseline.rate = summary.editErrorBaseline.totalEdits === 0 ? 0 : summary.editErrorBaseline.totalErrors / summary.editErrorBaseline.totalEdits;
17131
+ summary.calibration = computeCalibration(summary.estimated, summary.measured);
17009
17132
  summary.runtimeMs = Date.now() - started;
17010
17133
  return summary;
17011
17134
  }
@@ -17046,11 +17169,20 @@ function formatSessionAudit(s) {
17046
17169
  ["system lines", e.system],
17047
17170
  ["local bookkeeping (never sent)", e.otherLocal]
17048
17171
  ];
17049
- const modelVisibleBytes = rows.slice(0, 8).reduce((acc, [, c]) => acc + c.bytes, 0);
17172
+ const cal = s.calibration;
17173
+ const modelVisibleBytes = cal.modelVisibleBytes;
17050
17174
  for (const [label, cat] of rows.sort((a, b) => b[1].bytes - a[1].bytes)) {
17051
17175
  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
17176
  }
17053
17177
  lines.push("");
17178
+ lines.push("## Estimator calibration (the estimate above against the billed ledger above it)");
17179
+ lines.push(`Model-visible bytes: ${fmt(cal.modelVisibleBytes).padStart(18)}`);
17180
+ lines.push(`Estimated tokens (chars/3): ${fmt(cal.estimatedTokens).padStart(18)}`);
17181
+ 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)`);
17182
+ lines.push(`Estimator error: ${(cal.relativeError >= 0 ? "+" : "") + (cal.relativeError * 100).toFixed(1) + "%"} ${cal.relativeError >= 0 ? "over" : "under"} measured`);
17183
+ lines.push(`Implied bytes per token: ${cal.bytesPerMeasuredToken.toFixed(2)} (against the 3.0 the estimate divides by)`);
17184
+ 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.");
17185
+ lines.push("");
17054
17186
  lines.push("## Tool results by tool (estimated content size; calls = tool_use invocations)");
17055
17187
  for (const t of s.tools.slice(0, 25)) {
17056
17188
  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 +17753,8 @@ async function runMemoryCommand(opts = {}) {
17621
17753
 
17622
17754
  // src/cli_waste.ts
17623
17755
  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
17756
  import * as fs38 from "node:fs";
17630
17757
  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
17758
  function printCopilotReport(report) {
17761
17759
  const w = (text) => {
17762
17760
  process.stdout.write(text);
@@ -17955,9 +17953,50 @@ function printResidentContext(resident, w) {
17955
17953
  w(resident.compactionCount === 0 ? "\n" : ", each re-injecting the fixed preamble (CLAUDE.md, memory, skill and agent listings)\n");
17956
17954
  }
17957
17955
  async function runWasteCommand(opts = {}) {
17956
+ const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
17957
+ if (opts.transcript !== void 0) {
17958
+ const resolvedPath = path35.resolve(opts.transcript);
17959
+ if (!fs38.existsSync(resolvedPath)) {
17960
+ if (opts.copilot === true) {
17961
+ const detail = `Copilot session event log not found: ${resolvedPath}`;
17962
+ if (opts.json === true) {
17963
+ process.stdout.write(`${displaySafeJson({ error: detail }, 0)}
17964
+ `);
17965
+ } else {
17966
+ process.stdout.write("\n# token-goat waste (Copilot CLI)\n");
17967
+ process.stdout.write(`${detail}
17968
+ `);
17969
+ }
17970
+ process.exitCode = 1;
17971
+ return;
17972
+ }
17973
+ process.stderr.write(`token-goat: transcript not found: ${resolvedPath}
17974
+ `);
17975
+ process.exitCode = 1;
17976
+ return;
17977
+ }
17978
+ if (opts.copilot === true || isCopilotTranscript(resolvedPath)) {
17979
+ const copilotReport = buildCopilotWasteReport(resolvedPath);
17980
+ if (opts.json === true) {
17981
+ process.stdout.write(`${displaySafeJson(copilotReport, 0)}
17982
+ `);
17983
+ return;
17984
+ }
17985
+ printCopilotReport(copilotReport);
17986
+ return;
17987
+ }
17988
+ const report2 = await buildWasteReport(resolvedPath, opts.top !== void 0 ? { topN: opts.top } : {});
17989
+ if (opts.json === true) {
17990
+ process.stdout.write(`${displaySafeJson(report2, 0)}
17991
+ `);
17992
+ return;
17993
+ }
17994
+ printReport2(report2);
17995
+ return;
17996
+ }
17958
17997
  if (opts.copilot === true) {
17959
- const eventsPath = opts.transcript !== void 0 ? path36.resolve(opts.transcript) : findLatestCopilotSession();
17960
- if (eventsPath === null || !fs39.existsSync(eventsPath)) {
17998
+ const eventsPath = opts.project !== void 0 ? findLatestCopilotSession({ projectRoot }) : findLatestCopilotSession({ projectRoot }) ?? findLatestCopilotSession();
17999
+ if (eventsPath === null || !fs38.existsSync(eventsPath)) {
17961
18000
  const detail = eventsPath === null ? "no Copilot CLI session found under <copilot-home>/session-state" : `Copilot session event log not found: ${eventsPath}`;
17962
18001
  if (opts.json === true) {
17963
18002
  process.stdout.write(`${displaySafeJson({ error: detail }, 0)}
@@ -17979,9 +18018,8 @@ async function runWasteCommand(opts = {}) {
17979
18018
  printCopilotReport(copilotReport);
17980
18019
  return;
17981
18020
  }
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) {
18021
+ const detected = findProjectSession(projectRoot);
18022
+ if (detected === null) {
17985
18023
  if (opts.json === true) {
17986
18024
  process.stdout.write(`${displaySafeJson({ error: "no session transcript found", project: projectRoot }, 0)}
17987
18025
  `);
@@ -17994,13 +18032,17 @@ async function runWasteCommand(opts = {}) {
17994
18032
  process.exitCode = 1;
17995
18033
  return;
17996
18034
  }
17997
- if (!fs39.existsSync(transcriptPath)) {
17998
- process.stderr.write(`token-goat: transcript not found: ${transcriptPath}
18035
+ if (detected.kind === "copilot") {
18036
+ const copilotReport = buildCopilotWasteReport(detected.path);
18037
+ if (opts.json === true) {
18038
+ process.stdout.write(`${displaySafeJson(copilotReport, 0)}
17999
18039
  `);
18000
- process.exitCode = 1;
18040
+ return;
18041
+ }
18042
+ printCopilotReport(copilotReport);
18001
18043
  return;
18002
18044
  }
18003
- const report = await buildWasteReport(transcriptPath, opts.top !== void 0 ? { topN: opts.top } : {});
18045
+ const report = await buildWasteReport(detected.path, opts.top !== void 0 ? { topN: opts.top } : {});
18004
18046
  if (opts.json === true) {
18005
18047
  process.stdout.write(`${displaySafeJson(report, 0)}
18006
18048
  `);
@@ -18011,13 +18053,13 @@ async function runWasteCommand(opts = {}) {
18011
18053
 
18012
18054
  // src/cli_mcp_audit.ts
18013
18055
  init_define_import_meta_env();
18014
- import * as fs40 from "node:fs";
18056
+ import * as fs39 from "node:fs";
18015
18057
  import * as os18 from "node:os";
18016
- import * as path37 from "node:path";
18058
+ import * as path36 from "node:path";
18017
18059
  function readMcpJsonFile(configPath2) {
18018
18060
  try {
18019
- if (!fs40.existsSync(configPath2)) return null;
18020
- const content = fs40.readFileSync(configPath2, "utf-8");
18061
+ if (!fs39.existsSync(configPath2)) return null;
18062
+ const content = fs39.readFileSync(configPath2, "utf-8");
18021
18063
  const parsed = JSON.parse(content);
18022
18064
  const servers = parsed && typeof parsed === "object" ? parsed.mcpServers ?? ("servers" in parsed ? null : parsed) : null;
18023
18065
  return servers && typeof servers === "object" ? servers : null;
@@ -18033,8 +18075,8 @@ function driveLetterCaseVariants(p) {
18033
18075
  }
18034
18076
  function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
18035
18077
  try {
18036
- if (!fs40.existsSync(claudeJsonPath)) return null;
18037
- const parsed = JSON.parse(fs40.readFileSync(claudeJsonPath, "utf-8"));
18078
+ if (!fs39.existsSync(claudeJsonPath)) return null;
18079
+ const parsed = JSON.parse(fs39.readFileSync(claudeJsonPath, "utf-8"));
18038
18080
  const projects = parsed && typeof parsed === "object" ? parsed.projects : null;
18039
18081
  if (!projects || typeof projects !== "object") return null;
18040
18082
  const slashForms = [projectRoot, projectRoot.replace(/\\/g, "/"), projectRoot.replace(/\//g, "\\")];
@@ -18052,9 +18094,9 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
18052
18094
  }
18053
18095
  }
18054
18096
  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");
18097
+ const mcpJsonPath = path36.join(projectRoot, ".mcp.json");
18098
+ const claudeJsonPath = path36.join(home, ".claude.json");
18099
+ const copilotJsonPath = path36.join(home, ".copilot", "mcp-config.json");
18058
18100
  const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
18059
18101
  const fromMcpJson = readMcpJsonFile(mcpJsonPath);
18060
18102
  if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
@@ -18247,7 +18289,7 @@ async function runStatuslineCommand(opts = {}) {
18247
18289
  return;
18248
18290
  }
18249
18291
  const line = renderStatusline(data);
18250
- process.stdout.write(`${colorStdout() ? line : stripAnsi(line)}
18292
+ process.stdout.write(`${colorStdout() ? line : stripAnsiEscapes(line)}
18251
18293
  `);
18252
18294
  }
18253
18295
 
@@ -18276,7 +18318,7 @@ function printSummary(rows) {
18276
18318
  const w = (text) => {
18277
18319
  process.stdout.write(text);
18278
18320
  };
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");
18321
+ 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
18322
  for (const row of rows) {
18281
18323
  w(
18282
18324
  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 +18330,7 @@ function printTotals(totals) {
18288
18330
  const legacyNote = totals.legacyEmissions > 0 ? ` (excludes ${totals.legacyEmissions} legacy emission(s) recorded before spend tracking)` : "";
18289
18331
  process.stdout.write(
18290
18332
  `
18291
- TOTAL saved=${totals.savedBytes} (all-time, every hint kind) spent=${spent} (hint_emissions ledger only)${legacyNote}
18333
+ TOTAL saved-bytes=${totals.savedBytes} (all-time, every hint kind) spent-bytes=${spent} (hint_emissions ledger only)${legacyNote}
18292
18334
  `
18293
18335
  );
18294
18336
  }
@@ -18370,10 +18412,18 @@ function cmdWaste(opts = {}) {
18370
18412
  ...opts.project !== void 0 ? { project: opts.project } : {},
18371
18413
  ...opts.transcript !== void 0 ? { transcript: opts.transcript } : {},
18372
18414
  ...opts.json === true ? { json: true } : {},
18373
- ...opts.top !== void 0 ? { top: requireNonNegativeInt2("--top", opts.top) } : {},
18415
+ ...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
18374
18416
  ...opts.copilot === true ? { copilot: true } : {}
18375
18417
  });
18376
18418
  }
18419
+ async function cmdAudit(opts = {}) {
18420
+ const { runAuditCommand } = await import("./token-goat-chunk-DYUBZZNP.mjs");
18421
+ return runAuditCommand({
18422
+ ...opts.project !== void 0 ? { project: opts.project } : {},
18423
+ ...opts.transcript !== void 0 ? { transcript: opts.transcript } : {},
18424
+ ...opts.json === true ? { json: true } : {}
18425
+ });
18426
+ }
18377
18427
  async function cmdSessionAudit(opts = {}) {
18378
18428
  let summary;
18379
18429
  try {
@@ -18400,7 +18450,7 @@ ${formatSessionOutline(turns)}`;
18400
18450
  }
18401
18451
  function sessionTranscriptSize(transcriptPath) {
18402
18452
  try {
18403
- return fs41.statSync(transcriptPath).size;
18453
+ return fs40.statSync(transcriptPath).size;
18404
18454
  } catch {
18405
18455
  return 0;
18406
18456
  }
@@ -18436,7 +18486,7 @@ function cmdRecall(query, opts = {}) {
18436
18486
  }
18437
18487
  runRecallCommand(query, {
18438
18488
  ...type !== void 0 ? { type } : {},
18439
- ...opts.limit !== void 0 ? { limit: requireNonNegativeInt2("--limit", opts.limit) } : {},
18489
+ ...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
18440
18490
  ...opts.json === true ? { json: true } : {}
18441
18491
  });
18442
18492
  }
@@ -18460,14 +18510,14 @@ function cmdHintStats(opts = {}) {
18460
18510
 
18461
18511
  // src/cli_skills.ts
18462
18512
  init_define_import_meta_env();
18463
- import * as fs42 from "node:fs";
18464
- import * as path38 from "node:path";
18513
+ import * as fs41 from "node:fs";
18514
+ import * as path37 from "node:path";
18465
18515
  async function cmdSkillBody(name, opts) {
18466
18516
  const filePath = await getSkillFilePath(name);
18467
18517
  if (filePath === null) {
18468
18518
  throw new CliError(`skill '${name}' not found`);
18469
18519
  }
18470
- const body = decodeSource(fs42.readFileSync(filePath));
18520
+ const body = decodeSource(fs41.readFileSync(filePath));
18471
18521
  if (opts.compact === true) {
18472
18522
  const emitted = extractCompactFromMarker(body) ?? body;
18473
18523
  out(emitted);
@@ -18494,7 +18544,7 @@ async function cmdSkillCompact(name, opts) {
18494
18544
  }
18495
18545
  let body2;
18496
18546
  try {
18497
- body2 = decodeSource(fs42.readFileSync(filePath));
18547
+ body2 = decodeSource(fs41.readFileSync(filePath));
18498
18548
  } catch {
18499
18549
  unresolvable++;
18500
18550
  continue;
@@ -18528,19 +18578,19 @@ async function cmdSkillCompact(name, opts) {
18528
18578
  if (!opts.path.trim()) {
18529
18579
  throw new CliError("--path cannot be empty");
18530
18580
  }
18531
- if (!fs42.existsSync(opts.path)) {
18581
+ if (!fs41.existsSync(opts.path)) {
18532
18582
  throw new CliError(`skill file not found: ${opts.path}`);
18533
18583
  }
18534
18584
  try {
18535
- body = fs42.readFileSync(opts.path, "utf-8");
18585
+ body = fs41.readFileSync(opts.path, "utf-8");
18536
18586
  } catch (e) {
18537
18587
  if (e.code === "ENOENT") {
18538
18588
  throw new CliError(`skill file not found: ${opts.path}`);
18539
18589
  }
18540
18590
  throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
18541
18591
  }
18542
- cacheName = name ?? path38.basename(path38.dirname(path38.resolve(opts.path)));
18543
- sourcePath = path38.resolve(opts.path);
18592
+ cacheName = name ?? path37.basename(path37.dirname(path37.resolve(opts.path)));
18593
+ sourcePath = path37.resolve(opts.path);
18544
18594
  } else {
18545
18595
  if (name === void 0 || !name.trim()) {
18546
18596
  throw new CliError("skill-compact requires a <name> or --path <file>");
@@ -18549,7 +18599,7 @@ async function cmdSkillCompact(name, opts) {
18549
18599
  if (filePath === null) {
18550
18600
  throw new CliError(`skill '${name}' not found`);
18551
18601
  }
18552
- body = fs42.readFileSync(filePath, "utf-8");
18602
+ body = fs41.readFileSync(filePath, "utf-8");
18553
18603
  cacheName = name;
18554
18604
  sourcePath = filePath;
18555
18605
  }
@@ -18681,8 +18731,8 @@ async function cmdSkillDiff(name) {
18681
18731
  }
18682
18732
  const newer = versions[0];
18683
18733
  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);
18734
+ const newerBody = await fs41.promises.readFile(path37.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
18735
+ const olderBody = await fs41.promises.readFile(path37.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
18686
18736
  if (newerBody === null || olderBody === null) {
18687
18737
  out(`a cached version of '${name}' was evicted while diffing -- try again`);
18688
18738
  return;
@@ -18711,7 +18761,7 @@ async function cmdSkillSection(nameHeading, headingArg) {
18711
18761
  if (!filePath) {
18712
18762
  throw new CliError(`skill '${skillName}' not found`);
18713
18763
  }
18714
- const body = decodeSource(fs42.readFileSync(filePath));
18764
+ const body = decodeSource(fs41.readFileSync(filePath));
18715
18765
  const extracted = extractNamedSection(body, heading);
18716
18766
  if (!extracted) {
18717
18767
  const allHeadings = listSections(filePath);
@@ -18732,94 +18782,12 @@ async function cmdSkillSection(nameHeading, headingArg) {
18732
18782
  // src/cache_session_commands.ts
18733
18783
  init_define_import_meta_env();
18734
18784
 
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
18785
  // src/cli_stats.ts
18818
18786
  init_define_import_meta_env();
18819
- import * as fs43 from "node:fs";
18820
- import * as path39 from "node:path";
18787
+ import * as fs42 from "node:fs";
18788
+ import * as path38 from "node:path";
18821
18789
  function writeRaw(text) {
18822
- const payload = colorStdout() ? text : stripAnsi(text);
18790
+ const payload = colorStdout() ? text : stripAnsiEscapes(text);
18823
18791
  process.stdout.write(ensureNewline(payload));
18824
18792
  }
18825
18793
  function rankByReadCount(entries, topN) {
@@ -18829,8 +18797,8 @@ function formatTopFiles(ranked) {
18829
18797
  if (ranked.length === 0) return "";
18830
18798
  const lines = ["Top files this session:"];
18831
18799
  for (const { path: filePath, count } of ranked) {
18832
- const basename11 = path39.basename(filePath);
18833
- lines.push(` ${count.toString().padStart(3)}x ${basename11} (${filePath})`);
18800
+ const basename10 = path38.basename(filePath);
18801
+ lines.push(` ${count.toString().padStart(3)}x ${basename10} (${filePath})`);
18834
18802
  }
18835
18803
  return lines.join("\n");
18836
18804
  }
@@ -18847,11 +18815,11 @@ function renderTopSessionFiles(topN = 5) {
18847
18815
  function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
18848
18816
  try {
18849
18817
  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);
18818
+ if (!fs42.existsSync(dir)) return "";
18819
+ 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
18820
  for (const { name } of files) {
18853
18821
  try {
18854
- const raw = fs43.readFileSync(path39.join(dir, name), "utf-8");
18822
+ const raw = fs42.readFileSync(path38.join(dir, name), "utf-8");
18855
18823
  const data = JSON.parse(raw);
18856
18824
  const filesList = data["files"];
18857
18825
  if (!Array.isArray(filesList)) continue;
@@ -18943,13 +18911,10 @@ function runStats(opts = {}) {
18943
18911
  }
18944
18912
 
18945
18913
  // src/cache_session_commands.ts
18946
- function emitErr4(text) {
18947
- process.stderr.write(ensureNewline(text));
18948
- }
18949
18914
  function capAndNote(rows, limit) {
18950
18915
  const shown = rows.slice(0, limit);
18951
18916
  if (shown.length < rows.length) {
18952
- emitErr4(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
18917
+ emitErr(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
18953
18918
  }
18954
18919
  return shown;
18955
18920
  }
@@ -18959,11 +18924,11 @@ function parseLimitOpt(cmdName, limitStr, dflt = 30) {
18959
18924
  try {
18960
18925
  n = requireNonNegativeStrictInt("--limit", limitStr);
18961
18926
  } catch (e) {
18962
- emitErr4(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18927
+ emitErr(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18963
18928
  throw new Error(`invalid --limit: ${limitStr}`, { cause: e });
18964
18929
  }
18965
18930
  if (n === 0) {
18966
- emitErr4(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18931
+ emitErr(`${cmdName}: --limit must be a positive number, got: "${limitStr}"`);
18967
18932
  throw new Error(`invalid --limit: ${limitStr}`);
18968
18933
  }
18969
18934
  return n;
@@ -19201,11 +19166,17 @@ function cmdCompactHint(opts) {
19201
19166
  const cache = sessionId !== null ? loadSessionCache(sessionId) : null;
19202
19167
  const transcriptPath = sessionId !== null ? resolveSessionTranscript(sessionId) : null;
19203
19168
  const pressure = getContextPressure(cache ?? void 0, transcriptPath ?? void 0);
19204
- const [manifest, eventCount] = sessionId !== null ? buildManifestWithCount(sessionId) : ["", 0];
19169
+ let manifest = "";
19170
+ let events = 0;
19171
+ if (sessionId !== null && cache !== null) {
19172
+ loadSessionState(sessionId);
19173
+ manifest = buildManifest(sessionId, process.cwd(), transcriptPath ?? void 0);
19174
+ events = eventCount(cache);
19175
+ }
19205
19176
  const manifestTokens = estimateTokens(manifest);
19206
19177
  const pct3 = (pressure.fillFraction * 100).toFixed(1);
19207
19178
  if (opts.json === true) {
19208
- const out2 = { tier: pressure.tier, fillFraction: pressure.fillFraction, pct: Number(pct3), manifestTokens, eventCount };
19179
+ const out2 = { tier: pressure.tier, fillFraction: pressure.fillFraction, pct: Number(pct3), manifestTokens, eventCount: events };
19209
19180
  if (sessionId !== null) out2["sessionId"] = sessionId;
19210
19181
  if (opts.trigger !== void 0) out2["trigger"] = opts.trigger;
19211
19182
  process.stdout.write(displaySafeJson(out2) + "\n");
@@ -19215,7 +19186,7 @@ function cmdCompactHint(opts) {
19215
19186
  `);
19216
19187
  if (sessionId !== null) process.stdout.write(`Session: ${sessionId}
19217
19188
  `);
19218
- process.stdout.write(`Manifest: ${manifestTokens} tokens, ${countNoun(eventCount, "event")}
19189
+ process.stdout.write(`Manifest: ${manifestTokens} tokens, ${countNoun(events, "event")}
19219
19190
  `);
19220
19191
  if (opts.trigger === "auto") {
19221
19192
  const remaining = Math.max(0, Math.round((1 - pressure.fillFraction) * CONTEXT_AUTOCOMPACT_TOKENS));
@@ -19318,6 +19289,7 @@ function registerSessionCommands(program2, guard) {
19318
19289
  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
19290
  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
19291
  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));
19292
+ 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
19293
  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
19294
  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
19295
  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 +19404,15 @@ var BRIDGE_CAPABILITY_MATRIX = [
19432
19404
  harness: "codex",
19433
19405
  label: "Codex CLI",
19434
19406
  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.",
19407
+ verification: "dogfooded",
19408
+ 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
19409
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "user_prompt_submit", "subagent_stop"]),
19438
19410
  reasons: [
19439
19411
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
19440
- { events: ["post_compact"], reason: NO_POST_COMPACT_EVENT_REASON },
19441
19412
  { events: ["notification", "stop"], reason: NO_SERVER_HANDLER_REASON },
19442
19413
  {
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"
19414
+ events: ["post_compact", "session_start"],
19415
+ 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
19416
  }
19446
19417
  ]
19447
19418
  },
@@ -19582,7 +19553,7 @@ var BRIDGE_CAPABILITY_MATRIX = [
19582
19553
  reasons: [
19583
19554
  {
19584
19555
  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"
19556
+ 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
19557
  },
19587
19558
  { events: ["post_compact"], reason: NO_POST_COMPACT_EVENT_REASON },
19588
19559
  { events: ["notification", "stop"], reason: NO_SERVER_HANDLER_REASON }
@@ -19735,25 +19706,25 @@ function installVerificationNotice(harness) {
19735
19706
 
19736
19707
  // src/purge.ts
19737
19708
  init_define_import_meta_env();
19738
- import * as fs44 from "node:fs";
19739
- import * as path40 from "node:path";
19709
+ import * as fs43 from "node:fs";
19710
+ import * as path39 from "node:path";
19740
19711
  function directorySize(root) {
19741
19712
  let total = 0;
19742
19713
  let entries;
19743
19714
  try {
19744
- entries = fs44.readdirSync(root, { withFileTypes: true });
19715
+ entries = fs43.readdirSync(root, { withFileTypes: true });
19745
19716
  } catch {
19746
19717
  return 0;
19747
19718
  }
19748
19719
  for (const entry of entries) {
19749
- const full = path40.join(root, entry.name);
19720
+ const full = path39.join(root, entry.name);
19750
19721
  if (entry.isDirectory()) {
19751
19722
  total += directorySize(full);
19752
19723
  continue;
19753
19724
  }
19754
19725
  if (!entry.isFile()) continue;
19755
19726
  try {
19756
- total += fs44.statSync(full).size;
19727
+ total += fs43.statSync(full).size;
19757
19728
  } catch {
19758
19729
  }
19759
19730
  }
@@ -19772,7 +19743,7 @@ function purgeDataDirectories() {
19772
19743
  }
19773
19744
  const bytes = directorySize(root);
19774
19745
  try {
19775
- fs44.rmSync(root, { recursive: true, force: true });
19746
+ fs43.rmSync(root, { recursive: true, force: true });
19776
19747
  result.removed.push({ path: root, bytes });
19777
19748
  } catch (e) {
19778
19749
  result.failed.push({ path: root, reason: e instanceof Error ? e.message : String(e) });
@@ -19919,8 +19890,8 @@ async function getSectionContent(fileId, heading, opts = {}) {
19919
19890
 
19920
19891
  // src/cli_bench.ts
19921
19892
  init_define_import_meta_env();
19922
- import * as fs45 from "node:fs";
19923
- import * as path41 from "node:path";
19893
+ import * as fs44 from "node:fs";
19894
+ import * as path40 from "node:path";
19924
19895
  var ControlFilter = class extends ToolFilter {
19925
19896
  constructor(name, transform) {
19926
19897
  super();
@@ -19941,13 +19912,13 @@ var CONTROL_FILTERS = {
19941
19912
  };
19942
19913
  var MAX_BENCH_FILE_BYTES = 5 * 1024 * 1024;
19943
19914
  function readCaseFile(id, filePath, what) {
19944
- const bytes = fs45.statSync(filePath).size;
19915
+ const bytes = fs44.statSync(filePath).size;
19945
19916
  if (bytes > MAX_BENCH_FILE_BYTES) {
19946
19917
  throw new Error(
19947
19918
  `bench case ${id}: ${what} is ${bytes} bytes, over the ${MAX_BENCH_FILE_BYTES}-byte limit for a corpus file (${filePath})`
19948
19919
  );
19949
19920
  }
19950
- return fs45.readFileSync(filePath, "utf8");
19921
+ return fs44.readFileSync(filePath, "utf8");
19951
19922
  }
19952
19923
  function parseCase(id, metaPath, outputPath) {
19953
19924
  const metaText = readCaseFile(id, metaPath, "metadata");
@@ -19982,7 +19953,7 @@ function parseCase(id, metaPath, outputPath) {
19982
19953
  function loadCorpus(dir) {
19983
19954
  let entries;
19984
19955
  try {
19985
- entries = fs45.readdirSync(dir);
19956
+ entries = fs44.readdirSync(dir);
19986
19957
  } catch {
19987
19958
  throw new Error(`bench corpus not found: ${dir}`);
19988
19959
  }
@@ -19990,9 +19961,9 @@ function loadCorpus(dir) {
19990
19961
  for (const entry of entries.sort()) {
19991
19962
  if (!entry.endsWith(".json")) continue;
19992
19963
  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));
19964
+ const outputPath = path40.join(dir, `${id}.txt`);
19965
+ if (!fs44.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
19966
+ cases.push(parseCase(id, path40.join(dir, entry), outputPath));
19996
19967
  }
19997
19968
  if (cases.length === 0) throw new Error(`bench corpus is empty: ${dir}`);
19998
19969
  return cases;
@@ -20112,10 +20083,10 @@ function appendTsv(file, report, commit) {
20112
20083
  report.mustKeepTotal,
20113
20084
  report.coveredFilters
20114
20085
  ].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");
20086
+ const dir = path40.dirname(path40.resolve(file));
20087
+ fs44.mkdirSync(dir, { recursive: true });
20088
+ const header = fs44.existsSync(file) ? "" : TSV_HEADER;
20089
+ fs44.appendFileSync(file, header + row + "\n", "utf8");
20119
20090
  }
20120
20091
  function currentCommit(cwd) {
20121
20092
  const opts = cwd === void 0 ? {} : { cwd };
@@ -20144,7 +20115,7 @@ function runBenchCommand(opts) {
20144
20115
  var CliError = class extends Error {
20145
20116
  };
20146
20117
  function out(text) {
20147
- const payload = colorStdout() ? text : stripAnsi(text);
20118
+ const payload = colorStdout() ? text : stripAnsiEscapes(text);
20148
20119
  process.stdout.write(ensureNewline(payload));
20149
20120
  }
20150
20121
  function err(text) {
@@ -20155,10 +20126,10 @@ function readBoundedText(text, file) {
20155
20126
  if (text === void 0 && file === void 0) throw new CliError("provide text or --file");
20156
20127
  let value;
20157
20128
  if (file !== void 0) {
20158
- if (fs46.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
20129
+ if (fs45.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
20159
20130
  throw new CliError(`file exceeds the ${CONTENT_MAX_INPUT_CHARS}-byte safety limit`);
20160
20131
  }
20161
- value = fs46.readFileSync(file, "utf8");
20132
+ value = fs45.readFileSync(file, "utf8");
20162
20133
  } else {
20163
20134
  if (text === void 0) throw new CliError("provide text or --file");
20164
20135
  value = text;
@@ -20203,35 +20174,11 @@ function cmdHandoffResolve(name, opts) {
20203
20174
  if (result === null) throw new CliError(`no local handoff named "${name}" in this project`);
20204
20175
  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
20176
  }
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
20177
  async function cmdSemantic(query, opts) {
20231
20178
  if (!query && !opts.preflight && !opts.warm) {
20232
20179
  throw new CliError("missing required argument: query");
20233
20180
  }
20234
- const limit = opts.limit !== void 0 ? requireNonNegativeInt2("--limit", opts.limit) : 20;
20181
+ const limit = opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20;
20235
20182
  const { text, code } = await runSemantic(query ?? "", {
20236
20183
  limit,
20237
20184
  ...opts.json === true ? { json: true } : {},
@@ -20268,7 +20215,7 @@ async function cmdIndex(pathArg, opts = {}) {
20268
20215
  const ixCfg = loadConfig().indexing;
20269
20216
  if ((loadConfig().indexing?.embeddings_enabled ?? true) && embeddingsDepsAvailable(getDb(dbPath))) {
20270
20217
  ensureEmbeddingProvenance(getDb(dbPath));
20271
- pruneUnembeddableChunks(getDb(dbPath), ixCfg.max_chunks_per_file, deleteFileEmbeddings);
20218
+ pruneUnembeddableChunks(getDb(dbPath), ixCfg.max_chunks_per_file, deleteFileEmbeddings, { asset: assetEmbedSha, maxChunks: maxChunksEmbedSha });
20272
20219
  }
20273
20220
  let indexed = 0;
20274
20221
  let failed = 0;
@@ -20305,7 +20252,8 @@ async function cmdIndex(pathArg, opts = {}) {
20305
20252
  continue;
20306
20253
  }
20307
20254
  const sha = fingerprintFile(key);
20308
- if (sha === null && !fs46.existsSync(key)) continue;
20255
+ if (sha === null && !fs45.existsSync(key)) continue;
20256
+ recordKnownRootThrottled(key, dataDir(), dbPath);
20309
20257
  const entry = sha !== null ? getFileEntry(key, dbPath) : null;
20310
20258
  const spellingStale = entry !== null && indexedPathSpellingIsStale(entry.filePath, key);
20311
20259
  const parseUnchanged = !force && !spellingStale && sha !== null && entry?.sha === sha && entry.parserSha === parserFingerprintForLanguage(entry.language);
@@ -20341,7 +20289,7 @@ async function cmdIndex(pathArg, opts = {}) {
20341
20289
  }
20342
20290
  continue;
20343
20291
  }
20344
- if (!fs46.existsSync(key)) continue;
20292
+ if (!fs45.existsSync(key)) continue;
20345
20293
  }
20346
20294
  const embedFresh = parseUnchanged ? embedUnchanged : embedFreshFor(getFileEntry(key, dbPath)?.embedSha);
20347
20295
  if (!embedFresh) {
@@ -20402,7 +20350,7 @@ async function cmdMcpServe() {
20402
20350
  let StdioServerTransport;
20403
20351
  try {
20404
20352
  ;
20405
- ({ createMcpServer } = await import("./token-goat-chunk-DA6TIDLI.mjs"));
20353
+ ({ createMcpServer } = await import("./token-goat-chunk-3OGZOFLS.mjs"));
20406
20354
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
20407
20355
  } catch (err2) {
20408
20356
  process.stderr.write(
@@ -20427,11 +20375,11 @@ async function cmdHook(event, opts) {
20427
20375
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
20428
20376
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
20429
20377
  }
20430
- const { relay } = await import("./token-goat-chunk-NYFYIRG3.mjs");
20378
+ const { relay } = await import("./token-goat-chunk-G6SYQQO7.mjs");
20431
20379
  await relay(event);
20432
20380
  }
20433
20381
  function projectHooksCommitNote(pathFiles, hooksConfigPath) {
20434
- const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path42.join(path42.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
20382
+ const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path41.join(path41.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
20435
20383
  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
20384
  }
20437
20385
  function visualStudioManualSteps(scope) {
@@ -20464,7 +20412,7 @@ async function cmdInstall(opts) {
20464
20412
  if (opts.project === true && opts.user === true) {
20465
20413
  throw new Error("install takes either -p/--project or --user, not both.");
20466
20414
  }
20467
- await import("./token-goat-chunk-NYFYIRG3.mjs");
20415
+ await import("./token-goat-chunk-G6SYQQO7.mjs");
20468
20416
  const scope = opts.project === true ? "project" : "user";
20469
20417
  if (wantsClaudeCodeBase(opts)) {
20470
20418
  const result = installHooks(scope);
@@ -20610,16 +20558,16 @@ async function cmdInstall(opts) {
20610
20558
  );
20611
20559
  }
20612
20560
  try {
20613
- const skillDir2 = path42.join(claudeConfigDir(), "skills");
20614
- if (fs46.existsSync(skillDir2)) {
20615
- const entries = fs46.readdirSync(skillDir2, { withFileTypes: true });
20561
+ const skillDir2 = path41.join(claudeConfigDir(), "skills");
20562
+ if (fs45.existsSync(skillDir2)) {
20563
+ const entries = fs45.readdirSync(skillDir2, { withFileTypes: true });
20616
20564
  const skillNames = [];
20617
20565
  const sessionId = getSessionId();
20618
20566
  for (const entry of entries) {
20619
20567
  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");
20568
+ const skillFile = path41.join(skillDir2, entry.name, "SKILL.md");
20569
+ if (fs45.existsSync(skillFile)) {
20570
+ const body = fs45.readFileSync(skillFile, "utf-8");
20623
20571
  const compact = extractCompactFromMarker(body);
20624
20572
  if (compact === null) continue;
20625
20573
  const sourceSha = contentHash(body);
@@ -20630,9 +20578,9 @@ async function cmdInstall(opts) {
20630
20578
  if (skillNames.length > 0) {
20631
20579
  const dir = skillOutputsDir();
20632
20580
  ensureDirSync(dir);
20633
- const pregenPath = path42.join(dir, "pregen.json");
20581
+ const pregenPath = path41.join(dir, "pregen.json");
20634
20582
  const pregenData = { ts: Date.now(), names: skillNames };
20635
- await fs46.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
20583
+ await fs45.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
20636
20584
  out(`Pre-generated ${skillNames.length} skill compacts.`);
20637
20585
  }
20638
20586
  }
@@ -20772,7 +20720,7 @@ function cmdWorkerStatus() {
20772
20720
  out(isWorkerRunning() ? "Worker is running." : "Worker is not running.");
20773
20721
  }
20774
20722
  function cmdStats(opts = {}) {
20775
- const windowDays = opts.windowDays !== void 0 ? requireNonNegativeInt2("--window-days", opts.windowDays) : 30;
20723
+ const windowDays = opts.windowDays !== void 0 ? requireNonNegativeInt("--window-days", opts.windowDays) : 30;
20776
20724
  const statsOpts = {
20777
20725
  json: opts.json === true,
20778
20726
  windowDays,
@@ -20812,7 +20760,7 @@ async function cmdDoctor(opts) {
20812
20760
  }
20813
20761
  }
20814
20762
  function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTag = UNTRUSTED_WEB_TAG) {
20815
- const emit4 = (text) => {
20763
+ const emit2 = (text) => {
20816
20764
  if (!fenceByProvenance || text === "") {
20817
20765
  out(text);
20818
20766
  return text;
@@ -20843,7 +20791,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
20843
20791
  }
20844
20792
  }
20845
20793
  if (opts.grep !== void 0 && opts.maxMatches !== void 0) {
20846
- const cap = requireNonNegativeInt2("--max-matches", opts.maxMatches);
20794
+ const cap = requireNonNegativeInt("--max-matches", opts.maxMatches);
20847
20795
  const matched = content === "" ? [] : content.split(/\r?\n/);
20848
20796
  if (matched.length > cap) {
20849
20797
  content = [...matched.slice(0, cap), "[token-goat: showing first " + cap + " of " + matched.length + " matching lines; raise --max-matches for more]"].join("\n");
@@ -20851,11 +20799,11 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
20851
20799
  }
20852
20800
  const rawLines = content.split(/\r?\n/);
20853
20801
  if (opts.full === true) {
20854
- return emit4(rawLines.join("\n"));
20802
+ return emit2(rawLines.join("\n"));
20855
20803
  }
20856
20804
  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;
20805
+ const headN = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 30;
20806
+ const tailN = opts.tail !== void 0 ? requireNonNegativeInt("--tail", opts.tail) : 80;
20859
20807
  const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 + 1 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
20860
20808
  const noteLineCap = (which, flag, shown, total) => {
20861
20809
  if (shown >= total) return;
@@ -20874,7 +20822,7 @@ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTa
20874
20822
  result = lines.slice(Math.max(0, lines.length - tailN));
20875
20823
  noteLineCap("last", "tail", result.length, lines.length);
20876
20824
  }
20877
- return emit4(result.join("\n"));
20825
+ return emit2(result.join("\n"));
20878
20826
  }
20879
20827
  function cmdBashOutput(id, opts) {
20880
20828
  const parseVerifyThreshold = (optVal) => {
@@ -20895,7 +20843,7 @@ function cmdBashOutput(id, opts) {
20895
20843
  }
20896
20844
  let content;
20897
20845
  try {
20898
- const st = fs46.statSync(opts.file);
20846
+ const st = fs45.statSync(opts.file);
20899
20847
  if (st.isFIFO() || st.isSocket()) {
20900
20848
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
20901
20849
  }
@@ -20910,7 +20858,7 @@ function cmdBashOutput(id, opts) {
20910
20858
  `);
20911
20859
  }
20912
20860
  }
20913
- content = redactIfDotenv(opts.file, decodeSource(fs46.readFileSync(opts.file)));
20861
+ content = redactIfDotenv(opts.file, decodeSource(fs45.readFileSync(opts.file)));
20914
20862
  } catch (e) {
20915
20863
  if (e instanceof CliError) throw e;
20916
20864
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -20974,15 +20922,15 @@ function cmdMcpOutput(id, opts) {
20974
20922
  if (opts.file.includes("\0")) {
20975
20923
  throw new CliError("--file path contains a null byte");
20976
20924
  }
20977
- if (!fs46.existsSync(opts.file)) {
20925
+ if (!fs45.existsSync(opts.file)) {
20978
20926
  throw new CliError(`file not found: ${opts.file}`);
20979
20927
  }
20980
20928
  try {
20981
- const st = fs46.statSync(opts.file);
20929
+ const st = fs45.statSync(opts.file);
20982
20930
  if (st.isFIFO() || st.isSocket()) {
20983
20931
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
20984
20932
  }
20985
- content = decodeSource(fs46.readFileSync(opts.file));
20933
+ content = decodeSource(fs45.readFileSync(opts.file));
20986
20934
  } catch (e) {
20987
20935
  if (e instanceof CliError) throw e;
20988
20936
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -21003,7 +20951,7 @@ function cmdMcpOutput(id, opts) {
21003
20951
  const data = extractJsonFromMcpOutput(content);
21004
20952
  let head;
21005
20953
  if (opts.head !== void 0) {
21006
- head = requireNonNegativeInt2("--head", opts.head);
20954
+ head = requireNonNegativeInt("--head", opts.head);
21007
20955
  }
21008
20956
  const queryResult = queryJson(data, opts.jsonQuery);
21009
20957
  const { head: _omittedHead, ...restOpts } = opts;
@@ -21055,12 +21003,12 @@ async function cmdCompress(commandArgs, opts = {}) {
21055
21003
  process.exitCode = 1;
21056
21004
  return;
21057
21005
  }
21058
- const bashRunner = await import("./token-goat-chunk-P46RTOAQ.mjs");
21006
+ const bashRunner = await import("./token-goat-chunk-CBXLE54Q.mjs");
21059
21007
  if (opts.compress === false) {
21060
21008
  process.exitCode = bashRunner.runRaw(command, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
21061
21009
  return;
21062
21010
  }
21063
- const maxTokens = opts.maxTokens !== void 0 ? requireNonNegativeInt2("--max-tokens", opts.maxTokens) : 0;
21011
+ const maxTokens = opts.maxTokens !== void 0 ? requireNonNegativeInt("--max-tokens", opts.maxTokens) : 0;
21064
21012
  process.exitCode = await bashRunner.run(command, {
21065
21013
  filterName: opts.filter,
21066
21014
  timeout: parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS),
@@ -21160,7 +21108,7 @@ function buildProgram() {
21160
21108
  more,
21161
21109
  () => runSymbol({
21162
21110
  ...name !== void 0 ? { name } : {},
21163
- limit: opts.limit !== void 0 ? requireNonNegativeInt2("--limit", opts.limit) : 20,
21111
+ limit: opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20,
21164
21112
  ...opts.file !== void 0 ? { file: opts.file } : {},
21165
21113
  ...opts.kind !== void 0 ? { kind: opts.kind } : {},
21166
21114
  ...projectRoot !== void 0 ? { projectRoot } : {},
@@ -21202,8 +21150,8 @@ function buildProgram() {
21202
21150
  return runBrief({
21203
21151
  spec,
21204
21152
  ...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) } : {},
21153
+ ...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
21154
+ ...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
21207
21155
  ...opts.excludeTests === true ? { excludeTests: true } : {},
21208
21156
  ...opts.grep !== void 0 ? { grep: opts.grep } : {}
21209
21157
  });
@@ -21211,14 +21159,26 @@ function buildProgram() {
21211
21159
  );
21212
21160
  program2.command("section <spec> [more...]").description(
21213
21161
  '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(() => {
21162
+ ).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) => {
21163
+ const maxLinesRaw = opts.maxLines ?? opts.head;
21164
+ const maxLines = maxLinesRaw !== void 0 ? requireNonNegativeInt("--max-lines", maxLinesRaw) : void 0;
21165
+ return opts.list === true ? runExit(() => {
21216
21166
  emitExtraFileArgsNote("section --list", spec, more, { mergeable: false });
21217
21167
  return runListSections({ file: spec, ...opts.json === true ? { json: true } : {}, ...opts.grep !== void 0 ? { grep: opts.grep } : {} });
21218
21168
  }) : runExitText(
21219
- () => noteExtraFileArgs("section", spec, more, () => runSection({ spec, ...opts.json === true ? { json: true } : {} }), { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) })
21220
- )
21221
- );
21169
+ () => noteExtraFileArgs(
21170
+ "section",
21171
+ spec,
21172
+ more,
21173
+ () => runSection({
21174
+ spec,
21175
+ ...opts.json === true ? { json: true } : {},
21176
+ ...maxLines !== void 0 ? { maxLines } : {}
21177
+ }),
21178
+ { noun: "spec", mergeable: namedSpecsMergeable([spec, ...more]) }
21179
+ )
21180
+ );
21181
+ });
21222
21182
  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
21183
  const registerSymbolListing = (name, description, run2) => {
21224
21184
  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 +21190,7 @@ function buildProgram() {
21230
21190
  () => run2({
21231
21191
  file,
21232
21192
  ...opts.json === true ? { json: true } : {},
21233
- ...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt2("--min-lines", opts.minLines) } : {},
21193
+ ...opts.minLines !== void 0 ? { minLines: requireNonNegativeInt("--min-lines", opts.minLines) } : {},
21234
21194
  ...opts.grep !== void 0 ? { grep: opts.grep } : {},
21235
21195
  ...opts.forceRefresh === true ? { forceRefresh: true } : {},
21236
21196
  ...opts.stats === true ? { stats: true } : {}
@@ -21259,9 +21219,9 @@ function buildProgram() {
21259
21219
  spec,
21260
21220
  ...opts.callers === true ? { callers: true } : {},
21261
21221
  ...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) } : {},
21222
+ ...opts.limit !== void 0 ? { limit: requireNonNegativeInt("--limit", opts.limit) } : {},
21223
+ ...opts.top !== void 0 ? { top: requireNonNegativeInt("--top", opts.top) } : {},
21224
+ ...opts.context !== void 0 ? { context: requireNonNegativeInt("--context", opts.context) } : {},
21265
21225
  ...opts.excludeTests === true ? { excludeTests: true } : {},
21266
21226
  ...opts.grep !== void 0 ? { grep: opts.grep } : {}
21267
21227
  });
@@ -21275,7 +21235,7 @@ function buildProgram() {
21275
21235
  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
21236
  (opts) => runExitText(
21277
21237
  () => runBenchCommand({
21278
- corpus: opts.corpus ?? path42.join("tests", "fixtures", "bench"),
21238
+ corpus: opts.corpus ?? path41.join("tests", "fixtures", "bench"),
21279
21239
  ...opts.tsv !== void 0 ? { tsv: opts.tsv } : {},
21280
21240
  ...opts.validate === true ? { validate: true } : {},
21281
21241
  ...opts.json === true ? { json: true } : {}
@@ -21309,7 +21269,7 @@ function buildProgram() {
21309
21269
  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
21270
  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
21271
  (opts) => guard(async () => {
21312
- const { cmdUpgrade } = await import("./token-goat-chunk-VJQBYSXG.mjs");
21272
+ const { cmdUpgrade } = await import("./token-goat-chunk-AU2K2QHR.mjs");
21313
21273
  await cmdUpgrade(opts, () => cmdInstall({}));
21314
21274
  })()
21315
21275
  );
@@ -21379,6 +21339,9 @@ async function run(argv = process.argv) {
21379
21339
  }
21380
21340
 
21381
21341
  export {
21342
+ requireInt,
21343
+ requireNonNegativeInt,
21344
+ requirePositiveInt,
21382
21345
  expandGlobs,
21383
21346
  fenceFileText,
21384
21347
  fenceFileFieldIfMatched,
@@ -21438,9 +21401,6 @@ export {
21438
21401
  CliError,
21439
21402
  out,
21440
21403
  err,
21441
- requireInt2 as requireInt,
21442
- requireNonNegativeInt2 as requireNonNegativeInt,
21443
- requirePositiveInt2 as requirePositiveInt,
21444
21404
  cmdIndex,
21445
21405
  visualStudioManualSteps,
21446
21406
  leftoverIntegrations,