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
package/src/cli/output.ts DELETED
@@ -1,382 +0,0 @@
1
- /** PORT #35 — machine-readable output for `rovecode run`: --output text | json | ndjson.
2
- *
3
- * Ported shape decisions (pi @ 853a80d, packages/coding-agent/src):
4
- * - modes/print-mode.ts:108-111 — `--mode json` writes ONE JSON line per session event
5
- * (`JSON.stringify(toJsonEvent(event)) + "\n"` through writeRawStdout); the RPC protocol
6
- * frames its output identically (modes/rpc/rpc-mode.ts:60-62, :355-356). rovecode's ndjson mode
7
- * emits every RunEvent VERBATIM that way (no reformatting: the loop's events are already
8
- * deltas — pi's toJsonEvent only strips its cumulative partials, json-event.ts:40-45) and
9
- * closes with one {type:"result", …} line.
10
- * - core/output-guard.ts:45-70 takeOverStdout — outside the interactive TUI process.stdout.write
11
- * is redirected to stderr and only the raw writer bound BEFORE the takeover reaches fd 1
12
- * (installed for every appMode !== "interactive", main.ts:633-636). guardStdout below is that
13
- * guard; Bun's console.log does not route through process.stdout.write, so console.log/info/
14
- * debug are redirected too. stdout purity is structural, not a discipline. cmdRun installs it
15
- * right after parseOutputMode — BEFORE bootRuntime, whose session_open hooks may print — and
16
- * hands the sink the raw writer it bound first (fix-wave 4 MED-C); the sink's own install below
17
- * serves embedders that pass process.stdout itself.
18
- * - print-mode.ts:139-156 — text mode prints only the final assistant text; error/aborted →
19
- * message on stderr, exit 1 (:145-147); signals exit 128+n (:57-61: 143 SIGTERM, 129 SIGHUP).
20
- * rovecode keeps its text mode byte-identical to the pre-port cmdRun (progress lines + summary on
21
- * stdout) and maps an ABORTED run to 130 by the same 128+signal convention: the sink's signal
22
- * aborts on the first SIGINT and rides into LoopDeps.signal (port #21), so Ctrl-C ends the run
23
- * "stopped" with a well-formed result instead of a hard kill (Windows: exit 0xC000013A, no output).
24
- *
25
- * Result schema (json: the ONLY stdout line; ndjson: the LAST line, with type:"result"):
26
- * status "done" | "stopped" | "error" | "budget" — run_end.status ("error" when the loop
27
- * ended without a run_end)
28
- * summary run_end.summary: the final assistant text, or the error text
29
- * sessionId run_start.sessionId (null if never seen) — the store under .rovecode/sessions
30
- * model { provider, model } requested
31
- * origin { provider, model } that SERVED the last turn (router fallback may differ), or null
32
- * usage { input, output, cacheRead, cacheWrite } summed over the run's assistant messages
33
- * costUsd number, or null when any usage-bearing turn has no catalog pricing (mock, unknown
34
- * models) — an honest unknown, never a silent lower bound (core/usage.ts costUsd)
35
- * toolCalls [{ tool, ok, ms? }] in event order, one entry per ISSUED call — keyed per
36
- * `<turn>:<callId>`, so a call id a provider reuses across turns (the SSE adapter's
37
- * `tc<idx>` fallback, providers/stream.ts) is one entry PER TURN, the telemetry/otel.ts
38
- * rovecode.tool_calls count (LOW-B, #39); ms absent for calls that never executed
39
- * (permission_denied / truncated / not_found → ok:false)
40
- * durationMs sink construction → finish
41
- * exitCode the process exit code below
42
- * Exit codes: 0 done · 1 error / budget / no run_end · 2 usage error (bad --output value; also
43
- * the startup-error class, port #27) · 130 stopped (the run was aborted: SIGINT). Text mode keeps
44
- * its 0 done / 1 otherwise, plus 130 for the (newly reachable) aborted run. */
45
-
46
- import { format } from "node:util";
47
- import type { Message, ModelRef, RunEvent, RunOutstanding, StreamFn } from "../core/types.ts";
48
- import { outstandingClause, type LoopDeps } from "../core/loop.ts";
49
- import type { Runtime } from "./runtime.ts";
50
- import { costUsd, type PricingRow } from "../core/usage.ts";
51
- import { ModelCatalog } from "../providers/catalog.ts";
52
- import { VALUE_FLAGS } from "./dispatch.ts";
53
-
54
- export type OutputMode = "text" | "json" | "ndjson";
55
- export const OUTPUT_MODES: readonly OutputMode[] = ["text", "json", "ndjson"];
56
- export type RunEndStatus = Extract<RunEvent, { type: "run_end" }>["status"];
57
- type RunEnd = { status: RunEndStatus; summary: string; outstanding?: RunOutstanding };
58
-
59
- export interface RunResult {
60
- status: RunEndStatus;
61
- summary: string;
62
- sessionId: string | null;
63
- model: { provider: string; model: string };
64
- origin: { provider: string; model: string } | null;
65
- usage: { input: number; output: number; cacheRead: number; cacheWrite: number };
66
- costUsd: number | null;
67
- toolCalls: { tool: string; ok: boolean; ms?: number }[];
68
- durationMs: number;
69
- exitCode: number;
70
- }
71
-
72
- export interface Writer { write(chunk: string): unknown }
73
-
74
- /** ModelCatalog.lookup's shape — tests inject fixed pricing. */
75
- export interface PricingSource { lookup(provider: string, model: string): { pricing?: PricingRow } | undefined }
76
-
77
- export interface OutputSinkOptions {
78
- stdout: Writer;
79
- stderr: Writer;
80
- /** requested model → result.model, and the pricing fallback for origin-less messages */
81
- model: ModelRef;
82
- /** live view of the run's session store; read at finish (usage, origin, tool names) */
83
- messages: () => Message[];
84
- catalog?: PricingSource;
85
- /** SIGINT hookup seam; returns the uninstaller. Default: process.once("SIGINT"). */
86
- onInterrupt?: (handler: () => void) => () => void;
87
- }
88
-
89
- export interface OutputSink {
90
- readonly mode: OutputMode;
91
- /** aborts on the first SIGINT — thread into LoopDeps.signal so the run ends "stopped" (130) */
92
- readonly signal: AbortSignal;
93
- onEvent(ev: RunEvent): void;
94
- /** After the loop settles: writes the text summary / the json result / the ndjson result line
95
- * and returns the exit code. No `end` = the loop ended without run_end → status "error", 1. */
96
- finish(end?: RunEnd): number;
97
- /** Uninstalls the stdout guard (installed for json/ndjson over the REAL process.stdout; a no-op
98
- * otherwise) so console.log / process.stdout.write reach fd 1 again — for embedders and tests.
99
- * cmdRun never calls it: hooks.close()/mcp.close() run AFTER finish and may still print, so the
100
- * guard must hold until process.exit. */
101
- close(): void;
102
- }
103
-
104
- // ---------- argv ----------
105
-
106
- const USAGE = "--output <mode>: text (default) | json | ndjson";
107
-
108
- const usageExit = (msg: string): never => {
109
- process.stderr.write(`error: ${msg} — ${USAGE}\n`);
110
- return process.exit(2);
111
- };
112
-
113
- function isOutputMode(v: string): v is OutputMode {
114
- return (OUTPUT_MODES as readonly string[]).includes(v);
115
- }
116
-
117
- /** `--output <mode>` or `--output=<mode>`, anywhere in argv (dispatch.ts VALUE_FLAGS keeps the
118
- * value from being taken for the command). Missing or unknown value → one-line stderr usage
119
- * error, exit 2 (the usage/startup-error class; `fail` is injectable for tests). Last one wins.
120
- * cmdRun calls this FIRST — before bootRuntime — so a usage error leaves no trace: no
121
- * .rovecode/sessions/<id> (meta.json, memory dir), no sandbox probe, no MCP children to reap. */
122
- export function parseOutputMode(argv: readonly string[], fail: (msg: string) => never = usageExit): OutputMode {
123
- const args = argv.slice(2);
124
- let mode: string | undefined;
125
- for (let i = 0; i < args.length; i++) {
126
- const a = args[i]!;
127
- if (a === "--output") {
128
- const v = args[++i];
129
- if (v === undefined || v.startsWith("-")) return fail("--output needs a value");
130
- mode = v;
131
- } else if (a.startsWith("--output=")) {
132
- mode = a.slice("--output=".length);
133
- }
134
- }
135
- if (mode === undefined) return "text";
136
- return isOutputMode(mode) ? mode : fail(`unknown --output mode "${mode}"`);
137
- }
138
-
139
- /** The one-shot prompt words for cmdRun. parseCli's `rest` keeps a POST-command value flag's
140
- * value (its contract — owners drop their own, like cmdAuth's --key and export.ts's --out), so
141
- * `rovecode run "hi" --output json` arrives as rest ["hi", "json"]. The token that followed --output
142
- * is removed by POSITION, never by value (a prompt may legitimately contain the word "json"). A
143
- * pre-command --output value never enters rest; a dangling --output is parseOutputMode's error. */
144
- export function runPromptWords(cli: { cmd: string; rest: string[] }, argv: readonly string[]): string[] {
145
- const words = cli.cmd === "run" ? [...cli.rest] : [cli.cmd, ...cli.rest]; // bare prompt keeps cmd as word 0
146
- const args = argv.slice(2);
147
- const isFlag = (a: string) => a.startsWith("-");
148
- const cmdIdx = args.findIndex((a, i) => !isFlag(a) && !(i > 0 && VALUE_FLAGS.has(args[i - 1]!)));
149
- if (cmdIdx === -1) return words;
150
- // words = the non-flag tokens from `from` on; a value's word index = the non-flag tokens before it.
151
- // EVERY value flag's value, not only --output's: `rovecode run "hi" --max-turns 1` used to send the prompt
152
- // "hi 1" — the ceiling's number rode into the words because only --output dropped its own.
153
- const from = cli.cmd === "run" ? cmdIdx + 1 : cmdIdx;
154
- const drop: number[] = [];
155
- args.forEach((a, oi) => {
156
- const value = args[oi + 1];
157
- if (!VALUE_FLAGS.has(a) || oi < cmdIdx || value === undefined || isFlag(value)) return;
158
- drop.push(args.slice(from, oi + 1).filter((t) => !isFlag(t)).length);
159
- });
160
- for (const k of drop.reverse()) words.splice(k, 1); // descending: earlier indexes stay valid
161
- return words;
162
- }
163
-
164
- // ---------- piped stdin (`git diff | rovecode run "review this"`) ----------
165
-
166
- /** What a pipe on stdin handed us, or "" — never from a terminal, and never waited on forever.
167
- *
168
- * The one hazard: stdin that is not a TTY and not a pipe anybody writes to — a child spawned with an
169
- * inherited-but-idle handle, an agent's own bash tool running `rovecode run`. `readFileSync(0)` there
170
- * blocks until something closes the handle, which may be never. So the first byte gets a deadline
171
- * (default 3 s): nothing by then means nothing is coming, a note says so, and the run proceeds with the
172
- * prompt alone. A producer that HAS started is read to EOF however long it takes — the deadline is on the
173
- * first byte, not the whole stream. Bounded at `maxChars` (1 MB of text) with a note, because a 200 MB log
174
- * piped in by accident should not become one 200 MB prompt. */
175
- export async function readPipedStdin(
176
- stdin: NodeJS.ReadableStream & { isTTY?: boolean; pause?: () => unknown; resume?: () => unknown },
177
- opts: { firstByteMs?: number; maxChars?: number; note?: (line: string) => void } = {},
178
- ): Promise<string> {
179
- if (stdin.isTTY === true) return "";
180
- const firstByteMs = opts.firstByteMs ?? 3_000;
181
- const maxChars = opts.maxChars ?? 1_000_000;
182
- return new Promise<string>((resolve) => {
183
- const chunks: Buffer[] = [];
184
- let got = false, done = false;
185
- const finish = (): void => {
186
- if (done) return;
187
- done = true; clearTimeout(timer);
188
- const text = Buffer.concat(chunks).toString("utf8");
189
- if (text.length > maxChars) opts.note?.(`stdin: ${text.length.toLocaleString("en-US")} characters piped in — kept the first ${maxChars.toLocaleString("en-US")}`);
190
- resolve(text.slice(0, maxChars));
191
- };
192
- const timer = setTimeout(() => {
193
- if (got || done) return;
194
- opts.note?.(`stdin: not a terminal, but nothing arrived in ${firstByteMs / 1000} s — ignored (pipe your input, or pass --no-stdin)`);
195
- try { stdin.pause?.(); } catch { /* nothing to pause */ }
196
- finish();
197
- }, firstByteMs);
198
- stdin.on("data", (c: Buffer | string) => { got = true; chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)); });
199
- stdin.on("end", finish);
200
- stdin.on("close", finish);
201
- stdin.on("error", finish);
202
- try { stdin.resume?.(); } catch { finish(); }
203
- });
204
- }
205
-
206
- /** The prompt with the piped text under it as a fenced block. The fence grows until it cannot occur inside the
207
- * text (a diff of a markdown file carries ``` of its own). No words → "Here is the input:" introduces the
208
- * block, so the model is never handed a bare fence. Empty pipe → the prompt unchanged. */
209
- export function withPipedInput(prompt: string, piped: string): string {
210
- const text = piped.replace(/\r\n?/g, "\n").replace(/\n+$/, "");
211
- if (text.length === 0) return prompt;
212
- let fence = "```";
213
- while (text.includes(fence)) fence += "`";
214
- const head = prompt.trim().length > 0 ? prompt.trim() : "Here is the input:";
215
- return `${head}\n\n${fence}\n${text}\n${fence}`;
216
- }
217
-
218
- // ---------- stdout guard (pi core/output-guard.ts:45-70) ----------
219
-
220
- /** Redirect every stray stdout writer — console.log/info/debug (Bun writes them natively, not via
221
- * process.stdout.write) and process.stdout.write itself — to `stderr`, so only a writer bound
222
- * BEFORE the guard can reach fd 1. Returns the restorer. */
223
- export function guardStdout(stderr: Writer): { restore(): void } {
224
- const saved = { log: console.log, info: console.info, debug: console.debug, write: process.stdout.write };
225
- const toErr = (...args: unknown[]): void => { stderr.write(`${format(...args)}\n`); };
226
- console.log = toErr; console.info = toErr; console.debug = toErr;
227
- process.stdout.write = ((chunk: string | Uint8Array): boolean => {
228
- stderr.write(typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk));
229
- return true;
230
- }) as typeof process.stdout.write;
231
- return {
232
- restore() {
233
- console.log = saved.log; console.info = saved.info; console.debug = saved.debug;
234
- process.stdout.write = saved.write;
235
- },
236
- };
237
- }
238
-
239
- // ---------- exit codes ----------
240
-
241
- /** 0 done · 130 stopped (loop.ts yields "stopped" ONLY on abort — :133/:219/:302) · 1 otherwise. */
242
- export function exitCodeFor(status: RunEndStatus | undefined): number {
243
- if (status === "done") return 0;
244
- if (status === "stopped") return 130;
245
- return 1;
246
- }
247
-
248
- // ---------- sink ----------
249
-
250
- const installSigint = (handler: () => void): (() => void) => {
251
- // once: the first Ctrl-C aborts the run (a well-formed result follows); the second is the
252
- // platform default again (hard exit), so a signal-deaf tool cannot hold the terminal hostage
253
- try { process.once("SIGINT", handler); } catch { return () => {}; }
254
- return () => { try { process.off("SIGINT", handler); } catch { /* already gone */ } };
255
- };
256
-
257
- /** one issued call; `id` = the provider's call id (the store's tool_call part id — the name fallback) */
258
- type CallRecord = { id: string; tool?: string; ok: boolean; ms?: number };
259
-
260
- export function createOutputSink(mode: OutputMode, opts: OutputSinkOptions): OutputSink {
261
- const t0 = Date.now();
262
- // bind the raw writer BEFORE guarding: the guard turns process.stdout.write into a stderr relay
263
- const real = opts.stdout === process.stdout;
264
- const out: Writer = real ? { write: process.stdout.write.bind(process.stdout) } : opts.stdout;
265
- const guard = mode !== "text" && real ? guardStdout(opts.stderr) : null;
266
- const ac = new AbortController();
267
- const uninstall = (opts.onInterrupt ?? installSigint)(() => ac.abort());
268
- let sessionId: string | null = null;
269
- let baseline = 0; // store length at run_start — only THIS run's messages are accounted
270
- let turn = 0; // the issuing turn: a turn's tool events follow its turn_end (loop.ts:232 → :295)
271
- // issued calls by `<turn>:<callId>` in event order (the telemetry/otel.ts toolKey idiom): a call id a
272
- // provider reuses across turns is one call PER TURN, never a merge — keyed by callId alone, `same`
273
- // issued by 3 turns was ONE toolCall while rovecode.tool_calls said 3 (LOW-B, #39)
274
- const calls = new Map<string, CallRecord>();
275
- const call = (id: string): CallRecord => {
276
- const key = `${turn}:${id}`;
277
- const c = calls.get(key) ?? { id, ok: false };
278
- calls.set(key, c);
279
- return c;
280
- };
281
- // human progress: stdout in text mode (byte-identical to the pre-port console.log lines),
282
- // stderr in json mode (a terminal user still sees progress; stdout stays the one object),
283
- // nothing extra in ndjson mode (the event stream IS the progress)
284
- const human = (line: string): void => {
285
- if (mode === "text") out.write(`${line}\n`);
286
- else if (mode === "json") opts.stderr.write(`${line}\n`);
287
- };
288
- return {
289
- mode, signal: ac.signal,
290
- onEvent(ev) {
291
- if (mode === "ndjson") out.write(`${JSON.stringify(ev)}\n`);
292
- if (ev.type === "run_start") { sessionId = ev.sessionId; baseline = opts.messages().length; }
293
- else if (ev.type === "turn_start") turn = ev.turn;
294
- else if (ev.type === "tool_execution_start") {
295
- call(ev.callId).tool = ev.tool;
296
- human(`→ ${ev.tool} ${String(JSON.stringify(ev.args)).slice(0, 100)}`);
297
- } else if (ev.type === "tool_execution_end") {
298
- Object.assign(call(ev.callId), { ok: ev.ok, ms: ev.durationMs });
299
- human(`← ${ev.ok ? "ok" : "FAIL"} ${ev.output.slice(0, 200).replace(/\n/g, " ⏎ ")}`);
300
- } else if (ev.type === "tool_call_failed") {
301
- call(ev.callId).ok = false;
302
- } else if (ev.type === "verify") {
303
- // the verify gate, in the same two-line shape as a tool call: what runs, then how it ended
304
- human(ev.state === "running" ? `→ verify ${ev.command.slice(0, 100)}` : `← ${ev.state === "passed" ? "ok" : "FAIL"} verify ${ev.detail ?? ev.state}`);
305
- }
306
- },
307
- finish(end) {
308
- uninstall();
309
- const exitCode = exitCodeFor(end?.status);
310
- if (mode === "text") {
311
- if (end) out.write(`\n${end.summary}\n`);
312
- // "done" is the model's silence, not a verdict: one clause says what the transcript says was left
313
- // (a failed call never recovered, an unanswered question, open todos). A run that only answered a
314
- // question carries no `outstanding` at all and prints exactly the bytes it always did.
315
- if (end?.status === "done" && end.outstanding) {
316
- const clause = outstandingClause(end.outstanding);
317
- if (clause !== null) out.write(`done · ${clause}\n`);
318
- }
319
- return exitCode;
320
- }
321
- const result = summarize(
322
- end ?? { status: "error", summary: "stream ended without run_end" }, exitCode, sessionId, opts.model,
323
- opts.messages().slice(baseline), [...calls.values()], opts.catalog ?? new ModelCatalog(), Date.now() - t0,
324
- );
325
- out.write(`${JSON.stringify(mode === "json" ? result : { type: "result", ...result })}\n`);
326
- return exitCode;
327
- },
328
- close() { guard?.restore(); },
329
- };
330
- }
331
-
332
- // ---------- run deps ----------
333
-
334
- /** cmdRun's LoopDeps, built in one place so the wiring is unit-testable (LOW-2): the runtime's
335
- * registry/store/tools, guard (port #4), cwd (port #26) and hooks (port #29), with the sink's
336
- * SIGINT signal (port #21) — the fields agentLoop reads. Same object literal cmdRun used to inline,
337
- * evaluated at the same argument position (tools listed at call time). */
338
- export function buildRunDeps(rt: Pick<Runtime, "registry" | "store" | "guard" | "planReminder" | "cwd" | "hooks">, stream: StreamFn, sink: Pick<OutputSink, "signal">): LoopDeps {
339
- return { stream, registry: rt.registry, store: rt.store, tools: rt.registry.list().map((t) => t.schema), guard: rt.guard, planReminder: rt.planReminder, cwd: rt.cwd, signal: sink.signal, hooks: rt.hooks };
340
- }
341
-
342
- function summarize(
343
- end: RunEnd, exitCode: number, sessionId: string | null, model: ModelRef, msgs: Message[],
344
- calls: CallRecord[], catalog: PricingSource, durationMs: number,
345
- ): RunResult {
346
- const assistants = msgs.filter((m) => m.role === "assistant");
347
- const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
348
- let cost: number | null = 0;
349
- for (const m of assistants) {
350
- const u = m.usage;
351
- if (!u) continue;
352
- const n = { input: u.input, output: u.output, cacheRead: u.cacheRead ?? 0, cacheWrite: u.cacheWrite ?? 0 };
353
- usage.input += n.input; usage.output += n.output; usage.cacheRead += n.cacheRead; usage.cacheWrite += n.cacheWrite;
354
- if (cost === null || (n.input === 0 && n.output === 0 && n.cacheRead === 0 && n.cacheWrite === 0)) continue;
355
- // priced PER MESSAGE at the model that served it (Message.origin — the tui/cost.ts idiom)
356
- const o = m.origin ?? model;
357
- const pricing = catalog.lookup(o.provider, o.model)?.pricing;
358
- const c = pricing ? costUsd(n, pricing) : undefined;
359
- cost = c === undefined ? null : cost + c;
360
- }
361
- // names for calls that never started (denied/truncated/not_found) come from the tool_call parts, paired
362
- // by OCCURRENCE — the n-th record with a call id ↔ the n-th part carrying it (one part per issued call),
363
- // so a reused id names each turn's own call rather than the last part that mentioned the id
364
- const names = new Map<string, string[]>();
365
- for (const m of assistants) for (const p of m.parts) if (p.kind === "tool_call") names.set(p.id, [...(names.get(p.id) ?? []), p.tool]);
366
- const nth = new Map<string, number>();
367
- const served = assistants.at(-1)?.origin;
368
- return {
369
- status: end.status, summary: end.summary, sessionId,
370
- // what "done" left behind (core/loop.ts assessOutstanding): failed tool calls in the last turn, an
371
- // unanswered ask_user, successful edit/write count, open todos, whether the finish check asked once
372
- ...(end.outstanding ? { outstanding: end.outstanding } : {}),
373
- model: { provider: model.provider, model: model.model },
374
- origin: served ? { provider: served.provider, model: served.model } : null,
375
- usage, costUsd: cost,
376
- toolCalls: calls.map((c) => {
377
- const n = nth.get(c.id) ?? 0; nth.set(c.id, n + 1);
378
- return { tool: c.tool ?? names.get(c.id)?.[n] ?? "unknown", ok: c.ok, ...(c.ms !== undefined ? { ms: c.ms } : {}) };
379
- }),
380
- durationMs, exitCode,
381
- };
382
- }
package/src/cli/repl.ts DELETED
@@ -1,250 +0,0 @@
1
- /** Interactive agent chat (omp/claude-code style): persistent session, streaming
2
- * output, y/n/a approvals, slash commands. Bare `rovecode` drops here. */
3
-
4
- import readline from "node:readline";
5
- import { agentLoop } from "../core/loop.ts";
6
- import { resetTurnFailureCount } from "../memory/tools.ts";
7
- import type { ApprovalFn, RunEvent } from "../core/types.ts";
8
- import type { AskFn } from "../tools/ask-user.ts";
9
- import { bootRuntime, NO_PROVIDER_HINT } from "./runtime.ts";
10
- import { modeSwitchNote } from "../core/voice.ts";
11
- import { SandboxConfigError, describeSandbox } from "../core/sandbox-config.ts";
12
- import { WorkspaceRootError } from "../core/workspace.ts";
13
- import { appendMemory, memoryCommand, memoryNoteLine } from "../tui/memory-note.ts";
14
- import { runPlainGitCommand } from "../tui/git-plain.ts"; // port #65: /commit /undo on the plain REPL
15
- import { compactNoteLines, compactSession, copyAssistantMessage } from "../tui/context-cmds.ts"; // port #53
16
- import { expandBuiltinSlash } from "../tui/builtin-prompts.ts"; // port #53: /init
17
- import { shellLine } from "../tui/shell-cmd.ts"; // port #78: the existing shell classifier
18
- import { askPlainApproval, expandPlainInput, runPlainShellLine, type PlainInputDeps } from "../tui/input-plain.ts"; // port #78
19
-
20
- export interface ReplState {
21
- yolo: boolean;
22
- provider: string;
23
- model: string;
24
- turns: number;
25
- tokensIn: number;
26
- tokensOut: number;
27
- }
28
-
29
- // port #78: an interrupted approval must release its readline question as well as its tool call.
30
- function ask(rl: readline.Interface, q: string, signal?: AbortSignal): Promise<string> {
31
- if (signal?.aborted) return Promise.resolve("");
32
- return new Promise((res) => {
33
- const onAbort = (): void => { res(""); };
34
- signal?.addEventListener("abort", onAbort, { once: true });
35
- rl.question(q, { signal }, (a) => {
36
- signal?.removeEventListener("abort", onAbort);
37
- res(a.trim().toLowerCase());
38
- });
39
- });
40
- } // port #78
41
-
42
- /** port #33: the `--plain` asker behind ask_user — `--plain` HAS a human (the y/n/a approvals prove
43
- * it), headless surfaces leave the tool unbound and it fails closed. Numbered options plus free text
44
- * when allowed: a number picks, other text is the typed answer, an empty line declines (null). Piped
45
- * stdin is fine — it just consumes the next line. The run's abort resolves null AND is handed to
46
- * rl.question itself (WIRE-1 LOW): an aborted question's callback is DISARMED, so the user's next
47
- * line is a normal `line` event again instead of being swallowed by the dead callback. `out` is
48
- * console.log; tests capture it. */
49
- export function readlineAsker(rl: readline.Interface, out: (line: string) => void = console.log): AskFn {
50
- return (q, signal) => new Promise((resolve) => {
51
- const options = q.options ?? [];
52
- const free = q.allowFreeText !== false;
53
- out(`\n question: ${q.question}`);
54
- options.forEach((o, i) => out(` ${i + 1}) ${o}`));
55
- const hint = [options.length > 0 ? `1-${options.length}` : "", free ? "text" : ""].filter(Boolean).join(" or ");
56
- const onAbort = (): void => { resolve(null); };
57
- signal.addEventListener("abort", onAbort, { once: true });
58
- rl.question(` answer [${hint}; empty = decline]: `, { signal }, (line) => {
59
- signal.removeEventListener("abort", onAbort);
60
- const a = line.trim();
61
- const n = Number(a);
62
- if (a === "") resolve(null);
63
- else if (Number.isInteger(n) && n >= 1 && n <= options.length) resolve({ choice: n - 1, label: options[n - 1] });
64
- else if (free) resolve({ text: a });
65
- else { out(" (not one of the options — declined)"); resolve(null); }
66
- });
67
- });
68
- }
69
-
70
- export async function runRepl( /* eslint-disable-line complexity */
71
- opts: { yolo?: boolean; model?: string; addDirs?: readonly string[] } = {},
72
- ): Promise<void> {
73
- // port #27: sandbox misconfig / unavailable configured rung → one-line startup error, exit 2; a --add-dir value
74
- // that stopped being a directory between the parse and the boot is the same class of error (core/workspace.ts)
75
- const rt = await bootRuntime(opts.addDirs !== undefined && opts.addDirs.length > 0 ? { addDirs: opts.addDirs } : {}).catch((e: unknown): never => {
76
- if (e instanceof SandboxConfigError || e instanceof WorkspaceRootError) { console.error(`error: ${e.message}`); process.exit(2); }
77
- throw e;
78
- });
79
-
80
- const state: ReplState = {
81
- yolo: opts.yolo ?? process.env.ROVECODE_YOLO === "1",
82
- provider: "mock",
83
- model: opts.model ?? process.env.ROVECODE_MODEL ?? "",
84
- turns: 0, tokensIn: 0, tokensOut: 0,
85
- };
86
-
87
- // the registry is live: `rovecode provider add …` / `rovecode auth set …` from another terminal is
88
- // picked up on the next line — no restart and no ad-hoc base-url prompt here
89
- const stream = rt.stream ?? undefined;
90
- const adoptDefault = (): void => {
91
- const d = rt.providers.defaultRef();
92
- if (d !== null) { state.provider = d.provider; state.model = state.model || d.model || "gpt-4o-mini"; }
93
- };
94
- adoptDefault();
95
- if (state.provider === "mock") console.log(NO_PROVIDER_HINT);
96
- rt.hooks.onWarning((w) => console.error(`hooks: ${w}`)); // port #29: load + runtime hook notes → stderr (cmdRun idiom)
97
-
98
- console.log(`◆ rovecode here — plain chat with ${state.provider}/${state.model}`);
99
- console.log(`session ${rt.sessionId.slice(0, 8)} in ${rt.cwd}`);
100
- console.log(modeSwitchNote(state.yolo));
101
- console.log(`commands: /exit /new /yolo /model <provider/model> /status /skills /memory [--user] [text] /compact [focus] /copy [n] /init · #<text> remembers a line`);
102
-
103
- console.log("input: !cmd runs through the bash tool (same policy + approval, no model turn) · @path attaches files with read/edit anchors; images ride on the next message"); // port #78
104
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: "rovecode> " });
105
-
106
- rt.setAskUser(readlineAsker(rl)); // port #33: ask_user over the same readline (readlineAsker above)
107
-
108
- const approval: ApprovalFn = (req) => askPlainApproval((q) => ask(rl, q, running?.ac.signal), req.tool, JSON.stringify(req.revisedArgs).slice(0, 140)); // port #78: one ladder for model and shell calls
109
-
110
- rl.prompt();
111
-
112
- // port #21: one AbortController per run — Ctrl+C mid-run aborts the in-flight
113
- // fetch/tools for real (abort first, then return() settles the generator); idle
114
- // Ctrl+C keeps its old meaning (close the repl)
115
- let running: { ac: AbortController; gen: AsyncGenerator<RunEvent> } | null = null;
116
- let compacting: AbortController | null = null; // port #53: /compact's controller — Ctrl+C aborts the in-flight summarize call
117
- // port #78: the shell owns the same busy/interrupt lifecycle, without creating a model run.
118
- let closing = false; // port #78: do not prompt again after a mid-shell quit
119
- let shellRun: AbortController | null = null;
120
- let shellPending: Promise<void> | null = null;
121
- let gitRun: AbortController | null = null; // ports #65 + #78: git commands share the same lifecycle
122
- let gitPending: Promise<void> | null = null;
123
- let compactPending: Promise<void> | null = null;
124
- const busy = (): boolean => Boolean(running || compacting || shellRun || shellPending || gitRun || gitPending);
125
- const busyNote = (): boolean => { if (busy()) { console.log("finish or interrupt the run first (Ctrl+C)"); return true; } return false; };
126
- const plainInput: PlainInputDeps = { rt, yolo: () => state.yolo, busy, bindAbort: (ac) => { shellRun = ac; }, ask: (q) => ask(rl, q, shellRun?.signal), modelRef: () => ({ provider: state.provider, model: state.model }) };
127
- // port #78 end
128
- rl.on("SIGINT", () => {
129
- if (compacting) { compacting.abort(); console.log("\n [interrupted]"); return; } // port #53: compaction first — a run cannot be in flight beside it (the busy gate)
130
- if (running) { running.ac.abort(); void running.gen.return(undefined as never); console.log("\n [interrupted]"); }
131
- else if (shellRun || gitRun) { (shellRun ?? gitRun)!.abort(); console.log(`${String.fromCharCode(10)} [interrupted]`); } // ports #65 + #78
132
- else rl.close();
133
- });
134
-
135
- rl.on("line", async (line) => {
136
- const text = line.trim();
137
- if (!text) { rl.prompt(); return; }
138
- // port #78: shell lines precede the provider check and never reach agentLoop, even with no provider.
139
- if (shellLine(text) !== null) {
140
- if (!busyNote()) {
141
- shellPending = runPlainShellLine(plainInput, text);
142
- try { await shellPending; }
143
- catch (e) { console.log(`error: ${e instanceof Error ? e.message : String(e)}`); }
144
- finally { shellPending = null; }
145
- }
146
- if (!closing) rl.prompt(); return;
147
- } // port #78
148
- if (text === "/exit" || text === "/quit") { rl.close(); return; }
149
- if ((text === "/new" || text === "/compact" || text.startsWith("/compact ") || text === "/commit" || text.startsWith("/commit ") || text === "/undo") && busyNote()) { rl.prompt(); return; } // port #78: do not fork the record mid-shell
150
- if (text === "/yolo") { state.yolo = !state.yolo; console.log(modeSwitchNote(state.yolo)); rl.prompt(); return; }
151
- if (text === "/status") { console.log(`provider=${state.provider} model=${state.model} turns=${state.turns} tokens=${state.tokensIn}in/${state.tokensOut}out\nsandbox: ${describeSandbox(rt.sandbox)}`); rl.prompt(); return; }
152
- if (text === "/skills") { for (const s of rt.skillStore.list()) console.log(` ${s.name.padEnd(20)} ${s.description}`); rl.prompt(); return; }
153
- // the same two memory affordances the TUI has (tui/memory-note.ts): `/memory [--user] [text]` and the
154
- // `#<text>` note, which short-circuits before any model turn
155
- if (text === "/memory" || text.startsWith("/memory ")) { console.log(memoryCommand(rt.blockStore, text.slice("/memory".length)).text); rl.prompt(); return; }
156
- { const note = memoryNoteLine(text); if (note !== null) { console.log(appendMemory(rt.blockStore, "memory", note).text); rl.prompt(); return; } }
157
- if (text.startsWith("/model ")) {
158
- const ref = rt.providers.resolveSelector(text.slice(7), state.provider);
159
- if ("error" in ref) console.log(ref.error); else { state.provider = ref.provider; state.model = ref.model; console.log(`model → ${ref.provider}/${ref.model}`); }
160
- rl.prompt(); return;
161
- }
162
- if (text === "/new") { rt.store.branch(rt.store.messages()[0]?.id ?? ""); console.log("branched to session start"); rl.prompt(); return; }
163
- // port #65: /commit and /undo run through git-plain.ts — the SAME cmdCommit/cmdUndo as the TUI,
164
- // the approval card as this REPL's own y/n question. A busy run is checked below like any submit;
165
- // the git commands check it themselves and are refused with a note instead.
166
- if (text === "/undo" || text === "/commit" || text.startsWith("/commit ")) {
167
- gitRun = new AbortController(); // /undo has a confirmation but no tool-run controller of its own
168
- gitPending = runPlainGitCommand({ rt, yolo: state.yolo, approve: state.yolo ? undefined : approval, ask: (q) => ask(rl, q, gitRun?.signal), busy: () => Boolean(running || compacting || shellRun || shellPending), bindAbort: (ac) => { gitRun = ac; }, out: (l) => console.log(l) }, text);
169
- try { await gitPending; }
170
- catch (e) { console.log(`error: ${e instanceof Error ? e.message : String(e)}`); }
171
- finally { gitPending = null; gitRun = null; }
172
- if (!closing) rl.prompt();
173
- return;
174
- }
175
- // port #53: /compact and /copy share the TUI's helpers (context-cmds.ts) — the ONE compaction strategy
176
- // set (marker persisted, compacted branch durable), the same clipboard chain; /init expands below
177
- if (text === "/compact" || text.startsWith("/compact ")) {
178
- if (running || compacting) { console.log("finish or interrupt the run first (Ctrl+C)"); rl.prompt(); return; }
179
- const focus = text.slice("/compact".length).trim();
180
- compacting = new AbortController();
181
- compactPending = compactSession(rt.store, rt.buildCfg(state.yolo, approval), { model: { provider: state.provider, model: state.model }, signal: compacting.signal }).then((result) => { for (const l of compactNoteLines(result, focus)) console.log(l); });
182
- try { await compactPending; }
183
- catch (e) { console.log(`compaction failed: ${e instanceof Error ? e.message : String(e)} — the session is unchanged`); }
184
- finally { compacting = null; compactPending = null; }
185
- if (!closing) rl.prompt(); return;
186
- }
187
- if (text === "/copy" || text.startsWith("/copy ")) {
188
- const arg = text.slice("/copy".length).trim();
189
- const n = arg === "" ? 1 : /^[1-9]\d*$/.test(arg) ? Number(arg) : NaN;
190
- console.log(Number.isNaN(n) ? "usage: /copy [n] — copies the last assistant message; n counts back from the latest" : (await copyAssistantMessage(rt.store.messages(), n)).text);
191
- rl.prompt(); return;
192
- }
193
-
194
- if (busyNote()) { rl.prompt(); return; } // port #78: no overlapping model/shell records
195
- const reason = rt.noProviderReason(); // live — a provider added since boot is picked up here
196
- if (!stream || reason !== null) { console.log(reason ?? "no provider stream"); rl.prompt(); return; }
197
- if (state.provider === "mock") adoptDefault();
198
-
199
- const goal = expandPlainInput(plainInput, expandBuiltinSlash(text, rt.cwd) ?? text); // port #78: after #53's /init expansion, using the current model for images
200
- const def = rt.buildDef({ provider: state.provider, model: state.model });
201
-
202
- const ac = new AbortController();
203
- rt.tasks.bindRun(ac.signal); // port #26: Ctrl-C (ac.abort above) also cancels the background tasks this run started
204
- // port #29: hooks ride the deps like every surface; port #26: the runtime's ONE steering queue (not a
205
- // fresh one) so background-task completion notes — and any hook-pushed steer — reach the next turn
206
- const gen = agentLoop(def, goal, {}, rt.buildCfg(state.yolo, approval), { stream, registry: rt.registry, store: rt.store, tools: rt.registry.list().map((t) => t.schema), guard: rt.guard, planReminder: rt.planReminder, cwd: rt.cwd, signal: ac.signal, hooks: rt.hooks }, rt.steering); // port #78: expanded goal
207
- running = { ac, gen };
208
- try {
209
- let live = "";
210
- for await (const ev of gen) {
211
- if (ev.type === "turn_start") { resetTurnFailureCount(); state.turns++; }
212
- if (ev.type === "message_update") { process.stdout.write(ev.delta); live += ev.delta; }
213
- if (ev.type === "tool_execution_start") { console.log(`\n → ${ev.tool} ${JSON.stringify(ev.args).slice(0, 120)}`); }
214
- if (ev.type === "tool_execution_end") { console.log(` ← ${ev.ok ? "ok" : "FAIL"} ${ev.output.slice(0, 160).replace(/\n/g, " ⏎ ")}`); }
215
- if (ev.type === "run_end") {
216
- if (!live.trim()) console.log(ev.summary);
217
- else console.log();
218
- if (ev.status !== "done") console.log(` [${ev.status}]`);
219
- }
220
- }
221
- for (const n of rt.drainRouterNotes()) console.log(` [${n}]`); // port #14 fallback advances
222
- for (const m of rt.store.messages()) if (m.usage) { state.tokensIn += m.usage.input; state.tokensOut += m.usage.output; }
223
- } catch (e) {
224
- console.log(`error: ${e instanceof Error ? e.message : String(e)}`);
225
- } finally {
226
- running = null;
227
- }
228
- rl.prompt();
229
- });
230
-
231
- // every quit path lands here (Ctrl+D, /exit, /quit, idle Ctrl+C → rl.close()), so this is the ONE exit
232
- rl.on("close", async () => {
233
- closing = true; // port #78
234
- // port #29: a run still in flight dies with the surface (abort, then let its generator settle) BEFORE
235
- // session_close fires once — after in-flight on_event taps drained (hooks.close() waits for them)
236
- if (running) { running.ac.abort(); await running.gen.return(undefined as never).catch(() => {}); }
237
- // port #26: quitting leaves no background children — their runs (and subprocess trees) die
238
- // now and settle, bounded, before the process goes (same policy as cmdRun's exit())
239
- shellRun?.abort(); // port #78: release approval or kill the shell before closing hooks
240
- gitRun?.abort(); compacting?.abort(); // ports #53 + #65: no background command survives the surface
241
- await Promise.all([shellPending, gitPending, compactPending].map((p) => p?.catch(() => {}))); // settle before session_close
242
- rt.tasks.cancelAll();
243
- await rt.tasks.drain(2_000);
244
- rt.bashJobs.dispose(); // #55: kill this session's background jobs before session_close
245
- await rt.hooks.close().catch(() => {});
246
- void rt.mcp?.close().catch(() => {}); // kill MCP child processes (TUI does the same in app.ts)
247
- console.log(`\nbye — session ${rt.sessionId.slice(0, 8)} saved (${state.turns} turns, ${state.tokensIn}in/${state.tokensOut}out tokens)`);
248
- process.exit(0);
249
- });
250
- }
@@ -1,14 +0,0 @@
1
- /** Background indexing is useful in a project, not at the home/filesystem root. A launch from home
2
- * otherwise parses unrelated SDK/cache files under AppData until the 2000-source cap is reached.
3
- * This gates only automatic interactive warmup; explicit headless builds are unchanged. */
4
- import { realpathSync } from "node:fs";
5
- import { homedir } from "node:os";
6
- import { parse, relative, resolve } from "node:path";
7
-
8
- export function shouldWarmRepoMap(cwd: string, home: string = homedir()): boolean {
9
- const canonical = (path: string): string => {
10
- try { return realpathSync(path); } catch { return resolve(path); }
11
- };
12
- const dir = canonical(cwd);
13
- return relative(dir, parse(dir).root) !== "" && relative(dir, canonical(home)) !== "";
14
- }