token-goat 2.9.25 → 2.9.28

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 (57) hide show
  1. package/README.md +28 -88
  2. package/SECURITY.md +14 -0
  3. package/dist/{token-goat-chunk-ORO4GOMA.mjs → token-goat-chunk-4JJBFT7V.mjs} +3 -3
  4. package/dist/token-goat-chunk-4JMWPWCS.mjs +96 -0
  5. package/dist/token-goat-chunk-4RN64ZND.mjs +954 -0
  6. package/dist/{token-goat-chunk-6W2FFT6H.mjs → token-goat-chunk-4STDIGYC.mjs} +2 -2
  7. package/dist/{token-goat-chunk-EZVRN44Y.mjs → token-goat-chunk-5NNYIUGV.mjs} +2 -2
  8. package/dist/{token-goat-chunk-ILROZWXE.mjs → token-goat-chunk-66LODAUT.mjs} +1 -1
  9. package/dist/{token-goat-chunk-VAIJUZG6.mjs → token-goat-chunk-6CCQDAYY.mjs} +69 -566
  10. package/dist/{token-goat-chunk-UGCNTVF4.mjs → token-goat-chunk-6DQNNFJ6.mjs} +5 -3
  11. package/dist/{token-goat-chunk-ZWFP4ZU7.mjs → token-goat-chunk-6SGLIPQB.mjs} +40 -20
  12. package/dist/{token-goat-chunk-TWVFMUTZ.mjs → token-goat-chunk-6Z5ULNE5.mjs} +3570 -3970
  13. package/dist/token-goat-chunk-7MAFDRVR.mjs +47 -0
  14. package/dist/{token-goat-chunk-PPNKB5HP.mjs → token-goat-chunk-7R2XYKC5.mjs} +6 -5
  15. package/dist/{token-goat-chunk-KYV7E6ZJ.mjs → token-goat-chunk-BCN2YQC6.mjs} +10 -8
  16. package/dist/token-goat-chunk-BJBRGPBX.mjs +244 -0
  17. package/dist/{token-goat-chunk-Z5ZTBSHL.mjs → token-goat-chunk-CBMY2FGI.mjs} +479 -544
  18. package/dist/{token-goat-chunk-UTKNLKYE.mjs → token-goat-chunk-CC2PYJM7.mjs} +2 -1
  19. package/dist/{token-goat-chunk-7PQYG6TN.mjs → token-goat-chunk-D5ICEPSB.mjs} +14 -14
  20. package/dist/token-goat-chunk-D7GHQKQD.mjs +30 -0
  21. package/dist/{token-goat-chunk-NNXCBELZ.mjs → token-goat-chunk-EL7M26WP.mjs} +7623 -8455
  22. package/dist/{token-goat-chunk-TVTDINTO.mjs → token-goat-chunk-F7QZRIWM.mjs} +69 -20
  23. package/dist/{token-goat-chunk-MBRX5AP6.mjs → token-goat-chunk-FSTWAOW6.mjs} +3 -3
  24. package/dist/token-goat-chunk-GGLEE35R.mjs +564 -0
  25. package/dist/{token-goat-chunk-YZ67F2HJ.mjs → token-goat-chunk-HUDC5GS5.mjs} +2 -2
  26. package/dist/{token-goat-chunk-BIBZQRC6.mjs → token-goat-chunk-HXNAC4ZF.mjs} +22 -964
  27. package/dist/token-goat-chunk-I7CB3X4K.mjs +1379 -0
  28. package/dist/{token-goat-chunk-KMCVX3P2.mjs → token-goat-chunk-IFORDBGC.mjs} +32 -27
  29. package/dist/{token-goat-chunk-25NRXKIW.mjs → token-goat-chunk-IHKZ3QNA.mjs} +26 -15
  30. package/dist/token-goat-chunk-IZXKVT4A.mjs +711 -0
  31. package/dist/{token-goat-chunk-UEQUZDHQ.mjs → token-goat-chunk-JXK2HQXS.mjs} +22 -8
  32. package/dist/{token-goat-chunk-YE4TGC4Z.mjs → token-goat-chunk-L45ZQRT7.mjs} +1 -1
  33. package/dist/{token-goat-chunk-IEF2NZFZ.mjs → token-goat-chunk-L7SDVGBT.mjs} +196 -14
  34. package/dist/token-goat-chunk-LEG3JNJ6.mjs +139 -0
  35. package/dist/{token-goat-chunk-3FSAZR32.mjs → token-goat-chunk-M56BWPBN.mjs} +1 -1
  36. package/dist/{token-goat-chunk-QZZPWFVF.mjs → token-goat-chunk-MAMQPC43.mjs} +2 -87
  37. package/dist/{token-goat-chunk-OHMKCBZI.mjs → token-goat-chunk-MMLMALWK.mjs} +5 -3
  38. package/dist/{token-goat-chunk-KOL5WXDW.mjs → token-goat-chunk-MVSWBNMD.mjs} +7 -231
  39. package/dist/{token-goat-chunk-J2PRBQ24.mjs → token-goat-chunk-NILVK7XM.mjs} +4 -4
  40. package/dist/{token-goat-chunk-OUQIGPW3.mjs → token-goat-chunk-Q4S3UQGO.mjs} +1 -1
  41. package/dist/{token-goat-chunk-EX2DVKAT.mjs → token-goat-chunk-QM76S6YS.mjs} +3 -3
  42. package/dist/{token-goat-chunk-G7YWBH25.mjs → token-goat-chunk-RJH64KQH.mjs} +86 -72
  43. package/dist/{token-goat-chunk-RLDBVMXR.mjs → token-goat-chunk-RSS3TLXU.mjs} +2 -2
  44. package/dist/{token-goat-chunk-T6652D3F.mjs → token-goat-chunk-SJEKA7Y2.mjs} +21 -16
  45. package/dist/{token-goat-chunk-II5WBDFB.mjs → token-goat-chunk-UBUKHVYW.mjs} +1 -1
  46. package/dist/{token-goat-chunk-2SSDSUSF.mjs → token-goat-chunk-VE27HPIF.mjs} +14 -10
  47. package/dist/{token-goat-chunk-Y463KPAQ.mjs → token-goat-chunk-VWZKZPSR.mjs} +100 -23
  48. package/dist/{token-goat-chunk-K4TNHLGS.mjs → token-goat-chunk-XI2VFAV6.mjs} +6 -6
  49. package/dist/{token-goat-chunk-ZDFDCXU2.mjs → token-goat-chunk-XKY4VKJ4.mjs} +1 -1
  50. package/dist/{token-goat-chunk-BQ6ZDLTF.mjs → token-goat-chunk-YWSP6IU7.mjs} +1 -1
  51. package/dist/token-goat-hook.mjs +23 -17
  52. package/dist/token-goat.core.mjs +32 -27
  53. package/docs/cli.md +32 -21
  54. package/docs/install.md +2 -0
  55. package/package.json +1 -1
  56. package/dist/token-goat-chunk-EDCVQLBR.mjs +0 -26
  57. package/dist/token-goat-chunk-WEYODFTA.mjs +0 -41
@@ -1,17 +1,5 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
- import {
4
- compareSemver
5
- } from "./token-goat-chunk-EZVRN44Y.mjs";
6
- import {
7
- buildCopilotWasteReport,
8
- findLatestCopilotSession,
9
- findProjectSession,
10
- isCopilotTranscript
11
- } from "./token-goat-chunk-UEQUZDHQ.mjs";
12
- import {
13
- buildResumePacket
14
- } from "./token-goat-chunk-K4TNHLGS.mjs";
15
3
  import {
16
4
  ALL_SYMBOLS_IN_FILE_LIMIT,
17
5
  AMBIGUOUS_HEADING_LIMIT,
@@ -90,6 +78,7 @@ import {
90
78
  runScreenshot,
91
79
  runSection,
92
80
  runSemantic,
81
+ runSemanticMulti,
93
82
  runSimilar,
94
83
  runSkeleton,
95
84
  runSqliteQuery,
@@ -107,7 +96,21 @@ import {
107
96
  symbolNamesInFile,
108
97
  upsertNote,
109
98
  warnIfFilesStale
110
- } from "./token-goat-chunk-Y463KPAQ.mjs";
99
+ } from "./token-goat-chunk-VWZKZPSR.mjs";
100
+ import {
101
+ compareSemver
102
+ } from "./token-goat-chunk-5NNYIUGV.mjs";
103
+ import {
104
+ MCP_DISABLE_NOTE,
105
+ buildCopilotWasteReport,
106
+ findLatestCopilotSession,
107
+ findProjectSession,
108
+ isCopilotTranscript,
109
+ unusedMcpServers
110
+ } from "./token-goat-chunk-JXK2HQXS.mjs";
111
+ import {
112
+ buildResumePacket
113
+ } from "./token-goat-chunk-XI2VFAV6.mjs";
111
114
  import {
112
115
  DEFAULT_RECONCILE_BUDGET_MS,
113
116
  HOOK_EVENTS,
@@ -126,19 +129,9 @@ import {
126
129
  runRecallCommand,
127
130
  runReconcile,
128
131
  storeWebOutput
129
- } from "./token-goat-chunk-2SSDSUSF.mjs";
130
- import {
131
- buildWasteReport,
132
- extractResultText,
133
- findLatestTranscript,
134
- projectTranscriptsDir,
135
- safeStringify
136
- } from "./token-goat-chunk-UGCNTVF4.mjs";
132
+ } from "./token-goat-chunk-VE27HPIF.mjs";
137
133
  import {
138
- buildManifest
139
- } from "./token-goat-chunk-IEF2NZFZ.mjs";
140
- import {
141
- GEMINI_TOOL_NAME_MAP,
134
+ HINT_CATEGORIES,
142
135
  MAX_FILES_SCANNED,
143
136
  SKIP_DIRS,
144
137
  WORKER_HEARTBEAT_STALE_MS,
@@ -208,15 +201,26 @@ import {
208
201
  visionTokensSavedByText,
209
202
  walkProject,
210
203
  writeCompact
211
- } from "./token-goat-chunk-TWVFMUTZ.mjs";
204
+ } from "./token-goat-chunk-6Z5ULNE5.mjs";
205
+ import {
206
+ GEMINI_TOOL_NAME_MAP
207
+ } from "./token-goat-chunk-IZXKVT4A.mjs";
212
208
  import {
213
209
  CompressedOutput,
214
210
  TOOL_FILTERS,
215
211
  ToolFilter,
216
212
  deliverCompressed,
217
213
  detectFromCommand
218
- } from "./token-goat-chunk-G7YWBH25.mjs";
214
+ } from "./token-goat-chunk-RJH64KQH.mjs";
219
215
  import {
216
+ buildWasteReport,
217
+ extractResultText,
218
+ findLatestTranscript,
219
+ projectTranscriptsDir,
220
+ safeStringify
221
+ } from "./token-goat-chunk-6DQNNFJ6.mjs";
222
+ import {
223
+ COPILOT_CLI_HOOK_SCRIPT,
220
224
  HOOKS_SCRIPT_FILE,
221
225
  MATERIALIZE_SHRUNK_IMAGE_JS,
222
226
  buildGuidanceBlock,
@@ -253,7 +257,14 @@ import {
253
257
  visualStudioProjectMcpPath,
254
258
  visualStudioSolutionVscodeMcpPath,
255
259
  visualStudioUserMcpPath
256
- } from "./token-goat-chunk-OHMKCBZI.mjs";
260
+ } from "./token-goat-chunk-MMLMALWK.mjs";
261
+ import {
262
+ buildManifest,
263
+ clearAll,
264
+ loadEntries,
265
+ setEntry,
266
+ unsetEntry
267
+ } from "./token-goat-chunk-L7SDVGBT.mjs";
257
268
  import {
258
269
  AGENT_SALT_MARKER,
259
270
  CONTEXT_AUTOCOMPACT_TOKENS,
@@ -266,8 +277,6 @@ import {
266
277
  findLatestSessionId,
267
278
  formatAge,
268
279
  getContextPressure,
269
- getSessionFiles,
270
- getSessionId,
271
280
  getSkillFilePath,
272
281
  incrementSkillHit,
273
282
  listOutputs,
@@ -279,27 +288,39 @@ import {
279
288
  skillOutputsDir,
280
289
  storeCompact,
281
290
  storeOutput
282
- } from "./token-goat-chunk-VAIJUZG6.mjs";
291
+ } from "./token-goat-chunk-6CCQDAYY.mjs";
292
+ import {
293
+ getSessionFiles,
294
+ getSessionId
295
+ } from "./token-goat-chunk-GGLEE35R.mjs";
283
296
  import {
284
- capJsonRows,
285
297
  claudeConfigDir,
286
- estimateTokens,
287
- estimateTokensFromLength,
288
298
  formatBytes,
289
- formatTokenEstimate,
290
- trimToBudget
291
- } from "./token-goat-chunk-QZZPWFVF.mjs";
299
+ formatTokenEstimate
300
+ } from "./token-goat-chunk-MAMQPC43.mjs";
292
301
  import {
293
302
  BASH_OUTPUT_SUBDIR,
294
- clipLongMatchLine,
295
- combineStreams,
296
303
  getBashOutput,
297
304
  isRecallCacheType
298
- } from "./token-goat-chunk-BIBZQRC6.mjs";
305
+ } from "./token-goat-chunk-HXNAC4ZF.mjs";
299
306
  import {
300
- C,
301
307
  DEFAULT_MAX_AGE_MS,
302
308
  DEFAULT_MAX_COUNT,
309
+ listBlobs,
310
+ pruneBlobs
311
+ } from "./token-goat-chunk-BJBRGPBX.mjs";
312
+ import {
313
+ capJsonRows,
314
+ estimateTokens,
315
+ estimateTokensFromLength,
316
+ trimToBudget
317
+ } from "./token-goat-chunk-4JMWPWCS.mjs";
318
+ import {
319
+ clipLongMatchLine,
320
+ combineStreams
321
+ } from "./token-goat-chunk-4RN64ZND.mjs";
322
+ import {
323
+ C,
303
324
  HOOK_STATS_RETENTION_DAYS,
304
325
  RESET,
305
326
  _useRichStats,
@@ -308,8 +329,6 @@ import {
308
329
  fg,
309
330
  formatLocalTimestamp,
310
331
  getGlobalDb,
311
- listBlobs,
312
- pruneBlobs,
313
332
  pruneStalePatternCoveredUnmappedTools,
314
333
  readUnmappedTools,
315
334
  recordStat,
@@ -321,19 +340,19 @@ import {
321
340
  statsHasHarnessColumn,
322
341
  stripAnsiEscapes,
323
342
  summarize
324
- } from "./token-goat-chunk-KOL5WXDW.mjs";
343
+ } from "./token-goat-chunk-MVSWBNMD.mjs";
325
344
  import {
326
345
  docxOutline,
327
346
  docxTables,
328
347
  docxText,
329
348
  formatDocxTables
330
- } from "./token-goat-chunk-ZDFDCXU2.mjs";
349
+ } from "./token-goat-chunk-XKY4VKJ4.mjs";
331
350
  import {
332
351
  pptxNotesText,
333
352
  pptxOutline,
334
353
  pptxSlideText,
335
354
  pptxTextGrep
336
- } from "./token-goat-chunk-OUQIGPW3.mjs";
355
+ } from "./token-goat-chunk-Q4S3UQGO.mjs";
337
356
  import {
338
357
  formatXlsxColumns,
339
358
  formatXlsxRange,
@@ -342,11 +361,11 @@ import {
342
361
  querySheet,
343
362
  rangeSheet,
344
363
  xlsxColumns
345
- } from "./token-goat-chunk-RLDBVMXR.mjs";
364
+ } from "./token-goat-chunk-RSS3TLXU.mjs";
346
365
  import {
347
366
  formatCsvTable,
348
367
  parseWhereSpecs
349
- } from "./token-goat-chunk-BQ6ZDLTF.mjs";
368
+ } from "./token-goat-chunk-YWSP6IU7.mjs";
350
369
  import {
351
370
  assetEmbedSha,
352
371
  deleteFileEmbeddings,
@@ -372,12 +391,12 @@ import {
372
391
  readSection,
373
392
  treeSitterCoreAvailable,
374
393
  treeSitterCoreLoadError
375
- } from "./token-goat-chunk-7PQYG6TN.mjs";
394
+ } from "./token-goat-chunk-D5ICEPSB.mjs";
376
395
  import {
377
396
  getFileEntry,
378
397
  getProjectFileEntries,
379
398
  querySymbols
380
- } from "./token-goat-chunk-YZ67F2HJ.mjs";
399
+ } from "./token-goat-chunk-HUDC5GS5.mjs";
381
400
  import {
382
401
  TREE_SITTER_LANGUAGES,
383
402
  detectLanguage,
@@ -388,7 +407,7 @@ import {
388
407
  matchGroovySlashy,
389
408
  nonTreeSitterLanguageCount,
390
409
  redactIfDotenv
391
- } from "./token-goat-chunk-3FSAZR32.mjs";
410
+ } from "./token-goat-chunk-M56BWPBN.mjs";
392
411
  import {
393
412
  CONFIG_KEY_ENV_OVERRIDES,
394
413
  PROJECT_LOCKED_KEYS,
@@ -417,7 +436,7 @@ import {
417
436
  stringify,
418
437
  validateEnumField,
419
438
  validateNumericField
420
- } from "./token-goat-chunk-UTKNLKYE.mjs";
439
+ } from "./token-goat-chunk-CC2PYJM7.mjs";
421
440
  import {
422
441
  ENV_KEYS,
423
442
  LOCK_WAIT_MS_HARDENED,
@@ -478,7 +497,7 @@ import {
478
497
  withRetryOnLock,
479
498
  writeIfDifferent,
480
499
  writeJsonSettings
481
- } from "./token-goat-chunk-YE4TGC4Z.mjs";
500
+ } from "./token-goat-chunk-L45ZQRT7.mjs";
482
501
  import {
483
502
  UNTRUSTED_FILE_TAG,
484
503
  UNTRUSTED_TOOL_TAG,
@@ -3626,9 +3645,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3626
3645
  * @param {string} [path]
3627
3646
  * @return {(string|null|Command)}
3628
3647
  */
3629
- executableDir(path42) {
3630
- if (path42 === void 0) return this._executableDir;
3631
- this._executableDir = path42;
3648
+ executableDir(path41) {
3649
+ if (path41 === void 0) return this._executableDir;
3650
+ this._executableDir = path41;
3632
3651
  return this;
3633
3652
  }
3634
3653
  /**
@@ -3928,8 +3947,8 @@ function attemptedCommandName(argv) {
3928
3947
  }
3929
3948
 
3930
3949
  // src/cli.ts
3931
- import * as fs45 from "fs";
3932
- import * as path41 from "path";
3950
+ import * as fs44 from "fs";
3951
+ import * as path40 from "path";
3933
3952
 
3934
3953
  // src/walk_index.ts
3935
3954
  init_define_import_meta_env();
@@ -4210,6 +4229,8 @@ var CANONICAL_SKILL_MD = gunzipSync(Buffer.from(CANONICAL_SKILL_GZIP_BASE64, "ba
4210
4229
  var HOOK_EVENT_MAP = [
4211
4230
  ["PreToolUse", "pre_tool_use"],
4212
4231
  ["PostToolUse", "post_tool_use"],
4232
+ // A failed call never reaches PostToolUse: Claude Code 2.1.281 sends a missing-file Read and a non-zero Bash exit here instead, carrying `error`, `is_interrupt` and `duration_ms` (captured, 2026-09-24).
4233
+ ["PostToolUseFailure", "post_tool_use_failure"],
4213
4234
  ["PreCompact", "pre_compact"],
4214
4235
  ["PostCompact", "post_compact"],
4215
4236
  ["UserPromptSubmit", "user_prompt_submit"],
@@ -4423,16 +4444,19 @@ function uninstallHooksScoped(scope) {
4423
4444
  return true;
4424
4445
  }
4425
4446
  function isInstalled(scope = "user") {
4426
- const settings = readSettings(settingsPath(scope));
4427
- const hooks = settings.hooks;
4428
- if (hooks === void 0) return false;
4447
+ if (!fs3.existsSync(claudeHookScriptPath())) return false;
4448
+ const missing = missingHookEvents(scope);
4449
+ return missing !== null && missing.length === 0;
4450
+ }
4451
+ function missingHookEvents(scope = "user") {
4452
+ const hooks = readSettings(settingsPath(scope)).hooks;
4453
+ if (hooks === void 0) return null;
4454
+ if (!HOOK_EVENT_MAP.some(([eventKey]) => groupHasTokenGoat(hooks[eventKey], isTokenGoatHookCommand))) return null;
4429
4455
  const scriptPath = claudeHookScriptPath();
4430
- if (!fs3.existsSync(scriptPath)) return false;
4431
- for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
4456
+ return HOOK_EVENT_MAP.filter(([eventKey, eventArg]) => {
4432
4457
  const expected = expectedHookEntryFor(scriptPath, eventArg);
4433
- if (!groupHasTokenGoat(hooks[eventKey], (c, a) => hookEntryMatches(c, a, expected))) return false;
4434
- }
4435
- return true;
4458
+ return !groupHasTokenGoat(hooks[eventKey], (c, a) => hookEntryMatches(c, a, expected));
4459
+ }).map(([eventKey]) => eventKey);
4436
4460
  }
4437
4461
  var CLAUDE_MD_BEGIN = "<!-- token-goat-begin -->";
4438
4462
  var CLAUDE_MD_END = "<!-- token-goat-end -->";
@@ -7329,6 +7353,15 @@ function checkVscodeClaudeHooks(useClaudeHooks, claudeHooksInstalled, vscodeHook
7329
7353
  message: vscodeHooksInstalled2 ? "VS Code has chat.useClaudeHooks on and token-goat hooks are in both ~/.claude/settings.json and the Copilot hooks file, so each hook fires twice in VS Code. Turn chat.useClaudeHooks off in VS Code settings." : 'VS Code has chat.useClaudeHooks on, so it runs the token-goat hooks from ~/.claude/settings.json in Claude Code wire format, which VS Code reads only in part. Run "token-goat install --vscode" and turn chat.useClaudeHooks off in VS Code settings.'
7330
7354
  };
7331
7355
  }
7356
+ function checkClaudeHookEvents(missing) {
7357
+ const name = "Claude Code hook events";
7358
+ if (missing.user === null && missing.project === null) return null;
7359
+ const gaps = [];
7360
+ if (missing.user !== null && missing.user.length > 0) gaps.push(`user scope lacks ${missing.user.join(", ")}; run: token-goat install`);
7361
+ if (missing.project !== null && missing.project.length > 0) gaps.push(`project scope lacks ${missing.project.join(", ")}; run: token-goat install --project`);
7362
+ if (gaps.length === 0) return { name, status: "ok", message: "every event this build handles is wired" };
7363
+ return { name, status: "warn", message: `${gaps.join(". ")}. Those events never reach token-goat until then; restart any running session afterwards.` };
7364
+ }
7332
7365
  function dedupeByResolvedPath(paths) {
7333
7366
  const seen = /* @__PURE__ */ new Set();
7334
7367
  return paths.filter((p) => {
@@ -8055,8 +8088,22 @@ function checkCopilotCli(configPath2, scriptPath) {
8055
8088
  message: 'hook did not return valid JSON -- Copilot treats this as a hook error and denies every tool call. Recovery: run "token-goat install --copilot", then fully restart Copilot CLI.'
8056
8089
  };
8057
8090
  }
8091
+ if (!shimIsCurrent(scriptPath, COPILOT_CLI_HOOK_SCRIPT)) {
8092
+ return { name: "Copilot CLI", status: "warn", message: staleShimMessage(scriptPath, "Copilot", "token-goat install --copilot") + ", then fully restart Copilot CLI." };
8093
+ }
8058
8094
  return { name: "Copilot CLI", status: "ok", message: "preToolUse hook invokes cleanly and returns valid JSON" };
8059
8095
  }
8096
+ function shimIsCurrent(scriptPath, expected) {
8097
+ return fs18.readFileSync(scriptPath, "utf-8").replace(/\r\n/g, "\n") === expected.replace(/\r\n/g, "\n");
8098
+ }
8099
+ function staleShimMessage(scriptPath, harness, reinstall) {
8100
+ return `hook shim at ${scriptPath} was written by an older token-goat build, so fixes to it since then are not reaching ${harness}. Recovery: run "${reinstall}"`;
8101
+ }
8102
+ function checkHookShim(name, scriptPath, expected, reinstall) {
8103
+ if (!fs18.existsSync(scriptPath)) return null;
8104
+ if (shimIsCurrent(scriptPath, expected)) return { name, status: "ok", message: `hook shim at ${scriptPath} matches this build` };
8105
+ return { name, status: "warn", message: staleShimMessage(scriptPath, name, reinstall) + ", then restart any running session." };
8106
+ }
8060
8107
  var COMPACTION_CHANNEL_WINDOW = 5;
8061
8108
  var COMPACTION_STATS_BATCH_SIZE = 50;
8062
8109
  var COMPACTION_STATS_SCAN_CEILING = 2e3;
@@ -8218,6 +8265,12 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
8218
8265
  results.push(checkDiskSpace(actualDataDir));
8219
8266
  const copilotResult = checkCopilotCli(copilotCliConfigPath(), copilotCliScriptPath());
8220
8267
  if (copilotResult) results.push(copilotResult);
8268
+ const claudeShimResult = checkHookShim("Claude Code", claudeHookScriptPath(), CLAUDECODE_HOOK_SCRIPT, isInstalled("user") || !isInstalled("project") ? "token-goat install" : "token-goat install --project");
8269
+ if (claudeShimResult) results.push(claudeShimResult);
8270
+ const claudeEventsResult = checkClaudeHookEvents({ user: missingHookEvents("user"), project: missingHookEvents("project") });
8271
+ if (claudeEventsResult) results.push(claudeEventsResult);
8272
+ const codexShimResult = checkHookShim("Codex", codexHookScriptPath(), CODEX_HOOK_SCRIPT, "token-goat install --codex");
8273
+ if (codexShimResult) results.push(codexShimResult);
8221
8274
  const vscodeHooksResult = checkVscodeClaudeHooks(
8222
8275
  vscodeUsesClaudeHooks(),
8223
8276
  isInstalled("user") || isInstalled("project"),
@@ -8470,7 +8523,7 @@ async function runDoctorAndExit(opts) {
8470
8523
  }
8471
8524
  }
8472
8525
  try {
8473
- const { checkUpdateStatus } = await import("./token-goat-chunk-ORO4GOMA.mjs");
8526
+ const { checkUpdateStatus } = await import("./token-goat-chunk-4JJBFT7V.mjs");
8474
8527
  const update = await checkUpdateStatus(1500);
8475
8528
  if (update.updateAvailable && update.latest) {
8476
8529
  console.log(`
@@ -8566,7 +8619,7 @@ function generateCompactHelp() {
8566
8619
  " -h, --help display help for command",
8567
8620
  "",
8568
8621
  "Reads: symbol, read, brief, section, semantic, skeleton, outline, refs, scope,",
8569
- " similar, context-for, call-chain, impact, callers, dead, test-for",
8622
+ " similar, context-for, call-chain, impact, callers, dead, test-for, locate",
8570
8623
  "",
8571
8624
  "Analysis: arch, affected, blame, deps, types, coverage-gaps, changed, diff, log,",
8572
8625
  " exports, imports, find, grep",
@@ -8579,19 +8632,18 @@ function generateCompactHelp() {
8579
8632
  "",
8580
8633
  "Index & Search: index, map, reconcile, doctor, commands, ask, answer, pack, tokens,",
8581
8634
  " budget, failures, todo, trace, logfold, lockdeps, dep-docs, recent, hot,",
8582
- " snapshot-snapshot, baseline, cost, coverage-report-gaps, clean-cache,",
8635
+ " baseline, cost, coverage-report-gaps, clean-cache,",
8583
8636
  " cache-audit, reclaim-index, project",
8584
8637
  "",
8585
8638
  "Session: stats, waste, session-outline, session-audit, session-slice,",
8586
8639
  " session-summary, context-stats, bootstrap-audit, memory, hint-stats,",
8587
- " session-schema, mcp-audit",
8640
+ " session-schema, mcp-audit, audit",
8588
8641
  "",
8589
8642
  "Skills: skill-body, skill-compact, skill-list, skill-size, skill-history,",
8590
8643
  " skill-diff, skill-section",
8591
8644
  "",
8592
8645
  "Compression: compress-text, retrieve, handoff-create, handoff-resolve,",
8593
- " compress, recall, bash-output, web-output, mcp-output, compress-text,",
8594
- " compress, bench",
8646
+ " compress, recall, bash-output, web-output, mcp-output, bench",
8595
8647
  "",
8596
8648
  "Config & Integration: install, uninstall, upgrade, mcp-serve, mcp-status, hook,",
8597
8649
  " bridges-status, worker, statusline, version, config, config-get,",
@@ -8600,9 +8652,9 @@ function generateCompactHelp() {
8600
8652
  "Data: note, note-add, note-get, note-list, describe, sqlite-query,",
8601
8653
  " sqlite-tables, sqlite-schema, csv-profile, csv-query",
8602
8654
  "",
8603
- "Utils: ignores, bash-history, web-history, openapi-op, openapi-outline,",
8604
- " gdrive-sections, screenshot, opencode-*, fetch-image, image-meta,",
8605
- " image-text, start, stop, resume, replace, insert-section, pr-slice,",
8655
+ "Utils: ignores, bash-history, web-history, history, openapi-op, openapi-outline,",
8656
+ " gdrive-sections, screenshot, fetch-image, image-meta,",
8657
+ " image-text, resume, replace, insert-section, pr-slice,",
8606
8658
  " sharepoint-resolve, transcript, transcript-outline, video-chapters,",
8607
8659
  " write-file, zip-list, zip-read, mcp-history, compact-doc, compact-hint,",
8608
8660
  " conflicts, prune-cache",
@@ -8904,12 +8956,9 @@ function performHttpFetch(targetUrl, opts) {
8904
8956
  });
8905
8957
  }
8906
8958
 
8907
- // src/config_commands.ts
8908
- function saveConfigSafe(cfg) {
8909
- ensureDirSync(path18.dirname(configPath()));
8910
- saveConfig(cfg);
8911
- }
8912
- function levenshtein(a, b, cap = 3) {
8959
+ // src/util_suggest.ts
8960
+ init_define_import_meta_env();
8961
+ function levenshteinDistance(a, b, cap = 3) {
8913
8962
  if (Math.abs(a.length - b.length) > cap) return cap + 1;
8914
8963
  const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
8915
8964
  for (let i = 1; i <= a.length; i++) {
@@ -8922,8 +8971,17 @@ function levenshtein(a, b, cap = 3) {
8922
8971
  }
8923
8972
  return prev[b.length] ?? cap + 1;
8924
8973
  }
8974
+ function suggestPackageNames(query, names) {
8975
+ return [...new Set(names)].map((n) => ({ n, d: levenshteinDistance(query.toLowerCase(), n.toLowerCase()) })).filter((x) => x.d <= 3).sort((a, b) => a.d - b.d).slice(0, 5).map((x) => x.n);
8976
+ }
8977
+
8978
+ // src/config_commands.ts
8979
+ function saveConfigSafe(cfg) {
8980
+ ensureDirSync(path18.dirname(configPath()));
8981
+ saveConfig(cfg);
8982
+ }
8925
8983
  function closestKeys(unknown, known) {
8926
- return known.map((k) => ({ k, d: levenshtein(unknown, k) })).filter((x) => x.d <= 3).sort((a, b) => a.d - b.d).slice(0, 3).map((x) => x.k);
8984
+ return known.map((k) => ({ k, d: levenshteinDistance(unknown, k) })).filter((x) => x.d <= 3).sort((a, b) => a.d - b.d).slice(0, 3).map((x) => x.k);
8927
8985
  }
8928
8986
  function didYouMeanKeySuffix(unknownKey) {
8929
8987
  const knownKeys = flattenConfig(defaultConfig()).map(([k]) => k);
@@ -14012,13 +14070,13 @@ function registerFormatCommands(program2, guard) {
14012
14070
  []
14013
14071
  ).option("--head <n>", "limit to the first N matching rows").option("--json", "emit rows as a JSON array of objects instead of CSV").option("--delimiter <char>", "field delimiter (default: ,)").option("--no-header", "treat the first row as data, not a header (columns become col1, col2, ...)").action(guard(cmdCsvQuery));
14014
14072
  program2.command("csv-profile <file>").description("per-column type/null/distinct/range summary of a CSV instead of a raw Read").option("--delimiter <char>", "field delimiter (default: ,)").option("--no-header", "treat the first row as data, not a header (columns become col1, col2, ...)").action(guard(cmdCsvProfile));
14015
- program2.command("json-outline <file>").description("structural summary of a JSON document (array shape / object key types) instead of a raw Read").option("--json", "emit the outline as JSON instead of text").action(guard(cmdJsonOutline));
14073
+ program2.command("json-outline <file>").description("structural summary of a JSON document (array shape / object key types) instead of a raw Read").option("--json", "emit the outline as JSON instead of text").option("--filter <text>", "list only the top-level keys containing TEXT (case-insensitive), with a count of how many matched").action(guard(cmdJsonOutline));
14016
14074
  program2.command("json-query <file> <path>").description(
14017
- "extract one value or a projected/filtered subset from a JSON document by dot-path instead of a raw Read\n\npath grammar: dot-separated keys with optional bracket segments -- [n] index, [*] wildcard (projects every element/value), [field=value] filter (keeps array elements whose field stringifies to value). Examples: data.items[3].name, items[*].id, items[status=active]"
14075
+ 'extract one value or a projected/filtered subset from a JSON document by dot-path instead of a raw Read\n\npath grammar: dot-separated keys with optional bracket segments -- [n] index, [*] wildcard (projects every element/value), [field=value] filter (keeps array elements whose field stringifies to value), ["key"] for a key holding a dot or space. Examples: data.items[3].name, items[*].id, items[status=active], ["a.b"].c'
14018
14076
  ).option("--head <n>", "limit a projected/filtered result to the first N items").option("--json", "emit the result as JSON instead of text").action(guard(cmdJsonQuery));
14019
- program2.command("yaml-outline <file>").description("structural summary of a YAML document (array shape / object key types) instead of a raw Read -- multi-document streams (---separated) outline as an array of documents").option("--json", "emit the outline as JSON instead of text").action(guard(cmdYamlOutline));
14077
+ program2.command("yaml-outline <file>").description("structural summary of a YAML document (array shape / object key types) instead of a raw Read -- multi-document streams (---separated) outline as an array of documents").option("--json", "emit the outline as JSON instead of text").option("--filter <text>", "list only the top-level keys containing TEXT (case-insensitive), with a count of how many matched").action(guard(cmdYamlOutline));
14020
14078
  program2.command("yaml-query <file> <path>").description(
14021
- "extract one value or a projected/filtered subset from a YAML document by dot-path instead of a raw Read (same grammar as json-query)\n\npath grammar: dot-separated keys with optional bracket segments -- [n] index, [*] wildcard (projects every element/value), [field=value] filter (keeps array elements whose field stringifies to value). Examples: spec.containers[0].image, items[*].name, items[kind=Service]"
14079
+ 'extract one value or a projected/filtered subset from a YAML document by dot-path instead of a raw Read (same grammar as json-query)\n\npath grammar: dot-separated keys with optional bracket segments -- [n] index, [*] wildcard (projects every element/value), [field=value] filter (keeps array elements whose field stringifies to value), ["key"] for a key holding a dot or space. Examples: spec.containers[0].image, items[*].name, items[kind=Service]'
14022
14080
  ).option("--head <n>", "limit a projected/filtered result to the first N items").option("--json", "emit the result as JSON instead of text").action(guard(cmdYamlQuery));
14023
14081
  program2.command("xml-outline <file>").description("structural summary of an XML document (element hierarchy / attribute names / child counts) instead of a raw Read").option("--json", "emit the outline as JSON instead of text").option("--max-depth <n>", "max depth of element hierarchy to show").option("--depth <n>", "alias for --max-depth").action(guard(cmdXmlOutline));
14024
14082
  program2.command("xml-query <file> [path]").description(
@@ -14383,153 +14441,13 @@ function runAffected(opts) {
14383
14441
 
14384
14442
  // src/text_commands.ts
14385
14443
  init_define_import_meta_env();
14386
- import * as fs32 from "fs";
14387
- import * as path29 from "path";
14388
-
14389
- // src/project_memory.ts
14390
- init_define_import_meta_env();
14391
- import * as fs28 from "node:fs";
14392
- import * as path26 from "node:path";
14393
- var MAX_ENTRIES = 30;
14394
- var KEY_RE2 = /^[A-Za-z0-9_-]{1,80}$/;
14395
- function ordinal(a, b) {
14396
- return a < b ? -1 : a > b ? 1 : 0;
14397
- }
14398
- function memoryPath(projectHash) {
14399
- return path26.join(dataDir(), "projects", `${projectHash}_memory.toml`);
14400
- }
14401
- function validateKey2(key) {
14402
- if (!KEY_RE2.test(key)) {
14403
- throw new Error(
14404
- `Invalid memory key ${JSON.stringify(key)}: use only letters, digits, hyphens, underscores (max 80 chars)`
14405
- );
14406
- }
14407
- }
14408
- function parseTOML(content) {
14409
- const result = {};
14410
- for (const line of content.split("\n")) {
14411
- const trimmed = line.trim();
14412
- if (!trimmed || trimmed.startsWith("#")) {
14413
- continue;
14414
- }
14415
- const match2 = trimmed.match(/^([A-Za-z0-9_-]+)\s*=\s*"(.*)"\s*$/);
14416
- if (match2) {
14417
- const [, key, value] = match2;
14418
- if (key && value !== void 0) {
14419
- const unescaped = value.replace(/\\([\\nrt"])/g, (_, c) => {
14420
- switch (c) {
14421
- case "\\":
14422
- return "\\";
14423
- case "n":
14424
- return "\n";
14425
- case "r":
14426
- return "\r";
14427
- case '"':
14428
- return '"';
14429
- default:
14430
- return _;
14431
- }
14432
- });
14433
- result[key] = unescaped;
14434
- }
14435
- }
14436
- }
14437
- return result;
14438
- }
14439
- function loadRaw(filePath) {
14440
- try {
14441
- if (!fs28.existsSync(filePath)) {
14442
- return {};
14443
- }
14444
- const content = fs28.readFileSync(filePath, "utf-8");
14445
- return parseTOML(content);
14446
- } catch {
14447
- return {};
14448
- }
14449
- }
14450
- function save(filePath, entries) {
14451
- const lines = [];
14452
- const sorted = Object.entries(entries).sort(([a], [b]) => ordinal(a, b));
14453
- for (const [k, v] of sorted) {
14454
- const escaped = v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
14455
- lines.push(`${k} = "${escaped}"`);
14456
- }
14457
- const content = lines.length > 0 ? lines.join("\n") + "\n" : "";
14458
- const dir = path26.dirname(filePath);
14459
- ensureDirSync(dir);
14460
- atomicWriteText(filePath, content);
14461
- }
14462
- function loadEntries(projectHash) {
14463
- return loadRaw(memoryPath(projectHash));
14464
- }
14465
- function setEntry(projectHash, key, value) {
14466
- validateKey2(key);
14467
- const p = memoryPath(projectHash);
14468
- const dir = path26.dirname(p);
14469
- ensureDirSync(dir);
14470
- const doSet = () => {
14471
- const entries = loadRaw(p);
14472
- const isNewKey = !(key in entries);
14473
- if (isNewKey && Object.keys(entries).length >= MAX_ENTRIES) {
14474
- const keysToKeep = MAX_ENTRIES - 1;
14475
- const allKeys = Object.keys(entries).sort((a, b) => ordinal(a, b));
14476
- for (const k of allKeys.slice(keysToKeep)) {
14477
- delete entries[k];
14478
- }
14479
- }
14480
- entries[key] = value;
14481
- save(p, entries);
14482
- return true;
14483
- };
14484
- if (withFileLock(`${p}.lock`, doSet) === void 0) doSet();
14485
- }
14486
- function unsetEntry(projectHash, key) {
14487
- validateKey2(key);
14488
- const p = memoryPath(projectHash);
14489
- const doUnset = () => {
14490
- const entries = loadRaw(p);
14491
- if (key in entries) {
14492
- delete entries[key];
14493
- save(p, entries);
14494
- }
14495
- return true;
14496
- };
14497
- if (withFileLock(`${p}.lock`, doUnset) === void 0) doUnset();
14498
- }
14499
- function clearAll(projectHash) {
14500
- const p = memoryPath(projectHash);
14501
- const doClear = () => {
14502
- if (fs28.existsSync(p)) {
14503
- save(p, {});
14504
- }
14505
- return true;
14506
- };
14507
- if (withFileLock(`${p}.lock`, doClear) === void 0) doClear();
14508
- }
14509
-
14510
- // src/util_suggest.ts
14511
- init_define_import_meta_env();
14512
- function packageNameDistance(a, b, cap = 3) {
14513
- if (Math.abs(a.length - b.length) > cap) return cap + 1;
14514
- const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
14515
- for (let i = 1; i <= a.length; i++) {
14516
- const curr = [i];
14517
- for (let j = 1; j <= b.length; j++) {
14518
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
14519
- curr.push(Math.min((curr[j - 1] ?? 0) + 1, (prev[j] ?? 0) + 1, (prev[j - 1] ?? 0) + cost));
14520
- }
14521
- prev.splice(0, prev.length, ...curr);
14522
- }
14523
- return prev[b.length] ?? cap + 1;
14524
- }
14525
- function suggestPackageNames(query, names) {
14526
- return [...new Set(names)].map((n) => ({ n, d: packageNameDistance(query.toLowerCase(), n.toLowerCase()) })).filter((x) => x.d <= 3).sort((a, b) => a.d - b.d).slice(0, 5).map((x) => x.n);
14527
- }
14444
+ import * as fs31 from "fs";
14445
+ import * as path28 from "path";
14528
14446
 
14529
14447
  // src/text_todo.ts
14530
14448
  init_define_import_meta_env();
14531
- import * as fs29 from "fs";
14532
- import * as path27 from "path";
14449
+ import * as fs28 from "fs";
14450
+ import * as path26 from "path";
14533
14451
  var DEFAULT_KINDS = ["TODO", "FIXME", "HACK", "XXX", "NOTE"];
14534
14452
  function splitLines(text) {
14535
14453
  return text.split(/\r?\n/);
@@ -14558,7 +14476,7 @@ function isMarkerOccurrence(matched, hasColon) {
14558
14476
  function scanFileForTodos(filePath, kindSet) {
14559
14477
  let text;
14560
14478
  try {
14561
- text = decodeSource(fs29.readFileSync(filePath));
14479
+ text = decodeSource(fs28.readFileSync(filePath));
14562
14480
  } catch {
14563
14481
  return [];
14564
14482
  }
@@ -14586,9 +14504,9 @@ function collectTodoFiles(patterns) {
14586
14504
  if (patterns.length > 0) {
14587
14505
  const results = [];
14588
14506
  for (const p of patterns) {
14589
- const abs = path27.resolve(p);
14507
+ const abs = path26.resolve(p);
14590
14508
  try {
14591
- const stat = fs29.statSync(abs);
14509
+ const stat = fs28.statSync(abs);
14592
14510
  if (stat.isDirectory()) {
14593
14511
  pushAll(results, walkProject(abs).files);
14594
14512
  } else {
@@ -14653,11 +14571,11 @@ function cmdTodo(patterns, opts) {
14653
14571
 
14654
14572
  // src/text_trace.ts
14655
14573
  init_define_import_meta_env();
14656
- import * as fs30 from "fs";
14657
- import * as path28 from "path";
14574
+ import * as fs29 from "fs";
14575
+ import * as path27 from "path";
14658
14576
  function readInput(src) {
14659
- if (src !== void 0) return fs30.readFileSync(src, "utf8");
14660
- return fs30.readFileSync(0, "utf8");
14577
+ if (src !== void 0) return fs29.readFileSync(src, "utf8");
14578
+ return fs29.readFileSync(0, "utf8");
14661
14579
  }
14662
14580
  function splitLines2(text) {
14663
14581
  return text.split(/\r?\n/);
@@ -14869,7 +14787,7 @@ function isProjectFrame(framePath, cwd) {
14869
14787
  if (framePath.includes("/rustc/")) return false;
14870
14788
  if (framePath.includes(".cargo/registry") || framePath.includes(".cargo\\registry")) return false;
14871
14789
  if (/^<.+>$/.test(framePath)) return false;
14872
- if (!path28.isAbsolute(framePath) && !framePath.startsWith("..")) return true;
14790
+ if (!path27.isAbsolute(framePath) && !framePath.startsWith("..")) return true;
14873
14791
  return false;
14874
14792
  }
14875
14793
  function resolveFrameSymbol(frame, projectRoot) {
@@ -14977,7 +14895,7 @@ function cmdTrace(src, opts) {
14977
14895
 
14978
14896
  // src/text_logfold.ts
14979
14897
  init_define_import_meta_env();
14980
- import * as fs31 from "fs";
14898
+ import * as fs30 from "fs";
14981
14899
 
14982
14900
  // src/filters.ts
14983
14901
  init_define_import_meta_env();
@@ -15107,8 +15025,8 @@ function applyFiltersAndFold(lines, noNormalize, foldRepeats) {
15107
15025
  return folded;
15108
15026
  }
15109
15027
  function readInput2(src) {
15110
- if (src !== void 0) return fs31.readFileSync(src, "utf8");
15111
- return fs31.readFileSync(0, "utf8");
15028
+ if (src !== void 0) return fs30.readFileSync(src, "utf8");
15029
+ return fs30.readFileSync(0, "utf8");
15112
15030
  }
15113
15031
  function splitLines3(text) {
15114
15032
  return text.split(/\r?\n/);
@@ -15159,21 +15077,21 @@ var LOCK_PRIORITY = [
15159
15077
  "Cargo.lock"
15160
15078
  ];
15161
15079
  function findLockfile(startPath) {
15162
- const stat = fs32.statSync(startPath, { throwIfNoEntry: false });
15080
+ const stat = fs31.statSync(startPath, { throwIfNoEntry: false });
15163
15081
  if (stat !== void 0 && stat.isFile()) {
15164
15082
  return { file: startPath, others: [] };
15165
15083
  }
15166
- const dir = stat !== void 0 && stat.isDirectory() ? startPath : path29.dirname(startPath);
15084
+ const dir = stat !== void 0 && stat.isDirectory() ? startPath : path28.dirname(startPath);
15167
15085
  const found = [];
15168
15086
  for (const name of LOCK_PRIORITY) {
15169
- const candidate = path29.join(dir, name);
15170
- if (fs32.existsSync(candidate)) found.push(candidate);
15087
+ const candidate = path28.join(dir, name);
15088
+ if (fs31.existsSync(candidate)) found.push(candidate);
15171
15089
  }
15172
15090
  try {
15173
- const entries = fs32.readdirSync(dir);
15091
+ const entries = fs31.readdirSync(dir);
15174
15092
  for (const e of entries) {
15175
15093
  if (/^requirements.*\.txt$/.test(e)) {
15176
- const p = path29.join(dir, e);
15094
+ const p = path28.join(dir, e);
15177
15095
  if (!found.includes(p)) found.push(p);
15178
15096
  }
15179
15097
  }
@@ -15370,8 +15288,8 @@ function parsePnpmLock(content) {
15370
15288
  return deps;
15371
15289
  }
15372
15290
  function parseLockFile(filePath) {
15373
- const base = path29.basename(filePath);
15374
- const content = fs32.readFileSync(filePath, "utf8");
15291
+ const base = path28.basename(filePath);
15292
+ const content = fs31.readFileSync(filePath, "utf8");
15375
15293
  if (base === "package-lock.json") return { format: "npm", deps: parsePackageLockJson(content) };
15376
15294
  if (base === "yarn.lock") return { format: "yarn", deps: parseYarnLock(content) };
15377
15295
  if (base === "pnpm-lock.yaml") return { format: "pnpm", deps: parsePnpmLock(content) };
@@ -15444,7 +15362,7 @@ function cmdLockdepsPackage(lockfile, format, deps, query, json) {
15444
15362
  }
15445
15363
  const primary = matches2.find((d) => d.kind === "direct") ?? matches2[0];
15446
15364
  const otherVersions = [...new Set(matches2.filter((d) => d.version !== primary.version).map((d) => d.version))];
15447
- const graph = format === "npm" ? buildNpmEdges(fs32.readFileSync(lockfile, "utf8")) : null;
15365
+ const graph = format === "npm" ? buildNpmEdges(fs31.readFileSync(lockfile, "utf8")) : null;
15448
15366
  const graphAvailable = graph !== null;
15449
15367
  const dependsOn = graph !== null ? [...graph.edges.get(query) ?? []].sort() : [];
15450
15368
  const dependedOnBy = graph !== null ? findReverseDirectDeps(query, graph.edges, graph.directNames) : [];
@@ -15484,7 +15402,7 @@ Depended on by direct/top-level deps (${dependedOnBy.length}):
15484
15402
  `);
15485
15403
  }
15486
15404
  function cmdLockdeps(filePath, opts) {
15487
- const target = filePath !== void 0 ? path29.resolve(filePath) : process.cwd();
15405
+ const target = filePath !== void 0 ? path28.resolve(filePath) : process.cwd();
15488
15406
  const found = findLockfile(target);
15489
15407
  if (found === null) {
15490
15408
  throw new Error("No lockfile found. Expected one of: " + LOCK_PRIORITY.join(", ") + ", requirements*.txt");
@@ -15572,20 +15490,20 @@ function cmdNote(action, key, value, opts) {
15572
15490
  throw new Error(`Unknown note action: ${action}. Use: set, get, unset, list, clear`);
15573
15491
  }
15574
15492
  function loadAllSessionReadCounts() {
15575
- const sessionsDir2 = path29.join(tokenGoatHome(), "sessions");
15493
+ const sessionsDir2 = path28.join(tokenGoatHome(), "sessions");
15576
15494
  const totals = /* @__PURE__ */ new Map();
15577
15495
  let entries;
15578
15496
  try {
15579
- entries = fs32.readdirSync(sessionsDir2, { withFileTypes: true });
15497
+ entries = fs31.readdirSync(sessionsDir2, { withFileTypes: true });
15580
15498
  } catch {
15581
15499
  return totals;
15582
15500
  }
15583
15501
  for (const entry of entries) {
15584
15502
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
15585
- const filePath = path29.join(sessionsDir2, entry.name);
15503
+ const filePath = path28.join(sessionsDir2, entry.name);
15586
15504
  let raw;
15587
15505
  try {
15588
- raw = JSON.parse(fs32.readFileSync(filePath, "utf8"));
15506
+ raw = JSON.parse(fs31.readFileSync(filePath, "utf8"));
15589
15507
  } catch {
15590
15508
  continue;
15591
15509
  }
@@ -15718,7 +15636,7 @@ function cmdIgnores(opts) {
15718
15636
  }
15719
15637
  function countPackIgnorePatterns(root) {
15720
15638
  try {
15721
- return fs32.readFileSync(path29.join(root, ".tokengoatignore"), "utf8").split("\n").map((ln) => ln.trim()).filter((ln) => ln.length > 0 && !ln.startsWith("#")).length;
15639
+ return fs31.readFileSync(path28.join(root, ".tokengoatignore"), "utf8").split("\n").map((ln) => ln.trim()).filter((ln) => ln.length > 0 && !ln.startsWith("#")).length;
15722
15640
  } catch {
15723
15641
  return 0;
15724
15642
  }
@@ -15727,8 +15645,8 @@ function countPackIgnorePatterns(root) {
15727
15645
  // src/dep_docs.ts
15728
15646
  init_define_import_meta_env();
15729
15647
  import { createRequire } from "node:module";
15730
- import * as fs33 from "node:fs";
15731
- import * as path30 from "node:path";
15648
+ import * as fs32 from "node:fs";
15649
+ import * as path29 from "node:path";
15732
15650
  var _require = createRequire(import.meta.url);
15733
15651
  var _ts = null;
15734
15652
  var _tsLoadAttempted = false;
@@ -15747,21 +15665,21 @@ function loadTs() {
15747
15665
  }
15748
15666
  function dirExists(p) {
15749
15667
  try {
15750
- return fs33.statSync(p).isDirectory();
15668
+ return fs32.statSync(p).isDirectory();
15751
15669
  } catch {
15752
15670
  return false;
15753
15671
  }
15754
15672
  }
15755
15673
  function fileExists(p) {
15756
15674
  try {
15757
- return fs33.statSync(p).isFile();
15675
+ return fs32.statSync(p).isFile();
15758
15676
  } catch {
15759
15677
  return false;
15760
15678
  }
15761
15679
  }
15762
15680
  function readFileTextOrNull(p) {
15763
15681
  try {
15764
- return fs33.readFileSync(p, "utf8");
15682
+ return fs32.readFileSync(p, "utf8");
15765
15683
  } catch {
15766
15684
  return null;
15767
15685
  }
@@ -15769,7 +15687,7 @@ function readFileTextOrNull(p) {
15769
15687
  function listInstalledPackageNames(nodeModulesDir) {
15770
15688
  let entries;
15771
15689
  try {
15772
- entries = fs33.readdirSync(nodeModulesDir, { withFileTypes: true });
15690
+ entries = fs32.readdirSync(nodeModulesDir, { withFileTypes: true });
15773
15691
  } catch {
15774
15692
  return [];
15775
15693
  }
@@ -15779,7 +15697,7 @@ function listInstalledPackageNames(nodeModulesDir) {
15779
15697
  if (entry.name.startsWith("@")) {
15780
15698
  let scoped;
15781
15699
  try {
15782
- scoped = fs33.readdirSync(path30.join(nodeModulesDir, entry.name), { withFileTypes: true });
15700
+ scoped = fs32.readdirSync(path29.join(nodeModulesDir, entry.name), { withFileTypes: true });
15783
15701
  } catch {
15784
15702
  continue;
15785
15703
  }
@@ -15796,12 +15714,12 @@ var README_NAME_RE = /^readme(\.(md|markdown|txt))?$/i;
15796
15714
  function findReadmeFile(pkgDir) {
15797
15715
  const direct = findReadmeIn(pkgDir);
15798
15716
  if (direct !== null) return direct;
15799
- return findReadmeIn(path30.join(pkgDir, "docs"));
15717
+ return findReadmeIn(path29.join(pkgDir, "docs"));
15800
15718
  }
15801
15719
  function findReadmeIn(dir) {
15802
15720
  let entries;
15803
15721
  try {
15804
- entries = fs33.readdirSync(dir, { withFileTypes: true });
15722
+ entries = fs32.readdirSync(dir, { withFileTypes: true });
15805
15723
  } catch {
15806
15724
  return null;
15807
15725
  }
@@ -15813,7 +15731,7 @@ function findReadmeIn(dir) {
15813
15731
  return 1;
15814
15732
  };
15815
15733
  matches2.sort((a, b) => rank(a.name) - rank(b.name));
15816
- return path30.join(dir, matches2[0].name);
15734
+ return path29.join(dir, matches2[0].name);
15817
15735
  }
15818
15736
  function typesPackageDirName(pkgName) {
15819
15737
  if (pkgName.startsWith("@")) {
@@ -15840,30 +15758,30 @@ function resolveTypesLocation(pkgDir, pkgJson, nodeModulesDir, pkgName) {
15840
15758
  if (declared !== void 0) {
15841
15759
  const candidates = [declared, declared.endsWith(".d.ts") ? declared : `${declared}.d.ts`, declared.replace(/\.[cm]?[jt]s$/, ".d.ts")];
15842
15760
  for (const c of candidates) {
15843
- const p = path30.join(pkgDir, c);
15761
+ const p = path29.join(pkgDir, c);
15844
15762
  if (fileExists(p)) return { path: p, source: "bundled" };
15845
15763
  }
15846
15764
  }
15847
15765
  const main = typeof pkgJson["main"] === "string" ? pkgJson["main"] : "index.js";
15848
- const mainDts = path30.join(pkgDir, main.replace(/\.[cm]?js$/, ".d.ts"));
15766
+ const mainDts = path29.join(pkgDir, main.replace(/\.[cm]?js$/, ".d.ts"));
15849
15767
  if (fileExists(mainDts)) return { path: mainDts, source: "bundled" };
15850
- const indexDts = path30.join(pkgDir, "index.d.ts");
15768
+ const indexDts = path29.join(pkgDir, "index.d.ts");
15851
15769
  if (fileExists(indexDts)) return { path: indexDts, source: "bundled" };
15852
15770
  const typesDirName = typesPackageDirName(pkgName);
15853
- const typesPkgDir = path30.join(nodeModulesDir, "@types", typesDirName);
15854
- const typesPkgJsonRaw = readFileTextOrNull(path30.join(typesPkgDir, "package.json"));
15771
+ const typesPkgDir = path29.join(nodeModulesDir, "@types", typesDirName);
15772
+ const typesPkgJsonRaw = readFileTextOrNull(path29.join(typesPkgDir, "package.json"));
15855
15773
  if (typesPkgJsonRaw !== null) {
15856
15774
  try {
15857
15775
  const typesPkgJson = JSON.parse(typesPkgJsonRaw);
15858
15776
  const entry = (typeof typesPkgJson["types"] === "string" ? typesPkgJson["types"] : void 0) ?? (typeof typesPkgJson["main"] === "string" ? typesPkgJson["main"] : void 0) ?? "index.d.ts";
15859
15777
  const entryCandidates = [entry, entry.endsWith(".d.ts") ? entry : `${entry}.d.ts`, entry.replace(/\.[cm]?[jt]s$/, ".d.ts")];
15860
15778
  for (const c of entryCandidates) {
15861
- const entryPath = path30.join(typesPkgDir, c);
15779
+ const entryPath = path29.join(typesPkgDir, c);
15862
15780
  if (fileExists(entryPath)) return { path: entryPath, source: "@types" };
15863
15781
  }
15864
15782
  } catch {
15865
15783
  }
15866
- const fallback = path30.join(typesPkgDir, "index.d.ts");
15784
+ const fallback = path29.join(typesPkgDir, "index.d.ts");
15867
15785
  if (fileExists(fallback)) return { path: fallback, source: "@types" };
15868
15786
  }
15869
15787
  return null;
@@ -15930,16 +15848,16 @@ function recordDepDocsStat(fullSourceBytes, emittedText, packageName) {
15930
15848
  }
15931
15849
  function runDepDocs(opts) {
15932
15850
  const root = resolveProjectRoot({ project: opts.projectRoot ?? process.cwd() });
15933
- const nodeModulesDir = path30.join(root, "node_modules");
15934
- const pkgDir = path30.join(nodeModulesDir, opts.packageName);
15935
- const pkgJsonPath = path30.join(pkgDir, "package.json");
15851
+ const nodeModulesDir = path29.join(root, "node_modules");
15852
+ const pkgDir = path29.join(nodeModulesDir, opts.packageName);
15853
+ const pkgJsonPath = path29.join(pkgDir, "package.json");
15936
15854
  if (!dirExists(pkgDir) || !fileExists(pkgJsonPath)) {
15937
15855
  const known = listInstalledPackageNames(nodeModulesDir);
15938
15856
  const suggestions = suggestPackageNames(opts.packageName, known);
15939
15857
  const hint = suggestions.length > 0 ? ` (did you mean: ${suggestions.map(displaySafeText).join(", ")}?)` : "";
15940
15858
  return { text: `Package '${displaySafeText(opts.packageName)}' not found under ${displaySafeText(nodeModulesDir)}${hint}`, code: 1 };
15941
15859
  }
15942
- const pkgJsonRaw = fs33.readFileSync(pkgJsonPath, "utf8");
15860
+ const pkgJsonRaw = fs32.readFileSync(pkgJsonPath, "utf8");
15943
15861
  let pkgJson;
15944
15862
  try {
15945
15863
  pkgJson = JSON.parse(pkgJsonRaw);
@@ -16352,13 +16270,13 @@ function renderCapabilities(caps) {
16352
16270
 
16353
16271
  // src/cli_session.ts
16354
16272
  init_define_import_meta_env();
16355
- import * as fs40 from "node:fs";
16273
+ import * as fs39 from "node:fs";
16356
16274
 
16357
16275
  // src/session_audit.ts
16358
16276
  init_define_import_meta_env();
16359
- import * as fs34 from "node:fs";
16277
+ import * as fs33 from "node:fs";
16360
16278
  import * as os16 from "node:os";
16361
- import * as path31 from "node:path";
16279
+ import * as path30 from "node:path";
16362
16280
  import * as readline from "node:readline";
16363
16281
  var MODEL_VISIBLE_CATEGORIES = ["toolResults", "assistantText", "assistantThinking", "toolUseInputs", "attachments", "userTurns", "harnessMeta", "system"];
16364
16282
  function computeCalibration(estimated, measured) {
@@ -16419,10 +16337,13 @@ var ATTACHMENT_VISIBLE_FIELDS = {
16419
16337
  };
16420
16338
  var CACHE_WRITE_MULTIPLIER = 1.25;
16421
16339
  var CACHE_READ_MULTIPLIER = 0.1;
16422
- var READ_DIVERT_MARKER_RE = /(?:was already read this session|Already read |You've already read|Use `token-goat (?:section|read|bash-output|config-get|skeleton)|token-goat bash-output --file)/;
16340
+ var READ_DIVERT_MARKER_RE = /(?:was already read this session|Already read |You've already read|(?:Use|Run) `token-goat (?:section|read|bash-output|config-get|skeleton|json-query|yaml-query)|token-goat bash-output --file)/;
16423
16341
  var READ_DIVERT_MAX_BYTES = 2500;
16424
16342
  var READ_FULL_SERVE_MIN_BYTES = 10240;
16425
16343
  var DENY_TEMPLATES = [
16344
+ // hint_target.ts's sharpenRepeatedDeny replaces a verbatim repeat of any deny with this one line, so the kind the first copy had is not recoverable from the second.
16345
+ { kind: "repeat_refusal_deny", re: /Repeat refusal of this exact call/, tool: "Read" },
16346
+ { kind: "skill_repeat_refusal_deny", re: /Repeat refusal of this exact call/, tool: "Skill" },
16426
16347
  { kind: "node_modules_deny", re: /node_modules is typically noise/, tool: "Read" },
16427
16348
  { kind: "lock_file_deny", re: /Lock files are rarely useful to read in full/, tool: "Read" },
16428
16349
  { kind: "tsbuildinfo_deny", re: /TypeScript incremental build cache file/, tool: "Read" },
@@ -16432,13 +16353,7 @@ var DENY_TEMPLATES = [
16432
16353
  // Must stay ABOVE markdown_heading_tree_deny: this deny's message embeds the same "Large markdown file (N headings)" guidance block, so the generic alternative would swallow it and the new intervention would be uncountable. DENY_TEMPLATES is scanned with `.find`, so the more specific wording has to come first.
16433
16354
  { kind: "subagent_markdown_first_read_deny", re: /Subagent first read of a large markdown file/, tool: "Read" },
16434
16355
  { kind: "markdown_heading_tree_deny", re: /Large markdown file \(\d+ headings\)/, tool: "Read" },
16435
- // The first alternative is a wording hooks_read.ts no longer emits (it claimed a compact-manifest
16436
- // section that never existed, reworded in 3d044feb). It stays because this table classifies a
16437
- // historical corpus, not just today's output: transcripts written before the rewording still
16438
- // carry the old text, and dropping the alternative silently shrank this kind from 51 events to
16439
- // 30 -- 41% of its history -- with a green suite. A superseded alternative is only removable
16440
- // once no transcript contains it, which source code cannot tell you. See the superseded-wording
16441
- // test in tests/deny_outcomes.test.ts.
16356
+ // The first alternative is a wording hooks_read.ts no longer emits (it claimed a compact-manifest section that never existed, reworded in 3d044feb). It stays because this table classifies a historical corpus, not just today's output: transcripts written before the rewording still carry the old text, and dropping the alternative silently shrank this kind from 51 events to 30 -- 41% of its history -- with a green suite. A superseded alternative is only removable once no transcript contains it, which source code cannot tell you. See the superseded-wording test in tests/deny_outcomes.test.ts.
16442
16357
  { kind: "memory_md_reread_deny", re: /MEMORY\.md was read this session\. Its content is in the compact manifest|already read this session\. Memory files rarely change mid-session/, tool: "Read" },
16443
16358
  { kind: "improve_state_reread_deny", re: /Orchestrator state already read this session/, tool: "Read" },
16444
16359
  { kind: "env_reread_deny", re: /Environment files rarely change mid-session/, tool: "Read" },
@@ -16448,12 +16363,12 @@ var DENY_TEMPLATES = [
16448
16363
  { kind: "session_artifact_large_deny", re: /(?:Session transcript|Tool-result file) is large \(/, tool: "Read" },
16449
16364
  { kind: "session_artifact_generic_reread_deny", re: /already read this session\. Use `token-goat bash-output --file/, tool: "Read" },
16450
16365
  { kind: "truncated_read_deny", re: /File was truncated on last read \(>33K tokens\)/, tool: "Read" },
16451
- { kind: "doc_unchanged_deny", re: /is unchanged since last read\. Use `token-goat (?:section|read)/, tool: "Read" },
16452
- { kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?Use `token-goat (?:section|read)/, tool: "Read" },
16366
+ { kind: "doc_unchanged_deny", re: /is unchanged since last read\. (?:Use|Run) `token-goat (?:section|read|skeleton|json-query|yaml-query)/, tool: "Read" },
16367
+ { kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?(?:Use|Run) `token-goat (?:section|read|skeleton|json-query|yaml-query)/, tool: "Read" },
16453
16368
  { kind: "read_served_deny", re: /was already served in this session, byte for byte/, tool: "Read" },
16454
16369
  // 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.
16455
16370
  { kind: "range_reread_deny", re: /Lines \d+\.\.\d+ of [\s\S]*?was already read this session/, tool: "Read" },
16456
- { kind: "markdown_already_read_deny", re: /Markdown file already read this session\. Use `token-goat section/, tool: "Read" },
16371
+ { kind: "markdown_already_read_deny", re: /Markdown file already read this session\./, tool: "Read" },
16457
16372
  { kind: "read_count_deny", re: /(?:Read|Tried to read) this file \d+ times already/, tool: "Read" },
16458
16373
  // 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.
16459
16374
  { kind: "sequential_paging_deny", re: /Sequential line-range paging detected on /, tool: "Read" },
@@ -16543,9 +16458,9 @@ function toolResultText(content) {
16543
16458
  return "";
16544
16459
  }
16545
16460
  function listCorpusTranscripts(corpusDir) {
16546
- if (!fs34.existsSync(corpusDir)) return [];
16461
+ if (!fs33.existsSync(corpusDir)) return [];
16547
16462
  try {
16548
- const stat = fs34.statSync(corpusDir);
16463
+ const stat = fs33.statSync(corpusDir);
16549
16464
  if (stat.isFile()) {
16550
16465
  return corpusDir.endsWith(".jsonl") ? [corpusDir] : [];
16551
16466
  }
@@ -16555,13 +16470,13 @@ function listCorpusTranscripts(corpusDir) {
16555
16470
  const found = [];
16556
16471
  const walk = (dir, entries) => {
16557
16472
  for (const entry of entries) {
16558
- const full = path31.join(dir, entry.name);
16473
+ const full = path30.join(dir, entry.name);
16559
16474
  if (entry.name.endsWith(".jsonl")) {
16560
16475
  found.push(full);
16561
16476
  } else if (entry.isDirectory()) {
16562
16477
  let inner;
16563
16478
  try {
16564
- inner = fs34.readdirSync(full, { withFileTypes: true });
16479
+ inner = fs33.readdirSync(full, { withFileTypes: true });
16565
16480
  } catch {
16566
16481
  continue;
16567
16482
  }
@@ -16570,31 +16485,41 @@ function listCorpusTranscripts(corpusDir) {
16570
16485
  }
16571
16486
  };
16572
16487
  try {
16573
- walk(corpusDir, fs34.readdirSync(corpusDir, { withFileTypes: true }));
16488
+ walk(corpusDir, fs33.readdirSync(corpusDir, { withFileTypes: true }));
16574
16489
  } catch {
16575
16490
  }
16576
16491
  return found.sort();
16577
16492
  }
16493
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
16494
+ function modifiedSince(files, sinceMs) {
16495
+ return files.filter((file) => {
16496
+ try {
16497
+ return fs33.statSync(file).mtimeMs >= sinceMs;
16498
+ } catch {
16499
+ return false;
16500
+ }
16501
+ });
16502
+ }
16578
16503
  function defaultCorpusDir() {
16579
- const claudeDir = path31.join(claudeConfigDir(), "projects");
16580
- if (fs34.existsSync(claudeDir) && listCorpusTranscripts(claudeDir).length > 0) {
16504
+ const claudeDir = path30.join(claudeConfigDir(), "projects");
16505
+ if (fs33.existsSync(claudeDir) && listCorpusTranscripts(claudeDir).length > 0) {
16581
16506
  return claudeDir;
16582
16507
  }
16583
16508
  const home = os16.homedir();
16584
- const appData = process.env["APPDATA"] || path31.join(home, "AppData", "Roaming");
16509
+ const appData = process.env["APPDATA"] || path30.join(home, "AppData", "Roaming");
16585
16510
  const candidates = [
16586
16511
  claudeDir,
16587
- path31.join(appData, "Code", "User", "workspaceStorage"),
16588
- path31.join(appData, "Code - Insiders", "User", "workspaceStorage"),
16589
- path31.join(home, "Library", "Application Support", "Code", "User", "workspaceStorage"),
16590
- path31.join(home, "Library", "Application Support", "Code - Insiders", "User", "workspaceStorage"),
16591
- path31.join(home, ".config", "Code", "User", "workspaceStorage"),
16592
- path31.join(home, ".config", "Code - Insiders", "User", "workspaceStorage"),
16593
- path31.join(home, ".copilot", "session-state")
16512
+ path30.join(appData, "Code", "User", "workspaceStorage"),
16513
+ path30.join(appData, "Code - Insiders", "User", "workspaceStorage"),
16514
+ path30.join(home, "Library", "Application Support", "Code", "User", "workspaceStorage"),
16515
+ path30.join(home, "Library", "Application Support", "Code - Insiders", "User", "workspaceStorage"),
16516
+ path30.join(home, ".config", "Code", "User", "workspaceStorage"),
16517
+ path30.join(home, ".config", "Code - Insiders", "User", "workspaceStorage"),
16518
+ path30.join(home, ".copilot", "session-state")
16594
16519
  ];
16595
16520
  for (const cand of candidates) {
16596
16521
  try {
16597
- if (fs34.existsSync(cand) && listCorpusTranscripts(cand).length > 0) {
16522
+ if (fs33.existsSync(cand) && listCorpusTranscripts(cand).length > 0) {
16598
16523
  return cand;
16599
16524
  }
16600
16525
  } catch {
@@ -16602,11 +16527,13 @@ function defaultCorpusDir() {
16602
16527
  }
16603
16528
  return claudeDir;
16604
16529
  }
16605
- async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows) {
16530
+ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows, toolErrorAcc) {
16606
16531
  const isLane = filePath.split(/[\\/]/).includes("subagents");
16607
16532
  let laneFirstPrefix = null;
16608
16533
  let laneBriefBytes = -1;
16609
16534
  const toolNameById = /* @__PURE__ */ new Map();
16535
+ const modelById = /* @__PURE__ */ new Map();
16536
+ const bashCommandById = /* @__PURE__ */ new Map();
16610
16537
  const bashHeadById = /* @__PURE__ */ new Map();
16611
16538
  let pendingBashReread = [];
16612
16539
  const readCallById = /* @__PURE__ */ new Map();
@@ -16645,7 +16572,7 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16645
16572
  }
16646
16573
  pendingBashReread = keptBash;
16647
16574
  };
16648
- const stream = fs34.createReadStream(filePath);
16575
+ const stream = fs33.createReadStream(filePath);
16649
16576
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
16650
16577
  const streamFailure = new Promise((_, reject) => stream.on("error", reject));
16651
16578
  const consume = (async () => {
@@ -16709,11 +16636,15 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16709
16636
  if (!toolNameById.has(block["id"])) {
16710
16637
  toolNameById.set(block["id"], name);
16711
16638
  toolRollup(toolMap, name).calls += 1;
16639
+ const model = typeof message.model === "string" && message.model !== "" ? message.model : void 0;
16640
+ if (model !== void 0) modelById.set(block["id"], model);
16641
+ toolErrorAcc.countCall(name, model);
16712
16642
  const input = block["input"] !== null && typeof block["input"] === "object" ? block["input"] : {};
16713
16643
  let readNorm;
16714
16644
  let editNorm;
16715
16645
  if (name === "Bash" && typeof input["command"] === "string") {
16716
16646
  bashHeadById.set(block["id"], commandHead(input["command"]));
16647
+ bashCommandById.set(block["id"], input["command"]);
16717
16648
  } else if (name === "Read" && typeof input["file_path"] === "string") {
16718
16649
  readNorm = normalizeReadPath(input["file_path"]);
16719
16650
  const priorEpoch = readPathEpoch.get(readNorm);
@@ -16767,9 +16698,17 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16767
16698
  const name = toolNameById.get(id) ?? "(unknown)";
16768
16699
  addCategory(s.estimated.toolResults, bytes);
16769
16700
  const roll = toolRollup(toolMap, name);
16770
- if (!toolNameById.has(id)) roll.calls += 1;
16701
+ if (!toolNameById.has(id)) {
16702
+ roll.calls += 1;
16703
+ toolErrorAcc.countCall(name, void 0);
16704
+ }
16771
16705
  roll.resultBytes += bytes;
16772
16706
  roll.resultEstTokens += estimateTokensFromLength(bytes);
16707
+ if (block["is_error"] === true) {
16708
+ const denialKind = obj["toolDenialKind"];
16709
+ const command = bashCommandById.get(id);
16710
+ toolErrorAcc.countError(name, modelById.get(id), text, { ...typeof denialKind === "string" ? { denialKind } : {}, ...command !== void 0 ? { command } : {} });
16711
+ }
16773
16712
  if (name === "Edit" && block["is_error"] === true) {
16774
16713
  s.editErrorBaseline.totalErrors += 1;
16775
16714
  editErrorById.set(id, true);
@@ -16777,13 +16716,13 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16777
16716
  if (name === "Read" || name === "Skill") {
16778
16717
  const denyTemplate = DENY_TEMPLATES.find((t) => t.tool === name && t.re.test(text));
16779
16718
  if (denyTemplate !== void 0) {
16780
- const path42 = readPathById.get(id) ?? "";
16781
- const lastSlash = path42.lastIndexOf("/");
16719
+ const path41 = readPathById.get(id) ?? "";
16720
+ const lastSlash = path41.lastIndexOf("/");
16782
16721
  openDenies.push({
16783
16722
  kind: denyTemplate.kind,
16784
16723
  withheldBytes: parseWithheldBytes(denyTemplate.kind, text),
16785
- path: path42,
16786
- basename: lastSlash === -1 ? path42 : path42.slice(lastSlash + 1),
16724
+ path: path41,
16725
+ basename: lastSlash === -1 ? path41 : path41.slice(lastSlash + 1),
16787
16726
  callIndexAtDeny: perCall.length,
16788
16727
  compactEpochAtOpen: compactEpoch,
16789
16728
  compactEpochAtWindow: null,
@@ -16973,7 +16912,7 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
16973
16912
  if (isLane) {
16974
16913
  let agentType = "(none)";
16975
16914
  try {
16976
- const meta = JSON.parse(fs34.readFileSync(filePath.replace(/\.jsonl$/i, ".meta.json"), "utf8"));
16915
+ const meta = JSON.parse(fs33.readFileSync(filePath.replace(/\.jsonl$/i, ".meta.json"), "utf8"));
16977
16916
  if (typeof meta["agentType"] === "string" && meta["agentType"] !== "") agentType = meta["agentType"];
16978
16917
  } catch {
16979
16918
  }
@@ -17054,14 +16993,19 @@ function aggregateDenyOutcomes(rows) {
17054
16993
  return result.sort((a, b) => b.count - a.count || a.kind.localeCompare(b.kind));
17055
16994
  }
17056
16995
  async function auditSessionCorpus(opts = {}) {
17057
- const corpusDir = path31.resolve(opts.dir ?? defaultCorpusDir());
17058
- if (!fs34.existsSync(corpusDir)) {
16996
+ const corpusDir = path30.resolve(opts.dir ?? defaultCorpusDir());
16997
+ if (!fs33.existsSync(corpusDir)) {
17059
16998
  throw new Error(`session corpus directory not found: ${corpusDir}`);
17060
16999
  }
17061
- const files = listCorpusTranscripts(corpusDir);
17062
- if (files.length === 0) {
17000
+ const windowDays = opts.windowDays ?? 0;
17001
+ const allFiles = listCorpusTranscripts(corpusDir);
17002
+ if (allFiles.length === 0) {
17063
17003
  throw new Error(`no .jsonl session transcripts found under ${corpusDir}`);
17064
17004
  }
17005
+ const files = windowDays === 0 ? allFiles : modifiedSince(allFiles, Date.now() - windowDays * MS_PER_DAY);
17006
+ if (files.length === 0) {
17007
+ throw new Error(`no .jsonl session transcripts under ${corpusDir} were modified in the last ${windowDays} days`);
17008
+ }
17065
17009
  const started = Date.now();
17066
17010
  const summary = {
17067
17011
  corpusDir,
@@ -17096,8 +17040,11 @@ async function auditSessionCorpus(opts = {}) {
17096
17040
  denyOutcomes: [],
17097
17041
  editErrorBaseline: { totalEdits: 0, totalErrors: 0, rate: 0 },
17098
17042
  lineTypes: {},
17099
- omissionMarkers: { fires: 0, linesOmitted: 0 }
17043
+ omissionMarkers: { fires: 0, linesOmitted: 0 },
17044
+ windowDays,
17045
+ toolErrors: { byTool: [], byModel: [], unknownPrefixes: [] }
17100
17046
  };
17047
+ const toolErrorAcc = (await import("./token-goat-chunk-LEG3JNJ6.mjs")).newToolErrorAccumulator();
17101
17048
  const toolMap = /* @__PURE__ */ new Map();
17102
17049
  const attachmentMap = /* @__PURE__ */ new Map();
17103
17050
  const hookMap = /* @__PURE__ */ new Map();
@@ -17106,7 +17053,7 @@ async function auditSessionCorpus(opts = {}) {
17106
17053
  const denyRows = [];
17107
17054
  for (const file of files) {
17108
17055
  try {
17109
- await auditOneFile(file, summary, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows);
17056
+ await auditOneFile(file, summary, toolMap, attachmentMap, hookMap, laneObservations, bashHeadMap, denyRows, toolErrorAcc);
17110
17057
  summary.filesScanned += 1;
17111
17058
  } catch {
17112
17059
  summary.filesFailed += 1;
@@ -17146,6 +17093,7 @@ async function auditSessionCorpus(opts = {}) {
17146
17093
  summary.denyOutcomes = aggregateDenyOutcomes(denyRows);
17147
17094
  summary.editErrorBaseline.rate = summary.editErrorBaseline.totalEdits === 0 ? 0 : summary.editErrorBaseline.totalErrors / summary.editErrorBaseline.totalEdits;
17148
17095
  summary.calibration = computeCalibration(summary.estimated, summary.measured);
17096
+ summary.toolErrors = toolErrorAcc.finish();
17149
17097
  summary.runtimeMs = Date.now() - started;
17150
17098
  return summary;
17151
17099
  }
@@ -17266,17 +17214,17 @@ function formatSessionAudit(s) {
17266
17214
 
17267
17215
  // src/session_read.ts
17268
17216
  init_define_import_meta_env();
17269
- import * as fs35 from "node:fs";
17270
- import * as path32 from "node:path";
17217
+ import * as fs34 from "node:fs";
17218
+ import * as path31 from "node:path";
17271
17219
  import * as readline2 from "node:readline";
17272
17220
  function resolveSessionTranscript(arg, opts = {}) {
17273
17221
  if (arg !== void 0 && arg !== "") {
17274
- if (fs35.existsSync(arg) && fs35.statSync(arg).isFile()) return arg;
17222
+ if (fs34.existsSync(arg) && fs34.statSync(arg).isFile()) return arg;
17275
17223
  const projectRoot2 = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
17276
17224
  const dir = projectTranscriptsDir(projectRoot2);
17277
17225
  const byId = arg.endsWith(".jsonl") ? arg : `${arg}.jsonl`;
17278
- const candidate = path32.join(dir, byId);
17279
- if (fs35.existsSync(candidate) && fs35.statSync(candidate).isFile()) return candidate;
17226
+ const candidate = path31.join(dir, byId);
17227
+ if (fs34.existsSync(candidate) && fs34.statSync(candidate).isFile()) return candidate;
17280
17228
  return null;
17281
17229
  }
17282
17230
  const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
@@ -17334,7 +17282,7 @@ function toolCallsForBlocks(blocks) {
17334
17282
  return blocks.filter((b) => b.type === "tool_use" && b.name !== void 0).map((b) => b.name);
17335
17283
  }
17336
17284
  async function* streamTurns(transcriptPath) {
17337
- const input = fs35.createReadStream(transcriptPath, { encoding: "utf8" });
17285
+ const input = fs34.createReadStream(transcriptPath, { encoding: "utf8" });
17338
17286
  const rl = readline2.createInterface({ input, crlfDelay: Infinity });
17339
17287
  try {
17340
17288
  let lineNumber = 0;
@@ -17433,9 +17381,9 @@ function formatSessionSlice(turns) {
17433
17381
 
17434
17382
  // src/cli_bootstrap_audit.ts
17435
17383
  init_define_import_meta_env();
17436
- import * as fs36 from "node:fs";
17384
+ import * as fs35 from "node:fs";
17437
17385
  import * as os17 from "node:os";
17438
- import * as path33 from "node:path";
17386
+ import * as path32 from "node:path";
17439
17387
  import * as readline3 from "node:readline";
17440
17388
  var FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["description", "tools", "allowed-tools", "allowed_tools"]);
17441
17389
  function parseBudget(name, value) {
@@ -17444,7 +17392,7 @@ function parseBudget(name, value) {
17444
17392
  return value;
17445
17393
  }
17446
17394
  async function readFrontmatter(filePath) {
17447
- const stream = fs36.createReadStream(filePath, { encoding: "utf8" });
17395
+ const stream = fs35.createReadStream(filePath, { encoding: "utf8" });
17448
17396
  const lines = readline3.createInterface({ input: stream, crlfDelay: Infinity });
17449
17397
  let first = true;
17450
17398
  let inFrontmatter = false;
@@ -17490,44 +17438,44 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
17490
17438
  let logicalRoot;
17491
17439
  let rootIsLink;
17492
17440
  try {
17493
- rootIsLink = (await fs36.promises.lstat(root)).isSymbolicLink();
17494
- logicalRoot = await fs36.promises.realpath(root);
17441
+ rootIsLink = (await fs35.promises.lstat(root)).isSymbolicLink();
17442
+ logicalRoot = await fs35.promises.realpath(root);
17495
17443
  } catch (error) {
17496
17444
  if (error.code === "ENOENT") return entries;
17497
- diagnostics.push({ path: path33.resolve(root), reason: "unreadable root" });
17445
+ diagnostics.push({ path: path32.resolve(root), reason: "unreadable root" });
17498
17446
  return entries;
17499
17447
  }
17500
17448
  if (rootIsLink && !followLinks) {
17501
- diagnostics.push({ path: path33.resolve(root), reason: "external root link skipped (use --follow-links)" });
17449
+ diagnostics.push({ path: path32.resolve(root), reason: "external root link skipped (use --follow-links)" });
17502
17450
  return entries;
17503
17451
  }
17504
17452
  const canonicalKey = (candidate) => process.platform === "win32" ? candidate.toLocaleLowerCase() : candidate;
17505
17453
  const isContained = (candidate, allowedRoot) => {
17506
- const relative3 = path33.relative(canonicalKey(allowedRoot), canonicalKey(candidate));
17507
- return relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${path33.sep}`) && !path33.isAbsolute(relative3);
17454
+ const relative3 = path32.relative(canonicalKey(allowedRoot), canonicalKey(candidate));
17455
+ return relative3 === "" || relative3 !== ".." && !relative3.startsWith(`..${path32.sep}`) && !path32.isAbsolute(relative3);
17508
17456
  };
17509
17457
  async function visit(candidate, allowedRoot, topLevel = false) {
17510
17458
  let real;
17511
17459
  let candidateIsLink;
17512
17460
  try {
17513
- candidateIsLink = (await fs36.promises.lstat(candidate)).isSymbolicLink();
17514
- real = await fs36.promises.realpath(candidate);
17461
+ candidateIsLink = (await fs35.promises.lstat(candidate)).isSymbolicLink();
17462
+ real = await fs35.promises.realpath(candidate);
17515
17463
  } catch {
17516
- diagnostics.push({ path: path33.resolve(candidate), reason: "broken or unreadable link" });
17464
+ diagnostics.push({ path: path32.resolve(candidate), reason: "broken or unreadable link" });
17517
17465
  return;
17518
17466
  }
17519
17467
  const withinAllowedRoot = allowedRoot === void 0 ? isContained(real, logicalRoot) : isContained(real, allowedRoot);
17520
17468
  const canFollowExternal = followLinks && topLevel;
17521
17469
  if (!withinAllowedRoot && !canFollowExternal) {
17522
- diagnostics.push({ path: path33.resolve(candidate), reason: candidateIsLink ? "external link skipped (use --follow-links)" : "canonical target outside scan root" });
17470
+ diagnostics.push({ path: path32.resolve(candidate), reason: candidateIsLink ? "external link skipped (use --follow-links)" : "canonical target outside scan root" });
17523
17471
  return;
17524
17472
  }
17525
17473
  const trustedRoot = !withinAllowedRoot && canFollowExternal ? real : allowedRoot ?? real;
17526
17474
  let stat;
17527
17475
  try {
17528
- stat = await fs36.promises.stat(real);
17476
+ stat = await fs35.promises.stat(real);
17529
17477
  } catch {
17530
- diagnostics.push({ path: path33.resolve(candidate), reason: "unreadable" });
17478
+ diagnostics.push({ path: path32.resolve(candidate), reason: "unreadable" });
17531
17479
  return;
17532
17480
  }
17533
17481
  const canonical = process.platform === "win32" ? real.toLocaleLowerCase() : real;
@@ -17536,16 +17484,16 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
17536
17484
  visitedDirs.add(canonical);
17537
17485
  let children;
17538
17486
  try {
17539
- children = await fs36.promises.readdir(real, { withFileTypes: true });
17487
+ children = await fs35.promises.readdir(real, { withFileTypes: true });
17540
17488
  } catch {
17541
- diagnostics.push({ path: path33.resolve(candidate), reason: "unreadable directory" });
17489
+ diagnostics.push({ path: path32.resolve(candidate), reason: "unreadable directory" });
17542
17490
  return;
17543
17491
  }
17544
17492
  children.sort((a, b) => a.name.localeCompare(b.name));
17545
- for (const child of children) await visit(path33.join(real, child.name), trustedRoot);
17493
+ for (const child of children) await visit(path32.join(real, child.name), trustedRoot);
17546
17494
  return;
17547
17495
  }
17548
- if (!stat.isFile() || path33.extname(real).toLowerCase() !== ".md") return;
17496
+ if (!stat.isFile() || path32.extname(real).toLowerCase() !== ".md") return;
17549
17497
  if (seenFiles.has(canonical)) return;
17550
17498
  seenFiles.add(canonical);
17551
17499
  try {
@@ -17555,35 +17503,35 @@ async function scanMetadataRoot(root, kind, diagnostics, visitedDirs = /* @__PUR
17555
17503
  const toolsBytes = Buffer.byteLength(metadata.tools, "utf8");
17556
17504
  entries.push({
17557
17505
  kind,
17558
- path: path33.resolve(real),
17506
+ path: path32.resolve(real),
17559
17507
  description_bytes: descriptionBytes,
17560
17508
  tools_bytes: toolsBytes,
17561
17509
  metadata_bytes: descriptionBytes + toolsBytes,
17562
17510
  estimated_tokens: Math.floor((descriptionBytes + toolsBytes) / 4)
17563
17511
  });
17564
17512
  } catch {
17565
- diagnostics.push({ path: path33.resolve(candidate), reason: "unreadable file" });
17513
+ diagnostics.push({ path: path32.resolve(candidate), reason: "unreadable file" });
17566
17514
  }
17567
17515
  }
17568
17516
  try {
17569
- const children = await fs36.promises.readdir(logicalRoot, { withFileTypes: true });
17517
+ const children = await fs35.promises.readdir(logicalRoot, { withFileTypes: true });
17570
17518
  children.sort((a, b) => a.name.localeCompare(b.name));
17571
- for (const child of children) await visit(path33.join(logicalRoot, child.name), void 0, true);
17519
+ for (const child of children) await visit(path32.join(logicalRoot, child.name), void 0, true);
17572
17520
  } catch {
17573
- diagnostics.push({ path: path33.resolve(root), reason: "unreadable root" });
17521
+ diagnostics.push({ path: path32.resolve(root), reason: "unreadable root" });
17574
17522
  }
17575
17523
  return entries;
17576
17524
  }
17577
17525
  async function buildBootstrapAudit(opts = {}) {
17578
17526
  const project = resolveProjectRoot(opts.project === void 0 ? {} : { project: opts.project });
17579
- const home = path33.resolve(opts.home ?? os17.homedir());
17527
+ const home = path32.resolve(opts.home ?? os17.homedir());
17580
17528
  const context = buildStats(project, home, opts.project === void 0 ? [] : [opts.project]);
17581
17529
  const diagnostics = [];
17582
17530
  const top = opts.top === void 0 ? 10 : parseBudget("--top", opts.top) ?? 10;
17583
17531
  const visitedDirs = /* @__PURE__ */ new Set();
17584
17532
  const seenFiles = /* @__PURE__ */ new Set();
17585
- const agents = await scanMetadataRoot(path33.join(claudeConfigDir(home), "agents"), "agent", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
17586
- const skills = await scanMetadataRoot(path33.join(claudeConfigDir(home), "skills"), "skill", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
17533
+ const agents = await scanMetadataRoot(path32.join(claudeConfigDir(home), "agents"), "agent", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
17534
+ const skills = await scanMetadataRoot(path32.join(claudeConfigDir(home), "skills"), "skill", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
17587
17535
  const rankedEntries = [...agents, ...skills].sort((a, b) => b.metadata_bytes - a.metadata_bytes || a.path.localeCompare(b.path));
17588
17536
  const largestTotal = rankedEntries.length;
17589
17537
  const largest = rankedEntries.slice(0, top);
@@ -17650,8 +17598,8 @@ async function runBootstrapAudit(opts = {}) {
17650
17598
 
17651
17599
  // src/cli_memory.ts
17652
17600
  init_define_import_meta_env();
17653
- import * as fs37 from "node:fs";
17654
- import * as path34 from "node:path";
17601
+ import * as fs36 from "node:fs";
17602
+ import * as path33 from "node:path";
17655
17603
  function removeExactDupLines(text, report) {
17656
17604
  if (report.exactDupLines.length === 0) return text;
17657
17605
  const toRemove = new Set(report.exactDupLines.map(([, dupIdx]) => dupIdx));
@@ -17709,7 +17657,7 @@ function printReport(reports, clusters) {
17709
17657
  } else {
17710
17658
  for (const cluster of clusters) {
17711
17659
  w(
17712
- ` [${cluster.method}, similarity ${cluster.similarity}, ~${cluster.tokens} tok] ${cluster.members.map((m) => displaySafeText(path34.basename(m))).join(", ")}
17660
+ ` [${cluster.method}, similarity ${cluster.similarity}, ~${cluster.tokens} tok] ${cluster.members.map((m) => displaySafeText(path33.basename(m))).join(", ")}
17713
17661
  `
17714
17662
  );
17715
17663
  }
@@ -17724,7 +17672,7 @@ async function runMemoryCommand(opts = {}) {
17724
17672
  const claudeMds = findClaudeMdFiles(projectRoot);
17725
17673
  const reports = auditClaudeMd(claudeMds);
17726
17674
  const memoryMdPath = findMemoryMd(projectRoot);
17727
- const clusters = memoryMdPath !== null ? await findContentDuplicates(path34.dirname(memoryMdPath)) : [];
17675
+ const clusters = memoryMdPath !== null ? await findContentDuplicates(path33.dirname(memoryMdPath)) : [];
17728
17676
  printReport(reports, clusters);
17729
17677
  if (opts.fix !== true) return;
17730
17678
  const changes = [];
@@ -17732,7 +17680,7 @@ async function runMemoryCommand(opts = {}) {
17732
17680
  if (report.exactDupLines.length === 0) continue;
17733
17681
  let before;
17734
17682
  try {
17735
- before = fs37.readFileSync(report.path, "utf-8");
17683
+ before = fs36.readFileSync(report.path, "utf-8");
17736
17684
  } catch {
17737
17685
  continue;
17738
17686
  }
@@ -17770,8 +17718,8 @@ async function runMemoryCommand(opts = {}) {
17770
17718
 
17771
17719
  // src/cli_waste.ts
17772
17720
  init_define_import_meta_env();
17773
- import * as fs38 from "node:fs";
17774
- import * as path35 from "node:path";
17721
+ import * as fs37 from "node:fs";
17722
+ import * as path34 from "node:path";
17775
17723
  function printCopilotReport(report) {
17776
17724
  const w = (text) => {
17777
17725
  process.stdout.write(text);
@@ -17825,8 +17773,9 @@ function printCopilotReport(report) {
17825
17773
  } else {
17826
17774
  for (const server of mcp.servers) {
17827
17775
  const tokens = server.estimatedTokens.toLocaleString();
17776
+ const calls = report.mcpCalls === null ? "" : `, ${countNoun(report.mcpCalls[server.serverName] ?? 0, "call")} this session`;
17828
17777
  w(` ${server.serverName}: ${countNoun(server.toolCount, "tool")}, `);
17829
- w(`${formatBytes(server.definitionBytes)}, ~${tokens} tok
17778
+ w(`${formatBytes(server.definitionBytes)}, ~${tokens} tok${calls}
17830
17779
  `);
17831
17780
  }
17832
17781
  const mcpTokens = mcp.servers.reduce((sum, server) => sum + server.estimatedTokens, 0);
@@ -17846,6 +17795,15 @@ function printCopilotReport(report) {
17846
17795
  w(" tool-filter flags above still can.\n");
17847
17796
  }
17848
17797
  w(" Dropping a server saves its line above on every request for the rest of the session.\n");
17798
+ const unused = unusedMcpServers(report);
17799
+ if (unused === null) {
17800
+ w(" This log records no tool calls, so it cannot say which servers went unused.\n");
17801
+ } else {
17802
+ for (const server of unused) w(` Never called this session: \`copilot mcp disable ${server.serverName}\` drops it from future sessions.
17803
+ `);
17804
+ if (unused.length > 0) w(` ${MCP_DISABLE_NOTE}
17805
+ `);
17806
+ }
17849
17807
  }
17850
17808
  if (mcp.unreadable > 0) {
17851
17809
  const verb = mcp.unreadable === 1 ? "is" : "are";
@@ -17972,8 +17930,8 @@ function printResidentContext(resident, w) {
17972
17930
  async function runWasteCommand(opts = {}) {
17973
17931
  const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
17974
17932
  if (opts.transcript !== void 0) {
17975
- const resolvedPath = path35.resolve(opts.transcript);
17976
- if (!fs38.existsSync(resolvedPath)) {
17933
+ const resolvedPath = path34.resolve(opts.transcript);
17934
+ if (!fs37.existsSync(resolvedPath)) {
17977
17935
  if (opts.copilot === true) {
17978
17936
  const detail = `Copilot session event log not found: ${resolvedPath}`;
17979
17937
  if (opts.json === true) {
@@ -18013,7 +17971,7 @@ async function runWasteCommand(opts = {}) {
18013
17971
  }
18014
17972
  if (opts.copilot === true) {
18015
17973
  const eventsPath = opts.project !== void 0 ? findLatestCopilotSession({ projectRoot }) : findLatestCopilotSession({ projectRoot }) ?? findLatestCopilotSession();
18016
- if (eventsPath === null || !fs38.existsSync(eventsPath)) {
17974
+ if (eventsPath === null || !fs37.existsSync(eventsPath)) {
18017
17975
  const detail = eventsPath === null ? "no Copilot CLI session found under <copilot-home>/session-state" : `Copilot session event log not found: ${eventsPath}`;
18018
17976
  if (opts.json === true) {
18019
17977
  process.stdout.write(`${displaySafeJson({ error: detail }, 0)}
@@ -18070,13 +18028,13 @@ async function runWasteCommand(opts = {}) {
18070
18028
 
18071
18029
  // src/cli_mcp_audit.ts
18072
18030
  init_define_import_meta_env();
18073
- import * as fs39 from "node:fs";
18031
+ import * as fs38 from "node:fs";
18074
18032
  import * as os18 from "node:os";
18075
- import * as path36 from "node:path";
18033
+ import * as path35 from "node:path";
18076
18034
  function readMcpJsonFile(configPath2) {
18077
18035
  try {
18078
- if (!fs39.existsSync(configPath2)) return null;
18079
- const content = fs39.readFileSync(configPath2, "utf-8");
18036
+ if (!fs38.existsSync(configPath2)) return null;
18037
+ const content = fs38.readFileSync(configPath2, "utf-8");
18080
18038
  const parsed = JSON.parse(content);
18081
18039
  const servers = parsed && typeof parsed === "object" ? parsed.mcpServers ?? ("servers" in parsed ? null : parsed) : null;
18082
18040
  return servers && typeof servers === "object" ? servers : null;
@@ -18092,8 +18050,8 @@ function driveLetterCaseVariants(p) {
18092
18050
  }
18093
18051
  function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
18094
18052
  try {
18095
- if (!fs39.existsSync(claudeJsonPath)) return null;
18096
- const parsed = JSON.parse(fs39.readFileSync(claudeJsonPath, "utf-8"));
18053
+ if (!fs38.existsSync(claudeJsonPath)) return null;
18054
+ const parsed = JSON.parse(fs38.readFileSync(claudeJsonPath, "utf-8"));
18097
18055
  const projects = parsed && typeof parsed === "object" ? parsed.projects : null;
18098
18056
  if (!projects || typeof projects !== "object") return null;
18099
18057
  const slashForms = [projectRoot, projectRoot.replace(/\\/g, "/"), projectRoot.replace(/\//g, "\\")];
@@ -18111,9 +18069,9 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
18111
18069
  }
18112
18070
  }
18113
18071
  function discoverMcpConfig(projectRoot, home) {
18114
- const mcpJsonPath = path36.join(projectRoot, ".mcp.json");
18115
- const claudeJsonPath = path36.join(home, ".claude.json");
18116
- const copilotJsonPath = path36.join(home, ".copilot", "mcp-config.json");
18072
+ const mcpJsonPath = path35.join(projectRoot, ".mcp.json");
18073
+ const claudeJsonPath = path35.join(home, ".claude.json");
18074
+ const copilotJsonPath = path35.join(home, ".copilot", "mcp-config.json");
18117
18075
  const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
18118
18076
  const fromMcpJson = readMcpJsonFile(mcpJsonPath);
18119
18077
  if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
@@ -18434,7 +18392,7 @@ function cmdWaste(opts = {}) {
18434
18392
  });
18435
18393
  }
18436
18394
  async function cmdAudit(opts = {}) {
18437
- const { runAuditCommand } = await import("./token-goat-chunk-25NRXKIW.mjs");
18395
+ const { runAuditCommand } = await import("./token-goat-chunk-IHKZ3QNA.mjs");
18438
18396
  return runAuditCommand({
18439
18397
  ...opts.project !== void 0 ? { project: opts.project } : {},
18440
18398
  ...opts.transcript !== void 0 ? { transcript: opts.transcript } : {},
@@ -18444,10 +18402,15 @@ async function cmdAudit(opts = {}) {
18444
18402
  async function cmdSessionAudit(opts = {}) {
18445
18403
  let summary;
18446
18404
  try {
18447
- summary = await auditSessionCorpus({ ...opts.dir !== void 0 ? { dir: opts.dir } : {} });
18405
+ summary = await auditSessionCorpus({ ...opts.dir !== void 0 ? { dir: opts.dir } : {}, ...opts.windowDays !== void 0 ? { windowDays: opts.windowDays } : {} });
18448
18406
  } catch (err2) {
18449
18407
  throw new CliError(err2 instanceof Error ? err2.message : String(err2));
18450
18408
  }
18409
+ if (opts.toolErrors === true) {
18410
+ const { formatToolErrorCensus } = await import("./token-goat-chunk-LEG3JNJ6.mjs");
18411
+ out(opts.json === true ? displaySafeJson({ filesScanned: summary.filesScanned, windowDays: summary.windowDays, ...summary.toolErrors }, 0) : formatToolErrorCensus(summary.toolErrors, summary));
18412
+ return;
18413
+ }
18451
18414
  out(opts.json === true ? displaySafeJson(summary, 0) : formatSessionAudit(summary));
18452
18415
  }
18453
18416
  async function cmdSessionOutline(sessionIdOrPath, opts = {}) {
@@ -18467,7 +18430,7 @@ ${formatSessionOutline(turns)}`;
18467
18430
  }
18468
18431
  function sessionTranscriptSize(transcriptPath) {
18469
18432
  try {
18470
- return fs40.statSync(transcriptPath).size;
18433
+ return fs39.statSync(transcriptPath).size;
18471
18434
  } catch {
18472
18435
  return 0;
18473
18436
  }
@@ -18512,10 +18475,10 @@ function cmdStatusline(opts = {}) {
18512
18475
  }
18513
18476
  function cmdHintStats(opts = {}) {
18514
18477
  if (opts.markEffective !== void 0 && !isHintCategory(opts.markEffective)) {
18515
- throw new CliError(`--mark-effective must be one of: bash_redirect, bash_recall, read_reread_dedup, read_structural_nav, edit_reread_suggest (got: ${opts.markEffective})`);
18478
+ throw new CliError(`--mark-effective must be one of: ${HINT_CATEGORIES.join(", ")} (got: ${opts.markEffective})`);
18516
18479
  }
18517
18480
  if (opts.markIneffective !== void 0 && !isHintCategory(opts.markIneffective)) {
18518
- throw new CliError(`--mark-ineffective must be one of: bash_redirect, bash_recall, read_reread_dedup, read_structural_nav, edit_reread_suggest (got: ${opts.markIneffective})`);
18481
+ throw new CliError(`--mark-ineffective must be one of: ${HINT_CATEGORIES.join(", ")} (got: ${opts.markIneffective})`);
18519
18482
  }
18520
18483
  runHintStatsCommand({
18521
18484
  ...opts.json === true ? { json: true } : {},
@@ -18527,14 +18490,14 @@ function cmdHintStats(opts = {}) {
18527
18490
 
18528
18491
  // src/cli_skills.ts
18529
18492
  init_define_import_meta_env();
18530
- import * as fs41 from "node:fs";
18531
- import * as path37 from "node:path";
18493
+ import * as fs40 from "node:fs";
18494
+ import * as path36 from "node:path";
18532
18495
  async function cmdSkillBody(name, opts) {
18533
18496
  const filePath = await getSkillFilePath(name);
18534
18497
  if (filePath === null) {
18535
18498
  throw new CliError(`skill '${name}' not found`);
18536
18499
  }
18537
- const body = decodeSource(fs41.readFileSync(filePath));
18500
+ const body = decodeSource(fs40.readFileSync(filePath));
18538
18501
  if (opts.compact === true) {
18539
18502
  const emitted = extractCompactFromMarker(body) ?? body;
18540
18503
  out(emitted);
@@ -18561,7 +18524,7 @@ async function cmdSkillCompact(name, opts) {
18561
18524
  }
18562
18525
  let body2;
18563
18526
  try {
18564
- body2 = decodeSource(fs41.readFileSync(filePath));
18527
+ body2 = decodeSource(fs40.readFileSync(filePath));
18565
18528
  } catch {
18566
18529
  unresolvable++;
18567
18530
  continue;
@@ -18595,19 +18558,19 @@ async function cmdSkillCompact(name, opts) {
18595
18558
  if (!opts.path.trim()) {
18596
18559
  throw new CliError("--path cannot be empty");
18597
18560
  }
18598
- if (!fs41.existsSync(opts.path)) {
18561
+ if (!fs40.existsSync(opts.path)) {
18599
18562
  throw new CliError(`skill file not found: ${opts.path}`);
18600
18563
  }
18601
18564
  try {
18602
- body = fs41.readFileSync(opts.path, "utf-8");
18565
+ body = fs40.readFileSync(opts.path, "utf-8");
18603
18566
  } catch (e) {
18604
18567
  if (e.code === "ENOENT") {
18605
18568
  throw new CliError(`skill file not found: ${opts.path}`);
18606
18569
  }
18607
18570
  throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
18608
18571
  }
18609
- cacheName = name ?? path37.basename(path37.dirname(path37.resolve(opts.path)));
18610
- sourcePath = path37.resolve(opts.path);
18572
+ cacheName = name ?? path36.basename(path36.dirname(path36.resolve(opts.path)));
18573
+ sourcePath = path36.resolve(opts.path);
18611
18574
  } else {
18612
18575
  if (name === void 0 || !name.trim()) {
18613
18576
  throw new CliError("skill-compact requires a <name> or --path <file>");
@@ -18616,7 +18579,7 @@ async function cmdSkillCompact(name, opts) {
18616
18579
  if (filePath === null) {
18617
18580
  throw new CliError(`skill '${name}' not found`);
18618
18581
  }
18619
- body = fs41.readFileSync(filePath, "utf-8");
18582
+ body = fs40.readFileSync(filePath, "utf-8");
18620
18583
  cacheName = name;
18621
18584
  sourcePath = filePath;
18622
18585
  }
@@ -18748,8 +18711,8 @@ async function cmdSkillDiff(name) {
18748
18711
  }
18749
18712
  const newer = versions[0];
18750
18713
  const older = versions[1];
18751
- const newerBody = await fs41.promises.readFile(path37.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
18752
- const olderBody = await fs41.promises.readFile(path37.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
18714
+ const newerBody = await fs40.promises.readFile(path36.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
18715
+ const olderBody = await fs40.promises.readFile(path36.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
18753
18716
  if (newerBody === null || olderBody === null) {
18754
18717
  out(`a cached version of '${name}' was evicted while diffing -- try again`);
18755
18718
  return;
@@ -18778,7 +18741,7 @@ async function cmdSkillSection(nameHeading, headingArg) {
18778
18741
  if (!filePath) {
18779
18742
  throw new CliError(`skill '${skillName}' not found`);
18780
18743
  }
18781
- const body = decodeSource(fs41.readFileSync(filePath));
18744
+ const body = decodeSource(fs40.readFileSync(filePath));
18782
18745
  const extracted = extractNamedSection(body, heading);
18783
18746
  if (!extracted) {
18784
18747
  const allHeadings = listSections(filePath);
@@ -18801,8 +18764,8 @@ init_define_import_meta_env();
18801
18764
 
18802
18765
  // src/cli_stats.ts
18803
18766
  init_define_import_meta_env();
18804
- import * as fs42 from "node:fs";
18805
- import * as path38 from "node:path";
18767
+ import * as fs41 from "node:fs";
18768
+ import * as path37 from "node:path";
18806
18769
  function writeRaw(text) {
18807
18770
  const payload = colorStdout() ? text : stripAnsiEscapes(text);
18808
18771
  process.stdout.write(ensureNewline(payload));
@@ -18814,7 +18777,7 @@ function formatTopFiles(ranked) {
18814
18777
  if (ranked.length === 0) return "";
18815
18778
  const lines = ["Top files this session:"];
18816
18779
  for (const { path: filePath, count } of ranked) {
18817
- const basename10 = path38.basename(filePath);
18780
+ const basename10 = path37.basename(filePath);
18818
18781
  lines.push(` ${count.toString().padStart(3)}x ${basename10} (${filePath})`);
18819
18782
  }
18820
18783
  return lines.join("\n");
@@ -18832,11 +18795,11 @@ function renderTopSessionFiles(topN = 5) {
18832
18795
  function renderTopSessionFilesFromDisk(topN = 5, overrideSessionsDir) {
18833
18796
  try {
18834
18797
  const dir = overrideSessionsDir ?? sessionsDir();
18835
- if (!fs42.existsSync(dir)) return "";
18836
- 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);
18798
+ if (!fs41.existsSync(dir)) return "";
18799
+ const files = fs41.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, mtime: fs41.statSync(path37.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).slice(0, 3);
18837
18800
  for (const { name } of files) {
18838
18801
  try {
18839
- const raw = fs42.readFileSync(path38.join(dir, name), "utf-8");
18802
+ const raw = fs41.readFileSync(path37.join(dir, name), "utf-8");
18840
18803
  const data = JSON.parse(raw);
18841
18804
  const filesList = data["files"];
18842
18805
  if (!Array.isArray(filesList)) continue;
@@ -18971,6 +18934,19 @@ function getNewestSessionFiles() {
18971
18934
  const filesArr = Array.isArray(raw["files"]) ? raw["files"] : [];
18972
18935
  return { id: newest.id, sessionCount: blobs.length, filesArr };
18973
18936
  }
18937
+ function emitHistoryOutput(allItems, limit, json, emptyMessage, printTable) {
18938
+ const sorted = allItems.sort((a, b) => b.storedAt - a.storedAt);
18939
+ const items = capAndNote(sorted, limit);
18940
+ if (json === true) {
18941
+ process.stdout.write(displaySafeJson(items) + "\n");
18942
+ return;
18943
+ }
18944
+ if (items.length === 0) {
18945
+ process.stdout.write(emptyMessage);
18946
+ return;
18947
+ }
18948
+ printTable(items);
18949
+ }
18974
18950
  function cmdBashHistory(opts) {
18975
18951
  const limit = parseLimitOpt("bash-history", opts.limit);
18976
18952
  const blobs = listBlobs(BASH_OUTPUT_SUBDIR);
@@ -18985,24 +18961,17 @@ function cmdBashHistory(opts) {
18985
18961
  exitCode: typeof v["exitCode"] === "number" ? v["exitCode"] : -1,
18986
18962
  sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
18987
18963
  };
18988
- }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
18989
- const items = capAndNote(allItems, limit);
18990
- if (opts.json === true) {
18991
- process.stdout.write(displaySafeJson(items) + "\n");
18992
- return;
18993
- }
18994
- if (items.length === 0) {
18995
- process.stdout.write("No bash output entries cached.\n");
18996
- return;
18997
- }
18998
- process.stdout.write(`${pad("id", 18)} ${pad("bytes", 8)} ${pad("exit", 4)} command
18964
+ }).filter((x) => x !== null);
18965
+ emitHistoryOutput(allItems, limit, opts.json, "No bash output entries cached.\n", (items) => {
18966
+ process.stdout.write(`${pad("id", 18)} ${pad("bytes", 8)} ${pad("exit", 4)} command
18999
18967
  `);
19000
- for (const item of items) {
19001
- const flatCommand = displaySafeText(item.command);
19002
- const preview = flatCommand.length > 80 ? flatCommand.slice(0, 77) + "..." : flatCommand;
19003
- process.stdout.write(`${pad(displaySafeText(item.id), 18)} ${pad(String(item.sizeBytes), 8)} ${pad(String(item.exitCode), 4)} ${preview}
18968
+ for (const item of items) {
18969
+ const flatCommand = displaySafeText(item.command);
18970
+ const preview = flatCommand.length > 80 ? flatCommand.slice(0, 77) + "..." : flatCommand;
18971
+ process.stdout.write(`${pad(displaySafeText(item.id), 18)} ${pad(String(item.sizeBytes), 8)} ${pad(String(item.exitCode), 4)} ${preview}
19004
18972
  `);
19005
- }
18973
+ }
18974
+ });
19006
18975
  }
19007
18976
  function cmdWebHistory(opts) {
19008
18977
  const limit = parseLimitOpt("web-history", opts.limit);
@@ -19016,22 +18985,15 @@ function cmdWebHistory(opts) {
19016
18985
  bytes: typeof v["content"] === "string" ? Buffer.byteLength(v["content"], "utf8") : 0,
19017
18986
  storedAt: mtime
19018
18987
  };
19019
- }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
19020
- const items = capAndNote(allItems, limit);
19021
- if (opts.json === true) {
19022
- process.stdout.write(displaySafeJson(items) + "\n");
19023
- return;
19024
- }
19025
- if (items.length === 0) {
19026
- process.stdout.write("No web output entries cached.\n");
19027
- return;
19028
- }
19029
- process.stdout.write(`${pad("id", 18)} ${pad("bytes", 8)} url
18988
+ }).filter((x) => x !== null);
18989
+ emitHistoryOutput(allItems, limit, opts.json, "No web output entries cached.\n", (items) => {
18990
+ process.stdout.write(`${pad("id", 18)} ${pad("bytes", 8)} url
19030
18991
  `);
19031
- for (const item of items) {
19032
- process.stdout.write(`${pad(displaySafeText(item.id), 18)} ${pad(String(item.bytes), 8)} ${displaySafeText(item.url)}
18992
+ for (const item of items) {
18993
+ process.stdout.write(`${pad(displaySafeText(item.id), 18)} ${pad(String(item.bytes), 8)} ${displaySafeText(item.url)}
19033
18994
  `);
19034
- }
18995
+ }
18996
+ });
19035
18997
  }
19036
18998
  function cmdMcpHistory(opts) {
19037
18999
  const limit = parseLimitOpt("mcp-history", opts.limit);
@@ -19047,22 +19009,15 @@ function cmdMcpHistory(opts) {
19047
19009
  storedAt: typeof v["storedAt"] === "number" ? v["storedAt"] : mtime,
19048
19010
  sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
19049
19011
  };
19050
- }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
19051
- const items = capAndNote(allItems, limit);
19052
- if (opts.json === true) {
19053
- process.stdout.write(displaySafeJson(items) + "\n");
19054
- return;
19055
- }
19056
- if (items.length === 0) {
19057
- process.stdout.write("No mcp output entries cached.\n");
19058
- return;
19059
- }
19060
- process.stdout.write(`${pad("id", 18)} ${pad("bytes", 8)} tool
19012
+ }).filter((x) => x !== null);
19013
+ emitHistoryOutput(allItems, limit, opts.json, "No mcp output entries cached.\n", (items) => {
19014
+ process.stdout.write(`${pad("id", 18)} ${pad("bytes", 8)} tool
19061
19015
  `);
19062
- for (const item of items) {
19063
- process.stdout.write(`${pad(displaySafeText(item.id), 18)} ${pad(String(item.sizeBytes), 8)} ${displaySafeText(item.toolName)}
19016
+ for (const item of items) {
19017
+ process.stdout.write(`${pad(displaySafeText(item.id), 18)} ${pad(String(item.sizeBytes), 8)} ${displaySafeText(item.toolName)}
19064
19018
  `);
19065
- }
19019
+ }
19020
+ });
19066
19021
  }
19067
19022
  function cmdCleanCache(opts) {
19068
19023
  const removed = {};
@@ -19309,7 +19264,14 @@ function registerSessionCommands(program2, guard) {
19309
19264
  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));
19310
19265
  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));
19311
19266
  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));
19312
- 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));
19267
+ 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("--window-days <days>", "only transcripts whose file was modified in the last N days (0 = all time)", "0").option("--tool-errors", "print only the failed-tool-call report: calls, errors, expected failures by reason and unknown ones, per tool and per model, with the top unknown error prefixes").option("--json", "output JSON").action(guard(
19268
+ (opts) => cmdSessionAudit({
19269
+ ...opts.dir !== void 0 ? { dir: opts.dir } : {},
19270
+ ...opts.json === true ? { json: true } : {},
19271
+ ...opts.toolErrors === true ? { toolErrors: true } : {},
19272
+ windowDays: requireNonNegativeInt("--window-days", opts.windowDays)
19273
+ })
19274
+ ));
19313
19275
  program2.command("mcp-audit").description("MCP server schema cost-vs-usage report: estimate per-server token cost from cached tool calls").option("--project <path>", "project root to analyze").option("--json", "output JSON").action(guard(cmdMcpAudit));
19314
19276
  program2.command("recall [query]").description("search across every cached bash-output, web-output, and mcp-output entry (full-text); with no query, list them newest-first").option("--type <type>", "filter to one cache type: bash, web, or mcp").option("--limit <n>", "max results to return (default: 10)").option("--json", "output JSON").action(guard(cmdRecall));
19315
19277
  program2.command("hint-stats").description("per-category efficacy report for token-goat's discretionary hint hooks (emitted/acted-on/suppression)").option("--json", "output JSON").option("--reset", "clear all tracked emissions and manual marks").option("--mark-effective <category>", 'record a manual "effective" vote for a hint category').option("--mark-ineffective <category>", 'record a manual "ineffective" vote for a hint category').action(guard(cmdHintStats));
@@ -19392,7 +19354,7 @@ function registerSessionCommands(program2, guard) {
19392
19354
  init_define_import_meta_env();
19393
19355
  var NO_SERVER_HANDLER_REASON = "token-goat has no registered server-side handler for this event (zero registerHook('notification'|'stop', ...) call sites in src/) -- wiring it client-side would currently be a no-op";
19394
19356
  var NO_POST_COMPACT_EVENT_REASON = "post_compact is a Claude Code event (hook_event_name PostCompact, carrying compact_summary); no equivalent has been confirmed for this harness, so it is left unwired rather than guessed at";
19395
- var NO_SEPARATE_FAILURE_EVENT_REASON = "post_tool_use_failure exists because Copilot CLI routes a failed tool result to a separate postToolUseFailure event instead of postToolUse. No separate failure event is wired for this harness, so a failed tool call either arrives on post_tool_use like any other result or is not seen; which of the two has not been checked here";
19357
+ var NO_SEPARATE_FAILURE_EVENT_REASON = "post_tool_use_failure exists because Copilot CLI (postToolUseFailure) and Claude Code (PostToolUseFailure) route a failed tool result to a separate event instead of their post-tool one. No separate failure event is wired for this harness, so a failed tool call either arrives on post_tool_use like any other result or is not seen; which of the two has not been checked here";
19396
19358
  var COPILOT_NO_POST_COMPACT_REASON = "Copilot CLI has no post-compaction hook: its HookType enum (schemas/api.schema.json, 1.0.79 and 1.0.80) declares preCompact and no postCompact, and both preCompact call sites in app.js await the hook and never assign its result. Whether the summary is reachable another way is open. app.js does emit session.compaction_complete carrying summaryContent, and the session event writer subscribes to '*' -- but emit() vs emitEphemeral() does NOT gate that writer: dispatchEventHandlers runs outside the ephemeral branch in emitInternal, on('*') early-returns past every filter, and the writer's callback applies no filter of its own before handing the JSON to native recordEventJson. The durable-vs-ephemeral decision is in Rust (api_session_event_writer.rs) and was not readable. Nor is the event uniformly summary-bearing: two of the four emit sites are the branches taken when no summary exists, summaryContent is optional in the schema, and the relay session class emits the same event via emitEphemeral. No compaction has ever been observed on the machines checked, so nothing empirical anchors any of it. Context can be written back on the next turn through userPromptSubmitted additionalContext";
19397
19359
  var VERIFICATION_RANK = {
19398
19360
  dogfooded: 0,
@@ -19411,9 +19373,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
19411
19373
  sourceFile: "src/install.ts (HOOK_EVENT_MAP)",
19412
19374
  verification: "dogfooded",
19413
19375
  verificationNote: "The harness this repo is developed and released under; CLAUDE.md requires every CLI/hook change be run against the built binary here before it ships.",
19414
- implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "post_compact", "user_prompt_submit", "subagent_stop", "session_start"]),
19376
+ implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "post_tool_use_failure", "pre_compact", "post_compact", "user_prompt_submit", "subagent_stop", "session_start"]),
19415
19377
  reasons: [
19416
- { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
19417
19378
  { events: ["notification", "stop"], reason: NO_SERVER_HANDLER_REASON }
19418
19379
  ]
19419
19380
  },
@@ -19450,55 +19411,7 @@ var BRIDGE_CAPABILITY_MATRIX = [
19450
19411
  }
19451
19412
  ]
19452
19413
  },
19453
- /**
19454
- * What Copilot CLI's wired events can actually carry, read from the shipping 1.0.80 bundle
19455
- * rather than from its hooks reference page. A row's `implemented` set says an event is wired;
19456
- * it deliberately says nothing about how much of that event's response the harness honors, and
19457
- * for Copilot the difference matters:
19458
- *
19459
- * - `pre_tool_use` deny is real, and the reason text reaches the model. The native string table
19460
- * (runtime.node offset 98057208) carries `permissionDecision`, `permissionDecisionReason`, the
19461
- * default "No reason provided." and the formatter "Denied by preToolUse hook: " as distinct
19462
- * literals, and app.js sets `{textResultForLlm: reason, resultType: "denied"}` per denial and
19463
- * filters that call out of execution. This was previously only an inference drawn from an
19464
- * incident comment; it is now confirmed. The fail-closed "(hook errored)" deny-all strings sit
19465
- * beside these, so that incident was behavior layered on top of a working deny handler, not a
19466
- * substitute for one.
19467
- * - `post_tool_use` honors `modifiedResult`, so compression, injection fencing and image shrink
19468
- * do reach the model: `postToolExecution` (app.js offset 2043150) assigns the returned
19469
- * `toolResultJson` onto the tool result in place.
19470
- * - `post_tool_use` drops `additionalContext` on the JS path -- no supplier for the
19471
- * `onAdditionalContext` callback anywhere in the bundle, and no `additional_contexts` key in
19472
- * that event's native return payload, unlike its pre-tool sibling. Whether native folds it
19473
- * into the returned result instead was not verified.
19474
- * - Failed tool calls never reach `post_tool_use` at all. Copilot routes them to a separate
19475
- * `postToolUseFailure` event, which is handed only a stringified error and honors only
19476
- * `additionalContext`; `modifiedResult` is documented as not honored there. So on Copilot the
19477
- * output of a failed tool call reaches the model unfenced, uncompressed and unshrunk, and no
19478
- * response shape token-goat's shim can emit changes that. Known gap, deliberately recorded.
19479
- * What `additionalContext` does on that event is no longer doc-derived: app.js offset 2043380
19480
- * reads the native processor's return and, when it carries `additionalContext`, either folds
19481
- * it into `textResultForLlm` (if `appendFailureContextToToolResult` is set) or formats it and
19482
- * pushes `{content, source: "system"}` onto `toolResult.newMessages`. So the channel is real
19483
- * and model-visible -- it just carries advice alongside the failure, never a replacement for
19484
- * it. It is wired now, as its own `post_tool_use_failure` event rather than a reuse of
19485
- * `post_tool_use`: routing failures through the success event would let success-path handlers
19486
- * mark a file as successfully read when the read failed. Because the channel spends tokens
19487
- * instead of saving them, its handler (`src/hooks_tool_failure.ts`) stays silent on a first
19488
- * failure and speaks only on an exact repeat.
19489
- * - `pre_compact` is wired and fires, but nothing it returns can reach the model. Both dispatch
19490
- * sites call it in statement position and drop the result: app.js offset 2467452 (the manual
19491
- * `/compact` path) and offset 2571216 (`case "execute_pre_compact_hook"`). The contrast is
19492
- * what makes this conclusive rather than an absence of evidence -- the same `event()` runner's
19493
- * return IS read for other events, e.g. `notification` at offset 2296123 assigns it and
19494
- * forwards `additionalContext` as a prepended system prompt. The compaction prompt is then
19495
- * built from session history and the user's own focus text, with no hook channel into it.
19496
- * So token-goat's `pre_compact` registration here is observe-only by construction, not merely
19497
- * unused: a future attempt to inject a session manifest through it would silently do nothing.
19498
- * Not verified: whether the native declarative runner applies `additionalContext` itself. The
19499
- * trail ends in stripped Rust. That it would make the explicit JS handling for `notification`
19500
- * and `userPromptSubmitted` redundant is an argument, not a proof.
19501
- */
19414
+ /** What Copilot CLI's wired events can actually carry, read from the shipping 1.0.80 bundle rather than from its hooks reference page. A row's `implemented` set says an event is wired; it deliberately says nothing about how much of that event's response the harness honors, and for Copilot the difference matters: - `pre_tool_use` deny is real, and the reason text reaches the model. The native string table (runtime.node offset 98057208) carries `permissionDecision`, `permissionDecisionReason`, the default "No reason provided." and the formatter "Denied by preToolUse hook: " as distinct literals, and app.js sets `{textResultForLlm: reason, resultType: "denied"}` per denial and filters that call out of execution. This was previously only an inference drawn from an incident comment; it is now confirmed. The fail-closed "(hook errored)" deny-all strings sit beside these, so that incident was behavior layered on top of a working deny handler, not a substitute for one. - `post_tool_use` honors `modifiedResult`, so compression, injection fencing and image shrink do reach the model: `postToolExecution` (app.js offset 2043150) assigns the returned `toolResultJson` onto the tool result in place. - `post_tool_use` drops `additionalContext` on the JS path -- no supplier for the `onAdditionalContext` callback anywhere in the bundle, and no `additional_contexts` key in that event's native return payload, unlike its pre-tool sibling. Whether native folds it into the returned result instead was not verified. - Failed tool calls never reach `post_tool_use` at all. Copilot routes them to a separate `postToolUseFailure` event, which is handed only a stringified error and honors only `additionalContext`; `modifiedResult` is documented as not honored there. So on Copilot the output of a failed tool call reaches the model unfenced, uncompressed and unshrunk, and no response shape token-goat's shim can emit changes that. Known gap, deliberately recorded. What `additionalContext` does on that event is no longer doc-derived: app.js offset 2043380 reads the native processor's return and, when it carries `additionalContext`, either folds it into `textResultForLlm` (if `appendFailureContextToToolResult` is set) or formats it and pushes `{content, source: "system"}` onto `toolResult.newMessages`. So the channel is real and model-visible -- it just carries advice alongside the failure, never a replacement for it. It is wired now, as its own `post_tool_use_failure` event rather than a reuse of `post_tool_use`: routing failures through the success event would let success-path handlers mark a file as successfully read when the read failed. Because the channel spends tokens instead of saving them, its handler (`src/hooks_tool_failure.ts`) stays silent on a first failure and speaks only on an exact repeat. - `pre_compact` is wired and fires, but nothing it returns can reach the model. Both dispatch sites call it in statement position and drop the result: app.js offset 2467452 (the manual `/compact` path) and offset 2571216 (`case "execute_pre_compact_hook"`). The contrast is what makes this conclusive rather than an absence of evidence -- the same `event()` runner's return IS read for other events, e.g. `notification` at offset 2296123 assigns it and forwards `additionalContext` as a prepended system prompt. The compaction prompt is then built from session history and the user's own focus text, with no hook channel into it. So token-goat's `pre_compact` registration here is observe-only by construction, not merely unused: a future attempt to inject a session manifest through it would silently do nothing. Not verified: whether the native declarative runner applies `additionalContext` itself. The trail ends in stripped Rust. That it would make the explicit JS handling for `notification` and `userPromptSubmitted` redundant is an argument, not a proof. */
19502
19415
  {
19503
19416
  harness: "copilot_cli",
19504
19417
  label: "Copilot CLI",
@@ -19723,25 +19636,25 @@ function installVerificationNotice(harness) {
19723
19636
 
19724
19637
  // src/purge.ts
19725
19638
  init_define_import_meta_env();
19726
- import * as fs43 from "node:fs";
19727
- import * as path39 from "node:path";
19639
+ import * as fs42 from "node:fs";
19640
+ import * as path38 from "node:path";
19728
19641
  function directorySize(root) {
19729
19642
  let total = 0;
19730
19643
  let entries;
19731
19644
  try {
19732
- entries = fs43.readdirSync(root, { withFileTypes: true });
19645
+ entries = fs42.readdirSync(root, { withFileTypes: true });
19733
19646
  } catch {
19734
19647
  return 0;
19735
19648
  }
19736
19649
  for (const entry of entries) {
19737
- const full = path39.join(root, entry.name);
19650
+ const full = path38.join(root, entry.name);
19738
19651
  if (entry.isDirectory()) {
19739
19652
  total += directorySize(full);
19740
19653
  continue;
19741
19654
  }
19742
19655
  if (!entry.isFile()) continue;
19743
19656
  try {
19744
- total += fs43.statSync(full).size;
19657
+ total += fs42.statSync(full).size;
19745
19658
  } catch {
19746
19659
  }
19747
19660
  }
@@ -19760,7 +19673,7 @@ function purgeDataDirectories() {
19760
19673
  }
19761
19674
  const bytes = directorySize(root);
19762
19675
  try {
19763
- fs43.rmSync(root, { recursive: true, force: true });
19676
+ fs42.rmSync(root, { recursive: true, force: true });
19764
19677
  result.removed.push({ path: root, bytes });
19765
19678
  } catch (e) {
19766
19679
  result.failed.push({ path: root, reason: e instanceof Error ? e.message : String(e) });
@@ -19907,8 +19820,8 @@ async function getSectionContent(fileId, heading, opts = {}) {
19907
19820
 
19908
19821
  // src/cli_bench.ts
19909
19822
  init_define_import_meta_env();
19910
- import * as fs44 from "node:fs";
19911
- import * as path40 from "node:path";
19823
+ import * as fs43 from "node:fs";
19824
+ import * as path39 from "node:path";
19912
19825
  var ControlFilter = class extends ToolFilter {
19913
19826
  constructor(name, transform) {
19914
19827
  super();
@@ -19929,13 +19842,13 @@ var CONTROL_FILTERS = {
19929
19842
  };
19930
19843
  var MAX_BENCH_FILE_BYTES = 5 * 1024 * 1024;
19931
19844
  function readCaseFile(id, filePath, what) {
19932
- const bytes = fs44.statSync(filePath).size;
19845
+ const bytes = fs43.statSync(filePath).size;
19933
19846
  if (bytes > MAX_BENCH_FILE_BYTES) {
19934
19847
  throw new Error(
19935
19848
  `bench case ${id}: ${what} is ${bytes} bytes, over the ${MAX_BENCH_FILE_BYTES}-byte limit for a corpus file (${filePath})`
19936
19849
  );
19937
19850
  }
19938
- return fs44.readFileSync(filePath, "utf8");
19851
+ return fs43.readFileSync(filePath, "utf8");
19939
19852
  }
19940
19853
  function parseCase(id, metaPath, outputPath) {
19941
19854
  const metaText = readCaseFile(id, metaPath, "metadata");
@@ -19970,7 +19883,7 @@ function parseCase(id, metaPath, outputPath) {
19970
19883
  function loadCorpus(dir) {
19971
19884
  let entries;
19972
19885
  try {
19973
- entries = fs44.readdirSync(dir);
19886
+ entries = fs43.readdirSync(dir);
19974
19887
  } catch {
19975
19888
  throw new Error(`bench corpus not found: ${dir}`);
19976
19889
  }
@@ -19978,9 +19891,9 @@ function loadCorpus(dir) {
19978
19891
  for (const entry of entries.sort()) {
19979
19892
  if (!entry.endsWith(".json")) continue;
19980
19893
  const id = entry.slice(0, -".json".length);
19981
- const outputPath = path40.join(dir, `${id}.txt`);
19982
- if (!fs44.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
19983
- cases.push(parseCase(id, path40.join(dir, entry), outputPath));
19894
+ const outputPath = path39.join(dir, `${id}.txt`);
19895
+ if (!fs43.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
19896
+ cases.push(parseCase(id, path39.join(dir, entry), outputPath));
19984
19897
  }
19985
19898
  if (cases.length === 0) throw new Error(`bench corpus is empty: ${dir}`);
19986
19899
  return cases;
@@ -20100,10 +20013,10 @@ function appendTsv(file, report, commit) {
20100
20013
  report.mustKeepTotal,
20101
20014
  report.coveredFilters
20102
20015
  ].join(" ");
20103
- const dir = path40.dirname(path40.resolve(file));
20104
- fs44.mkdirSync(dir, { recursive: true });
20105
- const header = fs44.existsSync(file) ? "" : TSV_HEADER;
20106
- fs44.appendFileSync(file, header + row + "\n", "utf8");
20016
+ const dir = path39.dirname(path39.resolve(file));
20017
+ fs43.mkdirSync(dir, { recursive: true });
20018
+ const header = fs43.existsSync(file) ? "" : TSV_HEADER;
20019
+ fs43.appendFileSync(file, header + row + "\n", "utf8");
20107
20020
  }
20108
20021
  function currentCommit(cwd) {
20109
20022
  const opts = cwd === void 0 ? {} : { cwd };
@@ -20143,10 +20056,10 @@ function readBoundedText(text, file) {
20143
20056
  if (text === void 0 && file === void 0) throw new CliError("provide text or --file");
20144
20057
  let value;
20145
20058
  if (file !== void 0) {
20146
- if (fs45.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
20059
+ if (fs44.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
20147
20060
  throw new CliError(`file exceeds the ${CONTENT_MAX_INPUT_CHARS}-byte safety limit`);
20148
20061
  }
20149
- value = fs45.readFileSync(file, "utf8");
20062
+ value = fs44.readFileSync(file, "utf8");
20150
20063
  } else {
20151
20064
  if (text === void 0) throw new CliError("provide text or --file");
20152
20065
  value = text;
@@ -20191,19 +20104,23 @@ function cmdHandoffResolve(name, opts) {
20191
20104
  if (result === null) throw new CliError(`no local handoff named "${name}" in this project`);
20192
20105
  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"));
20193
20106
  }
20194
- async function cmdSemantic(query, opts) {
20107
+ async function cmdSemantic(query, more, opts) {
20195
20108
  if (!query && !opts.preflight && !opts.warm) {
20196
20109
  throw new CliError("missing required argument: query");
20197
20110
  }
20111
+ if (opts.preflight === true && more.length > 0) {
20112
+ throw new CliError("--preflight checks the embedding setup and runs no query; drop the queries, or drop --preflight to search");
20113
+ }
20198
20114
  const limit = opts.limit !== void 0 ? requireNonNegativeInt("--limit", opts.limit) : 20;
20199
- const { text, code } = await runSemantic(query ?? "", {
20115
+ const semanticOpts = {
20200
20116
  limit,
20201
20117
  ...opts.json === true ? { json: true } : {},
20202
20118
  ...opts.grep !== void 0 ? { grep: opts.grep } : {},
20203
20119
  ...opts.excludeTests === true ? { excludeTests: true } : {},
20204
20120
  ...opts.preflight === true ? { preflight: true } : {},
20205
20121
  ...opts.warm === true ? { warm: true } : {}
20206
- });
20122
+ };
20123
+ const { text, code } = more.length > 0 ? await runSemanticMulti([query ?? "", ...more], semanticOpts) : await runSemantic(query ?? "", semanticOpts);
20207
20124
  (opts.json === true || code === 0 ? out : err)(text);
20208
20125
  process.exitCode = code;
20209
20126
  }
@@ -20269,7 +20186,7 @@ async function cmdIndex(pathArg, opts = {}) {
20269
20186
  continue;
20270
20187
  }
20271
20188
  const sha = fingerprintFile(key);
20272
- if (sha === null && !fs45.existsSync(key)) continue;
20189
+ if (sha === null && !fs44.existsSync(key)) continue;
20273
20190
  recordKnownRootThrottled(key, dataDir(), dbPath);
20274
20191
  const entry = sha !== null ? getFileEntry(key, dbPath) : null;
20275
20192
  const spellingStale = entry !== null && indexedPathSpellingIsStale(entry.filePath, key);
@@ -20306,7 +20223,7 @@ async function cmdIndex(pathArg, opts = {}) {
20306
20223
  }
20307
20224
  continue;
20308
20225
  }
20309
- if (!fs45.existsSync(key)) continue;
20226
+ if (!fs44.existsSync(key)) continue;
20310
20227
  }
20311
20228
  const embedFresh = parseUnchanged ? embedUnchanged : embedFreshFor(getFileEntry(key, dbPath)?.embedSha);
20312
20229
  if (!embedFresh) {
@@ -20367,7 +20284,7 @@ async function cmdMcpServe() {
20367
20284
  let StdioServerTransport;
20368
20285
  try {
20369
20286
  ;
20370
- ({ createMcpServer } = await import("./token-goat-chunk-T6652D3F.mjs"));
20287
+ ({ createMcpServer } = await import("./token-goat-chunk-SJEKA7Y2.mjs"));
20371
20288
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
20372
20289
  } catch (err2) {
20373
20290
  process.stderr.write(
@@ -20392,11 +20309,11 @@ async function cmdHook(event, opts) {
20392
20309
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
20393
20310
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
20394
20311
  }
20395
- const { relay } = await import("./token-goat-chunk-WEYODFTA.mjs");
20312
+ const { relay } = await import("./token-goat-chunk-7MAFDRVR.mjs");
20396
20313
  await relay(event);
20397
20314
  }
20398
20315
  function projectHooksCommitNote(pathFiles, hooksConfigPath) {
20399
- const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path41.join(path41.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
20316
+ const shim = hooksConfigPath === void 0 ? "" : ` (${displaySafePath(path40.join(path40.dirname(hooksConfigPath), HOOKS_SCRIPT_FILE))} is generated with them)`;
20400
20317
  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.`;
20401
20318
  }
20402
20319
  function visualStudioManualSteps(scope) {
@@ -20429,7 +20346,7 @@ async function cmdInstall(opts) {
20429
20346
  if (opts.project === true && opts.user === true) {
20430
20347
  throw new Error("install takes either -p/--project or --user, not both.");
20431
20348
  }
20432
- await import("./token-goat-chunk-WEYODFTA.mjs");
20349
+ await import("./token-goat-chunk-7MAFDRVR.mjs");
20433
20350
  const scope = opts.project === true ? "project" : "user";
20434
20351
  if (wantsClaudeCodeBase(opts)) {
20435
20352
  const result = installHooks(scope);
@@ -20575,16 +20492,16 @@ async function cmdInstall(opts) {
20575
20492
  );
20576
20493
  }
20577
20494
  try {
20578
- const skillDir2 = path41.join(claudeConfigDir(), "skills");
20579
- if (fs45.existsSync(skillDir2)) {
20580
- const entries = fs45.readdirSync(skillDir2, { withFileTypes: true });
20495
+ const skillDir2 = path40.join(claudeConfigDir(), "skills");
20496
+ if (fs44.existsSync(skillDir2)) {
20497
+ const entries = fs44.readdirSync(skillDir2, { withFileTypes: true });
20581
20498
  const skillNames = [];
20582
20499
  const sessionId = getSessionId();
20583
20500
  for (const entry of entries) {
20584
20501
  if (!entry.isDirectory()) continue;
20585
- const skillFile = path41.join(skillDir2, entry.name, "SKILL.md");
20586
- if (fs45.existsSync(skillFile)) {
20587
- const body = fs45.readFileSync(skillFile, "utf-8");
20502
+ const skillFile = path40.join(skillDir2, entry.name, "SKILL.md");
20503
+ if (fs44.existsSync(skillFile)) {
20504
+ const body = fs44.readFileSync(skillFile, "utf-8");
20588
20505
  const compact = extractCompactFromMarker(body);
20589
20506
  if (compact === null) continue;
20590
20507
  const sourceSha = contentHash(body);
@@ -20595,9 +20512,9 @@ async function cmdInstall(opts) {
20595
20512
  if (skillNames.length > 0) {
20596
20513
  const dir = skillOutputsDir();
20597
20514
  ensureDirSync(dir);
20598
- const pregenPath = path41.join(dir, "pregen.json");
20515
+ const pregenPath = path40.join(dir, "pregen.json");
20599
20516
  const pregenData = { ts: Date.now(), names: skillNames };
20600
- await fs45.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
20517
+ await fs44.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
20601
20518
  out(`Pre-generated ${skillNames.length} skill compacts.`);
20602
20519
  }
20603
20520
  }
@@ -20860,7 +20777,7 @@ function cmdBashOutput(id, opts) {
20860
20777
  }
20861
20778
  let content;
20862
20779
  try {
20863
- const st = fs45.statSync(opts.file);
20780
+ const st = fs44.statSync(opts.file);
20864
20781
  if (st.isFIFO() || st.isSocket()) {
20865
20782
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
20866
20783
  }
@@ -20875,7 +20792,7 @@ function cmdBashOutput(id, opts) {
20875
20792
  `);
20876
20793
  }
20877
20794
  }
20878
- content = redactIfDotenv(opts.file, decodeSource(fs45.readFileSync(opts.file)));
20795
+ content = redactIfDotenv(opts.file, decodeSource(fs44.readFileSync(opts.file)));
20879
20796
  } catch (e) {
20880
20797
  if (e instanceof CliError) throw e;
20881
20798
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -20939,15 +20856,15 @@ function cmdMcpOutput(id, opts) {
20939
20856
  if (opts.file.includes("\0")) {
20940
20857
  throw new CliError("--file path contains a null byte");
20941
20858
  }
20942
- if (!fs45.existsSync(opts.file)) {
20859
+ if (!fs44.existsSync(opts.file)) {
20943
20860
  throw new CliError(`file not found: ${opts.file}`);
20944
20861
  }
20945
20862
  try {
20946
- const st = fs45.statSync(opts.file);
20863
+ const st = fs44.statSync(opts.file);
20947
20864
  if (st.isFIFO() || st.isSocket()) {
20948
20865
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
20949
20866
  }
20950
- content = decodeSource(fs45.readFileSync(opts.file));
20867
+ content = decodeSource(fs44.readFileSync(opts.file));
20951
20868
  } catch (e) {
20952
20869
  if (e instanceof CliError) throw e;
20953
20870
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -21020,9 +20937,14 @@ async function cmdCompress(commandArgs, opts = {}) {
21020
20937
  process.exitCode = 1;
21021
20938
  return;
21022
20939
  }
21023
- const bashRunner = await import("./token-goat-chunk-KYV7E6ZJ.mjs");
20940
+ const bashRunner = await import("./token-goat-chunk-BCN2YQC6.mjs");
21024
20941
  if (opts.compress === false) {
21025
- process.exitCode = bashRunner.runRaw(command, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
20942
+ process.exitCode = bashRunner.runRaw(
20943
+ command,
20944
+ parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS),
20945
+ opts.native,
20946
+ opts.shell
20947
+ );
21026
20948
  return;
21027
20949
  }
21028
20950
  const maxTokens = opts.maxTokens !== void 0 ? requireNonNegativeInt("--max-tokens", opts.maxTokens) : 0;
@@ -21030,9 +20952,11 @@ async function cmdCompress(commandArgs, opts = {}) {
21030
20952
  filterName: opts.filter,
21031
20953
  timeout: parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS),
21032
20954
  maxTokens,
20955
+ ...opts.capHintB64 !== void 0 ? { capHint: Buffer.from(opts.capHintB64, "base64").toString("utf8") } : {},
21033
20956
  ...opts.profile !== void 0 ? { compressionProfile: opts.profile } : {},
21034
20957
  ...opts.quietSuccess === true ? { quietSuccess: true } : {},
21035
- ...opts.native === true ? { nativeShell: true } : {}
20958
+ ...opts.native === true ? { nativeShell: true } : {},
20959
+ ...opts.shell !== void 0 ? { shellType: opts.shell } : {}
21036
20960
  });
21037
20961
  } catch (e) {
21038
20962
  err(`token-goat: ${displaySafeText(extractErrorMessage(e))}`);
@@ -21196,7 +21120,7 @@ function buildProgram() {
21196
21120
  )
21197
21121
  );
21198
21122
  });
21199
- 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));
21123
+ program2.command("semantic [query] [more...]").description("semantic search (falls back to full-text search); several queries run in one call, one headed block per query").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));
21200
21124
  const registerSymbolListing = (name, description, run2) => {
21201
21125
  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(
21202
21126
  (file, more, opts) => runExitText(
@@ -21252,7 +21176,7 @@ function buildProgram() {
21252
21176
  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(
21253
21177
  (opts) => runExitText(
21254
21178
  () => runBenchCommand({
21255
- corpus: opts.corpus ?? path41.join("tests", "fixtures", "bench"),
21179
+ corpus: opts.corpus ?? path40.join("tests", "fixtures", "bench"),
21256
21180
  ...opts.tsv !== void 0 ? { tsv: opts.tsv } : {},
21257
21181
  ...opts.validate === true ? { validate: true } : {},
21258
21182
  ...opts.json === true ? { json: true } : {}
@@ -21283,10 +21207,10 @@ function buildProgram() {
21283
21207
  registerAnalysisCommands(program2, guard);
21284
21208
  registerSessionCommands(program2, guard);
21285
21209
  program2.command("gdrive-sections <file-id>").description("fetch and list sections from a public Google Doc").option("--heading <name>", "get content of one named section").option("--fresh", "skip the on-disk cache and force a live fetch").action(guard(cmdGdriveSections));
21286
- 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);
21210
+ program2.command("compress [command...]").alias("bash").alias("run").description("run a shell command (under a POSIX shell / bash) and emit a compressed view of its output").allowUnknownOption(true).option("-c, --cmd <command>", "the shell command to run, as one string (use / for paths across platforms)").option("--cmd-b64 <payload>", "the shell command as a base64-encoded string (preserves quotes, backslashes, and symbols across platforms)").option("--shell <type>", "shell interpreter to run under: bash | pwsh | powershell | native").option("-f, --filter <name>", "filter name (auto-detected from the command when omitted)").option("--timeout <seconds>", "wall-clock timeout in seconds (0 = built-in default)").option("--no-compress", "stream output raw without compression (debug the wrapper)").option("--profile <name>", "compression profile: aggressive | balanced | minimal").option("--max-tokens <n>", "post-compress token cap (0 = no cap)").option("--cap-hint-b64 <payload>", "base64 text printed after the output when --max-tokens cut it (the Bash hook sets it to the narrower read command)").option("-q, --quiet-success", "on exit code 0, emit only [tg: ok] summary and store full output for recall via bash-output").option("--native", "use native platform shell (e.g. cmd.exe on Windows) instead of bash, preserving Windows path backslashes").action(cmdCompress);
21287
21211
  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(
21288
21212
  (opts) => guard(async () => {
21289
- const { cmdUpgrade } = await import("./token-goat-chunk-ORO4GOMA.mjs");
21213
+ const { cmdUpgrade } = await import("./token-goat-chunk-4JJBFT7V.mjs");
21290
21214
  await cmdUpgrade(opts, () => cmdInstall({}));
21291
21215
  })()
21292
21216
  );
@@ -21303,8 +21227,19 @@ function buildProgram() {
21303
21227
  return;
21304
21228
  }
21305
21229
  if (cmd) {
21230
+ const known = program2.commands.some((sub) => sub.name() === cmd || sub.aliases().includes(cmd));
21231
+ if (!known) {
21232
+ err(`token-goat: unknown command '${displaySafeText(cmd)}'. Run 'token-goat commands --grep <pattern>' to search the list.`);
21233
+ process.exitCode = 1;
21234
+ return;
21235
+ }
21306
21236
  const argv = [process.argv[0] ?? "node", process.argv[1] ?? "token-goat", cmd, "--help"];
21307
- program2.parse(argv);
21237
+ try {
21238
+ program2.parse(argv);
21239
+ } catch (e) {
21240
+ const code = e.code;
21241
+ if (code !== "commander.help" && code !== "commander.helpDisplayed") throw e;
21242
+ }
21308
21243
  } else {
21309
21244
  out(generateCompactHelp());
21310
21245
  }