rovecode 0.3.3 → 0.4.0-beta.3

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 (495) hide show
  1. package/CHANGELOG.md +527 -0
  2. package/README.md +46 -48
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-j6gn14w3.js +2 -0
  5. package/dist/cli/ask-user-cwstt8fz.js +2 -0
  6. package/dist/cli/auth-login-9bbp9915.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-16zqdms5.js +9 -0
  9. package/dist/cli/catalog-1xchffa4.js +2 -0
  10. package/dist/cli/cli-1n1zb64f.js +2 -0
  11. package/dist/cli/client-2t9gjkck.js +2 -0
  12. package/dist/cli/commands-exafvm2b.js +2 -0
  13. package/dist/cli/connect-6zde0kn3.js +2 -0
  14. package/dist/cli/context-cmd-5t43wgqt.js +2 -0
  15. package/dist/cli/context-report-kt01pw8y.js +2 -0
  16. package/dist/cli/count-remote-ap7x3vh6.js +2 -0
  17. package/dist/cli/design-ne5zszyh.js +2 -0
  18. package/dist/cli/dispatch-2r5myxye.js +2 -0
  19. package/dist/cli/doctor-ws4fh4tn.js +3 -0
  20. package/dist/cli/executor-bdrjn634.js +2 -0
  21. package/dist/cli/export-1mxb9g5p.js +2 -0
  22. package/dist/cli/files-g104xghh.js +2 -0
  23. package/dist/cli/gauntlet-07xrjpj7.js +2 -0
  24. package/dist/cli/gauntlet-runner-xvy64436.js +10 -0
  25. package/dist/cli/gauntlet-wave3-jm91yt5w.js +5 -0
  26. package/dist/cli/gauntlet-wave4-r13py7p1.js +14 -0
  27. package/dist/cli/hashline-znvrat11.js +2 -0
  28. package/dist/cli/http-xafw6fsh.js +143 -0
  29. package/dist/cli/index-1sgjm25y.js +2 -0
  30. package/dist/cli/init-g2m0tn4m.js +51 -0
  31. package/dist/cli/install-avaqjjqq.js +2 -0
  32. package/dist/cli/loop-mmpfft01.js +2 -0
  33. package/dist/cli/main-0904f6ps.js +5 -0
  34. package/dist/cli/main-0ab9fc26.js +9 -0
  35. package/dist/cli/main-0jys2ccn.js +3 -0
  36. package/dist/cli/main-0mtcdbs7.js +3 -0
  37. package/dist/cli/main-0z1w2zsg.js +3 -0
  38. package/dist/cli/main-1dchs7xv.js +18 -0
  39. package/dist/cli/main-1ereejm1.js +3 -0
  40. package/dist/cli/main-1k1kw6b5.js +3 -0
  41. package/dist/cli/main-27y4sm2k.js +38 -0
  42. package/dist/cli/main-2wwjex5j.js +58 -0
  43. package/dist/cli/main-2yeveeve.js +6 -0
  44. package/dist/cli/main-2yfck9b5.js +3 -0
  45. package/dist/cli/main-2zmzgkwh.js +3 -0
  46. package/dist/cli/main-351pz3z7.js +7 -0
  47. package/dist/cli/main-3gjqfh7a.js +6 -0
  48. package/dist/cli/main-3nf3kgve.js +3 -0
  49. package/dist/cli/main-3pjrb2hd.js +3 -0
  50. package/dist/cli/main-3rxcvgna.js +19 -0
  51. package/dist/cli/main-4b3jgy66.js +19 -0
  52. package/dist/cli/main-4wndhjdc.js +7 -0
  53. package/dist/cli/main-4xcmvxnk.js +3 -0
  54. package/dist/cli/main-5tbz0wbz.js +4 -0
  55. package/dist/cli/main-5ywnwthm.js +3 -0
  56. package/dist/cli/main-6b62vkz0.js +14 -0
  57. package/dist/cli/main-6dnk69vp.js +3 -0
  58. package/dist/cli/main-6genrmhs.js +136 -0
  59. package/dist/cli/main-73g7eff4.js +15 -0
  60. package/dist/cli/main-7c5thhjd.js +5 -0
  61. package/dist/cli/main-7rn6bqje.js +3 -0
  62. package/dist/cli/main-80haw7qk.js +4 -0
  63. package/dist/cli/main-875s60s2.js +4 -0
  64. package/dist/cli/main-8kjxbpw4.js +8 -0
  65. package/dist/cli/main-90ds1z4e.js +10 -0
  66. package/dist/cli/main-9etavkew.js +3 -0
  67. package/dist/cli/main-a9njrkk1.js +3 -0
  68. package/dist/cli/main-aecrjq2d.js +12 -0
  69. package/dist/cli/main-ck9asesq.js +9 -0
  70. package/dist/cli/main-cta9racd.js +4 -0
  71. package/dist/cli/main-ddv7j2ag.js +3 -0
  72. package/dist/cli/main-dfreez27.js +10 -0
  73. package/dist/cli/main-f7rw7des.js +3 -0
  74. package/dist/cli/main-ggcn7rd7.js +5 -0
  75. package/dist/cli/main-gzkmycnv.js +3 -0
  76. package/dist/cli/main-hq51jg8v.js +18 -0
  77. package/dist/cli/main-jft389w9.js +8 -0
  78. package/dist/cli/main-k1eqkg83.js +3 -0
  79. package/dist/cli/main-k2y8a2aw.js +9 -0
  80. package/dist/cli/main-kcpbykxz.js +4 -0
  81. package/dist/cli/main-kd488vje.js +22 -0
  82. package/dist/cli/main-kh32yvgk.js +5 -0
  83. package/dist/cli/main-kqxnqjnv.js +25 -0
  84. package/dist/cli/main-kyn0xnsg.js +3 -0
  85. package/dist/cli/main-m1kk6fp5.js +21 -0
  86. package/dist/cli/main-mv40pcr2.js +4 -0
  87. package/dist/cli/main-n0t3973w.js +3 -0
  88. package/dist/cli/main-nqveez48.js +4 -0
  89. package/dist/cli/main-pknhvrmj.js +3 -0
  90. package/dist/cli/main-pn1w7a7j.js +3 -0
  91. package/dist/cli/main-prxxs70n.js +4 -0
  92. package/dist/cli/main-q3vsesf9.js +3 -0
  93. package/dist/cli/main-qsevpgsv.js +3 -0
  94. package/dist/cli/main-rdgdw24b.js +25 -0
  95. package/dist/cli/main-rfth4tbm.js +16 -0
  96. package/dist/cli/main-rg0wn0xf.js +5 -0
  97. package/dist/cli/main-sdmxhtv8.js +4 -0
  98. package/dist/cli/main-skbp13js.js +18 -0
  99. package/dist/cli/main-t4xnd213.js +7 -0
  100. package/dist/cli/main-vqak588n.js +4 -0
  101. package/dist/cli/main-w2n1303f.js +9 -0
  102. package/dist/cli/main-wbrdspr2.js +5 -0
  103. package/dist/cli/main-wsrg79c1.js +7 -0
  104. package/dist/cli/main-x4r0fne4.js +5 -0
  105. package/dist/cli/main-xea2f3tn.js +6 -0
  106. package/dist/cli/main-xg704a3c.js +3 -0
  107. package/dist/cli/main-xvnrabfp.js +16 -0
  108. package/dist/cli/main-xy53xf0r.js +4 -0
  109. package/dist/cli/main-y1fqy60y.js +3 -0
  110. package/dist/cli/main-yn8cd281.js +34 -0
  111. package/dist/cli/main-yr0ksc0h.js +4 -0
  112. package/dist/cli/main-z2ex2vyf.js +4 -0
  113. package/dist/cli/main-z3aayzvq.js +3 -0
  114. package/dist/cli/main-zaqh35jg.js +3 -0
  115. package/dist/cli/main-zc2e8e46.js +4 -0
  116. package/dist/cli/main-zzrfw6cf.js +13 -0
  117. package/dist/cli/main.js +280 -0
  118. package/dist/cli/market-cmd-e14kmx9n.js +5 -0
  119. package/dist/cli/mcp-login-wq7ktdek.js +2 -0
  120. package/dist/cli/mcp-market-cmd-9mg3jecy.js +2 -0
  121. package/dist/cli/notify-b7qc0cjb.js +2 -0
  122. package/dist/cli/oauth-z8whcgfx.js +2 -0
  123. package/dist/cli/output-b3ewj3ps.js +16 -0
  124. package/dist/cli/profiles-6mr5he5e.js +2 -0
  125. package/dist/cli/provider-config-g7j42q8x.js +2 -0
  126. package/dist/cli/provider-jr1y8vvm.js +2 -0
  127. package/dist/cli/registry-s8yk86g0.js +2 -0
  128. package/dist/cli/registry-t6p8d4mn.js +2 -0
  129. package/dist/cli/repl-bajwe1mh.js +11 -0
  130. package/dist/cli/resume-rwn9nz7y.js +2 -0
  131. package/dist/cli/run-flags-nah7ndpt.js +2 -0
  132. package/dist/cli/runtime-n7gafzhb.js +2 -0
  133. package/dist/cli/sandbox-config-emdy18x4.js +2 -0
  134. package/dist/cli/server-b0nvs2bn.js +5 -0
  135. package/dist/cli/session-arg-y75wd4kj.js +2 -0
  136. package/dist/cli/session-j62evmjq.js +2 -0
  137. package/dist/cli/sessions-cmd-tsnwz0ns.js +7 -0
  138. package/dist/cli/settings-df10wfez.js +2 -0
  139. package/dist/cli/setup-jzvv72fg.js +2 -0
  140. package/dist/cli/sextant-smoke-37m81ke6.js +5 -0
  141. package/dist/cli/skills-cmd-gjxnxnhx.js +2 -0
  142. package/dist/cli/smoke-p7748apt.js +8 -0
  143. package/dist/cli/start-chat-s4st3mm0.js +12 -0
  144. package/dist/cli/stream-gmeyewds.js +2 -0
  145. package/dist/cli/task-gh0kkp3n.js +2 -0
  146. package/dist/cli/tasks-z1kfpe8e.js +2 -0
  147. package/dist/cli/thinking-0eqkrz6t.js +2 -0
  148. package/dist/cli/todo-5brcrt9m.js +2 -0
  149. package/dist/cli/tools-7pzm0vj9.js +2 -0
  150. package/dist/cli/tools-s635p6s8.js +2 -0
  151. package/dist/cli/trust-cmd-cjav8zgm.js +2 -0
  152. package/dist/cli/update-check-pt31bm2f.js +2 -0
  153. package/dist/cli/update-cmd-tk131s9t.js +2 -0
  154. package/dist/cli/voice-56nabd8d.js +2 -0
  155. package/dist/cli/webfetch-xd8q596m.js +2 -0
  156. package/dist/cli/websearch-5hkf98k1.js +2 -0
  157. package/dist/cli/workflow-cmd-cy3cvzjp.js +4 -0
  158. package/dist/cli/workspace-q10g5z3e.js +2 -0
  159. package/dist/lib/index.js +62 -0
  160. package/dist/lib/models-index.json +1 -0
  161. package/dist/lib/plugins.js +55 -0
  162. package/dist/lib/providers.js +17 -0
  163. package/dist/lib/public-api.js +20 -0
  164. package/dist/lib/sdk.js +360 -0
  165. package/package.json +84 -72
  166. package/bin/rovecode.ts +0 -21
  167. package/src/acp/server.ts +0 -374
  168. package/src/cli/auth-login.ts +0 -122
  169. package/src/cli/connect.ts +0 -244
  170. package/src/cli/context-cmd.ts +0 -199
  171. package/src/cli/dispatch.ts +0 -82
  172. package/src/cli/doctor.ts +0 -362
  173. package/src/cli/export.ts +0 -276
  174. package/src/cli/help.ts +0 -293
  175. package/src/cli/is-tui-invocation.ts +0 -8
  176. package/src/cli/main.ts +0 -583
  177. package/src/cli/market-cmd.ts +0 -658
  178. package/src/cli/mcp-login.ts +0 -141
  179. package/src/cli/mcp-market-cmd.ts +0 -302
  180. package/src/cli/output.ts +0 -382
  181. package/src/cli/repl.ts +0 -250
  182. package/src/cli/repomap-root.ts +0 -14
  183. package/src/cli/resume.ts +0 -57
  184. package/src/cli/run-flags.ts +0 -43
  185. package/src/cli/run-limits.ts +0 -78
  186. package/src/cli/runtime.ts +0 -931
  187. package/src/cli/session-arg.ts +0 -30
  188. package/src/cli/sessions-cmd.ts +0 -145
  189. package/src/cli/setup.ts +0 -153
  190. package/src/cli/skills-cmd.ts +0 -194
  191. package/src/cli/start-chat.ts +0 -65
  192. package/src/cli/trust-cmd.ts +0 -52
  193. package/src/coding/bash.ts +0 -148
  194. package/src/coding/checkpoints.ts +0 -327
  195. package/src/coding/diff.ts +0 -138
  196. package/src/coding/files.ts +0 -341
  197. package/src/coding/hashline.ts +0 -274
  198. package/src/coding/lsp-gate.ts +0 -254
  199. package/src/coding/lsp-servers.ts +0 -147
  200. package/src/coding/lsp.ts +0 -283
  201. package/src/coding/repomap-cache.ts +0 -99
  202. package/src/coding/repomap-files.ts +0 -192
  203. package/src/coding/repomap.ts +0 -481
  204. package/src/core/agents.ts +0 -255
  205. package/src/core/compaction.ts +0 -259
  206. package/src/core/config.ts +0 -289
  207. package/src/core/context-report.ts +0 -228
  208. package/src/core/context.ts +0 -60
  209. package/src/core/count-remote.ts +0 -107
  210. package/src/core/execpolicy-rules.ts +0 -196
  211. package/src/core/execpolicy.ts +0 -385
  212. package/src/core/executor.ts +0 -454
  213. package/src/core/guardrails.ts +0 -400
  214. package/src/core/hooks.ts +0 -411
  215. package/src/core/images.ts +0 -230
  216. package/src/core/intro.ts +0 -266
  217. package/src/core/loop.ts +0 -567
  218. package/src/core/modes.ts +0 -372
  219. package/src/core/orchestrator.ts +0 -245
  220. package/src/core/proc-group.ts +0 -48
  221. package/src/core/project-trust.ts +0 -98
  222. package/src/core/reflection.ts +0 -165
  223. package/src/core/sandbox-config.ts +0 -186
  224. package/src/core/session-id.ts +0 -24
  225. package/src/core/session-images.ts +0 -73
  226. package/src/core/session-ops.ts +0 -183
  227. package/src/core/session-text.ts +0 -29
  228. package/src/core/session.ts +0 -469
  229. package/src/core/settings.ts +0 -170
  230. package/src/core/tasks.ts +0 -646
  231. package/src/core/token-scale.ts +0 -108
  232. package/src/core/tools.ts +0 -309
  233. package/src/core/trust.ts +0 -104
  234. package/src/core/types.ts +0 -330
  235. package/src/core/update-check.ts +0 -171
  236. package/src/core/usage.ts +0 -204
  237. package/src/core/validate.ts +0 -121
  238. package/src/core/verify-gate.ts +0 -159
  239. package/src/core/verify.ts +0 -236
  240. package/src/core/voice.ts +0 -158
  241. package/src/core/win-job.ts +0 -183
  242. package/src/core/workspace.ts +0 -184
  243. package/src/design/audit.ts +0 -797
  244. package/src/design/direction.ts +0 -190
  245. package/src/design/rules.ts +0 -157
  246. package/src/eval/bench.ts +0 -150
  247. package/src/eval/gauntlet-runner.ts +0 -215
  248. package/src/eval/gauntlet-support.ts +0 -84
  249. package/src/eval/gauntlet-wave3.ts +0 -269
  250. package/src/eval/gauntlet-wave4.ts +0 -217
  251. package/src/eval/gauntlet.ts +0 -253
  252. package/src/index.ts +0 -17
  253. package/src/lanes/agy.ts +0 -95
  254. package/src/lanes/approval.ts +0 -24
  255. package/src/lanes/claude.ts +0 -129
  256. package/src/lanes/codex.ts +0 -127
  257. package/src/lanes/events.ts +0 -130
  258. package/src/lanes/job.ts +0 -142
  259. package/src/lanes/opencode.ts +0 -122
  260. package/src/lanes/process.ts +0 -184
  261. package/src/lanes/progress.ts +0 -183
  262. package/src/lanes/registry.ts +0 -178
  263. package/src/lanes/runner.ts +0 -124
  264. package/src/lanes/types.ts +0 -112
  265. package/src/market/catalogs/mcp-docs.json +0 -111
  266. package/src/market/catalogs/plugins.json +0 -111
  267. package/src/market/catalogs/skills.json +0 -478
  268. package/src/market/clone.ts +0 -72
  269. package/src/market/context-cost.ts +0 -121
  270. package/src/market/digest.ts +0 -106
  271. package/src/market/index.ts +0 -22
  272. package/src/market/install.ts +0 -578
  273. package/src/market/manifest.ts +0 -187
  274. package/src/market/prereq.ts +0 -145
  275. package/src/market/registry.ts +0 -363
  276. package/src/market/resolve.ts +0 -111
  277. package/src/market/types.ts +0 -236
  278. package/src/market/validate.ts +0 -227
  279. package/src/mcp/client.ts +0 -449
  280. package/src/mcp/config.ts +0 -252
  281. package/src/mcp/local-package.ts +0 -211
  282. package/src/mcp/market-catalog.ts +0 -84
  283. package/src/mcp/market-install.ts +0 -289
  284. package/src/mcp/market.ts +0 -362
  285. package/src/mcp/oauth.ts +0 -251
  286. package/src/mcp/prompts-resources.ts +0 -249
  287. package/src/mcp/shared.ts +0 -149
  288. package/src/mcp/status.ts +0 -67
  289. package/src/mcp/tools.ts +0 -275
  290. package/src/mcp/transport.ts +0 -122
  291. package/src/mcp/trust.ts +0 -25
  292. package/src/memory/blocks.ts +0 -278
  293. package/src/memory/recall.ts +0 -355
  294. package/src/memory/scope.ts +0 -182
  295. package/src/memory/store.ts +0 -105
  296. package/src/memory/tools.ts +0 -99
  297. package/src/plugins/cli.ts +0 -119
  298. package/src/plugins/discover.ts +0 -108
  299. package/src/plugins/index.ts +0 -50
  300. package/src/plugins/install.ts +0 -184
  301. package/src/plugins/load.ts +0 -124
  302. package/src/plugins/manifest.ts +0 -92
  303. package/src/plugins/state.ts +0 -83
  304. package/src/providers/auth.ts +0 -408
  305. package/src/providers/cache.ts +0 -223
  306. package/src/providers/catalog-local.ts +0 -160
  307. package/src/providers/catalog.ts +0 -421
  308. package/src/providers/middleware-context.ts +0 -86
  309. package/src/providers/middleware.ts +0 -373
  310. package/src/providers/model-list.ts +0 -23
  311. package/src/providers/oauth/common.ts +0 -105
  312. package/src/providers/oauth/device-code.ts +0 -107
  313. package/src/providers/oauth/github-copilot.ts +0 -146
  314. package/src/providers/oauth/loopback.ts +0 -158
  315. package/src/providers/oauth/openai.ts +0 -163
  316. package/src/providers/oauth/openrouter.ts +0 -89
  317. package/src/providers/oauth/pkce.ts +0 -45
  318. package/src/providers/oauth/registry.ts +0 -39
  319. package/src/providers/oauth/seam.ts +0 -89
  320. package/src/providers/profile-glm53.ts +0 -111
  321. package/src/providers/profile-sonnet5-persona.ts +0 -65
  322. package/src/providers/profile-sonnet5-voice.ts +0 -23
  323. package/src/providers/profiles.ts +0 -156
  324. package/src/providers/provider-config.ts +0 -311
  325. package/src/providers/registry.ts +0 -333
  326. package/src/providers/responses.ts +0 -209
  327. package/src/providers/retry.ts +0 -234
  328. package/src/providers/router.ts +0 -294
  329. package/src/providers/sse.ts +0 -26
  330. package/src/providers/stream-errors.ts +0 -117
  331. package/src/providers/stream.ts +0 -566
  332. package/src/providers/thinking.ts +0 -189
  333. package/src/providers/wire-messages.ts +0 -129
  334. package/src/providers/wire-responses.ts +0 -79
  335. package/src/providers/wire-select.ts +0 -53
  336. package/src/server/http.ts +0 -291
  337. package/src/server/openapi.ts +0 -246
  338. package/src/sextant/card-hits.ts +0 -102
  339. package/src/sextant/card-keys.ts +0 -55
  340. package/src/sextant/context-source.ts +0 -157
  341. package/src/sextant/crew-cards.ts +0 -350
  342. package/src/sextant/draw-agents.ts +0 -273
  343. package/src/sextant/draw-code.ts +0 -388
  344. package/src/sextant/draw-context.ts +0 -222
  345. package/src/sextant/draw-frame.ts +0 -164
  346. package/src/sextant/draw-market.ts +0 -573
  347. package/src/sextant/draw-messages.ts +0 -386
  348. package/src/sextant/draw-pet.ts +0 -230
  349. package/src/sextant/draw-plan.ts +0 -187
  350. package/src/sextant/draw-tabs.ts +0 -85
  351. package/src/sextant/draw-util.ts +0 -65
  352. package/src/sextant/draw-wizard.ts +0 -378
  353. package/src/sextant/engine.ts +0 -230
  354. package/src/sextant/frame-hits.ts +0 -25
  355. package/src/sextant/frame.ts +0 -101
  356. package/src/sextant/git-status.ts +0 -197
  357. package/src/sextant/grid.ts +0 -59
  358. package/src/sextant/input.ts +0 -119
  359. package/src/sextant/keys.ts +0 -521
  360. package/src/sextant/layout.ts +0 -86
  361. package/src/sextant/local-commands.ts +0 -169
  362. package/src/sextant/market-source.ts +0 -287
  363. package/src/sextant/mentions.ts +0 -200
  364. package/src/sextant/message-hits.ts +0 -26
  365. package/src/sextant/model.ts +0 -387
  366. package/src/sextant/overlays.ts +0 -456
  367. package/src/sextant/panel-hits.ts +0 -38
  368. package/src/sextant/pet.ts +0 -399
  369. package/src/sextant/screen.ts +0 -324
  370. package/src/sextant/scroll-hits.ts +0 -66
  371. package/src/sextant/scrollbar.ts +0 -82
  372. package/src/sextant/sextant-bridge.ts +0 -174
  373. package/src/sextant/sextant-cards.ts +0 -142
  374. package/src/sextant/sextant-diff-base.ts +0 -63
  375. package/src/sextant/sextant-files.ts +0 -154
  376. package/src/sextant/sextant-frame-loop.ts +0 -335
  377. package/src/sextant/sextant-renderer.ts +0 -574
  378. package/src/sextant/sextant-repo.ts +0 -140
  379. package/src/sextant/theme.ts +0 -66
  380. package/src/sextant/tool-rows.ts +0 -189
  381. package/src/sextant/types.ts +0 -493
  382. package/src/skills/index.ts +0 -387
  383. package/src/skills/pack.ts +0 -220
  384. package/src/skills/spec.ts +0 -162
  385. package/src/skills/tools.ts +0 -69
  386. package/src/skills/versioned.ts +0 -227
  387. package/src/telemetry/otel-export.ts +0 -122
  388. package/src/telemetry/otel-lanes.ts +0 -89
  389. package/src/telemetry/otel-logs.ts +0 -131
  390. package/src/telemetry/otel-metrics.ts +0 -136
  391. package/src/telemetry/otel.ts +0 -397
  392. package/src/telemetry/otlp.ts +0 -76
  393. package/src/tools/ask-user.ts +0 -156
  394. package/src/tools/bash-bg.ts +0 -94
  395. package/src/tools/bash-jobs.ts +0 -237
  396. package/src/tools/design.ts +0 -151
  397. package/src/tools/evalcell.ts +0 -338
  398. package/src/tools/html-text.ts +0 -139
  399. package/src/tools/provider.ts +0 -149
  400. package/src/tools/task.ts +0 -250
  401. package/src/tools/todo.ts +0 -320
  402. package/src/tools/webfetch.ts +0 -332
  403. package/src/tools/websearch.ts +0 -359
  404. package/src/tui/agents-cmd.ts +0 -41
  405. package/src/tui/app.ts +0 -749
  406. package/src/tui/attach.ts +0 -127
  407. package/src/tui/boot-notes.ts +0 -41
  408. package/src/tui/builtin-prompts.ts +0 -59
  409. package/src/tui/checkpoints-cmd.ts +0 -70
  410. package/src/tui/clipboard-image.ts +0 -81
  411. package/src/tui/clipboard.ts +0 -78
  412. package/src/tui/commands.ts +0 -283
  413. package/src/tui/config-view.ts +0 -53
  414. package/src/tui/context-cmds.ts +0 -282
  415. package/src/tui/cost.ts +0 -108
  416. package/src/tui/crash-guard.ts +0 -173
  417. package/src/tui/focus-terminal.ts +0 -34
  418. package/src/tui/git-cmds.ts +0 -273
  419. package/src/tui/git-plain.ts +0 -58
  420. package/src/tui/info-cmd.ts +0 -150
  421. package/src/tui/input-plain.ts +0 -76
  422. package/src/tui/mcp-cmd.ts +0 -128
  423. package/src/tui/memory-note.ts +0 -77
  424. package/src/tui/modes-cmd.ts +0 -45
  425. package/src/tui/notify-seq.ts +0 -100
  426. package/src/tui/notify.ts +0 -318
  427. package/src/tui/overlays.ts +0 -97
  428. package/src/tui/pi-renderer.ts +0 -428
  429. package/src/tui/providers-cmd.ts +0 -377
  430. package/src/tui/reasoning-view.ts +0 -56
  431. package/src/tui/renderer.ts +0 -128
  432. package/src/tui/replay-marker.ts +0 -29
  433. package/src/tui/session-cmd.ts +0 -148
  434. package/src/tui/session-manage.ts +0 -95
  435. package/src/tui/sextant-attach.ts +0 -102
  436. package/src/tui/sextant-io.ts +0 -202
  437. package/src/tui/sextant-smoke.ts +0 -110
  438. package/src/tui/shell-cmd.ts +0 -158
  439. package/src/tui/smoke.ts +0 -72
  440. package/src/tui/staged-terminal.ts +0 -50
  441. package/src/tui/startup.ts +0 -12
  442. package/src/tui/theme.ts +0 -59
  443. package/src/tui/todo-label.ts +0 -7
  444. package/src/tui/trust-card.ts +0 -107
  445. package/src/tui/tui-commands.ts +0 -87
  446. package/tsconfig.json +0 -30
  447. package/vendor/pi-tui/LICENSE +0 -21
  448. package/vendor/pi-tui/PATCHES.md +0 -12
  449. package/vendor/pi-tui/PROVENANCE.md +0 -12
  450. package/vendor/pi-tui/README.upstream.md +0 -854
  451. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  452. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  453. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  454. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  455. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  456. package/vendor/pi-tui/src/components/box.ts +0 -138
  457. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  458. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  459. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  460. package/vendor/pi-tui/src/components/image.ts +0 -128
  461. package/vendor/pi-tui/src/components/input.ts +0 -448
  462. package/vendor/pi-tui/src/components/loader.ts +0 -93
  463. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  464. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  465. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  466. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  467. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  468. package/vendor/pi-tui/src/components/stack.ts +0 -155
  469. package/vendor/pi-tui/src/components/text.ts +0 -108
  470. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  471. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  472. package/vendor/pi-tui/src/editor-component.ts +0 -75
  473. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  474. package/vendor/pi-tui/src/index.ts +0 -149
  475. package/vendor/pi-tui/src/keybindings.ts +0 -321
  476. package/vendor/pi-tui/src/keys.ts +0 -1402
  477. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  478. package/vendor/pi-tui/src/latex.ts +0 -1381
  479. package/vendor/pi-tui/src/layout-node.ts +0 -52
  480. package/vendor/pi-tui/src/layout.ts +0 -411
  481. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  482. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  483. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  484. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  485. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  486. package/vendor/pi-tui/src/terminal.ts +0 -554
  487. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  488. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  489. package/vendor/pi-tui/src/tui.ts +0 -1264
  490. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  491. package/vendor/pi-tui/src/utils.ts +0 -1327
  492. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  493. package/vendor/pi-tui/test/test-themes.ts +0 -39
  494. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  495. /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
- }