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
@@ -1,47 +1,253 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
- DEFAULT_MAX_AGE_MS,
5
- blobPath,
6
- isBlobStale,
7
- loadBlob,
8
- storeBlob
9
- } from "./token-goat-chunk-D4ETHJD6.mjs";
10
- import {
11
- hasUnquotedOperator
12
- } from "./token-goat-chunk-2WOUYWLM.mjs";
4
+ sanitizeFtsQuery
5
+ } from "./token-goat-chunk-IK2DRMFX.mjs";
13
6
  import {
14
- redactSecrets
15
- } from "./token-goat-chunk-CIQ2FTY3.mjs";
7
+ recordStat
8
+ } from "./token-goat-chunk-U4WHMG6Y.mjs";
16
9
  import {
17
- sanitizeFtsQuery
18
- } from "./token-goat-chunk-435UQTKD.mjs";
10
+ getDb,
11
+ getReadDb
12
+ } from "./token-goat-chunk-GVB2MUST.mjs";
19
13
  import {
20
- getDb
21
- } from "./token-goat-chunk-7CPMDFGB.mjs";
14
+ hasUnquotedOperator
15
+ } from "./token-goat-chunk-4GILLLWY.mjs";
22
16
  import {
23
- shortFingerprint
24
- } from "./token-goat-chunk-TGOXNAHW.mjs";
17
+ stripAnsiEscapes
18
+ } from "./token-goat-chunk-HAVAOB3S.mjs";
25
19
  import {
26
- registerReset
27
- } from "./token-goat-chunk-YZAAZU2S.mjs";
20
+ redactSerializedJson
21
+ } from "./token-goat-chunk-T5P5ACNQ.mjs";
28
22
  import {
29
- runGit
30
- } from "./token-goat-chunk-XYWPOSCN.mjs";
23
+ loadConfig
24
+ } from "./token-goat-chunk-7EZTZOMC.mjs";
31
25
  import {
32
- normalizePath
33
- } from "./token-goat-chunk-NMTKNYGF.mjs";
26
+ atomicWriteText,
27
+ ensureDirSync,
28
+ sanitizeIdForFilename
29
+ } from "./token-goat-chunk-HU4TORC5.mjs";
34
30
  import {
35
- globalDbPath
36
- } from "./token-goat-chunk-MELOKFDJ.mjs";
31
+ globalDbPath,
32
+ tokenGoatHome
33
+ } from "./token-goat-chunk-CO3UYX6G.mjs";
37
34
  import {
38
35
  init_define_import_meta_env
39
36
  } from "./token-goat-chunk-A37V4PBF.mjs";
40
37
 
41
- // src/bash_output_cache.ts
38
+ // src/disk_cache.ts
42
39
  init_define_import_meta_env();
43
- import { readdirSync, readFileSync, statSync } from "fs";
44
- import { resolve } from "path";
40
+ import * as fs from "node:fs";
41
+ import * as path from "node:path";
42
+ var DEFAULT_MAX_COUNT = 200;
43
+ var DEFAULT_MAX_AGE_MS = 24 * 3600 * 1e3;
44
+ function sanitizeId(id) {
45
+ return sanitizeIdForFilename(id, 64);
46
+ }
47
+ function blobDir(subdir) {
48
+ return path.join(tokenGoatHome(), subdir);
49
+ }
50
+ function blobPath(subdir, id) {
51
+ const safe = sanitizeId(id);
52
+ if (!safe) return null;
53
+ const dir = blobDir(subdir);
54
+ const candidate = path.join(dir, `${safe}.json`);
55
+ try {
56
+ const rel = path.relative(dir, candidate);
57
+ if (rel.startsWith("..")) return null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ return candidate;
62
+ }
63
+ function isBlobStale(subdir, id) {
64
+ const p = blobPath(subdir, id);
65
+ if (p === null) return false;
66
+ try {
67
+ const stat = fs.statSync(p);
68
+ return Date.now() - stat.mtimeMs > DEFAULT_MAX_AGE_MS;
69
+ } catch {
70
+ return false;
71
+ }
72
+ }
73
+ function subdirCacheDefaults(subdir) {
74
+ try {
75
+ if (subdir === "bash_outputs") {
76
+ const bc = loadConfig().bash_compress;
77
+ return { maxCount: bc.cache_max_file_count, maxBytes: bc.cache_max_bytes, maxBytesPerItem: bc.cache_max_bytes_per_output };
78
+ }
79
+ if (subdir === "web_outputs") {
80
+ const wf = loadConfig().webfetch;
81
+ return { maxCount: wf.max_file_count, maxBytes: wf.max_bytes, maxBytesPerItem: Number.POSITIVE_INFINITY };
82
+ }
83
+ } catch {
84
+ }
85
+ return { maxCount: DEFAULT_MAX_COUNT, maxBytes: Number.POSITIVE_INFINITY, maxBytesPerItem: Number.POSITIVE_INFINITY };
86
+ }
87
+ function storeBlob(subdir, id, value, opts = {}) {
88
+ const p = blobPath(subdir, id);
89
+ if (!p) return false;
90
+ const defaults = subdirCacheDefaults(subdir);
91
+ const maxBytesPerItem = opts.maxBytesPerItem ?? defaults.maxBytesPerItem;
92
+ let rawJson;
93
+ try {
94
+ rawJson = JSON.stringify(value);
95
+ } catch {
96
+ return false;
97
+ }
98
+ let json;
99
+ try {
100
+ const result = redactSerializedJson(rawJson);
101
+ json = result.text;
102
+ if (result.count > 0) recordStat("secret_redacted", 0, result.count, void 0, subdir);
103
+ } catch {
104
+ return false;
105
+ }
106
+ if (Number.isFinite(maxBytesPerItem) && Buffer.byteLength(json, "utf-8") > maxBytesPerItem) return false;
107
+ try {
108
+ const dir = path.dirname(p);
109
+ if (!fs.existsSync(dir)) ensureDirSync(dir);
110
+ atomicWriteText(p, json);
111
+ } catch {
112
+ return false;
113
+ }
114
+ pruneBlobs(
115
+ subdir,
116
+ opts.maxCount ?? defaults.maxCount,
117
+ opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS,
118
+ opts.maxBytes ?? defaults.maxBytes,
119
+ p
120
+ );
121
+ return true;
122
+ }
123
+ function loadBlob(subdir, id) {
124
+ const p = blobPath(subdir, id);
125
+ if (!p) return null;
126
+ try {
127
+ if (!fs.existsSync(p)) return null;
128
+ return JSON.parse(fs.readFileSync(p, "utf8"));
129
+ } catch {
130
+ return null;
131
+ }
132
+ }
133
+ function listBlobs(subdir) {
134
+ const dir = blobDir(subdir);
135
+ const out = [];
136
+ try {
137
+ if (!fs.existsSync(dir)) return out;
138
+ for (const file of fs.readdirSync(dir)) {
139
+ if (!file.endsWith(".json")) continue;
140
+ const id = file.slice(0, -5);
141
+ let mtime = 0;
142
+ try {
143
+ mtime = fs.statSync(path.join(dir, file)).mtimeMs;
144
+ } catch {
145
+ }
146
+ const value = loadBlob(subdir, id);
147
+ if (value !== null) out.push({ id, mtime, value });
148
+ }
149
+ } catch {
150
+ return out;
151
+ }
152
+ return out;
153
+ }
154
+ function pruneBlobs(subdir, maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS, maxBytes = Number.POSITIVE_INFINITY, protectedPath) {
155
+ return pruneBlobDir(blobDir(subdir), maxCount, maxAgeMs, maxBytes, protectedPath);
156
+ }
157
+ function pruneBlobDir(dir, maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS, maxBytes = Number.POSITIVE_INFINITY, protectedPath) {
158
+ let removed = 0;
159
+ try {
160
+ if (!fs.existsSync(dir)) return 0;
161
+ const cutoff = Date.now() - maxAgeMs;
162
+ let kept = [];
163
+ let protectedEntry;
164
+ for (const file of fs.readdirSync(dir)) {
165
+ const full = path.join(dir, file);
166
+ let stat;
167
+ try {
168
+ stat = fs.statSync(full);
169
+ } catch {
170
+ continue;
171
+ }
172
+ if (!stat.isFile()) continue;
173
+ if (protectedPath !== void 0 && full === protectedPath) {
174
+ protectedEntry = [full, stat.mtimeMs, stat.size];
175
+ continue;
176
+ }
177
+ if (stat.mtimeMs < cutoff) {
178
+ try {
179
+ fs.unlinkSync(full);
180
+ removed++;
181
+ } catch {
182
+ continue;
183
+ }
184
+ } else if (file.endsWith(".json")) {
185
+ kept.push([full, stat.mtimeMs, stat.size]);
186
+ }
187
+ }
188
+ const countBudget = protectedEntry ? Math.max(0, maxCount - 1) : maxCount;
189
+ if (kept.length > countBudget) {
190
+ kept.sort((a, b) => a[1] - b[1]);
191
+ const excess = kept.slice(0, kept.length - countBudget);
192
+ kept = kept.slice(kept.length - countBudget);
193
+ for (const [full] of excess) {
194
+ try {
195
+ fs.unlinkSync(full);
196
+ removed++;
197
+ } catch {
198
+ continue;
199
+ }
200
+ }
201
+ }
202
+ if (Number.isFinite(maxBytes)) {
203
+ kept.sort((a, b) => a[1] - b[1]);
204
+ let total = kept.reduce((sum, [, , size]) => sum + size, 0) + (protectedEntry ? protectedEntry[2] : 0);
205
+ while (total > maxBytes && kept.length > 0) {
206
+ const oldest = kept.shift();
207
+ if (!oldest) break;
208
+ const [full, , size] = oldest;
209
+ try {
210
+ fs.unlinkSync(full);
211
+ removed++;
212
+ total -= size;
213
+ } catch {
214
+ break;
215
+ }
216
+ }
217
+ }
218
+ } catch {
219
+ return removed;
220
+ }
221
+ return removed;
222
+ }
223
+ var SWEEPABLE_CACHE_SUBDIRS = [
224
+ { subdir: "bash_outputs", countCapped: true },
225
+ { subdir: "web_outputs", countCapped: true },
226
+ { subdir: "mcp_outputs", countCapped: true },
227
+ { subdir: "sessions", countCapped: false }
228
+ ];
229
+ function sweepCacheRoots(extraRoots = []) {
230
+ let removed = 0;
231
+ const roots = [tokenGoatHome(), ...extraRoots];
232
+ const seen = /* @__PURE__ */ new Set();
233
+ for (const root of roots) {
234
+ if (!root || seen.has(root)) continue;
235
+ seen.add(root);
236
+ for (const { subdir, countCapped } of SWEEPABLE_CACHE_SUBDIRS) {
237
+ try {
238
+ const defaults = subdirCacheDefaults(subdir);
239
+ removed += pruneBlobDir(
240
+ path.join(root, subdir),
241
+ countCapped ? defaults.maxCount : Number.POSITIVE_INFINITY,
242
+ DEFAULT_MAX_AGE_MS,
243
+ countCapped ? defaults.maxBytes : Number.POSITIVE_INFINITY
244
+ );
245
+ } catch {
246
+ }
247
+ }
248
+ }
249
+ return removed;
250
+ }
45
251
 
46
252
  // src/hints/lang_patterns.ts
47
253
  init_define_import_meta_env();
@@ -140,7 +346,7 @@ var BUILD_COMMAND_PATTERNS = [
140
346
  /^\s*(?:gradle|\.\/gradlew|gradlew)\b/i,
141
347
  // Python / pip
142
348
  /^\s*pip\s+(install|freeze)\b/i,
143
- // npm audit / npm outdated — read-only reporting commands with no side effects, safe to cache/recall. Each resolves a `lockfile`-scoped fingerprint via bash_output_cache.ts's isNpmAuditCommand/isNpmOutdatedCommand, but that fingerprinting never actually ran outside their own unit tests until this entry, since neither subcommand was recognized by isBuildCommand (the generic cache-reachability gate in hooks_bash.ts). `npm install`/`npm ci`/`npm list`/`npm ls` are deliberately left out — see the negative-case test in lang_patterns.test.ts.
349
+ // npm audit / npm outdated — read-only reporting commands with no side effects, safe to cache/recall. Each resolves a `lockfile`-scoped fingerprint via bash_output_cache.ts's isNpmAuditCommand/isNpmOutdatedCommand, but that fingerprinting never actually ran outside their own unit tests until this entry, since neither subcommand was recognized by isBuildCommand (the generic cache-reachability gate in hooks_bash.ts and hooks_bash_post.ts). `npm install`/`npm ci`/`npm list`/`npm ls` are deliberately left out — see the negative-case test in lang_patterns.test.ts.
144
350
  /^\s*npm\s+(audit|outdated)\b/i,
145
351
  // Poetry
146
352
  /^\s*poetry\s+(install|update)\b/i,
@@ -225,6 +431,8 @@ var TEST_RUNNER_COMMAND_PATTERNS = [
225
431
  /^go test(?:\s|$)/i,
226
432
  // Rust / Cargo
227
433
  /^cargo test(?:\s|$)/i,
434
+ // dotnet test
435
+ /^\s*dotnet\s+test(?:\s|$)/i,
228
436
  // npm test / npm run test
229
437
  /^npm (?:test|run test)(?:\s|$)/i,
230
438
  // yarn test / yarn run test / pnpm test / pnpm run test
@@ -233,6 +441,20 @@ var TEST_RUNNER_COMMAND_PATTERNS = [
233
441
  function isTestRunnerCommand(cmd) {
234
442
  return TEST_RUNNER_COMMAND_PATTERNS.some((re) => re.test(cmd));
235
443
  }
444
+ var DEV_SERVER_COMMAND_PATTERNS = [
445
+ { pattern: /^(?:npx\s+)?next dev/, recallHint: '--tail 30 --grep "error|warn|ready|compiled"' },
446
+ { pattern: /^(?:npx\s+)?next build/, recallHint: '--grep "error|warn|Failed|\u2713"' },
447
+ // vite ends at whitespace or the end of the command, not at a word boundary, which a hyphen satisfies: vite-node is another tool, so it is neither recalled nor fingerprinted as a dev server.
448
+ { pattern: /^(?:npx\s+)?vite(?:\s|$)/, recallHint: '--tail 20 --grep "error|warn|ready"' },
449
+ // nuxt dev ends the same way: `nuxt devtools enable` is another nuxt command.
450
+ { pattern: /^(?:npx\s+)?nuxt dev(?:\s|$)/, recallHint: '--tail 30 --grep "error|warn|ready"' },
451
+ { pattern: /^(?:npx\s+)?remix dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
452
+ { pattern: /^(?:npx\s+)?astro dev/, recallHint: '--tail 20 --grep "error|warn|ready"' }
453
+ ];
454
+ function isDevServerCommand(cmd) {
455
+ const trimmed = cmd.trim();
456
+ return DEV_SERVER_COMMAND_PATTERNS.some(({ pattern }) => pattern.test(trimmed));
457
+ }
236
458
  var MONITORING_COMMAND_PATTERNS = [
237
459
  // GitHub CI
238
460
  { pattern: /^gh run (?:watch|view|list)/, recallHint: '--grep "fail|error|pass|\u2713|\u2717|conclusion"' },
@@ -242,16 +464,10 @@ var MONITORING_COMMAND_PATTERNS = [
242
464
  { pattern: /^gh pr list/, recallHint: '--grep "OPEN|DRAFT|MERGED|#"' },
243
465
  { pattern: /^gh issue view/, recallHint: '--grep "state|title|label|OPEN|CLOSED"' },
244
466
  { pattern: /^gh workflow (?:run|list|view)/, recallHint: '--grep "completed|failed|in_progress"' },
245
- // Dev servers (Next, Vite, Nuxt, Remix, Astro)
246
- { pattern: /^(?:npx\s+)?next dev/, recallHint: '--tail 30 --grep "error|warn|ready|compiled"' },
247
- { pattern: /^(?:npx\s+)?next build/, recallHint: '--grep "error|warn|Failed|\u2713"' },
248
- { pattern: /^(?:npx\s+)?vite\b(?:\s+dev|\s+build|\s+preview)?/, recallHint: '--tail 20 --grep "error|warn|ready"' },
249
- { pattern: /^(?:npx\s+)?nuxt dev/, recallHint: '--tail 30 --grep "error|warn|ready"' },
250
- { pattern: /^(?:npx\s+)?remix dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
251
- { pattern: /^(?:npx\s+)?astro dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
252
- // Test watchers
253
- { pattern: /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?/, recallHint: '--grep "FAIL|PASS|Error|\u2713|\u2717"' },
254
- { pattern: /^(?:npx\s+)?jest(?:\s+--watch)?/, recallHint: '--grep "FAIL|PASS|Error|Tests:"' },
467
+ ...DEV_SERVER_COMMAND_PATTERNS,
468
+ // Test watchers. jest and vitest must be followed by whitespace or the end of the command, as in TEST_RUNNER_COMMAND_PATTERNS, not merely by a word boundary: a cached run goes stale only through the git fingerprint isTestRunnerCommand grants, and a longer name (jest-codemods, vitest-preview) is another tool that predicate rejects.
469
+ { pattern: /^(?:npx\s+)?vitest(?:\s|$)/, recallHint: '--grep "FAIL|PASS|Error|\u2713|\u2717"' },
470
+ { pattern: /^(?:npx\s+)?jest(?:\s|$)/, recallHint: '--grep "FAIL|PASS|Error|Tests:"' },
255
471
  { pattern: /^pytest(?:\s|$)/, recallHint: '--grep "FAILED|PASSED|ERROR|passed|failed"' },
256
472
  { pattern: /^(?:cargo\s+test|cargo\s+watch)/, recallHint: '--grep "FAILED|ok|error\\["' },
257
473
  { pattern: /^go test/, recallHint: '--grep "FAIL|ok|---"' },
@@ -268,7 +484,8 @@ var MONITORING_COMMAND_PATTERNS = [
268
484
  { pattern: /^(?:npx\s+)?prettier(?:\s|$)/, recallHint: '--grep "unchanged|reformatted|error"' },
269
485
  { pattern: /^npx\s+tsc(?:\s|$)/, recallHint: '--grep "error TS|Cannot find|Type "' },
270
486
  { pattern: /^ruff(?:\s|$)/, recallHint: '--grep "error|warning|Found"' },
271
- { pattern: /^(?:cargo\s+)?clippy/, recallHint: '--grep "error\\[|warning\\["' },
487
+ // Ends the same way: clippy-driver is clippy's rustc wrapper, run without cargo, so isBuildCommand gives it no fingerprint to go stale by.
488
+ { pattern: /^(?:cargo\s+)?clippy(?:\s|$)/, recallHint: '--grep "error\\[|warning\\["' },
272
489
  // git diff (full diff output — can be very large; excludes --stat which is small)
273
490
  { pattern: /^git diff(?!\s+--stat)(?:\s+HEAD)?(?:\s|$)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
274
491
  { pattern: /^git diff\s+--cached(?!\s+--stat)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
@@ -318,16 +535,46 @@ function getMonitoringRecallHint(cmd) {
318
535
  return null;
319
536
  }
320
537
 
538
+ // src/git_porcelain.ts
539
+ init_define_import_meta_env();
540
+ function parsePorcelainZ(stdout) {
541
+ const fields = stdout.split("\0");
542
+ const out = [];
543
+ for (let i = 0; i < fields.length; i++) {
544
+ const field = fields[i] ?? "";
545
+ if (field.length < 4) continue;
546
+ const entry = { path: field.slice(3) };
547
+ if (field.startsWith("??")) entry.untracked = true;
548
+ if (/[RC]/.test(field.slice(0, 2))) {
549
+ i++;
550
+ const orig = fields[i];
551
+ if (orig !== void 0 && orig !== "") entry.origPath = orig;
552
+ }
553
+ out.push(entry);
554
+ }
555
+ return out;
556
+ }
557
+
321
558
  // src/recall_index.ts
322
559
  init_define_import_meta_env();
323
- import * as fs from "node:fs";
560
+ import * as fs2 from "node:fs";
561
+
562
+ // src/snippet_window.ts
563
+ init_define_import_meta_env();
564
+ function snippetAround(text, idx, maxLen) {
565
+ const start = Math.max(0, idx - Math.floor(maxLen / 3));
566
+ const end = Math.min(text.length, start + maxLen);
567
+ return (start > 0 ? "..." : "") + text.slice(start, end) + (end < text.length ? "..." : "");
568
+ }
569
+
570
+ // src/recall_index.ts
324
571
  function subdirForCacheType(cacheType) {
325
572
  return cacheType === "web" ? "web_outputs" : "bash_outputs";
326
573
  }
327
574
  function blobStillExists(cacheType, id) {
328
575
  try {
329
576
  const p = blobPath(subdirForCacheType(cacheType), id);
330
- return p !== null && fs.existsSync(p);
577
+ return p !== null && fs2.existsSync(p);
331
578
  } catch {
332
579
  return false;
333
580
  }
@@ -347,7 +594,8 @@ function indexRecallEntry(cacheType, id, label, content, storedAt) {
347
594
  label = excluded.label,
348
595
  content = excluded.content,
349
596
  stored_at = excluded.stored_at`
350
- ).run({ cacheType, id, label, content, storedAt });
597
+ // Stripped so a query spanning a colour boundary matches the text a reader sees; the cached blob itself stays raw.
598
+ ).run({ cacheType, id, label: stripAnsiEscapes(label), content: stripAnsiEscapes(content), storedAt });
351
599
  } catch {
352
600
  }
353
601
  pruneCacheRecallRows();
@@ -364,7 +612,7 @@ var _ftsAvailable = null;
364
612
  function hasFtsTable() {
365
613
  if (_ftsAvailable !== null) return _ftsAvailable;
366
614
  try {
367
- const db = getDb(globalDbPath());
615
+ const db = getReadDb(globalDbPath());
368
616
  const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cache_recall_fts'`).get();
369
617
  _ftsAvailable = row !== void 0;
370
618
  } catch {
@@ -373,7 +621,7 @@ function hasFtsTable() {
373
621
  return _ftsAvailable;
374
622
  }
375
623
  function buildSnippet(content, query, maxLen = 160) {
376
- const flat = content.replace(/\s+/g, " ").trim();
624
+ const flat = stripAnsiEscapes(content).replace(/\s+/g, " ").trim();
377
625
  const tokens = query.split(/\s+/).filter(Boolean);
378
626
  let idx = -1;
379
627
  const lowerFlat = flat.toLowerCase();
@@ -384,14 +632,7 @@ function buildSnippet(content, query, maxLen = 160) {
384
632
  break;
385
633
  }
386
634
  }
387
- if (idx === -1) {
388
- return flat.length > maxLen ? flat.slice(0, maxLen) + "..." : flat;
389
- }
390
- const start = Math.max(0, idx - Math.floor(maxLen / 3));
391
- const end = Math.min(flat.length, start + maxLen);
392
- const prefix = start > 0 ? "..." : "";
393
- const suffix = end < flat.length ? "..." : "";
394
- return prefix + flat.slice(start, end) + suffix;
635
+ return snippetAround(flat, Math.max(0, idx), maxLen);
395
636
  }
396
637
  function toFtsMatchExpr(query) {
397
638
  if (query.trim().length === 0) return null;
@@ -410,7 +651,7 @@ function mapRowsToHits(rows, snippetQuery) {
410
651
  function ftsSearch(query, type, limit) {
411
652
  const matchExpr = toFtsMatchExpr(query);
412
653
  if (matchExpr === null) return [];
413
- const db = getDb(globalDbPath());
654
+ const db = getReadDb(globalDbPath());
414
655
  const rows = type !== void 0 ? db.prepare(
415
656
  `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
416
657
  FROM cache_recall_fts
@@ -431,7 +672,7 @@ function ftsSearch(query, type, limit) {
431
672
  function likeSearch(query, type, limit) {
432
673
  const trimmed = query.trim();
433
674
  if (!trimmed) return [];
434
- const db = getDb(globalDbPath());
675
+ const db = getReadDb(globalDbPath());
435
676
  const needle = `%${trimmed.replace(/\\/g, "\\\\").replace(/[%_]/g, (c) => `\\${c}`)}%`;
436
677
  const rows = type !== void 0 ? db.prepare(
437
678
  `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
@@ -452,7 +693,7 @@ function listRecentRecall(opts = {}) {
452
693
  const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
453
694
  const columns = `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt FROM cache_recall`;
454
695
  try {
455
- const db = getDb(globalDbPath());
696
+ const db = getReadDb(globalDbPath());
456
697
  const rows = opts.type !== void 0 ? db.prepare(`${columns} WHERE cache_type = ? ORDER BY stored_at DESC LIMIT ?`).all(opts.type, limit) : db.prepare(`${columns} ORDER BY stored_at DESC LIMIT ?`).all(limit);
457
698
  return mapRowsToHits(rows, "");
458
699
  } catch {
@@ -476,401 +717,29 @@ function searchRecall(query, opts = {}) {
476
717
  }
477
718
  }
478
719
 
479
- // src/bash_output_cache.ts
480
- var BASH_OUTPUT_SUBDIR = "bash_outputs";
481
- var _byId = /* @__PURE__ */ new Map();
482
- var COMMAND_PATTERNS = {
483
- gitMutable: /^\s*git\s+(diff|status)\b/i,
484
- gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
485
- gitDiffScoped: /\s--\s+\S/,
486
- dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
487
- depList: /^\s*(?:npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:ls|list)\b|pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|freeze)\b|uv\s+pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|freeze)\b|pnpm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|ls)\b|yarn\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list)\b|cargo\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*tree\b|bundle\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|show)\b|composer\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*show\b)/i,
488
- npmInstall: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:install|ci)\b/i,
489
- npmAudit: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*audit\b(?!.*(?:--fix|fix)\b)/i,
490
- npmOutdated: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*outdated\b/i,
491
- envProbe: /^\s*(?:node\s+(?:-v|--version)|npm\s+(?:-v|--version)|python3?\s+(?:(?:-V)\b|--?version)|git\s+--version|uv\s+--version|go\s+version|rustc\s+--version|cargo\s+--version|java\s+--version|ruby\s+--version|gem\s+--version|php\s+--version|which\b|where\b)/i,
492
- npx: /^(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)\s*npx\s+(?:--?yes\s+)?/i,
493
- gitPush: /^\s*git\s+push\b/i,
494
- testRunner: /^\s*(?:npx\s+)?(?:pytest|vitest|jest|go\s+test)\b/i,
495
- lintCommand: /^\s*(?:(?:npx\s+)?eslint|(?:uv\s+run\s+)?ruff)\b/i,
496
- npmRunScript: /^\s*npm\s+run(?:-script)?\b/i,
497
- catCommand: /^\s*cat\b/i
498
- };
499
- var DEP_LOCKFILES = {
500
- npm: ["package-lock.json", "yarn.lock"],
501
- pip: ["requirements.txt"],
502
- uv: ["uv.lock", "requirements.txt"],
503
- pnpm: ["pnpm-lock.yaml"],
504
- yarn: ["yarn.lock"],
505
- cargo: ["Cargo.lock"],
506
- bundle: ["Gemfile.lock"],
507
- composer: ["composer.lock"]
508
- };
509
- function isCommandOfType(cmd, type) {
510
- const pattern = COMMAND_PATTERNS[type];
511
- return pattern?.test(cmd) ?? false;
512
- }
513
- var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
514
- var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
515
- var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
516
- var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
517
- var isNpmAuditCommand = (cmd) => isCommandOfType(cmd, "npmAudit");
518
- var isNpmOutdatedCommand = (cmd) => isCommandOfType(cmd, "npmOutdated");
519
- var isGitPushCommand = (cmd) => isCommandOfType(cmd, "gitPush");
520
- var isTestRunnerCommand2 = (cmd) => isCommandOfType(cmd, "testRunner");
521
- var isLintCommand = (cmd) => isCommandOfType(cmd, "lintCommand");
522
- var isNpmRunScriptCommand = (cmd) => isCommandOfType(cmd, "npmRunScript");
523
- var isCatCommand = (cmd) => isCommandOfType(cmd, "catCommand");
524
- function isScopedGitStatusOrDiffStatCommand(cmd) {
525
- if (!isCommandOfType(cmd, "gitMutable")) return false;
526
- if (!isCommandOfType(cmd, "gitDiffScoped")) return false;
527
- if (/^\s*git\s+diff\b/i.test(cmd) && !/--stat\b/.test(cmd)) return false;
528
- return true;
529
- }
530
- function gitStateFingerprintSync(cwd) {
531
- try {
532
- const headResult = runGit(["rev-parse", "HEAD"], { cwd });
533
- if (headResult.exitCode !== 0) return null;
534
- const headSha = headResult.stdout.trim();
535
- let statusHash = "";
536
- const statusResult = runGit(["status", "--porcelain"], { cwd });
537
- if (statusResult.exitCode === 0) {
538
- statusHash = shortFingerprint(statusResult.stdout);
539
- }
540
- const key = `${headSha}\0${statusHash}`;
541
- return shortFingerprint(key);
542
- } catch {
543
- return null;
544
- }
545
- }
546
- var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
547
- function dirStateFingerprintSync(path) {
548
- try {
549
- const stat = statSync(path);
550
- if (!stat.isDirectory()) return null;
551
- const entries = readdirSync(path);
552
- if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
553
- return shortFingerprint(entries.slice().sort().join("\0"));
554
- }
555
- return shortFingerprint(stat.mtimeMs.toString());
556
- } catch {
557
- return null;
558
- }
559
- }
560
- var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
561
- function fileStateFingerprintSync(path) {
562
- try {
563
- const stat = statSync(path);
564
- if (!stat.isFile()) return null;
565
- if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
566
- return shortFingerprint(readFileSync(path));
567
- }
568
- return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
569
- } catch {
570
- return null;
571
- }
572
- }
573
- function depLockfileFingerprintSync(cmd, cwd) {
574
- if (!cwd) return null;
575
- const stripped = cmd.trim();
576
- const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
577
- if (!firstToken) return null;
578
- const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
579
- if (!candidates) return null;
580
- for (const lockfile of candidates) {
581
- try {
582
- const content = readFileSync(resolve(cwd, lockfile));
583
- return shortFingerprint(content);
584
- } catch {
585
- continue;
586
- }
587
- }
588
- return null;
589
- }
590
- function normalizeCommandForCacheKey(cmd) {
591
- const trimmed = cmd.trim();
592
- const tokens = [];
593
- let current = "";
594
- let quoteChar = null;
595
- const flush = () => {
596
- if (current.length > 0) {
597
- tokens.push(current);
598
- current = "";
599
- }
600
- };
601
- for (let i = 0; i < trimmed.length; i++) {
602
- const ch = trimmed[i];
603
- if (quoteChar !== null) {
604
- current += ch;
605
- if (ch === quoteChar) quoteChar = null;
606
- continue;
607
- }
608
- if (ch === '"' || ch === "'") {
609
- quoteChar = ch;
610
- current += ch;
611
- continue;
612
- }
613
- if (/\s/.test(ch)) {
614
- flush();
615
- continue;
616
- }
617
- current += ch === "\\" ? "/" : ch;
618
- }
619
- flush();
620
- const normalized_tokens = tokens.map((token) => {
621
- if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
622
- return token;
623
- }
624
- if (token.startsWith("./") && !token.startsWith("../")) {
625
- token = token.slice(2);
626
- }
627
- if (token.endsWith("/") && token !== "/") {
628
- token = token.slice(0, -1);
629
- }
630
- return token || ".";
631
- });
632
- return normalized_tokens.join(" ");
633
- }
634
- function commandHashSync(command, cwd = null) {
635
- const normalized = normalizeCommandForCacheKey(command);
636
- let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
637
- if (cwd && isGitMutableCommand(command)) {
638
- const fp = gitStateFingerprintSync(cwd);
639
- if (fp) key = `${key}\0git:${fp}`;
640
- }
641
- if (cwd && isDirListingCommand(command)) {
642
- const target = extractFirstPathArg(command, cwd, cwd);
643
- if (target) {
644
- const fp = dirStateFingerprintSync(target);
645
- if (fp) key = `${key}\0dir:${fp}`;
646
- }
647
- }
648
- if (isDepListCommand(command)) {
649
- const fp = depLockfileFingerprintSync(command, cwd);
650
- if (fp) key = `${key}\0lockfile:${fp}`;
651
- }
652
- if (cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
653
- const fp = depLockfileFingerprintSync(command, cwd);
654
- if (fp) key = `${key}\0npm-install:${fp}`;
655
- }
656
- return shortFingerprint(key);
657
- }
658
- function commandHash(command, cwd = null) {
659
- return Promise.resolve(commandHashSync(command, cwd));
660
- }
661
- function bashOutputIdSync(command, output, cwd = null) {
662
- return shortFingerprint(`${commandHashSync(command, cwd)}\0${output}`);
663
- }
664
- function computeBashFingerprints(command, cwd) {
665
- const fingerprints = {};
666
- if (cwd && (isGitMutableCommand(command) || isGitPushCommand(command) || isTestRunnerCommand2(command) || isLintCommand(command) || isBuildCommand(command) || isNpmRunScriptCommand(command))) {
667
- const fp = gitStateFingerprintSync(cwd);
668
- if (fp) fingerprints.git = fp;
669
- }
670
- if (cwd && isDirListingCommand(command)) {
671
- const target = extractFirstPathArg(command, cwd, cwd);
672
- if (target) {
673
- const fp = dirStateFingerprintSync(target);
674
- if (fp) fingerprints.dir = fp;
675
- }
676
- }
677
- if (isDepListCommand(command) || cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
678
- const fp = depLockfileFingerprintSync(command, cwd);
679
- if (fp) fingerprints.lockfile = fp;
680
- }
681
- if (cwd && isCatCommand(command)) {
682
- const target = extractFirstPathArg(command, cwd, null);
683
- if (target) {
684
- const fp = fileStateFingerprintSync(target);
685
- if (fp) fingerprints.file = fp;
686
- }
687
- }
688
- return Object.keys(fingerprints).length > 0 ? fingerprints : void 0;
689
- }
690
- function isBashEntryStale(entry, command, cwd) {
691
- const stored = entry.fingerprints;
692
- if (!stored) return false;
693
- const current = computeBashFingerprints(command, cwd);
694
- if (stored.git !== void 0 && stored.git !== current?.git) return true;
695
- if (stored.dir !== void 0 && stored.dir !== current?.dir) return true;
696
- if (stored.lockfile !== void 0 && stored.lockfile !== current?.lockfile) return true;
697
- if (stored.file !== void 0 && stored.file !== current?.file) return true;
698
- return false;
699
- }
700
- function tokenizeShellArgs(cmd) {
701
- const trimmed = cmd.trim();
702
- const tokens = [];
703
- let current = "";
704
- let quoteChar = null;
705
- let hasToken = false;
706
- for (let i = 0; i < trimmed.length; i++) {
707
- const ch = trimmed[i];
708
- if (quoteChar !== null) {
709
- if (ch === quoteChar) {
710
- quoteChar = null;
711
- } else {
712
- current += ch;
713
- }
714
- continue;
715
- }
716
- if (ch === '"' || ch === "'") {
717
- quoteChar = ch;
718
- hasToken = true;
719
- continue;
720
- }
721
- if (/\s/.test(ch)) {
722
- if (hasToken) {
723
- tokens.push(current);
724
- current = "";
725
- hasToken = false;
726
- }
727
- continue;
728
- }
729
- current += ch;
730
- hasToken = true;
731
- }
732
- if (hasToken) tokens.push(current);
733
- return tokens;
734
- }
735
- function extractFirstPathArg(cmd, cwd, fallback) {
736
- const tokens = tokenizeShellArgs(cmd);
737
- for (let i = 1; i < tokens.length; i++) {
738
- const token = tokens[i];
739
- if (!token.startsWith("-")) {
740
- if (!token.startsWith("/")) {
741
- return resolve(cwd, token);
742
- }
743
- return token;
744
- }
745
- }
746
- return fallback;
747
- }
748
- var ISSUE_LINE_PATTERN = /\b(?:error|fail(?:ed|ure)?|warning)\b/i;
749
- function summarizeOutputDelta(oldOutput, newOutput) {
750
- if (oldOutput === newOutput) return null;
751
- const oldLines = oldOutput.split("\n");
752
- const newLines = newOutput.split("\n");
753
- const oldIssueLines = oldLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
754
- if (oldIssueLines.length > 0) {
755
- const newIssueLines = newLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
756
- const priorTotal = oldIssueLines.length;
757
- const availableCounts = /* @__PURE__ */ new Map();
758
- for (const l of newIssueLines) availableCounts.set(l, (availableCounts.get(l) ?? 0) + 1);
759
- let resolved = 0;
760
- for (const l of oldIssueLines) {
761
- const avail = availableCounts.get(l) ?? 0;
762
- if (avail > 0) {
763
- availableCounts.set(l, avail - 1);
764
- } else {
765
- resolved++;
766
- }
767
- }
768
- const remaining = newIssueLines.length;
769
- return `[token-goat: delta] ${resolved} of ${priorTotal} prior issues resolved; remaining: ${remaining}`;
770
- }
771
- return `[token-goat: delta] output changed: ${oldLines.length} -> ${newLines.length} lines`;
772
- }
773
- function storeBashOutputSync(command, output, exitCode, cwd = null) {
774
- const id = bashOutputIdSync(command, output, cwd);
775
- const commandKey = commandHashSync(command, cwd);
776
- const fingerprints = computeBashFingerprints(command, cwd);
777
- const redactedOutput = redactSecrets(output).text;
778
- const redactedCommand = redactSecrets(command).text;
779
- const entry = {
780
- id,
781
- command: redactedCommand,
782
- output: redactedOutput,
783
- exitCode,
784
- storedAt: Date.now(),
785
- sizeBytes: Buffer.byteLength(redactedOutput, "utf-8"),
786
- ...fingerprints ? { fingerprints } : {}
787
- };
788
- _byId.set(id, entry);
789
- _byId.set(commandKey, entry);
790
- storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
791
- if (commandKey !== id) storeBlob(BASH_OUTPUT_SUBDIR, commandKey, { latestId: id });
792
- indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
793
- ${redactedOutput}`, entry.storedAt);
794
- return id;
795
- }
796
- function coerceBashRedirect(raw) {
797
- if (raw === null || typeof raw !== "object") return null;
798
- const latest = raw["latestId"];
799
- return typeof latest === "string" && latest.length > 0 ? latest : null;
800
- }
801
- function storeBashOutput(command, output, exitCode, cwd = null) {
802
- return Promise.resolve(storeBashOutputSync(command, output, exitCode, cwd));
803
- }
804
- function coerceBashEntry(raw) {
805
- if (raw === null || typeof raw !== "object") return null;
806
- const o = raw;
807
- if (typeof o["id"] !== "string" || typeof o["command"] !== "string" || typeof o["output"] !== "string" || typeof o["exitCode"] !== "number" || typeof o["storedAt"] !== "number" || typeof o["sizeBytes"] !== "number") {
808
- return null;
809
- }
810
- const entry = {
811
- id: o["id"],
812
- command: o["command"],
813
- output: o["output"],
814
- exitCode: o["exitCode"],
815
- storedAt: o["storedAt"],
816
- sizeBytes: o["sizeBytes"]
817
- };
818
- const rawFingerprints = o["fingerprints"];
819
- if (rawFingerprints !== null && typeof rawFingerprints === "object") {
820
- const f = rawFingerprints;
821
- const fingerprints = {};
822
- if (typeof f["git"] === "string") fingerprints.git = f["git"];
823
- if (typeof f["dir"] === "string") fingerprints.dir = f["dir"];
824
- if (typeof f["lockfile"] === "string") fingerprints.lockfile = f["lockfile"];
825
- if (typeof f["file"] === "string") fingerprints.file = f["file"];
826
- if (Object.keys(fingerprints).length > 0) return { ...entry, fingerprints };
827
- }
828
- return entry;
829
- }
830
- function getBashOutput(id) {
831
- const hit = _byId.get(id);
832
- if (hit !== void 0) return hit;
833
- if (isBlobStale(BASH_OUTPUT_SUBDIR, id)) return null;
834
- const raw = loadBlob(BASH_OUTPUT_SUBDIR, id);
835
- const target = coerceBashRedirect(raw);
836
- if (target !== null) {
837
- if (isBlobStale(BASH_OUTPUT_SUBDIR, target)) return null;
838
- const latest = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, target));
839
- if (latest === null) return null;
840
- _byId.set(target, latest);
841
- _byId.set(id, latest);
842
- return latest;
843
- }
844
- const entry = coerceBashEntry(raw);
845
- if (entry === null) return null;
846
- _byId.set(id, entry);
847
- return entry;
848
- }
849
- registerReset(() => {
850
- _byId = /* @__PURE__ */ new Map();
851
- }, { perRequest: true });
852
-
853
720
  export {
721
+ parsePorcelainZ,
722
+ DEFAULT_MAX_COUNT,
723
+ DEFAULT_MAX_AGE_MS,
724
+ blobPath,
725
+ isBlobStale,
726
+ storeBlob,
727
+ loadBlob,
728
+ listBlobs,
729
+ pruneBlobs,
730
+ sweepCacheRoots,
854
731
  isLockFile,
855
732
  isManifestFile,
856
733
  isInBuildDir,
857
734
  isGeneratedFile,
858
735
  isBuildCommand,
859
736
  isTestRunnerCommand,
737
+ isDevServerCommand,
860
738
  getMonitoringRecallHint,
739
+ snippetAround,
861
740
  RECALL_DEFAULT_LIMIT,
862
741
  isRecallCacheType,
863
742
  indexRecallEntry,
864
743
  listRecentRecall,
865
- searchRecall,
866
- BASH_OUTPUT_SUBDIR,
867
- isScopedGitStatusOrDiffStatCommand,
868
- normalizeCommandForCacheKey,
869
- commandHash,
870
- bashOutputIdSync,
871
- isBashEntryStale,
872
- summarizeOutputDelta,
873
- storeBashOutputSync,
874
- storeBashOutput,
875
- getBashOutput
744
+ searchRecall
876
745
  };