token-goat 2.9.2 → 2.9.4

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 (26) hide show
  1. package/README.md +22 -50
  2. package/dist/{token-goat-chunk-UZ2NFOOZ.mjs → token-goat-chunk-3ZR4NLB3.mjs} +165 -8
  3. package/dist/{token-goat-chunk-NY4HYRSK.mjs → token-goat-chunk-5BHXZEOI.mjs} +10 -12
  4. package/dist/{token-goat-chunk-Q4LOQY44.mjs → token-goat-chunk-63XEQMNU.mjs} +137 -12
  5. package/dist/{token-goat-chunk-KKIB7O3Z.mjs → token-goat-chunk-6MU6FHE3.mjs} +463 -106
  6. package/dist/token-goat-chunk-A37V4PBF.mjs +56 -0
  7. package/dist/{token-goat-chunk-LJ3CHCTT.mjs → token-goat-chunk-CBO45DYZ.mjs} +3728 -61
  8. package/dist/{token-goat-chunk-G6XLWJWI.mjs → token-goat-chunk-E3K3BTEQ.mjs} +5 -2
  9. package/dist/{token-goat-chunk-AO2QD2AG.mjs → token-goat-chunk-EEIDFMEM.mjs} +4 -0
  10. package/dist/{token-goat-chunk-CGWACYYZ.mjs → token-goat-chunk-EPX5DWGM.mjs} +6 -1
  11. package/dist/token-goat-chunk-EQKNR7CN.mjs +23 -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-VCNW7BGU.mjs → token-goat-chunk-FSC44QOH.mjs} +20 -3
  15. package/dist/{token-goat-chunk-JMUBXBG7.mjs → token-goat-chunk-GEXHIIKM.mjs} +7 -7
  16. package/dist/{token-goat-chunk-5CVKO3DA.mjs → token-goat-chunk-LYWIRYCF.mjs} +5 -2
  17. package/dist/{token-goat-chunk-LEBYARO3.mjs → token-goat-chunk-OZAEJQ7L.mjs} +9 -6
  18. package/dist/{token-goat-chunk-R4SR7MQY.mjs → token-goat-chunk-PXWBHSFB.mjs} +2 -2
  19. package/dist/{token-goat-chunk-FXAPKVRG.mjs → token-goat-chunk-US57I7GN.mjs} +26 -5
  20. package/dist/token-goat-hook.mjs +12 -7
  21. package/dist/token-goat.core.mjs +10 -7
  22. package/docs/cli.md +68 -8
  23. package/docs/security.md +6 -2
  24. package/package.json +8 -2
  25. package/dist/token-goat-chunk-AEX54RUZ.mjs +0 -11
  26. package/dist/token-goat-chunk-ZVN5WHTO.mjs +0 -23
@@ -9,7 +9,7 @@ import {
9
9
  isBlobStale,
10
10
  loadBlob,
11
11
  storeBlob
12
- } from "./token-goat-chunk-LJ3CHCTT.mjs";
12
+ } from "./token-goat-chunk-CBO45DYZ.mjs";
13
13
  import {
14
14
  SYMBOL_BODY_CHAR_CAP,
15
15
  copilotCliMcpToolsDir,
@@ -24,12 +24,22 @@ import {
24
24
  resolveIndexPath,
25
25
  shortFingerprint,
26
26
  toDisplayPath
27
- } from "./token-goat-chunk-UZ2NFOOZ.mjs";
27
+ } from "./token-goat-chunk-3ZR4NLB3.mjs";
28
28
  import {
29
29
  registerReset
30
- } from "./token-goat-chunk-AO2QD2AG.mjs";
30
+ } from "./token-goat-chunk-EEIDFMEM.mjs";
31
+ import {
32
+ init_define_import_meta_env
33
+ } from "./token-goat-chunk-A37V4PBF.mjs";
34
+
35
+ // src/hooks_cli.ts
36
+ init_define_import_meta_env();
37
+
38
+ // src/copilot_mcp_names.ts
39
+ init_define_import_meta_env();
31
40
 
32
41
  // src/copilot_mcp_tools.ts
42
+ init_define_import_meta_env();
33
43
  import fs from "node:fs";
34
44
  import path from "node:path";
35
45
  function wireShapeLength(tools) {
@@ -314,6 +324,7 @@ function normalizePayload(payload, harness = "claude") {
314
324
  }
315
325
 
316
326
  // src/web_cache.ts
327
+ init_define_import_meta_env();
317
328
  var WEB_OUTPUT_SUBDIR = "web_outputs";
318
329
  var _byId = /* @__PURE__ */ new Map();
319
330
  var _rawById = /* @__PURE__ */ new Map();
@@ -370,6 +381,7 @@ registerReset(() => {
370
381
  });
371
382
 
372
383
  // src/types.ts
384
+ init_define_import_meta_env();
373
385
  var HOOK_EVENTS = [
374
386
  "pre_tool_use",
375
387
  "post_tool_use",
@@ -384,6 +396,7 @@ var HOOK_EVENTS = [
384
396
  ];
385
397
 
386
398
  // src/cli_commands.ts
399
+ init_define_import_meta_env();
387
400
  function toEntry(cmd) {
388
401
  return {
389
402
  name: cmd.name(),
@@ -452,6 +465,7 @@ function formatCommandManifest(manifest) {
452
465
  }
453
466
 
454
467
  // src/reconcile.ts
468
+ init_define_import_meta_env();
455
469
  import * as fs2 from "node:fs";
456
470
  var DEFAULT_RECONCILE_BUDGET_MS = 1500;
457
471
  function runReconcile(opts = {}) {
@@ -575,6 +589,7 @@ function reconcileProject(opts = {}) {
575
589
  }
576
590
 
577
591
  // src/stdin_json.ts
592
+ init_define_import_meta_env();
578
593
  var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
579
594
  var MAX_STDIN_WALL_MS = 6e4;
580
595
  var MAX_STDIN_BYTES = 64 * 1024 * 1024;
@@ -641,6 +656,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
641
656
  }
642
657
 
643
658
  // src/symbol_body_probe.ts
659
+ init_define_import_meta_env();
644
660
  import * as fs3 from "fs";
645
661
  var OVERSIZED_BODY_PROBE_SQL = `SELECT 1 FROM symbols WHERE LENGTH(body) > ${SYMBOL_BODY_CHAR_CAP} LIMIT 1`;
646
662
  function checkSymbolBodySize(dbPath) {
@@ -668,6 +684,7 @@ function checkSymbolBodySize(dbPath) {
668
684
  }
669
685
 
670
686
  // src/resident_context.ts
687
+ init_define_import_meta_env();
671
688
  import * as fs4 from "node:fs";
672
689
  var LARGE_TASK_LIST_BYTES = 2e4;
673
690
  var LARGE_SKILL_BODY_BYTES = 2e4;
@@ -7,13 +7,13 @@ import {
7
7
  expandGlobs,
8
8
  leftoverIntegrations,
9
9
  run
10
- } from "./token-goat-chunk-KKIB7O3Z.mjs";
11
- import "./token-goat-chunk-FXAPKVRG.mjs";
12
- import "./token-goat-chunk-VCNW7BGU.mjs";
13
- import "./token-goat-chunk-LJ3CHCTT.mjs";
14
- import "./token-goat-chunk-UZ2NFOOZ.mjs";
15
- import "./token-goat-chunk-AO2QD2AG.mjs";
16
- import "./token-goat-chunk-AEX54RUZ.mjs";
10
+ } from "./token-goat-chunk-6MU6FHE3.mjs";
11
+ import "./token-goat-chunk-US57I7GN.mjs";
12
+ import "./token-goat-chunk-FSC44QOH.mjs";
13
+ import "./token-goat-chunk-CBO45DYZ.mjs";
14
+ import "./token-goat-chunk-3ZR4NLB3.mjs";
15
+ import "./token-goat-chunk-EEIDFMEM.mjs";
16
+ import "./token-goat-chunk-A37V4PBF.mjs";
17
17
  export {
18
18
  applyExitOverride,
19
19
  buildProgram,
@@ -2,10 +2,13 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  external_exports
5
- } from "./token-goat-chunk-WN5T5EW5.mjs";
6
- import "./token-goat-chunk-AEX54RUZ.mjs";
5
+ } from "./token-goat-chunk-FQD3OB5W.mjs";
6
+ import {
7
+ init_define_import_meta_env
8
+ } from "./token-goat-chunk-A37V4PBF.mjs";
7
9
 
8
10
  // src/mcp_jsonrpc.ts
11
+ init_define_import_meta_env();
9
12
  var LATEST_PROTOCOL_VERSION = "2025-11-25";
10
13
  var SUPPORTED_PROTOCOL_VERSIONS = [
11
14
  LATEST_PROTOCOL_VERSION,
@@ -25,7 +25,7 @@ import {
25
25
  runSkeleton,
26
26
  runSymbol,
27
27
  withPinnedReads
28
- } from "./token-goat-chunk-FXAPKVRG.mjs";
28
+ } from "./token-goat-chunk-US57I7GN.mjs";
29
29
  import {
30
30
  buildProjectMap,
31
31
  embeddingsDepsAvailable,
@@ -34,7 +34,7 @@ import {
34
34
  getProjectIndexCounts,
35
35
  isWorkerRunning,
36
36
  mapLookupBytesSaved
37
- } from "./token-goat-chunk-LJ3CHCTT.mjs";
37
+ } from "./token-goat-chunk-CBO45DYZ.mjs";
38
38
  import {
39
39
  ENV_KEYS,
40
40
  VERSION,
@@ -48,11 +48,14 @@ import {
48
48
  recordStat,
49
49
  resolveProjectRoot,
50
50
  savedTokensFromBytes
51
- } from "./token-goat-chunk-UZ2NFOOZ.mjs";
52
- import "./token-goat-chunk-AO2QD2AG.mjs";
53
- import "./token-goat-chunk-AEX54RUZ.mjs";
51
+ } from "./token-goat-chunk-3ZR4NLB3.mjs";
52
+ import "./token-goat-chunk-EEIDFMEM.mjs";
53
+ import {
54
+ init_define_import_meta_env
55
+ } from "./token-goat-chunk-A37V4PBF.mjs";
54
56
 
55
57
  // src/mcp_server.ts
58
+ init_define_import_meta_env();
56
59
  import * as fs from "fs";
57
60
  import * as path from "path";
58
61
  var TOKEN_GOAT_RETRY_RE = /token-goat (\w[\w-]*) "([^"]+)"/g;
@@ -208,7 +211,7 @@ function mcpToolAllowlist() {
208
211
  return names.length === 0 ? null : new Set(names);
209
212
  }
210
213
  async function createMcpServer() {
211
- const [{ McpServer }, { z }] = await Promise.all([import("./token-goat-chunk-5CVKO3DA.mjs"), import("./token-goat-chunk-R4SR7MQY.mjs")]);
214
+ const [{ McpServer }, { z }] = await Promise.all([import("./token-goat-chunk-LYWIRYCF.mjs"), import("./token-goat-chunk-PXWBHSFB.mjs")]);
212
215
  const server = new McpServer({ name: "token-goat", version: VERSION });
213
216
  const allowlist = mcpToolAllowlist();
214
217
  if (allowlist !== null) {
@@ -240,8 +240,8 @@ import {
240
240
  xid,
241
241
  xor,
242
242
  zod_default
243
- } from "./token-goat-chunk-WN5T5EW5.mjs";
244
- import "./token-goat-chunk-AEX54RUZ.mjs";
243
+ } from "./token-goat-chunk-FQD3OB5W.mjs";
244
+ import "./token-goat-chunk-A37V4PBF.mjs";
245
245
  export {
246
246
  $brand,
247
247
  $input,
@@ -68,7 +68,7 @@ import {
68
68
  walkProject,
69
69
  yamlLineClosesQuote,
70
70
  yamlOpenQuoteAfter
71
- } from "./token-goat-chunk-LJ3CHCTT.mjs";
71
+ } from "./token-goat-chunk-CBO45DYZ.mjs";
72
72
  import {
73
73
  Database,
74
74
  PER_FILE_COUNTERFACTUAL_CEILING,
@@ -122,12 +122,16 @@ import {
122
122
  unsupportedLanguageName,
123
123
  windowsCmdQuoteArg,
124
124
  withExtension
125
- } from "./token-goat-chunk-UZ2NFOOZ.mjs";
125
+ } from "./token-goat-chunk-3ZR4NLB3.mjs";
126
126
  import {
127
127
  registerReset
128
- } from "./token-goat-chunk-AO2QD2AG.mjs";
128
+ } from "./token-goat-chunk-EEIDFMEM.mjs";
129
+ import {
130
+ init_define_import_meta_env
131
+ } from "./token-goat-chunk-A37V4PBF.mjs";
129
132
 
130
133
  // src/section_reader.ts
134
+ init_define_import_meta_env();
131
135
  import { readFileSync } from "node:fs";
132
136
  function parseHeadingSpec(spec, headers) {
133
137
  const m = /^([^#\r\n]+)#(\d+)$/.exec(spec);
@@ -447,6 +451,7 @@ function listSections(filePath, readFn) {
447
451
  }
448
452
 
449
453
  // src/graph_commands.ts
454
+ init_define_import_meta_env();
450
455
  import * as fs6 from "node:fs";
451
456
  import * as os from "node:os";
452
457
  import * as path6 from "node:path";
@@ -454,10 +459,12 @@ import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
454
459
  import { randomUUID } from "node:crypto";
455
460
 
456
461
  // src/read_commands.ts
462
+ init_define_import_meta_env();
457
463
  import * as fs4 from "node:fs";
458
464
  import * as path4 from "node:path";
459
465
 
460
466
  // src/json_query.ts
467
+ init_define_import_meta_env();
461
468
  function jsonType(value) {
462
469
  if (value === null) return "null";
463
470
  if (Array.isArray(value)) return "array";
@@ -596,6 +603,7 @@ function queryJson(data, spec) {
596
603
  }
597
604
 
598
605
  // src/xml_query.ts
606
+ init_define_import_meta_env();
599
607
  var XML_NAME_START = "A-Za-z_:\xC0-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uD800-\uDFFF\uF900-\uFDCF\uFDF0-\uFFFD";
600
608
  var XML_NAME_REST = "A-Za-z_:\xC0-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uD800-\uDFFF\uF900-\uFDCF\uFDF0-\uFFFD0-9.\xB7\u0300-\u036F\u203F-\u2040-";
601
609
  var XML_NAME = `[${XML_NAME_START}][${XML_NAME_REST}]*`;
@@ -1064,6 +1072,7 @@ function queryXml(xmlText, pathStr) {
1064
1072
  }
1065
1073
 
1066
1074
  // node_modules/js-yaml/dist/js-yaml.mjs
1075
+ init_define_import_meta_env();
1067
1076
  var NOT_RESOLVED = /* @__PURE__ */ Symbol("NOT_RESOLVED");
1068
1077
  function defineScalarTag(tagName, options) {
1069
1078
  return {
@@ -3530,6 +3539,7 @@ var CHOMPING_STRIP = CHOMPING_MODE.STRIP;
3530
3539
  var CHOMPING_KEEP = CHOMPING_MODE.KEEP;
3531
3540
 
3532
3541
  // src/openapi_query.ts
3542
+ init_define_import_meta_env();
3533
3543
  import * as path from "node:path";
3534
3544
  var HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
3535
3545
  function parseOpenApiSpec(text, filePath) {
@@ -3676,6 +3686,7 @@ function formatOperationDetail(op) {
3676
3686
  }
3677
3687
 
3678
3688
  // src/archive_query.ts
3689
+ init_define_import_meta_env();
3679
3690
  var loadFflate = createLazyModuleLoader(
3680
3691
  async () => await import("fflate"),
3681
3692
  "archive reading disabled (fflate unavailable)"
@@ -3719,6 +3730,7 @@ async function extractZipEntry(data, entryPath) {
3719
3730
  }
3720
3731
 
3721
3732
  // src/pr_slice.ts
3733
+ init_define_import_meta_env();
3722
3734
  import { spawnSync } from "node:child_process";
3723
3735
  var _ghAvailable;
3724
3736
  function isGhAvailable() {
@@ -3888,6 +3900,7 @@ ${c.body}`).join("\n\n");
3888
3900
  }
3889
3901
 
3890
3902
  // src/sqlite_query.ts
3903
+ init_define_import_meta_env();
3891
3904
  import * as fs from "node:fs";
3892
3905
  var SQLITE_QUERY_ROW_CAP = 5e3;
3893
3906
  var SQLITE_MAGIC = Buffer.from([83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0]);
@@ -4150,6 +4163,7 @@ function quoteCsvCellLocal(cell) {
4150
4163
  }
4151
4164
 
4152
4165
  // src/coverage_query.ts
4166
+ init_define_import_meta_env();
4153
4167
  function num(v) {
4154
4168
  return typeof v === "number" && Number.isFinite(v) ? v : 0;
4155
4169
  }
@@ -4451,6 +4465,7 @@ function formatCoverageGaps(report) {
4451
4465
  }
4452
4466
 
4453
4467
  // src/conflict_query.ts
4468
+ init_define_import_meta_env();
4454
4469
  var OURS_RE = /^<{7}(?:\s+(.*))?$/;
4455
4470
  var BASE_RE = /^\|{7}(?:\s+(.*))?$/;
4456
4471
  var SEP_RE = /^={7}\s*$/;
@@ -4615,6 +4630,7 @@ function formatConflictSummaries(summaries) {
4615
4630
  }
4616
4631
 
4617
4632
  // src/screenshot.ts
4633
+ init_define_import_meta_env();
4618
4634
  import dns from "node:dns/promises";
4619
4635
  import fs2 from "node:fs";
4620
4636
  import path2 from "node:path";
@@ -4941,6 +4957,7 @@ async function takeScreenshot(url, destPath, opts) {
4941
4957
  }
4942
4958
 
4943
4959
  // src/notes.ts
4960
+ init_define_import_meta_env();
4944
4961
  import * as fs3 from "node:fs";
4945
4962
  var WHOLE_FILE_NOTE_SYMBOL = "";
4946
4963
  function toNoteRow(row) {
@@ -5016,6 +5033,7 @@ function isNoteStale(note, dbPath = globalDbPath()) {
5016
5033
  }
5017
5034
 
5018
5035
  // src/ts_refs.ts
5036
+ init_define_import_meta_env();
5019
5037
  import { createRequire } from "node:module";
5020
5038
  import * as path3 from "node:path";
5021
5039
  var _require = createRequire(import.meta.url);
@@ -7498,7 +7516,7 @@ async function runImageMeta(file) {
7498
7516
  throw e;
7499
7517
  }
7500
7518
  if (probe === null) {
7501
- return { width: 0, height: 0, format: null, bytes, sharpAvailable: false, wouldShrink: false, shrunkBytes: null };
7519
+ return { width: 0, height: 0, format: null, bytes, decodable: false, wouldShrink: false, shrunkBytes: null };
7502
7520
  }
7503
7521
  const shrink = await shrinkImage(data, { sizeThresholdBytes: 0 });
7504
7522
  return {
@@ -7506,7 +7524,7 @@ async function runImageMeta(file) {
7506
7524
  height: probe.height,
7507
7525
  format: probe.format,
7508
7526
  bytes,
7509
- sharpAvailable: true,
7527
+ decodable: true,
7510
7528
  wouldShrink: shrink !== null,
7511
7529
  shrunkBytes: shrink !== null ? shrink.shrunkBytes : null
7512
7530
  };
@@ -9206,6 +9224,7 @@ function runNoteList(opts = {}) {
9206
9224
  }
9207
9225
 
9208
9226
  // src/import_graph.ts
9227
+ init_define_import_meta_env();
9209
9228
  import * as fs5 from "node:fs";
9210
9229
  import * as path5 from "node:path";
9211
9230
  var DIRECT_PROBE_EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cjs", ".cts", ".py"];
@@ -9257,6 +9276,7 @@ function buildImportGraph(cwd) {
9257
9276
  }
9258
9277
 
9259
9278
  // src/modules.ts
9279
+ init_define_import_meta_env();
9260
9280
  var WEAK_MODULARITY = 0.3;
9261
9281
  var MAX_PASSES = 32;
9262
9282
  var GAIN_EPSILON = 1e-12;
@@ -10762,6 +10782,7 @@ ANSWER:`;
10762
10782
  }
10763
10783
 
10764
10784
  // src/content_store.ts
10785
+ init_define_import_meta_env();
10765
10786
  import { deflateRawSync } from "node:zlib";
10766
10787
  import * as path7 from "node:path";
10767
10788
  var CONTENT_MAX_INPUT_CHARS = 512 * 1024;
@@ -2,13 +2,18 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  relayInProcess
5
- } from "./token-goat-chunk-Q4LOQY44.mjs";
6
- import "./token-goat-chunk-VCNW7BGU.mjs";
7
- import "./token-goat-chunk-LJ3CHCTT.mjs";
8
- import "./token-goat-chunk-CGWACYYZ.mjs";
9
- import "./token-goat-chunk-UZ2NFOOZ.mjs";
10
- import "./token-goat-chunk-AO2QD2AG.mjs";
11
- import "./token-goat-chunk-AEX54RUZ.mjs";
5
+ } from "./token-goat-chunk-63XEQMNU.mjs";
6
+ import "./token-goat-chunk-FSC44QOH.mjs";
7
+ import "./token-goat-chunk-CBO45DYZ.mjs";
8
+ import "./token-goat-chunk-EPX5DWGM.mjs";
9
+ import "./token-goat-chunk-3ZR4NLB3.mjs";
10
+ import "./token-goat-chunk-EEIDFMEM.mjs";
11
+ import {
12
+ init_define_import_meta_env
13
+ } from "./token-goat-chunk-A37V4PBF.mjs";
14
+
15
+ // src/hook_lib.ts
16
+ init_define_import_meta_env();
12
17
  export {
13
18
  relayInProcess
14
19
  };
@@ -2,16 +2,19 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./token-goat-chunk-KKIB7O3Z.mjs";
6
- import "./token-goat-chunk-FXAPKVRG.mjs";
7
- import "./token-goat-chunk-VCNW7BGU.mjs";
8
- import "./token-goat-chunk-LJ3CHCTT.mjs";
5
+ } from "./token-goat-chunk-6MU6FHE3.mjs";
6
+ import "./token-goat-chunk-US57I7GN.mjs";
7
+ import "./token-goat-chunk-FSC44QOH.mjs";
8
+ import "./token-goat-chunk-CBO45DYZ.mjs";
9
9
  import {
10
10
  installEpipeGuard
11
- } from "./token-goat-chunk-UZ2NFOOZ.mjs";
12
- import "./token-goat-chunk-AO2QD2AG.mjs";
13
- import "./token-goat-chunk-AEX54RUZ.mjs";
11
+ } from "./token-goat-chunk-3ZR4NLB3.mjs";
12
+ import "./token-goat-chunk-EEIDFMEM.mjs";
13
+ import {
14
+ init_define_import_meta_env
15
+ } from "./token-goat-chunk-A37V4PBF.mjs";
14
16
 
15
17
  // src/main.ts
18
+ init_define_import_meta_env();
16
19
  installEpipeGuard();
17
20
  void run();
package/docs/cli.md CHANGED
@@ -167,10 +167,10 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
167
167
  | `token-goat project exclude <path>` | Add a project root to the blocklist so the worker never indexes it. Writes the resolved absolute path to `[worker] blocked_roots` in `config.toml`; idempotent. It also removes anything already indexed under that path and says how many files went, so excluding a directory means its contents stop being readable through `symbol` rather than merely stopping future indexing. Remove the entry from the config to re-enable indexing, then run `token-goat index` to bring the contents back. |
168
168
  | `token-goat project prune [--dry-run]` | Remove blocked/excluded roots that no longer exist on disk. `--dry-run` previews removals without touching the config file. Useful after deleting or moving projects. |
169
169
  | `token-goat install` | Wire up hooks (and, with the harness flags below, other AI tool integrations). No `--dry-run` or `--verify` flag — run `token-goat doctor` after install to audit the result. |
170
- | `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
170
+ | `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Parser freshness** check reports how much of the index for this project was written by a different build of the extractor. The stamp only refreshes when something touches a file, so after an upgrade a project goes on answering `symbol`, `read`, `outline` and `skeleton` from the previous extractor with nothing saying so. It warns past a quarter and names the fix: a plain `token-goat index` in that project, which is enough on its own, since a parser mismatch reparses without `--force`. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
171
171
  | `token-goat capabilities` | List every capability that can send data off this machine or leave data on it, with whether it is currently on, the config key that decides that, and the exact `file::symbol` where the decision is made — so a reviewer can open the code rather than take the list's word for it. `--json` emits the same thing for a pipeline to assert on, which is the point: the answer comes from the binary installed on your machine, not from documentation that may describe a different build. A test in the suite fails the build when a module that can open a network connection is missing from this list, and equally when the list names one that no longer connects anywhere. |
172
172
  | `token-goat baseline` | Emit a project map: file count, per-language file counts, the top indexed symbols (by name/kind/location), and the most recently modified files. `--subagent` emits a terser variant (fewer symbols, fewer recent files) for context handed to a freshly spawned subagent; `--json` for the machine-readable form. |
173
- | `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, saving 80–95% of context tokens. Use `--force` to rebuild, `--sentences N` to control lines per section (default 2), `--show` to print the result. The sidecar is automatically marked stale when you edit the source file. Config: `[hints] stable_doc_compacts = true` (default on). |
173
+ | `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, typically saving 60–95% of the tokens the full read would cost (measured across this repo's own 44 docs: median 67%, range 5–99%, depending on how much of the doc is prose under headings). Use `--force` to rebuild, `--sentences N` to control lines per section (default 2), `--show` to print the result. The sidecar is automatically marked stale when you edit the source file. Config: `[hints] stable_doc_compacts = true` (default on). |
174
174
 
175
175
  Missed lookups recover surgically: `read` and `section` print a "Did you mean…?" list on a miss, and `section` auto-redirects on an unambiguous heading-prefix match — a typo costs at most one extra glance, not a re-read.
176
176
 
@@ -302,12 +302,12 @@ that category — the real cost of emitting it, not just how often it fired):
302
302
 
303
303
  ```
304
304
  $ token-goat hint-stats
305
- category emitted acted-on efficacy suppressed manual+ manual- spent
306
- bash_redirect 42 9 21.4% no 0 0 3150
307
- bash_recall 18 15 83.3% no 0 0 1080
308
- read_reread_dedup 11 2 18.2% no 0 0 660
309
- read_structural_nav 7 1 14.3% yes 0 1 420
310
- edit_reread_suggest 3 0 0% no 0 0 180
305
+ category emitted acted-on efficacy suppressed manual+ manual- spent
306
+ bash_redirect 42 9 21.4% no 0 0 3150
307
+ bash_recall 18 15 83.3% no 0 0 1080
308
+ read_reread_dedup 11 2 18.2% * no 0 0 660
309
+ read_structural_nav 7 1 14.3% yes 0 1 420
310
+ edit_reread_suggest 3 0 0% * no 0 0 180
311
311
 
312
312
  TOTAL saved=48200 spent=5490 net=42710
313
313
  ```
@@ -325,6 +325,13 @@ emissions (default 5) AND its efficacy falls below `hint_stats.suppress_threshol
325
325
  emission. Once suppressed, that hook stops emitting that category until `token-goat hint-stats
326
326
  --reset` clears the tracked data. Configure both knobs with `token-goat config set hint_stats.min_sample_size <n>` / `token-goat config set hint_stats.suppress_threshold_pct <pct>`.
327
327
 
328
+ A `*` on an efficacy figure means that category is scored on an absence. Those hints ask the
329
+ agent *not* to do something, so a window that expires with no re-read counts as compliance, while
330
+ an unmarked category only scores when the agent actually runs the command the hint named. The two
331
+ percentages sit on different scales and comparing them straight across gives the wrong answer: a
332
+ high starred figure means the warned-against read was not seen, not that the hint persuaded
333
+ anyone.
334
+
328
335
  "Acted on" is a real, session-scoped signal (the exact file path or cached-output id the hint's
329
336
  own text pointed at is checked against the next few tool calls in that session) — not a guess —
330
337
  but it is a proxy for correlation, not proof of causation: a match means the agent ran the
@@ -337,3 +344,56 @@ are shown alongside the automatic percentage but never blended into it. `--json`
337
344
  Note that what this feature calls "harness" (Claude Code, Codex, Gemini, ...) is not the same as
338
345
  "which LLM model" — no bridge in this codebase exposes an LLM model identifier to hooks, so
339
346
  harness is the closest real signal available.
347
+
348
+ ### Scoring the compressors — `token-goat bench`
349
+
350
+ `token-goat bench` replays a fixed corpus of captured command output through the same function
351
+ that decides what a real shell command hands back to the model, and reports how much smaller the
352
+ result is. It exists so a change to a compressor can be measured instead of guessed at:
353
+
354
+ ```
355
+ $ token-goat bench
356
+ case filter in out saved fidelity
357
+ ---------------------------------------------------------------
358
+ git-log-stat git-log 57227 3027 94.7% 2/2
359
+ npm-ls-all dep-list 34546 1060 96.9% 2/2
360
+ vitest-run vitest 22684 22682 - 2/2
361
+ ---------------------------------------------------------------
362
+ TOTAL 114457 26769 76.6% 6/6
363
+
364
+ ratio 76.6% saved (PRIMARY -- must improve; measured floor 0.0%, headroom 23.4%)
365
+ fidelity 6/6 kept (GUARD -- must not regress; any miss exits 1)
366
+ coverage 3/157 filters exercised, 2/3 cases compressed
367
+ ```
368
+
369
+ There are two numbers on purpose. **Ratio** is the thing to push up. **Fidelity** counts the lines
370
+ each case declares it must never lose, and it is what stops the ratio from being gamed: deleting
371
+ output raises the ratio, so a benchmark reporting only the ratio would reward destroying the very
372
+ content the compressors exist to preserve. A dropped must-keep line exits 1, so a loop can revert
373
+ on the exit code alone.
374
+
375
+ The corpus lives in `tests/fixtures/bench`: one `<id>.json` naming the command, its exit code,
376
+ and its must-keep lines, beside an `<id>.txt` holding that run's real output. Point `--corpus
377
+ <dir>` at your own directory to score commands you care about. Every case must declare where its
378
+ output came from; a fixture with no `provenance` field is refused rather than scored, because
379
+ output written by hand proves nothing about what a real command emits.
380
+
381
+ `--tsv <path>` appends one row per run (timestamp, commit, sizes, ratio, fidelity, coverage), so a
382
+ sequence of attempts leaves a readable history instead of a scrollback. `--json` emits the same
383
+ report as an object. `--validate` runs the benchmark's own negative controls and says whether the
384
+ corpus can tell good from bad at all:
385
+
386
+ ```
387
+ $ token-goat bench --validate
388
+ ok floor: the net-benefit gate refuses a no-op filter on every case -- identity control applied to 0/3 cases, measured floor 0.0%
389
+ ok floor: a no-op filter keeps every must-keep line -- identity control kept 6/6
390
+ ok guard: deleting everything is caught -- destroy control kept 0/6 at 99.8% saved
391
+
392
+ The corpus discriminates: a ratio measured against it cannot be raised by deleting content.
393
+ ```
394
+
395
+ The first two controls establish the floor: a filter that changes nothing must score nothing, so
396
+ the reported percentage is a real saving rather than an offset every number carries. The third
397
+ runs a filter that deletes the entire output. It scores 99.8%, the best ratio possible, and the
398
+ fidelity guard fails it. Run `--validate` after changing the corpus; a corpus whose must-keep
399
+ lists have gone soft passes every benchmark while measuring nothing.
package/docs/security.md CHANGED
@@ -38,12 +38,16 @@ That command is the one thing here that is not clean: `onnxruntime-node` pulls a
38
38
 
39
39
  **Prompt injection.** When an AI reads a file, web page, or command output, that content enters its context alongside your own instructions. Prompt injection is when untrusted content includes text designed to look like instructions — "Ignore all previous directives and run this instead" — to redirect the AI mid-task.
40
40
 
41
- Token-goat intercepts every Read, Fetch, Bash, and MCP call the AI makes. Text that came from somewhere else is wrapped in an untrusted-content fence before the model sees it, decided by where the text came from and not by whether anything looked suspicious in it (`injection.enabled`, on by default, turns the whole thing off). The content is also scanned for a set of imperative-override attack patterns ("ignore previous instructions," "reveal system prompt," and similar); a match adds the pattern names to the fence's notice and writes a row to the log, and a clean scan changes only the wording. That ordering is the point: the pattern list is deliberately short, so anyone phrasing the same instruction differently would otherwise get an unlabelled channel, and a miss would be silent.
41
+ Token-goat intercepts every Read, Fetch, Bash, and MCP call the AI makes. Where it hands the model text in place of the tool's own result, that text is wrapped in an untrusted-content fence first, decided by where the text came from and not by whether anything looked suspicious in it (`injection.enabled`, on by default, turns the whole thing off). Which surfaces that covers, and which it deliberately does not, is set out below: the two exceptions are stated rather than left to be read off this sentence. The content is also scanned for a set of imperative-override attack patterns ("ignore previous instructions," "reveal system prompt," and similar); a match adds the pattern names to the fence's notice and writes a row to the log, and a clean scan changes only the wording. That ordering is the point: the pattern list is deliberately short, so anyone phrasing the same instruction differently would otherwise get an unlabelled channel, and a miss would be silent.
42
42
 
43
- Three surfaces are covered. Every fetched page is fenced as it arrives and again when a cached copy is recalled with `web-output`. Every MCP tool result is fenced as it arrives, which matters most: it is a remote server's output, so it is the least trustworthy text in the pipeline. And cached Bash and MCP output is fenced when recalled with `bash-output` or `mcp-output`, since the output of a build or test run in a project with a hostile dependency is written by a third party as much as any web page is. Document extraction (`pdf-extract`, `docx-text`, the `xlsx-*` and `pptx-*` commands), `pr-slice`, `gdrive-sections`, and `recall` are covered the same way. The fence naming tool output is a different tag from the one naming web content, so the label tells the model where the text came from.
43
+ The line that decides is substitution: wherever token-goat replaces a tool result with text of its own, that text is fenced. Every fetched page is fenced as it arrives and again when a cached copy is recalled with `web-output`. Every MCP tool result is fenced as it arrives, which matters most: it is a remote server's output, so it is the least trustworthy text in the pipeline. Bash output is fenced when token-goat compresses it, since a compressed body is not the command's output but token-goat's account of it, and it is fenced again when a cached copy is recalled with `bash-output` or `mcp-output`: the output of a build or test run in a project with a hostile dependency is written by a third party as much as any web page is. Document extraction (`pdf-extract`, `docx-text`, the `xlsx-*` and `pptx-*` commands), `pr-slice`, `gdrive-sections`, and `recall` are covered the same way. The fence naming tool output is a different tag from the one naming web content, so the label tells the model where the text came from.
44
+
45
+ In every one of those cases the fence wraps the third-party bytes and stops there. Token-goat's own notice, the filter's marker, and the pointer telling the model how to recall the full output all sit outside the closing tag, because the fence is the model's one signal for where token-goat stops speaking. Fold them inside and that signal is gone, and anyone who guesses the marker's wording can write a line the model reads as token-goat's own.
44
46
 
45
47
  Read is the exception: file content passes through to the model unfiltered, because filtering it would silently break legitimate use cases. Where token-goat splices a piece of a file into its own hint or denial message, that excerpt is fenced. Outside of the fence, the primary defense is the model's own training to treat tool output as data, not as commands from a trusted party.
46
48
 
49
+ Bash output that token-goat does not rewrite is the same kind of exception, and worth stating plainly rather than leaving to be inferred from the paragraph above. A command whose output is short, or too incompressible to be worth touching, reaches the model exactly as the harness delivered it, with no fence. So does output whose only change was stripping the colour codes a terminal would have rendered, since that path emits the command's own bytes and adds nothing of token-goat's to delimit. Fencing those cases would mean rewriting the result of every shell command an agent runs, a permanent cost on the most-used path in the tool, to re-label bytes the model was going to receive in that form anyway. The fence is worth its bytes where token-goat has substituted its own account of the output and the model can no longer tell whose words are whose. Where token-goat has stayed out of the way, the defense is the same as for Read: the model's training to treat tool output as data.
50
+
47
51
  One deliberate gap: `--json` output cannot carry a fence around the envelope, because a fence wrapped around JSON is no longer JSON and callers parse it. Those envelopes fence individual fields on a pattern match instead, since the fixed wrapper would otherwise cost more than a short field is worth. The printed (non-`--json`) form of the same command is always fenced.
48
52
 
49
53
  Separately from that pass-through case: when a read hook *denies* a Read and substitutes its own message, any file bytes it embeds in that message (a markdown heading tree, a served compact or notebook sidecar, a re-read diff, a CSV header row, an HTML title) are wrapped in an `<untrusted-file-content>` fence first, so a hostile repo cannot get its own text presented to the model as token-goat speaking. That fencing is unconditional, not gated on the pattern scan — as all of it now is.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-goat",
3
- "version": "2.9.2",
3
+ "version": "2.9.4",
4
4
  "description": "Surgical token-reduction companion for Claude Code and other AI coding agents",
5
5
  "type": "module",
6
6
  "main": "./dist/token-goat.mjs",
@@ -73,7 +73,9 @@
73
73
  "@eslint/js": "^10.0.1",
74
74
  "@modelcontextprotocol/sdk": "^1.30.0",
75
75
  "@types/better-sqlite3": "^9.6.0",
76
+ "@types/jpeg-js": "^0.3.0",
76
77
  "@types/node": "^26.2.0",
78
+ "@types/omggif": "^1.0.5",
77
79
  "@xenova/transformers": "^2.17.2",
78
80
  "better-sqlite3": "^12.11.1",
79
81
  "commander": "^15.0.0",
@@ -84,8 +86,10 @@
84
86
  "exceljs": "^4.4.0",
85
87
  "fast-xml-parser": "^5.9.3",
86
88
  "html-to-text": "^10.0.1",
89
+ "jpeg-js": "^0.4.4",
87
90
  "js-yaml": "^5.2.1",
88
91
  "lefthook": "^2.1.10",
92
+ "omggif": "^1.0.10",
89
93
  "onnxruntime-node": "^1.27.0",
90
94
  "smol-toml": "^1.8.0",
91
95
  "tsx": "^4.23.12",
@@ -119,6 +123,8 @@
119
123
  "uuid": "^14.0.1",
120
124
  "sharp": "$sharp",
121
125
  "unzipper": "^0.12.5",
122
- "adm-zip": "^0.6.0"
126
+ "adm-zip": "^0.6.0",
127
+ "fast-uri": "^3.1.6",
128
+ "qs": "^6.16.0"
123
129
  }
124
130
  }
@@ -1,11 +0,0 @@
1
- import { createRequire as __cjsRequire } from 'node:module';
2
- const require = __cjsRequire(import.meta.url);
3
- var __defProp = Object.defineProperty;
4
- var __export = (target, all) => {
5
- for (var name in all)
6
- __defProp(target, name, { get: all[name], enumerable: true });
7
- };
8
-
9
- export {
10
- __export
11
- };
@@ -1,23 +0,0 @@
1
- import { createRequire as __cjsRequire } from 'node:module';
2
- const require = __cjsRequire(import.meta.url);
3
- import {
4
- buildEvent,
5
- relay,
6
- relayInProcess
7
- } from "./token-goat-chunk-Q4LOQY44.mjs";
8
- import {
9
- MAX_STDIN_BYTES,
10
- readStdinJson
11
- } from "./token-goat-chunk-VCNW7BGU.mjs";
12
- import "./token-goat-chunk-LJ3CHCTT.mjs";
13
- import "./token-goat-chunk-CGWACYYZ.mjs";
14
- import "./token-goat-chunk-UZ2NFOOZ.mjs";
15
- import "./token-goat-chunk-AO2QD2AG.mjs";
16
- import "./token-goat-chunk-AEX54RUZ.mjs";
17
- export {
18
- MAX_STDIN_BYTES,
19
- buildEvent,
20
- readStdinJson,
21
- relay,
22
- relayInProcess
23
- };