@rulemetric/cli 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (547) hide show
  1. package/dist/base-command.js +1 -14
  2. package/dist/chunk-2TC6WCUT.js +34 -0
  3. package/dist/chunk-2UC3ULIG.js +1 -0
  4. package/dist/chunk-3AV3GTMP.js +1 -0
  5. package/dist/chunk-4Z5ECCJE.js +1 -0
  6. package/dist/chunk-5RBLIVBF.js +1 -0
  7. package/dist/chunk-67QCYUJU.js +1 -0
  8. package/dist/chunk-6QKI756T.js +2 -0
  9. package/dist/chunk-6V4WM73L.js +1 -0
  10. package/dist/chunk-6XEC6474.js +1 -0
  11. package/dist/chunk-72XLQE3X.js +2 -0
  12. package/dist/chunk-7DFNBRLY.js +61 -0
  13. package/dist/chunk-7XQ4VPLC.js +2 -0
  14. package/dist/chunk-AHRHCERJ.js +1 -0
  15. package/dist/chunk-ARNZQBMX.js +1 -0
  16. package/dist/chunk-CADKBP3D.js +58 -0
  17. package/dist/chunk-CDI5CCHU.js +1 -0
  18. package/dist/chunk-CNQ5DTNC.js +1 -0
  19. package/dist/chunk-CRAKDQ4H.js +1 -0
  20. package/dist/chunk-DNF4HDI6.js +14 -0
  21. package/dist/chunk-DSO2U4Y6.js +56 -0
  22. package/dist/chunk-DT4GKEP5.js +5 -0
  23. package/dist/chunk-DVN7GFJG.js +1 -0
  24. package/dist/chunk-E4SFUPYR.js +15 -0
  25. package/dist/chunk-EUHNCVJQ.js +33 -0
  26. package/dist/chunk-F2VP4B4J.js +1 -0
  27. package/dist/chunk-FEHM5OKW.js +1 -0
  28. package/dist/chunk-FHVBTDBM.js +69 -0
  29. package/dist/chunk-GBMF3TP6.js +1 -0
  30. package/dist/chunk-GY5IJ4RS.js +1 -0
  31. package/dist/chunk-HOHD5HY2.js +5 -0
  32. package/dist/chunk-HP2BUPGS.js +24 -0
  33. package/dist/{chunk-OQBBVTDG.js → chunk-I2DZGJRR.js} +6 -71
  34. package/dist/chunk-I4YJ7NTX.js +1 -0
  35. package/dist/chunk-IY5NXR3N.js +1 -0
  36. package/dist/chunk-IZTIYR5Q.js +2 -0
  37. package/dist/chunk-KKDHD4XA.js +2 -0
  38. package/dist/chunk-LQRYQE6Q.js +4 -0
  39. package/dist/chunk-M6AJDVCL.js +5 -0
  40. package/dist/chunk-MGHFDHHM.js +1 -0
  41. package/dist/chunk-ML36XQW7.js +1 -0
  42. package/dist/chunk-MODXEGCU.js +1 -0
  43. package/dist/chunk-MVM6OGAF.js +4 -0
  44. package/dist/chunk-N7ODX6PO.js +1 -0
  45. package/dist/chunk-NBTC5FNV.js +1 -0
  46. package/dist/chunk-NJ5UED3H.js +1 -0
  47. package/dist/chunk-NVKX36YN.js +3 -0
  48. package/dist/chunk-NVOTOQBA.js +1 -0
  49. package/dist/chunk-O7ZMHCMG.js +31 -0
  50. package/dist/chunk-OMXUK4M6.js +1 -0
  51. package/dist/chunk-PZS2KOU7.js +1 -0
  52. package/dist/chunk-RSKQRVPJ.js +1 -0
  53. package/dist/chunk-RTEATXTP.js +1 -0
  54. package/dist/chunk-RTWRHIKY.js +7 -0
  55. package/dist/chunk-RVO4HV3M.js +1 -0
  56. package/dist/chunk-SBILN45P.js +1 -0
  57. package/dist/chunk-SZ62CP55.js +1 -0
  58. package/dist/chunk-T3W5N3BP.js +2 -0
  59. package/dist/chunk-TDD7IOV2.js +1 -0
  60. package/dist/chunk-TIQ7DQ3M.js +1 -0
  61. package/dist/chunk-TU2AR6XO.js +1 -0
  62. package/dist/chunk-UF6US7VU.js +1 -0
  63. package/dist/chunk-V5QFOF4F.js +9 -0
  64. package/dist/chunk-V5U3E4BZ.js +1 -0
  65. package/dist/chunk-VAFAH6QL.js +1 -0
  66. package/dist/chunk-VMWY7KHH.js +1 -0
  67. package/dist/chunk-VUGRXM7R.js +1 -0
  68. package/dist/{chunk-XZXS2W24.js → chunk-VWEXKDWG.js} +10 -56
  69. package/dist/chunk-W2MV25SB.js +1 -0
  70. package/dist/chunk-WJ3H73G6.js +1 -0
  71. package/dist/chunk-WJUQBMTF.js +9 -0
  72. package/dist/chunk-WLVQJH4E.js +1 -0
  73. package/dist/chunk-WPJ2BND4.js +1 -0
  74. package/dist/chunk-WSCK4WNK.js +1 -0
  75. package/dist/chunk-Y65CSJFF.js +3 -0
  76. package/dist/chunk-ZL4MZMUK.js +34 -0
  77. package/dist/chunk-ZLLER3HP.js +1 -0
  78. package/dist/chunk-ZXF5NE54.js +1 -0
  79. package/dist/commands/antigravity/backfill.js +2 -136
  80. package/dist/commands/auth/create-key.js +1 -52
  81. package/dist/commands/auth/login.js +1 -85
  82. package/dist/commands/auth/logout.js +1 -29
  83. package/dist/commands/auth/revoke-key.js +1 -33
  84. package/dist/commands/auth/signup.js +1 -89
  85. package/dist/commands/auth/status.js +1 -42
  86. package/dist/commands/catalog/index.js +2 -52
  87. package/dist/commands/catalog/recommend.js +3 -43
  88. package/dist/commands/convert.js +1 -75
  89. package/dist/commands/converters.js +1 -54
  90. package/dist/commands/crons/list.js +1 -41
  91. package/dist/commands/crons/run.js +2 -76
  92. package/dist/commands/dashboard.js +1 -46
  93. package/dist/commands/diagnostics.js +1 -117
  94. package/dist/commands/eval-targets/create.js +1 -53
  95. package/dist/commands/eval-targets/list.js +1 -54
  96. package/dist/commands/evals/agent.js +3 -310
  97. package/dist/commands/evals/benchmark.js +6 -73
  98. package/dist/commands/evals/compare.js +13 -96
  99. package/dist/commands/evals/create.js +1 -67
  100. package/dist/commands/evals/optimize-description.js +25 -239
  101. package/dist/commands/evals/optimize.js +5 -211
  102. package/dist/commands/evals/run.js +3 -183
  103. package/dist/commands/gateway/ensure.js +1 -104
  104. package/dist/commands/hooks/emit-instructions.js +1 -25
  105. package/dist/commands/hooks/install.js +1 -442
  106. package/dist/commands/hooks/run.js +3 -81
  107. package/dist/commands/hooks/uninstall.js +2 -156
  108. package/dist/commands/import.js +1 -66
  109. package/dist/commands/instructions/create.js +1 -74
  110. package/dist/commands/instructions/delete.js +1 -55
  111. package/dist/commands/instructions/fork.js +1 -34
  112. package/dist/commands/instructions/get.js +1 -68
  113. package/dist/commands/instructions/list.js +1 -65
  114. package/dist/commands/instructions/promote.js +1 -42
  115. package/dist/commands/instructions/pull.js +1 -39
  116. package/dist/commands/instructions/upstream.js +1 -38
  117. package/dist/commands/instructions/versions.js +1 -53
  118. package/dist/commands/launcher.js +1 -40
  119. package/dist/commands/org/current.js +1 -77
  120. package/dist/commands/org/list.js +1 -52
  121. package/dist/commands/org/switch.js +1 -71
  122. package/dist/commands/packs/add-instruction.js +1 -37
  123. package/dist/commands/packs/apply.js +1 -39
  124. package/dist/commands/packs/create.js +1 -50
  125. package/dist/commands/packs/fork.js +1 -34
  126. package/dist/commands/packs/get.js +3 -56
  127. package/dist/commands/packs/list.js +1 -66
  128. package/dist/commands/packs/remove-instruction.js +1 -35
  129. package/dist/commands/proxy/env.js +3 -131
  130. package/dist/commands/proxy/logs.js +3 -85
  131. package/dist/commands/proxy/restart.js +1 -35
  132. package/dist/commands/proxy/setup.js +15 -454
  133. package/dist/commands/proxy/start.js +10 -333
  134. package/dist/commands/proxy/status.js +1 -78
  135. package/dist/commands/proxy/stop.js +1 -94
  136. package/dist/commands/research/backfill.js +1 -59
  137. package/dist/commands/research/tail-transcript.js +1 -70
  138. package/dist/commands/service/install.js +57 -796
  139. package/dist/commands/service/status.js +1 -180
  140. package/dist/commands/service/uninstall.js +1 -119
  141. package/dist/commands/sessions/analyze.js +3 -48
  142. package/dist/commands/sessions/end.js +2 -85
  143. package/dist/commands/sessions/import.js +1 -46
  144. package/dist/commands/sessions/list.js +1 -48
  145. package/dist/commands/sessions/start.js +3 -90
  146. package/dist/commands/setup.js +3 -150
  147. package/dist/commands/skills/export-approved.js +1 -46
  148. package/dist/commands/skills/install.js +4 -107
  149. package/dist/commands/skills/list.js +4 -65
  150. package/dist/commands/skills/publish.js +2 -98
  151. package/dist/commands/skills/search.js +3 -72
  152. package/dist/commands/skills/update.js +3 -55
  153. package/dist/commands/suggestions/accept.js +8 -84
  154. package/dist/commands/suggestions/dismiss.js +1 -45
  155. package/dist/commands/suggestions/list.js +1 -61
  156. package/dist/commands/suggestions/refresh.js +1 -27
  157. package/dist/cursor-T7AMCMKS.js +13 -0
  158. package/dist/dashboard/Dashboard.js +1 -243
  159. package/dist/dashboard/data.js +1 -15
  160. package/dist/dashboard/launcher/ActiveJobsList.js +1 -10
  161. package/dist/dashboard/launcher/LauncherOverlay.js +1 -16
  162. package/dist/dashboard/launcher/NewLaunchForm.js +1 -10
  163. package/dist/dashboard/launcher/api.js +1 -16
  164. package/dist/dashboard/launcher/tool-labels.js +1 -10
  165. package/dist/dashboard/launcher/types.js +0 -1
  166. package/dist/dashboard/launcher/useLaunchJobs.js +1 -11
  167. package/dist/dashboard/panels/friction.js +1 -10
  168. package/dist/dashboard/panels/insights.js +1 -10
  169. package/dist/dashboard/panels/instructions.js +1 -10
  170. package/dist/dashboard/panels/outcomes.js +1 -10
  171. package/dist/dashboard/panels/overview.js +1 -10
  172. package/dist/dashboard/panels/shared.js +1 -19
  173. package/dist/dashboard/types.js +1 -24
  174. package/dist/dist-FAVALJ5C.js +1 -0
  175. package/dist/index.js +1 -8
  176. package/dist/lib/active-org-refresh.js +1 -15
  177. package/dist/lib/active-org.js +1 -16
  178. package/dist/lib/agent-loop.js +1 -47
  179. package/dist/lib/antigravity-transcript.js +1 -22
  180. package/dist/lib/api-client.js +1 -23
  181. package/dist/lib/auth.js +1 -30
  182. package/dist/lib/capture-hosts.js +1 -8
  183. package/dist/lib/detect-languages.js +1 -8
  184. package/dist/lib/detect-tmux.js +1 -9
  185. package/dist/lib/diagnostics.js +1 -12
  186. package/dist/lib/editor-proxy-pins.js +1 -14
  187. package/dist/lib/ensure-api-key.js +1 -11
  188. package/dist/lib/eval-analyzer.js +1 -9
  189. package/dist/lib/eval-benchmark.js +1 -12
  190. package/dist/lib/eval-conversation-handler.js +1 -13
  191. package/dist/lib/eval-executor.js +1 -8
  192. package/dist/lib/eval-grader.js +1 -9
  193. package/dist/lib/eval-meta-judge.js +1 -9
  194. package/dist/lib/eval-trigger-tester.js +1 -11
  195. package/dist/lib/eval-worktree.js +1 -8
  196. package/dist/lib/gateway-entry.js +1 -61
  197. package/dist/lib/gateway-identity.js +1 -16
  198. package/dist/lib/gateway-lifecycle.js +1 -32
  199. package/dist/lib/gateway.js +1 -14
  200. package/dist/lib/handlers/cron-refresh-pricing.js +1 -0
  201. package/dist/lib/handlers/cron-refresh-skills.js +1 -8
  202. package/dist/lib/handlers/cron-suggest-instructions.js +1 -16
  203. package/dist/lib/handlers/process-announcement.js +1 -11
  204. package/dist/lib/handlers/process-changelog.js +1 -11
  205. package/dist/lib/handlers/process-conversation.js +1 -14
  206. package/dist/lib/handlers/process-eval.js +1 -15
  207. package/dist/lib/handlers/process-insights.js +1 -15
  208. package/dist/lib/handlers/process-launch.js +1 -16
  209. package/dist/lib/handlers/process-run-cleanup.js +1 -13
  210. package/dist/lib/handlers/process-send-message.js +1 -10
  211. package/dist/lib/handlers/process-session-goal.js +1 -12
  212. package/dist/lib/hooks-config.js +1 -81
  213. package/dist/lib/insights-prompts.js +1 -12
  214. package/dist/lib/instruction-snapshot.js +1 -8
  215. package/dist/lib/job-helpers.js +0 -1
  216. package/dist/lib/llm-client.js +1 -20
  217. package/dist/lib/manual-tasks-meta.js +1 -8
  218. package/dist/lib/manual-tasks.js +1 -80
  219. package/dist/lib/output.js +1 -12
  220. package/dist/lib/permanent-job-error.js +1 -10
  221. package/dist/lib/pid-identity.js +1 -14
  222. package/dist/lib/proxy-respawn.js +1 -12
  223. package/dist/lib/research-client.js +1 -12
  224. package/dist/lib/setup-steps.js +1 -33
  225. package/dist/lib/skills-loader.js +1 -8
  226. package/dist/lib/statusline-shim.js +1 -20
  227. package/dist/lib/telemetry.js +1 -16
  228. package/dist/lib/terminal-target-heartbeat.js +1 -15
  229. package/dist/lib/tmux.js +1 -16
  230. package/dist/lib/transcript-rate-limit-watcher.js +1 -22
  231. package/dist/lib/types.js +1 -8
  232. package/dist/lib/version-check.js +1 -12
  233. package/dist/lib/which.js +1 -8
  234. package/dist/lib/worktree.js +1 -17
  235. package/dist/opencode-QRJ3BH23.js +1 -0
  236. package/oclif.manifest.json +135 -135
  237. package/package.json +9 -8
  238. package/dist/base-command.js.map +0 -7
  239. package/dist/chunk-2HK7RGZB.js +0 -82
  240. package/dist/chunk-2HK7RGZB.js.map +0 -7
  241. package/dist/chunk-2ORJWTNZ.js +0 -307
  242. package/dist/chunk-2ORJWTNZ.js.map +0 -7
  243. package/dist/chunk-37LN5O7M.js +0 -59
  244. package/dist/chunk-37LN5O7M.js.map +0 -7
  245. package/dist/chunk-3S7223UR.js +0 -81
  246. package/dist/chunk-3S7223UR.js.map +0 -7
  247. package/dist/chunk-3TIMQ3O6.js +0 -96
  248. package/dist/chunk-3TIMQ3O6.js.map +0 -7
  249. package/dist/chunk-42GFSAJP.js +0 -32
  250. package/dist/chunk-42GFSAJP.js.map +0 -7
  251. package/dist/chunk-4BHDHHKL.js +0 -48
  252. package/dist/chunk-4BHDHHKL.js.map +0 -7
  253. package/dist/chunk-4D537GXE.js +0 -99
  254. package/dist/chunk-4D537GXE.js.map +0 -7
  255. package/dist/chunk-4LYCDS2N.js +0 -139
  256. package/dist/chunk-4LYCDS2N.js.map +0 -7
  257. package/dist/chunk-4SCZK7AL.js +0 -129
  258. package/dist/chunk-4SCZK7AL.js.map +0 -7
  259. package/dist/chunk-4XAWK6S2.js +0 -161
  260. package/dist/chunk-4XAWK6S2.js.map +0 -7
  261. package/dist/chunk-52WIYRZH.js +0 -73
  262. package/dist/chunk-52WIYRZH.js.map +0 -7
  263. package/dist/chunk-5BAOGNUH.js +0 -79
  264. package/dist/chunk-5BAOGNUH.js.map +0 -7
  265. package/dist/chunk-5EDMGK3W.js +0 -39
  266. package/dist/chunk-5EDMGK3W.js.map +0 -7
  267. package/dist/chunk-6NBS5XFS.js +0 -99
  268. package/dist/chunk-6NBS5XFS.js.map +0 -7
  269. package/dist/chunk-7LVVKQMZ.js +0 -98
  270. package/dist/chunk-7LVVKQMZ.js.map +0 -7
  271. package/dist/chunk-7NNFYWKK.js +0 -243
  272. package/dist/chunk-7NNFYWKK.js.map +0 -7
  273. package/dist/chunk-7NYKWN7U.js +0 -12
  274. package/dist/chunk-7NYKWN7U.js.map +0 -7
  275. package/dist/chunk-BO76WKJR.js +0 -42
  276. package/dist/chunk-BO76WKJR.js.map +0 -7
  277. package/dist/chunk-BRD5RUGM.js +0 -53
  278. package/dist/chunk-BRD5RUGM.js.map +0 -7
  279. package/dist/chunk-BT6Z6545.js +0 -43
  280. package/dist/chunk-BT6Z6545.js.map +0 -7
  281. package/dist/chunk-BZDKEQKU.js +0 -39
  282. package/dist/chunk-BZDKEQKU.js.map +0 -7
  283. package/dist/chunk-DGHWRQXL.js +0 -17
  284. package/dist/chunk-DGHWRQXL.js.map +0 -7
  285. package/dist/chunk-DIQUV5FR.js +0 -257
  286. package/dist/chunk-DIQUV5FR.js.map +0 -7
  287. package/dist/chunk-DMX5YOTM.js +0 -167
  288. package/dist/chunk-DMX5YOTM.js.map +0 -7
  289. package/dist/chunk-DNKSVWMD.js +0 -108
  290. package/dist/chunk-DNKSVWMD.js.map +0 -7
  291. package/dist/chunk-DQ3KELXD.js +0 -74
  292. package/dist/chunk-DQ3KELXD.js.map +0 -7
  293. package/dist/chunk-DUJSRBLP.js +0 -122
  294. package/dist/chunk-DUJSRBLP.js.map +0 -7
  295. package/dist/chunk-E3BIT53W.js +0 -154
  296. package/dist/chunk-E3BIT53W.js.map +0 -7
  297. package/dist/chunk-EARWG4EV.js +0 -22
  298. package/dist/chunk-EARWG4EV.js.map +0 -7
  299. package/dist/chunk-EI5BHWXA.js +0 -1662
  300. package/dist/chunk-EI5BHWXA.js.map +0 -7
  301. package/dist/chunk-EJ3YAFDP.js +0 -37
  302. package/dist/chunk-EJ3YAFDP.js.map +0 -7
  303. package/dist/chunk-EKJ2CABV.js +0 -76
  304. package/dist/chunk-EKJ2CABV.js.map +0 -7
  305. package/dist/chunk-EKP32DLN.js +0 -16
  306. package/dist/chunk-EKP32DLN.js.map +0 -7
  307. package/dist/chunk-EYPAAGN5.js +0 -51
  308. package/dist/chunk-EYPAAGN5.js.map +0 -7
  309. package/dist/chunk-F5N6CLJJ.js +0 -1995
  310. package/dist/chunk-F5N6CLJJ.js.map +0 -7
  311. package/dist/chunk-GIUBARWS.js +0 -99
  312. package/dist/chunk-GIUBARWS.js.map +0 -7
  313. package/dist/chunk-GQNJO2P2.js +0 -102
  314. package/dist/chunk-GQNJO2P2.js.map +0 -7
  315. package/dist/chunk-IJGF2ETG.js +0 -68
  316. package/dist/chunk-IJGF2ETG.js.map +0 -7
  317. package/dist/chunk-ILYDELUE.js +0 -46
  318. package/dist/chunk-ILYDELUE.js.map +0 -7
  319. package/dist/chunk-J6QFW4LD.js +0 -55
  320. package/dist/chunk-J6QFW4LD.js.map +0 -7
  321. package/dist/chunk-J7N3DLH6.js +0 -117
  322. package/dist/chunk-J7N3DLH6.js.map +0 -7
  323. package/dist/chunk-JFDWKLQC.js +0 -23
  324. package/dist/chunk-JFDWKLQC.js.map +0 -7
  325. package/dist/chunk-JULOLTZS.js +0 -61
  326. package/dist/chunk-JULOLTZS.js.map +0 -7
  327. package/dist/chunk-KJKBDQ4D.js +0 -220
  328. package/dist/chunk-KJKBDQ4D.js.map +0 -7
  329. package/dist/chunk-KRBQLMOP.js +0 -41
  330. package/dist/chunk-KRBQLMOP.js.map +0 -7
  331. package/dist/chunk-LXKPNATC.js +0 -624
  332. package/dist/chunk-LXKPNATC.js.map +0 -7
  333. package/dist/chunk-NSBPE2FW.js +0 -17
  334. package/dist/chunk-NSBPE2FW.js.map +0 -7
  335. package/dist/chunk-OCKHW72U.js +0 -55
  336. package/dist/chunk-OCKHW72U.js.map +0 -7
  337. package/dist/chunk-OKWCKCPE.js +0 -414
  338. package/dist/chunk-OKWCKCPE.js.map +0 -7
  339. package/dist/chunk-OO7JDFS4.js +0 -157
  340. package/dist/chunk-OO7JDFS4.js.map +0 -7
  341. package/dist/chunk-OQBBVTDG.js.map +0 -7
  342. package/dist/chunk-OQSQC7VB.js +0 -77
  343. package/dist/chunk-OQSQC7VB.js.map +0 -7
  344. package/dist/chunk-PED7L5GX.js +0 -92
  345. package/dist/chunk-PED7L5GX.js.map +0 -7
  346. package/dist/chunk-QJQS6TUN.js +0 -44
  347. package/dist/chunk-QJQS6TUN.js.map +0 -7
  348. package/dist/chunk-QN34TGD3.js +0 -83
  349. package/dist/chunk-QN34TGD3.js.map +0 -7
  350. package/dist/chunk-QSN77T7C.js +0 -250
  351. package/dist/chunk-QSN77T7C.js.map +0 -7
  352. package/dist/chunk-R56PH7YE.js +0 -78
  353. package/dist/chunk-R56PH7YE.js.map +0 -7
  354. package/dist/chunk-RAXRZYOB.js +0 -299
  355. package/dist/chunk-RAXRZYOB.js.map +0 -7
  356. package/dist/chunk-RH7NHVSK.js +0 -16
  357. package/dist/chunk-RH7NHVSK.js.map +0 -7
  358. package/dist/chunk-RMTT4KDY.js +0 -191
  359. package/dist/chunk-RMTT4KDY.js.map +0 -7
  360. package/dist/chunk-RQ2TMLKG.js +0 -142
  361. package/dist/chunk-RQ2TMLKG.js.map +0 -7
  362. package/dist/chunk-SO3T35U7.js +0 -80
  363. package/dist/chunk-SO3T35U7.js.map +0 -7
  364. package/dist/chunk-UKMLICWS.js +0 -51
  365. package/dist/chunk-UKMLICWS.js.map +0 -7
  366. package/dist/chunk-VA5FFOS7.js +0 -243
  367. package/dist/chunk-VA5FFOS7.js.map +0 -7
  368. package/dist/chunk-W2YERO7E.js +0 -170
  369. package/dist/chunk-W2YERO7E.js.map +0 -7
  370. package/dist/chunk-W53GKIZQ.js +0 -50
  371. package/dist/chunk-W53GKIZQ.js.map +0 -7
  372. package/dist/chunk-WA4MWYRW.js +0 -254
  373. package/dist/chunk-WA4MWYRW.js.map +0 -7
  374. package/dist/chunk-XC6M4CSW.js +0 -44
  375. package/dist/chunk-XC6M4CSW.js.map +0 -7
  376. package/dist/chunk-XK42ZBDE.js +0 -23
  377. package/dist/chunk-XK42ZBDE.js.map +0 -7
  378. package/dist/chunk-XUV6DDBF.js +0 -124
  379. package/dist/chunk-XUV6DDBF.js.map +0 -7
  380. package/dist/chunk-XZXS2W24.js.map +0 -7
  381. package/dist/chunk-Y3WD5CSW.js +0 -49
  382. package/dist/chunk-Y3WD5CSW.js.map +0 -7
  383. package/dist/chunk-Y4BJXXYV.js +0 -148
  384. package/dist/chunk-Y4BJXXYV.js.map +0 -7
  385. package/dist/chunk-YNIH3KMU.js +0 -121
  386. package/dist/chunk-YNIH3KMU.js.map +0 -7
  387. package/dist/chunk-ZUJBM6HE.js +0 -125
  388. package/dist/chunk-ZUJBM6HE.js.map +0 -7
  389. package/dist/commands/antigravity/backfill.js.map +0 -7
  390. package/dist/commands/auth/create-key.js.map +0 -7
  391. package/dist/commands/auth/login.js.map +0 -7
  392. package/dist/commands/auth/logout.js.map +0 -7
  393. package/dist/commands/auth/revoke-key.js.map +0 -7
  394. package/dist/commands/auth/signup.js.map +0 -7
  395. package/dist/commands/auth/status.js.map +0 -7
  396. package/dist/commands/catalog/index.js.map +0 -7
  397. package/dist/commands/catalog/recommend.js.map +0 -7
  398. package/dist/commands/convert.js.map +0 -7
  399. package/dist/commands/converters.js.map +0 -7
  400. package/dist/commands/crons/list.js.map +0 -7
  401. package/dist/commands/crons/run.js.map +0 -7
  402. package/dist/commands/dashboard.js.map +0 -7
  403. package/dist/commands/diagnostics.js.map +0 -7
  404. package/dist/commands/eval-targets/create.js.map +0 -7
  405. package/dist/commands/eval-targets/list.js.map +0 -7
  406. package/dist/commands/evals/agent.js.map +0 -7
  407. package/dist/commands/evals/benchmark.js.map +0 -7
  408. package/dist/commands/evals/compare.js.map +0 -7
  409. package/dist/commands/evals/create.js.map +0 -7
  410. package/dist/commands/evals/optimize-description.js.map +0 -7
  411. package/dist/commands/evals/optimize.js.map +0 -7
  412. package/dist/commands/evals/run.js.map +0 -7
  413. package/dist/commands/gateway/ensure.js.map +0 -7
  414. package/dist/commands/hooks/emit-instructions.js.map +0 -7
  415. package/dist/commands/hooks/install.js.map +0 -7
  416. package/dist/commands/hooks/run.js.map +0 -7
  417. package/dist/commands/hooks/uninstall.js.map +0 -7
  418. package/dist/commands/import.js.map +0 -7
  419. package/dist/commands/instructions/create.js.map +0 -7
  420. package/dist/commands/instructions/delete.js.map +0 -7
  421. package/dist/commands/instructions/fork.js.map +0 -7
  422. package/dist/commands/instructions/get.js.map +0 -7
  423. package/dist/commands/instructions/list.js.map +0 -7
  424. package/dist/commands/instructions/promote.js.map +0 -7
  425. package/dist/commands/instructions/pull.js.map +0 -7
  426. package/dist/commands/instructions/upstream.js.map +0 -7
  427. package/dist/commands/instructions/versions.js.map +0 -7
  428. package/dist/commands/launcher.js.map +0 -7
  429. package/dist/commands/org/current.js.map +0 -7
  430. package/dist/commands/org/list.js.map +0 -7
  431. package/dist/commands/org/switch.js.map +0 -7
  432. package/dist/commands/packs/add-instruction.js.map +0 -7
  433. package/dist/commands/packs/apply.js.map +0 -7
  434. package/dist/commands/packs/create.js.map +0 -7
  435. package/dist/commands/packs/fork.js.map +0 -7
  436. package/dist/commands/packs/get.js.map +0 -7
  437. package/dist/commands/packs/list.js.map +0 -7
  438. package/dist/commands/packs/remove-instruction.js.map +0 -7
  439. package/dist/commands/proxy/env.js.map +0 -7
  440. package/dist/commands/proxy/logs.js.map +0 -7
  441. package/dist/commands/proxy/restart.js.map +0 -7
  442. package/dist/commands/proxy/setup.js.map +0 -7
  443. package/dist/commands/proxy/start.js.map +0 -7
  444. package/dist/commands/proxy/status.js.map +0 -7
  445. package/dist/commands/proxy/stop.js.map +0 -7
  446. package/dist/commands/research/backfill.js.map +0 -7
  447. package/dist/commands/research/tail-transcript.js.map +0 -7
  448. package/dist/commands/service/install.js.map +0 -7
  449. package/dist/commands/service/status.js.map +0 -7
  450. package/dist/commands/service/uninstall.js.map +0 -7
  451. package/dist/commands/sessions/analyze.js.map +0 -7
  452. package/dist/commands/sessions/end.js.map +0 -7
  453. package/dist/commands/sessions/import.js.map +0 -7
  454. package/dist/commands/sessions/list.js.map +0 -7
  455. package/dist/commands/sessions/start.js.map +0 -7
  456. package/dist/commands/setup.js.map +0 -7
  457. package/dist/commands/skills/export-approved.js.map +0 -7
  458. package/dist/commands/skills/install.js.map +0 -7
  459. package/dist/commands/skills/list.js.map +0 -7
  460. package/dist/commands/skills/publish.js.map +0 -7
  461. package/dist/commands/skills/search.js.map +0 -7
  462. package/dist/commands/skills/update.js.map +0 -7
  463. package/dist/commands/suggestions/accept.js.map +0 -7
  464. package/dist/commands/suggestions/dismiss.js.map +0 -7
  465. package/dist/commands/suggestions/list.js.map +0 -7
  466. package/dist/commands/suggestions/refresh.js.map +0 -7
  467. package/dist/cursor-BGQJ4CWI.js +0 -134
  468. package/dist/cursor-BGQJ4CWI.js.map +0 -7
  469. package/dist/dashboard/Dashboard.js.map +0 -7
  470. package/dist/dashboard/data.js.map +0 -7
  471. package/dist/dashboard/launcher/ActiveJobsList.js.map +0 -7
  472. package/dist/dashboard/launcher/LauncherOverlay.js.map +0 -7
  473. package/dist/dashboard/launcher/NewLaunchForm.js.map +0 -7
  474. package/dist/dashboard/launcher/api.js.map +0 -7
  475. package/dist/dashboard/launcher/tool-labels.js.map +0 -7
  476. package/dist/dashboard/launcher/types.js.map +0 -7
  477. package/dist/dashboard/launcher/useLaunchJobs.js.map +0 -7
  478. package/dist/dashboard/panels/friction.js.map +0 -7
  479. package/dist/dashboard/panels/insights.js.map +0 -7
  480. package/dist/dashboard/panels/instructions.js.map +0 -7
  481. package/dist/dashboard/panels/outcomes.js.map +0 -7
  482. package/dist/dashboard/panels/overview.js.map +0 -7
  483. package/dist/dashboard/panels/shared.js.map +0 -7
  484. package/dist/dashboard/types.js.map +0 -7
  485. package/dist/dist-UNYP5PJR.js +0 -56
  486. package/dist/dist-UNYP5PJR.js.map +0 -7
  487. package/dist/index.js.map +0 -7
  488. package/dist/lib/active-org-refresh.js.map +0 -7
  489. package/dist/lib/active-org.js.map +0 -7
  490. package/dist/lib/agent-loop.js.map +0 -7
  491. package/dist/lib/antigravity-transcript.js.map +0 -7
  492. package/dist/lib/api-client.js.map +0 -7
  493. package/dist/lib/auth.js.map +0 -7
  494. package/dist/lib/capture-hosts.js.map +0 -7
  495. package/dist/lib/detect-languages.js.map +0 -7
  496. package/dist/lib/detect-tmux.js.map +0 -7
  497. package/dist/lib/diagnostics.js.map +0 -7
  498. package/dist/lib/editor-proxy-pins.js.map +0 -7
  499. package/dist/lib/ensure-api-key.js.map +0 -7
  500. package/dist/lib/eval-analyzer.js.map +0 -7
  501. package/dist/lib/eval-benchmark.js.map +0 -7
  502. package/dist/lib/eval-conversation-handler.js.map +0 -7
  503. package/dist/lib/eval-executor.js.map +0 -7
  504. package/dist/lib/eval-grader.js.map +0 -7
  505. package/dist/lib/eval-meta-judge.js.map +0 -7
  506. package/dist/lib/eval-trigger-tester.js.map +0 -7
  507. package/dist/lib/eval-worktree.js.map +0 -7
  508. package/dist/lib/gateway-entry.js.map +0 -7
  509. package/dist/lib/gateway-identity.js.map +0 -7
  510. package/dist/lib/gateway-lifecycle.js.map +0 -7
  511. package/dist/lib/gateway.js.map +0 -7
  512. package/dist/lib/handlers/cron-refresh-skills.js.map +0 -7
  513. package/dist/lib/handlers/cron-suggest-instructions.js.map +0 -7
  514. package/dist/lib/handlers/process-announcement.js.map +0 -7
  515. package/dist/lib/handlers/process-changelog.js.map +0 -7
  516. package/dist/lib/handlers/process-conversation.js.map +0 -7
  517. package/dist/lib/handlers/process-eval.js.map +0 -7
  518. package/dist/lib/handlers/process-insights.js.map +0 -7
  519. package/dist/lib/handlers/process-launch.js.map +0 -7
  520. package/dist/lib/handlers/process-run-cleanup.js.map +0 -7
  521. package/dist/lib/handlers/process-send-message.js.map +0 -7
  522. package/dist/lib/handlers/process-session-goal.js.map +0 -7
  523. package/dist/lib/hooks-config.js.map +0 -7
  524. package/dist/lib/insights-prompts.js.map +0 -7
  525. package/dist/lib/instruction-snapshot.js.map +0 -7
  526. package/dist/lib/job-helpers.js.map +0 -7
  527. package/dist/lib/llm-client.js.map +0 -7
  528. package/dist/lib/manual-tasks-meta.js.map +0 -7
  529. package/dist/lib/manual-tasks.js.map +0 -7
  530. package/dist/lib/output.js.map +0 -7
  531. package/dist/lib/permanent-job-error.js.map +0 -7
  532. package/dist/lib/pid-identity.js.map +0 -7
  533. package/dist/lib/proxy-respawn.js.map +0 -7
  534. package/dist/lib/research-client.js.map +0 -7
  535. package/dist/lib/setup-steps.js.map +0 -7
  536. package/dist/lib/skills-loader.js.map +0 -7
  537. package/dist/lib/statusline-shim.js.map +0 -7
  538. package/dist/lib/telemetry.js.map +0 -7
  539. package/dist/lib/terminal-target-heartbeat.js.map +0 -7
  540. package/dist/lib/tmux.js.map +0 -7
  541. package/dist/lib/transcript-rate-limit-watcher.js.map +0 -7
  542. package/dist/lib/types.js.map +0 -7
  543. package/dist/lib/version-check.js.map +0 -7
  544. package/dist/lib/which.js.map +0 -7
  545. package/dist/lib/worktree.js.map +0 -7
  546. package/dist/opencode-IQ7X5YMJ.js +0 -190
  547. package/dist/opencode-IQ7X5YMJ.js.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/lib/setup-steps.ts"],
4
- "sourcesContent": ["import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { isAuthenticated, getDefaultConfigDir } from './auth.js';\nimport { apiGet } from './api-client.js';\nimport { GATEWAY_PORT, isGatewayListening } from './gateway-lifecycle.js';\nimport {\n detectInstalledTools,\n mergeClaudeCodeHooks,\n hasRulemetricClaudeHooks,\n removeRulemetricMachineState,\n RULEMETRIC_NO_PROXY,\n} from './hooks-config.js';\n\n/**\n * Data model for the four core onboarding steps, shared (by step-ID list +\n * install-monotonicity) with the web reducer `deriveSetupProgress()`.\n *\n * The two surfaces are separate implementations by necessity, but they no\n * longer read *different sources* for the same fact:\n * - Server-observable signals (cli-worker heartbeat / captured session) are\n * unified through the shared `GET /api/setup/status` endpoint \u2014 the CLI's\n * `workerActive()` and the web's `use-setup-progress.ts` both bottom out on\n * that one server-side authority, so they cannot drift on it.\n * - Local signals (auth = `~/.config/rulemetric` creds via `isAuthenticated()`,\n * hooks = cwd `.claude/settings.local.json`, falling back to the shared\n * `settings.json` for legacy installs) stay machine-local: the browser\n * cannot read the user's filesystem, so these legitimately differ from the\n * web's account-scoped proxies for the same step (see the design doc's\n * \"Shared step model, two implementations\").\n * Each step exposes:\n * - detect(): is it done? (reads a local/remote signal, never mutates)\n * - plan(): what would apply() write? (read-only, returns diffs/artifacts)\n * - apply(): perform the step (thin \u2014 shells out to existing commands)\n */\n\nexport const CORE_STEP_IDS = ['install', 'auth', 'hooks', 'worker'] as const;\n// Optional steps live in the walk (offered, recommended) but do NOT gate\n// `coreComplete` \u2014 declining them still leaves the user fully onboarded. The\n// proxy/CA capture now ships as the DEFAULT of the core `hooks` step (see that\n// step's plan(), which fully discloses the invasive MITM/keychain side effects\n// up front); it is not a separate opt-in step. The mechanism is retained for\n// any future optional step.\nexport const OPTIONAL_STEP_IDS = [] as const;\nexport const ALL_STEP_IDS = [...CORE_STEP_IDS, ...OPTIONAL_STEP_IDS] as const;\nexport type StepId = (typeof ALL_STEP_IDS)[number];\n\nexport type ArtifactKind = 'file' | 'command' | 'service';\n\nexport interface Artifact {\n path: string;\n kind: ArtifactKind;\n before: string | null;\n after: string | null;\n /** True when `after` contains a masked secret (never the raw value). */\n masked?: boolean;\n}\n\nexport interface StepPlan {\n id: StepId;\n title: string;\n command?: string;\n artifacts: Artifact[];\n}\n\nexport interface SetupContext {\n projectPath: string;\n configDir: string;\n}\n\nexport interface SetupStep {\n id: StepId;\n title: string;\n command?: string;\n /** Optional steps are offered in the walk but never gate `coreComplete`. */\n optional?: boolean;\n detect(ctx: SetupContext): Promise<boolean>;\n plan(ctx: SetupContext): Promise<StepPlan>;\n apply(ctx: SetupContext): Promise<void>;\n}\n\nexport interface StepStatus {\n id: StepId;\n done: boolean;\n}\n\nexport interface SetupStatus {\n steps: StepStatus[];\n coreComplete: boolean;\n currentStepId: StepId | null;\n}\n\n/** Build the default context from the current process (cwd + config dir). */\nexport function defaultContext(): SetupContext {\n return {\n projectPath: process.cwd(),\n configDir: getDefaultConfigDir(),\n };\n}\n\nconst CERT_PATH = join(homedir(), '.mitmproxy', 'mitmproxy-ca-cert.pem');\n\n/**\n * Mask a secret: keep the first 4 and last 4 characters, replace the middle\n * with asterisks. Strings too short to reveal safely are fully masked.\n */\nexport function maskToken(s: string): string {\n if (!s) return '';\n if (s.length <= 8) return '*'.repeat(s.length);\n const head = s.slice(0, 4);\n const tail = s.slice(-4);\n return `${head}${'*'.repeat(Math.max(4, s.length - 8))}${tail}`;\n}\n\n/**\n * Simple line-oriented +/- diff for terminal display. Not a real LCS \u2014 good\n * enough to show a human/agent exactly which lines an artifact would change.\n */\nexport function renderDiff(before: string | null, after: string | null): string {\n const beforeLines = before === null ? [] : before.replace(/\\n$/, '').split('\\n');\n const afterLines = after === null ? [] : after.replace(/\\n$/, '').split('\\n');\n const out: string[] = [];\n const max = Math.max(beforeLines.length, afterLines.length);\n for (let i = 0; i < max; i++) {\n const b = beforeLines[i];\n const a = afterLines[i];\n if (b === a) {\n out.push(` ${b}`);\n } else {\n if (b !== undefined) out.push(`-${b}`);\n if (a !== undefined) out.push(`+${a}`);\n }\n }\n return out.join('\\n');\n}\n\n/** Read a single KEY=value from the env file, or null if absent. */\nfunction readEnvValue(configDir: string, key: string): string | null {\n const envPath = join(configDir, 'env');\n if (!existsSync(envPath)) return null;\n try {\n const content = readFileSync(envPath, 'utf-8');\n const match = content.match(new RegExp(`^${key}=(.+)$`, 'm'));\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Is a CLI worker heartbeat active for the caller? (best-effort, network)\n *\n * Reads the shared `GET /api/setup/status` endpoint's `workerActive` field\n * rather than re-deriving it from `/api/workers/status`. That endpoint computes\n * the cli-worker heartbeat signal server-side using the SAME window +\n * staleness thresholds + `workerType === 'cli-worker'` filter the web strip\n * uses, so the CLI and web can't drift on this fact by construction (they used\n * to each filter/threshold client-side, which is exactly how they could\n * disagree). Fast-fails after 2.5s so `setup --json` / `setup plan` degrade to\n * \"not running\" instead of blocking on a stalled connection \u2014 detection is a\n * UX signal, not a correctness gate; offline \u2192 false.\n */\nasync function workerActive(): Promise<boolean> {\n if (!isAuthenticated()) return false;\n try {\n const status = await Promise.race([\n apiGet<{\n cliEverConnected?: boolean;\n anyCapturedSession?: boolean;\n workerActive?: boolean;\n }>('/api/setup/status'),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('setup/status timeout')), 2500),\n ),\n ]);\n return status?.workerActive === true;\n } catch {\n return false;\n }\n}\n\n/** Read a project `.claude/<fileName>` settings file for a read-only preview (no throw). */\nfunction readProjectSettings(\n projectPath: string,\n fileName: string,\n): { settings: Record<string, unknown>; before: string | null } {\n const settingsPath = join(projectPath, '.claude', fileName);\n let settings: Record<string, unknown> = {};\n let before: string | null = null;\n if (existsSync(settingsPath)) {\n try {\n before = readFileSync(settingsPath, 'utf-8').replace(/\\n$/, '');\n settings = JSON.parse(before) as Record<string, unknown>;\n } catch {\n settings = {};\n }\n }\n return { settings, before };\n}\n\n/**\n * Compute the in-memory `.claude/settings.local.json` after the RuleMetric\n * session HOOKS are merged, WITHOUT touching disk. This is the \u00A71 (hooks)\n * portion of the diff; the proxy env block that `hooks install` \u00A72 also writes\n * is disclosed separately via projectProxyEnvSettings so the two changes read\n * distinctly. Machine-local state targets settings.local.json \u2014 the shared\n * settings.json is typically checked into git (see claudeSettingsInstallPath).\n */\nfunction projectHooksSettings(projectPath: string): { before: string | null; after: string } {\n const { settings, before } = readProjectSettings(projectPath, 'settings.local.json');\n mergeClaudeCodeHooks(settings);\n return { before, after: JSON.stringify(settings, null, 2) };\n}\n\n/**\n * The proxy-env addition to `.claude/settings.local.json` that `hooks install`\n * \u00A72 makes on the default path \u2014 for the disclosure diff only. Read-only.\n */\nfunction projectProxyEnvSettings(projectPath: string): { before: string | null; after: string } {\n const { settings, before } = readProjectSettings(projectPath, 'settings.local.json');\n const env = { ...((settings.env as Record<string, string>) ?? {}) };\n env.HTTPS_PROXY = `http://localhost:${GATEWAY_PORT}`;\n env.NO_PROXY = RULEMETRIC_NO_PROXY;\n env.NODE_EXTRA_CA_CERTS = CERT_PATH;\n return { before, after: JSON.stringify({ ...settings, env }, null, 2) };\n}\n\n/**\n * The migration edit `hooks install` makes to the SHARED `.claude/settings.json`\n * when it still carries RuleMetric machine-local state (legacy installs wrote\n * hooks + proxy env there; that state moves to settings.local.json). Returns\n * null when there is nothing of ours to remove \u2014 including when the shared\n * file is absent or unparseable (install skips it then too). Read-only.\n */\nfunction projectSharedSettingsMigration(projectPath: string): { before: string | null; after: string } | null {\n const { settings, before } = readProjectSettings(projectPath, 'settings.json');\n if (before === null) return null;\n const { removed } = removeRulemetricMachineState(settings, GATEWAY_PORT);\n if (!removed) return null;\n return { before, after: JSON.stringify(settings, null, 2) };\n}\n\nconst WORKER_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', 'com.rulemetric.worker.plist');\nconst WORKER_SYSTEMD_PATH = join(homedir(), '.config', 'systemd', 'user', 'rulemetric-worker.service');\n\n/** Run a command with inherited stdio (interactive-friendly). Thin apply(). */\nfunction runCommand(cmd: string): void {\n execSync(cmd, { stdio: 'inherit' });\n}\n\nexport const STEPS: SetupStep[] = [\n {\n id: 'install',\n title: 'Install the CLI',\n command: 'npm install -g @rulemetric/cli',\n // Monotonic: the CLI must exist to produce any downstream signal, so it\n // greens off auth or worker presence (nothing local phones home before).\n async detect(): Promise<boolean> {\n return isAuthenticated() || (await workerActive());\n },\n async plan(): Promise<StepPlan> {\n return {\n id: 'install',\n title: 'Install the CLI',\n command: 'npm install -g @rulemetric/cli',\n artifacts: [\n {\n path: 'npm install -g @rulemetric/cli',\n kind: 'command',\n before: null,\n after: 'rulemetric --version # verifies the binary is on PATH',\n },\n ],\n };\n },\n async apply(): Promise<void> {\n runCommand('npm install -g @rulemetric/cli');\n },\n },\n {\n id: 'auth',\n title: 'Authenticate',\n command: 'rulemetric auth login',\n async detect(): Promise<boolean> {\n return isAuthenticated();\n },\n async plan(ctx): Promise<StepPlan> {\n const envPath = join(ctx.configDir, 'env');\n // Guard the read like the sibling helpers (readEnvValue /\n // projectHooksSettings) do: a TOCTOU race or permission error between\n // existsSync and readFileSync must NOT throw out of plan(). Fall back to\n // a secret-free marker rather than crashing `setup plan`/`--show`.\n let before: string | null = null;\n if (existsSync(envPath)) {\n try {\n before = maskEnvFile(readFileSync(envPath, 'utf-8'));\n } catch {\n before = '(unreadable)';\n }\n }\n const rawToken = readEnvValue(ctx.configDir, 'RULEMETRIC_ACCESS_TOKEN');\n const tokenDisplay = rawToken ? maskToken(rawToken) : maskToken('<token-from-login>');\n // Reconstruct EXACTLY what `auth login`'s saveEnvFile writes so the diff\n // doesn't show phantom changes: line order [TOKEN, KEY?, URL?], the key\n // preserved (masked) when present, and the URL sourced from\n // process.env.RULEMETRIC_API_URL \u2014 the same value login passes \u2014 and OMITTED\n // when unset (login writes no URL line then). All masking preserved; the raw\n // token/key are never emitted.\n const afterLines = [`RULEMETRIC_ACCESS_TOKEN=${tokenDisplay}`];\n const rawApiKey = readEnvValue(ctx.configDir, 'RULEMETRIC_API_KEY');\n if (rawApiKey) {\n afterLines.push(`RULEMETRIC_API_KEY=${maskToken(rawApiKey)}`);\n }\n const loginUrl = process.env.RULEMETRIC_API_URL;\n if (loginUrl) {\n afterLines.push(`RULEMETRIC_API_URL=${loginUrl}`);\n }\n const after = afterLines.join('\\n');\n return {\n id: 'auth',\n title: 'Authenticate',\n command: 'rulemetric auth login',\n artifacts: [\n {\n path: envPath,\n kind: 'file',\n before,\n after,\n masked: true,\n },\n // `auth login` (saveToken) also writes auth.json \u2014 disclose it so the\n // plan matches every file apply() touches.\n {\n path: join(ctx.configDir, 'auth.json'),\n kind: 'file',\n before: existsSync(join(ctx.configDir, 'auth.json')) ? '(existing session tokens)' : null,\n after: '{ accessToken, refreshToken, expiresAt, email } \u2014 OAuth session tokens (secret)',\n masked: true,\n },\n ],\n };\n },\n async apply(): Promise<void> {\n runCommand('rulemetric auth login');\n },\n },\n {\n id: 'hooks',\n title: 'Install hooks + capture proxy',\n // Default onboarding installs the FULL capture path: session hooks PLUS the\n // MITM proxy (rendered-system-prompt / instruction linking + cross-tool\n // capture). Every invasive side effect is disclosed up front in plan()\n // below \u2014 nothing is silent. `hooks install` fails SAFE: if the CA sudo\n // trust is declined it degrades to hooks-only (no machine-wide proxy pin),\n // and HTTPS_PROXY is only pinned once the gateway actually answers on\n // :8787 \u2014 so the default never bricks Claude Code. detect() greens on the\n // capture hooks being present, so `coreComplete` is reachable even when the\n // user declines the proxy.\n command: 'rulemetric hooks install',\n async detect(ctx): Promise<boolean> {\n // Current installs write machine-local hooks to settings.local.json;\n // legacy installs wrote the shared settings.json. Claude Code merges and\n // runs hooks from both, so presence in EITHER file means capture is wired\n // up. Ready when capture hooks are actually present \u2014 NOT when a proxy\n // env var was written (that greened even against a dead proxy port). The\n // proxy is a best-effort upgrade layered on top; hooks presence is the\n // honest \"capture is working\" signal.\n for (const fileName of ['settings.local.json', 'settings.json']) {\n const settingsPath = join(ctx.projectPath, '.claude', fileName);\n if (!existsSync(settingsPath)) continue;\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8')) as Record<string, unknown>;\n if (hasRulemetricClaudeHooks(settings)) return true;\n } catch {\n /* unparseable \u2014 treat as absent */\n }\n }\n return false;\n },\n async plan(ctx): Promise<StepPlan> {\n const settingsPath = join(ctx.projectPath, '.claude', 'settings.local.json');\n const { before, after } = projectHooksSettings(ctx.projectPath);\n const artifacts: Artifact[] = [\n { path: settingsPath, kind: 'file', before, after },\n // Disclose what the hooks DO, not just that they're registered \u2014 these\n // are the privacy-relevant behaviors a user is consenting to.\n {\n path: 'what these hooks capture',\n kind: 'service',\n before: null,\n after:\n 'session-end reimports the FULL transcript to the API; post-tool-use records each ' +\n 'tool call and snapshots the working tree to a LOCAL shadow git ref (paper-trail, ' +\n 'never pushed); user-prompt posts prompt text. All scoped to sessions in this project.',\n },\n // FULL DISCLOSURE of the proxy layer \u2014 every invasive side effect of\n // `hooks install` \u00A72 is enumerated here so the default path hides\n // nothing. Each degrades gracefully (see the step comment): a declined\n // CA-trust sudo skips the machine-wide layer and stays hooks-only.\n {\n // The proxy env block `hooks install` \u00A72 adds to the SAME\n // .claude/settings.local.json (HTTPS_PROXY / NO_PROXY / NODE_EXTRA_CA_CERTS).\n path: `${settingsPath} (proxy env block)`,\n kind: 'file',\n ...projectProxyEnvSettings(ctx.projectPath),\n },\n {\n path: 'system keychain \u2014 mitmproxy root CA',\n kind: 'service',\n before: existsSync(CERT_PATH) ? '(CA generated; trust state unchanged)' : '(no CA yet)',\n after:\n 'Installs + trusts the mitmproxy CA (requires sudo). This lets the ' +\n 'proxy DECRYPT Claude Code HTTPS request/response bodies \u2014 the ' +\n 'capability that powers rendered-system-prompt / instruction linking. ' +\n 'Decline the sudo prompt and setup stays hooks-only (still fully onboarded).',\n },\n {\n path: 'launchctl setenv HTTPS_PROXY (machine-wide, all GUI apps)',\n kind: 'command',\n before: null,\n after: `http://localhost:${GATEWAY_PORT} \u2014 routes every GUI app's HTTPS through the local gateway (only when the CA is trusted AND :${GATEWAY_PORT} is live)`,\n },\n {\n path: 'VS Code + Cursor user settings (http.proxy)',\n kind: 'file',\n before: null,\n after: `http.proxy \u2192 http://localhost:${GATEWAY_PORT} (+ http.proxyStrictSSL=false)`,\n },\n {\n path: `gateway process on :${GATEWAY_PORT}`,\n kind: 'service',\n before: null,\n after: 'started (routes Claude Code \u2192 mitmproxy \u2192 Anthropic; falls back to direct if mitmproxy is down)',\n },\n ];\n // Migration disclosure: when a legacy/poisoned shared settings.json still\n // carries RuleMetric machine-local state, install strips it (the state\n // moves to settings.local.json above). That is a write to a typically\n // checked-in file, so the plan must name it.\n const sharedMigration = projectSharedSettingsMigration(ctx.projectPath);\n if (sharedMigration) {\n artifacts.push({\n path: `${join(ctx.projectPath, '.claude', 'settings.json')} (removes RuleMetric machine-local state left by an older install \u2014 see the local-file artifacts above for where it moves)`,\n kind: 'file',\n ...sharedMigration,\n });\n }\n // Workspace-level editor configs, only if those dirs exist here.\n for (const tool of detectInstalledTools(ctx.projectPath).filter((t) => t === 'cursor' || t === 'copilot_agent')) {\n artifacts.push({\n path: `${ctx.projectPath} (${tool} workspace proxy config)`,\n kind: 'file',\n before: null,\n after: `proxy \u2192 http://localhost:${GATEWAY_PORT}`,\n });\n }\n // Cross-tool USER-level hooks \u2014 disclosed here, and (post-F2) written ONLY\n // when the tool is already present so we never manufacture config for a\n // tool the user doesn't have.\n for (const [tool, home, file] of [\n ['Cursor', join(homedir(), '.cursor'), join(homedir(), '.cursor', 'hooks.json')],\n ['Antigravity/Gemini', join(homedir(), '.gemini'), join(homedir(), '.gemini', 'config', 'hooks.json')],\n ] as const) {\n if (existsSync(home)) {\n artifacts.push({\n path: `${file} (${tool} user hooks)`,\n kind: 'file',\n before: existsSync(file) ? '(existing config)' : null,\n after: 'adds RuleMetric session-capture hooks for this tool',\n });\n }\n }\n return {\n id: 'hooks',\n title: 'Install hooks + capture proxy',\n command: 'rulemetric hooks install',\n artifacts,\n };\n },\n async apply(): Promise<void> {\n runCommand('rulemetric hooks install');\n },\n },\n {\n id: 'worker',\n title: 'Run the worker',\n command: 'rulemetric service install --worker-only',\n async detect(): Promise<boolean> {\n return workerActive();\n },\n async plan(): Promise<StepPlan> {\n const isMac = process.platform === 'darwin';\n const servicePath = isMac ? WORKER_PLIST_PATH : WORKER_SYSTEMD_PATH;\n // SECURITY: never read the installed plist/systemd unit into `before` \u2014\n // those files embed RULEMETRIC_ACCESS_TOKEN / RULEMETRIC_API_KEY verbatim\n // (service/install.ts writes them from ALLOWED_ENV_VARS), so echoing the\n // file contents would leak the raw secret through `plan`, `--show`, and\n // `--json`. Use a synthetic, secret-free marker for the current state.\n const before = existsSync(servicePath)\n ? `(service already installed at ${servicePath})`\n : null;\n // Concise representation (path + ExecStart node/cli invocation) rather\n // than reconstructing the full plist \u2014 keep read-only + legible.\n const after = isMac\n ? [\n `Label: com.rulemetric.worker`,\n `ProgramArguments: <node> <cli>/bin/run.js evals agent`,\n `RunAtLoad: true, KeepAlive: true`,\n ].join('\\n')\n : [\n `[Service]`,\n `ExecStart=<node> <cli>/bin/run.js evals agent`,\n `Restart=on-failure`,\n ].join('\\n');\n return {\n id: 'worker',\n title: 'Run the worker',\n command: 'rulemetric service install --worker-only',\n artifacts: [\n { path: servicePath, kind: 'service', before, after },\n ],\n };\n },\n async apply(): Promise<void> {\n runCommand('rulemetric service install --worker-only');\n },\n },\n];\n\nconst STEP_BY_ID = new Map<StepId, SetupStep>(STEPS.map((s) => [s.id, s]));\n\nexport function getStep(id: StepId): SetupStep {\n const step = STEP_BY_ID.get(id);\n if (!step) throw new Error(`Unknown setup step: ${id}`);\n return step;\n}\n\nexport function isStepId(value: string): value is StepId {\n return (ALL_STEP_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Keys whose values are safe to show verbatim in the env-file diff. Everything\n * NOT in this set is masked. Deny-by-default is deliberate: `~/.config/rulemetric/env`\n * can legitimately hold DATABASE_URL, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_JWT_SECRET,\n * GITHUB_TOKEN, RESEND_API_KEY, etc. (see ALLOWED_ENV_VARS in service/install.ts),\n * so an allowlist-to-hide would leak any secret we forgot to enumerate. Revealing\n * only known-safe keys means a future secret added to the file is masked automatically.\n */\nconst SAFE_ENV_KEYS = new Set([\n 'RULEMETRIC_API_URL',\n 'RULEMETRIC_USER_ID',\n 'RULEMETRIC_CLIENT_NAME',\n 'RESEND_FROM_EMAIL',\n 'SENTRY_DSN', // a DSN is not a secret\n 'PG_POOL_MAX',\n 'PG_IDLE_TIMEOUT',\n 'PG_STATEMENT_TIMEOUT_MS',\n 'NODE_ENV',\n]);\n\n/**\n * Mask secret-bearing lines in a raw env file for display. Deny-by-default:\n * every `KEY=value` line has its value masked unless KEY is in SAFE_ENV_KEYS.\n * Non-assignment lines (comments, blanks) pass through untouched.\n */\nfunction maskEnvFile(content: string): string {\n return content\n .replace(/\\n$/, '')\n .split('\\n')\n .map((line) => {\n const match = line.match(/^([A-Z0-9_]+)=(.+)$/);\n if (!match) return line; // comment / blank / non-assignment\n const [, key, value] = match;\n return SAFE_ENV_KEYS.has(key) ? line : `${key}=${maskToken(value)}`;\n })\n .join('\\n');\n}\n\n/**\n * Run every step's detect() and derive the overall status. `currentStepId` is\n * the first incomplete step in core order (mirrors the web reducer).\n */\nexport async function computeStatus(ctx: SetupContext = defaultContext()): Promise<SetupStatus> {\n const steps: StepStatus[] = [];\n for (const step of STEPS) {\n // Sequential: detect() may hit the network; order is stable + cheap.\n // eslint-disable-next-line no-await-in-loop\n const done = await step.detect(ctx);\n steps.push({ id: step.id, done });\n }\n // Monotonicity (mirrors the web reducer's `cliDetected`): the CLI binary must\n // exist to produce ANY downstream signal, so `install` greens whenever auth,\n // hooks, or worker is detected \u2014 even if `install.detect()` alone (which only\n // sees auth/worker, not hooks) came back false. Without this, a user with\n // hooks installed but no live worker sees \"Install the CLI: waiting\" while\n // \"Install hooks: done\" \u2014 a contradiction.\n const install = steps.find((s) => s.id === 'install');\n if (install && !install.done) {\n install.done = steps.some((s) => s.id !== 'install' && s.done);\n }\n\n // coreComplete + currentStepId are derived from CORE steps only. The proxy now\n // ships inside the `hooks` step (whose detect() greens on hooks-presence, so a\n // declined CA still completes it); any future OPTIONAL_STEP_IDS never block\n // \"done\" or steal the cursor. Mirrors the web reducer's optional-step handling.\n const coreIds = new Set<string>(CORE_STEP_IDS);\n const coreSteps = steps.filter((s) => coreIds.has(s.id));\n const currentStepId = coreSteps.find((s) => !s.done)?.id ?? null;\n const coreComplete = coreSteps.every((s) => s.done);\n return { steps, coreComplete, currentStepId };\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,157 +0,0 @@
1
- import {
2
- sendEmail
3
- } from "./chunk-QSN77T7C.js";
4
- import {
5
- announcementDeliveries,
6
- announcements,
7
- getDb,
8
- notificationPreferences,
9
- profiles
10
- } from "./chunk-EI5BHWXA.js";
11
- import {
12
- PermanentJobError
13
- } from "./chunk-DGHWRQXL.js";
14
-
15
- // src/lib/handlers/process-announcement.ts
16
- import { and, asc, eq, gt, isNotNull } from "drizzle-orm";
17
- var BATCH_SIZE = 100;
18
- var CONCURRENCY = 10;
19
- async function processAnnouncement(payload, deps = {}) {
20
- const db = deps.db ?? getDb();
21
- const sendEmail2 = deps.sendEmail ?? sendEmail;
22
- const log = deps.log ?? ((msg) => process.stderr.write(`[process_announcement] ${msg}
23
- `));
24
- const webBaseUrl = deps.webBaseUrl ?? process.env.RULEMETRIC_WEB_URL ?? "https://rulemetric.com";
25
- const { announcementId } = payload;
26
- const [announcement] = await db.select().from(announcements).where(eq(announcements.id, announcementId)).limit(1);
27
- if (!announcement) {
28
- throw new PermanentJobError(`Announcement ${announcementId} not found`);
29
- }
30
- if (announcement.status !== "queued" && announcement.status !== "sending") {
31
- throw new PermanentJobError(
32
- `Announcement ${announcementId} is in status='${announcement.status}'; expected 'queued' or 'sending'.`
33
- );
34
- }
35
- await db.update(announcements).set({ status: "sending", updatedAt: /* @__PURE__ */ new Date() }).where(eq(announcements.id, announcementId));
36
- log(`Sending announcement ${announcementId} (${announcement.subject ?? ""})`);
37
- let sent = 0;
38
- let failed = 0;
39
- let skipped = 0;
40
- let lastUserId = null;
41
- try {
42
- while (true) {
43
- const recipients = await db.select({
44
- userId: notificationPreferences.userId,
45
- email: profiles.email,
46
- unsubscribeToken: notificationPreferences.unsubscribeToken
47
- }).from(notificationPreferences).innerJoin(profiles, eq(profiles.id, notificationPreferences.userId)).where(
48
- and(
49
- eq(notificationPreferences.productAnnouncements, true),
50
- isNotNull(profiles.email),
51
- lastUserId ? gt(notificationPreferences.userId, lastUserId) : void 0
52
- )
53
- ).orderBy(asc(notificationPreferences.userId)).limit(BATCH_SIZE);
54
- if (recipients.length === 0) break;
55
- lastUserId = recipients[recipients.length - 1].userId;
56
- for (let i = 0; i < recipients.length; i += CONCURRENCY) {
57
- const chunk = recipients.slice(i, i + CONCURRENCY);
58
- const results = await Promise.allSettled(
59
- chunk.map(
60
- (r) => sendOne({
61
- recipient: r,
62
- announcement,
63
- webBaseUrl,
64
- sendEmail: sendEmail2,
65
- db
66
- })
67
- )
68
- );
69
- for (let j = 0; j < results.length; j++) {
70
- const res = results[j];
71
- if (res.status === "fulfilled") {
72
- if (res.value.ok === "sent") sent++;
73
- else if (res.value.ok === "skipped") skipped++;
74
- else {
75
- failed++;
76
- log(` recipient ${chunk[j].userId}: send failed \u2014 ${res.value.error}`);
77
- }
78
- } else {
79
- failed++;
80
- const reason = res.reason instanceof Error ? res.reason.message : String(res.reason);
81
- log(` recipient ${chunk[j].userId}: send failed \u2014 ${reason}`);
82
- }
83
- }
84
- }
85
- if (recipients.length < BATCH_SIZE) break;
86
- }
87
- const errorSummary = failed > 0 ? `${failed} of ${sent + failed} recipient(s) failed; see announcement_deliveries` : null;
88
- await db.update(announcements).set({
89
- status: "sent",
90
- recipientCount: sent,
91
- sentAt: /* @__PURE__ */ new Date(),
92
- error: errorSummary,
93
- updatedAt: /* @__PURE__ */ new Date()
94
- }).where(eq(announcements.id, announcementId));
95
- log(`Done: sent=${sent} failed=${failed} skipped=${skipped}`);
96
- return { sent, failed, skipped };
97
- } catch (err) {
98
- const message = err instanceof Error ? err.message : String(err);
99
- await db.update(announcements).set({
100
- status: "failed",
101
- error: message.slice(0, 1e3),
102
- updatedAt: /* @__PURE__ */ new Date()
103
- }).where(eq(announcements.id, announcementId)).catch(() => {
104
- });
105
- throw err;
106
- }
107
- }
108
- async function sendOne(args) {
109
- const { recipient, announcement, webBaseUrl, sendEmail: sendEmail2, db } = args;
110
- if (!recipient.email) {
111
- return { ok: "skipped", reason: "no-email" };
112
- }
113
- const reserved = await db.insert(announcementDeliveries).values({
114
- announcementId: announcement.id,
115
- userId: recipient.userId,
116
- email: recipient.email,
117
- status: "pending"
118
- }).onConflictDoNothing({
119
- target: [announcementDeliveries.announcementId, announcementDeliveries.userId]
120
- }).returning({ id: announcementDeliveries.id });
121
- if (reserved.length === 0) {
122
- return { ok: "skipped", reason: "already-claimed" };
123
- }
124
- const deliveryId = reserved[0].id;
125
- const unsubscribeUrl = `${webBaseUrl.replace(/\/$/, "")}/unsubscribe/${recipient.unsubscribeToken}`;
126
- const html = announcement.htmlBody.replaceAll("{{unsubscribe_url}}", unsubscribeUrl);
127
- const text = announcement.textBody.replaceAll("{{unsubscribe_url}}", unsubscribeUrl);
128
- try {
129
- const result = await sendEmail2({
130
- to: recipient.email,
131
- subject: announcement.subject,
132
- html,
133
- text,
134
- fromName: "RuleMetric",
135
- idempotencyKey: deliveryId
136
- });
137
- await db.update(announcementDeliveries).set({
138
- status: "sent",
139
- providerId: result.id,
140
- sentAt: /* @__PURE__ */ new Date()
141
- }).where(eq(announcementDeliveries.id, deliveryId));
142
- return { ok: "sent", providerId: result.id };
143
- } catch (err) {
144
- const message = err instanceof Error ? err.message : String(err);
145
- await db.update(announcementDeliveries).set({
146
- status: "failed",
147
- error: message.slice(0, 1e3)
148
- }).where(eq(announcementDeliveries.id, deliveryId)).catch(() => {
149
- });
150
- return { ok: "failed", error: message };
151
- }
152
- }
153
-
154
- export {
155
- processAnnouncement
156
- };
157
- //# sourceMappingURL=chunk-OO7JDFS4.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/lib/handlers/process-announcement.ts"],
4
- "sourcesContent": ["import { and, asc, eq, gt, isNotNull } from 'drizzle-orm';\nimport {\n announcements,\n announcementDeliveries,\n notificationPreferences,\n profiles,\n getDb,\n} from '@rulemetric/db';\nimport { sendEmail as sendEmailDefault } from '@rulemetric/email';\nimport { PermanentJobError } from '../permanent-job-error.js';\n\nconst BATCH_SIZE = 100;\nconst CONCURRENCY = 10;\n\nexport interface ProcessAnnouncementPayload {\n announcementId: string;\n}\n\nexport interface ProcessAnnouncementResult {\n sent: number;\n failed: number;\n skipped: number;\n}\n\ntype SendEmailFn = typeof sendEmailDefault;\n\nexport interface ProcessAnnouncementDeps {\n db?: any;\n sendEmail?: SendEmailFn;\n log?: (msg: string) => void;\n /** Base URL used to build the unsubscribe link. */\n webBaseUrl?: string;\n}\n\n/**\n * Graphile-shaped handler for the `process_announcement` task.\n *\n * Sends a queued product announcement to every user opted in via\n * `notification_preferences.product_announcements = true`. There is no\n * mirror table \u2014 the `announcements` row itself carries lifecycle status\n * (draft \u2192 queued \u2192 sending \u2192 sent / failed).\n *\n * Idempotency (reserve-first pattern):\n * - Per recipient we INSERT the `announcement_deliveries` row with\n * status='pending' BEFORE calling the email provider, using\n * `ON CONFLICT (announcement_id, user_id) DO NOTHING`. If zero rows are\n * returned, another attempt already claimed this recipient \u2014 we skip.\n * This closes the crash window between \"email sent\" and \"delivery row\n * written\" \u2014 even if we never reach the UPDATE, a retry sees the\n * reserved row and does not re-send.\n * - The reserved row's UUID is passed as `idempotencyKey` to sendEmail,\n * so the provider (Resend) dedupes server-side for 24h. Defense in\n * depth: even if the same delivery row got reused across a long\n * retry-storm, the provider would coalesce the requests.\n * - Status='sending' is treated as resumable (a previous attempt\n * crashed mid-stream). Status='sent' or 'failed' throws \u2014 Graphile\n * should not re-fire on a terminal row.\n *\n * Per-recipient failures (sendEmail throws, bad email, provider 5xx) are\n * logged + recorded with status='failed' on the delivery row and DO NOT\n * fail the job. Only DB / auth-level failures escalate, since those\n * indicate the run can't make forward progress at all.\n */\nexport async function processAnnouncement(\n payload: ProcessAnnouncementPayload,\n deps: ProcessAnnouncementDeps = {},\n): Promise<ProcessAnnouncementResult> {\n const db = deps.db ?? getDb();\n const sendEmail = deps.sendEmail ?? sendEmailDefault;\n const log = deps.log ?? ((msg: string) => process.stderr.write(`[process_announcement] ${msg}\\n`));\n const webBaseUrl =\n deps.webBaseUrl ?? process.env.RULEMETRIC_WEB_URL ?? 'https://rulemetric.com';\n\n const { announcementId } = payload;\n\n // 1. Load + validate state\n const [announcement] = await db\n .select()\n .from(announcements)\n .where(eq(announcements.id, announcementId))\n .limit(1);\n\n if (!announcement) {\n throw new PermanentJobError(`Announcement ${announcementId} not found`);\n }\n\n // Allow status='sending' (resume after crash). Reject everything else\n // that isn't 'queued' \u2014 Graphile must not re-send a terminal row.\n if (announcement.status !== 'queued' && announcement.status !== 'sending') {\n throw new PermanentJobError(\n `Announcement ${announcementId} is in status='${announcement.status}'; expected 'queued' or 'sending'.`,\n );\n }\n\n // 2. Flip to 'sending' (idempotent on resume).\n await db\n .update(announcements)\n .set({ status: 'sending', updatedAt: new Date() })\n .where(eq(announcements.id, announcementId));\n\n log(`Sending announcement ${announcementId} (${announcement.subject ?? ''})`);\n\n let sent = 0;\n let failed = 0;\n let skipped = 0;\n let lastUserId: string | null = null;\n\n try {\n // 3. Iterate recipients in deterministic batches (userId ASC).\n while (true) {\n const recipients: Array<{\n userId: string;\n email: string | null;\n unsubscribeToken: string;\n }> = await db\n .select({\n userId: notificationPreferences.userId,\n email: profiles.email,\n unsubscribeToken: notificationPreferences.unsubscribeToken,\n })\n .from(notificationPreferences)\n .innerJoin(profiles, eq(profiles.id, notificationPreferences.userId))\n .where(\n and(\n eq(notificationPreferences.productAnnouncements, true),\n isNotNull(profiles.email),\n lastUserId ? gt(notificationPreferences.userId, lastUserId) : undefined,\n ),\n )\n .orderBy(asc(notificationPreferences.userId))\n .limit(BATCH_SIZE);\n\n if (recipients.length === 0) break;\n lastUserId = recipients[recipients.length - 1].userId;\n\n // 4. Send in concurrent chunks of CONCURRENCY. Each sendOne reserves\n // its own delivery row first; a concurrent retry that lost the\n // race returns ok='skipped'.\n for (let i = 0; i < recipients.length; i += CONCURRENCY) {\n const chunk = recipients.slice(i, i + CONCURRENCY);\n const results = await Promise.allSettled(\n chunk.map((r) =>\n sendOne({\n recipient: r,\n announcement,\n webBaseUrl,\n sendEmail,\n db,\n }),\n ),\n );\n for (let j = 0; j < results.length; j++) {\n const res = results[j];\n if (res.status === 'fulfilled') {\n if (res.value.ok === 'sent') sent++;\n else if (res.value.ok === 'skipped') skipped++;\n else {\n failed++;\n log(` recipient ${chunk[j].userId}: send failed \u2014 ${res.value.error}`);\n }\n } else {\n failed++;\n const reason = res.reason instanceof Error ? res.reason.message : String(res.reason);\n log(` recipient ${chunk[j].userId}: send failed \u2014 ${reason}`);\n }\n }\n }\n\n if (recipients.length < BATCH_SIZE) break;\n }\n\n // 6. Finalize. Mark 'sent' even on partial success \u2014 the admin can see\n // per-recipient deliveries for failure details.\n const errorSummary =\n failed > 0\n ? `${failed} of ${sent + failed} recipient(s) failed; see announcement_deliveries`\n : null;\n await db\n .update(announcements)\n .set({\n status: 'sent',\n recipientCount: sent,\n sentAt: new Date(),\n error: errorSummary,\n updatedAt: new Date(),\n })\n .where(eq(announcements.id, announcementId));\n\n log(`Done: sent=${sent} failed=${failed} skipped=${skipped}`);\n return { sent, failed, skipped };\n } catch (err) {\n // DB / auth-level failure: mark the row 'failed' and rethrow so\n // Graphile counts the attempt.\n const message = err instanceof Error ? err.message : String(err);\n await db\n .update(announcements)\n .set({\n status: 'failed',\n error: message.slice(0, 1000),\n updatedAt: new Date(),\n })\n .where(eq(announcements.id, announcementId))\n .catch(() => {});\n throw err;\n }\n}\n\ntype SendOneResult =\n | { ok: 'sent'; providerId: string }\n | { ok: 'skipped'; reason: 'already-claimed' | 'no-email' }\n | { ok: 'failed'; error: string };\n\nasync function sendOne(args: {\n recipient: { userId: string; email: string | null; unsubscribeToken: string };\n announcement: {\n id: string;\n subject: string;\n htmlBody: string;\n textBody: string;\n };\n webBaseUrl: string;\n sendEmail: SendEmailFn;\n db: any;\n}): Promise<SendOneResult> {\n const { recipient, announcement, webBaseUrl, sendEmail, db } = args;\n\n if (!recipient.email) {\n return { ok: 'skipped', reason: 'no-email' };\n }\n\n // 1. Reserve the delivery row before sending. ON CONFLICT DO NOTHING\n // returns zero rows if another attempt already claimed this recipient,\n // which is our signal to skip (no email, no UPDATE).\n const reserved: Array<{ id: string }> = await db\n .insert(announcementDeliveries)\n .values({\n announcementId: announcement.id,\n userId: recipient.userId,\n email: recipient.email,\n status: 'pending',\n })\n .onConflictDoNothing({\n target: [announcementDeliveries.announcementId, announcementDeliveries.userId],\n })\n .returning({ id: announcementDeliveries.id });\n\n if (reserved.length === 0) {\n return { ok: 'skipped', reason: 'already-claimed' };\n }\n const deliveryId = reserved[0].id;\n\n // 2. Send. The delivery row's UUID is the provider idempotency key \u2014\n // a retry-after-crash that hits the same reserved row will send the\n // same key, and Resend will return the original response instead of\n // actually re-sending the email.\n const unsubscribeUrl = `${webBaseUrl.replace(/\\/$/, '')}/unsubscribe/${recipient.unsubscribeToken}`;\n const html = announcement.htmlBody.replaceAll('{{unsubscribe_url}}', unsubscribeUrl);\n const text = announcement.textBody.replaceAll('{{unsubscribe_url}}', unsubscribeUrl);\n\n try {\n const result = await sendEmail({\n to: recipient.email,\n subject: announcement.subject,\n html,\n text,\n fromName: 'RuleMetric',\n idempotencyKey: deliveryId,\n });\n\n // 3. Promote the reserved row to 'sent'.\n await db\n .update(announcementDeliveries)\n .set({\n status: 'sent',\n providerId: result.id,\n sentAt: new Date(),\n })\n .where(eq(announcementDeliveries.id, deliveryId));\n\n return { ok: 'sent', providerId: result.id };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // Promote the reserved row to 'failed'. We deliberately don't delete\n // the row \u2014 the reservation IS the dedup record, and the UPDATE keeps\n // a permanent audit trail of the failure reason.\n await db\n .update(announcementDeliveries)\n .set({\n status: 'failed',\n error: message.slice(0, 1000),\n })\n .where(eq(announcementDeliveries.id, deliveryId))\n .catch(() => {});\n return { ok: 'failed', error: message };\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["sendEmail"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/lib/statusline-shim.ts"],
4
- "sourcesContent": ["import { existsSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Path where the shim script lives \u2014 readable by the `statusLine` config key.\nexport const STATUSLINE_SHIM_PATH = join(homedir(), '.claude', 'statusline-rulemetric.sh');\n\n/**\n * Write ~/.claude/statusline-rulemetric.sh.\n *\n * The script is invoked by Claude Code on every statusline refresh cycle.\n * Claude Code feeds a JSON payload on stdin that includes `rate_limits`\n * (same shape as GET /api/oauth/usage) using its own in-memory token \u2014\n * so freshness is tied to the statusline cadence, not the proxy capturing\n * a stale keychain token.\n *\n * The script:\n * 1. Reads stdin once (Claude Code closes it after the payload).\n * 2. If jq is available, extracts each rate_limit window and POSTs it to\n * POST /api/internal/research/observe as an oauth_usage observation.\n * 3. If a `prevCommand` is supplied (the user's original statusLine value),\n * it is executed with the same stdin forwarded so the user's prompt text\n * is still displayed \u2014 we must chain, not replace.\n * 4. Exits 0 regardless so Claude Code always gets a clean statusline.\n *\n * Auth is read from the same priority chain as the hook scripts:\n * ~/.config/rulemetric/env (RULEMETRIC_ACCESS_TOKEN / RULEMETRIC_API_KEY)\n * RULEMETRIC_API_KEY / RULEMETRIC_ACCESS_TOKEN env vars\n *\n * Returns the path written.\n */\nexport function writeStatuslineShim(prevCommand?: string): string {\n const chainBlock = prevCommand\n ? `\n# Chain to the original statusLine command so the user's prompt is preserved.\necho \"$STDIN_PAYLOAD\" | ${prevCommand}`\n : '';\n\n const script = `#!/usr/bin/env bash\n# RuleMetric statusline shim \u2014 auto-generated by \\`rulemetric hooks install --statusline-usage\\`\n# Captures Claude Code rate_limits from statusline stdin and forwards to the research pipeline.\n# DO NOT EDIT \u2014 re-run \\`rulemetric hooks install --statusline-usage\\` to regenerate.\n\nset -euo pipefail\n\n# Read stdin once (Claude Code closes the pipe after the payload).\nSTDIN_PAYLOAD=\"$(cat)\"\n\n# \u2500\u2500 Auth \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCONFIG_ENV=\"$HOME/.config/rulemetric/env\"\nif [ -f \"$CONFIG_ENV\" ]; then\n # shellcheck source=/dev/null\n source \"$CONFIG_ENV\"\nfi\n\nAPI_URL=\"\\${RULEMETRIC_API_URL:-https://rulemetric.com}\"\nAUTH_TOKEN=\"\\${RULEMETRIC_ACCESS_TOKEN:-\\${RULEMETRIC_API_KEY:-}}\"\n\nif [ -z \"$AUTH_TOKEN\" ]; then\n # No auth \u2014 still chain so the user's statusline works.\n${chainBlock}\n exit 0\nfi\n\n# \u2500\u2500 Extract + POST rate_limit windows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nif ! command -v jq &>/dev/null; then\n # jq missing \u2014 chain and bail; don't break the user's statusline.\n${chainBlock}\n exit 0\nfi\n\nOBSERVED_AT=\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\nCACHE_DIR=\"$HOME/.config/rulemetric\"\nmkdir -p \"$CACHE_DIR\" 2>/dev/null || true\n\nfor WIRE_KEY in five_hour seven_day seven_day_opus; do\n # Map wire key \u2192 limit_type (case statement avoids bash 4 associative arrays).\n case \"$WIRE_KEY\" in\n five_hour) LIMIT_TYPE=\"five_hour_percent\" ;;\n seven_day) LIMIT_TYPE=\"seven_day_percent\" ;;\n seven_day_opus) LIMIT_TYPE=\"seven_day_opus_percent\" ;;\n *) continue ;;\n esac\n\n # Try both wire shapes:\n # Legacy: { \"rate_limits\": { \"five_hour\": { \"used_percentage\": ..., \"resets_at\": ... } } }\n # Current: { \"five_hour\": { \"used_percentage\": ..., \"resets_at\": ... } }\n WINDOW=\"$(echo \"$STDIN_PAYLOAD\" | jq -c \".rate_limits.\\${WIRE_KEY} // .\\${WIRE_KEY} // empty\" 2>/dev/null || true)\"\n\n if [ -z \"$WINDOW\" ] || [ \"$WINDOW\" = \"null\" ]; then\n continue\n fi\n\n USED_PCT=\"$(echo \"$WINDOW\" | jq '.used_percentage // empty' 2>/dev/null || true)\"\n RESETS_AT_EPOCH=\"$(echo \"$WINDOW\" | jq '.resets_at // empty' 2>/dev/null || true)\"\n\n if [ -z \"$USED_PCT\" ] || [ \"$USED_PCT\" = \"null\" ]; then\n continue\n fi\n\n # Convert unix epoch to ISO-8601 if present.\n if [ -n \"$RESETS_AT_EPOCH\" ] && [ \"$RESETS_AT_EPOCH\" != \"null\" ]; then\n RESETS_AT_ISO=\"$(date -u -r \"$RESETS_AT_EPOCH\" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \\\n || date -u -d \"@$RESETS_AT_EPOCH\" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \\\n || echo \"\")\"\n else\n RESETS_AT_ISO=\"\"\n fi\n\n # Skip stale windows. Claude Code can keep re-emitting a cached rate_limits\n # block after the reset time has passed; storing those rows floods the DB and\n # corrupts the \"current limits\" view.\n if [ -n \"$RESETS_AT_EPOCH\" ] && [ \"$RESETS_AT_EPOCH\" != \"null\" ]; then\n NOW_EPOCH=\"$(date -u +%s)\"\n if [ \"$RESETS_AT_EPOCH\" -lt \"$NOW_EPOCH\" ] 2>/dev/null; then\n continue\n fi\n else\n NOW_EPOCH=\"$(date -u +%s)\"\n fi\n\n # Local duplicate suppression mirrors the API's 30-minute freshness floor.\n # The statusline refreshes often; if the same window/value/reset is already\n # posted recently, another POST only burns an API request and DB connection.\n CACHE_FILE=\"$CACHE_DIR/statusline-usage-\\${LIMIT_TYPE}.cache\"\n CACHE_KEY=\"\\${LIMIT_TYPE}|\\${USED_PCT}|\\${RESETS_AT_ISO}\"\n if [ -f \"$CACHE_FILE\" ]; then\n read -r CACHED_AT CACHED_KEY < \"$CACHE_FILE\" || true\n if [ \"\\${CACHED_KEY:-}\" = \"$CACHE_KEY\" ] && [ $((NOW_EPOCH - \\${CACHED_AT:-0})) -lt 1800 ] 2>/dev/null; then\n continue\n fi\n fi\n printf '%s %s\\n' \"$NOW_EPOCH\" \"$CACHE_KEY\" > \"$CACHE_FILE.tmp\" 2>/dev/null \\\n && mv \"$CACHE_FILE.tmp\" \"$CACHE_FILE\" 2>/dev/null || true\n\n PAYLOAD=\"$(jq -nc \\\\\n --arg provider \"anthropic\" \\\\\n --arg plan \"unknown\" \\\\\n --argjson value \"$USED_PCT\" \\\\\n --arg limitType \"$LIMIT_TYPE\" \\\\\n --arg observedAt \"$OBSERVED_AT\" \\\\\n --arg resetsAt \"$RESETS_AT_ISO\" \\\\\n '{\n provider: $provider,\n plan: $plan,\n limitType: $limitType,\n value: $value,\n unit: \"percentage\",\n comparator: \"=\",\n sourceType: \"oauth_usage\",\n sourceUrl: null,\n confidence: 1.0,\n observedAt: $observedAt,\n resetsAt: (if $resetsAt == \"\" then null else $resetsAt end)\n }')\"\n\n curl -sf -o /dev/null \\\\\n -X POST \"$API_URL/api/internal/research/observe\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"Authorization: Bearer $AUTH_TOKEN\" \\\\\n --max-time 3 \\\\\n --data \"$PAYLOAD\" >/dev/null 2>&1 &\ndone\n\n${chainBlock}\nexit 0\n`;\n\n writeFileSync(STATUSLINE_SHIM_PATH, script, { encoding: 'utf-8' });\n chmodSync(STATUSLINE_SHIM_PATH, 0o755);\n return STATUSLINE_SHIM_PATH;\n}\n\n// The settings object shape for statusLine (Claude Code v1.x+).\nexport const STATUSLINE_SETTINGS_VALUE = {\n type: 'command' as const,\n command: STATUSLINE_SHIM_PATH,\n refreshInterval: 30,\n};\n\n/**\n * Read the current statusLine command from a settings object, if any.\n * Returns undefined if not set or already points to the shim (idempotent).\n * Handles both the legacy string form and the current object form.\n */\nexport function readCurrentStatusLine(\n settings: Record<string, unknown>,\n): string | undefined {\n const current = settings.statusLine;\n if (!current) return undefined;\n if (typeof current === 'string') {\n if (current === STATUSLINE_SHIM_PATH) return undefined;\n return current;\n }\n if (typeof current === 'object' && current !== null) {\n const cmd = (current as Record<string, unknown>).command;\n if (typeof cmd !== 'string') return undefined;\n if (cmd === STATUSLINE_SHIM_PATH) return undefined; // already installed\n return cmd;\n }\n return undefined;\n}\n\n/**\n * Remove our statusline shim from a settings object (uninstall). Only deletes\n * `settings.statusLine` when it points at OUR shim \u2014 a user-owned statusLine is\n * left untouched. Returns true iff it removed the shim. Without this, `hooks\n * uninstall` leaves Claude Code POSTing usage to rulemetric.com via the shim\n * even after the hooks + CLI are gone.\n */\nexport function removeStatuslineShim(settings: Record<string, unknown>): boolean {\n if (!isStatuslineShimInstalled(settings)) return false;\n delete settings.statusLine;\n return true;\n}\n\n// The shim's path suffix is identical on every machine \u2014 only the $HOME prefix\n// differs. Forward slashes only: settings files are written with POSIX paths\n// on the platforms the shim supports (the shim itself is a bash script).\nconst STATUSLINE_SHIM_SUFFIX = '/.claude/statusline-rulemetric.sh';\n\n/**\n * Like removeStatuslineShim, but also matches a shim written by ANOTHER\n * machine's install: the statusLine path is $HOME-anchored, so a shared\n * `.claude/settings.json` poisoned by a teammate's install carries THEIR home\n * prefix and the exact-path check misses it. Only our fixed shim filename can\n * match \u2014 a user-owned statusLine is never removed. Returns true iff removed.\n */\nexport function removeStatuslineShimAnyHome(settings: Record<string, unknown>): boolean {\n const current = settings.statusLine;\n const cmd =\n typeof current === 'string'\n ? current\n : typeof current === 'object' && current !== null\n ? (current as Record<string, unknown>).command\n : undefined;\n const isShim =\n typeof cmd === 'string' && (cmd === STATUSLINE_SHIM_PATH || cmd.endsWith(STATUSLINE_SHIM_SUFFIX));\n if (!isShim) return false;\n delete settings.statusLine;\n return true;\n}\n\n/**\n * Returns true if the settings object already points to the shim.\n */\nexport function isStatuslineShimInstalled(settings: Record<string, unknown>): boolean {\n const current = settings.statusLine;\n if (!current) return false;\n if (typeof current === 'string') return current === STATUSLINE_SHIM_PATH;\n if (typeof current === 'object' && current !== null) {\n return (current as Record<string, unknown>).command === STATUSLINE_SHIM_PATH;\n }\n return false;\n}\n"],
5
- "mappings": ";AAAA,SAAmC,eAAe,iBAAiB;AACnE,SAAS,eAAe;AACxB,SAAS,YAAY;AAGd,IAAM,uBAAuB,KAAK,QAAQ,GAAG,WAAW,0BAA0B;AA0BlF,SAAS,oBAAoB,aAA8B;AAChE,QAAM,aAAa,cACf;AAAA;AAAA,0BAEoB,WAAW,KAC/B;AAEJ,QAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBf,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiGV,UAAU;AAAA;AAAA;AAIV,gBAAc,sBAAsB,QAAQ,EAAE,UAAU,QAAQ,CAAC;AACjE,YAAU,sBAAsB,GAAK;AACrC,SAAO;AACT;AAGO,IAAM,4BAA4B;AAAA,EACvC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,iBAAiB;AACnB;AAOO,SAAS,sBACd,UACoB;AACpB,QAAM,UAAU,SAAS;AACzB,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,OAAO,YAAY,UAAU;AAC/B,QAAI,YAAY,qBAAsB,QAAO;AAC7C,WAAO;AAAA,EACT;AACA,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,UAAM,MAAO,QAAoC;AACjD,QAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAI,QAAQ,qBAAsB,QAAO;AACzC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASO,SAAS,qBAAqB,UAA4C;AAC/E,MAAI,CAAC,0BAA0B,QAAQ,EAAG,QAAO;AACjD,SAAO,SAAS;AAChB,SAAO;AACT;AAKA,IAAM,yBAAyB;AASxB,SAAS,4BAA4B,UAA4C;AACtF,QAAM,UAAU,SAAS;AACzB,QAAM,MACJ,OAAO,YAAY,WACf,UACA,OAAO,YAAY,YAAY,YAAY,OACxC,QAAoC,UACrC;AACR,QAAM,SACJ,OAAO,QAAQ,aAAa,QAAQ,wBAAwB,IAAI,SAAS,sBAAsB;AACjG,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS;AAChB,SAAO;AACT;AAKO,SAAS,0BAA0B,UAA4C;AACpF,QAAM,UAAU,SAAS;AACzB,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,OAAO,YAAY,SAAU,QAAO,YAAY;AACpD,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,WAAQ,QAAoC,YAAY;AAAA,EAC1D;AACA,SAAO;AACT;",
6
- "names": []
7
- }
@@ -1,77 +0,0 @@
1
- // src/lib/detect-languages.ts
2
- import { existsSync, statSync, readdirSync } from "node:fs";
3
- import path from "node:path";
4
- var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
5
- "node_modules",
6
- ".git",
7
- "dist",
8
- "build",
9
- ".next",
10
- ".turbo",
11
- ".worktrees",
12
- "target",
13
- "venv",
14
- "__pycache__",
15
- ".cache"
16
- ]);
17
- var MAX_DEPTH = 3;
18
- function detectLanguages(projectPath) {
19
- if (!existsSync(projectPath)) return [];
20
- const signals = /* @__PURE__ */ new Set();
21
- const walk = (dir, depth) => {
22
- if (depth > MAX_DEPTH) return;
23
- let entries;
24
- try {
25
- entries = readdirSync(dir);
26
- } catch {
27
- return;
28
- }
29
- for (const name of entries) {
30
- if (name === "package.json") signals.add("JavaScript/TypeScript");
31
- if (name === "tsconfig.json") signals.add("TypeScript-config");
32
- if (name === "pyproject.toml") signals.add("Python-pyproject");
33
- if (name === "setup.py") signals.add("Python-setup");
34
- if (name === "requirements.txt") signals.add("Python-requirements");
35
- if (name === "Pipfile") signals.add("Python-pipfile");
36
- if (name === "Cargo.toml") signals.add("Rust-cargo");
37
- if (name === "go.mod") signals.add("Go-mod");
38
- if (name === "pom.xml") signals.add("Java-pom");
39
- if (name === "build.gradle" || name === "build.gradle.kts") signals.add("Java-gradle");
40
- if (name === "Gemfile") signals.add("Ruby-gemfile");
41
- if (name === "composer.json") signals.add("PHP-composer");
42
- if (name.endsWith(".ts") || name.endsWith(".tsx")) signals.add("TypeScript-source");
43
- if (name.endsWith(".py")) signals.add("Python-source");
44
- if (name.endsWith(".rs")) signals.add("Rust-source");
45
- if (name.endsWith(".go")) signals.add("Go-source");
46
- }
47
- if (depth >= MAX_DEPTH) return;
48
- for (const name of entries) {
49
- if (EXCLUDE_DIRS.has(name)) continue;
50
- const full = path.join(dir, name);
51
- try {
52
- if (statSync(full).isDirectory()) walk(full, depth + 1);
53
- } catch {
54
- }
55
- }
56
- };
57
- walk(projectPath, 0);
58
- const result = /* @__PURE__ */ new Set();
59
- if (signals.has("TypeScript-source") || signals.has("TypeScript-config")) {
60
- result.add("TypeScript");
61
- } else if (signals.has("JavaScript/TypeScript")) {
62
- result.add("JavaScript");
63
- }
64
- if (signals.has("Python-pyproject") || signals.has("Python-setup") || signals.has("Python-requirements") || signals.has("Python-pipfile") || signals.has("Python-source")) result.add("Python");
65
- if (signals.has("Rust-cargo") || signals.has("Rust-source")) result.add("Rust");
66
- if (signals.has("Go-mod") || signals.has("Go-source")) result.add("Go");
67
- if (signals.has("Java-pom") || signals.has("Java-gradle")) result.add("Java");
68
- if (signals.has("Ruby-gemfile")) result.add("Ruby");
69
- if (signals.has("PHP-composer")) result.add("PHP");
70
- if (signals.size < 2) return [];
71
- return [...result];
72
- }
73
-
74
- export {
75
- detectLanguages
76
- };
77
- //# sourceMappingURL=chunk-OQSQC7VB.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/lib/detect-languages.ts"],
4
- "sourcesContent": ["import { existsSync, statSync, readdirSync } from 'node:fs';\nimport path from 'node:path';\n\nconst EXCLUDE_DIRS = new Set([\n 'node_modules', '.git', 'dist', 'build', '.next', '.turbo',\n '.worktrees', 'target', 'venv', '__pycache__', '.cache',\n]);\nconst MAX_DEPTH = 3;\n\n/**\n * Walk a directory up to MAX_DEPTH levels and collect filesystem signals.\n * Returns a deduplicated array of language names. Requires \u22652 distinct\n * signals before the caller scores; one signal is treated as insufficient.\n */\nexport function detectLanguages(projectPath: string): string[] {\n if (!existsSync(projectPath)) return [];\n const signals = new Set<string>();\n\n const walk = (dir: string, depth: number) => {\n if (depth > MAX_DEPTH) return;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n // Inspect file markers at this level. Each marker contributes a *distinct*\n // signal so two files for the same language (e.g. pyproject.toml +\n // requirements.txt) clear the \u22652-signals minimum on their own.\n for (const name of entries) {\n if (name === 'package.json') signals.add('JavaScript/TypeScript');\n if (name === 'tsconfig.json') signals.add('TypeScript-config');\n if (name === 'pyproject.toml') signals.add('Python-pyproject');\n if (name === 'setup.py') signals.add('Python-setup');\n if (name === 'requirements.txt') signals.add('Python-requirements');\n if (name === 'Pipfile') signals.add('Python-pipfile');\n if (name === 'Cargo.toml') signals.add('Rust-cargo');\n if (name === 'go.mod') signals.add('Go-mod');\n if (name === 'pom.xml') signals.add('Java-pom');\n if (name === 'build.gradle' || name === 'build.gradle.kts') signals.add('Java-gradle');\n if (name === 'Gemfile') signals.add('Ruby-gemfile');\n if (name === 'composer.json') signals.add('PHP-composer');\n if (name.endsWith('.ts') || name.endsWith('.tsx')) signals.add('TypeScript-source');\n if (name.endsWith('.py')) signals.add('Python-source');\n if (name.endsWith('.rs')) signals.add('Rust-source');\n if (name.endsWith('.go')) signals.add('Go-source');\n }\n\n if (depth >= MAX_DEPTH) return;\n\n // Recurse into non-excluded subdirectories\n for (const name of entries) {\n if (EXCLUDE_DIRS.has(name)) continue;\n const full = path.join(dir, name);\n try {\n if (statSync(full).isDirectory()) walk(full, depth + 1);\n } catch {\n // skip unreadable\n }\n }\n };\n\n walk(projectPath, 0);\n\n // Collapse the raw signals into language names for the LLM prompt\n const result = new Set<string>();\n if (signals.has('TypeScript-source') || signals.has('TypeScript-config')) {\n result.add('TypeScript');\n } else if (signals.has('JavaScript/TypeScript')) {\n result.add('JavaScript');\n }\n if (\n signals.has('Python-pyproject') ||\n signals.has('Python-setup') ||\n signals.has('Python-requirements') ||\n signals.has('Python-pipfile') ||\n signals.has('Python-source')\n ) result.add('Python');\n if (signals.has('Rust-cargo') || signals.has('Rust-source')) result.add('Rust');\n if (signals.has('Go-mod') || signals.has('Go-source')) result.add('Go');\n if (signals.has('Java-pom') || signals.has('Java-gradle')) result.add('Java');\n if (signals.has('Ruby-gemfile')) result.add('Ruby');\n if (signals.has('PHP-composer')) result.add('PHP');\n\n // The minimum-2-signals gate counts the raw signals, not the\n // normalized language list. A package.json + tsconfig.json + .ts source\n // is three signals \u2192 strong evidence even though the language collapses\n // to one entry. A bare package.json with no .ts/.js source is one signal.\n if (signals.size < 2) return [];\n return [...result];\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,92 +0,0 @@
1
- import {
2
- apiGet,
3
- apiPost
4
- } from "./chunk-ZUJBM6HE.js";
5
-
6
- // src/lib/eval-benchmark.ts
7
- function computeStats(values) {
8
- if (values.length === 0) return { mean: 0, stddev: 0, min: 0, max: 0 };
9
- const mean = values.reduce((a, b) => a + b, 0) / values.length;
10
- const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;
11
- return {
12
- mean: Math.round(mean * 1e3) / 1e3,
13
- stddev: Math.round(Math.sqrt(variance) * 1e3) / 1e3,
14
- min: Math.min(...values),
15
- max: Math.max(...values)
16
- };
17
- }
18
- async function aggregateBenchmark(targetId, iteration) {
19
- const iterQuery = iteration ? `&iteration=${iteration}` : "";
20
- const PAGE = 100;
21
- const runs = [];
22
- for (let offset = 0; offset < 1e5; offset += PAGE) {
23
- const page = await apiGet(
24
- `/api/eval-targets/${targetId}/runs?limit=${PAGE}&offset=${offset}${iterQuery}`
25
- );
26
- runs.push(...page);
27
- if (page.length < PAGE) break;
28
- }
29
- const completedRuns = runs.filter((r) => r.status === "completed");
30
- if (completedRuns.length === 0) {
31
- return { runSummary: {}, notes: ["No completed runs found"], totalRuns: 0 };
32
- }
33
- const byConfig = {};
34
- for (const run of completedRuns) {
35
- if (!byConfig[run.configuration]) byConfig[run.configuration] = [];
36
- byConfig[run.configuration].push(run);
37
- }
38
- const runSummary = {};
39
- const notes = [];
40
- for (const [config, configRuns] of Object.entries(byConfig)) {
41
- const passRates = configRuns.filter((r) => Object.keys(r.grading).length > 0).map((r) => r.grading.summary?.pass_rate ?? 0);
42
- const durations = configRuns.map((r) => r.timing.duration_ms / 1e3).filter((v) => !isNaN(v));
43
- const tokens = configRuns.map((r) => r.timing.total_tokens ?? 0).filter((v) => v > 0);
44
- runSummary[config] = {};
45
- if (passRates.length > 0) runSummary[config].pass_rate = computeStats(passRates);
46
- if (durations.length > 0) runSummary[config].time_seconds = computeStats(durations);
47
- if (tokens.length > 0) runSummary[config].tokens = computeStats(tokens);
48
- }
49
- if (runSummary.with_target && runSummary.without_target) {
50
- const delta = {};
51
- for (const metric of ["pass_rate", "time_seconds", "tokens"]) {
52
- const withVal = runSummary.with_target[metric]?.mean ?? 0;
53
- const withoutVal = runSummary.without_target[metric]?.mean ?? 0;
54
- const diff = withVal - withoutVal;
55
- delta[metric] = `${diff >= 0 ? "+" : ""}${Math.round(diff * 1e3) / 1e3}`;
56
- }
57
- runSummary.delta = delta;
58
- const passRateDelta = (runSummary.with_target.pass_rate?.mean ?? 0) - (runSummary.without_target.pass_rate?.mean ?? 0);
59
- if (passRateDelta > 0.1) {
60
- notes.push(`Target improves pass rate by ${(passRateDelta * 100).toFixed(0)}%`);
61
- } else if (passRateDelta < -0.1) {
62
- notes.push(`Target decreases pass rate by ${(Math.abs(passRateDelta) * 100).toFixed(0)}%`);
63
- } else {
64
- notes.push("Target has minimal impact on pass rate");
65
- }
66
- const timeDelta = (runSummary.with_target.time_seconds?.mean ?? 0) - (runSummary.without_target.time_seconds?.mean ?? 0);
67
- if (Math.abs(timeDelta) > 5) {
68
- notes.push(`Target ${timeDelta > 0 ? "adds" : "saves"} ${Math.abs(timeDelta).toFixed(1)}s average execution time`);
69
- }
70
- }
71
- for (const [config, stats] of Object.entries(runSummary)) {
72
- if (config === "delta") continue;
73
- const passRate = stats.pass_rate;
74
- if (passRate && passRate.stddev > 0.2) {
75
- notes.push(`${config} shows high pass rate variance (stddev=${passRate.stddev.toFixed(2)}) \u2014 results may be unreliable`);
76
- }
77
- }
78
- return { runSummary, notes, totalRuns: completedRuns.length };
79
- }
80
- async function postBenchmark(targetId, iteration, result) {
81
- return apiPost(`/api/eval-targets/${targetId}/benchmarks`, {
82
- iteration,
83
- runSummary: result.runSummary,
84
- notes: result.notes
85
- });
86
- }
87
-
88
- export {
89
- aggregateBenchmark,
90
- postBenchmark
91
- };
92
- //# sourceMappingURL=chunk-PED7L5GX.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/lib/eval-benchmark.ts"],
4
- "sourcesContent": ["import { apiGet, apiPost } from './api-client.js';\n\ninterface EvalRun {\n id: string;\n evalId: string;\n evalTargetId: string;\n evalTargetVersion: number;\n configuration: string;\n runNumber: number;\n iteration: number;\n status: string;\n outputs: Record<string, unknown>;\n timing: Record<string, unknown>;\n grading: Record<string, unknown>;\n createdAt: string;\n}\n\ninterface Stats {\n mean: number;\n stddev: number;\n min: number;\n max: number;\n}\n\nfunction computeStats(values: number[]): Stats {\n if (values.length === 0) return { mean: 0, stddev: 0, min: 0, max: 0 };\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;\n return {\n mean: Math.round(mean * 1000) / 1000,\n stddev: Math.round(Math.sqrt(variance) * 1000) / 1000,\n min: Math.min(...values),\n max: Math.max(...values),\n };\n}\n\nexport interface BenchmarkResult {\n runSummary: Record<string, Record<string, Stats>>;\n notes: string[];\n totalRuns: number;\n}\n\nexport async function aggregateBenchmark(\n targetId: string,\n iteration?: number,\n): Promise<BenchmarkResult> {\n // Page through ALL runs. The list endpoint caps each page at 100; aggregating\n // over a single truncated page would skew the with/without delta (regression\n // guard \u2014 the 50-row default cap was added 2026-06-22).\n const iterQuery = iteration ? `&iteration=${iteration}` : '';\n const PAGE = 100;\n const runs: EvalRun[] = [];\n for (let offset = 0; offset < 100_000; offset += PAGE) {\n const page = await apiGet<EvalRun[]>(\n `/api/eval-targets/${targetId}/runs?limit=${PAGE}&offset=${offset}${iterQuery}`,\n );\n runs.push(...page);\n if (page.length < PAGE) break;\n }\n\n const completedRuns = runs.filter((r) => r.status === 'completed');\n\n if (completedRuns.length === 0) {\n return { runSummary: {}, notes: ['No completed runs found'], totalRuns: 0 };\n }\n\n // Group by configuration\n const byConfig: Record<string, EvalRun[]> = {};\n for (const run of completedRuns) {\n if (!byConfig[run.configuration]) byConfig[run.configuration] = [];\n byConfig[run.configuration].push(run);\n }\n\n const runSummary: Record<string, Record<string, Stats>> = {};\n const notes: string[] = [];\n\n for (const [config, configRuns] of Object.entries(byConfig)) {\n const passRates = configRuns\n .filter((r) => Object.keys(r.grading).length > 0)\n .map((r) => (r.grading as any).summary?.pass_rate ?? 0);\n const durations = configRuns\n .map((r) => (r.timing as any).duration_ms / 1000)\n .filter((v) => !isNaN(v));\n const tokens = configRuns\n .map((r) => (r.timing as any).total_tokens ?? 0)\n .filter((v) => v > 0);\n\n runSummary[config] = {};\n if (passRates.length > 0) runSummary[config].pass_rate = computeStats(passRates);\n if (durations.length > 0) runSummary[config].time_seconds = computeStats(durations);\n if (tokens.length > 0) runSummary[config].tokens = computeStats(tokens);\n }\n\n // Compute delta if both configurations exist\n if (runSummary.with_target && runSummary.without_target) {\n const delta: Record<string, string> = {};\n for (const metric of ['pass_rate', 'time_seconds', 'tokens']) {\n const withVal = runSummary.with_target[metric]?.mean ?? 0;\n const withoutVal = runSummary.without_target[metric]?.mean ?? 0;\n const diff = withVal - withoutVal;\n delta[metric] = `${diff >= 0 ? '+' : ''}${Math.round(diff * 1000) / 1000}`;\n }\n (runSummary as any).delta = delta;\n\n // Generate analysis notes\n const passRateDelta = (runSummary.with_target.pass_rate?.mean ?? 0) - (runSummary.without_target.pass_rate?.mean ?? 0);\n if (passRateDelta > 0.1) {\n notes.push(`Target improves pass rate by ${(passRateDelta * 100).toFixed(0)}%`);\n } else if (passRateDelta < -0.1) {\n notes.push(`Target decreases pass rate by ${(Math.abs(passRateDelta) * 100).toFixed(0)}%`);\n } else {\n notes.push('Target has minimal impact on pass rate');\n }\n\n const timeDelta = (runSummary.with_target.time_seconds?.mean ?? 0) - (runSummary.without_target.time_seconds?.mean ?? 0);\n if (Math.abs(timeDelta) > 5) {\n notes.push(`Target ${timeDelta > 0 ? 'adds' : 'saves'} ${Math.abs(timeDelta).toFixed(1)}s average execution time`);\n }\n }\n\n // Check for high variance\n for (const [config, stats] of Object.entries(runSummary)) {\n if (config === 'delta') continue;\n const passRate = stats.pass_rate;\n if (passRate && passRate.stddev > 0.2) {\n notes.push(`${config} shows high pass rate variance (stddev=${passRate.stddev.toFixed(2)}) \u2014 results may be unreliable`);\n }\n }\n\n return { runSummary, notes, totalRuns: completedRuns.length };\n}\n\nexport async function postBenchmark(\n targetId: string,\n iteration: number,\n result: BenchmarkResult,\n): Promise<{ id: string }> {\n return apiPost<{ id: string }>(`/api/eval-targets/${targetId}/benchmarks`, {\n iteration,\n runSummary: result.runSummary,\n notes: result.notes,\n });\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,44 +0,0 @@
1
- // src/dashboard/types.ts
2
- var PERIODS = ["today", "week", "30days", "month", "all"];
3
- var PERIOD_LABELS = {
4
- today: "Today",
5
- week: "7 Days",
6
- "30days": "30 Days",
7
- month: "This Month",
8
- all: "All Time"
9
- };
10
- function getDateRange(period) {
11
- const now = /* @__PURE__ */ new Date();
12
- const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
13
- switch (period) {
14
- case "today":
15
- return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate()), end };
16
- case "week":
17
- return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7), end };
18
- case "30days":
19
- return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30), end };
20
- case "month":
21
- return { start: new Date(now.getFullYear(), now.getMonth(), 1), end };
22
- case "all":
23
- return { start: /* @__PURE__ */ new Date(0), end };
24
- }
25
- }
26
- var ORANGE = "#FF8C42";
27
- var DIM = "#555555";
28
- var GOLD = "#FFD700";
29
- var GREEN = "#5BF58C";
30
- var RED = "#F55B5B";
31
- var BLUE = "#5B9EF5";
32
-
33
- export {
34
- PERIODS,
35
- PERIOD_LABELS,
36
- getDateRange,
37
- ORANGE,
38
- DIM,
39
- GOLD,
40
- GREEN,
41
- RED,
42
- BLUE
43
- };
44
- //# sourceMappingURL=chunk-QJQS6TUN.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/dashboard/types.ts"],
4
- "sourcesContent": ["export type Period = 'today' | 'week' | '30days' | 'month' | 'all';\n\nexport const PERIODS: Period[] = ['today', 'week', '30days', 'month', 'all'];\n\nexport const PERIOD_LABELS: Record<Period, string> = {\n today: 'Today',\n week: '7 Days',\n '30days': '30 Days',\n month: 'This Month',\n all: 'All Time',\n};\n\nexport function getDateRange(period: Period): { start: Date; end: Date } {\n const now = new Date();\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);\n switch (period) {\n case 'today': return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate()), end };\n case 'week': return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7), end };\n case '30days': return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30), end };\n case 'month': return { start: new Date(now.getFullYear(), now.getMonth(), 1), end };\n case 'all': return { start: new Date(0), end };\n }\n}\n\n// Colors\nexport const ORANGE = '#FF8C42';\nexport const DIM = '#555555';\nexport const GOLD = '#FFD700';\nexport const GREEN = '#5BF58C';\nexport const RED = '#F55B5B';\nexport const BLUE = '#5B9EF5';\n\nexport interface LocalStats {\n totalSessions: number;\n totalCalls: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalCacheWriteTokens: number;\n totalCostUSD: number;\n providers: Record<string, number>;\n}\n\nexport interface Suggestion {\n label: string;\n description: string;\n command: string;\n}\n\nexport interface ApiInsights {\n total_sessions: number;\n sessions_with_meta: number;\n sessions_with_analysis: number;\n total_messages: number;\n total_duration_hours: number;\n total_input_tokens: number;\n total_output_tokens: number;\n total_cache_read_tokens: number;\n total_cache_write_tokens: number;\n estimated_cost_usd: number;\n outcomes: Record<string, number>;\n friction: Record<string, number>;\n goal_categories: Record<string, number>;\n tools_used: Record<string, number>;\n tool_counts: Record<string, number>;\n git_commits: number;\n days_active: number;\n messages_per_day: number;\n recommendations?: {\n at_a_glance?: Record<string, string>;\n suggestions?: {\n features_to_try?: Array<{ feature: string; one_liner: string; why_for_you: string; example_code: string }>;\n usage_patterns?: Array<{ title: string; suggestion: string; detail: string; copyable_prompt: string }>;\n claude_md_additions?: Array<{ addition: string; why: string; prompt_scaffold: string }>;\n };\n on_the_horizon?: {\n opportunities?: Array<{ title: string; whats_possible: string; how_to_try: string; copyable_prompt: string }>;\n };\n [key: string]: unknown;\n };\n}\n"],
5
- "mappings": ";AAEO,IAAM,UAAoB,CAAC,SAAS,QAAQ,UAAU,SAAS,KAAK;AAEpE,IAAM,gBAAwC;AAAA,EACnD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,KAAK;AACP;AAEO,SAAS,aAAa,QAA4C;AACvE,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,MAAM,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,GAAG,IAAI,IAAI,IAAI,GAAG;AACtF,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAS,aAAO,EAAE,OAAO,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,CAAC,GAAG,IAAI;AAAA,IAC9F,KAAK;AAAQ,aAAO,EAAE,OAAO,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,IAAI,CAAC,GAAG,IAAI;AAAA,IACjG,KAAK;AAAU,aAAO,EAAE,OAAO,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,IAAI,EAAE,GAAG,IAAI;AAAA,IACpG,KAAK;AAAS,aAAO,EAAE,OAAO,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,CAAC,GAAG,IAAI;AAAA,IAClF,KAAK;AAAO,aAAO,EAAE,OAAO,oBAAI,KAAK,CAAC,GAAG,IAAI;AAAA,EAC/C;AACF;AAGO,IAAM,SAAS;AACf,IAAM,MAAM;AACZ,IAAM,OAAO;AACb,IAAM,QAAQ;AACd,IAAM,MAAM;AACZ,IAAM,OAAO;",
6
- "names": []
7
- }