@ken-jo/agent-connector 0.4.95 → 0.4.97

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 (378) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +6 -6
  3. package/README.md +605 -599
  4. package/dist/{action-Q7GGO4PA.js → action-IPFDPOZU.js} +16 -16
  5. package/dist/action-IPFDPOZU.js.map +1 -0
  6. package/dist/{amazon-q-ETMAGY3D.js → amazon-q-IT44LEWV.js} +6 -6
  7. package/dist/amazon-q-IT44LEWV.js.map +1 -0
  8. package/dist/{amp-BQZOL5GR.js → amp-CEWOSWQO.js} +4 -4
  9. package/dist/amp-CEWOSWQO.js.map +1 -0
  10. package/dist/{amp-67BZONGF.js → amp-P35FRU2Z.js} +7 -7
  11. package/dist/amp-P35FRU2Z.js.map +1 -0
  12. package/dist/{antigravity-EA4L225L.js → antigravity-LHFEFNMC.js} +5 -5
  13. package/dist/antigravity-LHFEFNMC.js.map +1 -0
  14. package/dist/antigravity-TWIWXTQH.js +18 -0
  15. package/dist/{antigravity-cli-B2HXZFYQ.js → antigravity-cli-MOKFSWOU.js} +5 -5
  16. package/dist/antigravity-cli-MOKFSWOU.js.map +1 -0
  17. package/dist/{antigravity-cli-OM6SC3PE.js → antigravity-cli-MPE27LPI.js} +9 -9
  18. package/dist/antigravity-cli-MPE27LPI.js.map +1 -0
  19. package/dist/audit-3E5JAOP2.js +311 -0
  20. package/dist/audit-3E5JAOP2.js.map +1 -0
  21. package/dist/{chunk-PW42ZXCB.js → chunk-3EP5V2F4.js} +3 -3
  22. package/dist/chunk-3EP5V2F4.js.map +1 -0
  23. package/dist/{chunk-MGTDQLJJ.js → chunk-3FINQ2ZF.js} +4 -4
  24. package/dist/chunk-3FINQ2ZF.js.map +1 -0
  25. package/dist/{chunk-KHO5WNTP.js → chunk-4GH2KA7V.js} +1 -1
  26. package/dist/chunk-4GH2KA7V.js.map +1 -0
  27. package/dist/{chunk-2Y2MZRLP.js → chunk-7C2IPIIB.js} +3 -3
  28. package/dist/chunk-7C2IPIIB.js.map +1 -0
  29. package/dist/{chunk-NLE2J5P4.js → chunk-7HHW23AC.js} +29 -29
  30. package/dist/chunk-7HHW23AC.js.map +1 -0
  31. package/dist/{chunk-XD7MY6HB.js → chunk-7PSNXUFK.js} +1 -1
  32. package/dist/chunk-7PSNXUFK.js.map +1 -0
  33. package/dist/{chunk-5IOTP7LQ.js → chunk-7ZFK2WEO.js} +7 -5
  34. package/dist/chunk-7ZFK2WEO.js.map +1 -0
  35. package/dist/{chunk-XC5SJ35F.js → chunk-C63AAXK7.js} +5 -5
  36. package/dist/chunk-C63AAXK7.js.map +1 -0
  37. package/dist/{chunk-UM5S7DKK.js → chunk-C747DYNT.js} +1 -1
  38. package/dist/chunk-C747DYNT.js.map +1 -0
  39. package/dist/{chunk-YPAXJ7EW.js → chunk-FHYR5Q5T.js} +13 -13
  40. package/dist/chunk-FHYR5Q5T.js.map +1 -0
  41. package/dist/{chunk-I5UXHIDT.js → chunk-GCPEPUD7.js} +8 -8
  42. package/dist/chunk-GCPEPUD7.js.map +1 -0
  43. package/dist/{chunk-ISEJWBFF.js → chunk-GZZCHJDP.js} +2 -2
  44. package/dist/chunk-GZZCHJDP.js.map +1 -0
  45. package/dist/{chunk-UMQ6NBQR.js → chunk-ILWHLGIC.js} +2 -2
  46. package/dist/chunk-ILWHLGIC.js.map +1 -0
  47. package/dist/{chunk-IVAWP6HH.js → chunk-IOJDZJ72.js} +2 -2
  48. package/dist/chunk-IOJDZJ72.js.map +1 -0
  49. package/dist/{chunk-NBYKLUF6.js → chunk-KFPSJRHR.js} +2 -2
  50. package/dist/chunk-KFPSJRHR.js.map +1 -0
  51. package/dist/{chunk-O7SDRBVB.js → chunk-KM2YXO6V.js} +1 -1
  52. package/dist/chunk-KM2YXO6V.js.map +1 -0
  53. package/dist/{chunk-7F5KHLEP.js → chunk-L2C5UMKW.js} +4 -4
  54. package/dist/chunk-L2C5UMKW.js.map +1 -0
  55. package/dist/{chunk-OP4KBXUO.js → chunk-LFWYYWLV.js} +1 -1
  56. package/dist/chunk-LFWYYWLV.js.map +1 -0
  57. package/dist/{chunk-APZDTY76.js → chunk-LQ55E7QI.js} +7 -7
  58. package/dist/chunk-LQ55E7QI.js.map +1 -0
  59. package/dist/{chunk-PAPMUAR5.js → chunk-MLU7NDAZ.js} +12 -12
  60. package/dist/chunk-MLU7NDAZ.js.map +1 -0
  61. package/dist/{chunk-H33M77DD.js → chunk-N4ENLZEU.js} +22 -19
  62. package/dist/chunk-N4ENLZEU.js.map +1 -0
  63. package/dist/{chunk-D4ELPBB5.js → chunk-NDMNMAKP.js} +43 -43
  64. package/dist/chunk-NDMNMAKP.js.map +1 -0
  65. package/dist/{chunk-H53QZQJI.js → chunk-PSGTHJQU.js} +1 -1
  66. package/dist/chunk-PSGTHJQU.js.map +1 -0
  67. package/dist/{chunk-FBJQGRET.js → chunk-Q4ISQHUQ.js} +1 -1
  68. package/dist/chunk-Q4ISQHUQ.js.map +1 -0
  69. package/dist/{chunk-6TF7MHG5.js → chunk-Q7PR2WGT.js} +2 -2
  70. package/dist/chunk-Q7PR2WGT.js.map +1 -0
  71. package/dist/{chunk-GZGLCCEE.js → chunk-QYOUQCC6.js} +1 -1
  72. package/dist/chunk-QYOUQCC6.js.map +1 -0
  73. package/dist/{chunk-Z2MRSS2I.js → chunk-SLJI5BOM.js} +2 -2
  74. package/dist/chunk-SLJI5BOM.js.map +1 -0
  75. package/dist/{chunk-JKKU2WKG.js → chunk-TGZFDXNH.js} +4 -4
  76. package/dist/chunk-TGZFDXNH.js.map +1 -0
  77. package/dist/{chunk-FJCUKXGX.js → chunk-THI4PI4H.js} +2 -2
  78. package/dist/chunk-THI4PI4H.js.map +1 -0
  79. package/dist/{chunk-MQPNU2CY.js → chunk-UZZTWWGG.js} +1 -1
  80. package/dist/chunk-UZZTWWGG.js.map +1 -0
  81. package/dist/{chunk-3NH2ARIS.js → chunk-WPTNMYYM.js} +2 -2
  82. package/dist/chunk-WPTNMYYM.js.map +1 -0
  83. package/dist/{chunk-OPXAP3AC.js → chunk-XN7S44U4.js} +1 -1
  84. package/dist/chunk-XN7S44U4.js.map +1 -0
  85. package/dist/{chunk-6WZODR3Z.js → chunk-XVEJAFKW.js} +5 -5
  86. package/dist/chunk-XVEJAFKW.js.map +1 -0
  87. package/dist/{chunk-BEFVJ2OE.js → chunk-XXLCMSD3.js} +1 -1
  88. package/dist/chunk-XXLCMSD3.js.map +1 -0
  89. package/dist/{chunk-YT43MUSJ.js → chunk-XZWMJLIT.js} +1 -1
  90. package/dist/chunk-XZWMJLIT.js.map +1 -0
  91. package/dist/{chunk-4NHXLXSD.js → chunk-Y3K6KJMS.js} +2 -2
  92. package/dist/chunk-Y3K6KJMS.js.map +1 -0
  93. package/dist/{chunk-RK6VBLXP.js → chunk-ZSOS2KOR.js} +1 -1
  94. package/dist/chunk-ZSOS2KOR.js.map +1 -0
  95. package/dist/{claude-code-NMRKTAP7.js → claude-code-FS42PXZO.js} +4 -4
  96. package/dist/claude-code-FS42PXZO.js.map +1 -0
  97. package/dist/{claude-code-GRWO6XBZ.js → claude-code-QBWDT5RT.js} +11 -11
  98. package/dist/claude-code-QBWDT5RT.js.map +1 -0
  99. package/dist/cli/sdk.js +9 -8
  100. package/dist/cli/sdk.js.map +1 -1
  101. package/dist/cli.js +1 -1
  102. package/dist/cli.js.map +1 -1
  103. package/dist/{cline-25KW25IN.js → cline-JKEAYWVR.js} +6 -6
  104. package/dist/cline-JKEAYWVR.js.map +1 -0
  105. package/dist/{codebuddy-ZNYQACMF.js → codebuddy-EB6EYPC3.js} +7 -7
  106. package/dist/codebuddy-EB6EYPC3.js.map +1 -0
  107. package/dist/{codebuff-VR6JUDIY.js → codebuff-5RSXFU6G.js} +6 -6
  108. package/dist/codebuff-5RSXFU6G.js.map +1 -0
  109. package/dist/{codebuff-ETYT5UBL.js → codebuff-6KWBW6CT.js} +4 -4
  110. package/dist/codebuff-6KWBW6CT.js.map +1 -0
  111. package/dist/{codex-CPNKTXG3.js → codex-ART46CDD.js} +9 -9
  112. package/dist/codex-ART46CDD.js.map +1 -0
  113. package/dist/{codex-YAORPCOD.js → codex-DKBAAUCO.js} +4 -4
  114. package/dist/codex-DKBAAUCO.js.map +1 -0
  115. package/dist/{continue-WU2IB5KS.js → continue-JHZCMSPH.js} +8 -8
  116. package/dist/continue-JHZCMSPH.js.map +1 -0
  117. package/dist/{copilot-cli-MZTAWMPW.js → copilot-cli-CLBJACQ5.js} +4 -4
  118. package/dist/copilot-cli-CLBJACQ5.js.map +1 -0
  119. package/dist/{copilot-cli-PAP377OM.js → copilot-cli-QVAU5CFM.js} +7 -7
  120. package/dist/copilot-cli-QVAU5CFM.js.map +1 -0
  121. package/dist/{crush-3AVQA4YU.js → crush-7TORBYUL.js} +7 -7
  122. package/dist/crush-7TORBYUL.js.map +1 -0
  123. package/dist/{crush-QBXLNN7T.js → crush-A5C5USRL.js} +4 -4
  124. package/dist/crush-A5C5USRL.js.map +1 -0
  125. package/dist/{cursor-DIW43TRZ.js → cursor-D5SWDVSW.js} +3 -3
  126. package/dist/cursor-D5SWDVSW.js.map +1 -0
  127. package/dist/cursor-W7I4QUFO.js +22 -0
  128. package/dist/{detect-6V4LC3MA.js → detect-JHSA3G3U.js} +4 -4
  129. package/dist/detect-JHSA3G3U.js.map +1 -0
  130. package/dist/{devin-QMEM3OFA.js → devin-SSR7S4WX.js} +7 -7
  131. package/dist/devin-SSR7S4WX.js.map +1 -0
  132. package/dist/{doctor-2YDUVSAU.js → doctor-CQJ7TZPA.js} +26 -26
  133. package/dist/doctor-CQJ7TZPA.js.map +1 -0
  134. package/dist/{droid-MOKV53J4.js → droid-45PJ7JGF.js} +7 -7
  135. package/dist/droid-45PJ7JGF.js.map +1 -0
  136. package/dist/{droid-U3ZBLQFO.js → droid-PFQ2PZSF.js} +4 -4
  137. package/dist/droid-PFQ2PZSF.js.map +1 -0
  138. package/dist/{gemini-cli-47ZHCOZR.js → gemini-cli-24PFDK7D.js} +8 -8
  139. package/dist/gemini-cli-24PFDK7D.js.map +1 -0
  140. package/dist/{gemini-cli-NMVVBO3K.js → gemini-cli-Y6RJMU24.js} +4 -4
  141. package/dist/gemini-cli-Y6RJMU24.js.map +1 -0
  142. package/dist/{goose-64V46Y2W.js → goose-K2HKUTBK.js} +4 -4
  143. package/dist/goose-K2HKUTBK.js.map +1 -0
  144. package/dist/{goose-4EI5MYTI.js → goose-U5D3LCPC.js} +9 -9
  145. package/dist/goose-U5D3LCPC.js.map +1 -0
  146. package/dist/{grok-cli-QYQPDSVB.js → grok-cli-L2MHAPBY.js} +7 -7
  147. package/dist/grok-cli-L2MHAPBY.js.map +1 -0
  148. package/dist/{hermes-F75OSK3Y.js → hermes-2S5JUTTJ.js} +7 -7
  149. package/dist/hermes-2S5JUTTJ.js.map +1 -0
  150. package/dist/{hermes-F3EGTK6N.js → hermes-MF3NX4ZY.js} +4 -4
  151. package/dist/hermes-MF3NX4ZY.js.map +1 -0
  152. package/dist/{hook-OEVRDMXD.js → hook-VHICXHXL.js} +16 -16
  153. package/dist/hook-VHICXHXL.js.map +1 -0
  154. package/dist/index.js +5 -5
  155. package/dist/{install-5NMCUDG5.js → install-WLENR4KF.js} +192 -28
  156. package/dist/install-WLENR4KF.js.map +1 -0
  157. package/dist/{jetbrains-copilot-ZGIWI6YV.js → jetbrains-copilot-Z4FC3RM3.js} +6 -6
  158. package/dist/jetbrains-copilot-Z4FC3RM3.js.map +1 -0
  159. package/dist/{junie-DIFN2P22.js → junie-ULGJJIHB.js} +6 -6
  160. package/dist/junie-ULGJJIHB.js.map +1 -0
  161. package/dist/{kilo-SW7BH6EG.js → kilo-SMHPOHJ5.js} +4 -4
  162. package/dist/kilo-SMHPOHJ5.js.map +1 -0
  163. package/dist/{kilo-EMZJBEBB.js → kilo-WWIJOVWD.js} +8 -8
  164. package/dist/kilo-WWIJOVWD.js.map +1 -0
  165. package/dist/{kilo-cli-S3GXBPAE.js → kilo-cli-QKE2EQLS.js} +8 -8
  166. package/dist/kilo-cli-QKE2EQLS.js.map +1 -0
  167. package/dist/{kilo-cli-ZF272ZHN.js → kilo-cli-T2SRRRJS.js} +5 -5
  168. package/dist/kilo-cli-T2SRRRJS.js.map +1 -0
  169. package/dist/{kimi-IQJOIY6B.js → kimi-JMUFTVSF.js} +7 -7
  170. package/dist/kimi-JMUFTVSF.js.map +1 -0
  171. package/dist/{kimi-5FHEXGZD.js → kimi-MSIDEMCI.js} +4 -4
  172. package/dist/kimi-MSIDEMCI.js.map +1 -0
  173. package/dist/{kiro-WGOI5CGT.js → kiro-NYRRBIKW.js} +4 -4
  174. package/dist/kiro-NYRRBIKW.js.map +1 -0
  175. package/dist/{kiro-BYX4HEAJ.js → kiro-UWUCRKR3.js} +7 -7
  176. package/dist/kiro-UWUCRKR3.js.map +1 -0
  177. package/dist/{leaderboard-XEKS7B3H.js → leaderboard-NJWKGMXP.js} +7 -7
  178. package/dist/leaderboard-NJWKGMXP.js.map +1 -0
  179. package/dist/{mimo-code-7R53QDHB.js → mimo-code-BPNYYIVM.js} +7 -7
  180. package/dist/mimo-code-BPNYYIVM.js.map +1 -0
  181. package/dist/{mistral-vibe-LHJ55FSB.js → mistral-vibe-LT3DAJ3L.js} +6 -6
  182. package/dist/mistral-vibe-LT3DAJ3L.js.map +1 -0
  183. package/dist/{mux-XM6AP35L.js → mux-7PIJ7HUQ.js} +4 -4
  184. package/dist/mux-7PIJ7HUQ.js.map +1 -0
  185. package/dist/{mux-NYCNGL43.js → mux-AHNUAY5Z.js} +6 -6
  186. package/dist/mux-AHNUAY5Z.js.map +1 -0
  187. package/dist/{nemoclaw-WSIPSE7U.js → nemoclaw-RE5RZASZ.js} +7 -7
  188. package/dist/nemoclaw-RE5RZASZ.js.map +1 -0
  189. package/dist/{omp-562NK5BJ.js → omp-D2EXEPH7.js} +7 -7
  190. package/dist/omp-D2EXEPH7.js.map +1 -0
  191. package/dist/{open-interpreter-I2CNOEXY.js → open-interpreter-SSGBEUKX.js} +6 -6
  192. package/dist/open-interpreter-SSGBEUKX.js.map +1 -0
  193. package/dist/openclaw-IJ33K45M.js +17 -0
  194. package/dist/{openclaw-5EQFGOWL.js → openclaw-KAFTNLR2.js} +4 -4
  195. package/dist/openclaw-KAFTNLR2.js.map +1 -0
  196. package/dist/{opencode-YS4WX2E2.js → opencode-YKUKZY3J.js} +7 -7
  197. package/dist/opencode-YKUKZY3J.js.map +1 -0
  198. package/dist/{opencode-QGP4ALQY.js → opencode-Z65JYILL.js} +4 -4
  199. package/dist/opencode-Z65JYILL.js.map +1 -0
  200. package/dist/{openhands-QMYIKNOS.js → openhands-SEZMCUC3.js} +7 -7
  201. package/dist/openhands-SEZMCUC3.js.map +1 -0
  202. package/dist/{package-G25GVTH2.js → package-JECVQEFT.js} +15 -15
  203. package/dist/package-JECVQEFT.js.map +1 -0
  204. package/dist/{pi-GGDQREVG.js → pi-DLKCPOJE.js} +4 -4
  205. package/dist/pi-DLKCPOJE.js.map +1 -0
  206. package/dist/{pi-644FIN3A.js → pi-YPYV2ULH.js} +6 -6
  207. package/dist/pi-YPYV2ULH.js.map +1 -0
  208. package/dist/{qwen-code-GQILBJ5B.js → qwen-code-26CSFDX6.js} +4 -4
  209. package/dist/qwen-code-26CSFDX6.js.map +1 -0
  210. package/dist/{qwen-code-STDMX7EM.js → qwen-code-ZHCLCYMC.js} +9 -9
  211. package/dist/qwen-code-ZHCLCYMC.js.map +1 -0
  212. package/dist/{roo-code-DLPIHHQ6.js → roo-code-7F4HYB34.js} +6 -6
  213. package/dist/roo-code-7F4HYB34.js.map +1 -0
  214. package/dist/{roo-code-SUWI4BBN.js → roo-code-HSAE2CJU.js} +4 -4
  215. package/dist/roo-code-HSAE2CJU.js.map +1 -0
  216. package/dist/runtime/index.js +14 -14
  217. package/dist/sdk/index.js +6 -6
  218. package/dist/sdk/index.js.map +1 -1
  219. package/dist/sdk/test.js +11 -11
  220. package/dist/{serve-XTMIUHKK.js → serve-LYSBH43G.js} +16 -16
  221. package/dist/serve-LYSBH43G.js.map +1 -0
  222. package/dist/{status-VJAGO4Z7.js → status-PHKKGEW5.js} +11 -11
  223. package/dist/status-PHKKGEW5.js.map +1 -0
  224. package/dist/{statusline-EY2E2UGF.js → statusline-ZDD4OZ4F.js} +15 -15
  225. package/dist/statusline-ZDD4OZ4F.js.map +1 -0
  226. package/dist/{synthetic-6FRA7P5Q.js → synthetic-LUT6V6SD.js} +4 -4
  227. package/dist/synthetic-LUT6V6SD.js.map +1 -0
  228. package/dist/{telemetry-OLZQBADT.js → telemetry-HDGMI2BK.js} +10 -10
  229. package/dist/telemetry-HDGMI2BK.js.map +1 -0
  230. package/dist/{trae-CPPYASR2.js → trae-SIQI3C66.js} +4 -4
  231. package/dist/trae-SIQI3C66.js.map +1 -0
  232. package/dist/{trae-UOYD6JZZ.js → trae-Y67HSDOW.js} +6 -6
  233. package/dist/trae-Y67HSDOW.js.map +1 -0
  234. package/dist/{uninstall-AZEUPAND.js → uninstall-RWO2GM2I.js} +21 -21
  235. package/dist/uninstall-RWO2GM2I.js.map +1 -0
  236. package/dist/{upgrade-CAUUHSSC.js → upgrade-YJCXNUEE.js} +21 -21
  237. package/dist/upgrade-YJCXNUEE.js.map +1 -0
  238. package/dist/{usage-3KHZ4RYZ.js → usage-IWO4RQN6.js} +4 -4
  239. package/dist/usage-IWO4RQN6.js.map +1 -0
  240. package/dist/{usage-event-TZWOZPRJ.js → usage-event-AFFKCLHE.js} +15 -15
  241. package/dist/usage-event-AFFKCLHE.js.map +1 -0
  242. package/dist/{vscode-copilot-NIMH2DGU.js → vscode-copilot-5CI5B3X6.js} +7 -7
  243. package/dist/vscode-copilot-5CI5B3X6.js.map +1 -0
  244. package/dist/{warp-NRBBJQR2.js → warp-5YBSP7FP.js} +4 -4
  245. package/dist/warp-5YBSP7FP.js.map +1 -0
  246. package/dist/{warp-QYJGKURM.js → warp-INAKVGBV.js} +6 -6
  247. package/dist/warp-INAKVGBV.js.map +1 -0
  248. package/dist/{windsurf-75LLZ37C.js → windsurf-4HM57QAY.js} +6 -6
  249. package/dist/windsurf-4HM57QAY.js.map +1 -0
  250. package/dist/{zed-XJFOOKCO.js → zed-XXCNYOIN.js} +4 -4
  251. package/dist/zed-XXCNYOIN.js.map +1 -0
  252. package/dist/{zed-HKAZXEJW.js → zed-YN5X37PQ.js} +7 -7
  253. package/dist/zed-YN5X37PQ.js.map +1 -0
  254. package/package.json +92 -88
  255. package/dist/action-Q7GGO4PA.js.map +0 -1
  256. package/dist/amazon-q-ETMAGY3D.js.map +0 -1
  257. package/dist/amp-67BZONGF.js.map +0 -1
  258. package/dist/amp-BQZOL5GR.js.map +0 -1
  259. package/dist/antigravity-EA4L225L.js.map +0 -1
  260. package/dist/antigravity-VXLDLTO7.js +0 -18
  261. package/dist/antigravity-cli-B2HXZFYQ.js.map +0 -1
  262. package/dist/antigravity-cli-OM6SC3PE.js.map +0 -1
  263. package/dist/chunk-2Y2MZRLP.js.map +0 -1
  264. package/dist/chunk-3NH2ARIS.js.map +0 -1
  265. package/dist/chunk-4NHXLXSD.js.map +0 -1
  266. package/dist/chunk-5IOTP7LQ.js.map +0 -1
  267. package/dist/chunk-6TF7MHG5.js.map +0 -1
  268. package/dist/chunk-6WZODR3Z.js.map +0 -1
  269. package/dist/chunk-7F5KHLEP.js.map +0 -1
  270. package/dist/chunk-APZDTY76.js.map +0 -1
  271. package/dist/chunk-BEFVJ2OE.js.map +0 -1
  272. package/dist/chunk-D4ELPBB5.js.map +0 -1
  273. package/dist/chunk-FBJQGRET.js.map +0 -1
  274. package/dist/chunk-FJCUKXGX.js.map +0 -1
  275. package/dist/chunk-GZGLCCEE.js.map +0 -1
  276. package/dist/chunk-H33M77DD.js.map +0 -1
  277. package/dist/chunk-H53QZQJI.js.map +0 -1
  278. package/dist/chunk-I5UXHIDT.js.map +0 -1
  279. package/dist/chunk-ISEJWBFF.js.map +0 -1
  280. package/dist/chunk-IVAWP6HH.js.map +0 -1
  281. package/dist/chunk-JKKU2WKG.js.map +0 -1
  282. package/dist/chunk-KHO5WNTP.js.map +0 -1
  283. package/dist/chunk-MGTDQLJJ.js.map +0 -1
  284. package/dist/chunk-MQPNU2CY.js.map +0 -1
  285. package/dist/chunk-NBYKLUF6.js.map +0 -1
  286. package/dist/chunk-NLE2J5P4.js.map +0 -1
  287. package/dist/chunk-O7SDRBVB.js.map +0 -1
  288. package/dist/chunk-OP4KBXUO.js.map +0 -1
  289. package/dist/chunk-OPXAP3AC.js.map +0 -1
  290. package/dist/chunk-PAPMUAR5.js.map +0 -1
  291. package/dist/chunk-PW42ZXCB.js.map +0 -1
  292. package/dist/chunk-RK6VBLXP.js.map +0 -1
  293. package/dist/chunk-UM5S7DKK.js.map +0 -1
  294. package/dist/chunk-UMQ6NBQR.js.map +0 -1
  295. package/dist/chunk-XC5SJ35F.js.map +0 -1
  296. package/dist/chunk-XD7MY6HB.js.map +0 -1
  297. package/dist/chunk-YPAXJ7EW.js.map +0 -1
  298. package/dist/chunk-YT43MUSJ.js.map +0 -1
  299. package/dist/chunk-Z2MRSS2I.js.map +0 -1
  300. package/dist/claude-code-GRWO6XBZ.js.map +0 -1
  301. package/dist/claude-code-NMRKTAP7.js.map +0 -1
  302. package/dist/cline-25KW25IN.js.map +0 -1
  303. package/dist/codebuddy-ZNYQACMF.js.map +0 -1
  304. package/dist/codebuff-ETYT5UBL.js.map +0 -1
  305. package/dist/codebuff-VR6JUDIY.js.map +0 -1
  306. package/dist/codex-CPNKTXG3.js.map +0 -1
  307. package/dist/codex-YAORPCOD.js.map +0 -1
  308. package/dist/continue-WU2IB5KS.js.map +0 -1
  309. package/dist/copilot-cli-MZTAWMPW.js.map +0 -1
  310. package/dist/copilot-cli-PAP377OM.js.map +0 -1
  311. package/dist/crush-3AVQA4YU.js.map +0 -1
  312. package/dist/crush-QBXLNN7T.js.map +0 -1
  313. package/dist/cursor-DIW43TRZ.js.map +0 -1
  314. package/dist/cursor-GSLUET3V.js +0 -22
  315. package/dist/detect-6V4LC3MA.js.map +0 -1
  316. package/dist/devin-QMEM3OFA.js.map +0 -1
  317. package/dist/doctor-2YDUVSAU.js.map +0 -1
  318. package/dist/droid-MOKV53J4.js.map +0 -1
  319. package/dist/droid-U3ZBLQFO.js.map +0 -1
  320. package/dist/gemini-cli-47ZHCOZR.js.map +0 -1
  321. package/dist/gemini-cli-NMVVBO3K.js.map +0 -1
  322. package/dist/goose-4EI5MYTI.js.map +0 -1
  323. package/dist/goose-64V46Y2W.js.map +0 -1
  324. package/dist/grok-cli-QYQPDSVB.js.map +0 -1
  325. package/dist/hermes-F3EGTK6N.js.map +0 -1
  326. package/dist/hermes-F75OSK3Y.js.map +0 -1
  327. package/dist/hook-OEVRDMXD.js.map +0 -1
  328. package/dist/install-5NMCUDG5.js.map +0 -1
  329. package/dist/jetbrains-copilot-ZGIWI6YV.js.map +0 -1
  330. package/dist/junie-DIFN2P22.js.map +0 -1
  331. package/dist/kilo-EMZJBEBB.js.map +0 -1
  332. package/dist/kilo-SW7BH6EG.js.map +0 -1
  333. package/dist/kilo-cli-S3GXBPAE.js.map +0 -1
  334. package/dist/kilo-cli-ZF272ZHN.js.map +0 -1
  335. package/dist/kimi-5FHEXGZD.js.map +0 -1
  336. package/dist/kimi-IQJOIY6B.js.map +0 -1
  337. package/dist/kiro-BYX4HEAJ.js.map +0 -1
  338. package/dist/kiro-WGOI5CGT.js.map +0 -1
  339. package/dist/leaderboard-XEKS7B3H.js.map +0 -1
  340. package/dist/mimo-code-7R53QDHB.js.map +0 -1
  341. package/dist/mistral-vibe-LHJ55FSB.js.map +0 -1
  342. package/dist/mux-NYCNGL43.js.map +0 -1
  343. package/dist/mux-XM6AP35L.js.map +0 -1
  344. package/dist/nemoclaw-WSIPSE7U.js.map +0 -1
  345. package/dist/omp-562NK5BJ.js.map +0 -1
  346. package/dist/open-interpreter-I2CNOEXY.js.map +0 -1
  347. package/dist/openclaw-5EQFGOWL.js.map +0 -1
  348. package/dist/openclaw-7EYBQS4R.js +0 -17
  349. package/dist/opencode-QGP4ALQY.js.map +0 -1
  350. package/dist/opencode-YS4WX2E2.js.map +0 -1
  351. package/dist/openhands-QMYIKNOS.js.map +0 -1
  352. package/dist/package-G25GVTH2.js.map +0 -1
  353. package/dist/pi-644FIN3A.js.map +0 -1
  354. package/dist/pi-GGDQREVG.js.map +0 -1
  355. package/dist/qwen-code-GQILBJ5B.js.map +0 -1
  356. package/dist/qwen-code-STDMX7EM.js.map +0 -1
  357. package/dist/roo-code-DLPIHHQ6.js.map +0 -1
  358. package/dist/roo-code-SUWI4BBN.js.map +0 -1
  359. package/dist/serve-XTMIUHKK.js.map +0 -1
  360. package/dist/status-VJAGO4Z7.js.map +0 -1
  361. package/dist/statusline-EY2E2UGF.js.map +0 -1
  362. package/dist/synthetic-6FRA7P5Q.js.map +0 -1
  363. package/dist/telemetry-OLZQBADT.js.map +0 -1
  364. package/dist/trae-CPPYASR2.js.map +0 -1
  365. package/dist/trae-UOYD6JZZ.js.map +0 -1
  366. package/dist/uninstall-AZEUPAND.js.map +0 -1
  367. package/dist/upgrade-CAUUHSSC.js.map +0 -1
  368. package/dist/usage-3KHZ4RYZ.js.map +0 -1
  369. package/dist/usage-event-TZWOZPRJ.js.map +0 -1
  370. package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
  371. package/dist/warp-NRBBJQR2.js.map +0 -1
  372. package/dist/warp-QYJGKURM.js.map +0 -1
  373. package/dist/windsurf-75LLZ37C.js.map +0 -1
  374. package/dist/zed-HKAZXEJW.js.map +0 -1
  375. package/dist/zed-XJFOOKCO.js.map +0 -1
  376. /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-TWIWXTQH.js.map} +0 -0
  377. /package/dist/{cursor-GSLUET3V.js.map → cursor-W7I4QUFO.js.map} +0 -0
  378. /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-IJ33K45M.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/crush/index.ts"],"sourcesContent":["/**\r\n * adapters/crush — Crush (Charm) platform adapter for agent-connector.\r\n *\r\n * Crush is a json-stdio host. A single JSON config file holds BOTH the MCP\r\n * server registrations and the hook registrations, and agent-connector MERGES\r\n * into it (never replaces it — unrelated keys are preserved verbatim):\r\n * - User scope: ~/.config/crush/crush.json\r\n * (Windows: %LOCALAPPDATA%\\crush\\crush.json)\r\n * - Project scope: ./.crush.json (preferred) or ./crush.json\r\n *\r\n * MCP servers live under the ROOT KEY \"mcp\" (NOT \"mcpServers\"). A stdio entry is\r\n * { type:\"stdio\", command, args, env, timeout:120, disabled:false }.\r\n * Hooks live under the top-level \"hooks\" key. Crush supports PreToolUse ONLY;\r\n * the hook reads the host's JSON on stdin and writes a JSON object on stdout\r\n * carrying a `decision` field. A deny is `{ decision:\"deny\", reason }` on\r\n * stdout (exit 0); an allow is an empty stdout (exit 0). Crush cannot rewrite\r\n * tool args/output nor inject session context — every non-deny decision (and\r\n * every non-PreToolUse event) degrades to a silent allow.\r\n *\r\n * SECURITY NOTE: Crush expands `$(...)` command substitution in its config file\r\n * at load time. We therefore NEVER emit shell-substitution syntax into the\r\n * command/args/env we write — every value is a literal path/string built by the\r\n * spawn helpers (which quote, not interpolate). Connector ids are kebab-case and\r\n * the home-bin path is an absolute literal, so nothing we write can be coerced\r\n * into a `$(...)` substitution.\r\n */\r\n\r\nimport { existsSync, statSync } from \"node:fs\";\r\nimport { dirname, join } from \"node:path\";\r\n\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PreToolUseEvent,\r\n ServerDef,\r\n Transport,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport { localAppData, xdgConfigHome } from \"../../core/host-paths.js\";\r\nimport {\r\n buildHomeBinHookCommand,\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd } from \"../claude-code/render.js\";\r\n\r\nconst HOST: PlatformId = \"crush\";\r\n/** Crush registers MCP servers under \"mcp\" — NOT the \"mcpServers\" of Claude/Gemini. */\r\nconst MCP_ROOT_KEY = \"mcp\";\r\n/** Default per-server timeout (seconds) Crush expects on a stdio entry. */\r\nconst STDIO_TIMEOUT_SECONDS = 120;\r\n\r\n/**\r\n * Crush hook events agent-connector can register. Crush honors PreToolUse ONLY,\r\n * and only its deny decision is meaningful; every other normalized event has no\r\n * Crush equivalent and is skipped at install time.\r\n */\r\nconst CRUSH_HOOK_EVENTS = [\"PreToolUse\"] as const;\r\ntype CrushHookEventName = (typeof CRUSH_HOOK_EVENTS)[number];\r\n\r\n/**\r\n * Newer E1 canonical events with NO Crush analog (no permission-dialog,\r\n * tool-failure, or subagent lifecycle hook). Declared hooks for these warn-skip\r\n * (action `warn`, exit-1) — the established degradation convention. EVERY OTHER\r\n * unsupported event (the legacy SessionStart / Stop / … that previously vanished)\r\n * now reports a VISIBLE `skip` instead — see {@link CrushAdapter.declinedHookEvents}.\r\n * Nothing Crush cannot fire is ever silently dropped.\r\n */\r\nconst WARN_SKIP_EVENTS: ReadonlySet<HookEventName> = new Set([\r\n \"PermissionRequest\",\r\n \"PostToolUseFailure\",\r\n \"SubagentStart\",\r\n \"SubagentStop\",\r\n]);\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Native config shapes (only the parts we touch; rest preserved verbatim)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** One hook registration entry as Crush stores it under top-level \"hooks\". */\r\ninterface CrushHookEntry {\r\n matcher?: string;\r\n command: string;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Crush's crush.json shape — only the keys we read/merge. */\r\ninterface CrushConfigFile {\r\n mcp?: Record<string, unknown>;\r\n hooks?: Record<string, CrushHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Native stdio MCP server entry under \"mcp\". */\r\ninterface CrushStdioServer {\r\n type: \"stdio\";\r\n command: string;\r\n args: string[];\r\n env?: Record<string, string>;\r\n timeout: number;\r\n disabled: boolean;\r\n}\r\n\r\n/** Native HTTP MCP server entry under \"mcp\". */\r\ninterface CrushHttpServer {\r\n type: \"http\";\r\n url: string;\r\n headers?: Record<string, string>;\r\n timeout: number;\r\n disabled: boolean;\r\n}\r\n\r\n/** Raw Crush hook stdin payload (Claude-style: PascalCase event, snake_case fields). */\r\ninterface CrushHookInput {\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n session_id?: string;\r\n cwd?: string;\r\n hook_event_name?: string;\r\n connector?: unknown;\r\n}\r\n\r\nexport class CrushAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Crush\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\r\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\r\n supportsMemory: true,\r\n // PreToolUse deny is the only hook decision Crush honors.\r\n preToolUse: true,\r\n postToolUse: false,\r\n preCompact: false,\r\n sessionStart: false,\r\n sessionEnd: false,\r\n userPromptSubmit: false,\r\n stop: false,\r\n notification: false,\r\n // Newer events: Crush honors PreToolUse ONLY — no permission-dialog,\r\n // tool-failure, or subagent lifecycle events exist, so permissionRequest /\r\n // postToolUseFailure / subagentStart / subagentStop stay unset — install\r\n // reports the standard skip-warn for them.\r\n // Crush is deny-only: it cannot rewrite tool args/output nor inject context.\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n canInjectSessionContext: false,\r\n transports: [\"stdio\", \"http\"],\r\n // Content surface: Crush auto-discovers Agent-Skills dirs (dir-per-skill\r\n // SKILL.md). Paths are HARD-CODED — no crush.json entry needed\r\n // (charmbracelet/crush internal/config/load.go GlobalSkillsDirs +\r\n // ProjectSkillsDir, fetched 2026-06-16):\r\n // project → <projectDir>/.crush/skills/<name>/SKILL.md (crush-native, in\r\n // projectSkillSubdirs; preferred over .agents/.claude/.cursor)\r\n // user → ~/.config/crush/skills/<name>/SKILL.md (home.Config()/crush/\r\n // skills; XDG_CONFIG_HOME or ~/.config).\r\n supportsSkills: true,\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = this.userConfigDir();\r\n const userConfig = join(userDir, \"crush.json\");\r\n const projectDot = join(projectDir, \".crush.json\");\r\n const projectBare = join(projectDir, \"crush.json\");\r\n const userInstalled = existsSync(userDir) || existsSync(userConfig);\r\n const projInstalled = existsSync(projectDot) || existsSync(projectBare);\r\n const installed = userInstalled || projInstalled;\r\n // Report the scope/path that actually matched so a project-only install is\r\n // not misreported as a (non-existent) user install.\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const projectPath = existsSync(projectBare) && !existsSync(projectDot) ? projectBare : projectDot;\r\n const configPath = scope === \"project\" ? projectPath : userConfig;\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? scope === \"project\"\r\n ? `found project Crush config at ${configPath}`\r\n : `found Crush config under ${userDir}`\r\n : `no Crush config at ${userConfig}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\" ? ctx.projectDir : this.userConfigDir();\r\n }\r\n\r\n /** MCP servers and hooks share ONE crush.json file. */\r\n getServerConfigPath(ctx: InstallContext): string {\r\n if (ctx.scope === \"project\") {\r\n // Prefer ./.crush.json, but MERGE into an existing ./crush.json if that is\r\n // the file already present (so we don't fork the user's config in two).\r\n const dot = join(ctx.projectDir, \".crush.json\");\r\n const bare = join(ctx.projectDir, \"crush.json\");\r\n if (existsSync(bare) && !existsSync(dot)) return bare;\r\n return dot;\r\n }\r\n return join(this.userConfigDir(), \"crush.json\");\r\n }\r\n\r\n /** Hooks live in the SAME crush.json under the top-level \"hooks\" key. */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return this.getServerConfigPath(ctx);\r\n }\r\n\r\n /**\r\n * Crush's user config dir, mirroring its own `home.Config()` resolution\r\n * (charmbracelet/crush internal/home/home.go: `cmp.Or(XDG_CONFIG_HOME, ~/.config)`):\r\n * - Windows: %LOCALAPPDATA%\\crush (Local; falls back to AppData/Local)\r\n * - macOS / Linux: $XDG_CONFIG_HOME/crush or ~/.config/crush\r\n * (crush honors $XDG_CONFIG_HOME on Linux/macOS; the `xdgConfigHome()` helper\r\n * is byte-identical to ~/.config when XDG is unset.)\r\n */\r\n private userConfigDir(): string {\r\n if (process.platform === \"win32\") {\r\n return join(localAppData(), \"crush\");\r\n }\r\n return join(xdgConfigHome(), \"crush\");\r\n }\r\n\r\n // ── MCP server install / uninstall (crush.json → mcp.<id>) ───────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? \"server registration disabled for crush\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /**\r\n * Render a normalized ServerDef into Crush's native \"mcp\" entry. Crush has no\r\n * documented native `${env:VAR}` interpolation — and it expands `$(...)` at\r\n * load time — so refs are resolved to LITERALS at install time (the safe\r\n * posture for a host without native interpolation, matching Codex/Kimi).\r\n */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): CrushStdioServer | CrushHttpServer {\r\n const transport: Transport = server.transport;\r\n const disabled = server.enabled === false;\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n // Resolve to literals — never emit `$(...)` Crush would expand at load.\r\n command = resolveEnvRefsDeep(command);\r\n args = resolveEnvRefsDeep(args);\r\n\r\n const entry: CrushStdioServer = {\r\n type: \"stdio\",\r\n command,\r\n args,\r\n timeout: STDIO_TIMEOUT_SECONDS,\r\n disabled,\r\n };\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http (and any other remote transport we surface) — Crush registers a URL.\r\n const entry: CrushHttpServer = {\r\n type: \"http\",\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n timeout: STDIO_TIMEOUT_SECONDS,\r\n disabled,\r\n };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /** Resolve env/header values to literals (no native interpolation on Crush). */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(resolveEnvRefsDeep({ ...env }))) {\r\n out[k] = String(v);\r\n }\r\n return out;\r\n }\r\n\r\n // ── Hook install / uninstall (crush.json → hooks.PreToolUse[]) ───────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"hooks disabled for crush\" }];\r\n }\r\n\r\n const events = this.effectiveHookEvents(ctx);\r\n const declined = this.declinedHookEvents(ctx);\r\n const path = this.getHookConfigPath(ctx);\r\n\r\n if (events.length === 0 && declined.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", path, detail: \"no hooks declared\" }];\r\n }\r\n\r\n const cfg = this.readJson<CrushConfigFile>(path) ?? {};\r\n const __skip = this.malformedHookRootSkip(path, (cfg as Record<string, unknown>).hooks);\r\n if (__skip) return [__skip];\r\n const hooks = (cfg.hooks ??= {});\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n // EVERY declared event Crush cannot fire is reported, never silently dropped.\r\n // The 4 newer E1 events keep the established `warn` (exit-1); the legacy\r\n // events (SessionStart, Stop, …) — silently dropped before this fix — surface\r\n // as a VISIBLE `skip` so the install exit code is unchanged for them.\r\n for (const { event, action } of declined) {\r\n changes.push({\r\n platform: this.id,\r\n action,\r\n path,\r\n detail: `${event} has no Crush hook equivalent — skipped`,\r\n });\r\n }\r\n\r\n for (const event of events) {\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);\r\n const matcher = connector.hooks[event]?.matcher ?? \"\";\r\n const entry: CrushHookEntry = { matcher, command };\r\n\r\n const bucket = (hooks[event] ??= []);\r\n const existingIdx = bucket.findIndex((e) => this.isOurHook(e, ctx));\r\n\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `hooks.${event} already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({ platform: this.id, action: \"update\", path, detail: `hooks.${event}` });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({ platform: this.id, action: \"create\", path, detail: `hooks.${event}` });\r\n }\r\n mutated = true;\r\n }\r\n\r\n if (mutated) this.writeJson(path, cfg, ctx.dryRun);\r\n return changes;\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const path = this.getHookConfigPath(ctx);\r\n const cfg = this.readJson<CrushConfigFile>(path);\r\n const hooks = cfg?.hooks;\r\n if (!cfg || !hooks) {\r\n return [\r\n { platform: this.id, action: \"skip\", path, detail: \"no hooks section present\" },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const event of Object.keys(hooks)) {\r\n const bucket = hooks[event];\r\n if (!Array.isArray(bucket)) continue;\r\n\r\n // Keep every entry that is NOT ours (anchored match) so a shared-prefix\r\n // connector id — or the user's own hook — is never stripped.\r\n const kept = bucket.filter((e) => !this.isOurHook(e, ctx));\r\n const removed = bucket.length - kept.length;\r\n if (removed > 0) {\r\n if (kept.length > 0) hooks[event] = kept;\r\n else delete hooks[event];\r\n changes.push({\r\n platform: this.id,\r\n action: \"remove\",\r\n path,\r\n detail: `hooks.${event} (${removed})`,\r\n });\r\n mutated = true;\r\n }\r\n }\r\n\r\n if (mutated) this.writeJson(path, cfg, ctx.dryRun);\r\n if (changes.length === 0) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: \"no matching hook entries\",\r\n });\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Internal helpers ─────────────────────────────────────────────────────\r\n\r\n /** Which canonical hook events to register for Crush, honoring overrides. */\r\n private effectiveHookEvents(ctx: InstallContext): CrushHookEventName[] {\r\n if (ctx.connector.platforms[HOST]?.hooks === false) return [];\r\n return CRUSH_HOOK_EVENTS.filter((e) => ctx.connector.hookEvents.includes(e));\r\n }\r\n\r\n /**\r\n * Every declared event Crush has NO native equivalent for, tagged with the\r\n * severity install reports it at — so a host that cannot fire an event is\r\n * NEVER silent. The 4 newer E1 events ({@link WARN_SKIP_EVENTS}) keep `warn`\r\n * (exit-1, the established convention); every other unsupported event (the\r\n * legacy SessionStart / Stop / … that used to vanish) reports a visible\r\n * `skip` (counted, but exit-0-preserving).\r\n */\r\n private declinedHookEvents(\r\n ctx: InstallContext,\r\n ): { event: HookEventName; action: \"warn\" | \"skip\" }[] {\r\n if (ctx.connector.platforms[HOST]?.hooks === false) return [];\r\n return ctx.connector.hookEvents\r\n .filter((e) => !(CRUSH_HOOK_EVENTS as readonly string[]).includes(e))\r\n .map((event) => ({\r\n event,\r\n action: WARN_SKIP_EVENTS.has(event) ? (\"warn\" as const) : (\"skip\" as const),\r\n }));\r\n }\r\n\r\n /**\r\n * Does this hook entry belong to this connector? Anchored on the home-bin +\r\n * connector-id token (isHomeBinHookCommand) so uninstalling a shared-prefix\r\n * connector id never strips a sibling's hook.\r\n */\r\n private isOurHook(entry: CrushHookEntry | undefined, ctx: InstallContext): boolean {\r\n if (!entry || typeof entry !== \"object\") return false;\r\n return isHomeBinHookCommand(entry.command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Content surface: skills (dir-per-skill SKILL.md) ─────────────────────\r\n // CONTENT-ONLY: pure native-file writers, mirroring goose/openclaw. No\r\n // runtime dispatch, no home-bin pointer, no telemetry wrap. Each write is\r\n // idempotent (byte-identical → skip) via writeContentFile and reversible via\r\n // removeContentFile + removeDirIfEmpty (never rm -rf a shared dir). Honors\r\n // platforms[\"crush\"].skills === false. No crush.json entry is written — Crush\r\n // auto-discovers these hard-coded dirs (load.go GlobalSkillsDirs/ProjectSkillsDir).\r\n //\r\n // Native locations (Agent-Skills spec — dir-per-skill SKILL.md + resources):\r\n // project → <projectDir>/.crush/skills/<name>/SKILL.md\r\n // user → ~/.config/crush/skills/<name>/SKILL.md\r\n\r\n /** The skills ROOT dir for ctx.scope (parent of every per-skill dir). */\r\n private skillsDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".crush\", \"skills\")\r\n : join(this.userConfigDir(), \"skills\");\r\n }\r\n\r\n /** Per-skill directory: <skillsDir>/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.skillsDir(ctx), name);\r\n }\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for crush\" }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Guard a skills path occupied by a regular FILE (would throw ENOTDIR on\r\n // mkdir): skip-warn rather than crash (the cline `.clinerules`-is-a-file\r\n // precedent). The per-skill dir or any parent up to the scope root counts.\r\n const blockingFile = firstFileOnPath(this.skillsDir(ctx), dir);\r\n if (blockingFile !== null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: blockingFile,\r\n detail: `${blockingFile} is a file, not a directory; skill \"${skill.name}\" skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\r\n // (config-time validation already rejects these, but never trust input).\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const configPath = this.getServerConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = this.effectiveHookEvents(ctx);\r\n return [\r\n {\r\n name: `${this.name}: crush.json present`,\r\n check: () =>\r\n existsSync(configPath)\r\n ? { status: \"OK\", detail: configPath }\r\n : { status: \"FAIL\", detail: `not found: ${configPath}` },\r\n },\r\n {\r\n name: `${this.name}: mcp.${connectorId} registered`,\r\n check: () => {\r\n if (!ctx.connector.server) return { status: \"OK\", detail: \"no MCP server declared\" };\r\n const cfg = this.readJson<CrushConfigFile>(configPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n const present =\r\n typeof bucket === \"object\" && bucket !== null && connectorId in bucket;\r\n return present\r\n ? { status: \"OK\", detail: `mcp.${connectorId}` }\r\n : { status: \"FAIL\", detail: `mcp.${connectorId} not found in ${configPath}` };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const cfg = this.readJson<CrushConfigFile>(configPath);\r\n if (!cfg) return { status: \"FAIL\", detail: `cannot read ${configPath}` };\r\n const hooks = cfg.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) => isHomeBinHookCommand(e?.command, homeBin, connectorId)),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${configPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Runtime: parse Crush stdin JSON → normalized event ───────────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as CrushHookInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = typeof input.session_id === \"string\" ? input.session_id : \"\";\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n if (event === \"PreToolUse\") {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n\r\n // Crush only ever delivers PreToolUse. Surface anything else loudly rather\r\n // than silently mis-parsing a payload the host cannot actually produce.\r\n throw new Error(`unsupported crush hook event: ${String(event)}`);\r\n }\r\n\r\n // ── Runtime: normalized response → Crush native hook reply ───────────────\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // deny → Crush blocks the pending tool call via a stdout JSON object with a\r\n // `decision:\"deny\"` (+ reason). Only PreToolUse is honored; every other\r\n // event/decision degrades to a silent allow.\r\n if (decision === \"deny\" && event === \"PreToolUse\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n\r\n // allow / modify / context / ask / unsupported → pass through (exit 0, empty\r\n // stdout). Crush cannot rewrite args/output or inject context, so those are\r\n // dropped fail-open here (the deny path above is the only fail-safe lever).\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\n/**\r\n * Walk every path segment from `root` down to (and including) `leaf` and return\r\n * the FIRST one that exists as a regular file (which would make a mkdir for the\r\n * skill dir throw ENOTDIR), else null. Used to skip-warn instead of crashing\r\n * when a user has a `.crush/skills` file where we need a directory.\r\n */\r\nfunction firstFileOnPath(root: string, leaf: string): string | null {\r\n const segments: string[] = [];\r\n let cur = leaf;\r\n while (cur.length >= root.length) {\r\n segments.unshift(cur);\r\n if (cur === root) break;\r\n const parent = dirname(cur);\r\n if (parent === cur) break;\r\n cur = parent;\r\n }\r\n for (const p of segments) {\r\n if (!existsSync(p)) return null; // nothing planted from here down\r\n try {\r\n if (statSync(p).isFile()) return p;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nexport const adapter = new CrushAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  openSqlite
4
- } from "./chunk-YT43MUSJ.js";
4
+ } from "./chunk-XZWMJLIT.js";
5
5
  import {
6
6
  firstExistingRoot
7
- } from "./chunk-O7SDRBVB.js";
7
+ } from "./chunk-KM2YXO6V.js";
8
8
  import {
9
9
  emptyTokens
10
- } from "./chunk-XD7MY6HB.js";
10
+ } from "./chunk-7PSNXUFK.js";
11
11
 
12
12
  // src/usage/readers/crush.ts
13
13
  var PLATFORM_ID = "crush";
@@ -180,4 +180,4 @@ var crush_default = crushReader;
180
180
  export {
181
181
  crush_default as default
182
182
  };
183
- //# sourceMappingURL=crush-QBXLNN7T.js.map
183
+ //# sourceMappingURL=crush-A5C5USRL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/readers/crush.ts"],"sourcesContent":["/**\r\n * usage/readers/crush — Crush CLI usage reader (SQLite, cost-only).\r\n *\r\n * Faithful port of tokscale sessions/crush.rs. Crush persists usage in a single\r\n * SQLite database at ~/.cache/crush/crush.db (the host root, incl. the\r\n * AGENT_CONNECTOR_CRUSH_DIR override, is resolved by paths.ts hostRoots(\"crush\")).\r\n *\r\n * Crush stores reliable session-level COST but NOT a stable per-message token\r\n * breakdown. Tokscale v1 therefore preserves cost and assistant-message counts\r\n * without fabricating token precision:\r\n * - assistant messages (across the whole session tree) are grouped by LOCAL day;\r\n * - the root session's cost is allocated across those day buckets in proportion\r\n * to each bucket's assistant-message count (the last bucket absorbs the\r\n * rounding remainder so the per-day costs sum back to the stored total);\r\n * - all five token dimensions stay 0;\r\n * - confidence is \"host-estimated\" (cost is real, tokens are not reported).\r\n * A costed root session with no assistant messages emits a single zero-count,\r\n * cost-bearing record at its updated_at (else created_at) timestamp.\r\n *\r\n * Two SELECTs:\r\n * 1. root sessions — SELECT id, cost, created_at, updated_at FROM sessions\r\n * WHERE parent_session_id IS NULL\r\n * AND (COALESCE(message_count,0) > 0 OR COALESCE(cost,0) > 0)\r\n * ORDER BY created_at ASC;\r\n * 2. assistant message timestamps, attributed to their ROOT session via a\r\n * RECURSIVE CTE over parent_session_id (so a descendant session's assistant\r\n * messages count toward its root), filtered to role = 'assistant'.\r\n *\r\n * Timestamps tolerate both second-precision (auto ×1000) and millisecond-precision\r\n * values; non-positive timestamps are dropped. Model/provider are the constants\r\n * \"session-total\" / \"crush\". Session id = \"<db_path>:<root_session_id>\". No\r\n * project/workspace in the schema.\r\n *\r\n * Fail-open: db missing/locked/unreadable → openSqlite returns null → []; no root\r\n * sessions → []; a bad row is skipped, never thrown.\r\n */\r\n\r\nimport type { TokenBreakdown, UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { firstExistingRoot } from \"../paths.js\";\r\nimport { openSqlite } from \"../sqlite.js\";\r\n\r\nconst PLATFORM_ID = \"crush\" as const;\r\nconst CRUSH_MODEL_ID = \"session-total\";\r\nconst CRUSH_PROVIDER_ID = \"crush\";\r\n\r\n/** Root session row (cost lives here; tokens are not trustworthy per-message). */\r\ninterface CrushSession {\r\n id: string;\r\n cost: number;\r\n createdAt: number;\r\n updatedAt: number;\r\n}\r\n\r\n/** One local-day bucket of assistant messages within a root session. */\r\ninterface DayBucket {\r\n timestampMs: number;\r\n messageCount: number;\r\n}\r\n\r\nconst ROOT_SESSIONS_QUERY = `\r\n SELECT id, cost, created_at, updated_at\r\n FROM sessions\r\n WHERE parent_session_id IS NULL\r\n AND (COALESCE(message_count, 0) > 0 OR COALESCE(cost, 0) > 0)\r\n ORDER BY created_at ASC\r\n`;\r\n\r\nconst ASSISTANT_BUCKETS_QUERY = `\r\n WITH RECURSIVE session_tree(root_session_id, session_id) AS (\r\n SELECT id, id\r\n FROM sessions\r\n WHERE parent_session_id IS NULL\r\n\r\n UNION ALL\r\n\r\n SELECT st.root_session_id, s.id\r\n FROM sessions s\r\n JOIN session_tree st ON s.parent_session_id = st.session_id\r\n )\r\n SELECT st.root_session_id, m.created_at\r\n FROM session_tree st\r\n JOIN messages m ON m.session_id = st.session_id\r\n WHERE m.role = 'assistant'\r\n ORDER BY st.root_session_id ASC, m.created_at ASC\r\n`;\r\n\r\n/** Coerce an unknown SQLite cell to a finite number, or null when absent/garbage. */\r\nfunction toNumberOrNull(v: unknown): number | null {\r\n if (v === null || v === undefined) return null;\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n return Number.isFinite(n) ? n : null;\r\n}\r\n\r\n/** Coerce an unknown SQLite cell to an integer (0 on absence/garbage). */\r\nfunction toInt(v: unknown): number {\r\n const n = toNumberOrNull(v);\r\n return n === null ? 0 : Math.trunc(n);\r\n}\r\n\r\n/**\r\n * Normalize a raw Crush timestamp to epoch ms (port of normalize_crush_timestamp_ms):\r\n * non-positive → null; values >= 100_000_000_000 are already ms; smaller values are\r\n * seconds and are multiplied by 1000.\r\n */\r\nfunction normalizeCrushTimestampMs(raw: number): number | null {\r\n if (raw <= 0) return null;\r\n if (raw >= 100_000_000_000) return raw;\r\n return raw * 1000;\r\n}\r\n\r\n/**\r\n * Local-day key \"YYYY-MM-DD\" for an epoch-ms timestamp (port of local_day_key):\r\n * uses the host LOCAL timezone (matches chrono::Local), so buckets follow the\r\n * machine's calendar day. Returns null when the timestamp is not a valid date.\r\n */\r\nfunction localDayKey(timestampMs: number): string | null {\r\n const d = new Date(timestampMs);\r\n if (Number.isNaN(d.getTime())) return null;\r\n const year = d.getFullYear();\r\n const month = d.getMonth() + 1;\r\n const day = d.getDate();\r\n return `${year.toString().padStart(4, \"0\")}-${month\r\n .toString()\r\n .padStart(2, \"0\")}-${day.toString().padStart(2, \"0\")}`;\r\n}\r\n\r\n/**\r\n * Fallback session timestamp (port of fallback_session_timestamp_ms): updated_at,\r\n * else created_at, each run through the s↔ms normalization.\r\n */\r\nfunction fallbackSessionTimestampMs(updatedAt: number, createdAt: number): number | null {\r\n return normalizeCrushTimestampMs(updatedAt) ?? normalizeCrushTimestampMs(createdAt);\r\n}\r\n\r\n/** Load root sessions carrying a message count or cost (port of load_root_sessions). */\r\nfunction loadRootSessions(db: { all(sql: string): Array<Record<string, unknown>> }): CrushSession[] {\r\n const out: CrushSession[] = [];\r\n for (const row of db.all(ROOT_SESSIONS_QUERY)) {\r\n const id = row.id;\r\n if (typeof id !== \"string\") continue;\r\n out.push({\r\n id,\r\n cost: toNumberOrNull(row.cost) ?? 0,\r\n createdAt: toInt(row.created_at),\r\n updatedAt: toInt(row.updated_at),\r\n });\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Bucket assistant messages by root session + local day (port of\r\n * load_assistant_buckets). The recursive CTE attributes every descendant\r\n * session's assistant messages to its root; within a root the per-day bucket\r\n * keeps the MIN timestamp and counts the messages, and buckets are emitted in\r\n * ascending day order (BTreeMap → insertion-ordered Map keyed by day).\r\n */\r\nfunction loadAssistantBuckets(\r\n db: { all(sql: string): Array<Record<string, unknown>> },\r\n): Map<string, DayBucket[]> {\r\n // root session id → (local day → bucket), days kept sorted ascending.\r\n const sessionDays = new Map<string, Map<string, DayBucket>>();\r\n\r\n for (const row of db.all(ASSISTANT_BUCKETS_QUERY)) {\r\n const sessionId = row.root_session_id;\r\n if (typeof sessionId !== \"string\") continue;\r\n\r\n const createdAt = toInt(row.created_at);\r\n const timestampMs = normalizeCrushTimestampMs(createdAt);\r\n if (timestampMs === null) continue;\r\n const day = localDayKey(timestampMs);\r\n if (day === null) continue;\r\n\r\n let dayMap = sessionDays.get(sessionId);\r\n if (dayMap === undefined) {\r\n dayMap = new Map<string, DayBucket>();\r\n sessionDays.set(sessionId, dayMap);\r\n }\r\n const existing = dayMap.get(day);\r\n if (existing === undefined) {\r\n dayMap.set(day, { timestampMs, messageCount: 1 });\r\n } else {\r\n existing.timestampMs = Math.min(existing.timestampMs, timestampMs);\r\n existing.messageCount += 1;\r\n }\r\n }\r\n\r\n const out = new Map<string, DayBucket[]>();\r\n for (const [sessionId, dayMap] of sessionDays) {\r\n // Sort by day key ascending to mirror the Rust BTreeMap iteration order.\r\n const sortedDays = [...dayMap.keys()].sort();\r\n out.set(\r\n sessionId,\r\n sortedDays.map((day) => dayMap.get(day) as DayBucket),\r\n );\r\n }\r\n return out;\r\n}\r\n\r\n/** The Crush CLI usage reader singleton. */\r\nconst crushReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const dbPath = firstExistingRoot(PLATFORM_ID);\r\n if (dbPath === undefined) return []; // no crush.db → fail-open\r\n\r\n const db = await openSqlite(dbPath);\r\n if (db === null) return []; // missing / locked / unreadable / non-sqlite → fail-open\r\n\r\n try {\r\n const rootSessions = loadRootSessions(db);\r\n if (rootSessions.length === 0) return [];\r\n\r\n const assistantBuckets = loadAssistantBuckets(db);\r\n const records: UsageRecord[] = [];\r\n\r\n for (const session of rootSessions) {\r\n const sessionKey = `${dbPath}:${session.id}`;\r\n const dayBuckets = assistantBuckets.get(session.id);\r\n\r\n if (dayBuckets !== undefined && dayBuckets.length > 0) {\r\n const totalAssistantMessages = dayBuckets.reduce(\r\n (sum, bucket) => sum + bucket.messageCount,\r\n 0,\r\n );\r\n const safeCost = Math.max(0, session.cost);\r\n let allocatedCost = 0;\r\n\r\n for (let index = 0; index < dayBuckets.length; index++) {\r\n const bucket = dayBuckets[index] as DayBucket;\r\n // Last bucket absorbs the remainder so per-day costs sum to the total.\r\n const bucketCost =\r\n index + 1 === dayBuckets.length\r\n ? Math.max(0, safeCost - allocatedCost)\r\n : totalAssistantMessages > 0\r\n ? (safeCost * bucket.messageCount) / totalAssistantMessages\r\n : 0;\r\n allocatedCost += bucketCost;\r\n\r\n const tokens: TokenBreakdown = emptyTokens(); // tokens stay all-zero\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: CRUSH_MODEL_ID,\r\n providerId: CRUSH_PROVIDER_ID,\r\n sessionId: sessionKey,\r\n tokens,\r\n cost: bucketCost,\r\n ts: bucket.timestampMs,\r\n messageCount: Math.max(0, bucket.messageCount),\r\n confidence: \"host-estimated\",\r\n };\r\n if (sinceMs === undefined || record.ts >= sinceMs) records.push(record);\r\n }\r\n\r\n continue;\r\n }\r\n\r\n // No assistant messages: emit a single cost-only record (when costed).\r\n if (session.cost <= 0) continue;\r\n\r\n const ts = fallbackSessionTimestampMs(session.updatedAt, session.createdAt);\r\n if (ts === null) continue;\r\n\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: CRUSH_MODEL_ID,\r\n providerId: CRUSH_PROVIDER_ID,\r\n sessionId: sessionKey,\r\n tokens: emptyTokens(),\r\n cost: Math.max(0, session.cost),\r\n ts,\r\n messageCount: 0,\r\n confidence: \"host-estimated\",\r\n };\r\n if (sinceMs === undefined || record.ts >= sinceMs) records.push(record);\r\n }\r\n\r\n // Sort by timestamp asc, then session id (port of the Rust final sort).\r\n records.sort((a, b) => a.ts - b.ts || a.sessionId.localeCompare(b.sessionId));\r\n return records;\r\n } finally {\r\n db.close();\r\n }\r\n },\r\n};\r\n\r\nexport default crushReader;\r\n"],"mappings":"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","names":["record"]}
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  firstExistingRoot,
4
4
  walkFiles
5
- } from "./chunk-O7SDRBVB.js";
5
+ } from "./chunk-KM2YXO6V.js";
6
6
  import {
7
7
  emptyTokens,
8
8
  inferProvider
9
- } from "./chunk-XD7MY6HB.js";
9
+ } from "./chunk-7PSNXUFK.js";
10
10
 
11
11
  // src/usage/readers/cursor.ts
12
12
  import { basename } from "path";
@@ -191,4 +191,4 @@ var cursor_default = cursorReader;
191
191
  export {
192
192
  cursor_default as default
193
193
  };
194
- //# sourceMappingURL=cursor-DIW43TRZ.js.map
194
+ //# sourceMappingURL=cursor-D5SWDVSW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/readers/cursor.ts"],"sourcesContent":["/**\r\n * usage/readers/cursor — Cursor IDE usage reader (SYNCED platform).\r\n *\r\n * Faithful port of tokscale sessions/cursor.rs. Cursor's per-event token usage\r\n * lives behind the Cursor dashboard export API; tokscale fetches it and caches\r\n * the result as CSV under ~/.config/tokscale/cursor-cache/. THIS reader never\r\n * authenticates and never calls that API — it only parses the cached CSV when a\r\n * tokscale run has already produced it. Hence kind:\"synced\":\r\n * - cache present → parse the CSV(s) and emit one record per data row;\r\n * - cache absent → return [] (the scan layer notes \"requires sync, skipped\").\r\n *\r\n * Cache layout (per docs/research/usage-readers.json + design §3d):\r\n * ~/.config/tokscale/cursor-cache/usage.csv (active account)\r\n * ~/.config/tokscale/cursor-cache/usage.<account>.csv (secondary accounts)\r\n * ~/.config/tokscale/cursor-cache/archive/*.csv (rotated exports)\r\n * paths.ts resolves the cache dir (env override AGENT_CONNECTOR_TOKSCALE_DIR\r\n * first, the tokscale ~/.config/tokscale default second).\r\n *\r\n * CSV formats (column maps mirror cursor.rs exactly):\r\n * v1 (no \"Kind\"): Date,Model,Input(w/CacheWrite),Input(w/oCacheWrite),CacheRead,Output,Total,Cost,CostToYou\r\n * → model 1, in+cw 2, in 3, cacheRead 4, output 5, cost 7\r\n * v2 (has \"Kind\", <11 cols): Date,Kind,Model,MaxMode,Input(w/CW),Input(w/oCW),CacheRead,Output,Total,Cost\r\n * → model 2, in+cw 4, in 5, cacheRead 6, output 7, cost 9\r\n * v3 (has \"Kind\", ≥11 cols): Date,CloudAgentID,AutomationID,Kind,Model,MaxMode,Input(w/CW),Input(w/oCW),CacheRead,Output,Total,Cost\r\n * → model 4, in+cw 6, in 7, cacheRead 8, output 9, cost 11\r\n *\r\n * Per-row token math (port of parse_cursor_file):\r\n * input = Input(w/o CacheWrite) (clamped ≥ 0)\r\n * cacheWrite = Input(w/ CacheWrite) − Input(w/o CacheWrite) (clamped ≥ 0)\r\n * cacheRead = Cache Read (clamped ≥ 0)\r\n * output = Output Tokens (clamped ≥ 0)\r\n * reasoning = 0 (Cursor does not report it)\r\n * Cost is parsed from the Cost column (\"$1,234.56\" → 1234.56; \"Included\"/\"-\"/\r\n * \"NaN\"/\"\" → 0). Timestamp parses the Date column (ISO 8601 with/without ms/Z,\r\n * or date-only → noon UTC so local-day filtering never shifts the row).\r\n *\r\n * Attribution: provider inferred from the model (defaults to \"cursor\" for\r\n * Cursor-only ids like \"auto\"/\"composer-2\"); sessionId is the composite\r\n * `cursor-<account>-<date>` where account derives from the filename\r\n * (usage.csv → \"active\", usage.<x>.csv → sanitized \"<x>\"). No project scope\r\n * (account-level usage). Cursor ships no dedup key; the composite session id is\r\n * the implicit dedup, so dedupKey mirrors it. Confidence is \"host-reported\"\r\n * (the cached data carries real host token counts).\r\n *\r\n * Fail-open: no cache dir → []; unreadable/malformed file → skipped; a row\r\n * missing columns, with an empty model, or an unparseable date → skipped.\r\n */\r\n\r\nimport { basename } from \"node:path\";\r\n\r\nimport type { TokenBreakdown, UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { inferProvider } from \"../normalize.js\";\r\nimport { firstExistingRoot, walkFiles } from \"../paths.js\";\r\n\r\nimport { readFileSync } from \"node:fs\";\r\n\r\nconst PLATFORM_ID = \"cursor\" as const;\r\nconst DEFAULT_PROVIDER = \"cursor\";\r\n\r\n/** Column-index map for one CSV format (mirrors cursor.rs tuple order). */\r\ninterface ColumnMap {\r\n model: number;\r\n inputCacheWrite: number;\r\n inputNoCache: number;\r\n cacheRead: number;\r\n output: number;\r\n cost: number;\r\n}\r\n\r\nconst V1_COLUMNS: ColumnMap = {\r\n model: 1,\r\n inputCacheWrite: 2,\r\n inputNoCache: 3,\r\n cacheRead: 4,\r\n output: 5,\r\n cost: 7,\r\n};\r\nconst V2_COLUMNS: ColumnMap = {\r\n model: 2,\r\n inputCacheWrite: 4,\r\n inputNoCache: 5,\r\n cacheRead: 6,\r\n output: 7,\r\n cost: 9,\r\n};\r\nconst V3_COLUMNS: ColumnMap = {\r\n model: 4,\r\n inputCacheWrite: 6,\r\n inputNoCache: 7,\r\n cacheRead: 8,\r\n output: 9,\r\n cost: 11,\r\n};\r\n\r\n/**\r\n * Derive the account id from a cache filename (port of\r\n * account_id_from_cursor_cache_path):\r\n * - \"usage.csv\" → \"active\";\r\n * - \"usage.<x>.csv\" → \"<x>\" with non-[A-Za-z0-9-_.] chars replaced by '-'\r\n * (empty after cleaning → \"unknown\");\r\n * - anything else → \"unknown\".\r\n */\r\nfunction accountIdFromPath(path: string): string {\r\n const fileName = basename(path) || \"usage.csv\";\r\n if (fileName === \"usage.csv\") return \"active\";\r\n\r\n if (fileName.startsWith(\"usage.\") && fileName.endsWith(\".csv\")) {\r\n const stem = fileName.slice(\"usage.\".length, fileName.length - \".csv\".length);\r\n let cleaned = \"\";\r\n for (const ch of stem) {\r\n cleaned += /[A-Za-z0-9._-]/.test(ch) ? ch : \"-\";\r\n }\r\n return cleaned === \"\" ? \"unknown\" : cleaned;\r\n }\r\n\r\n return \"unknown\";\r\n}\r\n\r\n/** Coerce a CSV field to a non-negative integer (0 on absence/garbage). */\r\nfunction toNonNegInt(field: string): number {\r\n const trimmed = field.trim().replace(/^\"|\"$/g, \"\").trim();\r\n const n = Number(trimmed);\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/**\r\n * Parse a cost string like \"$0.50\" / \"1,234.56\" to a number. Port of parse_cost:\r\n * strip '$' and ',', then 0 for empty / \"NaN\" / values with no ASCII digit\r\n * (e.g. \"Included\", \"-\"). Negative results are clamped to 0 by the caller.\r\n */\r\nfunction parseCost(costStr: string): number {\r\n const cleaned = costStr.replace(/[$,]/g, \"\");\r\n const trimmed = cleaned.trim();\r\n if (trimmed === \"\" || trimmed.toLowerCase() === \"nan\" || !/[0-9]/.test(trimmed)) {\r\n return 0;\r\n }\r\n const n = Number(trimmed);\r\n return Number.isFinite(n) ? n : 0;\r\n}\r\n\r\n/**\r\n * Simple CSV line splitter that respects quoted fields (port of parse_csv_line):\r\n * a '\"' toggles quote state, a ',' outside quotes ends a field. Quote characters\r\n * are kept in the slice (callers strip surrounding quotes per field), matching\r\n * the Rust byte-index behavior.\r\n */\r\nfunction parseCsvLine(line: string): string[] {\r\n const fields: string[] = [];\r\n let start = 0;\r\n let inQuotes = false;\r\n for (let i = 0; i < line.length; i++) {\r\n const ch = line[i];\r\n if (ch === '\"') {\r\n inQuotes = !inQuotes;\r\n } else if (ch === \",\" && !inQuotes) {\r\n fields.push(line.slice(start, i));\r\n start = i + 1;\r\n }\r\n }\r\n fields.push(line.slice(start));\r\n return fields;\r\n}\r\n\r\n/** Trim a field and strip a single layer of surrounding double quotes. */\r\nfunction unquote(field: string): string {\r\n return field.trim().replace(/^\"|\"$/g, \"\");\r\n}\r\n\r\n/**\r\n * Parse a Cursor Date string to epoch ms (port of parse_date_to_timestamp).\r\n * Tries ISO 8601 with/without ms and trailing Z, then date-only (→ noon UTC so\r\n * the local date is stable for any tz from UTC-12..UTC+14). Returns null when\r\n * unparseable (the row is then skipped, matching the Rust ts==0 skip).\r\n */\r\nfunction parseDateToMs(dateStr: string): number | null {\r\n // ISO forms with explicit time: Date.parse handles\r\n // \"YYYY-MM-DDTHH:MM:SS\", \"...SS.mmm\", and either with a trailing \"Z\".\r\n if (dateStr.includes(\"T\")) {\r\n const ms = Date.parse(dateStr);\r\n return Number.isNaN(ms) ? null : ms;\r\n }\r\n\r\n // Date-only \"YYYY-MM-DD\" → noon UTC (12:00:00Z).\r\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\r\n if (m !== null) {\r\n const year = Number(m[1]);\r\n const month = Number(m[2]);\r\n const day = Number(m[3]);\r\n const ms = Date.UTC(year, month - 1, day, 12, 0, 0, 0);\r\n return Number.isNaN(ms) ? null : ms;\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Pick the column map for a header line (port of the cursor.rs format detect):\r\n * a \"Kind\" column with ≥11 total columns is v3; \"Kind\" with fewer is v2; no\r\n * \"Kind\" is v1. Returns null when the header is not a recognizable Cursor CSV\r\n * (must contain both \"Date\" and \"Model\").\r\n */\r\nfunction detectColumns(header: string): ColumnMap | null {\r\n if (!header.includes(\"Date\") || !header.includes(\"Model\")) return null;\r\n\r\n const fields = parseCsvLine(header);\r\n const hasKind = fields.some((f) => f.trim() === \"Kind\");\r\n const columnCount = fields.length;\r\n\r\n if (hasKind && columnCount >= 11) return V3_COLUMNS;\r\n if (hasKind) return V2_COLUMNS;\r\n return V1_COLUMNS;\r\n}\r\n\r\n/** Parse one cached Cursor CSV file into usage records (port of parse_cursor_file). */\r\nfunction parseCursorFile(path: string): UsageRecord[] {\r\n let content: string;\r\n try {\r\n content = readFileSync(path, \"utf8\");\r\n } catch {\r\n return []; // unreadable → fail-open\r\n }\r\n\r\n const lines = content.split(\"\\n\");\r\n if (lines.length === 0) return [];\r\n\r\n const header = lines[0];\r\n if (header === undefined) return [];\r\n\r\n const columns = detectColumns(header);\r\n if (columns === null) return []; // not a Cursor CSV\r\n\r\n const minFields = columns.cost + 1;\r\n const accountId = accountIdFromPath(path);\r\n const out: UsageRecord[] = [];\r\n\r\n for (let i = 1; i < lines.length; i++) {\r\n const rawLine = lines[i];\r\n if (rawLine === undefined) continue;\r\n // Strip a trailing CR (CRLF files) before the empty-line check.\r\n const line = rawLine.replace(/\\r$/, \"\");\r\n if (line.trim() === \"\") continue;\r\n\r\n const fields = parseCsvLine(line);\r\n if (fields.length < minFields) continue; // not enough columns for this format\r\n\r\n const dateStr = unquote(fields[0] ?? \"\");\r\n const model = unquote(fields[columns.model] ?? \"\");\r\n if (model === \"\") continue; // skip empty/errored entries\r\n\r\n const ts = parseDateToMs(dateStr);\r\n if (ts === null) continue; // unparseable date → skip (Rust ts==0)\r\n\r\n const inputWithCacheWrite = toNonNegInt(fields[columns.inputCacheWrite] ?? \"\");\r\n const inputWithoutCacheWrite = toNonNegInt(fields[columns.inputNoCache] ?? \"\");\r\n const cacheReadTokens = toNonNegInt(fields[columns.cacheRead] ?? \"\");\r\n const outputTokens = toNonNegInt(fields[columns.output] ?? \"\");\r\n const cost = Math.max(0, parseCost(unquote(fields[columns.cost] ?? \"\")));\r\n\r\n // cacheWrite = (w/ cache write) − (w/o cache write), clamped ≥ 0.\r\n const cacheWrite = Math.max(0, inputWithCacheWrite - inputWithoutCacheWrite);\r\n const input = Math.max(0, inputWithoutCacheWrite);\r\n\r\n const providerId = inferProvider(model) ?? DEFAULT_PROVIDER;\r\n // Composite session id is also the implicit dedup key (Cursor has none).\r\n const sessionId = `cursor-${accountId}-${dateStr}`;\r\n\r\n const tokens: TokenBreakdown = emptyTokens();\r\n tokens.input = input;\r\n tokens.output = outputTokens;\r\n tokens.cacheRead = cacheReadTokens;\r\n tokens.cacheWrite = cacheWrite;\r\n // reasoning stays 0 (Cursor does not report it).\r\n\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: model,\r\n providerId,\r\n sessionId,\r\n tokens,\r\n cost,\r\n ts,\r\n messageCount: 1,\r\n dedupKey: sessionId,\r\n confidence: \"host-reported\",\r\n };\r\n out.push(record);\r\n }\r\n\r\n return out;\r\n}\r\n\r\n/** The Cursor synced usage reader singleton. */\r\nconst cursorReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"synced\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n // The cache dir is ~/.config/tokscale/cursor-cache (env override first).\r\n // Absent → [] so the scan layer reports \"requires sync, skipped\".\r\n const root = firstExistingRoot(PLATFORM_ID);\r\n if (root === undefined) return [];\r\n\r\n // Enumerate cached exports: usage*.csv at the cache root AND any archive/\r\n // subdir (walkFiles recurses), covering the active + secondary accounts.\r\n const files = walkFiles(root, (name) => {\r\n const lower = name.toLowerCase();\r\n return lower.startsWith(\"usage\") && lower.endsWith(\".csv\");\r\n });\r\n\r\n const records: UsageRecord[] = [];\r\n for (const file of files) {\r\n const rows = parseCursorFile(file);\r\n for (const row of rows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default cursorReader;\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CURSOR_EVENT_MAP,
4
+ CURSOR_HOOKS_VERSION,
5
+ CursorAdapter,
6
+ adapter,
7
+ cursor_default
8
+ } from "./chunk-XVEJAFKW.js";
9
+ import "./chunk-PSGTHJQU.js";
10
+ import "./chunk-XXLCMSD3.js";
11
+ import "./chunk-3FINQ2ZF.js";
12
+ import "./chunk-Y3K6KJMS.js";
13
+ import "./chunk-ILWHLGIC.js";
14
+ import "./chunk-QYOUQCC6.js";
15
+ export {
16
+ CURSOR_EVENT_MAP,
17
+ CURSOR_HOOKS_VERSION,
18
+ CursorAdapter,
19
+ adapter,
20
+ cursor_default as default
21
+ };
22
+ //# sourceMappingURL=cursor-W7I4QUFO.js.map
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  print
4
- } from "./chunk-H33M77DD.js";
4
+ } from "./chunk-N4ENLZEU.js";
5
5
  import {
6
6
  detectInstalledPlatforms
7
- } from "./chunk-ISEJWBFF.js";
8
- import "./chunk-D4ELPBB5.js";
7
+ } from "./chunk-GZZCHJDP.js";
8
+ import "./chunk-NDMNMAKP.js";
9
9
 
10
10
  // src/cli/commands/detect.ts
11
11
  import { parseArgs } from "util";
@@ -71,4 +71,4 @@ async function run(argv) {
71
71
  export {
72
72
  run
73
73
  };
74
- //# sourceMappingURL=detect-6V4LC3MA.js.map
74
+ //# sourceMappingURL=detect-JHSA3G3U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/detect.ts"],"sourcesContent":["/**\r\n * cli/commands/detect — list the AI-agent platforms installed on this machine.\r\n *\r\n * `detect` probes every registered adapter (via detectInstalledPlatforms) and\r\n * prints, for each installed host: id, name, hook paradigm, install scope, the\r\n * native config path that would be written, and a one-line capabilities summary.\r\n * `--json` emits the raw DetectedPlatform[] for scripting.\r\n */\r\n\r\nimport { parseArgs } from \"node:util\";\r\n\r\nimport type { DetectedPlatform, PlatformCapabilities } from \"../../core/types.js\";\r\nimport { detectInstalledPlatforms } from \"../../adapters/detect.js\";\r\nimport { print } from \"../app.js\";\r\n\r\n/** Compact, human one-line summary of what a host can honor. */\r\nfunction capabilitySummary(caps: PlatformCapabilities): string {\r\n const events: string[] = [];\r\n if (caps.preToolUse) events.push(\"PreToolUse\");\r\n if (caps.postToolUse) events.push(\"PostToolUse\");\r\n if (caps.sessionStart) events.push(\"SessionStart\");\r\n if (caps.sessionEnd) events.push(\"SessionEnd\");\r\n if (caps.userPromptSubmit) events.push(\"UserPromptSubmit\");\r\n if (caps.preCompact) events.push(\"PreCompact\");\r\n if (caps.stop) events.push(\"Stop\");\r\n if (caps.notification) events.push(\"Notification\");\r\n // Newer optional per-event flags (absent ⇒ unsupported, read as `?? false`).\r\n if (caps.permissionRequest ?? false) events.push(\"PermissionRequest\");\r\n if (caps.postToolUseFailure ?? false) events.push(\"PostToolUseFailure\");\r\n if (caps.subagentStart ?? false) events.push(\"SubagentStart\");\r\n if (caps.subagentStop ?? false) events.push(\"SubagentStop\");\r\n if (caps.postCompact ?? false) events.push(\"PostCompact\");\r\n\r\n const extras: string[] = [];\r\n if (caps.canModifyArgs) extras.push(\"modifyArgs\");\r\n if (caps.canModifyOutput) extras.push(\"modifyOutput\");\r\n if (caps.canInjectSessionContext) extras.push(\"injectContext\");\r\n\r\n const transports = caps.transports.length > 0 ? caps.transports.join(\"/\") : \"none\";\r\n const parts = [\r\n `events: ${events.length > 0 ? events.join(\",\") : \"none\"}`,\r\n `transports: ${transports}`,\r\n ];\r\n if (extras.length > 0) parts.push(extras.join(\",\"));\r\n return parts.join(\" | \");\r\n}\r\n\r\nexport async function run(argv: string[]): Promise<number> {\r\n const { values } = parseArgs({\r\n args: argv,\r\n options: {\r\n json: { type: \"boolean\", default: false },\r\n project: { type: \"string\" },\r\n },\r\n allowPositionals: false,\r\n });\r\n\r\n const projectDir = values.project ?? process.cwd();\r\n const detected: DetectedPlatform[] = await detectInstalledPlatforms(projectDir);\r\n\r\n if (values.json) {\r\n print(JSON.stringify(detected, null, 2));\r\n return 0;\r\n }\r\n\r\n if (detected.length === 0) {\r\n print(\"No supported AI-agent platforms detected on this machine.\");\r\n return 0;\r\n }\r\n\r\n print(`Detected ${detected.length} platform(s):\\n`);\r\n for (const p of detected) {\r\n print(`${p.name} (${p.id})`);\r\n print(` paradigm: ${p.paradigm}`);\r\n print(` scope: ${p.scope}`);\r\n print(` configPath: ${p.configPath}`);\r\n print(` confidence: ${p.confidence} (${p.reason})`);\r\n print(` capabilities: ${capabilitySummary(p.capabilities)}`);\r\n print(\"\");\r\n }\r\n return 0;\r\n}\r\n"],"mappings":";;;;;;;;;;AASA,SAAS,iBAAiB;AAO1B,SAAS,kBAAkB,MAAoC;AAC7D,QAAM,SAAmB,CAAC;AAC1B,MAAI,KAAK,WAAY,QAAO,KAAK,YAAY;AAC7C,MAAI,KAAK,YAAa,QAAO,KAAK,aAAa;AAC/C,MAAI,KAAK,aAAc,QAAO,KAAK,cAAc;AACjD,MAAI,KAAK,WAAY,QAAO,KAAK,YAAY;AAC7C,MAAI,KAAK,iBAAkB,QAAO,KAAK,kBAAkB;AACzD,MAAI,KAAK,WAAY,QAAO,KAAK,YAAY;AAC7C,MAAI,KAAK,KAAM,QAAO,KAAK,MAAM;AACjC,MAAI,KAAK,aAAc,QAAO,KAAK,cAAc;AAEjD,MAAI,KAAK,qBAAqB,MAAO,QAAO,KAAK,mBAAmB;AACpE,MAAI,KAAK,sBAAsB,MAAO,QAAO,KAAK,oBAAoB;AACtE,MAAI,KAAK,iBAAiB,MAAO,QAAO,KAAK,eAAe;AAC5D,MAAI,KAAK,gBAAgB,MAAO,QAAO,KAAK,cAAc;AAC1D,MAAI,KAAK,eAAe,MAAO,QAAO,KAAK,aAAa;AAExD,QAAM,SAAmB,CAAC;AAC1B,MAAI,KAAK,cAAe,QAAO,KAAK,YAAY;AAChD,MAAI,KAAK,gBAAiB,QAAO,KAAK,cAAc;AACpD,MAAI,KAAK,wBAAyB,QAAO,KAAK,eAAe;AAE7D,QAAM,aAAa,KAAK,WAAW,SAAS,IAAI,KAAK,WAAW,KAAK,GAAG,IAAI;AAC5E,QAAM,QAAQ;AAAA,IACZ,WAAW,OAAO,SAAS,IAAI,OAAO,KAAK,GAAG,IAAI,MAAM;AAAA,IACxD,eAAe,UAAU;AAAA,EAC3B;AACA,MAAI,OAAO,SAAS,EAAG,OAAM,KAAK,OAAO,KAAK,GAAG,CAAC;AAClD,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,eAAsB,IAAI,MAAiC;AACzD,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACxC,SAAS,EAAE,MAAM,SAAS;AAAA,IAC5B;AAAA,IACA,kBAAkB;AAAA,EACpB,CAAC;AAED,QAAM,aAAa,OAAO,WAAW,QAAQ,IAAI;AACjD,QAAM,WAA+B,MAAM,yBAAyB,UAAU;AAE9E,MAAI,OAAO,MAAM;AACf,UAAM,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AACvC,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,2DAA2D;AACjE,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,SAAS,MAAM;AAAA,CAAiB;AAClD,aAAW,KAAK,UAAU;AACxB,UAAM,GAAG,EAAE,IAAI,KAAK,EAAE,EAAE,GAAG;AAC3B,UAAM,kBAAkB,EAAE,QAAQ,EAAE;AACpC,UAAM,kBAAkB,EAAE,KAAK,EAAE;AACjC,UAAM,kBAAkB,EAAE,UAAU,EAAE;AACtC,UAAM,kBAAkB,EAAE,UAAU,MAAM,EAAE,MAAM,GAAG;AACrD,UAAM,mBAAmB,kBAAkB,EAAE,YAAY,CAAC,EAAE;AAC5D,UAAM,EAAE;AAAA,EACV;AACA,SAAO;AACT;","names":[]}
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  normalizeSessionSource
4
- } from "./chunk-H53QZQJI.js";
4
+ } from "./chunk-PSGTHJQU.js";
5
5
  import {
6
6
  rewriteEnvRefs
7
- } from "./chunk-BEFVJ2OE.js";
7
+ } from "./chunk-XXLCMSD3.js";
8
8
  import {
9
9
  BaseAdapter
10
- } from "./chunk-MGTDQLJJ.js";
10
+ } from "./chunk-3FINQ2ZF.js";
11
11
  import {
12
12
  buildWrappedStdio,
13
13
  isHomeBinHookCommand
14
- } from "./chunk-4NHXLXSD.js";
15
- import "./chunk-UMQ6NBQR.js";
16
- import "./chunk-GZGLCCEE.js";
14
+ } from "./chunk-Y3K6KJMS.js";
15
+ import "./chunk-ILWHLGIC.js";
16
+ import "./chunk-QYOUQCC6.js";
17
17
 
18
18
  // src/adapters/devin/index.ts
19
19
  import { existsSync } from "fs";
@@ -507,4 +507,4 @@ export {
507
507
  adapter,
508
508
  devin_default as default
509
509
  };
510
- //# sourceMappingURL=devin-QMEM3OFA.js.map
510
+ //# sourceMappingURL=devin-SSR7S4WX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/devin/index.ts"],"sourcesContent":["/**\r\n * adapters/devin — Devin CLI (Cognition) platform adapter for agent-connector.\r\n *\r\n * Devin CLI is a Rust binary (`devin`, installed via `curl cli.devin.ai/install.sh`\r\n * → symlinked at `~/.local/bin/devin`; XDG namespace `devin`). It is a\r\n * **json-stdio** host: it ships a FULL Claude-Code-compatible lifecycle hook\r\n * system alongside MCP server registration. Both surfaces live in ONE JSON\r\n * config file per scope:\r\n *\r\n * - user scope → ~/.config/devin/config.json\r\n * (%APPDATA%\\devin\\config.json on Windows)\r\n * - project scope → <projectDir>/.devin/config.json\r\n *\r\n * MCP servers — root key `mcpServers` (object map keyed by server name; the\r\n * Claude-Desktop dialect). Native stdio entry { command, args?, env? } (NO\r\n * `type`/`disabled` discriminator); remote entry { url, transport?, headers?,\r\n * oauthClientId?, oauthClientSecret? } where transport is \"http\" (Streamable\r\n * HTTP, the URL default) or \"sse\" (legacy). Devin natively interpolates\r\n * `${env:VAR}` and `${file:/path}` in secret-bearing fields, so we PASS the\r\n * ${env:VAR} token THROUGH (nativeServerEnvInterpolation) rather than baking a\r\n * literal at install time. Primary source:\r\n * docs.devin.ai/cli/extensibility/mcp/configuration.\r\n *\r\n * Hooks — the `\"hooks\"` key IN the same config.json (root key `hooks`,\r\n * Claude NESTED-rule shape). Devin's hook format is documented as\r\n * \"compatible with Claude Code hooks\" — PascalCase event names, Claude\r\n * snake_case stdin wire fields. We deliberately write the `\"hooks\"` key in\r\n * config.json (a first-party-documented hook location whose object-map shape\r\n * fits the shared hook-merge engine exactly) rather than the alternative\r\n * standalone `.devin/hooks.v1.json` (whose event map is the WHOLE file with NO\r\n * `hooks` wrapper — incompatible with the engine's `file.hooks` contract). Both\r\n * are documented; config.json keeps us on the shared, Windows-safe engine with\r\n * zero hand-rolled file handling. Primary source:\r\n * docs.devin.ai/cli/extensibility/hooks/overview (\"Where Hooks Live\").\r\n *\r\n * Supported events (docs.devin.ai/cli/extensibility/hooks/lifecycle-hooks):\r\n * PreToolUse, PostToolUse, PermissionRequest, UserPromptSubmit, Stop,\r\n * PostCompaction (canonical PostCompact), SessionStart, SessionEnd. Devin\r\n * exposes no Notification / PreCompact / SubagentStart / SubagentStop /\r\n * PostToolUseFailure, so those degrade to a warn/skip at install time.\r\n *\r\n * Reply protocol — Devin's stdout reply is the SIMPLE top-level\r\n * { \"decision\": \"approve\"|\"block\"|\"deny\", \"reason\" } form (NOT Claude's\r\n * `hookSpecificOutput.permissionDecision` envelope). exit 0 = success,\r\n * exit 2 = block. \"block\" denies a tool/stop with a reason; on PermissionRequest\r\n * \"approve\"/\"deny\" are the grant/refuse verbs. Devin documents no\r\n * output-rewrite or context-injection reply channel, so canModifyOutput /\r\n * canInjectSessionContext stay false. Primary source:\r\n * docs.devin.ai/cli/extensibility/hooks/overview (\"Command Hooks\" output +\r\n * \"Exit Codes\") + lifecycle-hooks (PermissionRequest / Stop examples).\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir, platform as osPlatform } from \"node:os\";\r\nimport { join } from \"node:path\";\r\n\r\nimport { BaseAdapter, type HookMergeDescriptor } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, MemoryTarget, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PermissionRequestEvent,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostCompactEvent,\r\n PostToolUseEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionEndEvent,\r\n SessionStartEvent,\r\n SkillDef,\r\n StopEvent,\r\n Transport,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { rewriteEnvRefs } from \"../../core/interpolate.js\";\r\nimport {\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"devin\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Resolve the Devin user config directory: ~/.config/devin (POSIX) or\r\n * %APPDATA%\\devin (Windows). Mirrors the documented user-scope path in\r\n * docs.devin.ai/cli/extensibility/mcp/configuration.\r\n */\r\nfunction devinUserConfigDir(): string {\r\n if (osPlatform() === \"win32\") {\r\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\r\n return join(appData, \"devin\");\r\n }\r\n return join(homedir(), \".config\", \"devin\");\r\n}\r\n\r\n/**\r\n * Canonical events Devin actually fires. Devin's hook event names are\r\n * Claude-identical PascalCase, so each canonical name is registered directly\r\n * EXCEPT PostCompaction, which Devin spells \"PostCompaction\" on the wire while\r\n * agent-connector's canonical name is \"PostCompact\" — handled by the descriptor\r\n * mapEvent / parseEvent so the home binary still dispatches on the canonical\r\n * \"PostCompact\" command.\r\n */\r\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"PermissionRequest\",\r\n \"UserPromptSubmit\",\r\n \"Stop\",\r\n \"PostCompact\",\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n]);\r\n\r\n/** Devin's on-the-wire event name for the canonical PostCompact event. */\r\nconst DEVIN_POSTCOMPACT = \"PostCompaction\";\r\n\r\n/**\r\n * Native MCP server entry shapes Devin accepts under `mcpServers`.\r\n * A stdio entry is { command, args?, env? } — no type/disabled discriminator;\r\n * remote transports register a URL with an optional `transport` (\"http\"|\"sse\").\r\n */\r\ninterface DevinStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n}\r\ninterface DevinHttpServer {\r\n url: string;\r\n transport?: \"http\" | \"sse\";\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** A single Devin native hook registration entry (Claude-shaped, nested). */\r\ninterface DevinHookEntry {\r\n matcher: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of Devin's config.json hooks section (only the parts we touch). */\r\ninterface DevinHooksFile {\r\n hooks?: Record<string, DevinHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Raw Devin CLI hook stdin payload (Claude-compatible snake_case wire fields). */\r\ninterface DevinWireInput {\r\n hook_event_name?: string;\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n /** PostToolUse — object { success, output, error }. */\r\n tool_response?: unknown;\r\n /** UserPromptSubmit — the user's message text. */\r\n prompt?: string;\r\n /** SessionStart — how the session was started. */\r\n source?: string;\r\n /** SessionEnd — why the session ended. */\r\n reason?: string;\r\n /** Stop loop guard. */\r\n stop_hook_active?: boolean;\r\n /** PostCompaction — compactor summary (may be null). Rides on raw; no normalized field. */\r\n summary?: unknown;\r\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\r\n connector?: unknown;\r\n /** Session/cwd are not documented Devin wire fields; read defensively. */\r\n session_id?: string;\r\n cwd?: string;\r\n}\r\n\r\nexport class DevinAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Devin CLI (Cognition)\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block — project <projectDir>/AGENTS.md\r\n // via the base default; user scope → ~/.config/devin/AGENTS.md (override below).\r\n // Devin reads AGENTS.md / AGENT.md / CLAUDE.md identically as always-on rules\r\n // (docs.devin.ai/cli/extensibility/rules).\r\n supportsMemory: true,\r\n preToolUse: true,\r\n postToolUse: true,\r\n userPromptSubmit: true,\r\n stop: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n // PermissionRequest is a documented Devin hook event (decision-capable).\r\n permissionRequest: true,\r\n // PostCompaction (canonical PostCompact) — observe-only post-compaction hook.\r\n postCompact: true,\r\n // Devin documents NO Notification / PreCompact / SubagentStart / SubagentStop /\r\n // PostToolUseFailure event — those stay unset (install reports the skip-warn).\r\n notification: false,\r\n preCompact: false,\r\n // Devin's PreToolUse/PermissionRequest can block/deny via the simple\r\n // {decision} reply, but it documents no tool-arg/-output rewrite and no\r\n // context-injection reply channel.\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n canInjectSessionContext: false,\r\n // Devin registers stdio and remote MCP servers. Remote is Streamable HTTP\r\n // (\"http\", the URL default) with a legacy \"sse\" fallback — both map to our\r\n // remote-transport handling; advertise both.\r\n transports: [\"stdio\", \"http\", \"sse\"],\r\n // Devin natively interpolates ${env:VAR} / ${file:/path} in secret-bearing\r\n // server fields, so the token survives into the host config (pass-through).\r\n nativeServerEnvInterpolation: true,\r\n // Content surfaces. Devin reads Agent Skills from its `.devin` config tree\r\n // at <configDir>/skills/<name>/SKILL.md — the SKILL.md format + frontmatter\r\n // are byte-confirmed (docs.devin.ai/cli/extensibility/skills/creating-skills),\r\n // so skills IS wired.\r\n supportsSkills: true,\r\n // Devin ALSO documents native slash commands (/cli/reference/commands) and\r\n // subagents (/cli/subagents), but their exact on-disk dir names are NOT\r\n // byte-confirmed from a first-party config reference — to avoid fabricating a\r\n // path, commands/subagents stay unset (BaseAdapter skip-warn) in v1. See the\r\n // module header CEILING note.\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = devinUserConfigDir();\r\n const userCfg = join(userDir, \"config.json\");\r\n const projDir = join(projectDir, \".devin\");\r\n const projCfg = join(projDir, \"config.json\");\r\n // The installed binary symlink (~/.local/bin/devin) is a strong marker even\r\n // before any config file is written.\r\n const binMarker = join(homedir(), \".local\", \"bin\", \"devin\");\r\n\r\n const userInstalled = existsSync(userDir) || existsSync(userCfg) || existsSync(binMarker);\r\n const projInstalled = existsSync(projDir) || existsSync(projCfg);\r\n const installed = userInstalled || projInstalled;\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projCfg : userCfg;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? `found Devin CLI config (${scope}) at ${configPath}`\r\n : `no Devin CLI config at ${userDir} or ${projDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".devin\")\r\n : devinUserConfigDir();\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"config.json\");\r\n }\r\n\r\n /** Hooks live under the `\"hooks\"` key in the SAME config.json. */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return this.getServerConfigPath(ctx);\r\n }\r\n\r\n // ── Memory surface: user-scope AGENTS.md ─────────────────────────────────\r\n // Project scope rides on the AGENTS.md base default (project root). User scope\r\n // targets ~/.config/devin/AGENTS.md (%APPDATA%\\devin\\AGENTS.md on Windows) —\r\n // Devin's documented global-rules file (docs.devin.ai/cli/extensibility/rules).\r\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\r\n if (this.memoryOverride(ctx)?.path || ctx.scope !== \"user\") {\r\n return super.memoryTargets(ctx);\r\n }\r\n return [\r\n {\r\n path: join(devinUserConfigDir(), \"AGENTS.md\"),\r\n reason: \"Devin CLI global rules file (~/.config/devin/AGENTS.md)\",\r\n },\r\n ];\r\n }\r\n\r\n // ── MCP server install / uninstall ───────────────────────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? \"server registration disabled for devin\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /** Render a normalized ServerDef into Devin's native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): DevinStdioServer | DevinHttpServer {\r\n const transport: Transport = server.transport;\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n // command/args are passed verbatim (mirrors claude-code — they route\r\n // through the serve wrapper and rarely carry secrets).\r\n const entry: DevinStdioServer = { command };\r\n if (args.length > 0) entry.args = args;\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http / sse (and any other remote transport) — Devin registers a URL.\r\n // Map our \"http\" transport to Streamable HTTP (the URL default) and \"sse\" to\r\n // legacy SSE; omit `transport` for the default-http case to keep the entry\r\n // minimal (Devin infers http from a URL). ${env:VAR} survives into the\r\n // config via Devin's native interpolation (rewriteEnvRefs/devinEnvToken).\r\n const entry: DevinHttpServer = {\r\n url: rewriteEnvRefs(server.url ?? \"\", devinEnvToken),\r\n };\r\n if (transport === \"sse\") entry.transport = \"sse\";\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env/header values. Devin interpolates ${env:VAR} natively, so each\r\n * ${env:VAR} token is passed THROUGH (rewriteEnvRefs/devinEnvToken) rather\r\n * than baked to a literal — secrets never land in the committed config.\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(env)) {\r\n out[k] = rewriteEnvRefs(v, devinEnvToken);\r\n }\r\n return out;\r\n }\r\n\r\n // ── Hook install / uninstall (config.json \"hooks\" key, nested-rule shape) ──\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"hooks disabled for devin\" }];\r\n }\r\n if (connector.hookEvents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\r\n }\r\n\r\n const pending = connector.hookEvents.map((event) => ({\r\n event,\r\n matcher: connector.hooks[event]?.matcher ?? \"\",\r\n }));\r\n return this.upsertHookEntries(\r\n ctx,\r\n this.getHookConfigPath(ctx),\r\n pending,\r\n this.hookDescriptor(ctx),\r\n );\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n return this.removeHookEntries(ctx, this.getHookConfigPath(ctx), this.hookDescriptor(ctx));\r\n }\r\n\r\n /**\r\n * Devin's hook-merge descriptor (NESTED shape `{ matcher, hooks: [...] }`).\r\n * Bound to `ctx` because Devin's ownership is CONNECTOR-GENERIC (ANY of our\r\n * commands in the entry's inner `hooks`):\r\n * - mapEvent = drop events outside SUPPORTED_EVENTS; the canonical\r\n * PostCompact is rewritten to Devin's on-wire \"PostCompaction\" key while\r\n * all other supported events map 1:1.\r\n * - entryOwnsCommand reuses isOurCommand over the inner commands.\r\n * - uninstall = inner-strip ONLY (ownsEntryForRemove never-true).\r\n */\r\n private hookDescriptor(ctx: InstallContext): HookMergeDescriptor<DevinHookEntry> {\r\n return {\r\n mapEvent: (e) => {\r\n if (!SUPPORTED_EVENTS.has(e as HookEventName)) return undefined;\r\n return e === \"PostCompact\" ? DEVIN_POSTCOMPACT : e;\r\n },\r\n unmappedWarnDetail: (e) => `${e} has no Devin hook equivalent — skipped`,\r\n renderEntry: (_event, matcher, command) => ({\r\n matcher,\r\n hooks: [{ type: \"command\", command }],\r\n }),\r\n entryOwnsCommand: (entry, _command) =>\r\n (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx)),\r\n ownsEntryForRemove: () => () => false,\r\n stripInner: (c) => (entry) => {\r\n const innerBefore = entry.hooks?.length ?? 0;\r\n const inner = (entry.hooks ?? []).filter((h) => !this.isOurCommand(h.command, c));\r\n return {\r\n next: inner.length > 0 ? { matcher: entry.matcher ?? \"\", hooks: inner } : null,\r\n removed: innerBefore - inner.length,\r\n };\r\n },\r\n skipDetail: (event) => `hooks.${event} already registered`,\r\n removeDetail: (event, removed) => `hooks.${event} (${removed})`,\r\n absentDetail: \"no hooks section present\",\r\n noMatchDetail: \"no matching hook entries\",\r\n };\r\n }\r\n\r\n /** True when a hook command references our home binary AND this connector id\r\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\r\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\r\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Content surfaces: skills ──────────────────────────────────────────────\r\n // CEILING (v1): only the byte-confirmed SKILL.md layout is wired. Devin\r\n // documents the SKILL.md format + frontmatter\r\n // (docs.devin.ai/cli/extensibility/skills/creating-skills) under the `.devin`\r\n // tree (<configDir>/skills/<name>/SKILL.md). Commands/subagents on-disk dir\r\n // names are NOT byte-confirmed from a first-party config reference, so they\r\n // stay on the BaseAdapter skip-warn rather than guessing a path.\r\n\r\n /** Native skill dir: <configDir>/skills/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.getConfigDir(ctx), \"skills\", name);\r\n }\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for devin\" }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir.\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n /**\r\n * Render a skill's SKILL.md: frontmatter (name, description) + body. Devin's\r\n * SKILL.md frontmatter documents name/description; extra keys pass through\r\n * verbatim. No model/allowed-tools skill field is emitted.\r\n */\r\n private renderSkill(skill: SkillDef): string {\r\n const frontmatter: Record<string, unknown> = {\r\n name: skill.name,\r\n description: skill.description,\r\n };\r\n if (skill.extra) Object.assign(frontmatter, skill.extra);\r\n return this.renderFrontmatterMd(frontmatter, skill.body);\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const cfgPath = this.getServerConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = ctx.connector.hookEvents;\r\n return [\r\n {\r\n name: `${this.name}: config.json present`,\r\n check: () =>\r\n existsSync(cfgPath)\r\n ? { status: \"OK\", detail: cfgPath }\r\n : { status: \"FAIL\", detail: `not found: ${cfgPath}` },\r\n },\r\n {\r\n name: `${this.name}: server entry registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(cfgPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${cfgPath}` };\r\n }\r\n return connectorId in bucket\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\r\n : {\r\n status: \"FAIL\",\r\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${cfgPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const file = this.readJson<DevinHooksFile>(cfgPath);\r\n if (!file) return { status: \"FAIL\", detail: `cannot read ${cfgPath}` };\r\n const hooks = file.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) =>\r\n (e.hooks ?? []).some((h) =>\r\n isHomeBinHookCommand(h.command, homeBin, connectorId),\r\n ),\r\n ),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${cfgPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Runtime: parse Devin stdin JSON → normalized event ─────────────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as DevinWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = typeof input.session_id === \"string\" ? input.session_id : \"\";\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(toolResponseToString(input.tool_response) !== undefined\r\n ? { toolOutput: toolResponseToString(input.tool_response) }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PermissionRequest\": {\r\n // Devin PermissionRequest stdin: { tool_name, tool_input }. No\r\n // permission-suggestion entries are documented.\r\n const ev: PermissionRequestEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"Stop\": {\r\n const ev: StopEvent = {\r\n ...base,\r\n ...(typeof input.stop_hook_active === \"boolean\"\r\n ? { stopHookActive: input.stop_hook_active }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PostCompact\": {\r\n // Devin's PostCompaction wire carries { summary } (no trigger enum);\r\n // summary rides on base.raw (no normalized field).\r\n const ev: PostCompactEvent = { ...base };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(input.source),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\r\n const ev: SessionEndEvent = {\r\n ...base,\r\n ...(typeof input.reason === \"string\" ? { reason: input.reason } : {}),\r\n };\r\n return ev;\r\n }\r\n default: {\r\n // Devin never delivers Notification / PreCompact / SubagentStart /\r\n // SubagentStop / PostToolUseFailure (see SUPPORTED_EVENTS). Surface it\r\n // loudly rather than silently mis-parse.\r\n throw new Error(`unsupported devin hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → Devin native (simple {decision}) reply ──\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // PostCompaction / SessionEnd are observe-only — no Decision Control. Every\r\n // decision is an unconditional no-op passthrough.\r\n if (event === \"PostCompact\" || event === \"SessionEnd\") {\r\n return { exitCode: 0 };\r\n }\r\n\r\n // PermissionRequest: an explicit allow is an active GRANT (Devin's \"approve\"\r\n // verb); deny refuses the tool. ask/context/void fall through to the native\r\n // dialog (exit 0, no decision output).\r\n if (event === \"PermissionRequest\") {\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason: response.reason ?? \"Denied by hook\",\r\n });\r\n }\r\n if (decision === \"allow\") {\r\n return this.stdout({ decision: \"approve\" });\r\n }\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block the action with a reason (Devin's top-level {decision:\"block\"}\r\n // form, NOT Claude's hookSpecificOutput envelope). Covers PreToolUse and\r\n // Stop (Stop-block keeps the agent running with `reason` as guidance).\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n decision: \"block\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n\r\n // ask is not a distinct Devin reply verb (only approve/block/deny) — fall\r\n // through to allow. allow / modify (no rewrite channel) / context (no\r\n // injection channel) / void → pass through with exit 0.\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\n/**\r\n * Devin's native interpolation token reconstructor. Devin reads the SAME\r\n * `${env:VAR}` (and `${env:VAR:-default}`) form the connector author writes, so\r\n * the rewrite is a faithful round-trip of the token (Devin resolves it at\r\n * runtime — the secret never lands in the committed config).\r\n */\r\nfunction devinEnvToken(name: string, def?: string): string {\r\n return def !== undefined ? `\\${env:${name}:-${def}}` : `\\${env:${name}}`;\r\n}\r\n\r\n/** Coerce a Devin PostToolUse `tool_response` into a string for the normalized event. */\r\nfunction toolResponseToString(value: unknown): string | undefined {\r\n if (value == null) return undefined;\r\n if (typeof value === \"string\") return value;\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\nexport const adapter = new DevinAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -2,62 +2,62 @@
2
2
  import {
3
3
  marketplaceDoctorChecks,
4
4
  syncConnector
5
- } from "./chunk-PAPMUAR5.js";
6
- import "./chunk-APZDTY76.js";
5
+ } from "./chunk-MLU7NDAZ.js";
6
+ import "./chunk-LQ55E7QI.js";
7
7
  import {
8
8
  readMarketplaceInstalls
9
- } from "./chunk-2Y2MZRLP.js";
10
- import "./chunk-UM5S7DKK.js";
11
- import "./chunk-6WZODR3Z.js";
12
- import "./chunk-OPXAP3AC.js";
9
+ } from "./chunk-7C2IPIIB.js";
10
+ import "./chunk-C747DYNT.js";
11
+ import "./chunk-XVEJAFKW.js";
12
+ import "./chunk-XN7S44U4.js";
13
13
  import {
14
14
  fail,
15
15
  maybePrintBanner,
16
16
  parseScope,
17
17
  parseTargets,
18
18
  print
19
- } from "./chunk-H33M77DD.js";
19
+ } from "./chunk-N4ENLZEU.js";
20
20
  import {
21
21
  LineBuffer,
22
22
  idKey,
23
23
  isObject
24
- } from "./chunk-OP4KBXUO.js";
24
+ } from "./chunk-LFWYYWLV.js";
25
25
  import {
26
26
  spawnChild
27
- } from "./chunk-Z2MRSS2I.js";
27
+ } from "./chunk-SLJI5BOM.js";
28
28
  import {
29
29
  detectInstalledPlatforms
30
- } from "./chunk-ISEJWBFF.js";
30
+ } from "./chunk-GZZCHJDP.js";
31
31
  import {
32
32
  explainHooks
33
- } from "./chunk-7F5KHLEP.js";
34
- import "./chunk-I5UXHIDT.js";
35
- import "./chunk-KHO5WNTP.js";
36
- import "./chunk-IVAWP6HH.js";
37
- import "./chunk-3NH2ARIS.js";
33
+ } from "./chunk-L2C5UMKW.js";
34
+ import "./chunk-GCPEPUD7.js";
35
+ import "./chunk-IOJDZJ72.js";
36
+ import "./chunk-4GH2KA7V.js";
37
+ import "./chunk-WPTNMYYM.js";
38
38
  import {
39
39
  findConnectorConfig,
40
40
  listRegisteredConnectors,
41
41
  loadConnectorFromPath,
42
42
  readRegisteredMeta
43
- } from "./chunk-PW42ZXCB.js";
43
+ } from "./chunk-3EP5V2F4.js";
44
44
  import {
45
45
  MCP_PROTOCOL_VERSION
46
- } from "./chunk-5IOTP7LQ.js";
46
+ } from "./chunk-7ZFK2WEO.js";
47
47
  import {
48
48
  REGISTERED_PLATFORM_IDS,
49
49
  loadAdapter
50
- } from "./chunk-D4ELPBB5.js";
51
- import "./chunk-NBYKLUF6.js";
52
- import "./chunk-H53QZQJI.js";
53
- import "./chunk-BEFVJ2OE.js";
54
- import "./chunk-MGTDQLJJ.js";
55
- import "./chunk-4NHXLXSD.js";
56
- import "./chunk-UMQ6NBQR.js";
50
+ } from "./chunk-NDMNMAKP.js";
51
+ import "./chunk-KFPSJRHR.js";
52
+ import "./chunk-PSGTHJQU.js";
53
+ import "./chunk-XXLCMSD3.js";
54
+ import "./chunk-3FINQ2ZF.js";
55
+ import "./chunk-Y3K6KJMS.js";
56
+ import "./chunk-ILWHLGIC.js";
57
57
  import {
58
58
  dataRoot,
59
59
  homeBinPath
60
- } from "./chunk-GZGLCCEE.js";
60
+ } from "./chunk-QYOUQCC6.js";
61
61
 
62
62
  // src/cli/commands/doctor.ts
63
63
  import { parseArgs } from "util";
@@ -627,4 +627,4 @@ async function run(argv) {
627
627
  export {
628
628
  run
629
629
  };
630
- //# sourceMappingURL=doctor-2YDUVSAU.js.map
630
+ //# sourceMappingURL=doctor-CQJ7TZPA.js.map