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,182 +0,0 @@
1
- /** Memory scoping: WHERE a remembered fact lands, and the one-time move of the old per-session store.
2
- *
3
- * Until 2026-09-07 both blocks were built over ONE directory, `<cwd>/.rovecode/sessions/<id>/memory`, which got
4
- * two things wrong at once. Every fact died with the session id — a new session is a new uuid directory and
5
- * nothing carried anything forward — and the USER block, whose entire purpose is preferences that follow the
6
- * person from project to project, was written INSIDE the repository. A private preference in someone's checkout
7
- * is the wrong file in the wrong place; that is what this module fixes.
8
- *
9
- * MEMORY → `<cwd>/.rovecode/memory/MEMORY.md` — this project, shared by every session in it (two sessions in
10
- * the same checkout see the same facts; the ledger's optimistic concurrency serialises their edits)
11
- * USER → `<ROVECODE_HOME | ~/.rovecode>/memory/USER.md` — the person, across every project, never in a repo
12
- *
13
- * A note with NO scope goes to MEMORY: an unscoped project fact landing in the user file could carry something
14
- * private out of one project and into every other, while the reverse is only a nuisance. `#<text>` and
15
- * `/memory <text>` append to MEMORY; `/memory --user <text>` is the only composer path to USER; the memory_edit
16
- * tool has no default at all (`block` is a required enum).
17
- *
18
- * LEGACY (the pre-2026-09-07 layout): `<sessions>/<id>/memory/`. Processed ONCE per legacy store — when a block's
19
- * scoped target has never been used (neither its file nor its `.versions.jsonl` sidecar exists) the legacy file
20
- * and sidecar are copied forward, history intact, BEFORE the BlockStore takes its boot snapshot, so a resumed
21
- * session keeps its memory in the prompt. A target already in use is never overwritten; the block is reported as
22
- * skipped. NOTHING OF THE USER'S IS EVER DELETED: the legacy files stay exactly where they are, and a small
23
- * marker beside them records the outcome so the copy and its note happen once and are said once.
24
- *
25
- * TRUST: `<cwd>/.rovecode/memory/MEMORY.md` is repo data — committable, and it goes into the system prompt — so a
26
- * cloned repository could put text of its choosing in front of the model. The gate is core/trust.ts, the same
27
- * digest store as project mcp.json and hooks.ts, with one adaptation that matters: a plain digest gate over a file
28
- * that changes on every note would re-ask constantly and be switched off within a day, so the store SELF-TRUSTS
29
- * the file after a write it performed itself (the `mcp add --project` precedent — what we wrote, we vouch for).
30
- * Narrowly: only a write that actually landed through this store, never "the file looks close enough". A MEMORY.md
31
- * that arrived from a clone or a hand-edit is READ but WITHHELD from the prompt with one boot note, and writes to
32
- * it are REFUSED (memory/blocks.ts capCheck) — appending our own note to a stranger's file would launder its
33
- * content into the next prompt. `rovecode trust` accepts it, exactly as for the other project files. The USER
34
- * block lives in the user's own home and is never gated. */
35
-
36
- import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
37
- import { join } from "node:path";
38
- import { isTrustedFile, trustFile, untrustedFileNote } from "../core/trust.ts";
39
- import { rovecodeHome } from "../providers/auth.ts";
40
- import { LEDGER_SUFFIX } from "../skills/versioned.ts";
41
- import { BLOCK_FILES, BlockStore, cutAtCap, defaultCaps, type BlockCaps, type BlockDirs, type BlockName, type BlockStoreOptions } from "./blocks.ts";
42
-
43
- /** the memory sub-directory of a scope's state dir */
44
- export const MEMORY_DIR = "memory";
45
- /** written beside a legacy store's files once it has been processed (copied or skipped) — the "once" of the header */
46
- export const LEGACY_MARKER = ".copied-forward.json";
47
- /** an over-cap legacy block is copied cut to this share of the cap, so the migrated store can still take a note */
48
- export const LEGACY_CUT_RATIO = 0.9;
49
- const BLOCKS: readonly BlockName[] = ["memory", "user"];
50
-
51
- /** `<cwd>/.rovecode/memory` — the MEMORY block's home for this project */
52
- export function projectMemoryDir(cwd: string): string { return join(cwd, ".rovecode", MEMORY_DIR); }
53
- /** `<ROVECODE_HOME | ~/.rovecode>/memory` — the USER block's home, the same home providers.json and plugins.json use */
54
- export function userMemoryDir(home: string = rovecodeHome()): string { return join(home, MEMORY_DIR); }
55
- /** the pre-2026-09-07 per-session store: `<sessions>/<id>/memory` */
56
- export function legacySessionMemoryDir(sessionsDir: string, sessionId: string): string { return join(sessionsDir, sessionId, MEMORY_DIR); }
57
- /** the scoped directories a runtime's BlockStore is built over */
58
- export function scopedBlockDirs(cwd: string, home?: string): BlockDirs {
59
- return { memory: projectMemoryDir(cwd), user: userMemoryDir(home) };
60
- }
61
-
62
- /** true when `block` has never been written under `dir`: neither its file nor its ledger sidecar exists */
63
- export function blockUnused(dir: string, block: BlockName): boolean {
64
- const file = join(dir, BLOCK_FILES[block]);
65
- return !existsSync(file) && !existsSync(file + LEDGER_SUFFIX);
66
- }
67
-
68
- export interface MigrationResult {
69
- /** the legacy store that was read */
70
- from: string;
71
- /** blocks copied forward (file + sidecar when present) */
72
- copied: BlockName[];
73
- /** blocks the legacy store had but whose scoped target is already in use — left alone */
74
- skipped: BlockName[];
75
- /** copied blocks whose legacy file was over the block cap: the copy is cut at the cap (present only when non-empty) */
76
- cut?: BlockName[];
77
- }
78
-
79
- /** Copy a legacy `<session>/memory` store forward into the scoped dirs, block by block, only where the target is
80
- * unused (header rules), then leave the marker. The legacy files are never modified or removed. null when the
81
- * legacy dir holds no block file (nothing to migrate) or was processed before — the caller stays silent in both. */
82
- export function migrateLegacyMemory(legacyDir: string, dirs: BlockDirs, caps: BlockCaps = defaultCaps): MigrationResult | null {
83
- if (existsSync(join(legacyDir, LEGACY_MARKER))) return null;
84
- const result: MigrationResult = { from: legacyDir, copied: [], skipped: [] };
85
- const cut: BlockName[] = [];
86
- for (const block of BLOCKS) {
87
- const src = join(legacyDir, BLOCK_FILES[block]);
88
- if (!existsSync(src)) continue;
89
- if (!blockUnused(dirs[block], block)) { result.skipped.push(block); continue; }
90
- mkdirSync(dirs[block], { recursive: true });
91
- const text = readFileSync(src, "utf8");
92
- // over the cap: the copy is cut to LEGACY_CUT_RATIO of the cap (line boundary) so the store has room for notes
93
- // — the legacy file keeps the whole text, because nothing of the user's is deleted by a migration
94
- if (text.length > caps[block]) { writeFileSync(join(dirs[block], BLOCK_FILES[block]), cutAtCap(text, Math.floor(caps[block] * LEGACY_CUT_RATIO))); cut.push(block); }
95
- else copyFileSync(src, join(dirs[block], BLOCK_FILES[block]));
96
- if (existsSync(src + LEDGER_SUFFIX)) copyFileSync(src + LEDGER_SUFFIX, join(dirs[block], BLOCK_FILES[block] + LEDGER_SUFFIX));
97
- result.copied.push(block);
98
- }
99
- if (result.copied.length + result.skipped.length === 0) return null;
100
- if (cut.length > 0) result.cut = cut;
101
- writeFileSync(join(legacyDir, LEGACY_MARKER), JSON.stringify({ at: new Date().toISOString(), copied: result.copied, skipped: result.skipped, cut }) + "\n");
102
- return result;
103
- }
104
-
105
- /** The transcript note for a processed legacy store; null when there was nothing to process (or it was processed
106
- * before). A pure skip — every block's target already in use — says so once instead of dropping silently. */
107
- export function migrationNote(r: MigrationResult | null, dirs: BlockDirs): string | null {
108
- if (!r) return null;
109
- const files = (blocks: readonly BlockName[]): string => blocks.map((b) => BLOCK_FILES[b]).join(", ");
110
- if (r.copied.length === 0) {
111
- return `memory: legacy per-session store ${r.from} not copied — ${files(r.skipped)} already ${r.skipped.length === 1 ? "has" : "have"} content in the scoped store (/memory shows it; the legacy files are still there if you need the text — this note shows once)`;
112
- }
113
- const copied = r.copied.map((b) => `${BLOCK_FILES[b]} → ${dirs[b]}`).join(", ");
114
- const cut = r.cut && r.cut.length > 0 ? ` (${files(r.cut)} cut to fit the block cap — the legacy file keeps the whole text)` : "";
115
- const skipped = r.skipped.length > 0 ? `; ${files(r.skipped)} not copied — the scoped store already has content` : "";
116
- return `memory: copied ${copied} forward from the legacy per-session store ${r.from} (left in place; in the prompt from this run on)${cut}${skipped}`;
117
- }
118
-
119
- /** The copy-forward is OUR OWN write of the user's own file, so the file it produces is trusted at the moment it is
120
- * written — exactly like a note we append. Without this, migrating a legacy store would hand the person a MEMORY.md
121
- * that this machine then refused to put in the prompt, which is the opposite of what the migration is for. */
122
- function trustMigrated(r: MigrationResult | null, dirs: BlockDirs, home: string): void {
123
- if (r?.copied.includes("memory")) trustFile(home, join(dirs.memory, BLOCK_FILES.memory));
124
- }
125
-
126
- /** The store options for `cwd`: withhold a project MEMORY.md this machine has not approved, and keep the digest
127
- * current for the writes we perform ourselves (header — the narrow self-trust). A MEMORY.md that does not exist
128
- * yet is not withheld: the first note creates it, and creating it is our own write. */
129
- export function scopedStoreOptions(cwd: string, home: string = rovecodeHome()): BlockStoreOptions {
130
- const file = join(projectMemoryDir(cwd), BLOCK_FILES.memory);
131
- const withhold: BlockName[] = existsSync(file) && !isTrustedFile(home, file) ? ["memory"] : [];
132
- return {
133
- ...(withhold.length > 0 ? { withhold } : {}),
134
- // only the project block needs a digest: USER.md lives in the user's own home and is never gated
135
- onCommit: (block, path) => { if (block === "memory") trustFile(home, path); },
136
- };
137
- }
138
-
139
- /** The boot notes about the store itself: a withheld project block, a block over its cap. */
140
- export function storeNotes(blocks: BlockStore, cwd: string): string[] {
141
- const notes: string[] = [];
142
- void cwd;
143
- for (const b of BLOCKS) {
144
- if (blocks.isWithheld(b)) {
145
- notes.push(untrustedFileNote(blocks.path(b), "it is not in the prompt and cannot be written to (it came with this repository, and a repo file would be choosing what the model reads); /memory still shows it"));
146
- }
147
- const over = blocks.overCap(b);
148
- if (over) notes.push(`memory: ${blocks.path(b)} holds ${over.readCut ? "more than the read cap" : `${over.chars} chars`} — over the ${over.cap}-char cap; the prompt shows the first ${over.cap} and memory_edit is refused until the file is trimmed`);
149
- }
150
- return notes;
151
- }
152
-
153
- export interface ScopedMemory {
154
- blocks: BlockStore;
155
- /** the boot notes for the surface's transcript (the one-time copy-forward, a withheld or over-cap block), else null */
156
- note: string | null;
157
- }
158
-
159
- /** Build the runtime's BlockStore over the scoped dirs AFTER the one-time legacy copy for `sessionId`, so the boot
160
- * snapshot already carries a resumed session's migrated memory. */
161
- export function openScopedMemory(d: { cwd: string; sessionsDir: string; sessionId: string; home?: string }): ScopedMemory {
162
- const home = d.home ?? rovecodeHome();
163
- const dirs = scopedBlockDirs(d.cwd, home);
164
- const migrated = migrateLegacyMemory(legacySessionMemoryDir(d.sessionsDir, d.sessionId), dirs);
165
- trustMigrated(migrated, dirs, home); // before the store reads: what we just copied is ours, not the repo's
166
- const migration = migrationNote(migrated, dirs);
167
- const blocks = new BlockStore(dirs, defaultCaps, scopedStoreOptions(d.cwd, home));
168
- const notes = [...(migration ? [migration] : []), ...storeNotes(blocks, d.cwd)];
169
- return { blocks, note: notes.length > 0 ? notes.join("\n") : null };
170
- }
171
-
172
- /** TUI session switch (tui/app.ts): memory is project-scoped, so the store STAYS — unless the switched-to session
173
- * carries a legacy store that copies forward, in which case a FRESH BlockStore is handed back so the migrated text
174
- * is in the prompt now rather than only next run. A pure-skip note returns without a new store. */
175
- export function adoptLegacyMemory(cwd: string, sessionsDir: string, sessionId: string, home: string = rovecodeHome()): { blocks?: BlockStore; note: string | null } {
176
- const dirs = scopedBlockDirs(cwd, home);
177
- const r = migrateLegacyMemory(legacySessionMemoryDir(sessionsDir, sessionId), dirs);
178
- trustMigrated(r, dirs, home);
179
- const note = migrationNote(r, dirs);
180
- if (r && r.copied.length > 0) return { blocks: new BlockStore(dirs, defaultCaps, scopedStoreOptions(cwd, home)), note };
181
- return { note };
182
- }
@@ -1,105 +0,0 @@
1
- /** Bounded memory (ADR-008): typed records with budgets, decay, dedup, provenance.
2
- * Memory edits flow through the tool pipeline so policy applies. */
3
-
4
- import { createHash } from "node:crypto";
5
- import { mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
6
- import { join } from "node:path";
7
-
8
- export type MemoryKind = "task" | "episodic" | "semantic";
9
-
10
- export interface MemoryRecord {
11
- id: string;
12
- kind: MemoryKind;
13
- text: string;
14
- createdAt: number;
15
- lastAccessedAt: number;
16
- accessCount: number;
17
- /** where this came from: session id, tool call, user */
18
- provenance: string;
19
- tags: string[];
20
- }
21
-
22
- export interface MemoryLimits {
23
- maxRecords: number;
24
- maxCharsPerRecord: number;
25
- /** records unused for this many ms get decayed (deleted at the tail) */
26
- decayAfterMs: number;
27
- }
28
-
29
- export const defaultLimits: MemoryLimits = { maxRecords: 500, maxCharsPerRecord: 2_000, decayAfterMs: 1000 * 60 * 60 * 24 * 14 };
30
-
31
- function dedupKey(r: MemoryRecord): string {
32
- return createHash("sha256").update(r.kind + "|" + r.text.replace(/\s+/g, " ").trim().toLowerCase()).digest("hex").slice(0, 16);
33
- }
34
-
35
- export class MemoryStore {
36
- private records: MemoryRecord[] = [];
37
- private seen = new Set<string>();
38
-
39
- constructor(private readonly dir: string, private readonly limits: MemoryLimits = defaultLimits) {
40
- mkdirSync(dir, { recursive: true });
41
- const p = join(dir, "memory.json");
42
- if (existsSync(p)) {
43
- this.records = JSON.parse(readFileSync(p, "utf8")) as MemoryRecord[];
44
- this.seen = new Set(this.records.map(dedupKey));
45
- }
46
- }
47
-
48
- private persist(): void {
49
- writeFileSync(join(this.dir, "memory.json"), JSON.stringify(this.records, null, 2));
50
- }
51
-
52
- add(kind: MemoryKind, text: string, provenance: string, tags: string[] = []): { ok: boolean; reason?: string } {
53
- const trimmed = text.trim();
54
- if (trimmed.length === 0) return { ok: false, reason: "empty record" };
55
- if (trimmed.length > this.limits.maxCharsPerRecord) return { ok: false, reason: `record exceeds ${this.limits.maxCharsPerRecord} chars` };
56
- const rec: MemoryRecord = {
57
- id: createHash("sha256").update(kind + trimmed + Date.now()).digest("hex").slice(0, 12),
58
- kind, text: trimmed, createdAt: Date.now(), lastAccessedAt: Date.now(), accessCount: 0, provenance, tags,
59
- };
60
- const key = dedupKey(rec);
61
- if (this.seen.has(key)) return { ok: false, reason: "duplicate" };
62
- this.seen.add(key);
63
- this.records.push(rec);
64
- this.enforceLimits();
65
- this.persist();
66
- return { ok: true };
67
- }
68
-
69
- private enforceLimits(): void {
70
- const now = Date.now();
71
- // decay: drop stale records beyond the cap, oldest-accessed first
72
- this.records = this.records.filter((r) => now - r.lastAccessedAt < this.limits.decayAfterMs);
73
- if (this.records.length > this.limits.maxRecords) {
74
- this.records.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
75
- this.records = this.records.slice(this.records.length - this.limits.maxRecords);
76
- }
77
- }
78
-
79
- /** Recency × access × simple lexical relevance. */
80
- retrieve(query: string, limit = 5, kind?: MemoryKind): MemoryRecord[] {
81
- const terms = query.toLowerCase().split(/\W+/).filter((t) => t.length > 2);
82
- const now = Date.now();
83
- const scored = this.records
84
- .filter((r) => !kind || r.kind === kind)
85
- .map((r) => {
86
- const text = r.text.toLowerCase();
87
- const hits = terms.reduce((n, t) => n + (text.includes(t) ? 1 : 0), 0);
88
- const recency = 1 / (1 + (now - r.lastAccessedAt) / 3_600_000);
89
- const score = hits * 2 + Math.log1p(r.accessCount) + recency;
90
- return { r, score };
91
- })
92
- .filter((s) => s.score > 0.3)
93
- .sort((a, b) => b.score - a.score)
94
- .slice(0, limit);
95
- for (const s of scored) { s.r.accessCount++; s.r.lastAccessedAt = now; }
96
- this.persist();
97
- return scored.map((s) => s.r);
98
- }
99
-
100
- stats(): { count: number; byKind: Record<string, number> } {
101
- const byKind: Record<string, number> = {};
102
- for (const r of this.records) byKind[r.kind] = (byKind[r.kind] ?? 0) + 1;
103
- return { count: this.records.length, byKind };
104
- }
105
- }
@@ -1,99 +0,0 @@
1
- /** memory_edit tool (hermes memory_tool pattern): add/replace/remove on the bounded
2
- * markdown blocks. Failures (bad match, cap overflow, bad args) count toward a
3
- * per-turn cap of 3; at the cap the tool returns a terminal skip so the agent
4
- * stops burning turns on memory writes (hermes #42405). */
5
-
6
- import type { Tool, ToolContext, ToolOutput } from "../core/types.ts";
7
- import { BlockStore, type BlockName } from "./blocks.ts";
8
-
9
- const MAX_FAILURES_PER_TURN = 3;
10
- const CAP_MESSAGE = "save skipped: memory at capacity or repeatedly failing";
11
-
12
- let turnFailureCount = 0;
13
-
14
- export function resetTurnFailureCount(): void { turnFailureCount = 0; }
15
-
16
- export function turnFailures(): number { return turnFailureCount; }
17
-
18
- export interface MemoryEditArgs {
19
- op: "add" | "replace" | "remove";
20
- block: BlockName;
21
- text?: string;
22
- oldText?: string;
23
- newText?: string;
24
- }
25
-
26
- /** Build the tool bound to a store instance (the store is per-session). */
27
- export function memoryEditTool(store: BlockStore): Tool {
28
- return {
29
- schema: {
30
- name: "memory_edit",
31
- description:
32
- `Edit long-term memory blocks. Ops: add(text) appends a line; replace(oldText,newText) requires oldText to match exactly once; remove(oldText) same. ` +
33
- `block: "memory" (session/task facts, cap ${store.cap("memory")} chars) or "user" (stable user preferences, cap ${store.cap("user")} chars). ` +
34
- `Failures count toward a per-turn budget of ${MAX_FAILURES_PER_TURN}; after that saves are skipped until next turn.`,
35
- args: {
36
- type: "object",
37
- properties: {
38
- op: { type: "string", enum: ["add", "replace", "remove"] },
39
- block: { type: "string", enum: ["memory", "user"] },
40
- text: { type: "string", description: "text to add (op=add)" },
41
- oldText: { type: "string", description: "exact text to replace/remove; must match exactly once" },
42
- newText: { type: "string", description: "replacement text (op=replace; empty removes)" },
43
- },
44
- required: ["op", "block"],
45
- },
46
- },
47
- kind: "memory",
48
- sequential: true,
49
- async execute(args: unknown, _ctx: ToolContext): Promise<ToolOutput> {
50
- const a = args as MemoryEditArgs;
51
- if (turnFailureCount >= MAX_FAILURES_PER_TURN) {
52
- return { ok: false, output: CAP_MESSAGE };
53
- }
54
- const block = a.block === "user" ? "user" : a.block === "memory" ? "memory" : undefined;
55
- if (!block) return fail(`invalid block: ${String(a.block)}`);
56
-
57
- let res;
58
- switch (a.op) {
59
- case "add":
60
- if (typeof a.text !== "string" || a.text.trim().length === 0) {
61
- return fail("op=add requires non-empty text");
62
- }
63
- res = store.add(block, a.text);
64
- break;
65
- case "replace":
66
- if (typeof a.oldText !== "string" || a.oldText.length === 0) {
67
- return fail("op=replace requires oldText");
68
- }
69
- if (typeof a.newText !== "string") return fail("op=replace requires newText (use \"\" to delete)");
70
- res = store.replace(block, a.oldText, a.newText);
71
- break;
72
- case "remove":
73
- if (typeof a.oldText !== "string" || a.oldText.length === 0) {
74
- return fail("op=remove requires oldText");
75
- }
76
- res = store.remove(block, a.oldText);
77
- break;
78
- default:
79
- return fail(`invalid op: ${String(a.op)}`);
80
- }
81
-
82
- if (!res.ok) {
83
- return fail(res.reason ?? "edit failed", res.current, res.limit);
84
- }
85
- return {
86
- ok: true,
87
- output: `${a.op} ok: ${block} block now ${res.current}/${res.limit} chars`,
88
- data: { block, chars: res.current, limit: res.limit },
89
- };
90
- },
91
- };
92
- }
93
-
94
- /** Every failure path routes through here so the per-turn counter stays honest. */
95
- function fail(reason: string, current?: number, limit?: number): ToolOutput {
96
- turnFailureCount++;
97
- const detail = current !== undefined && limit !== undefined ? ` (current ${current}/${limit} chars)` : "";
98
- return { ok: false, output: `memory_edit failed: ${reason}${detail}; failures this turn: ${turnFailureCount}/${MAX_FAILURES_PER_TURN}` };
99
- }
@@ -1,119 +0,0 @@
1
- /** `rovecode plugin …` — list · add · remove · enable · disable · trust · untrust · show. A pure
2
- * command function over injected cwd/home/output so tests drive it without a process; cli/main.ts
3
- * wires `case "plugin"` to it with process.cwd() and rovecodeHome(). Nothing here imports plugin
4
- * code: `show` and `list` read manifests and folder digests only, so an untrusted project plugin can
5
- * be inspected before it is ever run — that inspection is what `trust` asks the human to do. */
6
-
7
- import { relative } from "node:path";
8
- import { rovecodeHome } from "../providers/auth.ts";
9
- import { discoverPlugins, type DiscoveredPlugin, type PluginScope } from "./discover.ts";
10
- import { addPlugin, removePlugin, scopeRoot, setPluginEnabled, trustPlugin, untrustPlugin, type Spawn } from "./install.ts";
11
- import { contributions } from "./manifest.ts";
12
- import { pluginFiles, statePath } from "./state.ts";
13
-
14
- export interface PluginCliDeps {
15
- cwd?: string;
16
- home?: string;
17
- out?: (line: string) => void;
18
- err?: (line: string) => void;
19
- spawn?: Spawn;
20
- }
21
-
22
- export const PLUGIN_USAGE = [
23
- "usage: rovecode plugin <command>",
24
- " list every plugin in ~/.rovecode/plugins and .rovecode/plugins, with its status",
25
- " add <folder|git-url> [--project] [--force]",
26
- " copy (or shallow-clone) a plugin into the user scope, or this repo's .rovecode/plugins",
27
- " remove <name> [--project] delete the installed copy",
28
- " enable <name> · disable <name>",
29
- " switch a plugin off/on in every scope (~/.rovecode/plugins.json)",
30
- " trust <name> approve a PROJECT plugin's current files on this machine — until they change",
31
- " untrust <name> withdraw that approval",
32
- " show <name> manifest, files and what it contributes, without running any of it",
33
- "status: active · disabled · untrusted (project plugin not yet approved here) · broken (see problems)",
34
- ].join("\n");
35
-
36
- const flag = (args: string[], name: string): boolean => args.includes(name);
37
- const words = (args: string[]): string[] => args.filter((a) => !a.startsWith("--"));
38
-
39
- export async function cmdPlugin(args: string[], deps: PluginCliDeps = {}): Promise<number> {
40
- const cwd = deps.cwd ?? process.cwd(), home = deps.home ?? rovecodeHome();
41
- const out = deps.out ?? ((l) => console.log(l)), err = deps.err ?? ((l) => console.error(l));
42
- const [sub = "list", ...rest] = args;
43
- const scope: PluginScope = flag(rest, "--project") ? "project" : "user";
44
- const name = words(rest)[0];
45
- switch (sub) {
46
- case "list": case "ls": {
47
- const { plugins, warnings } = discoverPlugins(cwd, { home });
48
- if (plugins.length === 0) { out(`no plugins. add one: rovecode plugin add <folder|git-url> (user: ${scopeRoot("user", cwd, home)}, project: ${relative(cwd, scopeRoot("project", cwd, home)) || "."})`); }
49
- for (const p of plugins) out(row(p));
50
- for (const w of warnings) err(`warning: ${w}`);
51
- return 0;
52
- }
53
- case "add": {
54
- const src = name;
55
- if (!src) { err(PLUGIN_USAGE); return 2; }
56
- const r = await addPlugin(src, { cwd, home, scope, force: flag(rest, "--force"), ...(deps.spawn ? { spawn: deps.spawn } : {}) });
57
- if (!r.ok) { err(`plugin add: ${r.error}`); return 1; }
58
- out(`added ${r.name}@${r.manifest.version} (${r.scope}) → ${r.dir}`);
59
- for (const c of contributions(r.manifest)) out(` ${c}`);
60
- if (r.scope === "project") out(` trusted on this machine (recorded in ${statePath(home)}); a change to its files will ask again`);
61
- out("restart rovecode to load it — plugins are read once per process, like hooks");
62
- return 0;
63
- }
64
- case "remove": case "rm": {
65
- if (!name) { err(PLUGIN_USAGE); return 2; }
66
- const r = removePlugin(name, { cwd, home, scope });
67
- if (!r.ok) { err(`plugin remove: ${r.error}`); return 1; }
68
- out(`removed ${name} (${scope}) from ${r.dir}`);
69
- return 0;
70
- }
71
- case "enable": case "disable": {
72
- if (!name) { err(PLUGIN_USAGE); return 2; }
73
- const known = discoverPlugins(cwd, { home }).plugins.some((p) => p.name === name);
74
- if (!known) err(`warning: no installed plugin named "${name}" — the setting is recorded anyway`);
75
- const path = setPluginEnabled(name, sub === "enable", home);
76
- out(`${name} ${sub}d (${path})`);
77
- return 0;
78
- }
79
- case "trust": {
80
- if (!name) { err(PLUGIN_USAGE); return 2; }
81
- const p = discoverPlugins(cwd, { home }).plugins.find((x) => x.name === name && x.scope === "project");
82
- if (!p) { err(`plugin trust: no project plugin "${name}" under ${scopeRoot("project", cwd, home)}`); return 1; }
83
- if (p.manifest === null) { err(`plugin trust: "${name}" is broken — fix it first:`); for (const w of p.problems) err(` ${w}`); return 1; }
84
- const r = trustPlugin(name, { cwd, home });
85
- if (!r.ok) { err(`plugin trust: ${r.error}`); return 1; }
86
- out(`trusted ${name}@${p.manifest.version} — ${pluginFiles(p.dir).length} files, digest ${r.digest.slice(0, 12)}…`);
87
- for (const c of contributions(p.manifest)) out(` ${c}`);
88
- out("it loads on the next start; `rovecode plugin show " + name + "` lists every file it brings");
89
- return 0;
90
- }
91
- case "untrust": {
92
- if (!name) { err(PLUGIN_USAGE); return 2; }
93
- out(untrustPlugin(name, { cwd, home }) ? `untrusted ${name}` : `${name} was not trusted`);
94
- return 0;
95
- }
96
- case "show": case "info": {
97
- if (!name) { err(PLUGIN_USAGE); return 2; }
98
- const p = discoverPlugins(cwd, { home }).plugins.find((x) => x.name === name);
99
- if (!p) { err(`plugin show: no plugin "${name}"`); return 1; }
100
- out(row(p));
101
- out(` folder: ${p.dir}`);
102
- if (p.digest) out(` digest: ${p.digest}`);
103
- if (p.manifest) for (const c of contributions(p.manifest)) out(` ${c}`);
104
- for (const f of pluginFiles(p.dir)) out(` - ${f}`);
105
- for (const w of p.problems) out(` ! ${w}`);
106
- if (p.status === "untrusted") out(` run \`rovecode plugin trust ${p.name}\` after reading the files above`);
107
- return 0;
108
- }
109
- case "help": case "--help": case "-h": out(PLUGIN_USAGE); return 0;
110
- default: err(`unknown plugin command "${sub}"\n${PLUGIN_USAGE}`); return 2;
111
- }
112
- }
113
-
114
- /** one list row: `name@version scope status — description` */
115
- export function row(p: DiscoveredPlugin): string {
116
- const v = p.manifest ? `@${p.manifest.version}` : "";
117
- const desc = p.manifest?.description ? ` — ${p.manifest.description}` : "";
118
- return `${(p.name + v).padEnd(28)} ${p.scope.padEnd(8)} ${p.status.padEnd(9)}${desc}`.trimEnd();
119
- }
@@ -1,108 +0,0 @@
1
- /** Plugins: discovery. Two roots — `~/.rovecode/plugins/<name>/` (user) and `<cwd>/.rovecode/plugins/
2
- * <name>/` (project) — each subfolder with a `plugin.json` is a plugin. Sync and filesystem-only:
3
- * nothing is imported here, so `rovecode plugin list` can show a hostile plugin without running it.
4
- *
5
- * Status decides what load.ts may do with a plugin:
6
- * active — will be imported and wired
7
- * disabled — the human switched it off (plugins.json)
8
- * untrusted — a PROJECT plugin whose folder digest the human has not approved on this machine;
9
- * listed, never imported (state.ts explains why the record lives in the user home)
10
- * broken — no usable manifest; listed with its reasons so the author can fix it
11
- * Same name in both roots: the project copy shadows the user copy (the commands loader's rule) and
12
- * says so. ROVECODE_NO_PLUGINS=1 skips discovery the way ROVECODE_NO_HOOKS=1 skips hooks. */
13
-
14
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
15
- import { join, resolve } from "node:path";
16
- import { rovecodeHome } from "../providers/auth.ts";
17
- import { normalizeEntry, type McpServerConfig } from "../mcp/config.ts";
18
- import { MANIFEST_FILE, parseManifest, type PluginManifest } from "./manifest.ts";
19
- import { isDir, isTrusted, loadState, pluginDigest, type PluginState } from "./state.ts";
20
-
21
- export type PluginScope = "user" | "project";
22
- export type PluginStatus = "active" | "disabled" | "untrusted" | "broken";
23
-
24
- export interface DiscoveredPlugin {
25
- name: string;
26
- /** absolute plugin folder */
27
- dir: string;
28
- scope: PluginScope;
29
- manifest: PluginManifest | null;
30
- status: PluginStatus;
31
- /** folder digest (state.ts); null when the folder could not be walked */
32
- digest: string | null;
33
- /** why it is broken / what the manifest dropped — the author's fix list */
34
- problems: string[];
35
- /** the declarative halves, resolved here because they need no code run: the runtime wires them
36
- * synchronously at construction (the commands and skills loaders, the MCP config) and only the
37
- * entry module waits for load.ts. Filled for every plugin with a manifest; the runtime reads
38
- * them for ACTIVE ones only. */
39
- commandsDir: string | null;
40
- skillsDir: string | null;
41
- mcp: McpServerConfig[];
42
- }
43
-
44
- export interface DiscoverOptions { home?: string; state?: PluginState }
45
- export interface Discovered { plugins: DiscoveredPlugin[]; warnings: string[] }
46
-
47
- /** the two roots, project last so it shadows; one root when cwd IS the home (commands.ts idiom) */
48
- export function pluginRoots(cwd: string, home: string): [PluginScope, string][] {
49
- const user = join(home, "plugins"), project = join(cwd, ".rovecode", "plugins");
50
- return resolve(user) === resolve(project) ? [["project", project]] : [["user", user], ["project", project]];
51
- }
52
-
53
- export function discoverPlugins(cwd: string, opts: DiscoverOptions = {}): Discovered {
54
- const out: Discovered = { plugins: [], warnings: [] };
55
- if (process.env.ROVECODE_NO_PLUGINS === "1") return out;
56
- const home = opts.home ?? rovecodeHome();
57
- const state = opts.state ?? loadState(home);
58
- const byName = new Map<string, DiscoveredPlugin>();
59
- for (const [scope, root] of pluginRoots(cwd, home)) {
60
- if (!isDir(root)) continue;
61
- let names: string[];
62
- try { names = readdirSync(root).sort(); } catch (e) { out.warnings.push(`${root}: cannot list — ${(e as Error).message}`); continue; }
63
- for (const folder of names) {
64
- const dir = join(root, folder);
65
- if (!isDir(dir) || !existsSync(join(dir, MANIFEST_FILE))) continue; // a stray file or a folder without a manifest is not a plugin
66
- const p = readPlugin(dir, folder, scope, state);
67
- const prev = byName.get(p.name);
68
- if (prev) out.warnings.push(`${dir}: plugin "${p.name}" shadows ${prev.dir} (project over user)`);
69
- byName.set(p.name, p);
70
- for (const w of p.problems) out.warnings.push(w);
71
- }
72
- }
73
- out.plugins = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
74
- return out;
75
- }
76
-
77
- /** one folder → its DiscoveredPlugin: manifest, status, digest, and the declarative halves resolved
78
- * (a named commands/skills dir must exist; MCP entries go through the same normalizer as mcp.json) */
79
- export function readPlugin(dir: string, folder: string, scope: PluginScope, state: PluginState): DiscoveredPlugin {
80
- const file = join(dir, MANIFEST_FILE);
81
- const problems: string[] = [];
82
- let manifest: PluginManifest | null = null;
83
- try { manifest = parseManifest(readFileSync(file, "utf8"), file, problems); }
84
- catch (e) { problems.push(`${file}: cannot read — ${(e as Error).message}`); }
85
- let digest: string | null = null;
86
- try { digest = pluginDigest(dir); } catch { digest = null; }
87
- const name = manifest?.name ?? folder;
88
- if (manifest && manifest.name !== folder) problems.push(`${file}: name "${manifest.name}" differs from its folder "${folder}" — the manifest name is the plugin's identity`);
89
- let status: PluginStatus;
90
- if (manifest === null) status = "broken";
91
- else if (state.disabled.includes(name)) status = "disabled";
92
- else if (scope === "project" && (digest === null || !isTrusted(state, dir, digest))) status = "untrusted";
93
- else status = "active";
94
- const tag = `plugin ${name}`;
95
- const commandsDir = manifest ? dirOrProblem(dir, manifest.commands, `${tag}: commands`, problems) : null;
96
- const skillsDir = manifest ? dirOrProblem(dir, manifest.skills, `${tag}: skills`, problems) : null;
97
- const mcp: McpServerConfig[] = [];
98
- if (manifest?.mcp) for (const [n, raw] of Object.entries(manifest.mcp)) { const c = normalizeEntry(n, raw, file, problems); if (c) mcp.push(c); }
99
- return { name, dir, scope, manifest, status, digest, problems, commandsDir, skillsDir, mcp };
100
- }
101
-
102
- function dirOrProblem(base: string, rel: string | undefined, what: string, problems: string[]): string | null {
103
- if (!rel) return null;
104
- const p = join(base, rel);
105
- try { if (statSync(p).isDirectory()) return p; } catch { /* below */ }
106
- problems.push(`${what} directory "${rel}" does not exist — ignored`);
107
- return null;
108
- }