token-goat 2.9.29 → 2.9.31

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 (197) hide show
  1. package/README.md +53 -42
  2. package/SECURITY.md +37 -11
  3. package/THIRD_PARTY_NOTICES.md +41 -4
  4. package/dist/native/linux-arm64/tg-hook +0 -0
  5. package/dist/native/linux-x64/tg-hook +0 -0
  6. package/dist/native/win32-arm64/tg-hook.exe +0 -0
  7. package/dist/native/win32-x64/tg-hook.exe +0 -0
  8. package/dist/ort-wasm-simd-threaded.mjs +59 -0
  9. package/dist/{token-goat-chunk-SE6E4BKJ.mjs → token-goat-chunk-25TRAWET.mjs} +23 -6
  10. package/dist/token-goat-chunk-2KXO6V2B.mjs +203 -0
  11. package/dist/{token-goat-chunk-UXSXQQIY.mjs → token-goat-chunk-2M7E4PF7.mjs} +43 -14
  12. package/dist/token-goat-chunk-2UPZGKKY.mjs +188 -0
  13. package/dist/token-goat-chunk-2WLPWKHV.mjs +135 -0
  14. package/dist/token-goat-chunk-2XBMMDBD.mjs +126 -0
  15. package/dist/token-goat-chunk-32ZOY4N5.mjs +23 -0
  16. package/dist/token-goat-chunk-3EZRNKTK.mjs +188 -0
  17. package/dist/token-goat-chunk-4ADZHI7J.mjs +63 -0
  18. package/dist/token-goat-chunk-4GBVPH3D.mjs +242 -0
  19. package/dist/{token-goat-chunk-2WOUYWLM.mjs → token-goat-chunk-4GILLLWY.mjs} +34 -7
  20. package/dist/token-goat-chunk-4NAPX7BA.mjs +90 -0
  21. package/dist/{token-goat-chunk-NMBWLRHW.mjs → token-goat-chunk-4SC7UJYV.mjs} +9981 -11563
  22. package/dist/token-goat-chunk-4SMRO4TO.mjs +366 -0
  23. package/dist/{token-goat-chunk-O6I2XU4P.mjs → token-goat-chunk-4ZBFWLXF.mjs} +310 -441
  24. package/dist/token-goat-chunk-57BIAXJL.mjs +286 -0
  25. package/dist/token-goat-chunk-5AC66HNP.mjs +115 -0
  26. package/dist/token-goat-chunk-5IMLKSOP.mjs +145 -0
  27. package/dist/token-goat-chunk-5RG5SBL6.mjs +221 -0
  28. package/dist/{token-goat-chunk-RRNZMM3A.mjs → token-goat-chunk-5Y6ZYL6U.mjs} +21 -2
  29. package/dist/token-goat-chunk-646E6U44.mjs +398 -0
  30. package/dist/token-goat-chunk-65N4RHP4.mjs +33 -0
  31. package/dist/{token-goat-chunk-6AK46ZT7.mjs → token-goat-chunk-6DG6HWP3.mjs} +13 -3
  32. package/dist/token-goat-chunk-6ENQSNRZ.mjs +573 -0
  33. package/dist/token-goat-chunk-6ZY4GNF2.mjs +91 -0
  34. package/dist/{token-goat-chunk-E5TK5DNO.mjs → token-goat-chunk-76FGDLRO.mjs} +574 -340
  35. package/dist/{token-goat-chunk-TGOXNAHW.mjs → token-goat-chunk-7EZTZOMC.mjs} +780 -767
  36. package/dist/token-goat-chunk-7MAOA6X4.mjs +3647 -0
  37. package/dist/token-goat-chunk-7R2ZVVAX.mjs +218 -0
  38. package/dist/{token-goat-chunk-TCQANXYC.mjs → token-goat-chunk-AM5V6GSX.mjs} +18 -2
  39. package/dist/token-goat-chunk-AUNGYYQX.mjs +184 -0
  40. package/dist/token-goat-chunk-AUQYRMMH.mjs +2985 -0
  41. package/dist/token-goat-chunk-B3PEKBYG.mjs +128 -0
  42. package/dist/{token-goat-chunk-KOPIR3KZ.mjs → token-goat-chunk-BA4HI6BW.mjs} +1910 -165
  43. package/dist/{token-goat-chunk-52NSDWYY.mjs → token-goat-chunk-BFCWY73P.mjs} +58 -20
  44. package/dist/token-goat-chunk-BPGCUMR7.mjs +30 -0
  45. package/dist/token-goat-chunk-BWGFUZZE.mjs +25 -0
  46. package/dist/token-goat-chunk-C75NA7PL.mjs +322 -0
  47. package/dist/token-goat-chunk-CBIQ7WTD.mjs +87 -0
  48. package/dist/token-goat-chunk-CETM74KC.mjs +307 -0
  49. package/dist/token-goat-chunk-CIMQ2BB3.mjs +233 -0
  50. package/dist/{token-goat-chunk-DNSPXQXQ.mjs → token-goat-chunk-CJLS3CDL.mjs} +70 -18
  51. package/dist/{token-goat-chunk-MELOKFDJ.mjs → token-goat-chunk-CO3UYX6G.mjs} +7 -1
  52. package/dist/token-goat-chunk-COPNXRJM.mjs +154 -0
  53. package/dist/token-goat-chunk-CSJ5L3NL.mjs +479 -0
  54. package/dist/token-goat-chunk-CVCEVUGK.mjs +315 -0
  55. package/dist/token-goat-chunk-CXHOFN7R.mjs +371 -0
  56. package/dist/token-goat-chunk-D735C4QM.mjs +603 -0
  57. package/dist/{token-goat-chunk-E4ZOJIZK.mjs → token-goat-chunk-DGS2LVNA.mjs} +70 -160
  58. package/dist/{token-goat-chunk-L7H3U27L.mjs → token-goat-chunk-DOWRSMWO.mjs} +1 -1
  59. package/dist/token-goat-chunk-E45INMY3.mjs +71 -0
  60. package/dist/token-goat-chunk-EC5F37F4.mjs +244 -0
  61. package/dist/{token-goat-chunk-XTQAOTSO.mjs → token-goat-chunk-EDBDRVH5.mjs} +4 -1
  62. package/dist/token-goat-chunk-EELTBKDM.mjs +613 -0
  63. package/dist/token-goat-chunk-EVMZJ27Z.mjs +147 -0
  64. package/dist/token-goat-chunk-EXKGNHEG.mjs +2096 -0
  65. package/dist/{token-goat-chunk-YOOVFPTP.mjs → token-goat-chunk-F4HYL64R.mjs} +12 -2
  66. package/dist/token-goat-chunk-F6PAUIEK.mjs +624 -0
  67. package/dist/token-goat-chunk-FDSREWLX.mjs +692 -0
  68. package/dist/{token-goat-chunk-NMTKNYGF.mjs → token-goat-chunk-FNFBKLRD.mjs} +74 -9
  69. package/dist/token-goat-chunk-G5PJ7AGM.mjs +36 -0
  70. package/dist/token-goat-chunk-GGGG7RWB.mjs +236 -0
  71. package/dist/{token-goat-chunk-TDUT2CL6.mjs → token-goat-chunk-GL3ETKSC.mjs} +276 -19
  72. package/dist/{token-goat-chunk-7CPMDFGB.mjs → token-goat-chunk-GVB2MUST.mjs} +265 -1567
  73. package/dist/token-goat-chunk-GWFLAUMQ.mjs +307 -0
  74. package/dist/token-goat-chunk-HAVAOB3S.mjs +87 -0
  75. package/dist/token-goat-chunk-HLPW63A7.mjs +48 -0
  76. package/dist/token-goat-chunk-HU4TORC5.mjs +1377 -0
  77. package/dist/token-goat-chunk-I7VKZZAW.mjs +35 -0
  78. package/dist/token-goat-chunk-IA6XSPE3.mjs +218 -0
  79. package/dist/token-goat-chunk-IAGXMHIK.mjs +863 -0
  80. package/dist/{token-goat-chunk-435UQTKD.mjs → token-goat-chunk-IK2DRMFX.mjs} +129 -74
  81. package/dist/{token-goat-chunk-ZER6EAXZ.mjs → token-goat-chunk-INNPTRUX.mjs} +2954 -2783
  82. package/dist/token-goat-chunk-JJ5BX4F4.mjs +129 -0
  83. package/dist/token-goat-chunk-JVFSHVFJ.mjs +129 -0
  84. package/dist/token-goat-chunk-KGEFFSHD.mjs +50 -0
  85. package/dist/token-goat-chunk-KKUFXAPD.mjs +25 -0
  86. package/dist/token-goat-chunk-KQBLRPBN.mjs +433 -0
  87. package/dist/token-goat-chunk-LCNQ2CNN.mjs +497 -0
  88. package/dist/token-goat-chunk-M3LFJDPU.mjs +58 -0
  89. package/dist/token-goat-chunk-M52KPT3C.mjs +87 -0
  90. package/dist/token-goat-chunk-MBEG42H7.mjs +150 -0
  91. package/dist/{token-goat-chunk-EVC4TOLE.mjs → token-goat-chunk-METECDOH.mjs} +3 -12
  92. package/dist/{token-goat-chunk-2AWQ2R5X.mjs → token-goat-chunk-MHX7XCC3.mjs} +3 -2
  93. package/dist/token-goat-chunk-MNWKPRAL.mjs +13 -0
  94. package/dist/token-goat-chunk-N5LSOVSU.mjs +137 -0
  95. package/dist/{token-goat-chunk-DPNLUJWB.mjs → token-goat-chunk-N5MMR2IW.mjs} +30 -8
  96. package/dist/token-goat-chunk-N7B3JQ2C.mjs +160 -0
  97. package/dist/{token-goat-chunk-VMFWNHGR.mjs → token-goat-chunk-NISNMD7T.mjs} +2545 -2953
  98. package/dist/token-goat-chunk-NMD42SFQ.mjs +434 -0
  99. package/dist/token-goat-chunk-NOSS5VWY.mjs +174 -0
  100. package/dist/token-goat-chunk-NSNSPPBG.mjs +69 -0
  101. package/dist/token-goat-chunk-OBOFIHKN.mjs +38 -0
  102. package/dist/token-goat-chunk-OBRFJ5O5.mjs +76 -0
  103. package/dist/token-goat-chunk-ODBT2USM.mjs +800 -0
  104. package/dist/token-goat-chunk-OS5EM5PP.mjs +107 -0
  105. package/dist/token-goat-chunk-OUKDTVNU.mjs +1178 -0
  106. package/dist/token-goat-chunk-OXFMP75N.mjs +566 -0
  107. package/dist/{token-goat-chunk-ASLWOQ3I.mjs → token-goat-chunk-OYIYKDDT.mjs} +2 -1
  108. package/dist/{token-goat-chunk-67BNIKKO.mjs → token-goat-chunk-P6HQSWQ3.mjs} +55 -47
  109. package/dist/token-goat-chunk-P6VGNTQT.mjs +71 -0
  110. package/dist/token-goat-chunk-PFWIMH2E.mjs +261 -0
  111. package/dist/token-goat-chunk-PJZXIS3G.mjs +39 -0
  112. package/dist/token-goat-chunk-PUO5INXP.mjs +606 -0
  113. package/dist/token-goat-chunk-PX5PQWTY.mjs +462 -0
  114. package/dist/token-goat-chunk-Q5QDBC6O.mjs +111 -0
  115. package/dist/token-goat-chunk-QFNQXSDT.mjs +40 -0
  116. package/dist/token-goat-chunk-QH7YVYOY.mjs +3957 -0
  117. package/dist/token-goat-chunk-QLMUHEPB.mjs +43 -0
  118. package/dist/{token-goat-chunk-PYSUADX4.mjs → token-goat-chunk-QQOJIHL3.mjs} +50 -14
  119. package/dist/token-goat-chunk-QQTWJY2O.mjs +85 -0
  120. package/dist/token-goat-chunk-QYHO5CPH.mjs +93 -0
  121. package/dist/token-goat-chunk-RELAVWAD.mjs +27 -0
  122. package/dist/token-goat-chunk-RSLPOBKQ.mjs +339 -0
  123. package/dist/token-goat-chunk-SAMFDIS2.mjs +712 -0
  124. package/dist/token-goat-chunk-SDELXLDM.mjs +48 -0
  125. package/dist/{token-goat-chunk-PGTH2U4C.mjs → token-goat-chunk-SOAFZNNZ.mjs} +164 -40
  126. package/dist/{token-goat-chunk-G23DWHV4.mjs → token-goat-chunk-SRTHT7KV.mjs} +33 -15
  127. package/dist/token-goat-chunk-T5P5ACNQ.mjs +212 -0
  128. package/dist/token-goat-chunk-T5YGG4V3.mjs +45 -0
  129. package/dist/token-goat-chunk-TAUZ4OFS.mjs +110 -0
  130. package/dist/token-goat-chunk-TM6MC754.mjs +40 -0
  131. package/dist/token-goat-chunk-TWUKBOMP.mjs +72 -0
  132. package/dist/token-goat-chunk-U2KSB4SG.mjs +56 -0
  133. package/dist/token-goat-chunk-U4WHMG6Y.mjs +898 -0
  134. package/dist/token-goat-chunk-UA5V6P3P.mjs +122 -0
  135. package/dist/token-goat-chunk-UGJHGR5A.mjs +52 -0
  136. package/dist/{token-goat-chunk-RQGGMPCR.mjs → token-goat-chunk-UOUDVA5U.mjs} +76 -16
  137. package/dist/{token-goat-chunk-HNFZZHFW.mjs → token-goat-chunk-URRJ33GR.mjs} +22 -4
  138. package/dist/token-goat-chunk-UZOROKIS.mjs +165 -0
  139. package/dist/token-goat-chunk-VIHGGGEI.mjs +4234 -0
  140. package/dist/token-goat-chunk-VTJLAA7P.mjs +21 -0
  141. package/dist/token-goat-chunk-VWDXLT4F.mjs +678 -0
  142. package/dist/token-goat-chunk-VWZA2H4P.mjs +368 -0
  143. package/dist/{token-goat-chunk-QO3LXVVR.mjs → token-goat-chunk-VXEXMJDL.mjs} +2964 -3153
  144. package/dist/token-goat-chunk-WBA5SKT4.mjs +547 -0
  145. package/dist/{token-goat-chunk-ONXH6NZ5.mjs → token-goat-chunk-WDH44VAU.mjs} +19 -22
  146. package/dist/token-goat-chunk-X2SUP3EI.mjs +107 -0
  147. package/dist/token-goat-chunk-X3BELZSJ.mjs +125 -0
  148. package/dist/{token-goat-chunk-RLP6J7WR.mjs → token-goat-chunk-XLRBNZCC.mjs} +40 -21
  149. package/dist/{token-goat-chunk-S4VSXURL.mjs → token-goat-chunk-XRKUX6CM.mjs} +4 -0
  150. package/dist/token-goat-chunk-XX6O25RN.mjs +82 -0
  151. package/dist/token-goat-chunk-XZSUHBVS.mjs +65 -0
  152. package/dist/token-goat-chunk-Y2ZTQOCL.mjs +35 -0
  153. package/dist/{token-goat-chunk-UA5BKWWQ.mjs → token-goat-chunk-YB4VMGK2.mjs} +15 -4
  154. package/dist/token-goat-chunk-YBIIY3HR.mjs +51 -0
  155. package/dist/{token-goat-chunk-PKRWMHVP.mjs → token-goat-chunk-YHVDKQBR.mjs} +389 -221
  156. package/dist/token-goat-chunk-YJB5XLWI.mjs +26 -0
  157. package/dist/{token-goat-chunk-PQ53BSCF.mjs → token-goat-chunk-YKB6AQG3.mjs} +53 -31
  158. package/dist/token-goat-chunk-YMTG2YA5.mjs +35 -0
  159. package/dist/token-goat-chunk-YPBZYQRV.mjs +40 -0
  160. package/dist/{token-goat-chunk-HUYUHODZ.mjs → token-goat-chunk-YY6AVUFL.mjs} +13246 -12247
  161. package/dist/token-goat-chunk-Z7N5OMMP.mjs +98 -0
  162. package/dist/{token-goat-chunk-BGHSSJUA.mjs → token-goat-chunk-ZFOBKUWH.mjs} +1 -1
  163. package/dist/token-goat-chunk-ZMBL6UVV.mjs +110 -0
  164. package/dist/token-goat-chunk-ZNMNUPAZ.mjs +938 -0
  165. package/dist/token-goat-hook-client.cjs +87 -23
  166. package/dist/token-goat-hook-client.mjs +3 -3
  167. package/dist/token-goat-hook.mjs +76 -31
  168. package/dist/token-goat.core.mjs +6 -6
  169. package/docs/C4_RUNTIME_ARCHITECTURE.md +2 -2
  170. package/docs/cli.md +113 -60
  171. package/docs/install.md +63 -20
  172. package/docs/security.md +10 -8
  173. package/package.json +18 -7
  174. package/scripts/verify-native-dist.mjs +386 -0
  175. package/dist/token-goat-chunk-2NNUA2LR.mjs +0 -143
  176. package/dist/token-goat-chunk-44BM7DDQ.mjs +0 -294
  177. package/dist/token-goat-chunk-BGYOZ6DO.mjs +0 -24
  178. package/dist/token-goat-chunk-C5YPNZKQ.mjs +0 -318
  179. package/dist/token-goat-chunk-CIQ2FTY3.mjs +0 -1990
  180. package/dist/token-goat-chunk-D4ETHJD6.mjs +0 -246
  181. package/dist/token-goat-chunk-DNRPXHYH.mjs +0 -1062
  182. package/dist/token-goat-chunk-EUSDXRXU.mjs +0 -1429
  183. package/dist/token-goat-chunk-FTP4U4QA.mjs +0 -64
  184. package/dist/token-goat-chunk-G7IWXCFF.mjs +0 -456
  185. package/dist/token-goat-chunk-GLXRTTLE.mjs +0 -36
  186. package/dist/token-goat-chunk-H5J3C56Q.mjs +0 -1511
  187. package/dist/token-goat-chunk-IVWI2OCK.mjs +0 -368
  188. package/dist/token-goat-chunk-JTKCQMQA.mjs +0 -418
  189. package/dist/token-goat-chunk-LZNBQTDK.mjs +0 -26
  190. package/dist/token-goat-chunk-ORNFMIER.mjs +0 -295
  191. package/dist/token-goat-chunk-RTMVLHWX.mjs +0 -7113
  192. package/dist/token-goat-chunk-VQ6F6T7X.mjs +0 -36
  193. package/dist/token-goat-chunk-VZQEWRVH.mjs +0 -312
  194. package/dist/token-goat-chunk-XYWPOSCN.mjs +0 -807
  195. package/dist/token-goat-chunk-YAGLESS3.mjs +0 -23
  196. package/dist/token-goat-chunk-YKSRAWUU.mjs +0 -33
  197. package/dist/token-goat-chunk-Z6KBMOBI.mjs +0 -53
package/README.md CHANGED
@@ -11,19 +11,19 @@ permalink: /
11
11
 
12
12
  ***Give the model what it needs, not everything you have.***
13
13
 
14
- **85%** smaller reads · **49%** of first-read bytes withheld · **97.4%** image compression · **205+** filter & interception rules · **94–99%** skill overhead cut · compaction memory · **prompt injection** guard · **3.7 GB** never reached the model · **1.1 Gt** tokens saved
14
+ **85%** smaller reads · **49%** of first-read bytes withheld · **97.4%** image compression · **leaner prompt cache** · **205+** filter & interception rules · **94–99%** skill overhead cut · compaction memory · **prompt injection** guard · **3.7 GB** never reached the model · **1.1 Gt** tokens saved
15
15
 
16
16
  **Reduces AI token use/costs by 40–90%, and improves its focus. Fully automated, always online.**
17
17
 
18
18
  **Also defends against prompt injection. Every fetched page, tool result, and extracted document is wrapped in an untrusted-content fence before hitting the model, whether or not it matched an attack pattern, and the scan only decides what the label says. One config line to disable.**
19
19
 
20
- **Your AI re-reads the same file three times. Every compaction causes amnesia. Every build log buries the one line that matters. You pay for all of it. Token-Goat fixes all of it — automatically.**
20
+ **Your AI re-reads the same file three times. Every whole-file dump is paid for again on every turn after it. Every compaction causes amnesia. Every build log buries the one line that matters. You pay for all of it. Token-Goat fixes all of it — automatically.**
21
21
 
22
22
  Token-Goat sits silently between your AI and your tools. Re-read a file? It gets a one-line hint and a narrow-slice suggestion instead of the full file again. Grab a screenshot? A 100 KB copy reaches the model instead of 10 MB. Run `pytest`, `npm install`, `docker build`, or `cargo`? The thousands of progress bars and passing-test names are stripped to the failures before the output even reaches the context window. Open a PDF, a large Markdown doc, or a CSV? The hook intercepts it — heading tree, page count, or column preview — so the model never pays for the full file. Run `gh run watch` or `next dev` a second time? Prior output is recalled rather than re-run. Compact a long session? It gets a clean structured manifest of edited files and key symbols so nothing important is forgotten. Sessions drop 40–90%+ in cost. You change nothing about how you work.
23
23
 
24
24
  The symbol index also understands native Nginx, Caddyfile, and Apache HTTP Server configuration blocks, so `token-goat read "file::symbol"` can jump directly to them.
25
25
 
26
- Works with **Claude Code**, **Gemini CLI**, **Qwen Code**, **Codex CLI**, **Aider**, **Cursor**, **Cline**, **Windsurf**, **Copilot CLI**, **VS Code** (Copilot agent), **Visual Studio** (Copilot agent, MCP tools and instructions only), **Zed** (MCP tools only), **Kimi Code**, **Grok CLI** (xAI Grok Build), and OpenCode, plus **pi** ([pi-coding-agent](https://github.com/earendil-works/pi-mono)).
26
+ Works with **Claude Code**, **Gemini CLI**, **Qwen Code**, **Codex CLI**, **Aider**, **Cursor**, **Cline**, **Windsurf**, **Copilot CLI**, **VS Code** (Copilot agent), **Visual Studio** (Copilot agent, MCP tools and instructions only), **Zed** (MCP tools only), **Kimi Code**, **Grok CLI** (xAI Grok Build), **Antigravity CLI** (`agy`), and OpenCode, plus **pi** ([pi-coding-agent](https://github.com/earendil-works/pi-mono)).
27
27
 
28
28
  **Ask your AI to install it fully (give it this GitHub link), or install in one command:**
29
29
 
@@ -51,16 +51,19 @@ Restart your AI sessions. Run `token-goat stats` a couple of minutes after your
51
51
 
52
52
  AIs read `auth.py`. Then reads it again. And again. Then a third time after compaction wipes the session. Then it can't find what it wanted and searches other lines and files. You pay for every token and most of it is waste.
53
53
 
54
- Long sessions accumulate waste five ways. Screenshots cross the model at full resolution. A single PNG can land at 10+ MB. The agent re-reads files it already parsed earlier in the same conversation. When a session compacts, the summary LLM doesn't know which files were edited or which symbols mattered, so it preserves the wrong things. And every `pytest`, `npm install`, `docker build`, or `git log` dumps thousands of lines of progress bars, deprecation warnings, and passing-test names that bury the one line that actually matters.
54
+ Long sessions accumulate waste six ways. Screenshots cross the model at full resolution. A single PNG can land at 10+ MB. The agent re-reads files it already parsed earlier in the same conversation. When a session compacts, the summary LLM doesn't know which files were edited or which symbols mattered, so it preserves the wrong things. Every `pytest`, `npm install`, `docker build`, or `git log` dumps thousands of lines of progress bars, deprecation warnings, and passing-test names that bury the one line that actually matters.
55
55
 
56
56
  The fifth waste is skills. A single large skill injects 10k–65k tokens every time. Run a five-iteration `/improve` loop and you've paid for five full copies of the same rules. Token-Goat now blocks repeat skill loads before they happen: a PreToolUse hook intercepts the second invocation, serves the cached compact (~400 tokens) instead, and only allows a reload when compaction may have evicted the skill from context. It also intercepts direct reads of skill files and ensures the compaction manifest carries the full skill index — so nothing is forgotten and the full body never re-enters context unnecessarily.
57
57
 
58
- The fastest way to reduce AI token costs is fixing these five, not writing shorter prompts. Each one is preventable. Token-Goat intercepts all five, automatically.
58
+ The sixth waste is what a large read costs after the turn it lands in. The prompt cache makes it cheaper, not free: Anthropic bills the first cache write at 1.25x the base input price (2x for the one-hour cache), then about 0.1x for every later turn that reads it back, until the session compacts. A 2,000-line file read to check a three-line helper is carried, and paid for, on every one of those turns. It also brings compaction closer, and the turn after a compaction writes the new prefix at the write price. Token-Goat's surgical reads add only the requested symbol or slice, so every later turn carries less.
59
+
60
+ The fastest way to reduce AI token costs is fixing these six, not writing shorter prompts. Each one is preventable. Token-Goat intercepts all six, automatically.
59
61
 
60
62
  ## What changes
61
63
 
62
64
  | Without Token-Goat | With Token-Goat |
63
65
  |--------------------|------------------|
66
+ | A full-file read is written to the prompt cache once, then read back on every later turn until compaction | Surgical reads add only the lines asked for, so every later turn reads back less and compaction comes later |
64
67
  | 3.3 MB screenshot lands in model context | 84 KB compressed copy, 97.4% smaller |
65
68
  | Agent re-reads files from earlier in the session | "Already read this" reminder with narrow slice suggestion |
66
69
  | Read tool asks for lines the session was already given | Answered with a pointer at the copy already delivered instead of the file, when the text this read would return matches the text already served for it whole line for whole line. Proof rather than a read count, so it also covers a file inside the recent-read protection window; a changed file, a wider range, and `reread_deny = false` all pass through |
@@ -109,14 +112,25 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
109
112
  | Index-only files (lockfiles, source maps, bundles) read on every session | Pre-Read suppression for read-only files (package-lock.json, *.map, dist/), skipped unless explicitly edited |
110
113
  | Large markdown file read in full (README.md, CHANGELOG.md, CLAUDE.md ≥8 KB) | Heading tree intercepted instead — H1–H3 with `#2`/`#3` disambiguation; `token-goat section` shortcuts listed for well-known files; post-edit injects a re-read suggestion rather than the full file |
111
114
  | Subagent reads a ≥30 KB markdown file whole, on its first look at it | Off by default. Set `subagent_markdown_first_read_deny = true` under `[hints]` to block that read and answer with the heading tree instead. Only fires in a subagent, only on a first, un-ranged read of a `.md`/`.mdx`/`.markdown` file with at least three headings; a read that already asks for a line range, and every main-session read, pass through untouched |
115
+ | Claude Code spawns an agent without `subagent_type` while tools-restricted agent definitions exist | Off by default. Set `agent_scoped_spawn_deny = true` under `[hints]` (or `TOKEN_GOAT_AGENT_SCOPED_SPAWN_DENY=1`) to refuse the first such spawn in a session and name the restricted agents, so the retry can pick one. An untyped spawn runs as general-purpose, whose context starts with every tool and MCP schema on the machine. Re-issuing the same call runs it: the refusal happens once per session. A spawn that names any `subagent_type`, `general-purpose` included, is never refused, and Copilot CLI and VS Code are left alone |
116
+ | An agent ends its turn after editing source files that no test, lint, typecheck or build run has covered since | A one-time note at the Stop event names the unverified files and the checks that would cover them. It is an advisory, never a block, because a block at Stop can trap a session in a loop. Claude Code and Codex CLI show it; Copilot CLI's and VS Code's stop hooks accept only a block, so it stays silent there. Turn it off with `stop_verification_advisory = false` under `[hints]` (or `TOKEN_GOAT_STOP_VERIFICATION_ADVISORY=0`) |
117
+ | A subagent finishes with a failed last check, source edited after its last passing check, or a report that claims a change it never made | Up to three lines are appended to the report the parent receives, saying which of those happened. A subagent with nothing to flag gets nothing added. On Copilot CLI the lines ride on the subagent report rewrite; the report part of that rewrite is the secret-redacted original, so a secret token-goat redacted stays redacted. Copilot's tool events carry no agent id, so there only the claimed-a-change-never-made check applies to a subagent (it reads the git tree), not the failed-check or unverified-edit flags. On Codex the lines ride on the result of the multi-agent v1 `wait_agent` tool, keyed on the subagent's thread id; Codex's v2 multi-agent tools return no report, so nothing is delivered there. VS Code Chat gets no receipt, because its SubagentStop payload carries no report text, and Visual Studio and JetBrains have no agent hooks. Turn it off with `agent_receipt = false` under `[hints]` (or `TOKEN_GOAT_AGENT_RECEIPT=0`) |
118
+ | Claude Code Bash command on Windows that contains two backslashes in a row | Run from a temp file when your permission rules already allow the command, otherwise refused before it runs, with the reason and what to do instead. Git Bash halves every run of two or more backslashes in the command it is handed, so an escaped path, a regular expression or a `printf` format string would run as different text with no error. A command that needs no prompt is written, as typed, to a file that deletes itself on its first line, and run from there; one that would prompt, or that uses `$`, a backtick, `eval`, rm or a protected path, is refused, because the prompt would show a file name and not the command. Then write the text to a file with the Write tool and run it by path, use the PowerShell tool, or use forward slashes in paths. A command over 7,000 characters with no such backslashes is left alone. Claude Code on Windows only; set `deny_bash_double_backslash = false` under `[hints]` (or `TOKEN_GOAT_DENY_BASH_DOUBLE_BACKSLASH=0`) to turn it off |
112
119
  | A source file is read whole for the first time, with no earlier copy to compare it against | Keeps each function's declaration and its first eight lines, replacing the rest with one line naming the symbol, the exact range removed, and the command that returns it. Set `fold_code_bodies = false` under `[hints]` to turn it off. Comment blocks of twelve lines or more fold to their first two on the same terms. Imports, signatures, type declarations, classes and interfaces are never folded, and a file whose index no longer matches what is on disk keeps its bodies. Measured on this repository's own source: 229 of 256 files fold, and 43% of what a read of them delivers is removed. A read that already names an offset or a limit, and any file holding something that looks like a secret, pass through untouched |
120
+ | A source or markdown file of 50 KB or more is read whole for the first time, and token-goat has indexed its symbols or headings | The read goes through with a note naming the file's size, its symbol or heading count and the `read`, `outline` or `skeleton` command that returns just the part you need. Set `first_read_symbol_policy = "deny"` under `[hints]` (or `TOKEN_GOAT_FIRST_READ_SYMBOL_POLICY=deny`) to refuse the whole-file read instead, or `"off"` to say nothing. `first_read_symbol_bytes` sets the size (50000 by default, 0 turns the check off). Both can only be set in your global config or the environment: a repository's `.token-goat.toml` cannot set them |
121
+ | A Write, Edit, MultiEdit or Codex `apply_patch` puts a placeholder such as `// ... existing code ...` or `[rest of file unchanged]` into a file, or a whole-file Write to a file that was only partly read deletes five or more lines the session never saw | Refused before it runs, with the placeholder or the unseen line ranges named and a `token-goat read` command for each range. Repeating the identical call goes through. Codex's pre-tool hook can refuse an `apply_patch`, and VS Code's `apply_patch` and `multi_replace_string_in_file` are checked; VS Code's `insert_edit_into_file` edits are not covered. Set `write_blind_drop = "warn"` under `[hints]` (or `TOKEN_GOAT_WRITE_BLIND_DROP=warn`) to add a note instead of refusing, or `"off"` to say nothing. A note is silenced during quiet hours, a refusal is not |
122
+ | A Write creates a new file named like a copy of an existing one in the same directory (`solver_v2.py`, `solver_fixed.py`, `solver.py.bak`) and repeats most of that file | A note, once per original per session, naming how much of the original it repeats and suggesting `Edit`, `token-goat replace` or `token-goat outline` on the original. It needs the original to reach `write_rewrite_min_lines` (40) and `write_rewrite_unchanged_pct` (75) under `[hints]`. Files under `migrations`, `versions`, `fixtures`, `__snapshots__` and any `vN` directory below the project root are skipped, `write_sibling_copy_allow` adds path globs to skip, and `write_sibling_copy = false` (or `TOKEN_GOAT_WRITE_SIBLING_COPY=0`) turns it off |
123
+ | A Read, Grep, Edit or Write names a path that does not exist, or a shell command fails with `No such file or directory` | The failed call is answered with up to three existing paths it probably meant (a stray `@` or `:12` suffix, a curly apostrophe, a one or two letter typo, a case difference, the same name in another directory), once per path per session. A credential-style file name is never offered. The read commands print the same list under `Did you mean:` |
124
+ | An Edit fails because its `old_string` was one invisible character off (trailing space, curly quote, indentation) | The reply quotes the exact matching lines as they are on disk, so the retry copies them instead of re-reading the file. A credential-style file name gets the line range only |
125
+ | A shell command fails that an earlier session fixed twice (an install, build or test command from a short allowlist) | One line offers the fix again, marked `not verified`. Only known runners and paths are kept, and no other part of the command is stored on disk, only digests |
126
+ | A whole-file re-read is refused and the agent asks again, or tries the same file at other line ranges | The whole-file refusal is given at most once per file per compaction, then the read goes through with a short note. A refused line range locks later line-range reads on that file until it changes, and the refusal names the `read "path@START-END"` or `skeleton` command to use instead |
113
127
  | PDF opened via Read | Full read denied; PDF shows page count and outline (`token-goat pdf-extract` pulls the actual text, optionally paged/sliced, when the outline isn't enough) |
114
128
  | Excel/PowerPoint/Word file (.xlsx/.pptx/.docx) opened via Read | Full read denied; redirects to the matching narrow-slice command family (`xlsx-sheets`/`xlsx-columns`/`xlsx-head`/`xlsx-range`/`xlsx-query`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `docx-outline`/`docx-tables`/`docx-text`) instead of extracting the whole document as text |
115
129
  | SQLite database (.db, .sqlite, .sqlite3, .db3) opened via Read | Full read denied; redirects to the SQLite narrow-slice family (`sqlite-tables`/`sqlite-schema`/`sqlite-query`) instead of reading binary bytes into context |
116
130
  | Parquet file (.parquet) opened via Read | Full read denied; redirects to DuckDB for SQL querying instead of reading binary columnar data |
117
131
  | Large JSON or YAML file (≥20 KB) read in full | Top-level keys or array summary previewed; redirects to `json-outline`/`json-query` or `yaml-outline`/`yaml-query` instead of blowing context on multi-megabyte payloads |
118
132
  | A file read through inline Python, Node or PowerShell (`python -c "...open('r.json')..."`) | Runs capped at 2,000 tokens with no other filtering, so a one-key projection comes back whole; a whole-file dump is cut, ends with a recall id, and names `json-outline`/`json-query` for JSON and YAML. Still refused where the wrapper cannot run: a pipeline or chain, a background `&`, compression turned off |
119
- | Findings you want to survive a compaction | `token-goat note set <key> "<finding>"` stores one per project; notes print at every session start and in the compaction manifest |
133
+ | Findings you want to survive a compaction | `token-goat note set <key> "<finding>"` stores one per project; notes print at every session start and in the compaction manifest. Add `--anchor src/auth.ts::login`, or name a `file::symbol` in the finding, and the note is marked "(changed since note)" once that symbol changes. If a slow start misses its 3-second deadline on Copilot CLI or VS Code, the session-start text and notes arrive with the first tool call |
120
134
  | Large JSON Lines file (.jsonl ≥20 KB) read in full | Record count and first-record schema shown; suggests slicing records with offset/limit instead of loading millions of tokens |
121
135
  | Other Office binary (.odt, .ods, .ott, .odp) opened via Read | Full read denied; redirects to `pandoc` for text extraction (no dedicated reader for these formats yet) |
122
136
  | Large CSV or TSV file (≥10 KB) read in full | Column headers, row count, and 3 sample rows shown; `token-goat csv-query` projects columns and/or filters rows instead of a full read; `duckdb` query suggestion for very large tabular data |
@@ -178,7 +192,7 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
178
192
  | Broad recursive Glob sweep (`*`, `**/*`) on root directory | Pre-Glob hook warns against tree-dumping and points at `token-goat map --compact` for fast, lightweight structure inspection |
179
193
  | Guessing database column names in `session_store_sql` / `sql` and falling back to `SELECT *` | `token-goat session-schema [table]` and `describe <target>` provide instant schema discovery; `post_tool_use_failure` hook intercepts unknown columns and guides the query — ~85–95% smaller than trial-and-error `SELECT *` dumps |
180
194
  | Compound test/build pipeline (`npm run build && npm run typecheck && npm test`) | Post-Bash hook routes chained build/test/lint commands to `generic-ci` compression, dropping verbose passing steps and compiler noise |
181
- | Verbose command output on Windows under Codex CLI, Copilot CLI, or a PowerShell session, where the compression hook used to skip every command | Pre-Bash hook hands the command to PowerShell itself, base64-encoded through an environment variable the launched script clears first, so backslashes, quotes, and redirection survive and the output is compressed like anywhere else |
195
+ | Verbose command output on Windows under Codex CLI or a PowerShell session, where the compression hook used to skip every command | Pre-Bash hook hands the command to PowerShell itself, base64-encoded through an environment variable the launched script clears first, so backslashes, quotes, and redirection survive and the output is compressed like anywhere else |
182
196
 
183
197
  On a per-token API plan, 100K wasted tokens per session runs about $0.30. Five sessions a week is ~$450/year. AI coding cost reduction at that scale comes from fixing the waste, not from using the product less. Token-goat is free. And on subscription plans, it can result in limits feeling 10x higher.
184
198
 
@@ -202,8 +216,8 @@ Numbers below come from synthetic-fixture benchmarks in the test suite, except t
202
216
 
203
217
  | Source | Improvement | Measured impact | Where |
204
218
  |--------|-------------|-----------------|-------|
205
- | Large-document outline | A read naming no `offset` or `limit` on a markdown document over 8 KB with six or more headings returns the lead-in and heading tree, plus the `token-goat section` command that reads any section verbatim | Measured on 13,870 real reads across 5,104 sessions: withholds 40.4% of all bytes the Read tool delivers. A 104,890 B document returns 1,541 B with its opening paragraph intact. Sections sum to the document, so a reader who pulls every one back pays about 3% over reading it whole | `src/hooks_read.ts` (`foldMarkdownOutline`) |
206
- | Large-source skeleton | A read naming no `offset` or `limit` on a source file over 12 KB with eight or more declarations returns the preamble and one line per declaration, each withheld run naming the `token-goat read "file::Symbol"` or ranged `Read` that returns it | Measured on the same corpus: withholds up to 8.6% of all bytes the Read tool delivers, a ceiling rather than a booked total, since the shipping gates are narrower than the measurement's. The skeleton is 10.5% of a `.ts` file. Median symbol body is 4.1% of its file, so a reader breaks even at roughly 22 bodies pulled back out of an average 30.8 | `src/hooks_read.ts` (`foldSourceSkeleton`) |
219
+ | Large-document outline | A read naming no `offset` or `limit` on a markdown document over 8 KB with six or more headings returns the lead-in and heading tree, plus the `token-goat section` command that reads any section verbatim | Measured on 13,870 real reads across 5,104 sessions: withholds 40.4% of all bytes the Read tool delivers. A 104,890 B document returns 1,541 B with its opening paragraph intact. Sections sum to the document, so a reader who pulls every one back pays about 3% over reading it whole | `src/hooks_read_post.ts` (`foldMarkdownOutline`) |
220
+ | Large-source skeleton | A read naming no `offset` or `limit` on a source file over 12 KB with eight or more declarations returns the preamble and one line per declaration, each withheld run naming the `token-goat read "file::Symbol"` or ranged `Read` that returns it | Measured on the same corpus: withholds up to 8.6% of all bytes the Read tool delivers, a ceiling rather than a booked total, since the shipping gates are narrower than the measurement's. The skeleton is 10.5% of a `.ts` file. Median symbol body is 4.1% of its file, so a reader breaks even at roughly 22 bodies pulled back out of an average 30.8 | `src/hooks_read_post.ts` (`foldSourceSkeleton`) |
207
221
  | Image shrink | Every still is encoded both ways and the smaller file wins, rather than one codec being assumed | 2560x1440 screenshot-shaped PNG: 2,782,963 -> 259,475 bytes (91% smaller) | `src/image_shrink.ts` (codec selection) |
208
222
  | Repomap output | `--compact` trims the top-symbols list to 10 (vs 30) and drops the recent-files section and per-symbol locations | Denser overview for the same byte budget | `src/baseline.ts` (`buildProjectMap`, `token-goat map --compact`) |
209
223
  | DB reindex | Batched single transaction + composite indexes on `(file_id, kind)` | 100 files / 10K rows: 84 s → 1 s (~80× faster) | `src/parser.ts`, `src/db.ts` (index migration) |
@@ -255,6 +269,8 @@ out: 38 lines # ~300 tokens (97% smaller)
255
269
 
256
270
  A line number works in place of a name, which is what you are holding when a grep hit, a stack frame, or a diff hunk points at code: `token-goat read "src/auth.py:142"` returns the symbol enclosing line 142, and `read "src/auth.py:142-190"` returns every symbol the range touches. A Windows drive letter stays part of the path, so `C:/src/auth.py:142` reads line 142 of that file, and a bare `C:142` stays a drive-relative path rather than a line number.
257
271
 
272
+ A line range given as a flag (`--lines 1-5`, `--line`, `--range`, `--start`/`--end`, `--from`/`--to`, `--offset`/`--limit`) is not an option of `read`, but when the file argument names no place yet and the range is complete it runs as `read "file@1-5"` and says so on stderr: ``Note: ran as `token-goat read "f.ts@1-5"` ...``. A guessed spelling of a real flag (`--top`, `-k`, `--max` or `--count` for `--limit` on `search`, `semantic` and `recall`; `--search`, `--filter` or `--pattern` for `--grep` on the cached-output commands) runs the same way with the same note when its value is usable. Anything less certain still fails, with a `Did you mean` line naming the command to run.
273
+
258
274
  Same applies to `token-goat section "README.md::Install"` — one heading instead of the whole document. Anchor IDs and setext headings resolve too, so `section "doc.md::Quick-start"` works when the file uses `Quick start` as an `<h2>` with an explicit `{#quick-start}` anchor.
259
275
 
260
276
  ### 3. Compact manifest — preserve what mattered
@@ -304,16 +320,20 @@ FAILED tests/test_x.py::test_one
304
320
  [token-goat: pytest filter compressed 4.8 KiB to 0.1 KiB (97% saved)]
305
321
  ```
306
322
 
307
- Built-in output compression covers 150+ dev tool CLIs: `pytest`, `jest` / `vitest`, `cargo`, `npm` / `pnpm` / `yarn` / `bun`, `docker`, `kubectl` / `helm`, `aws`, `ruff` / `eslint` / `mypy` / `pylint` / `oxlint`, `git`, `make` / `gradle` / `mvn` / `ant` / `bazel`, `go test` / `golangci-lint`, `terraform` / `pulumi` / `cdk`, `pip` / `uv` / `conda`, `python`, `gh`, `ansible`, `pre-commit`, `grep`, `eza` / `ls`, `fd`, `bat`, `jq`, `yq`, `curl` / `wget`, `rsync`, `dotnet`, `cmake` / `ctest`, `swift` / `xcodebuild`, `ruby` / `bundler`, `elixir` / `mix`, `php` / `composer`, `flutter` / `dart`, `rust` / `cargo`, `kotlin` / `ktlint`, `zig`, `crystal`, `haskell` / `cabal`, `nix`, `R`, `c++` (conan / vcpkg / cppcheck / clang-tidy), `wrangler` / `hardhat` / `serverless`, `erlang`, `fly.io`, `forge`, `elm`, `julia`, `tox`, `vault`, `packer`, `nx` / `lerna` / `turbo`, `prettier` / `biome`, `sass`, `wasm-pack`, `deno`, **and AI tool CLIs**: `aider`, `gemini`, `claude`, `gh copilot`, `copilot`, `cursor`, `windsurf` (incl. Cascade), `opencode`, `continue`, `cline`. Each filter strips ANSI escapes, collapses `\r` progress bars, dedupes repeated lines, groups linter issues by rule, keeps every error block verbatim, and caps total output at 1000 lines / 64 KiB. Compound commands (`cmd1 && cmd2`) are wrapped per segment, so `git diff && git log` compresses both halves. Disable globally with `TOKEN_GOAT_BASH_COMPRESS=0`, per-filter via `[bash_compress] disabled_filters = ["docker"]` in config.toml, or preview the output of any command with `token-goat compress --cmd '<your command>'`. To exclude project-specific directories from indexing (temporary venvs, build sandboxes), add `[indexing] skip_dirs = ["my-tmpdir"]` to config.toml. To exclude specific generated files by basename instead (coverage reports, bundler stats), set `[indexing] skip_files = [...]`; it defaults to `["coverage.json", "coverage-final.json"]`, so a project with a legitimately-named `coverage.json` it wants indexed can override the list to drop that entry.
323
+ This is semantic log filtering into natural plain text rather than encoded blobs or compressed data. The model reads standard output directly, requiring zero decoding steps or extra reasoning tokens.
324
+
325
+ Built-in output compression covers 150+ dev tool CLIs: `pytest`, `jest` / `vitest`, `cargo`, `npm` / `pnpm` / `yarn` / `bun`, `docker`, `kubectl` / `helm`, `aws`, `ruff` / `eslint` / `mypy` / `pylint` / `oxlint`, `git`, `make` / `gradle` / `mvn` / `ant` / `bazel`, `go test` / `golangci-lint`, `terraform` / `pulumi` / `cdk`, `pip` / `uv` / `conda`, `python`, `gh`, `ansible`, `pre-commit`, `grep`, `eza` / `ls`, `fd`, `bat`, `jq`, `yq`, `curl` / `wget`, `rsync`, `dotnet`, `cmake` / `ctest`, `swift` / `xcodebuild`, `ruby` / `bundler`, `elixir` / `mix`, `php` / `composer`, `flutter` / `dart`, `rust` / `cargo`, `kotlin` / `ktlint`, `zig`, `crystal`, `haskell` / `cabal`, `nix`, `R`, `c++` (conan / vcpkg / cppcheck / clang-tidy), `wrangler` / `hardhat` / `serverless`, `erlang`, `fly.io`, `forge`, `elm`, `julia`, `tox`, `vault`, `packer`, `nx` / `lerna` / `turbo`, `prettier` / `biome`, `sass`, `wasm-pack`, `deno`, **and AI tool CLIs**: `aider`, `gemini`, `claude`, `gh copilot`, `copilot`, `cursor`, `windsurf` (incl. Cascade), `opencode`, `continue`, `cline`. Each filter strips ANSI escapes, collapses `\r` progress bars, dedupes repeated lines, groups linter issues by rule, keeps every error block verbatim, and caps total output at 1000 lines / 64 KiB. Compound commands (`cmd1 && cmd2`) are wrapped per segment, so `git diff && git log` compresses both halves. Disable globally with `TOKEN_GOAT_BASH_COMPRESS=0` (output is still cached, so `bash-output` and `recall` keep working), per-filter via `[bash_compress] disabled_filters = ["docker"]` in config.toml, or preview the output of any command with `token-goat compress --cmd '<your command>'`. To exclude project-specific directories from indexing (temporary venvs, build sandboxes), add `[indexing] skip_dirs = ["my-tmpdir"]` to config.toml. To exclude specific generated files by basename instead (coverage reports, bundler stats), set `[indexing] skip_files = [...]`; it defaults to `["coverage.json", "coverage-final.json"]`, so a project with a legitimately-named `coverage.json` it wants indexed can override the list to drop that entry. Minified bundles (`*.min.js`, `*.min.mjs`, `*.min.cjs`, `*.min.css`, `*-min.js`) are left out of the index by default, because their one-letter names bury real symbols; set `[indexing] skip_minified = false` in the global config.toml to index them.
308
326
 
309
327
  `gh api` responses get an extra pass: boilerplate `*_url` fields (`followers_url`, `gists_url`, `starred_url`, and around a dozen others) are stripped from JSON objects; `html_url`, `avatar_url`, `clone_url`, and `ssh_url` are kept. User and repo objects typically shrink 60–80%. When token-goat sees a GitHub permission error in the output or a non-zero exit on a security endpoint, it injects a system message suggesting `gh auth refresh -s security_events`.
310
328
 
311
329
  A failing `pytest` / `jest` / `vitest` / `go test` / `cargo test` run (including bare `npm test`/`npm run test`/`yarn test`/`pnpm test`) also gets a one-line advisory naming the exact `token-goat bash-output <id> | token-goat failures` command to run instead of re-reading the raw dump. Silent on a passing run, a non-test command, or output too small to be worth reducing.
312
330
 
313
- Give the command three ways: as positional arguments (`token-goat compress pytest -v tests/`), as one string with `-c` / `--cmd`, or base64-encoded with `--cmd-b64`. The base64 form preserves quotes, backslashes, and dollar signs that a harness or an intermediate shell would otherwise mangle; it takes precedence when more than one form is supplied. `--native` runs the command through the platform's own shell (`cmd.exe` on Windows) instead of bash, which keeps Windows path backslashes intact for tools that reject the forward-slash form.
331
+ Agents and interactive users must pass ordinary plain shell commands directly: as positional arguments (`token-goat compress pytest -v tests/`) or as one string with `-c` / `--cmd`. `--cmd-b64` is strictly an internal hook transport flag generated programmatically by token-goat hooks; never synthesize base64 manually, because LLM tokenization can corrupt characters and file paths. The hook transport preserves quotes, backslashes, and dollar signs and takes precedence when more than one form is supplied. `--native` runs the command through the platform's own shell (`cmd.exe` on Windows) instead of bash, which keeps Windows path backslashes intact for tools that reject the forward-slash form.
314
332
 
315
333
  `-q` / `--quiet-success` trades a clean run's output for one line. On exit code 0 the full output is stored for recall and only the summary prints; on any other exit code the compressed output is delivered as usual.
316
334
 
335
+ The wrapped command gets no standard input by default, on every path (plain, `--max-tokens`, `-q`, a filter, `--no-compress`), so a hook-wrapped command can never wait on input the harness will not send. `--stdin` hands it the caller's standard input instead, byte for byte: `printf 'héllo' | token-goat compress --stdin -c cat`.
336
+
317
337
  ```
318
338
  $ token-goat compress -q --cmd "npm run lint"
319
339
  [tg: ok] npm run lint (22.2s, recall: token-goat bash-output 326d9800e732eb9d)
@@ -325,6 +345,14 @@ Token-goat tracks how close a session is to the autocompact trigger and tightens
325
345
 
326
346
  For recurring scheduler loops, the 25th, 100th, and 250th observed delivery in a session receive a one-time checkpoint/fresh-session reminder; it prevents further accumulation but cannot reclaim input already injected. The count is independent of the scheduler's bracketed identifier, which some hosts repeat for every delivery. Before a direct test command without a focused selector or explicit timeout, the Bash hook either preserves its existing compressor timeout or gives an advisory when compression cannot apply; it never stops the test.
327
347
 
348
+ ### 7. Prompt cache: less to read back on every turn
349
+
350
+ Anthropic bills a prompt cache write at 1.25x the base input price (2x for the one-hour cache) and a cache read at 0.1x. Other providers price cached input differently. The cache holds the prompt prefix, so appending a tool result does not invalidate what is already cached. Changing something earlier in the prompt does: the system prompt, the model, or the tool definitions, which connecting an MCP server changes.
351
+
352
+ What a whole-file read costs is everything after it. The file is written to the cache once, then read back on every later turn until the session compacts, and its extra lines bring that compaction closer. Compaction replaces the conversation with a summary, so the turn after it writes a new prefix at the write price.
353
+
354
+ Token-Goat's surgical commands (`read "file::symbol"`, `section "file::Heading"`, `skeleton file`, `json-query`) return only the lines asked for. Less is carried into every later turn, and compaction comes later.
355
+
328
356
  ## Install
329
357
 
330
358
  **Requirements:** Node.js 22.16 or later, on any platform.
@@ -335,11 +363,11 @@ token-goat install
335
363
  token-goat doctor # confirms hooks are wired; reports any failure it finds
336
364
  ```
337
365
 
338
- Three commands. Hooks register and start working immediately: no terminal popups, no tray icon, no service to babysit. That wires up Claude Code; other agent CLIs are added with a flag (`--codex`, `--copilot`, and siblings).
366
+ Three commands. Hooks register and start working immediately: no terminal popups, no tray icon, no service to babysit. That wires up Claude Code; other agent CLIs are added with a flag (`--codex`, `--copilot`, and siblings). On Windows and Linux, each hook runs through a small native client, `tg-hook`, that hands the call to token-goat's resident hook server instead of starting Node, and runs the Node command itself when the server is away; a Windows binary ships only when it is code-signed (without one, Windows uses the Node command as macOS does), and the Linux ones carry build provenance. Details and the opt-out: [Native hook client](docs/install.md#what-gets-installed).
339
367
 
340
368
  > **WSL performance tip:** Keep active repositories on WSL's native ext4 filesystem (`~/projects/...`) rather than Windows mounts (`/mnt/c/...`) to avoid 9P cross-OS filesystem translation overhead during initial indexing.
341
369
 
342
- Per-harness setup for Codex, Gemini, Qwen, Kimi, opencode, OpenClaw, pi, Copilot, Grok and Cline/Windsurf/Cursor, the companion CLI tools worth installing alongside it, upgrading, and the full list of what lands on your machine: **[Install guide](docs/install.md)**.
370
+ Per-harness setup for Codex, Gemini, Qwen, Kimi, opencode, OpenClaw, pi, Copilot, Grok, Antigravity and Cline/Windsurf/Cursor, the companion CLI tools worth installing alongside it, upgrading, and the full list of what lands on your machine: **[Install guide](docs/install.md)**.
343
371
 
344
372
  ## CLI
345
373
 
@@ -355,7 +383,7 @@ Every command, with its flags, its output, and the comparison workflows that cha
355
383
  token-goat mcp-serve
356
384
  ```
357
385
 
358
- Runs token-goat as an MCP ([Model Context Protocol](https://modelcontextprotocol.io)) stdio server, exposing surgical-read tools (including `brief`, a one-shot symbol orientation call bundling body, callers, and containing doc section, and `index_status`, which reports whether a project's index has ever been populated, current file/symbol counts, dirty-queue depth, worker liveness, and embeddings availability — call it after an unexpectedly empty result from another tool, since an MCP-only client has no hook layer to warn it apart from a genuine "not found") plus `compress_text`, `retrieve_text`, `handoff_create`, and `handoff_resolve`. These local-only tools use bounded, redacted storage; MCP never intercepts a client's built-in file reads.
386
+ Runs token-goat as an MCP ([Model Context Protocol](https://modelcontextprotocol.io)) stdio server, exposing seven tools by default (`symbol`, `read`, `section`, `outline`, `semantic`, `refs`, and `retrieve_text`, which recovers a compressed payload) and eleven more you switch on by name, since every tool listed is sent to the model on every turn. The others are surgical-read tools (including `brief`, a one-shot symbol orientation call bundling body, callers, and containing doc section, and `index_status`, which reports whether a project's index has ever been populated, current file/symbol counts, dirty-queue depth, worker liveness, and embeddings availability — call it after an unexpectedly empty result from another tool, since an MCP-only client has no hook layer to warn it apart from a genuine "not found") plus `compress_text`, `handoff_create`, and `handoff_resolve`. To switch on the others, set `TOKEN_GOAT_MCP_TOOLS` to the names you want, separated by commas (for example `symbol,read,section,outline,semantic,refs,retrieve_text,grep`); a list you set replaces the default, so name the seven too, and name all eighteen to get every tool. These local-only tools use bounded, redacted storage; MCP never intercepts a client's built-in file reads. Every tool takes an optional `projectRoot`. When a call leaves it out, the server infers the project: from an absolute path that exists, then from indexed file paths ending with the path you gave, then, for a bare name, from the project that defines that symbol. It only infers a project token-goat has indexed that sits inside the server's allowed roots; otherwise it keeps its own working directory, so with `mcp.confine_reads_to_project_root` on, a path outside it is still refused. Passing `projectRoot` always wins.
359
387
 
360
388
  ### Generic compression and handoffs
361
389
 
@@ -369,7 +397,7 @@ token-goat handoff-resolve review-notes --full
369
397
 
370
398
  `token-goat compress-text` returns a stable opaque ID, size metadata, an estimated token delta, and a recovery command. The deflate/base64url payload tokenizes far worse per byte than plain text, so for most real inputs inlining it costs more tokens than the original: it is printed only when it genuinely wins, and `--payload` forces it for the self-contained case where you need a blob that decodes on another machine. A negative `tokens_saved` is a cost, not a saving. `token-goat retrieve` restores the locally cached text. Handoffs are created with `token-goat handoff-create` and resolved with `token-goat handoff-resolve`; they are named and project-local, and resolve compactly by default or in full with `--full`. Content is limited to 512 KiB and stored in a bounded local cache with secret redaction. `token-goat stats` includes these outcomes alongside existing savings.
371
399
 
372
- `token-goat retrieve <id>` takes the same output filters as `bash-output`/`web-output`/`mcp-output`: `--head <n>`, `--tail <n>`, `--grep <pattern>`, `--max-matches <n>`, `--section <heading>`, and `--full`. With no filter at all, `retrieve` prints the stored text byte-verbatim -- this is its lossless round-trip contract and it never applies the siblings' default head/tail elision. Pass any filter and it switches to sibling semantics (including elision) for that call, so a large stored blob can be recalled a slice at a time instead of taking all of it.
400
+ `token-goat retrieve <id>` takes the same output filters as `bash-output`/`web-output`/`mcp-output`: `--head <n>`, `--tail <n>`, `--grep <pattern>`, `--max-matches <n>`, `--section <heading>`, `--lines <a-b>`, `--context <n>`, `-n` and `--full`. With no filter at all, `retrieve` prints the stored text byte-verbatim -- this is its lossless round-trip contract and it never applies the siblings' default head/tail elision. Pass any filter other than `-n`, which only numbers the lines, and it switches to sibling semantics (including elision) for that call, so a large stored blob can be recalled a slice at a time instead of taking all of it.
373
401
 
374
402
  **VS Code** — add it to `.vscode/mcp.json` under the `"servers"` key (this is the correct root key for VS Code's MCP config; it is not `"mcpServers"`):
375
403
 
@@ -385,7 +413,7 @@ token-goat handoff-resolve review-notes --full
385
413
  }
386
414
  ```
387
415
 
388
- `token-goat install --vscode` creates or idempotently updates VS Code's user-profile `mcp.json` by default (`%APPDATA%\Code\User\mcp.json` on Windows, `~/Library/Application Support/Code/User/mcp.json` on macOS, `~/.config/Code/User/mcp.json` on Linux) — add `-p`/`--project` for the project-local `.vscode/mcp.json` shown above instead. It also adds a delimited routing block, preserving unrelated JSON and user text: to `~/.copilot/instructions/token-goat.instructions.md` for the user install (a personal instructions file VS Code applies in every workspace, so the folder you run it from is left alone), or to `.github/copilot-instructions.md` with `-p`. It fails clearly on malformed JSON, and refuses to install into one scope if the other scope already has a token-goat-managed entry (registering it twice would duplicate its tool schemas in that workspace). It also installs agent hooks in `~/.copilot/hooks/` (or `.github/hooks/` with `-p`), the folder VS Code's Copilot agent reads hooks from, so token-goat sees the agent's built-in reads, edits, and terminal commands. `token-goat uninstall --vscode` (add `-p`/`--project` for the project scope) removes only token-goat's server entry, guidance block, and hooks, and keeps the hooks if `--copilot` still uses them.
416
+ `token-goat install --vscode` creates or idempotently updates VS Code's user-profile `mcp.json` by default (`%APPDATA%\Code\User\mcp.json` on Windows, `~/Library/Application Support/Code/User/mcp.json` on macOS, `~/.config/Code/User/mcp.json` on Linux, or under `$XDG_CONFIG_HOME` when that is set to an absolute path) — add `-p`/`--project` for the project-local `.vscode/mcp.json` shown above instead. It also adds a delimited routing block, preserving unrelated JSON and user text: to `~/.copilot/instructions/token-goat.instructions.md` for the user install (a personal instructions file VS Code applies in every workspace, so the folder you run it from is left alone), or to `.github/copilot-instructions.md` with `-p`. It fails clearly on malformed JSON, and refuses to install into one scope if the other scope already has a token-goat-managed entry (registering it twice would duplicate its tool schemas in that workspace). It also installs agent hooks in `~/.copilot/hooks/` (or `.github/hooks/` with `-p`), the folder VS Code's Copilot agent reads hooks from, so token-goat sees the agent's built-in reads, edits, and terminal commands. `token-goat uninstall --vscode` (add `-p`/`--project` for the project scope) removes only token-goat's server entry, guidance block, and hooks, and keeps the hooks if `--copilot` still uses them.
389
417
 
390
418
  **Visual Studio** (2022 17.14 or later, or 2026): `token-goat install --visualstudio` adds the same `servers` entry to `%USERPROFILE%\.mcp.json` and a routing block to `%USERPROFILE%\copilot-instructions.md` (Visual Studio 2026 reads that file). With `-p`/`--project` it writes `.mcp.json` and `.github/copilot-instructions.md` in the solution folder instead. Visual Studio has no agent hooks, so token-goat works there through its MCP tools and instructions only: no read dedup, hints, image shrink, or output folding. Two switches in Visual Studio turn it on: the Tools > Options checkbox for custom instructions, and the token-goat tools in the chat Tools picker, since new MCP tools start disabled. `token-goat uninstall --visualstudio` (add `-p` for the project) removes only token-goat's entry and block. See [Visual Studio users](docs/install.md#visual-studio-users).
391
419
 
@@ -411,24 +439,13 @@ Installing the extension is an alternative to `install --vscode`, not an additio
411
439
 
412
440
  If the extension is running somewhere that contribution did not take effect, it falls back to calling `token-goat mcp-status --vscode` (add `-p`/`--project` for the workspace scope too) to check whether `mcp.json` already configures the decoder, and offers to run `install --vscode` if not — the same path resolver `install`/`uninstall` write against, so the two can never drift on where `mcp.json` lives or what key name it looks for.
413
441
 
414
- **Copilot CLI** — add it to `~/.copilot/mcp-config.json`:
415
-
416
- ```json
417
- {
418
- "mcpServers": {
419
- "token-goat": {
420
- "command": "token-goat",
421
- "args": ["mcp-serve"]
422
- }
423
- }
424
- }
425
- ```
442
+ **Copilot CLI**: `token-goat install --copilot` registers the server in `~/.copilot/mcp-config.json` (or `$COPILOT_HOME/mcp-config.json`), in the same shape `copilot mcp add` writes, and `copilot mcp list` shows it. `token-goat uninstall --copilot` removes it and leaves your other servers alone. A `token-goat` entry you wrote by hand from an older version of this README is replaced on install.
426
443
 
427
444
  **Caveat.** Registering the server does not force any harness to prefer it. Unlike the hook-based bridges elsewhere in this project — which intercept a `Read`/`Grep`/`Glob` call before it reaches the model and can redirect or deny it outright — an MCP tool is just one more option in the harness's own tool-selection decision. Copilot (or any other MCP-aware client) decides for itself whether to call token-goat's `read` tool or fall back to its own built-in file-read tool; there is no interception mechanism for MCP the way there is for hooks. In VS Code the agent hooks from `install --vscode` do see the built-in reads: they can deny a repeated read, add a hint, and shrink an image before `view_image` loads it. VS Code gives hooks no way to change a tool's result, though, so there is no folding of what a read returns and no compression of a tool's output after it runs. Terminal output is not compressed in VS Code either: elsewhere token-goat does that by rewriting the command before it runs, but VS Code does not tell the hook which shell will run the command, so rewriting it safely is not possible.
428
445
 
429
446
  ## What gets installed?
430
447
 
431
- `token-goat install` writes hook entries and a delimited guidance block into your agent's config directory, plus an index, cache and models under a data directory. Nothing else, anywhere. Every entry is reversed by `token-goat uninstall`, integrations added with `--codex`, `--copilot` or a sibling flag are named rather than silently removed, and `token-goat doctor` lists what is present right now.
448
+ `token-goat install` writes hook entries and a delimited guidance block into your agent's config directory, plus an index, cache and models under a data directory. Nothing else, anywhere. Every entry is reversed by `token-goat uninstall`, integrations added with `--codex`, `--copilot` or a sibling flag are named rather than silently removed, and `token-goat doctor` lists what is present right now. When `CODEX_HOME` is set, `install --codex`, `uninstall` and `doctor --repair` use that directory for Codex's config, `AGENTS.md` and hooks instead of `~/.codex`.
432
449
 
433
450
  **What the index actually holds, in plain terms.** The point of a surgical read is returning a function body without the file around it, which means the database stores those bodies. `symbols.body` holds the source text of every indexed symbol, `symbols.docstring` its doc comment, `refs.context` the line around each reference, and `chunks.text` the passages that semantic search embeds. There is also a full-text index over the bodies and docstrings. So the database is not a list of names and line numbers: it is a substantial copy of your source, sitting in a plain unencrypted SQLite file outside the repository.
434
451
 
@@ -442,7 +459,7 @@ Hooks fire automatically on every tool call once installed — nothing to start
442
459
 
443
460
  Hooks also answer faster after the first call. That call starts a small resident process, the hook server, and later hook calls go to it instead of starting Node and loading token-goat each time. In measurements on Windows that took a hook call from 110 to 135 ms down to about 70 ms in Claude Code, Codex, Copilot CLI and VS Code chat, and a read command such as `token-goat section` from 122 ms to 60 ms when its output goes to the agent rather than a terminal. You never start it yourself. It stops after 30 minutes idle, after an upgrade, when you turn it off, and when `uninstall --purge` runs. `token-goat hook-server status` shows what is running and `token-goat hook-server stop` stops it. To turn it off, set `server = false` under `[hooks]` in your global config, or `TOKEN_GOAT_HOOK_SERVER=0`. A project's `.token-goat.toml` cannot change this setting, because one server answers every project.
444
461
 
445
- To move to a newer release, `token-goat upgrade` installs it from npm and then re-runs `token-goat install`, so your hooks and integration manifests point at the build that just landed. `token-goat upgrade --check` reports whether one is available without installing anything, and `--json` gives the same answer for a script. Both go quiet when `network.offline` is set, and `token-goat doctor` mentions an available update at the end of its report.
462
+ To move to a newer release, `token-goat upgrade` installs it from npm and then re-runs `token-goat install`, so your hooks and integration manifests point at the build that just landed. `token-goat upgrade --check` reports whether one is available without installing anything, and `--json` gives the same answer for a script. When it cannot learn the latest version (`network.offline` is set, or the registry did not answer), it exits 1, with the reason on stderr or, under `--json`, in the `error` field. The check asks `npm view` first, so a registry set up in npm (Artifactory, Nexus), its auth token and its CA certificate are used; it falls back to a direct request to the registry in `npm_config_registry`, or to npmjs.org. A successful result is cached for 24 hours, though `upgrade --check` itself always asks again. When a newer version is out, `token-goat doctor` mentions it at the end of its report, `token-goat stats` lists it in its insights, and the context added at session start says so. None of this happens when `network.offline` is set, and `token-goat doctor --repair` installs the update as its last step.
446
463
 
447
464
  ## Verify
448
465
 
@@ -455,25 +472,17 @@ token-goat stats
455
472
 
456
473
  ### Confirming hooks are wired
457
474
 
458
- `doctor` checks the binary, worker, database, and disk, and whether the installed Claude Code, Codex and Copilot hook shims match the running build. It does not inspect `settings.json` hook wiring. To confirm all three hooks are present, re-run `install`:
475
+ `doctor` checks the binary, worker, database, and disk, whether the installed Claude Code, Codex and Copilot hook shims match the running build, and the hook wiring itself. Its `Claude Code hook events` line reads `every event this build handles is wired` when `settings.json` has a current token-goat entry for each event; otherwise it names, per scope, the events that are missing, still run a command this build no longer writes, or point at a native hook client binary that is gone. The `Native hooks` lines report, per harness, which form of hook command is wired and whether the native client binary is present and passes its self-test. To fix any of them, re-run `install`:
459
476
 
460
477
  ```
461
478
  token-goat install
462
479
  ```
463
480
 
464
- `install` is idempotent: it adds any missing entries and leaves existing hook entries alone. Re-running it is always safe. To inspect the wiring directly:
481
+ `install` is idempotent: it adds missing entries, rewrites outdated token-goat entries in place, and leaves everything else in `settings.json` alone. Re-running it is always safe. Restart any running session afterwards so it picks up the new entries.
465
482
 
466
- ```bash
467
- # Linux / macOS / WSL
468
- jq '[.hooks | to_entries[] | select(.value[].hooks[]?.command | contains("token-goat hook")) | .key]' ~/.claude/settings.json
469
-
470
- # Windows (PowerShell with jq on your PATH)
471
- jq '[.hooks | to_entries[] | select(.value[].hooks[]?.command | contains("token-goat hook")) | .key]' "$env:USERPROFILE\.claude\settings.json"
472
- ```
483
+ `token-goat doctor --probe HOST` runs a host headless and reports whether its hooks reached the model. It exits 1 when a hook is at fault, 2 when the harness failed before its model replied (not logged in, access denied), and 3 for `vscode`, `visualstudio` and `jetbrains`, which run inside an editor and have no headless mode.
473
484
 
474
- A working install returns `["PreToolUse", "PostToolUse", "PreCompact"]`. Any missing event means a partial install; run `token-goat install` to fill it in.
475
-
476
- **AI agents:** after `token-goat install`, run the `jq` check above or re-run `token-goat install` before marking the install complete. `doctor` does not verify hook wiring.
485
+ **AI agents:** after `token-goat install`, run `token-goat doctor` and check the `Claude Code hook events` line before marking the install complete.
477
486
 
478
487
  ## Image support
479
488
 
@@ -489,6 +498,8 @@ The pipeline is written in TypeScript and decodes PNG, JPEG, GIF, and BMP itself
489
498
 
490
499
  The one thing the decoders will do is decline. A file whose header asks for more than 256 MB of pixel buffer, or that is in a variant token-goat cannot read exactly, is refused rather than guessed at, and the untouched original goes to the model. An untouched original is the right picture, so the only cost of a refusal is that the read is not shrunk.
491
500
 
501
+ On Claude Code the shrunk copy reaches the model by pointing the Read at a temporary file holding it. The hook approves that rewritten read, and Claude Code asks before reading a file outside the session's working directory, so token-goat shrinks only images inside it; one outside is read unshrunk, after Claude Code's own prompt.
502
+
492
503
  When `tesseract.js` is installed, text-heavy images can also have their text extracted via OCR (`token-goat image-text <file>`) to replace raw pixel data with plain text. English (`eng`) is always enabled by default, and users can combine one or more additional languages (`token-goat image-text <file> --lang "fra,spa"`). Language preferences persist across version updates when saved globally (`token-goat config set image_shrink.ocr_lang "fra,spa"`), per-project in `.token-goat.toml` (`[image_shrink] ocr_lang = "fra+spa"`), or via `TOKEN_GOAT_OCR_LANG`. 16 languages are supported (`eng`, `fra`, `spa`, `deu`, `ita`, `por`, `nld`, `pol`, `rus`, `tur`, `swe`, `ara`, `chi_sim`, `chi_tra`, `jpn`, `kor`), each cryptographically verified against pinned package checksums to protect against supply-chain tampering.
493
504
 
494
505
  ## Stats display
package/SECURITY.md CHANGED
@@ -39,37 +39,47 @@ The following are not treated as security issues unless paired with a working pr
39
39
  - Social-engineering attacks that require tricking the user into running malicious commands
40
40
  - Issues that require an already-compromised local user account
41
41
 
42
+ ## Check commands in the session record
43
+
44
+ When a test, lint, typecheck or build command runs, the session record keeps the runner, its subcommand, a short list of known switches and any path-shaped word, and replaces every other word with an ellipsis. A secret passed as a bare positional argument that looks like a path, such as `hunter2.txt`, would be kept, because a file name is what identifies the check; a secret that follows a flag, uses a `user:pass@host` form or is shaped like a JWT is dropped.
45
+
46
+ ## Remembered failure fixes
47
+
48
+ The failed commands token-goat learns fixes for are kept only as keyed digests, never as text, and the key sits in the same folder as the saved fixes, so a leak of that whole folder exposes both.
49
+
42
50
  ## Dependency advisories
43
51
 
44
- `npm audit` reports Token-Goat clean for anyone who installs it, and that has only been true of a default install since the embedding model became opt-in. This repository's own tree, development dependencies included, is not: two moderate advisories currently sit in dev-only transitive dependencies (`adm-zip` under `onnxruntime-node`, `hono` under `@modelcontextprotocol/sdk`), both behind Dependabot's cooldown rather than bumped ad hoc -- see [`.github/dependabot.yml`](.github/dependabot.yml) for why a plain `npm update` isn't the fix. Neither ships to an installer: both packages drop out the moment `--omit=dev` is added, which is what the next two rows scan. All three numbers below are reproducible with the commands shown. The one thing that is not clean in a default install is the opt-in package itself, and it is described further down rather than folded into these rows.
52
+ `npm audit` reports Token-Goat clean for anyone who installs it, and that has been true of a default install since the embedding library that carried every finding was removed. This repository's own tree is clean as well, development dependencies included: `adm-zip`, `hono`, `proxy-addr` and `source-map-js` were resolved to patched versions through the lock-file refresh, not bumped by hand the day a fix was published -- see [`.github/dependabot.yml`](.github/dependabot.yml) for why a plain `npm update` isn't the fix. All three numbers below are reproducible with the commands shown. The one place an advisory can still reach is the optional native runtime, which a default install does not carry, and it is described further down rather than folded into these rows.
45
53
 
46
54
  | What you scan | Command | Result |
47
55
  | --- | --- | --- |
48
- | this repository | `npm audit` | 2 moderate, development dependencies only (`adm-zip`, `hono`; see above) |
56
+ | this repository | `npm audit` | clean |
49
57
  | an install without optional packages | `npm install --omit=optional token-goat` then `npm audit --omit=dev --omit=optional` | clean, 2 packages |
50
58
  | a default install | `npm install token-goat` then `npm audit --omit=dev` | clean, 62 packages |
51
59
 
52
- The repository carries an `overrides` block in [`package.json`](package.json) that pins six packages to patched versions, and **npm applies `overrides` only in the root project**, so those pins do not travel to anyone who installs Token-Goat as a dependency. That distinction used to matter a great deal, because a clean repository scan was standing in for an install that was not clean. It decides one thing now, and the section on the embedding runtime below says which. Otherwise the packages those pins were protecting against are not in a consumer's tree at all, whether or not the pins travel; the block stays because the development tree still resolves them.
60
+ The repository carries an `overrides` block in [`package.json`](package.json) that pins nine packages to patched versions, and **npm applies `overrides` only in the root project**, so those pins do not travel to anyone who installs Token-Goat as a dependency. That distinction used to matter a great deal, because a clean repository scan was standing in for an install that was not clean. It decides one thing now, and the section on the embedding runtime below says which. Otherwise the packages those pins were protecting against are not in a consumer's tree at all, whether or not the pins travel; the block stays because the development tree still resolves them.
53
61
 
54
62
  There is no table of reaching advisories here any more, and the last entry in it is worth recording rather than deleting. `@xenova/transformers` was an optional dependency, which in npm means everyone got it unless they asked not to, and it carried [`protobufjs`](https://github.com/advisories/GHSA-xq3m-2v4x-88gg) through `onnx-proto` and `onnxruntime-web` -- one critical advisory and four more -- plus its own nested, older `sharp` carrying [four inherited libvips CVEs](https://github.com/advisories/GHSA-f88m-g3jw-g9cj). None of the six was fixable from here: every one sits in a transitive package with no patched version, and the pins that would have solved it do not travel to consumers.
55
63
 
56
- That package is gone entirely now. It had shipped no release since May 2024, so none of those six was going to be fixed upstream either, and what it did for Token-Goat was three separable things: tokenize the text, run an ONNX graph, average the result. All three are in [`src/embed_tokenizer.ts`](src/embed_tokenizer.ts) and [`src/embed_model.ts`](src/embed_model.ts) now, over [`onnxruntime-node`](https://www.npmjs.com/package/onnxruntime-node) -- ONNX Runtime's own Node binding, from the same project that produces the format the model is published in. Measured standalone into an empty project, that is 17 packages where `@xenova/transformers` was 80.
64
+ That package is gone entirely now. It had shipped no release since May 2024, so none of those six was going to be fixed upstream either, and what it did for Token-Goat was three separable things: tokenize the text, run an ONNX graph, average the result. All three are in [`src/embed_tokenizer.ts`](src/embed_tokenizer.ts) and [`src/embed_model.ts`](src/embed_model.ts) now, over ONNX Runtime itself, from the same project that produces the format the model is published in.
57
65
 
58
- The embedding runtime is opt-in, the same way the model was. `semantic` still works without it, because that command always consults keyword search alongside the vectors, so nothing errors and nothing comes back empty; what goes away is the embedding half, and with it the ability to match on meaning rather than words. To get it back:
66
+ A default install runs the model on the WebAssembly build of ONNX Runtime, [`onnxruntime-web`](https://www.npmjs.com/package/onnxruntime-web) 1.30.0, and that adds no package to it. The runtime's JavaScript is bundled into Token-Goat at build time; the one loader module that ships beside the bundle as a separate file is checked against a recorded SHA-256 and length before it is imported. Its 14 MB binary, `ort-wasm-simd-threaded.wasm`, is downloaded once, on first use. [`src/embed_runtime.ts`](src/embed_runtime.ts) fetches that release's tarball from `registry.npmjs.org` at a URL built entirely from constants, holds it to a recorded SHA-256 and byte length while it streams, takes the one file it needs out of it with its own tar reader, checks that file against a digest of its own, and deletes the tarball. Nothing is installed and no install script runs. The kept copy is re-verified on every load; `network.offline` refuses the download but still accepts a copy placed by hand, because it is checked the same way. If the runtime cannot start at all, `semantic` still works, because it always consults keyword search alongside the vectors, so nothing errors and nothing comes back empty; what goes away is matching on meaning rather than words.
67
+
68
+ [`onnxruntime-node`](https://www.npmjs.com/package/onnxruntime-node), ONNX Runtime's native Node binding, is used instead whenever it is installed. It is optional because it is large, about 288 MB installed with every platform's binaries, and because it is the one package on this page that runs an install script of its own:
59
69
 
60
70
  ```bash
61
71
  npm install -g onnxruntime-node # drop -g if token-goat is a project dependency
62
72
  ```
63
73
 
64
- **That command is the one thing on this page that is not clean, and the number is two.** `onnxruntime-node` depends on [`adm-zip`](https://www.npmjs.com/package/adm-zip) below 0.6.0, which carries [GHSA-xcpc-8h2w-3j85](https://github.com/advisories/GHSA-xcpc-8h2w-3j85) -- a crafted ZIP file provoking a 4 GB allocation -- and npm reports it twice, once against `adm-zip` and once against `onnxruntime-node` for depending on it. Both are the same finding. It is reachable only from `onnxruntime-node`'s own postinstall script, which uses it to unpack the prebuilt binary it has just downloaded from Microsoft; nothing in Token-Goat passes it an archive, and no file a user supplies reaches it. This repository pins `adm-zip` past it in `overrides`, which used to be why the repository row above was clean. It no longer is: a second, unrelated `adm-zip` advisory ([GHSA-vwc7-r8mq-g2x9](https://github.com/advisories/GHSA-vwc7-r8mq-g2x9), extraction following a destination symlink) reaches into the patched version itself, so the `^0.6.0` override no longer clears everything `adm-zip` carries -- that is the `adm-zip` finding the repository row above now discloses, alongside `hono`'s. By the rule stated above the override does not travel regardless: run the command and you will resolve the 0.5 line and see the original ZIP-bomb finding twice. If that matters where you are, the fix is an `overrides` entry of your own:
74
+ **That command installs clean from `onnxruntime-node` 1.30.0 onward, and runs clean in this repository.** Earlier releases depend on [`adm-zip`](https://www.npmjs.com/package/adm-zip) below 0.6.0, which carries [GHSA-xcpc-8h2w-3j85](https://github.com/advisories/GHSA-xcpc-8h2w-3j85) -- a crafted ZIP file provoking a 4 GB allocation -- and npm reports it twice, once against `adm-zip` and once against `onnxruntime-node` for depending on it. Both are the same finding. It is reachable only from `onnxruntime-node`'s own postinstall script, which uses it to unpack the prebuilt binary it has just downloaded from Microsoft; nothing in Token-Goat passes it an archive, and no file a user supplies reaches it. `adm-zip` 0.6.0 fixed that one and carries a second, unrelated advisory, [GHSA-vwc7-r8mq-g2x9](https://github.com/advisories/GHSA-vwc7-r8mq-g2x9) (extraction following a destination symlink), which 0.6.1 clears. `onnxruntime-node` 1.30.0 asks for `^0.6.0`, so a fresh install of it resolves 0.6.1 and `npm audit` reports nothing, measured on 1.30.0. This repository pins `adm-zip` to that version in `overrides` as well. If you are held on an older `onnxruntime-node`, the fix is an `overrides` entry of your own:
65
75
 
66
76
  ```json
67
- { "overrides": { "adm-zip": "^0.6.0" } }
77
+ { "overrides": { "adm-zip": "^0.6.1" } }
68
78
  ```
69
79
 
70
- in the package.json of the project you install into, then reinstall. Measured, because the obvious alternative does not work: installing `adm-zip@^0.6.0` alongside `onnxruntime-node` changes nothing, since the version it asks for is `^0.5.16` and npm nests a second copy rather than sharing yours. An override is what actually replaces it, and the prebuilt binary still extracts afterwards -- that pairing is what this repository builds and tests on. A global `npm install -g onnxruntime-node` has no project manifest to put an override in, so there it stands.
80
+ in the package.json of the project you install into, then reinstall. Measured, because the obvious alternative does not work: installing `adm-zip@^0.6.1` alongside an older `onnxruntime-node` changes nothing, since the version those releases ask for is `^0.5.16` and npm nests a second copy rather than sharing yours. An override is what actually replaces it, and the prebuilt binary still extracts afterwards. A global `npm install -g onnxruntime-node` has no project manifest to put an override in, so there, install 1.30.0 or later.
71
81
 
72
- Nothing else is needed. The index notices on its own: files skipped while the model was absent are recorded as skipped for that reason specifically, so the next index pass re-embeds them rather than treating them as already done. `token-goat doctor` reports which of the three states you are in -- available, switched off in config, or not installed with the command above -- because a fallback that works silently is exactly the kind of thing nobody discovers on their own.
82
+ Nothing else is needed. The index notices on its own: files skipped while the runtime could not run are recorded as skipped for that reason specifically, so the next index pass re-embeds them rather than treating them as already done. Vectors are stamped with the runtime that computed them, so moving between the two builds re-embeds rather than mixing them. `token-goat doctor` and `token-goat semantic --preflight` report which runtime is active, whether the WebAssembly binary has been downloaded yet, or that embeddings are switched off in config, because a fallback that works silently is exactly the kind of thing nobody discovers on their own.
73
83
 
74
84
  The model weights are no longer a package at all. They are downloaded once, on first use, from a URL whose every component is a constant in [`src/embed_model.ts`](src/embed_model.ts) -- repository, revision and filename alike, with nothing caller-supplied anywhere in it. The revision is an immutable commit rather than a branch, and each file is checked against a recorded SHA-256 and an exact byte length, on download and again on every load, with the length enforced while the body is still streaming so an overrunning response is cut off rather than written out. The digest is what is trusted, not the hostname: `huggingface.co` accepts uploads from anyone, which is the shape of [CVE-2026-54316](https://github.com/advisories/GHSA-fg94-h982-f3mm), so allowlisting the host would decide nothing. Because the digest decides, following the redirect that `resolve` issues to Hugging Face's CDN is safe. With `network.offline` set, the download is refused and the path says so rather than degrading quietly; files copied into the cache directory by hand are still accepted, because they are checked the same way.
75
85
 
@@ -87,7 +97,7 @@ The old model had cost something before any of this, through the nested `sharp`
87
97
 
88
98
  `better-sqlite3` stays as a development dependency, and like the MCP SDK that is load-bearing. Three parts of the driver are reimplementations rather than passthroughs, and a test written only against the driver would pin whatever the driver does, bugs included. So [`tests/sqlite_driver.test.ts`](tests/sqlite_driver.test.ts) runs both libraries over the same input and requires the answers to match -- most carefully for the `reader` flag, which is the third defence-in-depth layer in the `sqlite-query` read-only guard and the one thing the driver derives rather than reads. The two libraries differ in exactly one place, deliberately: asked for an integer above 2^53 without big-integer reads switched on, `better-sqlite3` returns the nearest double and `node:sqlite` refuses. Silently rounding is the behaviour `sqlite-query` already had to defend against, so the refusal is kept, and that test pins the difference rather than papering over it. That takes a default install from 106 packages to 70, and an install without optional packages from 40 to 2.
89
99
 
90
- Every package count on this page is a measurement, not a constant, and it was taken the same way each time: `npm install <the package>` into an empty project, then counting the directories under `node_modules`, the package itself included. They are counts as of 2026-09-11 and they drift upward on their own, because the version ranges a dependency declares resolve to whatever is newest at install time and other people's trees grow. This paragraph said 238 and 87 for a while for exactly that reason: both were true when they were written and neither was true a few releases later. Re-measure before quoting them rather than assuming they still hold. The default figure additionally depends on where you stand: `@napi-rs/canvas` and `sqlite-vec` each publish a prebuilt binary per platform, an install takes only the one that matches, and the rest are skipped, so a default install is a package larger on Linux than on Windows or macOS. The 62 above is the Linux x64 figure, which is the largest of the three; Windows and macOS are 61. The no-optional install has no prebuilt binary in it at all, so 2 is 2 everywhere.
100
+ Every package count on this page is a measurement, not a constant, and it was taken the same way each time: `npm install <the package>` into an empty project, then counting the directories under `node_modules`, the package itself included. They are counts as of 2026-09-11 and they drift upward on their own, because the version ranges a dependency declares resolve to whatever is newest at install time and other people's trees grow. This paragraph said 238 and 87 for a while for exactly that reason: both were true when they were written and neither was true a few releases later. Re-measure before quoting them rather than assuming they still hold. The default figure additionally depends on where you stand: `@napi-rs/canvas` and `sqlite-vec` each publish a prebuilt binary per platform, an install takes only the one that matches, and the rest are skipped, so a default install is a package larger on Linux than on Windows or macOS. The 62 above is the Linux x64 figure, which is the largest of the three; Windows and macOS are 61. The no-optional install pulls no prebuilt binary from any dependency, so 2 is 2 everywhere. The one native program in either install is token-goat's own: the package ships four `tg-hook` binaries under `dist/native/` (Windows and Linux, x64 and arm64), described under [What actually runs](#what-actually-runs). With the single Windows x64 binary present the unpacked package is 6,204,172 bytes; a release carrying all four is about 8.0 MB unpacked, an estimate from the individual binaries (roughly 480 KB for each signed Windows executable, 709 KB for Linux x64 and 619 KB for Linux arm64), not a measurement of a published tarball.
91
101
 
92
102
  One half of that drift is checked rather than trusted. [`tests/guards/dependency_advisory_disclosure.test.ts`](tests/guards/dependency_advisory_disclosure.test.ts) resolves both installs out of [`package-lock.json`](package-lock.json) the way npm resolves them, platform gating included, and holds this page to the answer: the no-optional figure has to match exactly, and the default figure may not be smaller than the lock file already proves it must be. That catches every package this project adds or removes on its own. It cannot catch the other half, which is other people's trees growing inside ranges an install already accepts, and that is what the date is for.
93
103
 
@@ -109,7 +119,9 @@ Which names may appear in that tree is pinned in [`tests/guards/runtime_dependen
109
119
 
110
120
  The lockfile is not the only thing that decides what loads, so it is not the only thing checked. [`tests/guards/bundle_runtime_imports_are_reviewed.test.ts`](tests/guards/bundle_runtime_imports_are_reviewed.test.ts) reads the built artifact instead and requires every package name it can resolve at run time to have been reviewed — including the ones the lockfile cannot show you, such as a module the host agent supplies out of its own process. Installing this package runs nothing: it declares no `preinstall`, `install`, or `postinstall` script, which [`tests/guards/lifecycle_script_is_published.test.ts`](tests/guards/lifecycle_script_is_published.test.ts) keeps at zero rather than leaving to habit. `npm audit signatures` runs in CI and before every commit that moves the lockfile, so a tarball altered after publication fails rather than installs.
111
121
 
112
- Releases are built and published by two separate jobs ([`.github/workflows/publish.yml`](.github/workflows/publish.yml)). Everything that executes dependency code — `npm ci`, the suite, the build — runs in a job that holds no registry credential, and the only thing crossing into the publishing job is the built bundle. That job installs nothing and passes `--ignore-scripts`, so no lifecycle hook can run beside the upload; [`tests/guards/publish_job_runs_no_dependency_code.test.ts`](tests/guards/publish_job_runs_no_dependency_code.test.ts) holds the split. This does not make a release untamperable, and it is not claimed to: the build itself is a bundler running dependency code, so the artifact is the residual risk. It is a smaller one than a writable checkout sharing a job with a publish token.
122
+ The package also ships one native program of its own: `tg-hook`, the hook client that `install` puts in front of each hook command on Windows and Linux ([Native hook client](docs/install.md#what-gets-installed)). It is a small Rust program in [`native/tg-hook/`](native/tg-hook/), built in the release workflow from the checked-in source with the committed `Cargo.lock`, and it does one thing: relay a hook call to token-goat's resident hook server over a named pipe or Unix socket, and run the Node hook command it wraps when the server cannot answer. It opens no network connection, the only file it reads is the hook server's key file in token-goat's data directory, and it runs only when a harness fires a hook. A user who would rather not run it sets `native = "off"` under `[hooks]` in the global config and runs `install` again; every hook is then the Node command alone, as on macOS, where no binary ships.
123
+
124
+ Releases are built and published by separate jobs ([`.github/workflows/publish.yml`](.github/workflows/publish.yml)). Everything that executes dependency code (`npm ci`, the suite, the build) runs in jobs that hold no registry credential. What crosses into the publishing job is the built bundle and the four native binaries, each from the build or signing job that produced it, and the publishing job verifies them before it publishes ([`scripts/verify-native-dist.mjs`](scripts/verify-native-dist.mjs)): exactly the release binaries and nothing else, each matching the hash its producing job recorded, each Windows file ending in a certificate table whose Authenticode signature names exactly one signer and whose image digest matches the file's own bytes, and each Linux file a static ELF executable for the right machine with code to run that names no shared library. The Windows check binds the signature to the file, so a signature lifted from another binary or bytes appended after it are refused. The check is structural: it requires the parts the image digest does not cover to be whole DER elements but does not look inside certificates or unsigned attributes, and it does not validate the certificate chain, which is what the signing job and `Get-AuthenticodeSignature` below are for. Anything else stops the release. A release built with no Windows signing configuration ships the two Linux binaries and no Windows ones, and the check then refuses any Windows file it finds; Windows installs fall back to the Node hook command. A signing configuration that names an unknown store type or lacks a secret stops the release rather than dropping Windows. That job installs nothing and passes `--ignore-scripts`, so no lifecycle hook can run beside the upload; [`tests/guards/publish_job_runs_no_dependency_code.test.ts`](tests/guards/publish_job_runs_no_dependency_code.test.ts) holds the split. This does not make a release untamperable, and it is not claimed to: the build itself is a bundler running dependency code, so the artifact is the residual risk. It is a smaller one than a writable checkout sharing a job with a publish token.
113
125
 
114
126
  None of this inspects what a dependency does once it runs. It bounds how much third-party code can run at all, and makes any change to that set something a person has to agree to.
115
127
 
@@ -125,6 +137,20 @@ npm audit signatures
125
137
 
126
138
  Run from a project that depends on token-goat, that command verifies the registry signature and the provenance attestation for every installed package, token-goat included. The package page on npm links the attestation to the exact commit, so you can read the source that produced the bytes you are running.
127
139
 
140
+ The native `tg-hook` binaries can be checked on their own. A release ships Windows binaries only when it signs them: both are then Authenticode-signed with a certificate held in a cloud HSM and timestamped, so the signature stays valid after the certificate expires. A release without them leaves Windows on the Node hook command, and `token-goat doctor` says the build ships no native hook client for the platform. In PowerShell:
141
+
142
+ ```
143
+ Get-AuthenticodeSignature "<path to tg-hook.exe>"
144
+ ```
145
+
146
+ `Status` reads `Valid` for a release binary. The path is the one `token-goat doctor` prints on the `Native hooks` line: on Windows that is the copy under `native\<id>\` in the data directory, and the packaged original sits at `dist\native\win32-x64\tg-hook.exe` (or `win32-arm64`) inside the installed package. Both Linux binaries carry build provenance attestations published to this repository, which tie the file to the workflow run and commit that built it:
147
+
148
+ ```
149
+ gh attestation verify "<path to tg-hook>" --repo DFKHelper/token-goat
150
+ ```
151
+
152
+ A binary that was not produced by the release workflow fails that check. The publishing job runs the same kind of check before it publishes, so a release cannot carry an unsigned Windows binary or a Linux binary the build job did not record. An `npm pack` or `npm publish` run from a working tree checks `dist/native` first as well (the `prepack` script, which npm runs for both, so a tarball packed by hand and published later is checked too), and refuses an unsigned Windows binary, such as the one a local build leaves there, or a dynamically linked Linux one. A user who does not want the native binary at all sets `native = "off"` under `[hooks]` in the global config and re-runs `install`.
153
+
128
154
  Every action used by that workflow, and by CI, is pinned to a full commit SHA rather than a tag, so a compromised action repository cannot silently change what runs. That is enforced by a test rather than by review: see [tests/guards/workflow_actions_pinned.test.ts](tests/guards/workflow_actions_pinned.test.ts).
129
155
 
130
156
  ## License
@@ -2,7 +2,7 @@
2
2
 
3
3
  Token-Goat itself is licensed separately: see [LICENSE](LICENSE). This file covers only other people's code.
4
4
 
5
- `dist/` is a bundle. Building it copies the source of the 23 packages below into the
5
+ `dist/` is a bundle. Building it copies the source of the 26 packages below into the
6
6
  shipped files, so their copyright and permission notices travel with this package and are
7
7
  reproduced here in full.
8
8
 
@@ -71,7 +71,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
71
71
  SOFTWARE.
72
72
  ```
73
73
 
74
- ## brace-expansion 5.0.9
74
+ ## brace-expansion 5.0.12
75
75
 
76
76
  License: MIT
77
77
 
@@ -130,7 +130,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
130
130
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
131
131
  ```
132
132
 
133
- ## csv-parse 7.0.2
133
+ ## csv-parse 7.0.3
134
134
 
135
135
  License: MIT
136
136
 
@@ -284,6 +284,24 @@ THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRE
284
284
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
285
285
  ```
286
286
 
287
+ ## entities 7.0.1
288
+
289
+ License: BSD-2-Clause
290
+
291
+ ```text
292
+ Copyright (c) Felix Böhm
293
+ All rights reserved.
294
+
295
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
296
+
297
+ Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
298
+
299
+ Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
300
+
301
+ THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
302
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
303
+ ```
304
+
287
305
  ## html-to-text 10.0.1
288
306
 
289
307
  License: MIT
@@ -523,6 +541,25 @@ including animation and compression. It does not rely on any specific
523
541
  underlying system, so should run in the browser, Node, or Plask.
524
542
  ```
525
543
 
544
+ ## onnxruntime-common 1.30.0
545
+
546
+ License: MIT
547
+
548
+ ```text
549
+ Copyright (c) Microsoft Corporation. All rights reserved.
550
+ Licensed under the MIT License.
551
+ ```
552
+
553
+ ## onnxruntime-web 1.30.0
554
+
555
+ License: MIT
556
+
557
+ ```text
558
+ ONNX Runtime Web v1.30.0
559
+ Copyright (c) Microsoft Corporation. All rights reserved.
560
+ Licensed under the MIT License.
561
+ ```
562
+
526
563
  ## parseley 0.13.1
527
564
 
528
565
  License: MIT
@@ -607,7 +644,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
607
644
  SOFTWARE.
608
645
  ```
609
646
 
610
- ## smol-toml 1.8.0
647
+ ## smol-toml 1.9.0
611
648
 
612
649
  License: BSD-3-Clause
613
650
 
Binary file
Binary file