token-goat 2.9.1 → 2.9.3

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 (29) hide show
  1. package/README.md +38 -765
  2. package/SECURITY.md +1 -1
  3. package/dist/{token-goat-chunk-RRNRPCLX.mjs → token-goat-chunk-2VVXTAGD.mjs} +192 -16
  4. package/dist/{token-goat-chunk-CD6U5OD6.mjs → token-goat-chunk-3UBORD6Z.mjs} +9 -6
  5. package/dist/{token-goat-chunk-FDXINYK4.mjs → token-goat-chunk-3XVGPLDS.mjs} +3592 -63
  6. package/dist/{token-goat-chunk-SQSB5P5P.mjs → token-goat-chunk-6FQMEMWX.mjs} +583 -107
  7. package/dist/{token-goat-chunk-SXSWQMNP.mjs → token-goat-chunk-7A7SBE6R.mjs} +166 -72
  8. package/dist/token-goat-chunk-A37V4PBF.mjs +56 -0
  9. package/dist/{token-goat-chunk-MR7MNSDR.mjs → token-goat-chunk-A4VBNWSL.mjs} +20 -3
  10. package/dist/{token-goat-chunk-G6XLWJWI.mjs → token-goat-chunk-E3K3BTEQ.mjs} +5 -2
  11. package/dist/{token-goat-chunk-AO2QD2AG.mjs → token-goat-chunk-EEIDFMEM.mjs} +4 -0
  12. package/dist/{token-goat-chunk-324QOJYZ.mjs → token-goat-chunk-EVC4TOLE.mjs} +4 -1
  13. package/dist/{token-goat-chunk-WN5T5EW5.mjs → token-goat-chunk-FQD3OB5W.mjs} +98 -2
  14. package/dist/token-goat-chunk-HC5NMGPD.mjs +23 -0
  15. package/dist/{token-goat-chunk-4ZDRTNGV.mjs → token-goat-chunk-KKHKXUVS.mjs} +317 -32
  16. package/dist/{token-goat-chunk-4KD2N46D.mjs → token-goat-chunk-LLNA42NI.mjs} +10 -12
  17. package/dist/{token-goat-chunk-5CVKO3DA.mjs → token-goat-chunk-LYWIRYCF.mjs} +5 -2
  18. package/dist/{token-goat-chunk-LHMC5ENL.mjs → token-goat-chunk-OKJX2JWW.mjs} +6 -1
  19. package/dist/{token-goat-chunk-R4SR7MQY.mjs → token-goat-chunk-PXWBHSFB.mjs} +2 -2
  20. package/dist/{token-goat-chunk-V5T3YNYR.mjs → token-goat-chunk-XSSQFI4C.mjs} +7 -7
  21. package/dist/token-goat-hook.mjs +12 -7
  22. package/dist/token-goat.core.mjs +10 -7
  23. package/docs/C4_RUNTIME_ARCHITECTURE.md +303 -0
  24. package/docs/cli.md +392 -0
  25. package/docs/install.md +331 -0
  26. package/docs/security.md +106 -0
  27. package/package.json +14 -3
  28. package/dist/token-goat-chunk-AEX54RUZ.mjs +0 -11
  29. package/dist/token-goat-chunk-AUM22UQ2.mjs +0 -23
@@ -86,7 +86,7 @@ import {
86
86
  runZipRead,
87
87
  symbolNamesInFile,
88
88
  upsertNote
89
- } from "./token-goat-chunk-SXSWQMNP.mjs";
89
+ } from "./token-goat-chunk-7A7SBE6R.mjs";
90
90
  import {
91
91
  DEFAULT_RECONCILE_BUDGET_MS,
92
92
  GEMINI_TOOL_NAME_MAP,
@@ -108,7 +108,7 @@ import {
108
108
  runReconcile,
109
109
  storeWebOutput,
110
110
  summarizeResidentContext
111
- } from "./token-goat-chunk-MR7MNSDR.mjs";
111
+ } from "./token-goat-chunk-A4VBNWSL.mjs";
112
112
  import {
113
113
  AGENT_SALT_MARKER,
114
114
  BASH_OUTPUT_SUBDIR,
@@ -238,15 +238,20 @@ import {
238
238
  visionTokensSavedByText,
239
239
  walkProject,
240
240
  writeCompact
241
- } from "./token-goat-chunk-FDXINYK4.mjs";
241
+ } from "./token-goat-chunk-3XVGPLDS.mjs";
242
242
  import {
243
243
  C,
244
244
  CONFIG_KEY_ENV_OVERRIDES,
245
+ CompressedOutput,
245
246
  ENV_KEYS,
246
247
  FILTERS,
247
248
  LOCK_WAIT_MS_HARDENED,
248
249
  MATERIALIZE_SHRUNK_IMAGE_JS,
250
+ PROJECT_LOCKED_KEYS,
251
+ PROJECT_LOCKED_SECTIONS,
249
252
  RESET,
253
+ TOOL_FILTERS,
254
+ ToolFilter,
250
255
  VERSION,
251
256
  _useRichStats,
252
257
  anchoredMarkerPattern,
@@ -255,6 +260,8 @@ import {
255
260
  canonicalize,
256
261
  cappedSourceBytesSaved,
257
262
  colorStdout,
263
+ combineStreams,
264
+ compileCustomPatterns,
258
265
  configPath,
259
266
  copilotCliConfigPath,
260
267
  copilotCliMcpToolsDir,
@@ -264,11 +271,14 @@ import {
264
271
  dataDir,
265
272
  decodeSource,
266
273
  defaultConfig,
274
+ deliverCompressed,
275
+ detectFromCommand,
267
276
  detectLanguage,
268
277
  detectSourceEncoding,
269
278
  encodeSource,
270
279
  ensureDirSync,
271
280
  ensureNewline,
281
+ envBool,
272
282
  escapeRegExp,
273
283
  extractErrorMessage,
274
284
  fg,
@@ -354,15 +364,24 @@ import {
354
364
  withFileLock,
355
365
  withRetryOnLock,
356
366
  writeJsonSettings
357
- } from "./token-goat-chunk-4ZDRTNGV.mjs";
367
+ } from "./token-goat-chunk-KKHKXUVS.mjs";
358
368
  import {
359
- __export
360
- } from "./token-goat-chunk-AEX54RUZ.mjs";
369
+ __export,
370
+ init_define_import_meta_env
371
+ } from "./token-goat-chunk-A37V4PBF.mjs";
361
372
 
362
373
  // src/cli.ts
374
+ init_define_import_meta_env();
363
375
  import { randomBytes } from "node:crypto";
364
376
 
377
+ // node_modules/commander/index.js
378
+ init_define_import_meta_env();
379
+
380
+ // node_modules/commander/lib/argument.js
381
+ init_define_import_meta_env();
382
+
365
383
  // node_modules/commander/lib/error.js
384
+ init_define_import_meta_env();
366
385
  var CommanderError = class extends Error {
367
386
  /**
368
387
  * Constructs the CommanderError class
@@ -513,6 +532,7 @@ function humanReadableArgName(arg) {
513
532
  }
514
533
 
515
534
  // node_modules/commander/lib/command.js
535
+ init_define_import_meta_env();
516
536
  import { EventEmitter } from "node:events";
517
537
  import childProcess from "node:child_process";
518
538
  import path from "node:path";
@@ -521,6 +541,7 @@ import process2 from "node:process";
521
541
  import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
522
542
 
523
543
  // node_modules/commander/lib/help.js
544
+ init_define_import_meta_env();
524
545
  import { stripVTControlCharacters } from "node:util";
525
546
  var Help = class {
526
547
  constructor() {
@@ -1113,6 +1134,7 @@ ${itemIndentStr}`);
1113
1134
  };
1114
1135
 
1115
1136
  // node_modules/commander/lib/option.js
1137
+ init_define_import_meta_env();
1116
1138
  var Option = class {
1117
1139
  /**
1118
1140
  * Initialize a new `Option` with the given `flags` and `description`.
@@ -1419,6 +1441,7 @@ function splitOptionFlags(flags) {
1419
1441
  }
1420
1442
 
1421
1443
  // node_modules/commander/lib/suggestSimilar.js
1444
+ init_define_import_meta_env();
1422
1445
  var maxDistance = 3;
1423
1446
  function editDistance(a, b) {
1424
1447
  if (Math.abs(a.length - b.length) > maxDistance)
@@ -3473,9 +3496,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3473
3496
  * @param {string} [path]
3474
3497
  * @return {(string|null|Command)}
3475
3498
  */
3476
- executableDir(path30) {
3477
- if (path30 === void 0) return this._executableDir;
3478
- this._executableDir = path30;
3499
+ executableDir(path31) {
3500
+ if (path31 === void 0) return this._executableDir;
3501
+ this._executableDir = path31;
3479
3502
  return this;
3480
3503
  }
3481
3504
  /**
@@ -3731,6 +3754,7 @@ function useColor() {
3731
3754
  var program = new Command();
3732
3755
 
3733
3756
  // src/command_intent.ts
3757
+ init_define_import_meta_env();
3734
3758
  var INTENT_SUGGESTIONS = [
3735
3759
  [
3736
3760
  ["search", "lookup", "query"],
@@ -3774,11 +3798,12 @@ function attemptedCommandName(argv) {
3774
3798
  }
3775
3799
 
3776
3800
  // src/cli.ts
3777
- import * as fs28 from "fs";
3778
- import * as path29 from "path";
3801
+ import * as fs29 from "fs";
3802
+ import * as path30 from "path";
3779
3803
  import { homedir as homedir13 } from "os";
3780
3804
 
3781
3805
  // src/walk_index.ts
3806
+ init_define_import_meta_env();
3782
3807
  import * as fs2 from "node:fs";
3783
3808
  import * as os from "node:os";
3784
3809
  import * as path2 from "node:path";
@@ -3827,6 +3852,7 @@ function collectWalkIndexFiles(root, opts = {}) {
3827
3852
  }
3828
3853
 
3829
3854
  // src/bridges/gemini_install.ts
3855
+ init_define_import_meta_env();
3830
3856
  import * as fs3 from "node:fs";
3831
3857
  import * as os2 from "node:os";
3832
3858
  import * as path3 from "node:path";
@@ -3970,6 +3996,7 @@ function isGeminiInstalled() {
3970
3996
  }
3971
3997
 
3972
3998
  // src/bridges/qwen_install.ts
3999
+ init_define_import_meta_env();
3973
4000
  import * as fs4 from "node:fs";
3974
4001
  import * as os3 from "node:os";
3975
4002
  import * as path4 from "node:path";
@@ -4094,11 +4121,13 @@ function isQwenInstalled() {
4094
4121
  }
4095
4122
 
4096
4123
  // src/bridges/pi_install.ts
4124
+ init_define_import_meta_env();
4097
4125
  import * as fs5 from "node:fs";
4098
4126
  import * as os4 from "node:os";
4099
4127
  import * as path5 from "node:path";
4100
4128
 
4101
4129
  // src/bridges/pi.ts
4130
+ init_define_import_meta_env();
4102
4131
  var PI_EXTENSION_SCRIPT = `// token-goat bridge extension for pi (pi-coding-agent)
4103
4132
  // Bridges pi's extension events to token-goat's subprocess hook protocol.
4104
4133
  // https://github.com/DFKHelper/token-goat
@@ -4317,7 +4346,7 @@ function materializeShrunkImage(context: string | undefined): string | undefined
4317
4346
  try {
4318
4347
  pruneMaterializedShrinks();
4319
4348
  const buf = Buffer.from(match[2], "base64");
4320
- const name = \`token-goat-shrink-\${process.pid}-\${Date.now()}-\${Math.random().toString(36).slice(2)}.\${match[1]}\`;
4349
+ const name = \`token-goat-shrink-\${process.pid}-\${Date.now()}-\${globalThis.crypto.randomUUID()}.\${match[1]}\`;
4321
4350
  const file = path.join(os.tmpdir(), name);
4322
4351
  fs.writeFileSync(file, buf);
4323
4352
  return file;
@@ -4489,11 +4518,16 @@ function isPiInstalled(opts = {}) {
4489
4518
  }
4490
4519
 
4491
4520
  // src/bridges/opencode_install.ts
4521
+ init_define_import_meta_env();
4492
4522
  import * as fs6 from "node:fs";
4493
4523
  import * as os5 from "node:os";
4494
4524
  import * as path6 from "node:path";
4495
4525
 
4526
+ // src/bridges/opencode.ts
4527
+ init_define_import_meta_env();
4528
+
4496
4529
  // src/bridges/relay_block.ts
4530
+ init_define_import_meta_env();
4497
4531
  var BRIDGE_RELAY_JS = `// resolveEntryPath reads a sidecar JSON file (token-goat-entry.json, written by
4498
4532
  // the install step next to this plugin file) containing the absolute path to
4499
4533
  // the token-goat CLI entry that was running at install time. Unlike
@@ -4783,11 +4817,13 @@ function isOpencodeInstalled() {
4783
4817
  }
4784
4818
 
4785
4819
  // src/bridges/openclaw_install.ts
4820
+ init_define_import_meta_env();
4786
4821
  import * as fs7 from "node:fs";
4787
4822
  import * as os6 from "node:os";
4788
4823
  import * as path7 from "node:path";
4789
4824
 
4790
4825
  // src/bridges/openclaw.ts
4826
+ init_define_import_meta_env();
4791
4827
  var OPENCLAW_PLUGIN_SCRIPT = `// token-goat bridge plugin for OpenClaw
4792
4828
  // Bridges OpenClaw's tool-call and session hooks to token-goat's subprocess
4793
4829
  // hook protocol. https://github.com/DFKHelper/token-goat
@@ -5094,6 +5130,7 @@ function isOpenclawInstalled() {
5094
5130
  }
5095
5131
 
5096
5132
  // src/bridges_status.ts
5133
+ init_define_import_meta_env();
5097
5134
  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";
5098
5135
  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";
5099
5136
  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";
@@ -5397,6 +5434,7 @@ function installVerificationNotice(harness) {
5397
5434
  }
5398
5435
 
5399
5436
  // src/sharepoint_resolve.ts
5437
+ init_define_import_meta_env();
5400
5438
  import * as fs8 from "node:fs";
5401
5439
  import * as os7 from "node:os";
5402
5440
  import * as path8 from "node:path";
@@ -5504,6 +5542,7 @@ function resolveLocalPath(parsed, env = process.env, home = os7.homedir()) {
5504
5542
  }
5505
5543
 
5506
5544
  // src/video_chapters.ts
5545
+ init_define_import_meta_env();
5507
5546
  import { spawnSync } from "node:child_process";
5508
5547
  var _ffprobeAvailable;
5509
5548
  function isFfprobeAvailable() {
@@ -5551,6 +5590,7 @@ function extractVideoChapters(file) {
5551
5590
  }
5552
5591
 
5553
5592
  // src/transcript_extract.ts
5593
+ init_define_import_meta_env();
5554
5594
  import * as fs9 from "node:fs";
5555
5595
  var TIMESTAMP_RE = /(\d{1,2}:)?(\d{2}):(\d{2})[.,](\d{1,3})/;
5556
5596
  var CUE_LINE_RE = new RegExp(`^\\s*${TIMESTAMP_RE.source}\\s*-->\\s*${TIMESTAMP_RE.source}`);
@@ -5664,6 +5704,7 @@ function formatCues(cues) {
5664
5704
  }
5665
5705
 
5666
5706
  // src/affected.ts
5707
+ init_define_import_meta_env();
5667
5708
  import * as path9 from "node:path";
5668
5709
  var DEFAULT_AFFECTED_DEPTH = 5;
5669
5710
  function emit(text) {
@@ -5765,6 +5806,7 @@ function runAffected(opts) {
5765
5806
  }
5766
5807
 
5767
5808
  // src/purge.ts
5809
+ init_define_import_meta_env();
5768
5810
  import * as fs10 from "node:fs";
5769
5811
  import * as path10 from "node:path";
5770
5812
  function directorySize(root) {
@@ -5818,6 +5860,7 @@ function formatBytes2(bytes) {
5818
5860
  }
5819
5861
 
5820
5862
  // src/cli_stats.ts
5863
+ init_define_import_meta_env();
5821
5864
  import * as fs11 from "node:fs";
5822
5865
  import * as path11 from "node:path";
5823
5866
  function writeRaw(text) {
@@ -5940,6 +5983,7 @@ function runStats(opts = {}) {
5940
5983
  }
5941
5984
 
5942
5985
  // src/cli_doctor.ts
5986
+ init_define_import_meta_env();
5943
5987
  import * as fs12 from "fs";
5944
5988
  import * as os8 from "os";
5945
5989
  import * as path12 from "path";
@@ -6153,19 +6197,19 @@ function checkEmbeddingCoverage(dbPath, rootDir) {
6153
6197
  if (indexedFiles === 0) {
6154
6198
  return { name: "Embedding coverage", status: "ok", message: "no indexed files yet" };
6155
6199
  }
6156
- const pct2 = Math.round(embeddedFiles / indexedFiles * 100);
6200
+ const pct3 = Math.round(embeddedFiles / indexedFiles * 100);
6157
6201
  const sizeKb = cfg.indexing.large_file_symbol_only_kb;
6158
6202
  if (embeddedFiles / indexedFiles < EMBED_COVERAGE_WARN_FRACTION) {
6159
6203
  return {
6160
6204
  name: "Embedding coverage",
6161
6205
  status: "warn",
6162
- message: `only ${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct2}%) have embeddings \u2014 'semantic' searches those files only, and reports finding nothing in the same words it uses after searching everything. Files over indexing.large_file_symbol_only_kb (currently ${sizeKb} KB) are indexed for symbols only and are the usual reason; raise it with 'token-goat config set indexing.large_file_symbol_only_kb <KB>' and re-embed with 'token-goat index --force' to widen coverage. Exact symbol lookups are unaffected`
6206
+ message: `only ${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct3}%) have embeddings \u2014 'semantic' searches those files only, and reports finding nothing in the same words it uses after searching everything. Files over indexing.large_file_symbol_only_kb (currently ${sizeKb} KB) are indexed for symbols only and are the usual reason; raise it with 'token-goat config set indexing.large_file_symbol_only_kb <KB>' and re-embed with 'token-goat index --force' to widen coverage. Exact symbol lookups are unaffected`
6163
6207
  };
6164
6208
  }
6165
6209
  return {
6166
6210
  name: "Embedding coverage",
6167
6211
  status: "ok",
6168
- message: `${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct2}%) have embeddings`
6212
+ message: `${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct3}%) have embeddings`
6169
6213
  };
6170
6214
  } catch (err2) {
6171
6215
  return {
@@ -6402,6 +6446,40 @@ function checkStrayClaudeMdBlocks(searchRoot) {
6402
6446
  message: `${strays.length} stray cop${strays.length === 1 ? "y" : "ies"} outside CLAUDE.md (never refreshed by install, never removed by uninstall, will go stale): ${strays.join(", ")}`
6403
6447
  };
6404
6448
  }
6449
+ var LOCKED_BOOLEAN_SAFE_VALUE = {
6450
+ "gdrive.enabled": false,
6451
+ "injection.enabled": true,
6452
+ "redaction.strict": true,
6453
+ "mcp.confine_reads_to_project_root": true,
6454
+ "screenshot.block_private_targets": true,
6455
+ "network.offline": true,
6456
+ "indexing.cross_project_symbols": false,
6457
+ "webfetch.compress_bodies": true
6458
+ };
6459
+ function lockedEnvOverridableKeys() {
6460
+ const out2 = [];
6461
+ for (const key of Object.keys(CONFIG_KEY_ENV_OVERRIDES)) {
6462
+ const section = key.split(".")[0] ?? "";
6463
+ const locked = PROJECT_LOCKED_SECTIONS.includes(section) || PROJECT_LOCKED_KEYS.includes(key);
6464
+ if (locked && (CONFIG_KEY_ENV_OVERRIDES[key] ?? []).length > 0) out2.push(key);
6465
+ }
6466
+ return out2.sort();
6467
+ }
6468
+ function envOverriddenSecuritySettings() {
6469
+ const out2 = [];
6470
+ for (const setting of lockedEnvOverridableKeys()) {
6471
+ const safe = LOCKED_BOOLEAN_SAFE_VALUE[setting];
6472
+ for (const envVar of CONFIG_KEY_ENV_OVERRIDES[setting] ?? []) {
6473
+ if (safe === void 0) {
6474
+ const raw = process.env[envVar];
6475
+ if (raw !== void 0 && raw.trim() !== "") out2.push({ setting, envVar, kind: "replaced" });
6476
+ } else if (envBool(envVar, safe) !== safe) {
6477
+ out2.push({ setting, envVar, kind: "weakened" });
6478
+ }
6479
+ }
6480
+ }
6481
+ return out2;
6482
+ }
6405
6483
  function checkSecurityPosture(cfg, dataDirPath) {
6406
6484
  const results = [];
6407
6485
  results.push({
@@ -6424,6 +6502,19 @@ function checkSecurityPosture(cfg, dataDirPath) {
6424
6502
  status: "ok",
6425
6503
  message: allow > 0 ? `${allow} allowed host pattern${allow === 1 ? "" : "s"}, ${deny} denied: nothing outside the allow list is fetched` : `no allow list, ${deny} denied pattern${deny === 1 ? "" : "s"}: any host not denied can be fetched`
6426
6504
  });
6505
+ const custom = compileCustomPatterns(cfg.redaction.custom_patterns);
6506
+ const strictNote = cfg.redaction.strict ? "strict mode on" : "strict mode off (redaction.strict)";
6507
+ const patternNote = custom.patterns.length === 0 ? "built-in patterns only" : `${custom.patterns.length} custom pattern${custom.patterns.length === 1 ? "" : "s"} plus the built-in ones`;
6508
+ results.push(
6509
+ custom.problems.length > 0 ? {
6510
+ name: "Security redaction",
6511
+ // A pattern the operator believes is redacting, but which never compiled, is the one
6512
+ // failure here that is invisible from the output itself -- so it warns rather than
6513
+ // being reported as a healthy count that happens to be short.
6514
+ status: "warn",
6515
+ message: `${custom.problems.length} custom redaction pattern${custom.problems.length === 1 ? "" : "s"} could not be used and ${custom.problems.length === 1 ? "is" : "are"} not redacting anything: ` + custom.problems.map((p) => `${p.pattern} (${p.reason})`).join("; ")
6516
+ } : { name: "Security redaction", status: "ok", message: `${patternNote}, ${strictNote}` }
6517
+ );
6427
6518
  const extraRoots = cfg.mcp.allowed_roots.length;
6428
6519
  results.push(
6429
6520
  cfg.mcp.confine_reads_to_project_root ? {
@@ -6432,6 +6523,27 @@ function checkSecurityPosture(cfg, dataDirPath) {
6432
6523
  message: extraRoots === 0 ? "reads are confined to the project root" : `reads are confined to the project root plus ${extraRoots} configured root${extraRoots === 1 ? "" : "s"}`
6433
6524
  } : { name: "Security mcp roots", status: "warn", message: "confinement is off (mcp.confine_reads_to_project_root): a read can leave the project" }
6434
6525
  );
6526
+ const overridden = envOverriddenSecuritySettings();
6527
+ const weakened = overridden.filter((o) => o.kind === "weakened");
6528
+ const replaced = overridden.filter((o) => o.kind === "replaced");
6529
+ const describe = (o) => `${o.setting} (${o.envVar})`;
6530
+ const parts = [];
6531
+ if (weakened.length > 0) parts.push(`held open: ${weakened.map(describe).join(", ")}`);
6532
+ if (replaced.length > 0) parts.push(`set from the environment: ${replaced.map(describe).join(", ")}`);
6533
+ results.push(
6534
+ overridden.length === 0 ? {
6535
+ name: "Security config overrides",
6536
+ status: "ok",
6537
+ // Naming the count keeps this from reading as a guarantee about settings it never looked
6538
+ // at, which is what the first version of this line did while nine locked keys went
6539
+ // unchecked behind it.
6540
+ message: `no environment variable is overriding any of the ${lockedEnvOverridableKeys().length} project-locked settings`
6541
+ } : {
6542
+ name: "Security config overrides",
6543
+ status: "warn",
6544
+ message: `the environment, not the config file, is deciding ${overridden.length === 1 ? "a" : "these"} project-locked setting${overridden.length === 1 ? "" : "s"}. ${parts.join("; ")}. A project config cannot change these; an environment variable can, and one can be set by a file in a cloned repository.`
6545
+ }
6546
+ );
6435
6547
  results.push(dataDirPermissionResult(dataDirPath));
6436
6548
  return results;
6437
6549
  }
@@ -6616,7 +6728,96 @@ async function runDoctorAndExit(opts) {
6616
6728
  return results.some((r) => r.status === "fail") ? 1 : 0;
6617
6729
  }
6618
6730
 
6731
+ // src/capabilities.ts
6732
+ init_define_import_meta_env();
6733
+ function collectCapabilities(config = loadConfig()) {
6734
+ const online = !config.network.offline;
6735
+ return [
6736
+ {
6737
+ id: "network.http_fetch",
6738
+ kind: "egress",
6739
+ what: "Fetches a URL when a command asks for one, including page text and remote images.",
6740
+ enabled: online,
6741
+ controlledBy: "network.offline",
6742
+ enforcedAt: "src/webfetch.ts::performHttpFetch"
6743
+ },
6744
+ {
6745
+ id: "network.google_drive",
6746
+ kind: "egress",
6747
+ what: "Exports a Google Doc so a section of it can be read.",
6748
+ // Two independent gates, so this reports the conjunction: turning either one off is enough.
6749
+ enabled: online && config.gdrive.enabled,
6750
+ controlledBy: "gdrive.enabled, network.offline",
6751
+ enforcedAt: "src/gdrive.ts::fetchDoc (network gate in src/webfetch.ts::performHttpFetch)"
6752
+ },
6753
+ {
6754
+ id: "network.embedding_model_download",
6755
+ kind: "egress",
6756
+ what: "Downloads the local embedding model once, so semantic search can run without a server.",
6757
+ enabled: online,
6758
+ controlledBy: "network.offline",
6759
+ enforcedAt: "src/embed_model.ts"
6760
+ },
6761
+ {
6762
+ id: "network.ocr_data_download",
6763
+ kind: "egress",
6764
+ what: "Downloads OCR language data once, so text can be read out of an image.",
6765
+ enabled: online,
6766
+ controlledBy: "network.offline",
6767
+ enforcedAt: "src/image_ocr.ts"
6768
+ },
6769
+ {
6770
+ id: "network.screenshot",
6771
+ kind: "egress",
6772
+ what: "Loads a page in a browser to capture it.",
6773
+ enabled: online,
6774
+ controlledBy: "network.offline",
6775
+ enforcedAt: "src/screenshot.ts"
6776
+ },
6777
+ {
6778
+ id: "at_rest.symbol_index",
6779
+ kind: "at-rest",
6780
+ what: "Stores the text of indexed source files in a local database, so a symbol can be returned without re-reading the file.",
6781
+ // Always on: it is the product. Reported anyway, because a reviewer deciding about data at
6782
+ // rest needs the always-on items more than the optional ones.
6783
+ enabled: true,
6784
+ controlledBy: "always on (this is what the tool does)",
6785
+ enforcedAt: "src/db.ts"
6786
+ },
6787
+ {
6788
+ id: "at_rest.command_output_cache",
6789
+ kind: "at-rest",
6790
+ what: "Keeps recent command and web output on disk so it can be recalled instead of re-run. Expires after 24 hours.",
6791
+ enabled: true,
6792
+ controlledBy: "always on, 24-hour expiry",
6793
+ enforcedAt: "src/disk_cache.ts"
6794
+ }
6795
+ ];
6796
+ }
6797
+ function renderCapabilities(caps) {
6798
+ const lines = [];
6799
+ for (const kind of ["egress", "at-rest"]) {
6800
+ const group = caps.filter((c) => c.kind === kind);
6801
+ if (group.length === 0) continue;
6802
+ lines.push(kind === "egress" ? "Can send data off this machine:" : "Leaves data on this machine:");
6803
+ for (const c of group) {
6804
+ lines.push(` [${c.enabled ? "ON " : "OFF"}] ${c.id}`);
6805
+ lines.push(` ${c.what}`);
6806
+ lines.push(` controlled by: ${c.controlledBy}`);
6807
+ lines.push(` enforced at: ${c.enforcedAt}`);
6808
+ }
6809
+ lines.push("");
6810
+ }
6811
+ lines.push("Verify rather than trust: `token-goat capabilities --json` reports the state of the");
6812
+ lines.push('binary you have installed, and each "enforced at" names the code that decides it.');
6813
+ return lines.join("\n");
6814
+ }
6815
+
6816
+ // src/gdrive.ts
6817
+ init_define_import_meta_env();
6818
+
6619
6819
  // src/webfetch.ts
6820
+ init_define_import_meta_env();
6620
6821
  import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as statSync4, unlinkSync as unlinkSync2 } from "fs";
6621
6822
  import * as http from "http";
6622
6823
  import * as https from "https";
@@ -7075,6 +7276,7 @@ async function getSectionContent(fileId, heading, opts = {}) {
7075
7276
  }
7076
7277
 
7077
7278
  // src/pack.ts
7279
+ init_define_import_meta_env();
7078
7280
  import * as fs13 from "node:fs";
7079
7281
  import * as path14 from "node:path";
7080
7282
 
@@ -7094,8 +7296,13 @@ __export(esm_exports, {
7094
7296
  sep: () => sep2,
7095
7297
  unescape: () => unescape
7096
7298
  });
7299
+ init_define_import_meta_env();
7300
+
7301
+ // node_modules/brace-expansion/dist/esm/index.js
7302
+ init_define_import_meta_env();
7097
7303
 
7098
7304
  // node_modules/balanced-match/dist/esm/index.js
7305
+ init_define_import_meta_env();
7099
7306
  var balanced = (a, b, str) => {
7100
7307
  const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
7101
7308
  const mb2 = b instanceof RegExp ? maybeMatch(b, str) : b;
@@ -7362,6 +7569,7 @@ function expand_(str, max, maxLength, isTop) {
7362
7569
  }
7363
7570
 
7364
7571
  // node_modules/minimatch/dist/esm/assert-valid-pattern.js
7572
+ init_define_import_meta_env();
7365
7573
  var MAX_PATTERN_LENGTH = 1024 * 64;
7366
7574
  var assertValidPattern = (pattern) => {
7367
7575
  if (typeof pattern !== "string") {
@@ -7372,7 +7580,11 @@ var assertValidPattern = (pattern) => {
7372
7580
  }
7373
7581
  };
7374
7582
 
7583
+ // node_modules/minimatch/dist/esm/ast.js
7584
+ init_define_import_meta_env();
7585
+
7375
7586
  // node_modules/minimatch/dist/esm/brace-expressions.js
7587
+ init_define_import_meta_env();
7376
7588
  var posixClasses = {
7377
7589
  "[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
7378
7590
  "[:alpha:]": ["\\p{L}\\p{Nl}", true],
@@ -7482,6 +7694,7 @@ var parseClass = (glob, position) => {
7482
7694
  };
7483
7695
 
7484
7696
  // node_modules/minimatch/dist/esm/unescape.js
7697
+ init_define_import_meta_env();
7485
7698
  var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
7486
7699
  if (magicalBraces) {
7487
7700
  return windowsPathsNoEscape ? s.replace(/\[([^/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^/\\])\]/g, "$1$2").replace(/\\([^/])/g, "$1");
@@ -8134,6 +8347,7 @@ var AST = class {
8134
8347
  _a = AST;
8135
8348
 
8136
8349
  // node_modules/minimatch/dist/esm/escape.js
8350
+ init_define_import_meta_env();
8137
8351
  var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
8138
8352
  if (magicalBraces) {
8139
8353
  return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
@@ -9409,12 +9623,12 @@ function formatBudgetText(result, contextK) {
9409
9623
  lines.push(` ${e.rel_path.padEnd(colW, " ")} ${String(e.lines).padStart(6, " ")} ${String(e.tokens).padStart(8, " ")}`);
9410
9624
  }
9411
9625
  lines.push(` ${"-".repeat(colW)} ${"-".repeat(6)} ${"-".repeat(8)}`);
9412
- let pct2 = "";
9626
+ let pct3 = "";
9413
9627
  if (contextK) {
9414
- pct2 = ` (${Math.round(result.total_tokens / (contextK * 1e3) * 100)}% of ${contextK}K)`;
9628
+ pct3 = ` (${Math.round(result.total_tokens / (contextK * 1e3) * 100)}% of ${contextK}K)`;
9415
9629
  }
9416
9630
  lines.push(
9417
- ` ${"Total".padEnd(colW, " ")} ${String(result.total_lines).padStart(6, " ")} ${String(result.total_tokens).padStart(8, " ")}${pct2}`
9631
+ ` ${"Total".padEnd(colW, " ")} ${String(result.total_lines).padStart(6, " ")} ${String(result.total_tokens).padStart(8, " ")}${pct3}`
9418
9632
  );
9419
9633
  if (result.skipped.length > 0) {
9420
9634
  const skipped = result.skipped.slice(0, 5).join(", ");
@@ -9426,6 +9640,7 @@ function formatBudgetText(result, contextK) {
9426
9640
  }
9427
9641
 
9428
9642
  // src/failures.ts
9643
+ init_define_import_meta_env();
9429
9644
  function getFailureCount(result) {
9430
9645
  return result.blocks.length || result.summaryLines.length;
9431
9646
  }
@@ -9850,6 +10065,7 @@ function formatFailureDeltaJson(delta, runner) {
9850
10065
  }
9851
10066
 
9852
10067
  // src/failures_state.ts
10068
+ init_define_import_meta_env();
9853
10069
  import * as fs14 from "node:fs";
9854
10070
  import * as path15 from "node:path";
9855
10071
  var KEY_RE = /^[A-Za-z0-9_-]{1,80}$/;
@@ -9895,10 +10111,12 @@ function saveFailureSnapshot(projectHash, key, snapshot) {
9895
10111
  }
9896
10112
 
9897
10113
  // src/text_commands.ts
10114
+ init_define_import_meta_env();
9898
10115
  import * as fs16 from "fs";
9899
10116
  import * as path17 from "path";
9900
10117
 
9901
10118
  // src/project_memory.ts
10119
+ init_define_import_meta_env();
9902
10120
  import * as fs15 from "node:fs";
9903
10121
  import * as path16 from "node:path";
9904
10122
  var MAX_ENTRIES = 30;
@@ -11122,6 +11340,7 @@ function countPackIgnorePatterns(root) {
11122
11340
  }
11123
11341
 
11124
11342
  // src/dep_docs.ts
11343
+ init_define_import_meta_env();
11125
11344
  import { createRequire } from "node:module";
11126
11345
  import * as fs17 from "node:fs";
11127
11346
  import * as path18 from "node:path";
@@ -11409,7 +11628,11 @@ function runDepDocs(opts) {
11409
11628
  return { text, code: 0 };
11410
11629
  }
11411
11630
 
11631
+ // src/cache_session_commands.ts
11632
+ init_define_import_meta_env();
11633
+
11412
11634
  // src/resume.ts
11635
+ init_define_import_meta_env();
11413
11636
  import { readFile } from "node:fs/promises";
11414
11637
  var MAX_RESUME_TOKENS = 2e3;
11415
11638
  var MAX_RESUME_CHARS = MAX_RESUME_TOKENS * 4;
@@ -11749,15 +11972,15 @@ function cmdCompactHint(opts) {
11749
11972
  const pressure = getContextPressure(cache ?? void 0);
11750
11973
  const [manifest, eventCount] = sessionId !== null ? buildManifestWithCount(sessionId) : ["", 0];
11751
11974
  const manifestTokens = estimateTokens(manifest);
11752
- const pct2 = (pressure.fillFraction * 100).toFixed(1);
11975
+ const pct3 = (pressure.fillFraction * 100).toFixed(1);
11753
11976
  if (opts.json === true) {
11754
- const out2 = { tier: pressure.tier, fillFraction: pressure.fillFraction, pct: Number(pct2), manifestTokens, eventCount };
11977
+ const out2 = { tier: pressure.tier, fillFraction: pressure.fillFraction, pct: Number(pct3), manifestTokens, eventCount };
11755
11978
  if (sessionId !== null) out2["sessionId"] = sessionId;
11756
11979
  if (opts.trigger !== void 0) out2["trigger"] = opts.trigger;
11757
11980
  process.stdout.write(JSON.stringify(out2, null, 2) + "\n");
11758
11981
  return;
11759
11982
  }
11760
- process.stdout.write(`Compact hint \u2014 context: ${pressure.tier} (${pct2}% full)
11983
+ process.stdout.write(`Compact hint \u2014 context: ${pressure.tier} (${pct3}% full)
11761
11984
  `);
11762
11985
  if (sessionId !== null) process.stdout.write(`Session: ${sessionId}
11763
11986
  `);
@@ -11844,6 +12067,7 @@ function cmdBaseline(opts) {
11844
12067
  }
11845
12068
 
11846
12069
  // src/index_reclaim.ts
12070
+ init_define_import_meta_env();
11847
12071
  import * as fs18 from "node:fs";
11848
12072
  import * as path19 from "node:path";
11849
12073
  var DERIVED_TABLES = ["chunk_vectors", "chunks", "refs", "symbols", "files"];
@@ -11970,6 +12194,7 @@ function cmdReclaimIndex(opts) {
11970
12194
  }
11971
12195
 
11972
12196
  // src/config_commands.ts
12197
+ init_define_import_meta_env();
11973
12198
  import * as fs19 from "node:fs";
11974
12199
  import * as os9 from "node:os";
11975
12200
  import * as path20 from "node:path";
@@ -12639,6 +12864,7 @@ function cmdHistory(opts) {
12639
12864
  }
12640
12865
 
12641
12866
  // src/cli_bootstrap_audit.ts
12867
+ init_define_import_meta_env();
12642
12868
  import * as fs20 from "node:fs";
12643
12869
  import * as os10 from "node:os";
12644
12870
  import * as path21 from "node:path";
@@ -12855,6 +13081,7 @@ async function runBootstrapAudit(opts = {}) {
12855
13081
  }
12856
13082
 
12857
13083
  // src/cli_memory.ts
13084
+ init_define_import_meta_env();
12858
13085
  import * as fs21 from "node:fs";
12859
13086
  import * as path22 from "node:path";
12860
13087
  function removeExactDupLines(text, report) {
@@ -12974,10 +13201,12 @@ async function runMemoryCommand(opts = {}) {
12974
13201
  }
12975
13202
 
12976
13203
  // src/cli_waste.ts
13204
+ init_define_import_meta_env();
12977
13205
  import * as fs24 from "node:fs";
12978
13206
  import * as path25 from "node:path";
12979
13207
 
12980
13208
  // src/waste.ts
13209
+ init_define_import_meta_env();
12981
13210
  import * as fs22 from "node:fs";
12982
13211
  import * as os11 from "node:os";
12983
13212
  import * as path23 from "node:path";
@@ -13225,6 +13454,7 @@ async function buildWasteReport(transcriptPath, opts = {}) {
13225
13454
  }
13226
13455
 
13227
13456
  // src/copilot_waste.ts
13457
+ init_define_import_meta_env();
13228
13458
  import * as fs23 from "node:fs";
13229
13459
  import * as path24 from "node:path";
13230
13460
  var HOOK_RECORD_TYPES = /* @__PURE__ */ new Set(["hook.start", "hook.end"]);
@@ -13379,10 +13609,10 @@ function printCopilotReport(report) {
13379
13609
  w(` Conversation: ${conversationTokens.toLocaleString()} tok
13380
13610
  `);
13381
13611
  if (total > 0) {
13382
- const pct2 = (fixed / total * 100).toFixed(1);
13612
+ const pct3 = (fixed / total * 100).toFixed(1);
13383
13613
  w(` ${fixed.toLocaleString()} tok of system prompt and tool definitions ships with every
13384
13614
  `);
13385
- w(` request; at shutdown that was ${pct2}% of the context. No hook can reach it: Copilot
13615
+ w(` request; at shutdown that was ${pct3}% of the context. No hook can reach it: Copilot
13386
13616
  `);
13387
13617
  w(" assembles both natively, with nothing between assembly and send. The levers are all\n");
13388
13618
  w(" config: fewer MCP servers and custom tools, and --excluded-tools / --available-tools /\n");
@@ -13442,9 +13672,9 @@ function printCopilotReport(report) {
13442
13672
  `);
13443
13673
  } else {
13444
13674
  for (const block of report.blocks) {
13445
- const pct2 = block.bytes > 0 ? (block.repeatBytes / block.bytes * 100).toFixed(1) : "0.0";
13675
+ const pct3 = block.bytes > 0 ? (block.repeatBytes / block.bytes * 100).toFixed(1) : "0.0";
13446
13676
  w(` ${block.kind}: ${countNoun(block.count, "injection")}, ${formatBytes(block.bytes)}`);
13447
- w(block.repeatBytes > 0 ? `, ${formatBytes(block.repeatBytes)} re-sent verbatim (${pct2}%)
13677
+ w(block.repeatBytes > 0 ? `, ${formatBytes(block.repeatBytes)} re-sent verbatim (${pct3}%)
13448
13678
  ` : "\n");
13449
13679
  }
13450
13680
  }
@@ -13609,6 +13839,7 @@ async function runWasteCommand(opts = {}) {
13609
13839
  }
13610
13840
 
13611
13841
  // src/session_read.ts
13842
+ init_define_import_meta_env();
13612
13843
  import * as fs25 from "node:fs";
13613
13844
  import * as path26 from "node:path";
13614
13845
  import * as readline2 from "node:readline";
@@ -13775,6 +14006,7 @@ function formatSessionSlice(turns) {
13775
14006
  }
13776
14007
 
13777
14008
  // src/session_audit.ts
14009
+ init_define_import_meta_env();
13778
14010
  import * as fs26 from "node:fs";
13779
14011
  import * as os12 from "node:os";
13780
14012
  import * as path27 from "node:path";
@@ -14132,13 +14364,13 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
14132
14364
  s.readInterception.readResults += 1;
14133
14365
  const denyTemplate = DENY_TEMPLATES.find((t) => t.re.test(text));
14134
14366
  if (denyTemplate !== void 0) {
14135
- const path30 = readPathById.get(id) ?? "";
14136
- const lastSlash = path30.lastIndexOf("/");
14367
+ const path31 = readPathById.get(id) ?? "";
14368
+ const lastSlash = path31.lastIndexOf("/");
14137
14369
  openDenies.push({
14138
14370
  kind: denyTemplate.kind,
14139
14371
  withheldBytes: parseWithheldBytes(denyTemplate.kind, text),
14140
- path: path30,
14141
- basename: lastSlash === -1 ? path30 : path30.slice(lastSlash + 1),
14372
+ path: path31,
14373
+ basename: lastSlash === -1 ? path31 : path31.slice(lastSlash + 1),
14142
14374
  callIndexAtDeny: perCall.length,
14143
14375
  compactEpochAtOpen: compactEpoch,
14144
14376
  compactEpochAtWindow: null,
@@ -14606,6 +14838,7 @@ function formatSessionAudit(s) {
14606
14838
  }
14607
14839
 
14608
14840
  // src/cli_mcp_audit.ts
14841
+ init_define_import_meta_env();
14609
14842
  import * as fs27 from "node:fs";
14610
14843
  import * as os13 from "node:os";
14611
14844
  import * as path28 from "node:path";
@@ -14759,6 +14992,7 @@ async function runMcpAuditCommand(opts = {}) {
14759
14992
  }
14760
14993
 
14761
14994
  // src/cli_recall.ts
14995
+ init_define_import_meta_env();
14762
14996
  var RECALL_COMMAND = {
14763
14997
  bash: "bash-output",
14764
14998
  web: "web-output",
@@ -14818,7 +15052,231 @@ function runRecallCommand(query, opts = {}) {
14818
15052
  printHits(browse ? void 0 : query, hits);
14819
15053
  }
14820
15054
 
15055
+ // src/cli_bench.ts
15056
+ init_define_import_meta_env();
15057
+ import * as fs28 from "node:fs";
15058
+ import * as path29 from "node:path";
15059
+ var ControlFilter = class extends ToolFilter {
15060
+ constructor(name, transform) {
15061
+ super();
15062
+ this.name = name;
15063
+ this.transform = transform;
15064
+ }
15065
+ name;
15066
+ transform;
15067
+ apply(stdout, stderr, exitCode) {
15068
+ const originalBytes = Buffer.byteLength(stdout, "utf8") + Buffer.byteLength(stderr, "utf8");
15069
+ const text = this.transform(combineStreams(stdout, stderr));
15070
+ return new CompressedOutput(text, originalBytes, Buffer.byteLength(text, "utf8"), this.name, exitCode);
15071
+ }
15072
+ };
15073
+ var CONTROL_FILTERS = {
15074
+ identity: new ControlFilter("control:identity", (s) => s),
15075
+ destroy: new ControlFilter("control:destroy", () => "")
15076
+ };
15077
+ var MAX_BENCH_FILE_BYTES = 5 * 1024 * 1024;
15078
+ function readCaseFile(id, filePath, what) {
15079
+ const bytes = fs28.statSync(filePath).size;
15080
+ if (bytes > MAX_BENCH_FILE_BYTES) {
15081
+ throw new Error(
15082
+ `bench case ${id}: ${what} is ${bytes} bytes, over the ${MAX_BENCH_FILE_BYTES}-byte limit for a corpus file (${filePath})`
15083
+ );
15084
+ }
15085
+ return fs28.readFileSync(filePath, "utf8");
15086
+ }
15087
+ function parseCase(id, metaPath, outputPath) {
15088
+ const metaText = readCaseFile(id, metaPath, "metadata");
15089
+ let raw;
15090
+ try {
15091
+ raw = JSON.parse(metaText);
15092
+ } catch (e) {
15093
+ throw new Error(`bench case ${id}: ${metaPath} is not valid JSON (${e instanceof Error ? e.message : String(e)})`, { cause: e });
15094
+ }
15095
+ if (typeof raw !== "object" || raw === null) throw new Error(`bench case ${id}: expected a JSON object`);
15096
+ const meta = raw;
15097
+ const str = (key) => {
15098
+ const v = meta[key];
15099
+ if (typeof v !== "string" || v.trim() === "") throw new Error(`bench case ${id}: "${key}" must be a non-empty string`);
15100
+ return v;
15101
+ };
15102
+ const mustKeep = meta["mustKeep"];
15103
+ if (!Array.isArray(mustKeep) || mustKeep.some((s) => typeof s !== "string" || s === "")) {
15104
+ throw new Error(`bench case ${id}: "mustKeep" must be an array of non-empty strings`);
15105
+ }
15106
+ const exitCode = meta["exitCode"];
15107
+ if (exitCode !== void 0 && typeof exitCode !== "number") throw new Error(`bench case ${id}: "exitCode" must be a number`);
15108
+ return {
15109
+ id,
15110
+ provenance: str("provenance"),
15111
+ command: str("command"),
15112
+ exitCode: typeof exitCode === "number" ? exitCode : 0,
15113
+ mustKeep,
15114
+ output: readCaseFile(id, outputPath, "output")
15115
+ };
15116
+ }
15117
+ function loadCorpus(dir) {
15118
+ let entries;
15119
+ try {
15120
+ entries = fs28.readdirSync(dir);
15121
+ } catch {
15122
+ throw new Error(`bench corpus not found: ${dir}`);
15123
+ }
15124
+ const cases = [];
15125
+ for (const entry of entries.sort()) {
15126
+ if (!entry.endsWith(".json")) continue;
15127
+ const id = entry.slice(0, -".json".length);
15128
+ const outputPath = path29.join(dir, `${id}.txt`);
15129
+ if (!fs28.existsSync(outputPath)) throw new Error(`bench case ${id}: missing output file ${outputPath}`);
15130
+ cases.push(parseCase(id, path29.join(dir, entry), outputPath));
15131
+ }
15132
+ if (cases.length === 0) throw new Error(`bench corpus is empty: ${dir}`);
15133
+ return cases;
15134
+ }
15135
+ function runCase(c, control = "none") {
15136
+ const detected = detectFromCommand(c.command);
15137
+ const filter2 = control === "none" ? detected?.filter : CONTROL_FILTERS[control];
15138
+ const argv = detected?.argv ?? [];
15139
+ const delivery = filter2 ? deliverCompressed(filter2, c.output, "", c.exitCode, argv) : null;
15140
+ const body = delivery ? delivery.text + delivery.marker : c.output;
15141
+ const originalBytes = Buffer.byteLength(c.output, "utf8");
15142
+ const deliveredBytes = Buffer.byteLength(body, "utf8");
15143
+ const missing = c.mustKeep.filter((needle) => !body.includes(needle));
15144
+ return {
15145
+ id: c.id,
15146
+ command: c.command,
15147
+ filter: filter2?.name ?? null,
15148
+ applied: delivery?.applied ?? false,
15149
+ originalBytes,
15150
+ deliveredBytes,
15151
+ savedPercent: originalBytes > 0 ? 100 * Math.max(0, originalBytes - deliveredBytes) / originalBytes : 0,
15152
+ kept: c.mustKeep.length - missing.length,
15153
+ mustKeepTotal: c.mustKeep.length,
15154
+ missing
15155
+ };
15156
+ }
15157
+ function runCorpus(cases, control = "none") {
15158
+ const results = cases.map((c) => runCase(c, control));
15159
+ const sum = (pick) => results.reduce((n, r) => n + pick(r), 0);
15160
+ const originalBytes = sum((r) => r.originalBytes);
15161
+ const deliveredBytes = sum((r) => r.deliveredBytes);
15162
+ const kept = sum((r) => r.kept);
15163
+ const mustKeepTotal = sum((r) => r.mustKeepTotal);
15164
+ return {
15165
+ cases: results,
15166
+ originalBytes,
15167
+ deliveredBytes,
15168
+ ratioPercent: originalBytes > 0 ? 100 * Math.max(0, originalBytes - deliveredBytes) / originalBytes : 0,
15169
+ kept,
15170
+ mustKeepTotal,
15171
+ appliedCases: results.filter((r) => r.applied).length,
15172
+ coveredFilters: new Set(results.map((r) => r.filter).filter((n) => n !== null)).size,
15173
+ registeredFilters: TOOL_FILTERS.length,
15174
+ fidelityIntact: kept === mustKeepTotal
15175
+ };
15176
+ }
15177
+ var pct2 = (n) => `${n.toFixed(1)}%`;
15178
+ function renderTable(report, floorPercent) {
15179
+ const rows = [...report.cases].sort((a, b) => b.originalBytes - a.originalBytes);
15180
+ const idWidth = Math.max(4, ...rows.map((r) => r.id.length));
15181
+ const filterWidth = Math.max(6, ...rows.map((r) => (r.filter ?? "-").length));
15182
+ const lines = [];
15183
+ const header = `${"case".padEnd(idWidth)} ${"filter".padEnd(filterWidth)} ${"in".padStart(9)} ${"out".padStart(9)} ${"saved".padStart(7)} fidelity`;
15184
+ lines.push(header, "-".repeat(header.length));
15185
+ for (const r of rows) {
15186
+ const saved = r.applied ? pct2(r.savedPercent) : "-";
15187
+ const fidelity = `${r.kept}/${r.mustKeepTotal}${r.missing.length ? " FAIL" : ""}`;
15188
+ lines.push(
15189
+ `${r.id.padEnd(idWidth)} ${(r.filter ?? "-").padEnd(filterWidth)} ${String(r.originalBytes).padStart(9)} ${String(r.deliveredBytes).padStart(9)} ${saved.padStart(7)} ${fidelity}`
15190
+ );
15191
+ }
15192
+ lines.push("-".repeat(header.length));
15193
+ lines.push(
15194
+ `${"TOTAL".padEnd(idWidth)} ${"".padEnd(filterWidth)} ${String(report.originalBytes).padStart(9)} ${String(report.deliveredBytes).padStart(9)} ${pct2(report.ratioPercent).padStart(7)} ${report.kept}/${report.mustKeepTotal}`
15195
+ );
15196
+ lines.push("");
15197
+ lines.push(`ratio ${pct2(report.ratioPercent)} saved (PRIMARY -- must improve; measured floor ${pct2(floorPercent)}, headroom ${pct2(100 - report.ratioPercent)})`);
15198
+ lines.push(`fidelity ${report.kept}/${report.mustKeepTotal} kept (GUARD -- must not regress; any miss exits 1)`);
15199
+ lines.push(`coverage ${report.coveredFilters}/${report.registeredFilters} filters exercised, ${report.appliedCases}/${report.cases.length} cases compressed`);
15200
+ for (const r of report.cases) {
15201
+ for (const needle of r.missing) lines.push(` DROPPED ${r.id}: ${JSON.stringify(needle)}`);
15202
+ }
15203
+ return lines.join("\n");
15204
+ }
15205
+ function renderValidation(cases) {
15206
+ const identity = runCorpus(cases, "identity");
15207
+ const destroy = runCorpus(cases, "destroy");
15208
+ const checks = [
15209
+ {
15210
+ // Not "scores exactly 0%": the unapplied path delivers the streams recombined, which can
15211
+ // differ from the captured bytes by a trailing newline. That is a real property of delivery,
15212
+ // so the floor is measured and reported rather than assumed -- what must hold is that the
15213
+ // net-benefit gate REFUSED every no-op rewrite, which is what makes the floor a floor.
15214
+ label: "floor: the net-benefit gate refuses a no-op filter on every case",
15215
+ ok: identity.appliedCases === 0,
15216
+ detail: `identity control applied to ${identity.appliedCases}/${identity.cases.length} cases, measured floor ${pct2(identity.ratioPercent)}`
15217
+ },
15218
+ {
15219
+ label: "floor: a no-op filter keeps every must-keep line",
15220
+ ok: identity.fidelityIntact,
15221
+ detail: `identity control kept ${identity.kept}/${identity.mustKeepTotal}`
15222
+ },
15223
+ {
15224
+ label: "guard: deleting everything is caught",
15225
+ ok: !destroy.fidelityIntact,
15226
+ detail: `destroy control kept ${destroy.kept}/${destroy.mustKeepTotal} at ${pct2(destroy.ratioPercent)} saved`
15227
+ }
15228
+ ];
15229
+ const failed = checks.filter((c) => !c.ok);
15230
+ const lines = checks.map((c) => `${c.ok ? "ok " : "FAIL"} ${c.label} -- ${c.detail}`);
15231
+ lines.push("");
15232
+ lines.push(
15233
+ failed.length === 0 ? "The corpus discriminates: a ratio measured against it cannot be raised by deleting content." : `${failed.length} control check(s) failed. Do not optimise against this corpus until they pass.`
15234
+ );
15235
+ return { text: lines.join("\n"), code: failed.length === 0 ? 0 : 1 };
15236
+ }
15237
+ var TSV_HEADER = "timestamp commit cases original_bytes delivered_bytes ratio_percent fidelity_kept fidelity_total covered_filters\n";
15238
+ function appendTsv(file, report, commit) {
15239
+ const row = [
15240
+ (/* @__PURE__ */ new Date()).toISOString(),
15241
+ commit,
15242
+ report.cases.length,
15243
+ report.originalBytes,
15244
+ report.deliveredBytes,
15245
+ report.ratioPercent.toFixed(2),
15246
+ report.kept,
15247
+ report.mustKeepTotal,
15248
+ report.coveredFilters
15249
+ ].join(" ");
15250
+ const dir = path29.dirname(path29.resolve(file));
15251
+ fs28.mkdirSync(dir, { recursive: true });
15252
+ const header = fs28.existsSync(file) ? "" : TSV_HEADER;
15253
+ fs28.appendFileSync(file, header + row + "\n", "utf8");
15254
+ }
15255
+ function currentCommit(cwd) {
15256
+ const opts = cwd === void 0 ? {} : { cwd };
15257
+ try {
15258
+ const r = runGit(["rev-parse", "--short", "HEAD"], opts);
15259
+ const sha = r.stdout.trim();
15260
+ if (r.exitCode !== 0 || sha === "") return "unknown";
15261
+ const st = runGit(["status", "--porcelain"], opts);
15262
+ return st.exitCode === 0 && st.stdout.trim() !== "" ? `${sha}-dirty` : sha;
15263
+ } catch {
15264
+ return "unknown";
15265
+ }
15266
+ }
15267
+ function runBenchCommand(opts) {
15268
+ const cases = loadCorpus(opts.corpus);
15269
+ if (opts.validate === true) return renderValidation(cases);
15270
+ const report = runCorpus(cases);
15271
+ if (opts.tsv !== void 0) appendTsv(opts.tsv, report, currentCommit());
15272
+ const code = report.fidelityIntact ? 0 : 1;
15273
+ const floorPercent = runCorpus(cases, "identity").ratioPercent;
15274
+ if (opts.json === true) return { text: JSON.stringify({ ...report, floorPercent }, null, 2), code };
15275
+ return { text: renderTable(report, floorPercent), code };
15276
+ }
15277
+
14821
15278
  // src/cli_hint_stats.ts
15279
+ init_define_import_meta_env();
14822
15280
  function formatSpentCell(row) {
14823
15281
  if (row.emitted === 0) return "n/a";
14824
15282
  if (row.bytesEmitted === null) return "n/a (legacy)";
@@ -14834,9 +15292,9 @@ function printSummary(rows) {
14834
15292
  };
14835
15293
  w(pad("category", 22) + pad("emitted", 9) + pad("acted-on", 10) + pad("efficacy", 10) + pad("suppressed", 17) + pad("manual+", 9) + pad("manual-", 9) + "spent\n");
14836
15294
  for (const row of rows) {
14837
- const pct2 = row.efficacyPct === null ? "n/a" : `${row.efficacyPct}%`;
15295
+ const pct3 = row.efficacyPct === null ? "n/a" : `${row.efficacyPct}%`;
14838
15296
  w(
14839
- pad(row.category, 22) + pad(String(row.emitted), 9) + pad(String(row.actedOn), 10) + pad(pct2, 10) + pad(suppressedCell(row), 17) + pad(String(row.manualEffective), 9) + pad(String(row.manualIneffective), 9) + formatSpentCell(row) + "\n"
15297
+ pad(row.category, 22) + pad(String(row.emitted), 9) + pad(String(row.actedOn), 10) + pad(pct3, 10) + pad(suppressedCell(row), 17) + pad(String(row.manualEffective), 9) + pad(String(row.manualIneffective), 9) + formatSpentCell(row) + "\n"
14840
15298
  );
14841
15299
  }
14842
15300
  }
@@ -14888,6 +15346,7 @@ hints.backoff_thresholds is empty, so suppression here is permanent rather than
14888
15346
  }
14889
15347
 
14890
15348
  // src/cli_statusline.ts
15349
+ init_define_import_meta_env();
14891
15350
  var STDIN_TIMEOUT_MS = 1500;
14892
15351
  function isPlainObject(v) {
14893
15352
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -14981,10 +15440,10 @@ function readBoundedText(text, file) {
14981
15440
  if (text === void 0 && file === void 0) throw new CliError("provide text or --file");
14982
15441
  let value;
14983
15442
  if (file !== void 0) {
14984
- if (fs28.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
15443
+ if (fs29.statSync(file).size > CONTENT_MAX_INPUT_CHARS) {
14985
15444
  throw new CliError(`file exceeds the ${CONTENT_MAX_INPUT_CHARS}-byte safety limit`);
14986
15445
  }
14987
- value = fs28.readFileSync(file, "utf8");
15446
+ value = fs29.readFileSync(file, "utf8");
14988
15447
  } else {
14989
15448
  if (text === void 0) throw new CliError("provide text or --file");
14990
15449
  value = text;
@@ -15122,7 +15581,7 @@ async function cmdIndex(pathArg, opts = {}) {
15122
15581
  continue;
15123
15582
  }
15124
15583
  const sha = fingerprintFile(key);
15125
- if (sha === null && !fs28.existsSync(key)) continue;
15584
+ if (sha === null && !fs29.existsSync(key)) continue;
15126
15585
  const entry = sha !== null ? getFileEntry(key, dbPath) : null;
15127
15586
  const spellingStale = entry !== null && indexedPathSpellingIsStale(entry.filePath, key);
15128
15587
  const parseUnchanged = !force && !spellingStale && sha !== null && entry?.sha === sha && entry.parserSha === PARSER_FINGERPRINT;
@@ -15148,7 +15607,7 @@ async function cmdIndex(pathArg, opts = {}) {
15148
15607
  }
15149
15608
  continue;
15150
15609
  }
15151
- if (!fs28.existsSync(key)) continue;
15610
+ if (!fs29.existsSync(key)) continue;
15152
15611
  }
15153
15612
  if (!embedUnchanged) {
15154
15613
  paintProgress("embedding");
@@ -15208,8 +15667,8 @@ async function cmdMcpServe() {
15208
15667
  let StdioServerTransport;
15209
15668
  try {
15210
15669
  ;
15211
- ({ createMcpServer } = await import("./token-goat-chunk-CD6U5OD6.mjs"));
15212
- ({ StdioServerTransport } = await import("./token-goat-chunk-324QOJYZ.mjs"));
15670
+ ({ createMcpServer } = await import("./token-goat-chunk-3UBORD6Z.mjs"));
15671
+ ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
15213
15672
  } catch (err2) {
15214
15673
  process.stderr.write(
15215
15674
  `token-goat: mcp-server unavailable (could not load the MCP server modules): ${String(err2)}
@@ -15221,8 +15680,8 @@ async function cmdMcpServe() {
15221
15680
  const server = await createMcpServer();
15222
15681
  const transport = new StdioServerTransport();
15223
15682
  await server.connect(transport);
15224
- await new Promise((resolve14) => {
15225
- server.onclose = resolve14;
15683
+ await new Promise((resolve15) => {
15684
+ server.onclose = resolve15;
15226
15685
  });
15227
15686
  }
15228
15687
  function printBridgeVerificationNotice(harness) {
@@ -15233,7 +15692,7 @@ async function cmdHook(event, opts) {
15233
15692
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
15234
15693
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
15235
15694
  }
15236
- const { relay } = await import("./token-goat-chunk-AUM22UQ2.mjs");
15695
+ const { relay } = await import("./token-goat-chunk-HC5NMGPD.mjs");
15237
15696
  await relay(event);
15238
15697
  }
15239
15698
  async function cmdInstall(opts) {
@@ -15346,16 +15805,16 @@ async function cmdInstall(opts) {
15346
15805
  );
15347
15806
  }
15348
15807
  try {
15349
- const skillDir = path29.join(homedir13(), ".claude", "skills");
15350
- if (fs28.existsSync(skillDir)) {
15351
- const entries = fs28.readdirSync(skillDir, { withFileTypes: true });
15808
+ const skillDir = path30.join(homedir13(), ".claude", "skills");
15809
+ if (fs29.existsSync(skillDir)) {
15810
+ const entries = fs29.readdirSync(skillDir, { withFileTypes: true });
15352
15811
  const skillNames = [];
15353
15812
  const sessionId = getSessionId();
15354
15813
  for (const entry of entries) {
15355
15814
  if (!entry.isDirectory()) continue;
15356
- const skillFile = path29.join(skillDir, entry.name, "SKILL.md");
15357
- if (fs28.existsSync(skillFile)) {
15358
- const body = fs28.readFileSync(skillFile, "utf-8");
15815
+ const skillFile = path30.join(skillDir, entry.name, "SKILL.md");
15816
+ if (fs29.existsSync(skillFile)) {
15817
+ const body = fs29.readFileSync(skillFile, "utf-8");
15359
15818
  const compact = extractCompactFromMarker(body);
15360
15819
  if (compact === null) continue;
15361
15820
  const sourceSha = contentHash(body);
@@ -15365,10 +15824,10 @@ async function cmdInstall(opts) {
15365
15824
  }
15366
15825
  if (skillNames.length > 0) {
15367
15826
  const dir = skillOutputsDir();
15368
- await fs28.promises.mkdir(dir, { recursive: true });
15369
- const pregenPath = path29.join(dir, "pregen.json");
15827
+ await fs29.promises.mkdir(dir, { recursive: true });
15828
+ const pregenPath = path30.join(dir, "pregen.json");
15370
15829
  const pregenData = { ts: Date.now(), names: skillNames };
15371
- await fs28.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
15830
+ await fs29.promises.writeFile(pregenPath, JSON.stringify(pregenData, null, 2));
15372
15831
  out(`Pre-generated ${skillNames.length} skill compacts.`);
15373
15832
  }
15374
15833
  }
@@ -15572,7 +16031,7 @@ ${formatSessionOutline(turns)}`;
15572
16031
  }
15573
16032
  function sessionTranscriptSize(transcriptPath) {
15574
16033
  try {
15575
- return fs28.statSync(transcriptPath).size;
16034
+ return fs29.statSync(transcriptPath).size;
15576
16035
  } catch {
15577
16036
  return 0;
15578
16037
  }
@@ -15702,7 +16161,7 @@ function fenceFileFieldIfMatched(text) {
15702
16161
  }
15703
16162
  function fileSizeOrZero(filePath) {
15704
16163
  try {
15705
- return fs28.statSync(filePath).size;
16164
+ return fs29.statSync(filePath).size;
15706
16165
  } catch {
15707
16166
  return 0;
15708
16167
  }
@@ -15717,11 +16176,11 @@ function cmdBashOutput(id, opts) {
15717
16176
  }
15718
16177
  let content;
15719
16178
  try {
15720
- const st = fs28.statSync(opts.file);
16179
+ const st = fs29.statSync(opts.file);
15721
16180
  if (st.isFIFO() || st.isSocket()) {
15722
16181
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
15723
16182
  }
15724
- content = redactIfDotenv(opts.file, decodeSource(fs28.readFileSync(opts.file)));
16183
+ content = redactIfDotenv(opts.file, decodeSource(fs29.readFileSync(opts.file)));
15725
16184
  } catch (e) {
15726
16185
  if (e instanceof CliError) throw e;
15727
16186
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -15830,9 +16289,9 @@ async function cmdPdfMeta(file, opts = {}) {
15830
16289
  }
15831
16290
  async function cmdImageMeta(file, opts = {}) {
15832
16291
  const meta = await runImageMeta(file);
15833
- if (!meta.sharpAvailable) {
15834
- const msg = "image-meta unavailable (install sharp to use this feature)";
15835
- const text2 = opts.json === true ? JSON.stringify({ bytes: meta.bytes, sharpAvailable: false, error: msg }, null, 2) : `Size: ${meta.bytes} bytes
16292
+ if (!meta.decodable) {
16293
+ const msg = "image-meta unavailable (not a format token-goat can read)";
16294
+ const text2 = opts.json === true ? JSON.stringify({ bytes: meta.bytes, decodable: false, error: msg }, null, 2) : `Size: ${meta.bytes} bytes
15836
16295
  ${msg}`;
15837
16296
  out(text2);
15838
16297
  return;
@@ -16131,7 +16590,7 @@ function readStdinPaths() {
16131
16590
  }
16132
16591
  let raw;
16133
16592
  try {
16134
- raw = fs28.readFileSync(0, "utf8");
16593
+ raw = fs29.readFileSync(0, "utf8");
16135
16594
  } catch (e) {
16136
16595
  throw new CliError(`could not read the file list from stdin: ${extractErrorMessage(e)}`);
16137
16596
  }
@@ -16167,7 +16626,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
16167
16626
  }
16168
16627
  async function cmdCompress(opts) {
16169
16628
  try {
16170
- const bashRunner = await import("./token-goat-chunk-4KD2N46D.mjs");
16629
+ const bashRunner = await import("./token-goat-chunk-LLNA42NI.mjs");
16171
16630
  if (opts.compress === false) {
16172
16631
  process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
16173
16632
  return;
@@ -16193,7 +16652,7 @@ async function cmdSkillBody(name, opts) {
16193
16652
  if (filePath === null) {
16194
16653
  throw new CliError(`skill '${name}' not found`);
16195
16654
  }
16196
- const body = decodeSource(fs28.readFileSync(filePath));
16655
+ const body = decodeSource(fs29.readFileSync(filePath));
16197
16656
  if (opts.compact === true) {
16198
16657
  const emitted = extractCompactFromMarker(body) ?? body;
16199
16658
  out(emitted);
@@ -16220,7 +16679,7 @@ async function cmdSkillCompact(name, opts) {
16220
16679
  }
16221
16680
  let body2;
16222
16681
  try {
16223
- body2 = decodeSource(fs28.readFileSync(filePath));
16682
+ body2 = decodeSource(fs29.readFileSync(filePath));
16224
16683
  } catch {
16225
16684
  unresolvable++;
16226
16685
  continue;
@@ -16254,19 +16713,19 @@ async function cmdSkillCompact(name, opts) {
16254
16713
  if (!opts.path.trim()) {
16255
16714
  throw new CliError("--path cannot be empty");
16256
16715
  }
16257
- if (!fs28.existsSync(opts.path)) {
16716
+ if (!fs29.existsSync(opts.path)) {
16258
16717
  throw new CliError(`skill file not found: ${opts.path}`);
16259
16718
  }
16260
16719
  try {
16261
- body = fs28.readFileSync(opts.path, "utf-8");
16720
+ body = fs29.readFileSync(opts.path, "utf-8");
16262
16721
  } catch (e) {
16263
16722
  if (e.code === "ENOENT") {
16264
16723
  throw new CliError(`skill file not found: ${opts.path}`);
16265
16724
  }
16266
16725
  throw new CliError(`failed to read skill file '${opts.path}': ${extractErrorMessage(e)}`);
16267
16726
  }
16268
- cacheName = name ?? path29.basename(path29.dirname(path29.resolve(opts.path)));
16269
- sourcePath = path29.resolve(opts.path);
16727
+ cacheName = name ?? path30.basename(path30.dirname(path30.resolve(opts.path)));
16728
+ sourcePath = path30.resolve(opts.path);
16270
16729
  } else {
16271
16730
  if (name === void 0 || !name.trim()) {
16272
16731
  throw new CliError("skill-compact requires a <name> or --path <file>");
@@ -16275,7 +16734,7 @@ async function cmdSkillCompact(name, opts) {
16275
16734
  if (filePath === null) {
16276
16735
  throw new CliError(`skill '${name}' not found`);
16277
16736
  }
16278
- body = fs28.readFileSync(filePath, "utf-8");
16737
+ body = fs29.readFileSync(filePath, "utf-8");
16279
16738
  cacheName = name;
16280
16739
  sourcePath = filePath;
16281
16740
  }
@@ -16407,8 +16866,8 @@ async function cmdSkillDiff(name) {
16407
16866
  }
16408
16867
  const newer = versions[0];
16409
16868
  const older = versions[1];
16410
- const newerBody = await fs28.promises.readFile(path29.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
16411
- const olderBody = await fs28.promises.readFile(path29.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
16869
+ const newerBody = await fs29.promises.readFile(path30.resolve(dir, `${newer.outputId}.txt`), "utf-8").catch(() => null);
16870
+ const olderBody = await fs29.promises.readFile(path30.resolve(dir, `${older.outputId}.txt`), "utf-8").catch(() => null);
16412
16871
  if (newerBody === null || olderBody === null) {
16413
16872
  out(`a cached version of '${name}' was evicted while diffing -- try again`);
16414
16873
  return;
@@ -16437,7 +16896,7 @@ async function cmdSkillSection(nameHeading, headingArg) {
16437
16896
  if (!filePath) {
16438
16897
  throw new CliError(`skill '${skillName}' not found`);
16439
16898
  }
16440
- const body = decodeSource(fs28.readFileSync(filePath));
16899
+ const body = decodeSource(fs29.readFileSync(filePath));
16441
16900
  const extracted = extractNamedSection(body, heading);
16442
16901
  if (!extracted) {
16443
16902
  const messages = [`Section '${heading}' not found in skill '${skillName}'`];
@@ -16452,7 +16911,7 @@ async function cmdSkillSection(nameHeading, headingArg) {
16452
16911
  }
16453
16912
  function atomicWriteBuffer(dest, data) {
16454
16913
  try {
16455
- if (fs28.statSync(dest).isDirectory()) {
16914
+ if (fs29.statSync(dest).isDirectory()) {
16456
16915
  const e = Object.assign(new Error(`EISDIR: illegal operation on a directory, open '${dest}'`), { code: "EISDIR", path: dest });
16457
16916
  throw e;
16458
16917
  }
@@ -16460,23 +16919,23 @@ function atomicWriteBuffer(dest, data) {
16460
16919
  if (e.code !== "ENOENT") throw e;
16461
16920
  }
16462
16921
  const rnd = randomBytes(4).toString("hex");
16463
- const tmp = path29.join(path29.dirname(path29.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
16922
+ const tmp = path30.join(path30.dirname(path30.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
16464
16923
  try {
16465
- fs28.writeFileSync(tmp, data, { mode: 384 });
16924
+ fs29.writeFileSync(tmp, data, { mode: 384 });
16466
16925
  try {
16467
- const destMode = fs28.statSync(dest).mode;
16468
- fs28.chmodSync(tmp, destMode);
16926
+ const destMode = fs29.statSync(dest).mode;
16927
+ fs29.chmodSync(tmp, destMode);
16469
16928
  } catch (e) {
16470
16929
  if (e.code !== "ENOENT") throw e;
16471
16930
  }
16472
16931
  withRetryOnLock(() => {
16473
16932
  try {
16474
- fs28.renameSync(tmp, dest);
16933
+ fs29.renameSync(tmp, dest);
16475
16934
  } catch (e) {
16476
16935
  if (e.code === "EXDEV") {
16477
- fs28.copyFileSync(tmp, dest);
16936
+ fs29.copyFileSync(tmp, dest);
16478
16937
  try {
16479
- fs28.unlinkSync(tmp);
16938
+ fs29.unlinkSync(tmp);
16480
16939
  } catch (ue) {
16481
16940
  process.stderr.write(`token-goat write-file: warning: could not remove temp file ${tmp}: ${ue.message}
16482
16941
  `);
@@ -16488,7 +16947,7 @@ function atomicWriteBuffer(dest, data) {
16488
16947
  });
16489
16948
  } catch (e) {
16490
16949
  try {
16491
- fs28.unlinkSync(tmp);
16950
+ fs29.unlinkSync(tmp);
16492
16951
  } catch {
16493
16952
  }
16494
16953
  throw e;
@@ -16498,9 +16957,9 @@ function mapFsError(e, src, dest, srcLabel = "source") {
16498
16957
  const fe = e;
16499
16958
  if (fe.code === "ENOENT") {
16500
16959
  const errPath = fe.path ?? "";
16501
- const isSource = src !== void 0 && path29.resolve(errPath) === path29.resolve(src);
16960
+ const isSource = src !== void 0 && path30.resolve(errPath) === path30.resolve(src);
16502
16961
  if (isSource) throw new CliError(`${/\bfile$/i.test(srcLabel) ? srcLabel : `${srcLabel} file`} not found: ${src}`);
16503
- const destDir = dest ? path29.dirname(path29.resolve(dest)) : path29.dirname(path29.resolve(errPath || "."));
16962
+ const destDir = dest ? path30.dirname(path30.resolve(dest)) : path30.dirname(path30.resolve(errPath || "."));
16504
16963
  throw new CliError(`destination directory does not exist: ${destDir}`);
16505
16964
  }
16506
16965
  if (fe.code === "ENOTDIR") {
@@ -16511,7 +16970,7 @@ function mapFsError(e, src, dest, srcLabel = "source") {
16511
16970
  }
16512
16971
  if (fe.code === "EISDIR") {
16513
16972
  const errPath = fe.path ?? "";
16514
- const isSource = src !== void 0 && (errPath === "" || path29.resolve(errPath) === path29.resolve(src));
16973
+ const isSource = src !== void 0 && (errPath === "" || path30.resolve(errPath) === path30.resolve(src));
16515
16974
  if (isSource) throw new CliError(`source is a directory, not a file: ${src}`);
16516
16975
  throw new CliError(`destination is a directory, not a file: ${dest ?? (errPath || "(unknown)")}`);
16517
16976
  }
@@ -16580,7 +17039,7 @@ function validateWritablePath(dest, label) {
16580
17039
  throw new CliError(`${label} path contains a null byte`);
16581
17040
  }
16582
17041
  if (isWindows()) {
16583
- const base = path29.basename(dest);
17042
+ const base = path30.basename(dest);
16584
17043
  const stem = base.replace(/\.[^.]*$/, "").toUpperCase();
16585
17044
  if (WIN_RESERVED.has(stem)) {
16586
17045
  throw new CliError(`${label} '${base}' is a reserved Windows device name`);
@@ -16610,7 +17069,7 @@ function readFileBoundedRaw(filePath, label, allowStdIn = false) {
16610
17069
  throw new CliError(`${label} ${filePath} requires piped input; use ${altLabel} for interactive use`);
16611
17070
  }
16612
17071
  try {
16613
- const st = fs28.statSync(filePath);
17072
+ const st = fs29.statSync(filePath);
16614
17073
  if (st.isFIFO() || st.isSocket()) {
16615
17074
  throw new CliError(`${label} '${filePath}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
16616
17075
  }
@@ -16618,7 +17077,7 @@ function readFileBoundedRaw(filePath, label, allowStdIn = false) {
16618
17077
  if (st.size > maxBytes) {
16619
17078
  throw new CliError(`${label} '${filePath}' exceeds size limit (${Math.round(st.size / 1024 / 1024)} MB); set TOKEN_GOAT_MAX_STDIN_MB to override`);
16620
17079
  }
16621
- return fs28.readFileSync(filePath);
17080
+ return fs29.readFileSync(filePath);
16622
17081
  } catch (e) {
16623
17082
  if (e instanceof CliError) throw e;
16624
17083
  mapFsError(e, filePath, void 0, label);
@@ -16662,7 +17121,7 @@ function cmdNoteAdd(file, opts) {
16662
17121
  throw new CliError("note content must be valid UTF-8 text");
16663
17122
  }
16664
17123
  const resolvedPath = resolveIndexPath(file);
16665
- if (!fs28.existsSync(resolvedPath)) {
17124
+ if (!fs29.existsSync(resolvedPath)) {
16666
17125
  throw new CliError(`File not found: '${resolvedPath}'`);
16667
17126
  }
16668
17127
  healStaleIndex(resolvedPath);
@@ -16721,7 +17180,7 @@ function cmdWriteFile(dest, opts) {
16721
17180
  throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
16722
17181
  }
16723
17182
  const maxBytes = maxMB * 1024 * 1024;
16724
- return new Promise((resolve14, reject) => {
17183
+ return new Promise((resolve15, reject) => {
16725
17184
  const chunks = [];
16726
17185
  let totalBytes = 0;
16727
17186
  let settled = false;
@@ -16745,7 +17204,7 @@ function cmdWriteFile(dest, opts) {
16745
17204
  try {
16746
17205
  atomicWriteBuffer(dest, Buffer.concat(chunks));
16747
17206
  enqueueDirtyPathSafe(dest);
16748
- resolve14();
17207
+ resolve15();
16749
17208
  } catch (e) {
16750
17209
  try {
16751
17210
  mapFsError(e, void 0, dest);
@@ -16876,14 +17335,14 @@ function writeReplacedBuffer(file, replacedBuf, preWriteStat) {
16876
17335
  const until = process.env["TOKEN_GOAT_TEST_REPLACE_DELAY_UNTIL"];
16877
17336
  const deadline = Date.now() + testDelayMs;
16878
17337
  if (until !== void 0 && until !== "") {
16879
- while (Date.now() < deadline && !fs28.existsSync(until)) sleepSync(5);
17338
+ while (Date.now() < deadline && !fs29.existsSync(until)) sleepSync(5);
16880
17339
  } else {
16881
17340
  sleepSync(testDelayMs);
16882
17341
  }
16883
17342
  }
16884
17343
  let preRenameStat;
16885
17344
  try {
16886
- preRenameStat = fs28.statSync(file);
17345
+ preRenameStat = fs29.statSync(file);
16887
17346
  } catch {
16888
17347
  }
16889
17348
  if (preRenameStat !== void 0 && (preRenameStat.mtimeMs !== preWriteStat.mtimeMs || preRenameStat.size !== preWriteStat.size)) {
@@ -16924,7 +17383,7 @@ function cmdReplace(file, opts) {
16924
17383
  const targetBuf = readFileBoundedRaw(file, "target file", true);
16925
17384
  let preWriteStat;
16926
17385
  try {
16927
- preWriteStat = fs28.statSync(file);
17386
+ preWriteStat = fs29.statSync(file);
16928
17387
  } catch {
16929
17388
  }
16930
17389
  const usingFrom = opts.oldFrom !== void 0 || opts.newFrom !== void 0;
@@ -17018,7 +17477,7 @@ function cmdInsertSection(file, opts) {
17018
17477
  }
17019
17478
  let preWriteStat;
17020
17479
  try {
17021
- preWriteStat = fs28.statSync(file);
17480
+ preWriteStat = fs29.statSync(file);
17022
17481
  } catch {
17023
17482
  }
17024
17483
  const result = readSection(file, opts.after);
@@ -17044,7 +17503,7 @@ function cmdInsertSection(file, opts) {
17044
17503
  }
17045
17504
  let rawBytes;
17046
17505
  try {
17047
- rawBytes = fs28.readFileSync(file);
17506
+ rawBytes = fs29.readFileSync(file);
17048
17507
  } catch (e) {
17049
17508
  mapFsError(e, void 0, file);
17050
17509
  }
@@ -17060,7 +17519,7 @@ function cmdInsertSection(file, opts) {
17060
17519
  if (preWriteStat !== void 0) {
17061
17520
  let preRenameStat;
17062
17521
  try {
17063
- preRenameStat = fs28.statSync(file);
17522
+ preRenameStat = fs29.statSync(file);
17064
17523
  } catch {
17065
17524
  }
17066
17525
  if (preRenameStat !== void 0 && (preRenameStat.mtimeMs !== preWriteStat.mtimeMs || preRenameStat.size !== preWriteStat.size)) {
@@ -17102,25 +17561,25 @@ ${content}`;
17102
17561
  }
17103
17562
  function expandGlobs(root, patterns, globFnOverride) {
17104
17563
  const out2 = [];
17105
- const globFn = globFnOverride ?? fs28["globSync"];
17564
+ const globFn = globFnOverride ?? fs29["globSync"];
17106
17565
  for (const p of patterns) {
17107
17566
  if (globFn !== void 0 && (p.includes("*") || p.includes("?") || p.includes("{"))) {
17108
17567
  try {
17109
17568
  const hits = globFn(p, { cwd: root });
17110
- for (const h of hits) out2.push(path29.isAbsolute(h) ? h : path29.join(root, h));
17569
+ for (const h of hits) out2.push(path30.isAbsolute(h) ? h : path30.join(root, h));
17111
17570
  continue;
17112
17571
  } catch {
17113
17572
  }
17114
17573
  }
17115
- out2.push(path29.isAbsolute(p) ? p : path29.join(root, p));
17574
+ out2.push(path30.isAbsolute(p) ? p : path30.join(root, p));
17116
17575
  }
17117
17576
  return out2;
17118
17577
  }
17119
17578
  function readIgnoreFile(root) {
17120
- const ignorePath = path29.join(root, ".tokengoatignore");
17579
+ const ignorePath = path30.join(root, ".tokengoatignore");
17121
17580
  let raw;
17122
17581
  try {
17123
- raw = fs28.readFileSync(ignorePath, "utf8");
17582
+ raw = fs29.readFileSync(ignorePath, "utf8");
17124
17583
  } catch {
17125
17584
  return void 0;
17126
17585
  }
@@ -17161,14 +17620,14 @@ function cmdPack(patterns, opts) {
17161
17620
  }
17162
17621
  let instruction;
17163
17622
  if (opts.instructionFile !== void 0) {
17164
- instruction = fs28.readFileSync(opts.instructionFile, "utf8");
17623
+ instruction = fs29.readFileSync(opts.instructionFile, "utf8");
17165
17624
  }
17166
17625
  const formatted = formatPack(result, style, {
17167
17626
  ...opts.lineNumbers === true ? { line_numbers: true } : {},
17168
17627
  ...instruction !== void 0 ? { instruction } : {}
17169
17628
  });
17170
17629
  if (opts.output !== void 0) {
17171
- fs28.writeFileSync(opts.output, formatted, "utf8");
17630
+ fs29.writeFileSync(opts.output, formatted, "utf8");
17172
17631
  } else {
17173
17632
  out(formatted);
17174
17633
  }
@@ -17188,17 +17647,17 @@ function cmdTokens(patterns, opts) {
17188
17647
  if (opts.tree === true) {
17189
17648
  const dirs = /* @__PURE__ */ new Map();
17190
17649
  for (const e of entries) {
17191
- const dir = path29.dirname(e.rel_path);
17650
+ const dir = path30.dirname(e.rel_path);
17192
17651
  if (!dirs.has(dir)) dirs.set(dir, []);
17193
17652
  dirs.get(dir).push(e);
17194
17653
  }
17195
17654
  const lines2 = [];
17196
17655
  for (const [dir, dirEntries] of dirs) {
17197
17656
  const dirTokens = dirEntries.reduce((s, e) => s + e.tokens, 0);
17198
- const pct2 = result.total_tokens > 0 ? Math.round(dirTokens / result.total_tokens * 100) : 0;
17199
- lines2.push(`${dir}/ (${dirTokens} tokens, ${pct2}%)`);
17657
+ const pct3 = result.total_tokens > 0 ? Math.round(dirTokens / result.total_tokens * 100) : 0;
17658
+ lines2.push(`${dir}/ (${dirTokens} tokens, ${pct3}%)`);
17200
17659
  for (const e of dirEntries) {
17201
- lines2.push(` ${path29.basename(e.rel_path).padEnd(30)} ${String(e.tokens).padStart(8)} tokens`);
17660
+ lines2.push(` ${path30.basename(e.rel_path).padEnd(30)} ${String(e.tokens).padStart(8)} tokens`);
17202
17661
  }
17203
17662
  }
17204
17663
  out(lines2.join("\n"));
@@ -17232,7 +17691,7 @@ function cmdBudget(patterns, opts) {
17232
17691
  }
17233
17692
  }
17234
17693
  function cmdFailures(src, opts) {
17235
- const text = src !== void 0 ? fs28.readFileSync(src, "utf8") : fs28.readFileSync(0, "utf8");
17694
+ const text = src !== void 0 ? fs29.readFileSync(src, "utf8") : fs29.readFileSync(0, "utf8");
17236
17695
  const result = extractFailures(text, opts.runner !== void 0 ? { runner: opts.runner } : {});
17237
17696
  if (opts.delta !== true) {
17238
17697
  out(opts.json === true ? formatFailuresJson(result) : formatFailuresText(result));
@@ -17286,10 +17745,11 @@ function generateCompactHelp() {
17286
17745
  "",
17287
17746
  "Compression: compress-text, retrieve, handoff-create, handoff-resolve,",
17288
17747
  " compress, recall, bash-output, web-output, mcp-output, compress-text,",
17289
- " compress",
17748
+ " compress, bench",
17290
17749
  "",
17291
17750
  "Config & Integration: install, uninstall, mcp-serve, mcp-status, hook,",
17292
- " bridges-status, worker, statusline, version, config, config-get, help",
17751
+ " bridges-status, worker, statusline, version, config, config-get,",
17752
+ " capabilities, help",
17293
17753
  "",
17294
17754
  "Data: note, note-add, note-get, note-list, sqlite-query, sqlite-schema,",
17295
17755
  " csv-profile, csv-query",
@@ -17471,6 +17931,16 @@ function buildProgram() {
17471
17931
  program2.command("bridges-status").description("hook-event parity matrix across every AI-harness bridge (read-only static analysis, never invokes a real harness binary)").option("--json", "emit the matrix as JSON instead of text").action(guard(cmdBridgesStatus));
17472
17932
  program2.command("commands").description("machine-readable manifest of every registered command, its options, and its arguments").option("--json", "emit the manifest as JSON instead of text").option("--grep <pattern>", "filter to commands whose name, description, or aliases match this regex").action(guard(cmdCommands));
17473
17933
  program2.command("mcp-serve").description("run token-goat as an MCP stdio server exposing surgical reads and local compression/handoff tools").action(guard(cmdMcpServe));
17934
+ 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(
17935
+ (opts) => runExitText(
17936
+ () => runBenchCommand({
17937
+ corpus: opts.corpus ?? path30.join("tests", "fixtures", "bench"),
17938
+ ...opts.tsv !== void 0 ? { tsv: opts.tsv } : {},
17939
+ ...opts.validate === true ? { validate: true } : {},
17940
+ ...opts.json === true ? { json: true } : {}
17941
+ })
17942
+ )
17943
+ );
17474
17944
  program2.command("compress-text [text]").description("compress arbitrary local text and print an opaque recovery ID plus compact payload").option("--file <path>", "read text from a local file instead of the argument").option("--payload", "always print the compact payload, even when inlining it costs more tokens than the original text").action(guard(cmdContentCompress));
17475
17945
  program2.command("retrieve <id>").description("retrieve original text previously stored by token-goat compress").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--max-matches <n>", "cap --grep output to the first N matching lines").option("--section <heading>", "extract a specific section from the retrieved text").option("--full", "print the entire retrieved text with no head/tail elision (default behaviour when no other flag is given)").action(guard(cmdRetrieve));
17476
17946
  program2.command("handoff-create <name> [text]").description("create a project-local named compressed handoff").option("--file <path>", "read handoff text from a local file instead of the argument").action(guard(cmdHandoffCreate));
@@ -17493,6 +17963,12 @@ function buildProgram() {
17493
17963
  })
17494
17964
  )
17495
17965
  );
17966
+ program2.command("capabilities").description("report every capability that can send data off this machine, and its current state").option("-j, --json", "emit as JSON, for asserting on in a pipeline").action(
17967
+ guard((opts) => {
17968
+ const caps = collectCapabilities();
17969
+ console.log(opts.json ? JSON.stringify({ version: VERSION, capabilities: caps }, null, 2) : renderCapabilities(caps));
17970
+ })
17971
+ );
17496
17972
  program2.command("doctor").description("diagnose token-goat health").option("--context", "include context footprint analysis").option("--json", "emit check results as JSON instead of text").action(guard(cmdDoctor));
17497
17973
  program2.command("context-stats").description("show context statistics").option("--project <path>", "project root to analyze").option("-j, --json", "output as JSON").option("--fix", "apply automatic fixes (confirm-gated; shows a diff before writing)").option("-y, --yes", "with --fix, apply without prompting (non-interactive / scripted use)").action(guard(cmdContextStats));
17498
17974
  program2.command("bootstrap-audit").description("audit Claude Code startup-context contributors without reading prompt bodies").option("--project <path>", "project root to analyze").option("--home <path>", "home directory override (for CI/testing)").option("--follow-links", "follow external symlink/junction roots and direct children").option("-j, --json", "output as JSON").option("--top <n>", "largest metadata entries to show (default 10)", "10").option("--warn-tokens <n>", "warn when total estimated startup tokens exceed n").option("--fail-tokens <n>", "fail when total estimated startup tokens exceed n").option("--warn-bytes <n>", "warn when agent/skill metadata bytes exceed n").option("--fail-bytes <n>", "fail when agent/skill metadata bytes exceed n").action(guard(cmdBootstrapAudit));
@@ -17913,7 +18389,7 @@ async function run(argv = process.argv) {
17913
18389
  return;
17914
18390
  }
17915
18391
  if (argv[2] === "--batch-serve" && typeof argv[3] === "string") {
17916
- const { serveBatch } = await import("./token-goat-chunk-G6XLWJWI.mjs");
18392
+ const { serveBatch } = await import("./token-goat-chunk-E3K3BTEQ.mjs");
17917
18393
  serveBatch(argv[3], (a) => run(a));
17918
18394
  return;
17919
18395
  }