rovecode 0.3.2 → 0.4.0-beta.2

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 (491) hide show
  1. package/CHANGELOG.md +512 -0
  2. package/README.md +33 -45
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-dybnr56b.js +2 -0
  5. package/dist/cli/ask-user-p8hq4xgj.js +2 -0
  6. package/dist/cli/auth-login-ewpgw5sm.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-xv3ypwev.js +9 -0
  9. package/dist/cli/catalog-737wb2s0.js +2 -0
  10. package/dist/cli/cli-arhg40m0.js +2 -0
  11. package/dist/cli/client-cf2pxx8q.js +2 -0
  12. package/dist/cli/commands-3p7e4xxs.js +2 -0
  13. package/dist/cli/connect-3q93d7cb.js +2 -0
  14. package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
  15. package/dist/cli/context-report-hbw9zfes.js +2 -0
  16. package/dist/cli/count-remote-mby98cd0.js +2 -0
  17. package/dist/cli/design-122y0axd.js +2 -0
  18. package/dist/cli/dispatch-b4egzvvh.js +2 -0
  19. package/dist/cli/doctor-x4jkv72e.js +3 -0
  20. package/dist/cli/executor-ftvg6tsy.js +2 -0
  21. package/dist/cli/export-pdgdhkch.js +2 -0
  22. package/dist/cli/files-cez9a96p.js +2 -0
  23. package/dist/cli/gauntlet-r3xxaszc.js +2 -0
  24. package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
  25. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
  26. package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
  27. package/dist/cli/hashline-ewg5hbe3.js +2 -0
  28. package/dist/cli/http-n0kehsk8.js +5 -0
  29. package/dist/cli/index-z5qt1s76.js +2 -0
  30. package/dist/cli/install-80mp63kx.js +2 -0
  31. package/dist/cli/loop-12twjcat.js +2 -0
  32. package/dist/cli/main-01pv9206.js +4 -0
  33. package/dist/cli/main-0jys2ccn.js +3 -0
  34. package/dist/cli/main-1ztz6fkj.js +10 -0
  35. package/dist/cli/main-23q7cmww.js +9 -0
  36. package/dist/cli/main-2rzbexn2.js +3 -0
  37. package/dist/cli/main-2wyax8k9.js +9 -0
  38. package/dist/cli/main-2yeveeve.js +6 -0
  39. package/dist/cli/main-2z3dek0b.js +3 -0
  40. package/dist/cli/main-2zgsknth.js +3 -0
  41. package/dist/cli/main-45ejth3a.js +4 -0
  42. package/dist/cli/main-45rn3trk.js +22 -0
  43. package/dist/cli/main-4p4e2w7x.js +4 -0
  44. package/dist/cli/main-4y0tnfpa.js +16 -0
  45. package/dist/cli/main-5py0rkmc.js +4 -0
  46. package/dist/cli/main-6dtqmbt6.js +7 -0
  47. package/dist/cli/main-6h9x282m.js +4 -0
  48. package/dist/cli/main-6vjeds42.js +3 -0
  49. package/dist/cli/main-78gq4bt9.js +6 -0
  50. package/dist/cli/main-7jd5vh3x.js +4 -0
  51. package/dist/cli/main-7kt6r53y.js +4 -0
  52. package/dist/cli/main-8c1tbazx.js +58 -0
  53. package/dist/cli/main-9a9rnh47.js +19 -0
  54. package/dist/cli/main-9ht36z12.js +3 -0
  55. package/dist/cli/main-a2yfvcy9.js +7 -0
  56. package/dist/cli/main-a3f51n0x.js +5 -0
  57. package/dist/cli/main-b8zq261k.js +3 -0
  58. package/dist/cli/main-bxtvnf6d.js +13 -0
  59. package/dist/cli/main-edxc3yzt.js +4 -0
  60. package/dist/cli/main-evgz4mp5.js +21 -0
  61. package/dist/cli/main-f33fc5je.js +9 -0
  62. package/dist/cli/main-fvnpq46y.js +12 -0
  63. package/dist/cli/main-gbbty4d4.js +3 -0
  64. package/dist/cli/main-gth53dnt.js +25 -0
  65. package/dist/cli/main-hqbz10aw.js +9 -0
  66. package/dist/cli/main-hrrvcfan.js +38 -0
  67. package/dist/cli/main-hzwtsb2m.js +5 -0
  68. package/dist/cli/main-j7ttv0sd.js +34 -0
  69. package/dist/cli/main-jak598k9.js +5 -0
  70. package/dist/cli/main-kba6zeyd.js +6 -0
  71. package/dist/cli/main-kwwsz6rq.js +3 -0
  72. package/dist/cli/main-m8vm17zq.js +3 -0
  73. package/dist/cli/main-mg4f96e1.js +3 -0
  74. package/dist/cli/main-mg9b20ac.js +18 -0
  75. package/dist/cli/main-mgb9ccnx.js +3 -0
  76. package/dist/cli/main-mjt2p7aj.js +3 -0
  77. package/dist/cli/main-n6qrdbmy.js +3 -0
  78. package/dist/cli/main-na7wse0x.js +5 -0
  79. package/dist/cli/main-nqveez48.js +4 -0
  80. package/dist/cli/main-ntqef02r.js +10 -0
  81. package/dist/cli/main-nvc3yjay.js +136 -0
  82. package/dist/cli/main-p0cfn6nr.js +16 -0
  83. package/dist/cli/main-qj2djy17.js +19 -0
  84. package/dist/cli/main-qsevpgsv.js +3 -0
  85. package/dist/cli/main-qvarybsp.js +3 -0
  86. package/dist/cli/main-rebtt91r.js +5 -0
  87. package/dist/cli/main-rpg7h8mb.js +3 -0
  88. package/dist/cli/main-rsy72qmw.js +15 -0
  89. package/dist/cli/main-rvetps99.js +18 -0
  90. package/dist/cli/main-s4bb0jav.js +3 -0
  91. package/dist/cli/main-s9v8k74e.js +3 -0
  92. package/dist/cli/main-tjvwmscs.js +3 -0
  93. package/dist/cli/main-tkgarpjj.js +4 -0
  94. package/dist/cli/main-v8y60bb2.js +3 -0
  95. package/dist/cli/main-vhrrq337.js +3 -0
  96. package/dist/cli/main-vp2dfb7s.js +4 -0
  97. package/dist/cli/main-vqbr22sz.js +8 -0
  98. package/dist/cli/main-vxnwe5xx.js +18 -0
  99. package/dist/cli/main-wgph00xf.js +5 -0
  100. package/dist/cli/main-wk2csfnj.js +5 -0
  101. package/dist/cli/main-wm997zjx.js +3 -0
  102. package/dist/cli/main-wpkyraxh.js +3 -0
  103. package/dist/cli/main-wqt32p5x.js +4 -0
  104. package/dist/cli/main-x9ct6y1a.js +3 -0
  105. package/dist/cli/main-xfekqh9m.js +7 -0
  106. package/dist/cli/main-xt9zc3n6.js +7 -0
  107. package/dist/cli/main-xx2z3zh5.js +4 -0
  108. package/dist/cli/main-y5c82rxr.js +3 -0
  109. package/dist/cli/main-yrjt2sqt.js +14 -0
  110. package/dist/cli/main-ys6zj3yr.js +3 -0
  111. package/dist/cli/main-ywbxshqc.js +8 -0
  112. package/dist/cli/main-z13755t8.js +25 -0
  113. package/dist/cli/main-zc7pyrbj.js +4 -0
  114. package/dist/cli/main.js +279 -0
  115. package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
  116. package/dist/cli/mcp-login-bthtfpt7.js +2 -0
  117. package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
  118. package/dist/cli/notify-54v5z9dz.js +2 -0
  119. package/dist/cli/oauth-g5gme95c.js +2 -0
  120. package/dist/cli/output-satndjap.js +16 -0
  121. package/dist/cli/profiles-sfhpbq3m.js +2 -0
  122. package/dist/cli/provider-config-hv3xtdt4.js +2 -0
  123. package/dist/cli/provider-kwzq6g84.js +2 -0
  124. package/dist/cli/registry-fh0hdnyn.js +2 -0
  125. package/dist/cli/registry-y1y8e94r.js +2 -0
  126. package/dist/cli/repl-t4z03mqq.js +11 -0
  127. package/dist/cli/resume-fqt4chg8.js +2 -0
  128. package/dist/cli/run-flags-rysbag9t.js +2 -0
  129. package/dist/cli/runtime-j19fjbsa.js +2 -0
  130. package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
  131. package/dist/cli/server-r0b6bksk.js +5 -0
  132. package/dist/cli/session-arg-txmn5g4x.js +2 -0
  133. package/dist/cli/session-ed250d9j.js +2 -0
  134. package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
  135. package/dist/cli/settings-y9rzcqx8.js +2 -0
  136. package/dist/cli/setup-jmbr11j0.js +2 -0
  137. package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
  138. package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
  139. package/dist/cli/smoke-1bg937kx.js +8 -0
  140. package/dist/cli/start-chat-p01cdks3.js +12 -0
  141. package/dist/cli/stream-4wmyaypz.js +2 -0
  142. package/dist/cli/task-eg4s093s.js +2 -0
  143. package/dist/cli/tasks-12v9rr9k.js +2 -0
  144. package/dist/cli/thinking-a5ngvqyh.js +2 -0
  145. package/dist/cli/todo-1wxpcecx.js +2 -0
  146. package/dist/cli/tools-2ftsya7w.js +2 -0
  147. package/dist/cli/tools-x1tj4fxm.js +2 -0
  148. package/dist/cli/trust-cmd-hccxehzb.js +2 -0
  149. package/dist/cli/update-check-ygt3vd7m.js +2 -0
  150. package/dist/cli/update-cmd-v23qhr8c.js +2 -0
  151. package/dist/cli/voice-g1gtck92.js +2 -0
  152. package/dist/cli/webfetch-0nnrjgb5.js +2 -0
  153. package/dist/cli/websearch-f0vr2p7d.js +2 -0
  154. package/dist/cli/workspace-9rq1w4ta.js +2 -0
  155. package/dist/lib/index.js +62 -0
  156. package/dist/lib/models-index.json +1 -0
  157. package/dist/lib/plugins.js +6 -0
  158. package/dist/lib/providers.js +17 -0
  159. package/dist/lib/public-api.js +20 -0
  160. package/dist/rovecode.exe +4 -0
  161. package/package.json +38 -11
  162. package/bin/rovecode.ts +0 -21
  163. package/src/acp/server.ts +0 -374
  164. package/src/cli/auth-login.ts +0 -122
  165. package/src/cli/connect.ts +0 -244
  166. package/src/cli/context-cmd.ts +0 -199
  167. package/src/cli/dispatch.ts +0 -82
  168. package/src/cli/doctor.ts +0 -362
  169. package/src/cli/export.ts +0 -276
  170. package/src/cli/help.ts +0 -293
  171. package/src/cli/is-tui-invocation.ts +0 -8
  172. package/src/cli/main.ts +0 -583
  173. package/src/cli/market-cmd.ts +0 -658
  174. package/src/cli/mcp-login.ts +0 -141
  175. package/src/cli/mcp-market-cmd.ts +0 -302
  176. package/src/cli/output.ts +0 -382
  177. package/src/cli/repl.ts +0 -250
  178. package/src/cli/repomap-root.ts +0 -14
  179. package/src/cli/resume.ts +0 -57
  180. package/src/cli/run-flags.ts +0 -43
  181. package/src/cli/run-limits.ts +0 -78
  182. package/src/cli/runtime.ts +0 -931
  183. package/src/cli/session-arg.ts +0 -30
  184. package/src/cli/sessions-cmd.ts +0 -145
  185. package/src/cli/setup.ts +0 -153
  186. package/src/cli/skills-cmd.ts +0 -194
  187. package/src/cli/start-chat.ts +0 -65
  188. package/src/cli/trust-cmd.ts +0 -52
  189. package/src/coding/bash.ts +0 -148
  190. package/src/coding/checkpoints.ts +0 -327
  191. package/src/coding/diff.ts +0 -138
  192. package/src/coding/files.ts +0 -341
  193. package/src/coding/hashline.ts +0 -274
  194. package/src/coding/lsp-gate.ts +0 -254
  195. package/src/coding/lsp-servers.ts +0 -147
  196. package/src/coding/lsp.ts +0 -283
  197. package/src/coding/repomap-cache.ts +0 -99
  198. package/src/coding/repomap-files.ts +0 -192
  199. package/src/coding/repomap.ts +0 -481
  200. package/src/core/agents.ts +0 -255
  201. package/src/core/compaction.ts +0 -259
  202. package/src/core/config.ts +0 -289
  203. package/src/core/context-report.ts +0 -228
  204. package/src/core/context.ts +0 -60
  205. package/src/core/count-remote.ts +0 -107
  206. package/src/core/execpolicy-rules.ts +0 -196
  207. package/src/core/execpolicy.ts +0 -385
  208. package/src/core/executor.ts +0 -454
  209. package/src/core/guardrails.ts +0 -400
  210. package/src/core/hooks.ts +0 -411
  211. package/src/core/images.ts +0 -230
  212. package/src/core/intro.ts +0 -266
  213. package/src/core/loop.ts +0 -567
  214. package/src/core/modes.ts +0 -372
  215. package/src/core/orchestrator.ts +0 -245
  216. package/src/core/proc-group.ts +0 -48
  217. package/src/core/project-trust.ts +0 -98
  218. package/src/core/reflection.ts +0 -165
  219. package/src/core/sandbox-config.ts +0 -186
  220. package/src/core/session-id.ts +0 -24
  221. package/src/core/session-images.ts +0 -73
  222. package/src/core/session-ops.ts +0 -183
  223. package/src/core/session-text.ts +0 -29
  224. package/src/core/session.ts +0 -469
  225. package/src/core/settings.ts +0 -170
  226. package/src/core/tasks.ts +0 -646
  227. package/src/core/token-scale.ts +0 -108
  228. package/src/core/tools.ts +0 -309
  229. package/src/core/trust.ts +0 -104
  230. package/src/core/types.ts +0 -330
  231. package/src/core/update-check.ts +0 -171
  232. package/src/core/usage.ts +0 -204
  233. package/src/core/validate.ts +0 -121
  234. package/src/core/verify-gate.ts +0 -159
  235. package/src/core/verify.ts +0 -236
  236. package/src/core/voice.ts +0 -158
  237. package/src/core/win-job.ts +0 -183
  238. package/src/core/workspace.ts +0 -184
  239. package/src/design/audit.ts +0 -797
  240. package/src/design/direction.ts +0 -190
  241. package/src/design/rules.ts +0 -157
  242. package/src/eval/bench.ts +0 -150
  243. package/src/eval/gauntlet-runner.ts +0 -215
  244. package/src/eval/gauntlet-support.ts +0 -84
  245. package/src/eval/gauntlet-wave3.ts +0 -269
  246. package/src/eval/gauntlet-wave4.ts +0 -217
  247. package/src/eval/gauntlet.ts +0 -253
  248. package/src/index.ts +0 -17
  249. package/src/lanes/agy.ts +0 -95
  250. package/src/lanes/approval.ts +0 -24
  251. package/src/lanes/claude.ts +0 -129
  252. package/src/lanes/codex.ts +0 -127
  253. package/src/lanes/events.ts +0 -130
  254. package/src/lanes/job.ts +0 -142
  255. package/src/lanes/opencode.ts +0 -122
  256. package/src/lanes/process.ts +0 -184
  257. package/src/lanes/progress.ts +0 -183
  258. package/src/lanes/registry.ts +0 -178
  259. package/src/lanes/runner.ts +0 -124
  260. package/src/lanes/types.ts +0 -112
  261. package/src/market/catalogs/mcp-docs.json +0 -111
  262. package/src/market/catalogs/plugins.json +0 -111
  263. package/src/market/catalogs/skills.json +0 -478
  264. package/src/market/clone.ts +0 -72
  265. package/src/market/context-cost.ts +0 -121
  266. package/src/market/digest.ts +0 -106
  267. package/src/market/index.ts +0 -22
  268. package/src/market/install.ts +0 -578
  269. package/src/market/manifest.ts +0 -187
  270. package/src/market/prereq.ts +0 -145
  271. package/src/market/registry.ts +0 -363
  272. package/src/market/resolve.ts +0 -111
  273. package/src/market/types.ts +0 -236
  274. package/src/market/validate.ts +0 -227
  275. package/src/mcp/client.ts +0 -449
  276. package/src/mcp/config.ts +0 -252
  277. package/src/mcp/local-package.ts +0 -211
  278. package/src/mcp/market-catalog.ts +0 -84
  279. package/src/mcp/market-install.ts +0 -289
  280. package/src/mcp/market.ts +0 -362
  281. package/src/mcp/oauth.ts +0 -251
  282. package/src/mcp/prompts-resources.ts +0 -249
  283. package/src/mcp/shared.ts +0 -149
  284. package/src/mcp/status.ts +0 -67
  285. package/src/mcp/tools.ts +0 -275
  286. package/src/mcp/transport.ts +0 -122
  287. package/src/mcp/trust.ts +0 -25
  288. package/src/memory/blocks.ts +0 -278
  289. package/src/memory/recall.ts +0 -355
  290. package/src/memory/scope.ts +0 -182
  291. package/src/memory/store.ts +0 -105
  292. package/src/memory/tools.ts +0 -99
  293. package/src/plugins/cli.ts +0 -119
  294. package/src/plugins/discover.ts +0 -108
  295. package/src/plugins/index.ts +0 -50
  296. package/src/plugins/install.ts +0 -184
  297. package/src/plugins/load.ts +0 -124
  298. package/src/plugins/manifest.ts +0 -92
  299. package/src/plugins/state.ts +0 -83
  300. package/src/providers/auth.ts +0 -408
  301. package/src/providers/cache.ts +0 -223
  302. package/src/providers/catalog-local.ts +0 -160
  303. package/src/providers/catalog.ts +0 -421
  304. package/src/providers/middleware-context.ts +0 -86
  305. package/src/providers/middleware.ts +0 -373
  306. package/src/providers/model-list.ts +0 -23
  307. package/src/providers/oauth/common.ts +0 -105
  308. package/src/providers/oauth/device-code.ts +0 -107
  309. package/src/providers/oauth/github-copilot.ts +0 -146
  310. package/src/providers/oauth/loopback.ts +0 -158
  311. package/src/providers/oauth/openai.ts +0 -163
  312. package/src/providers/oauth/openrouter.ts +0 -89
  313. package/src/providers/oauth/pkce.ts +0 -45
  314. package/src/providers/oauth/registry.ts +0 -39
  315. package/src/providers/oauth/seam.ts +0 -89
  316. package/src/providers/profile-glm53.ts +0 -111
  317. package/src/providers/profile-sonnet5-persona.ts +0 -65
  318. package/src/providers/profile-sonnet5-voice.ts +0 -23
  319. package/src/providers/profiles.ts +0 -156
  320. package/src/providers/provider-config.ts +0 -311
  321. package/src/providers/registry.ts +0 -333
  322. package/src/providers/responses.ts +0 -209
  323. package/src/providers/retry.ts +0 -234
  324. package/src/providers/router.ts +0 -294
  325. package/src/providers/sse.ts +0 -26
  326. package/src/providers/stream-errors.ts +0 -117
  327. package/src/providers/stream.ts +0 -566
  328. package/src/providers/thinking.ts +0 -189
  329. package/src/providers/wire-messages.ts +0 -129
  330. package/src/providers/wire-responses.ts +0 -79
  331. package/src/providers/wire-select.ts +0 -53
  332. package/src/server/http.ts +0 -291
  333. package/src/server/openapi.ts +0 -246
  334. package/src/sextant/card-hits.ts +0 -102
  335. package/src/sextant/card-keys.ts +0 -55
  336. package/src/sextant/context-source.ts +0 -157
  337. package/src/sextant/crew-cards.ts +0 -350
  338. package/src/sextant/draw-agents.ts +0 -273
  339. package/src/sextant/draw-code.ts +0 -388
  340. package/src/sextant/draw-context.ts +0 -222
  341. package/src/sextant/draw-frame.ts +0 -164
  342. package/src/sextant/draw-market.ts +0 -573
  343. package/src/sextant/draw-messages.ts +0 -386
  344. package/src/sextant/draw-pet.ts +0 -230
  345. package/src/sextant/draw-plan.ts +0 -187
  346. package/src/sextant/draw-tabs.ts +0 -85
  347. package/src/sextant/draw-util.ts +0 -65
  348. package/src/sextant/draw-wizard.ts +0 -378
  349. package/src/sextant/engine.ts +0 -230
  350. package/src/sextant/frame-hits.ts +0 -25
  351. package/src/sextant/frame.ts +0 -101
  352. package/src/sextant/git-status.ts +0 -197
  353. package/src/sextant/grid.ts +0 -59
  354. package/src/sextant/input.ts +0 -119
  355. package/src/sextant/keys.ts +0 -521
  356. package/src/sextant/layout.ts +0 -86
  357. package/src/sextant/local-commands.ts +0 -169
  358. package/src/sextant/market-source.ts +0 -287
  359. package/src/sextant/mentions.ts +0 -200
  360. package/src/sextant/message-hits.ts +0 -26
  361. package/src/sextant/model.ts +0 -387
  362. package/src/sextant/overlays.ts +0 -456
  363. package/src/sextant/panel-hits.ts +0 -38
  364. package/src/sextant/pet.ts +0 -399
  365. package/src/sextant/screen.ts +0 -324
  366. package/src/sextant/scroll-hits.ts +0 -66
  367. package/src/sextant/scrollbar.ts +0 -82
  368. package/src/sextant/sextant-bridge.ts +0 -174
  369. package/src/sextant/sextant-cards.ts +0 -142
  370. package/src/sextant/sextant-diff-base.ts +0 -63
  371. package/src/sextant/sextant-files.ts +0 -154
  372. package/src/sextant/sextant-frame-loop.ts +0 -335
  373. package/src/sextant/sextant-renderer.ts +0 -574
  374. package/src/sextant/sextant-repo.ts +0 -140
  375. package/src/sextant/theme.ts +0 -66
  376. package/src/sextant/tool-rows.ts +0 -189
  377. package/src/sextant/types.ts +0 -493
  378. package/src/skills/index.ts +0 -387
  379. package/src/skills/pack.ts +0 -220
  380. package/src/skills/spec.ts +0 -162
  381. package/src/skills/tools.ts +0 -69
  382. package/src/skills/versioned.ts +0 -227
  383. package/src/telemetry/otel-export.ts +0 -122
  384. package/src/telemetry/otel-lanes.ts +0 -89
  385. package/src/telemetry/otel-logs.ts +0 -131
  386. package/src/telemetry/otel-metrics.ts +0 -136
  387. package/src/telemetry/otel.ts +0 -397
  388. package/src/telemetry/otlp.ts +0 -76
  389. package/src/tools/ask-user.ts +0 -156
  390. package/src/tools/bash-bg.ts +0 -94
  391. package/src/tools/bash-jobs.ts +0 -237
  392. package/src/tools/design.ts +0 -151
  393. package/src/tools/evalcell.ts +0 -338
  394. package/src/tools/html-text.ts +0 -139
  395. package/src/tools/provider.ts +0 -149
  396. package/src/tools/task.ts +0 -250
  397. package/src/tools/todo.ts +0 -320
  398. package/src/tools/webfetch.ts +0 -332
  399. package/src/tools/websearch.ts +0 -359
  400. package/src/tui/agents-cmd.ts +0 -41
  401. package/src/tui/app.ts +0 -749
  402. package/src/tui/attach.ts +0 -127
  403. package/src/tui/boot-notes.ts +0 -41
  404. package/src/tui/builtin-prompts.ts +0 -59
  405. package/src/tui/checkpoints-cmd.ts +0 -70
  406. package/src/tui/clipboard-image.ts +0 -81
  407. package/src/tui/clipboard.ts +0 -78
  408. package/src/tui/commands.ts +0 -283
  409. package/src/tui/config-view.ts +0 -53
  410. package/src/tui/context-cmds.ts +0 -282
  411. package/src/tui/cost.ts +0 -108
  412. package/src/tui/crash-guard.ts +0 -173
  413. package/src/tui/focus-terminal.ts +0 -34
  414. package/src/tui/git-cmds.ts +0 -273
  415. package/src/tui/git-plain.ts +0 -58
  416. package/src/tui/info-cmd.ts +0 -150
  417. package/src/tui/input-plain.ts +0 -76
  418. package/src/tui/mcp-cmd.ts +0 -128
  419. package/src/tui/memory-note.ts +0 -77
  420. package/src/tui/modes-cmd.ts +0 -45
  421. package/src/tui/notify-seq.ts +0 -100
  422. package/src/tui/notify.ts +0 -318
  423. package/src/tui/overlays.ts +0 -97
  424. package/src/tui/pi-renderer.ts +0 -428
  425. package/src/tui/providers-cmd.ts +0 -377
  426. package/src/tui/reasoning-view.ts +0 -56
  427. package/src/tui/renderer.ts +0 -128
  428. package/src/tui/replay-marker.ts +0 -29
  429. package/src/tui/session-cmd.ts +0 -148
  430. package/src/tui/session-manage.ts +0 -95
  431. package/src/tui/sextant-attach.ts +0 -102
  432. package/src/tui/sextant-io.ts +0 -202
  433. package/src/tui/sextant-smoke.ts +0 -110
  434. package/src/tui/shell-cmd.ts +0 -158
  435. package/src/tui/smoke.ts +0 -72
  436. package/src/tui/staged-terminal.ts +0 -50
  437. package/src/tui/startup.ts +0 -12
  438. package/src/tui/theme.ts +0 -59
  439. package/src/tui/todo-label.ts +0 -7
  440. package/src/tui/trust-card.ts +0 -107
  441. package/src/tui/tui-commands.ts +0 -87
  442. package/tsconfig.json +0 -30
  443. package/vendor/pi-tui/LICENSE +0 -21
  444. package/vendor/pi-tui/PATCHES.md +0 -12
  445. package/vendor/pi-tui/PROVENANCE.md +0 -12
  446. package/vendor/pi-tui/README.upstream.md +0 -854
  447. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  448. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  449. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  450. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  451. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  452. package/vendor/pi-tui/src/components/box.ts +0 -138
  453. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  454. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  455. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  456. package/vendor/pi-tui/src/components/image.ts +0 -128
  457. package/vendor/pi-tui/src/components/input.ts +0 -448
  458. package/vendor/pi-tui/src/components/loader.ts +0 -93
  459. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  460. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  461. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  462. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  463. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  464. package/vendor/pi-tui/src/components/stack.ts +0 -155
  465. package/vendor/pi-tui/src/components/text.ts +0 -108
  466. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  467. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  468. package/vendor/pi-tui/src/editor-component.ts +0 -75
  469. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  470. package/vendor/pi-tui/src/index.ts +0 -149
  471. package/vendor/pi-tui/src/keybindings.ts +0 -321
  472. package/vendor/pi-tui/src/keys.ts +0 -1402
  473. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  474. package/vendor/pi-tui/src/latex.ts +0 -1381
  475. package/vendor/pi-tui/src/layout-node.ts +0 -52
  476. package/vendor/pi-tui/src/layout.ts +0 -411
  477. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  478. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  479. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  480. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  481. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  482. package/vendor/pi-tui/src/terminal.ts +0 -554
  483. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  484. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  485. package/vendor/pi-tui/src/tui.ts +0 -1264
  486. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  487. package/vendor/pi-tui/src/utils.ts +0 -1327
  488. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  489. package/vendor/pi-tui/test/test-themes.ts +0 -39
  490. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  491. /package/{src/providers → dist/cli}/models-index.json +0 -0
@@ -1,162 +0,0 @@
1
- /** agentskills.io-compatible SKILL.md frontmatter (port #72): the spec field set, a lenient | strict validator and
2
- * the skill-specific frontmatter walker. Pattern sources (no code copied): pi packages/coding-agent/src/core/skills.ts
3
- * (MIT — validateName :92-115, validateDescription :117-127, warn-and-load :276-345, name → parent-dir fallback :319),
4
- * opencode-2026 skill/index.ts:53-59 (MIT — type-only checks; unknown fields ignored, skills.mdx:45), hermes
5
- * agent/skill_utils.py:1182-1198 (MIT — the 60-char cut was a PROMPT truncation, never a load rule). The field set
6
- * and limits follow the agentskills.io specification (verified 2026-09-04): name ≤ 64, lower-case [a-z0-9] joined by
7
- * single hyphens, equal to the parent directory; description 1–1024; compatibility ≤ 500; metadata string → string;
8
- * allowed-tools one space-separated string (experimental — parsed as DATA, never enforced). The spec defines NO
9
- * archive format and says nothing about unknown keys: rovecode warns once and loads (`version` is its own known key).
10
- *
11
- * Why a second walker beside index.ts parseFrontmatter: that helper drops every colon-less line — core/agents.ts
12
- * checkFrontmatterLines and tui/commands.ts depend on exactly that, so it stays byte-identical — and therefore
13
- * cannot carry an indented `metadata:` map or a `>` / `|` block-scalar description. parseSkillFrontmatter reads the
14
- * flat `key: value` files the old loader read the same way (same quote strip, same body slice) and additionally:
15
- * nested pairs under `metadata:` become the metadata map; indented lines under any other key are continuation text
16
- * joined by ONE space (`>`, `>-`, `|` scalars read as one line); a colon inside an unquoted value is kept; BOM and
17
- * CRLF tolerated; a key whose children are `- item` lines is recorded as a list (allowed-tools must be a string). */
18
-
19
- export const NAME_MAX = 64;
20
- export const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
21
- export const DESCRIPTION_MAX = 1024;
22
- export const COMPATIBILITY_MAX = 500;
23
- /** the spec's six fields + rovecode's own `version` (never warned about) */
24
- export const KNOWN_KEYS: ReadonlySet<string> = new Set(["name", "description", "license", "compatibility", "metadata", "allowed-tools", "version"]);
25
-
26
- export type FieldShape = "scalar" | "list" | "map";
27
- export type ValidateMode = "lenient" | "strict";
28
-
29
- export interface SkillFrontmatter {
30
- /** top-level values: inline text, or continuation lines joined by one space; quotes stripped */
31
- fields: Record<string, string>;
32
- /** how every top-level key was written (metadata is a map when it has indented children) */
33
- shapes: Record<string, FieldShape>;
34
- /** the `metadata:` map, string values only */
35
- metadata: Record<string, string>;
36
- /** metadata keys whose value was a nested map, a list or empty — not a string */
37
- metadataNonString: string[];
38
- body: string;
39
- }
40
-
41
- /** the validated field set of one SKILL.md */
42
- export interface SkillMeta {
43
- name: string;
44
- description: string;
45
- /** top-level `version`, else `metadata.version`, else "" */
46
- version: string;
47
- license?: string;
48
- compatibility?: string;
49
- metadata: Record<string, string>;
50
- /** `allowed-tools` whitespace-split — data only */
51
- allowedTools: string[];
52
- }
53
-
54
- export interface Validation {
55
- meta: SkillMeta;
56
- /** the file must not load (lenient) / the check fails (strict) */
57
- errors: string[];
58
- /** lenient mode: loaded anyway, reported */
59
- warnings: string[];
60
- /** strict mode: unknown keys — printed, never a failure */
61
- notes: string[];
62
- }
63
-
64
- const BLOCK_SCALAR = /^[>|][+-]?$/;
65
- /** `key: value` where the key is identifier-shaped — a nested-map line, not a sentence holding a colon */
66
- const MAP_LINE = /^[A-Za-z0-9_-]+:(\s|$)/;
67
- const unquote = (v: string): string => v.trim().replace(/^["']|["']$/g, "");
68
-
69
- /** null when the text has no `---` block or the block is unterminated (the two INVALID shapes). */
70
- export function parseSkillFrontmatter(raw: string): SkillFrontmatter | null {
71
- const text = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
72
- const firstEnd = text.indexOf("\n");
73
- if (firstEnd === -1 || text.slice(0, firstEnd).replace(/\r$/, "") !== "---") return null;
74
- const close = /\r?\n---[ \t]*(?=\r?\n|$)/g;
75
- close.lastIndex = firstEnd;
76
- const m = close.exec(text);
77
- if (m === null) return null;
78
- const block = text.slice(firstEnd + 1, m.index);
79
- // past the closing `---` line, then one blank separator line — the parseFrontmatter body slice
80
- const body = text.slice(m.index + m[0].length).replace(/^\r?\n/, "").replace(/^\r?\n/, "");
81
-
82
- const fields: Record<string, string> = {};
83
- const shapes: Record<string, FieldShape> = {};
84
- const metadata: Record<string, string> = {};
85
- const metadataNonString: string[] = [];
86
- const markNonString = (k: string): void => { delete metadata[k]; if (!metadataNonString.includes(k)) metadataNonString.push(k); };
87
- let key: string | undefined; // the open top-level key
88
- let blockScalar = false;
89
- let metaKey: string | undefined; // the open metadata sub-key
90
- let metaIndent = -1;
91
- for (const line of block.split(/\r?\n/)) {
92
- const t = line.trim();
93
- if (t === "" || t.startsWith("#")) continue;
94
- const indent = line.length - line.trimStart().length;
95
- if (indent === 0) {
96
- const i = line.indexOf(":");
97
- if (i <= 0) { key = undefined; continue; } // not a `key: value` line — skipped, as parseFrontmatter skips it
98
- key = line.slice(0, i).trim();
99
- const rest = line.slice(i + 1).trim();
100
- metaKey = undefined; metaIndent = -1;
101
- blockScalar = BLOCK_SCALAR.test(rest);
102
- if (key === "metadata") { shapes[key] = rest === "" ? "map" : "scalar"; if (rest !== "") fields[key] = unquote(rest); continue; }
103
- shapes[key] = "scalar";
104
- fields[key] = blockScalar ? "" : unquote(rest);
105
- continue;
106
- }
107
- if (key === undefined) continue;
108
- if (key === "metadata" && shapes[key] === "map") {
109
- if (metaKey !== undefined && indent > metaIndent) { markNonString(metaKey); continue; } // nested map / list under a sub-key
110
- const i = t.indexOf(":");
111
- if (t.startsWith("- ") || i <= 0) { shapes[key] = "list"; continue; } // `metadata:` holding a list
112
- metaKey = t.slice(0, i).trim(); metaIndent = indent;
113
- const v = t.slice(i + 1).trim();
114
- if (v === "") markNonString(metaKey); else metadata[metaKey] = unquote(v);
115
- continue;
116
- }
117
- const cont = fields[key] ?? "";
118
- if (!blockScalar && shapes[key] === "scalar" && cont === "") {
119
- if (t.startsWith("- ")) shapes[key] = "list"; else if (MAP_LINE.test(t)) shapes[key] = "map";
120
- }
121
- fields[key] = cont === "" ? t : `${cont} ${t}`;
122
- }
123
- return { fields, shapes, metadata, metadataNonString, body };
124
- }
125
-
126
- /** The spec checks. Lenient (the loader): an empty or missing description is the ONE error, everything else a
127
- * warning and the file loads (a missing name falls back to the directory name — pi:319). Strict (`validate`,
128
- * `pack`): every spec violation is an error; unknown keys are notes in both modes' spirit — never a failure. */
129
- export function validateSkillMeta(fm: SkillFrontmatter, dirBasename: string, mode: ValidateMode): Validation {
130
- const errors: string[] = [], warnings: string[] = [], notes: string[] = [];
131
- const flag = (msg: string): void => { (mode === "strict" ? errors : warnings).push(msg); };
132
- const f = fm.fields;
133
- const description = f["description"] ?? "";
134
- if (description === "") errors.push(`description is required (1–${DESCRIPTION_MAX} characters)`);
135
- else if (description.length > DESCRIPTION_MAX) flag(`description is ${description.length} characters (max ${DESCRIPTION_MAX})`);
136
- let name = f["name"] ?? "";
137
- if (name === "") {
138
- if (mode === "strict") errors.push("name is required");
139
- else warnings.push(`name missing — using the directory name "${dirBasename}"`);
140
- name = dirBasename;
141
- } else {
142
- if (name.length > NAME_MAX) flag(`name is ${name.length} characters (max ${NAME_MAX})`);
143
- if (!NAME_RE.test(name)) flag(`name "${name}" must be lower-case letters, digits and single hyphens (no leading, trailing or double hyphen)`);
144
- if (name !== dirBasename) flag(`name "${name}" does not match its directory "${dirBasename}"`);
145
- }
146
- const compatibility = f["compatibility"] || undefined;
147
- if (compatibility !== undefined && compatibility.length > COMPATIBILITY_MAX) flag(`compatibility is ${compatibility.length} characters (max ${COMPATIBILITY_MAX})`);
148
- const metaShape = fm.shapes["metadata"];
149
- if (metaShape !== undefined && metaShape !== "map") flag("metadata must be a map of string keys to string values");
150
- if (fm.metadataNonString.length > 0) flag(`metadata values must be strings: ${fm.metadataNonString.join(", ")}`);
151
- const toolsShape = fm.shapes["allowed-tools"];
152
- if (toolsShape !== undefined && toolsShape !== "scalar") flag("allowed-tools must be one space-separated string");
153
- const unknown = Object.keys(fm.shapes).filter((k) => !KNOWN_KEYS.has(k));
154
- if (unknown.length > 0) (mode === "strict" ? notes : warnings).push(`unknown frontmatter keys: ${unknown.join(", ")}`);
155
- const version = f["version"] || fm.metadata["version"] || "";
156
- const license = f["license"] || undefined;
157
- const allowedTools = (f["allowed-tools"] ?? "").split(/\s+/).filter((t) => t !== "");
158
- const meta: SkillMeta = { name, description, version, metadata: fm.metadata, allowedTools };
159
- if (license !== undefined) meta.license = license;
160
- if (compatibility !== undefined) meta.compatibility = compatibility;
161
- return { meta, errors, warnings, notes };
162
- }
@@ -1,69 +0,0 @@
1
- /** Skill tools (hermes pattern): `skill_view` returns a body and bumps the
2
- * usage sidecar; `skills_list` returns the index. Above 50 skills the index
3
- * is too big for the system prompt, so `skills_list` is the only access path
4
- * and `buildSkillsIndex` returns "" (prompt_builder.py:2083 reject rule). */
5
-
6
- import type { Tool, ToolContext, ToolOutput } from "../core/types.ts";
7
- import { SkillStore, skillLifecycle, type Skill, type SkillUsage } from "./index.ts";
8
-
9
- export const INDEX_PROMPT_LIMIT = 50; // hermes: >50 skills → skills_list tool only
10
-
11
- function renderIndexLine(s: Skill, usage: SkillUsage | undefined, now: number): string {
12
- const state = skillLifecycle(s, usage, now);
13
- return `- ${s.name}${s.version ? ` (v${s.version})` : ""}: ${s.description}${state === "stale" ? " [stale]" : ""}`;
14
- }
15
-
16
- /** System-prompt index. Empty string when the catalogue outgrows the prompt. */
17
- export function buildSkillsIndex(store: SkillStore, now: number = Date.now()): string {
18
- const skills = store.list();
19
- if (skills.length === 0 || skills.length > INDEX_PROMPT_LIMIT) return "";
20
- return skills.map((s) => renderIndexLine(s, store.usage(s.name), now)).join("\n");
21
- }
22
-
23
- export function createSkillTools(store: SkillStore): Tool[] {
24
- const skillView: Tool = {
25
- schema: {
26
- name: "skill_view",
27
- description:
28
- "Read one skill's full instructions. Use when a skill in the index matches the task.",
29
- args: {
30
- type: "object",
31
- properties: { name: { type: "string", description: "skill name from the index" } },
32
- required: ["name"],
33
- },
34
- },
35
- kind: "read",
36
- sequential: true, // sidecar write
37
- execute(args): Promise<ToolOutput> {
38
- const raw = args as { name?: unknown };
39
- const name = typeof raw.name === "string" ? raw.name : "";
40
- const skill = store.get(name);
41
- if (!skill) return Promise.resolve({ ok: false, output: `no skill named '${name}'` });
42
- const usage = store.bumpUsage(skill);
43
- // the FULL description, not the index's clipped line: the model opened this skill to read it, so
44
- // the sentence saying when to use it belongs here in one piece
45
- const header = `# ${skill.name} (v${skill.version})\n\n${skill.fullDescription}\n\n`;
46
- return Promise.resolve({ ok: true, output: header + skill.body, data: usage });
47
- },
48
- };
49
-
50
- const skillsList: Tool = {
51
- schema: {
52
- name: "skills_list",
53
- description:
54
- "List available skills (name, version, description, staleness). Use to discover skills when the system prompt has no skill index.",
55
- args: { type: "object", properties: {} },
56
- },
57
- kind: "read",
58
- execute(_args: unknown, _ctx: ToolContext): Promise<ToolOutput> {
59
- store.scan(); // fresh catalogue even when the caller forgot to rescan
60
- const skills = store.list();
61
- if (skills.length === 0) return Promise.resolve({ ok: true, output: "no skills installed" });
62
- const now = Date.now();
63
- const lines = skills.map((s) => renderIndexLine(s, store.usage(s.name), now));
64
- return Promise.resolve({ ok: true, output: lines.join("\n"), data: { count: skills.length } });
65
- },
66
- };
67
-
68
- return [skillView, skillsList];
69
- }
@@ -1,227 +0,0 @@
1
- /** Versioned edit ledger for skill/memory files (PORT #16, prime-agent MIT).
2
- *
3
- * Port of the refinement/versioned-state machinery in prime-agent
4
- * packages/coding-agent/src/core/refinement/refinement.ts:
5
- * - per-edit before/after snapshots + version bump (AppliedRefinementEdit:85,
6
- * `version = before ? before.version + 1 : 1`:770, record push:788)
7
- * - optimistic-concurrency baseline check → typed reject, no write (735-749)
8
- * - rollback built from recorded before/after, itself a new history entry
9
- * (rollbackProposal:813-845, rollbackOf:100)
10
- * - append-only JSONL history beside the store; malformed lines skipped so a
11
- * bad append can never break rollback (refinements.jsonl:25, 374-400)
12
- *
13
- * Deliberately NOT ported (BLUEPRINT §5 + prime's reward-hacking findings):
14
- * no auto-refinement loop (prime's reviewAutoRefine LLM gate stays out), no
15
- * LLM proposal pass, and no grader/eval surface anywhere in this API
16
- * (ouroboros rule: graders stay hidden from agent-facing code). Edits happen
17
- * only when a human or an explicit tool call invokes commit()/rollback().
18
- *
19
- * Storage: sidecar JSONL beside the target file — `<target>.versions.jsonl`
20
- * (mirrors the house `SKILL.md.usage.json` sidecar). One line per edit:
21
- * {version, baseVersion, before, after, reason, timestamp, rollbackTo?}.
22
- * Every record carries FULL before/after content, so record v restores both
23
- * content@v (`after`) and content@v-1 (`before`). With the newest K records
24
- * retained, rollback covers versions [oldest.baseVersion .. newest.version].
25
- * History is bounded: past `maxEdits` records the oldest are evicted, and
26
- * versions older than the window become unreachable (typed reject).
27
- * Versions are monotonic and never reused after eviction. */
28
-
29
- import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
30
- import { dirname } from "node:path";
31
-
32
- export const LEDGER_SUFFIX = ".versions.jsonl";
33
- export const DEFAULT_MAX_EDITS = 20;
34
-
35
- export interface VersionedEdit {
36
- /** version this edit produced (monotonic; = baseVersion + 1) */
37
- version: number;
38
- /** version the editor read before making the change */
39
- baseVersion: number;
40
- /** full target content before the edit (content at baseVersion) */
41
- before: string;
42
- /** full target content after the edit (content at version) */
43
- after: string;
44
- reason: string;
45
- /** ms epoch */
46
- timestamp: number;
47
- /** set when this edit was produced by rollback(): the restored version */
48
- rollbackTo?: number;
49
- }
50
-
51
- /** Inclusive range of versions rollback can currently restore. */
52
- export interface VersionRange { from: number; to: number }
53
-
54
- export type CommitResult =
55
- | { ok: true; edit: VersionedEdit }
56
- | { ok: false; code: "version-conflict"; baseVersion: number; currentVersion: number; message: string };
57
-
58
- export type RollbackResult =
59
- | { ok: true; edit: VersionedEdit }
60
- | { ok: false; code: "unknown-version"; requested: number; available: VersionRange | null; message: string };
61
-
62
- // ---------- internals ----------
63
-
64
- /** Atomic write (house pattern, skills/index.ts writeUsageAtomic): tmp + rename. */
65
- function atomicWrite(path: string, text: string): void {
66
- mkdirSync(dirname(path), { recursive: true });
67
- const tmp = `${path}.${process.pid}.tmp`;
68
- writeFileSync(tmp, text);
69
- renameSync(tmp, path);
70
- }
71
-
72
- /** Parse one JSONL line into an edit, or null when malformed (prime: skip, never throw). */
73
- function parseEdit(line: string): VersionedEdit | null {
74
- let raw: unknown;
75
- try {
76
- raw = JSON.parse(line);
77
- } catch {
78
- return null;
79
- }
80
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
81
- const r = raw as Record<string, unknown>;
82
- if (
83
- typeof r["version"] !== "number" || typeof r["baseVersion"] !== "number" ||
84
- typeof r["before"] !== "string" || typeof r["after"] !== "string" ||
85
- typeof r["reason"] !== "string" || typeof r["timestamp"] !== "number"
86
- ) return null;
87
- const edit: VersionedEdit = {
88
- version: r["version"], baseVersion: r["baseVersion"],
89
- before: r["before"], after: r["after"],
90
- reason: r["reason"], timestamp: r["timestamp"],
91
- };
92
- if (typeof r["rollbackTo"] === "number") edit.rollbackTo = r["rollbackTo"];
93
- return edit;
94
- }
95
-
96
- // ---------- ledger ----------
97
-
98
- export interface VersionLedgerOptions {
99
- /** sidecar path (default: `<target>${LEDGER_SUFFIX}`) */
100
- ledgerPath?: string;
101
- /** bounded snapshot count; oldest records evicted past this (default 20, min 1) */
102
- maxEdits?: number;
103
- }
104
-
105
- export class VersionLedger {
106
- readonly ledgerPath: string;
107
- private readonly maxEdits: number;
108
-
109
- constructor(readonly targetPath: string, opts: VersionLedgerOptions = {}) {
110
- this.ledgerPath = opts.ledgerPath ?? targetPath + LEDGER_SUFFIX;
111
- this.maxEdits = Math.max(1, opts.maxEdits ?? DEFAULT_MAX_EDITS);
112
- }
113
-
114
- /** Retained edits, oldest → newest. Malformed lines are skipped; duplicate
115
- * versions keep the last occurrence (append order wins). */
116
- history(): VersionedEdit[] {
117
- if (!existsSync(this.ledgerPath)) return [];
118
- const byVersion = new Map<number, VersionedEdit>();
119
- for (const line of readFileSync(this.ledgerPath, "utf8").split("\n")) {
120
- const trimmed = line.trim();
121
- if (!trimmed) continue;
122
- const edit = parseEdit(trimmed);
123
- if (edit) byVersion.set(edit.version, edit);
124
- }
125
- return [...byVersion.values()].sort((a, b) => a.version - b.version);
126
- }
127
-
128
- /** Current version: 0 until the first recorded edit; unaffected by eviction. */
129
- version(): number {
130
- return this.history().at(-1)?.version ?? 0;
131
- }
132
-
133
- /** Current on-disk content of the target ("" when the file does not exist). */
134
- read(): string {
135
- return existsSync(this.targetPath) ? readFileSync(this.targetPath, "utf8") : "";
136
- }
137
-
138
- /** Versions rollback can restore right now, or null before the first edit.
139
- * Derived from the CONTIGUOUS suffix of retained records: a corruption hole
140
- * (missing/garbled record) breaks before/after coverage, so a naive
141
- * first..last span would advertise versions rollback cannot restore. */
142
- range(): VersionRange | null {
143
- const edits = this.history();
144
- const last = edits.at(-1);
145
- if (!last) return null;
146
- let start = edits.length - 1;
147
- while (start > 0 && edits[start - 1]!.version === edits[start]!.baseVersion) start--;
148
- return { from: edits[start]!.baseVersion, to: last.version };
149
- }
150
-
151
- /** Content at a recorded version, or undefined when evicted/unknown. Besides a
152
- * record's own `after`, any record with baseVersion === version restores it via
153
- * `before` — recoverable even when the record that PRODUCED it was corrupted. */
154
- contentAt(version: number): string | undefined {
155
- const edits = this.history();
156
- const direct = edits.find((e) => e.version === version);
157
- if (direct) return direct.after;
158
- return edits.find((e) => e.baseVersion === version)?.before;
159
- }
160
-
161
- /** True when the target was modified outside the ledger since the last edit. */
162
- drifted(): boolean {
163
- const last = this.history().at(-1);
164
- return last !== undefined && this.read() !== last.after;
165
- }
166
-
167
- /** Record an edit and write the target. Optimistic concurrency: `baseVersion`
168
- * must equal the current version or the commit is rejected BEFORE any disk
169
- * mutation — no partial write. `before` is read from disk here, so the
170
- * record stays truthful even if the target drifted externally. */
171
- commit(after: string, reason: string, baseVersion: number, now: number = Date.now()): CommitResult {
172
- const currentVersion = this.version();
173
- if (baseVersion !== currentVersion) {
174
- return {
175
- ok: false, code: "version-conflict", baseVersion, currentVersion,
176
- message: `baseVersion ${baseVersion} does not match current version ${currentVersion}; re-read and retry`,
177
- };
178
- }
179
- return { ok: true, edit: this.write(after, reason, currentVersion, now) };
180
- }
181
-
182
- /** One-call rollback to any retained version. Recorded as a NEW edit with
183
- * `rollbackTo` (prime `rollbackOf` pattern) — history is append-only, and
184
- * the restored content is byte-exact from the stored snapshot. */
185
- rollback(toVersion: number, reason?: string, now: number = Date.now()): RollbackResult {
186
- const content = this.contentAt(toVersion);
187
- if (content === undefined) {
188
- const available = this.range();
189
- return {
190
- ok: false, code: "unknown-version", requested: toVersion, available,
191
- message: available
192
- ? `version ${toVersion} is not restorable; available: ${available.from}..${available.to}`
193
- : `version ${toVersion} is not restorable; no recorded edits`,
194
- };
195
- }
196
- const edit = this.write(content, reason ?? `rollback to v${toVersion}`, this.version(), now, toVersion);
197
- return { ok: true, edit };
198
- }
199
-
200
- /** Append the record, evict past the bound, then atomically write the target.
201
- * Ledger-first ordering: a crash between the two leaves an honest record
202
- * (visible via drifted()) rather than an unrecorded content change. */
203
- private write(after: string, reason: string, currentVersion: number, now: number, rollbackTo?: number): VersionedEdit {
204
- const edit: VersionedEdit = {
205
- version: currentVersion + 1, baseVersion: currentVersion,
206
- before: this.read(), after, reason, timestamp: now,
207
- ...(rollbackTo !== undefined ? { rollbackTo } : {}),
208
- };
209
- mkdirSync(dirname(this.ledgerPath), { recursive: true });
210
- // Heal a torn trailing append: a crash mid-append can leave the last line
211
- // without "\n" — appending straight on would concatenate this record into the
212
- // torn line, making the NEW commit invisible to history()/version().
213
- let record = JSON.stringify(edit) + "\n";
214
- if (existsSync(this.ledgerPath)) {
215
- const raw = readFileSync(this.ledgerPath);
216
- if (raw.length > 0 && raw[raw.length - 1] !== 0x0a) record = "\n" + record;
217
- }
218
- appendFileSync(this.ledgerPath, record, "utf8");
219
- const edits = this.history();
220
- if (edits.length > this.maxEdits) {
221
- const kept = edits.slice(edits.length - this.maxEdits);
222
- atomicWrite(this.ledgerPath, kept.map((e) => JSON.stringify(e)).join("\n") + "\n");
223
- }
224
- atomicWrite(this.targetPath, after);
225
- return edit;
226
- }
227
- }
@@ -1,122 +0,0 @@
1
- /** OTLP/HTTP exporter pipe (port #82) — ONE exporter for the three signals the #39 hook set emits:
2
- * traces (`<base>/v1/traces`), metrics (`<base>/v1/metrics`) and logs (`<base>/v1/logs`). The base is the
3
- * configured ROVECODE_OTEL_ENDPOINT with a trailing `/v1/traces` stripped (so a full traces URL still yields
4
- * its siblings), each POST rides the #39 REF'D timeout (Bun unrefs AbortSignal.timeout — hooks.ts idiom)
5
- * and is tracked so flush() awaits every outstanding one; the export path never throws.
6
- *
7
- * Failure policy: 5xx / timeout / network stay per-export warnings with the #39 texts byte-identical
8
- * (`OTLP export to <url> failed: HTTP 500` / `… timed out after 5000ms` / `… ECONNREFUSED`) and never
9
- * disable anything. HTTP 404 / 405 / 501 on a signal mean the collector does not serve that path at all
10
- * (an old collector without the logs receiver, a traces-only proxy): THAT signal is disabled for the life
11
- * of this exporter (one per runtime: the process's one hook set on the CLI/TUI, one per session under
12
- * `rovecode serve`) after ONE note — `OTLP collector <base> does not accept <signal> (HTTP <n>) — <signal>
13
- * export disabled` — while the other signals continue. A malformed endpoint (`http://`, `host:4318`) makes
14
- * the exporter unusable: no POST is ever attempted (the #39 one-note rule; otel.ts raises it).
15
- *
16
- * Moved verbatim out of otel.ts (post / track / flush / withTimeout / TIMED_OUT / errText / hex /
17
- * normalizeEndpoint / validEndpoint) so that file stays under the 400-line cap; otel.ts re-exports the
18
- * public names, so `import … from "telemetry/otel.ts"` is unchanged. OTLP/HTTP paths and JSON field names
19
- * follow the public opentelemetry-proto spec (hand-encoded; no @opentelemetry dependency). */
20
-
21
- import { randomBytes } from "node:crypto";
22
-
23
- export const DEFAULT_EXPORT_TIMEOUT_MS = 5000;
24
- export const OTLP_TRACES_PATH = "/v1/traces";
25
- export type OtlpSignal = "traces" | "metrics" | "logs";
26
- export const OTLP_SIGNALS: readonly OtlpSignal[] = ["traces", "metrics", "logs"];
27
- /** HTTP statuses that say "this path is not served here" — the signal is switched off, not retried */
28
- const NOT_SERVED: ReadonlySet<number> = new Set([404, 405, 501]);
29
-
30
- /** base URL or full traces URL (either, with or without trailing slashes) → <base>/v1/traces */
31
- export function normalizeEndpoint(endpoint: string): string {
32
- const base = endpoint.trim().replace(/\/+$/, "");
33
- return base.endsWith(OTLP_TRACES_PATH) ? base : base + OTLP_TRACES_PATH;
34
- }
35
- /** the collector base: the normalized traces URL minus its `/v1/traces` — the siblings hang off it */
36
- export const baseOf = (endpoint: string): string => normalizeEndpoint(endpoint).slice(0, -OTLP_TRACES_PATH.length);
37
- export const signalUrl = (endpoint: string, signal: OtlpSignal): string => `${baseOf(endpoint)}/v1/${signal}`;
38
-
39
- /** an absolute http(s) URL with a host — `http://` does not parse and `host:4318` parses host-less,
40
- * and both would normalize to `http:/v1/traces` (host "v1") and stall every export to the timeout */
41
- export function validEndpoint(endpoint: string): boolean {
42
- try { const u = new URL(endpoint.trim()); return (u.protocol === "http:" || u.protocol === "https:") && u.hostname !== ""; } catch { return false; }
43
- }
44
-
45
- export interface OtlpExporterOptions {
46
- endpoint: string;
47
- headers?: Record<string, string>;
48
- fetch?: typeof fetch;
49
- timeoutMs?: number;
50
- /** every export note (per-export failures, the one-shot per-signal disable) */
51
- warn: (note: string) => void;
52
- }
53
- export interface OtlpExporter {
54
- readonly base: string;
55
- /** false = malformed endpoint: nothing is ever POSTed (the caller says so once) */
56
- readonly usable: boolean;
57
- url(signal: OtlpSignal): string;
58
- /** POST one encoded request; fire-and-forget, tracked for flush(); never throws */
59
- post(signal: OtlpSignal, body: string): void;
60
- /** await every outstanding POST (posts issued while waiting are awaited too) */
61
- flush(): Promise<void>;
62
- /** the signal was switched off by a 404/405/501 (diagnostic seam) */
63
- disabled(signal: OtlpSignal): boolean;
64
- }
65
-
66
- export function createOtlpExporter(opts: OtlpExporterOptions): OtlpExporter {
67
- const base = baseOf(opts.endpoint);
68
- const usable = validEndpoint(opts.endpoint);
69
- const headers = { ...(opts.headers ?? {}), "content-type": "application/json" };
70
- const fetchFn = opts.fetch ?? fetch;
71
- const timeoutMs = opts.timeoutMs ?? DEFAULT_EXPORT_TIMEOUT_MS;
72
- const pending = new Set<Promise<void>>();
73
- const off = new Set<OtlpSignal>();
74
- const url = (signal: OtlpSignal): string => `${base}/v1/${signal}`;
75
- const send = async (signal: OtlpSignal, body: string): Promise<void> => {
76
- if (!usable || off.has(signal)) return;
77
- const target = url(signal);
78
- const fail = (reason: string): void => opts.warn(`OTLP export to ${target} failed: ${reason}`);
79
- const ac = new AbortController();
80
- try {
81
- const res = await withTimeout(fetchFn(target, { method: "POST", headers, body, signal: ac.signal }), timeoutMs, ac);
82
- if (res === TIMED_OUT) { fail(`timed out after ${timeoutMs}ms`); return; }
83
- await res.arrayBuffer().catch(() => undefined); // release the connection; a success body is `{}`
84
- if (res.ok) return;
85
- if (NOT_SERVED.has(res.status)) { // one note, then silence for this signal (a second in-flight POST that raced the first says nothing more)
86
- if (!off.has(signal)) { off.add(signal); opts.warn(`OTLP collector ${base} does not accept ${signal} (HTTP ${res.status}) — ${signal} export disabled`); }
87
- return;
88
- }
89
- fail(`HTTP ${res.status}`);
90
- } catch (e) { fail(errText(e)); }
91
- };
92
- return {
93
- base, usable, url,
94
- post(signal, body) { const p = send(signal, body); pending.add(p); void p.then(() => { pending.delete(p); }); },
95
- async flush() { while (pending.size > 0) await Promise.all([...pending]); },
96
- disabled: (signal) => off.has(signal),
97
- };
98
- }
99
-
100
- // ---------- helpers (moved from otel.ts) ----------
101
-
102
- /** random hex id of `bytes` bytes; the all-zero id is invalid in OTel */
103
- export function hex(bytes: number): string {
104
- let h: string;
105
- do h = randomBytes(bytes).toString("hex"); while (/^0+$/.test(h));
106
- return h;
107
- }
108
- export const TIMED_OUT: unique symbol = Symbol("rovecode.otel.timeout");
109
- /** resolves TIMED_OUT after ms on a REF'D timer and aborts the request's controller (hooks.ts idiom);
110
- * the race is independent of the fetch honoring the signal, so a stuck transport still settles */
111
- export function withTimeout<T>(p: Promise<T>, ms: number, ac: AbortController): Promise<T | typeof TIMED_OUT> {
112
- return new Promise((resolve, reject) => {
113
- const timer = setTimeout(() => { ac.abort(); resolve(TIMED_OUT); }, ms);
114
- (timer as unknown as { ref?: () => void }).ref?.();
115
- p.then((v) => { clearTimeout(timer); resolve(v); }, (e: unknown) => { clearTimeout(timer); reject(e); });
116
- });
117
- }
118
- export function errText(e: unknown): string {
119
- if (e instanceof Error) return e.message;
120
- if (typeof e === "object" && e !== null && typeof (e as { message?: unknown }).message === "string") return (e as { message: string }).message;
121
- return String(e);
122
- }