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,283 +0,0 @@
1
- /** Custom slash commands (port #30): `.rovecode/commands/*.md` (project) and `~/.rovecode/commands/*.md`
2
- * (user scope, ROVECODE_HOME-aware) become `/name` commands in the TUI — autocomplete, /help,
3
- * and dispatch through the same user-turn path a typed prompt takes.
4
- *
5
- * Ported from opencode @ ebece6e (MIT), packages/opencode/src unless noted:
6
- * - discovery: markdown files under a `command(s)/` dir, name = path sans extension
7
- * (config/command.ts:13-39 load, config/entry-name.ts:15-19 configEntryNameFromPath);
8
- * the frontmatter is the command's metadata and the markdown body its template
9
- * (config/command.ts:26-30 `{ name, ...md.data, template: md.content.trim() }`); fields
10
- * description/model (packages/core/src/v1/config/command.ts:5-12 — agent/variant/subtask
11
- * have no rovecode counterpart; rovecode adds `mode` for plan/act, port #20).
12
- * - precedence: the global config dir is loaded before the project's and later entries
13
- * replace earlier ones (config/config.ts:473 mergeDeep) → project shadows user here.
14
- * - templating (session/prompt.ts:1372-1395): quote-aware argument split (argsRegex :1594,
15
- * quoteTrimRegex :1596), `$N` positional (placeholderRegex :1595), `$ARGUMENTS` = the whole
16
- * argument string (:1391), and a template with no placeholder gets the arguments appended
17
- * after a blank line (:1393-1395); `hints` (command/index.ts:36-44) lists the placeholders.
18
- * - a per-command `model` overrides the session model for THAT prompt only (:1411-1412).
19
- * Pattern reference only, no code ported: gemini-cli @ 0bd1d43 packages/cli/src/services/
20
- * FileCommandLoader.ts — user dir first, project dir second, "last wins" (:85-90, :204-228),
21
- * invalid files skipped with a report, never fatal (:277-298).
22
- *
23
- * Deviations: flat directory (no subdirectory namespaces); names are lowercased and must
24
- * match [a-z0-9_-]+; YAML-lite frontmatter (`key: value` lines — skills/index.ts
25
- * parseFrontmatter, reused) instead of gray-matter, so an unterminated block, a `mode`
26
- * outside plan|act, or an empty body skips the file with a boot warning (never a throw);
27
- * substitution is ONE pass, so substituted text is never re-scanned, `$$` is a literal `$`,
28
- * and a positional `$N` is exactly one token (upstream's "last placeholder absorbs the rest",
29
- * prompt.ts:1387, is not ported); no `!\`shell\`` / `@file` injection (prompt.ts:1397-1408) —
30
- * the template reaches the model as plain text; a built-in TUI command always beats a custom
31
- * one of the same name (warned at boot); a symlinked *.md counts when it points at a file (a
32
- * dangling link is reported unreadable); boot-warning echoes and descriptions are bounded.
33
- *
34
- * TUI semantics (runCustomCommand): `mode` switches through the /plan-/act path and STAYS
35
- * switched after the run — the switch is a durable session entry (port #20), and silently
36
- * flipping back would desynchronize the status line from the session record; `model` is set
37
- * the way /model does and restored after the run unless the user re-pointed it mid-run
38
- * (/model is not busy-gated). With planActSeparateModels off, /model mirrors writes into
39
- * both slots, so the restore does too — same as typing /model <prev> by hand. */
40
-
41
- import { readdirSync, readFileSync, statSync } from "node:fs";
42
- import { join, resolve } from "node:path";
43
- import type { AgentMode, ModeManager } from "../core/modes.ts";
44
- import { rovecodeHome } from "../providers/auth.ts";
45
- import { parseFrontmatter } from "../skills/index.ts";
46
- import { togglePlanAct, type ModeStateSlice } from "./modes-cmd.ts";
47
- import { expandBuiltinSlash } from "./builtin-prompts.ts"; // port #53: built-in prompt commands beat same-named custom ones
48
- import type { Renderer, SlashCommand } from "./renderer.ts";
49
-
50
- export type CommandScope = "project" | "user";
51
-
52
- export interface CustomCommand {
53
- /** `/name`: the filename sans `.md`, lowercased; must match [a-z0-9_-]+ */
54
- name: string;
55
- description: string;
56
- /** frontmatter `model:` — per-run override, applied the way /model does */
57
- model?: string;
58
- /** frontmatter `mode:` — the run happens in this plan/act mode */
59
- mode?: AgentMode;
60
- /** the markdown body: the prompt template */
61
- body: string;
62
- /** placeholders the template uses, for /help — opencode hints(): "$1 $2 $ARGUMENTS" */
63
- hints: string[];
64
- path: string;
65
- scope: CommandScope;
66
- }
67
-
68
- export interface ParsedCommandFile { description?: string; model?: string; mode?: AgentMode; body: string }
69
-
70
- const NAME_RE = /^[a-z0-9_-]+$/;
71
- /** opencode prompt.ts:1594 minus its `[Image N]` alternative (no image parts reach this path). */
72
- const ARGS_RE = /(?:"[^"]*"|'[^']*'|[^\s"']+)/g;
73
- const QUOTE_TRIM_RE = /^["']|["']$/g;
74
- /** one pass: `$$` (literal), `$ARGUMENTS`, `$1`..`$9` */
75
- const PLACEHOLDER_RE = /\$(\$|ARGUMENTS|[1-9])/g;
76
- /** /help + palette rows stay one line; a 20k-char frontmatter field must not become one */
77
- const MAX_DESCRIPTION_CHARS = 200;
78
-
79
- /** Bounded echo (todo.ts `show` idiom): file content quoted back in a warning is clipped. */
80
- function clip(s: string, max: number): string {
81
- return s.length > max ? s.slice(0, max - 1) + "…" : s;
82
- }
83
-
84
- // ---------- file format ----------
85
-
86
- /** Optional `---` frontmatter + body → command fields. `{ error }` names why a file must be
87
- * skipped (unterminated block, `mode` outside plan|act, empty body). Never throws. */
88
- export function parseCommandFile(text: string): ParsedCommandFile | { error: string } {
89
- let src = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text; // drop a UTF-8 BOM
90
- let fm: Record<string, string> = {};
91
- let body = src;
92
- if (src.startsWith("---")) {
93
- if (!src.endsWith("\n")) src += "\n"; // parseFrontmatter finds the closing fence by its line end
94
- const parsed = parseFrontmatter(src);
95
- if (!parsed) return { error: "unterminated frontmatter (no closing ---)" };
96
- fm = parsed.fm; body = parsed.body;
97
- }
98
- body = body.trim();
99
- if (body === "") return { error: "empty command body (nothing to send)" };
100
- const field = (k: string): string | undefined => { const v = fm[k]?.trim(); return v ? v : undefined; };
101
- const mode = field("mode");
102
- if (mode !== undefined && mode !== "plan" && mode !== "act") return { error: `mode must be "plan" or "act" (got "${clip(mode, 40)}")` }; // LOW-2: bounded echo
103
- return { description: field("description"), model: field("model"), mode, body };
104
- }
105
-
106
- /** Placeholders a template uses: `$1..$9` sorted, then `$ARGUMENTS` (command/index.ts:36-44).
107
- * `$$` escapes are dropped first so `$$1` does not count. */
108
- export function hints(body: string): string[] {
109
- const src = body.replace(/\$\$/g, "");
110
- const out = [...new Set(src.match(/\$[1-9]/g) ?? [])].sort();
111
- if (src.includes("$ARGUMENTS")) out.push("$ARGUMENTS");
112
- return out;
113
- }
114
-
115
- // ---------- discovery ----------
116
-
117
- export interface DiscoverOptions {
118
- /** user-scope rovecode dir; default rovecodeHome() (ROVECODE_HOME → ~/.rovecode). Commands: `<home>/commands` */
119
- home?: string;
120
- /** built-in command names — a custom command with one of these names is dropped, with a warning */
121
- reserved?: readonly string[];
122
- /** more command folders — a plugin's (src/plugins) — scanned AFTER the folder of the same scope, so
123
- * your own `~/.rovecode/commands/x.md` or `.rovecode/commands/x.md` keeps `/x` over a plugin's */
124
- extraDirs?: readonly { dir: string; scope: CommandScope }[];
125
- }
126
-
127
- export interface DiscoveredCommands { commands: CustomCommand[]; warnings: string[] }
128
-
129
- /** Scan `<home>/commands` then `<cwd>/.rovecode/commands`; a later (project) entry replaces an
130
- * earlier (user) one of the same name. Missing dirs are silent (the common case); every
131
- * other problem is a warning for the boot transcript, never a throw. Sorted by name. */
132
- export function discoverCommands(cwd: string, opts: DiscoverOptions = {}): DiscoveredCommands {
133
- const warnings: string[] = [];
134
- const reserved = new Set(opts.reserved ?? []);
135
- const userDir = join(opts.home ?? rovecodeHome(), "commands");
136
- const projectDir = join(cwd, ".rovecode", "commands");
137
- const extra = (scope: CommandScope): [CommandScope, string][] => (opts.extraDirs ?? []).filter((e) => e.scope === scope).map((e) => [scope, e.dir]);
138
- // cwd IS the rovecode home: one directory, scanned once (gemini-cli FileCommandLoader.ts:221-228)
139
- const dirs: [CommandScope, string][] = resolve(userDir) === resolve(projectDir)
140
- ? [["project", projectDir], ...extra("user"), ...extra("project")]
141
- : [["user", userDir], ...extra("user"), ["project", projectDir], ...extra("project")];
142
- const byName = new Map<string, CustomCommand>();
143
- for (const [scope, dir] of dirs) {
144
- for (const cmd of scanCommandDir(dir, scope, warnings)) {
145
- // the verdict first, the path last: an 80-column terminal wraps a long path mid-line and the
146
- // sentence must survive at the line start
147
- if (reserved.has(cmd.name)) { warnings.push(`/${cmd.name} is a built-in command — built-in kept (${cmd.path})`); continue; }
148
- const prev = byName.get(cmd.name);
149
- if (prev?.scope === scope) { warnings.push(`${cmd.path}: /${cmd.name} already defined by ${prev.path} — first kept`); continue; }
150
- byName.set(cmd.name, cmd); // project (scanned second) shadows user
151
- }
152
- }
153
- return { commands: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), warnings };
154
- }
155
-
156
- /** LOW-3: a symlinked `*.md` counts when its target is a file (a link to a directory is skipped
157
- * like any directory); a DANGLING link stays in the list so the read below reports it unreadable. */
158
- function linksToFile(path: string): boolean {
159
- try { return statSync(path).isFile(); } catch { return true; }
160
- }
161
-
162
- /** One directory's `*.md` files (top level only, sorted), each parsed or warned about. */
163
- function scanCommandDir(dir: string, scope: CommandScope, warnings: string[]): CustomCommand[] {
164
- let files: string[];
165
- try {
166
- files = readdirSync(dir, { withFileTypes: true })
167
- .filter((e) => /\.md$/i.test(e.name) && (e.isFile() || (e.isSymbolicLink() && linksToFile(join(dir, e.name)))))
168
- .map((e) => e.name).sort();
169
- } catch {
170
- return []; // no such directory — silent, like the skills store
171
- }
172
- const out: CustomCommand[] = [];
173
- for (const file of files) {
174
- const path = join(dir, file);
175
- const name = file.slice(0, -3).toLowerCase();
176
- if (!NAME_RE.test(name)) { warnings.push(`${path}: skipped — command name "${name}" must match [a-z0-9_-]+`); continue; }
177
- let text: string;
178
- try {
179
- text = readFileSync(path, "utf8");
180
- } catch (e) {
181
- warnings.push(`${path}: skipped — unreadable (${e instanceof Error ? e.message : String(e)})`);
182
- continue;
183
- }
184
- const parsed = parseCommandFile(text);
185
- if ("error" in parsed) { warnings.push(`${path}: skipped — ${parsed.error}`); continue; }
186
- out.push({
187
- name, description: clip(parsed.description ?? `custom command (${file})`, MAX_DESCRIPTION_CHARS), model: parsed.model, mode: parsed.mode,
188
- body: parsed.body, hints: hints(parsed.body), path, scope,
189
- });
190
- }
191
- return out;
192
- }
193
-
194
- // ---------- templating ----------
195
-
196
- /** Template → prompt. `$ARGUMENTS` = the whole argument string, `$1..$9` = one quote-aware token
197
- * each ("" when missing), `$$` = a literal `$`. One pass, so substituted text is never
198
- * re-scanned. A template without placeholders gets the arguments appended after a blank line
199
- * (prompt.ts:1393-1395) so nothing the user typed is silently dropped. */
200
- export function renderCommand(cmd: Pick<CustomCommand, "body">, argString: string): string {
201
- const raw = argString.trim();
202
- const args = (raw.match(ARGS_RE) ?? []).map((a) => a.replace(QUOTE_TRIM_RE, ""));
203
- let placeholder = false;
204
- const out = cmd.body.replace(PLACEHOLDER_RE, (_m, tok: string) => {
205
- if (tok === "$") return "$";
206
- placeholder = true;
207
- return tok === "ARGUMENTS" ? raw : (args[Number(tok) - 1] ?? "");
208
- });
209
- return placeholder || raw === "" ? out : `${out}\n\n${raw}`;
210
- }
211
-
212
- /** `rovecode run "/name args"` hook (main.ts call sites — see the port notes): a leading `/name`
213
- * that names a discovered command renders to its prompt; anything else passes through
214
- * verbatim. `model`/`mode` are TUI semantics and are not applied here. */
215
- export function expandSlashPrompt(prompt: string, cwd: string, opts: DiscoverOptions = {}): string {
216
- // port #53: the built-in prompt commands (/init) are consulted FIRST — a built-in beats a
217
- // same-named custom command, as in the TUI (aion commands.ts:216). Order matters: reversed, a
218
- // repo file named init.md would silently swallow rovecode run "/init".
219
- const builtin = expandBuiltinSlash(prompt, cwd);
220
- if (builtin !== undefined) return builtin;
221
- const m = /^\/(\S+)(?:\s+([\s\S]*))?$/.exec(prompt.trim());
222
- if (!m) return prompt;
223
- const cmd = discoverCommands(cwd, opts).commands.find((c) => c.name === m[1]!.toLowerCase());
224
- return cmd ? renderCommand(cmd, m[2] ?? "") : prompt;
225
- }
226
-
227
- // ---------- TUI surface ----------
228
-
229
- /** Entries for Renderer.setCommands (editor autocomplete) — appended after the built-ins. */
230
- export function commandsForPalette(list: readonly CustomCommand[]): SlashCommand[] {
231
- return list.map((c) => ({ name: c.name, description: c.description }));
232
- }
233
-
234
- /** `/help` tail: "" when there are none, else a "custom:" section (name, hints, description, scope). */
235
- export function helpForCommands(list: readonly CustomCommand[]): string {
236
- if (list.length === 0) return "";
237
- return "\ncustom:\n" + list
238
- .map((c) => `/${c.name}${c.hints.length > 0 ? " " + c.hints.join(" ") : ""} — ${c.description} (${c.scope})`)
239
- .join("\n");
240
- }
241
-
242
- /** What the TUI hands the dispatcher (app.ts builds it once; state is read live). */
243
- export interface CustomCommandCtx {
244
- renderer: Renderer;
245
- modes: ModeManager;
246
- state: ModeStateSlice;
247
- pushStatus: () => void;
248
- /** the plain user-turn path (app.ts `submit`): echo, flush a pending mode switch, start the run */
249
- submit: (text: string) => Promise<void>;
250
- }
251
-
252
- /** handleSlash `default:` hook: false when `name` is not a custom command (the caller reports
253
- * "unknown command"); true after kicking off the run — fire-and-forget, the same shape as the
254
- * other async slash commands. */
255
- export function dispatchCustomCommand(ctx: CustomCommandCtx, list: readonly CustomCommand[], name: string, arg: string): boolean {
256
- const cmd = list.find((c) => c.name === name);
257
- if (!cmd) return false;
258
- void runCustomCommand(ctx, cmd, arg);
259
- return true;
260
- }
261
-
262
- /** Busy-gated like /plan and /checkpoints — a rendered prompt with a model/mode attached cannot
263
- * be queued as steering. `mode` → togglePlanAct (stays); `model` → setModel like /model,
264
- * restored after the run unless the user re-pointed it meanwhile (header: TUI semantics). */
265
- export async function runCustomCommand(ctx: CustomCommandCtx, cmd: CustomCommand, arg: string): Promise<void> {
266
- if (ctx.state.busy) { ctx.renderer.addSystemNote("finish or interrupt the run first (Esc)", "warn"); return; }
267
- if (cmd.mode !== undefined && cmd.mode !== ctx.modes.mode) togglePlanAct(ctx.modes, cmd.mode, ctx.state, ctx.renderer, ctx.pushStatus);
268
- const prev = ctx.modes.modelFor().model;
269
- const override = cmd.model !== undefined && cmd.model !== prev;
270
- if (override) {
271
- ctx.modes.setModel({ model: cmd.model });
272
- ctx.state.model = ctx.modes.modelFor().model;
273
- ctx.renderer.addSystemNote(`model → ${cmd.model} for /${cmd.name} (restored after the run)`);
274
- ctx.pushStatus();
275
- }
276
- try {
277
- await ctx.submit(renderCommand(cmd, arg));
278
- } finally {
279
- if (override && ctx.modes.modelFor().model === cmd.model) {
280
- ctx.modes.setModel({ model: prev }); ctx.state.model = prev; ctx.pushStatus();
281
- }
282
- }
283
- }
@@ -1,53 +0,0 @@
1
- /** `/config [all]` (aion port, re-shaped): the read-only TUI view of rovecode's ACTUAL settings model — the two
2
- * settings.json scopes (core/settings.ts) merged the way the loaders merge them, plus the environment the
3
- * process is running with. Aion's `describeSettings`/knob table is NOT ported: rovecode has no such table, and
4
- * a view that invented one would describe a settings model the real loaders do not use. What IS shown is what
5
- * the loaders read, keyed the way they read it:
6
- * - files first: which of the two settings.json files exists (a missing project file is the common case,
7
- * and "you could create one" is more useful than pretending it is empty);
8
- * - then every settings.json key that is SET, with the scope that wins for it (project beats user — the
9
- * merge in loadSettings) and the value; `verify: false` shown as read (a refusal, honoured from any file);
10
- * - then the ROVECODE_* variables this process carries (names + values, values of keys never shown);
11
- * - the permission level is resolved the way the CLI resolves it and stated in words.
12
- * Nothing is written here; editing is the file by hand (rovecode has no `config set`). A value changed on
13
- * disk mid-session is picked up on the next read — the loaders read per call — so this view is a snapshot of
14
- * NOW, not of boot. Same ctx idiom as info-cmd.ts: renderer + cwd. */
15
-
16
- import type { Renderer } from "./renderer.ts";
17
- import { existsSync } from "node:fs";
18
- import { loadSettings, loadSettingsScoped, resolvePermission, settingsPath, type Settings } from "../core/settings.ts";
19
- import { rovecodeHome } from "../providers/auth.ts";
20
-
21
- export interface ConfigViewCtx { renderer: Pick<Renderer, "addSystemNote">; cwd: string; /** the CLI's explicit flag, when it gave one */ permissionFlag?: Parameters<typeof resolvePermission>[1] }
22
-
23
- const KEYS: readonly (keyof Settings)[] = ["permission", "effort", "bell", "notify", "notify_when", "notify_command", "verify", "lsp"];
24
- const short = (v: unknown): string => { const s = typeof v === "string" ? v : JSON.stringify(v); return s.length > 80 ? `${s.slice(0, 79)}…` : s; };
25
-
26
- /** the note text, pure — `all` also lists the keys nothing set and the env vars named-but-unset is out of scope:
27
- * rovecode's env page (`rovecode help env`) is the complete list, this view shows what THIS run carries */
28
- export function renderConfigView(cwd: string, all: boolean, env: Readonly<Record<string, string | undefined>> = process.env, home: string = rovecodeHome(), flag?: Parameters<typeof resolvePermission>[1]): string {
29
- const user = settingsPath("user", cwd);
30
- const project = settingsPath("project", cwd);
31
- const scoped = loadSettingsScoped(cwd, home);
32
- const merged = loadSettings(cwd);
33
- const lines: string[] = [];
34
- lines.push(`settings — user: ${user}${existsSync(user) ? "" : " (absent)"} · project: ${project}${existsSync(project) ? "" : " (absent)"}`);
35
- lines.push(`permission: ${resolvePermission(cwd, flag, env)}${scoped.dropped.length > 0 ? ` · the project file's ${scoped.dropped.join(", ")} ${scoped.dropped.length === 1 ? "is" : "are"} ignored until you trust that file (rovecode trust show)` : ""}`);
36
- const set = KEYS.filter((k) => merged[k] !== undefined && k !== "permission");
37
- if (set.length === 0) lines.push("(no settings.json key is set — every knob is at its default)");
38
- for (const k of set) {
39
- const scope = scoped.project[k] !== undefined ? "project" : "user";
40
- lines.push(`${k} = ${short(merged[k])} [${scope}]`);
41
- }
42
- const envNames = Object.keys(env).filter((k) => k.startsWith("ROVECODE_") && env[k] !== "").sort();
43
- if (envNames.length > 0) {
44
- if (all) for (const n of envNames) lines.push(`${n} = ${/KEY|TOKEN|SECRET/i.test(n) ? "(set — value not shown)" : short(env[n])} [env]`);
45
- else lines.push(`env: ${envNames.length} ROVECODE_* variable${envNames.length === 1 ? "" : "s"} set (/config all names them)`);
46
- }
47
- return lines.join("\n");
48
- }
49
-
50
- /** `/config [all]` */
51
- export function cmdConfigView(ctx: ConfigViewCtx, arg: string): void {
52
- ctx.renderer.addSystemNote(renderConfigView(ctx.cwd, arg.trim() === "all", process.env, rovecodeHome(), ctx.permissionFlag));
53
- }
@@ -1,282 +0,0 @@
1
- /** TUI context commands (port #53): /compact [focus] · /clear · /init · /copy [n]. Split out of
2
- * app.ts for the ADR-002 cap; app.ts adds the four palette entries (CONTEXT_COMMANDS) and the
3
- * dispatch cases. The CALLER owns the swappable store, the mode manager and the session-switch
4
- * routine, so everything runs through the injected ctx (session-cmd.ts shape).
5
- *
6
- * /compact ADR-007: the ONE strategy set. compactSession calls core/compaction.ts planCompaction +
7
- * applyCompaction with the run's own RunConfig (rt.buildCfg) and the loop's CompactionCtx
8
- * shape (trigger "manual"; tokenText = partsTokenText). PORT NOTE — this tree has no
9
- * summarizer wired anywhere (rovecode's runtime builds none; grep `.summarize` finds only
10
- * memory/recall's own), and its `Summarizer` is `(texts) => Promise<string>` with no signal
11
- * argument (aion's #68 one takes the signal and returns usage/origin). So on THIS tree
12
- * head-summarize falls back to keep-window at PLAN time and the note says "no summarizer
13
- * wired on this surface" — that note is the feature working, not a degradation, and it is
14
- * the user-visible text until #68 lands (owned by nimbus-2d as a core piece). The signal
15
- * still matters: loop.ts:215 threads ctx.signal, and when #68 lands the abort path is
16
- * already here — cmdCompact OWNS an AbortController bound through ctx.bindAbort (the app's
17
- * Esc / ⌃c target, ShellCtx.bindAbort shape; the --plain REPL binds Ctrl-C the same way).
18
- * A manual compaction must be DURABLE or the next run would see the full history again
19
- * (the automatic one is per-run and transient — loop.ts rebuilds history from
20
- * store.messages() every run): the outcome history becomes a new branch of the append-only
21
- * tree (fresh-id copies chained under the summary/marker message as a new root; ADR-004 —
22
- * nothing deleted, the earlier turns stay in the file on the previous branch), the leaf
23
- * moves there (meta.json, like /new), and the compaction marker is persisted with
24
- * store.appendEvent — the same event shape and call the loop uses (loop.ts:216-217) — so
25
- * replay and `rovecode export` show it. A focus hint is accepted but NOT applied: none of
26
- * the strategies takes instructions (the summarizer signature is texts only); the note says
27
- * so.
28
- * /clear a fresh session in place through the app's switchSession: same cwd/renderer, the live
29
- * mode and the model slots survive, the transcript is emptied, the previous session is
30
- * untouched and resumable.
31
- * /init submits the built-in AGENTS.md prompt (builtin-prompts.ts, ported alongside) as a plain
32
- * user turn — the write/edit goes through the approval seam like any tool call.
33
- * /copy the last (or n-th from the end) assistant message's text → the clipboard (clipboard.ts).
34
- * All four are busy-gated with the shared "finish or interrupt the run first" note.
35
- *
36
- * Pattern reference only, no code ported: gemini-cli @ 0bd1d43 packages/cli/src/ui/commands/
37
- * {compressCommand,clearCommand,initCommand,copyCommand}.ts — force-compress shows original→new
38
- * token counts (:49-61); /clear ends the recording, mints a new session id and clears the UI
39
- * (:41-76); /copy takes the last model message's text parts (:24-41). */
40
-
41
- import { randomUUID } from "node:crypto";
42
- import { existsSync } from "node:fs";
43
- import { basename, isAbsolute, join } from "node:path";
44
- import { applyCompaction, planCompaction, DEFAULT_COMPACTION_STRATEGY, type CompactionCtx, type CompactionStrategy } from "../core/compaction.ts";
45
- import { estimateTokens } from "../core/context.ts";
46
- import { partsText, partsTokenText } from "../core/loop.ts";
47
- import { buildModeChangeEntry, modeFromEntries, type AgentMode, type ModeManager } from "../core/modes.ts";
48
- import type { SessionStore } from "../core/session.ts";
49
- import { ATTACHMENTS_DIR } from "../core/session-images.ts";
50
- import type { Message, MessagePart, RunConfig } from "../core/types.ts";
51
- import { expandBuiltinSlash, INIT_TARGET } from "./builtin-prompts.ts";
52
- import { copyToClipboard, type ClipboardOptions } from "./clipboard.ts";
53
- import type { ModeStateSlice } from "./modes-cmd.ts";
54
- import type { Renderer, SlashCommand } from "./renderer.ts";
55
- import { compactionNote, type CompactionEvent } from "./replay-marker.ts";
56
-
57
- /** palette + /help entries, appended to TUI_COMMANDS (reserved against custom commands like the rest).
58
- * rovecode's SlashCommand carries a `group` — the /help topic (info-cmd.ts). /compact and /copy are
59
- * session-scope facts (what the context holds, what to copy out of it); /init writes a project file,
60
- * /clear starts over, so they sit with files & history and session. */
61
- export const CONTEXT_COMMANDS: SlashCommand[] = [
62
- { name: "compact", description: "Compact the context now: /compact [focus] — the automatic strategy set, marker persisted", group: "session" },
63
- { name: "clear", description: "Fresh session in place (transcript emptied; the previous session is kept — /resume <id>)", group: "session" },
64
- { name: "init", description: "Analyse the repo and write AGENTS.md (an existing one gets targeted improvements)", group: "files & history" },
65
- { name: "copy", description: "Copy the last assistant message to the clipboard: /copy [n] (n-th from the end)", group: "session" },
66
- ];
67
-
68
- /** a session below this many messages on the active path is "short": one note, no plan, no model call */
69
- export const MIN_COMPACT_MESSAGES = 6;
70
- const BUSY_WARN = "finish or interrupt the run first (Esc)";
71
-
72
- export interface ContextCmdCtx {
73
- renderer: Renderer;
74
- cwd: string;
75
- busy(): boolean;
76
- /** the ACTIVE session store, read live */
77
- store(): SessionStore;
78
- modes: ModeManager;
79
- /** the app's status slice (mode/model/provider/busy) — mutated in place like modes-cmd does */
80
- state: ModeStateSlice;
81
- /** the modes config's defaultMode ?? "act": what a session with no mode entry resumes to */
82
- defaultMode: AgentMode;
83
- /** the RunConfig a run would start with right now (rt.buildCfg) — the compaction strategy/budget source */
84
- buildCfg(): RunConfig;
85
- /** THIS TREE: no summarizer is wired anywhere (see the header), so this stays undefined in the TUI;
86
- * the field exists so #68's runtime wiring has a seam to land in and the tests can inject one.
87
- * rovecode's Summarizer shape: (texts: string[]) => Promise<string> */
88
- summarize?: CompactionCtx["summarize"];
89
- /** the app's interrupt target (Esc / ⌃c / quit abort it) — bound for /compact's lifetime, then
90
- * cleared with null (ShellCtx.bindAbort shape) */
91
- bindAbort?(ac: AbortController | null): void;
92
- /** rebind the app to session id (store, memory, mode) and replay it (app.ts switchSession) */
93
- switchSession(id: string, announce?: boolean): void;
94
- replayHistory(): void;
95
- refreshUsage(): void;
96
- pushStatus(): void;
97
- /** the plain user-turn path (app.ts submit) */
98
- submit(text: string): Promise<void>;
99
- /** test seam: platform + spawner for /copy; default = the real chain */
100
- clipboard?: ClipboardOptions;
101
- }
102
-
103
- // ---------- /compact ----------
104
-
105
- export type CompactOutcome =
106
- | { kind: "compacted"; event: CompactionEvent; fallbackFrom?: CompactionStrategy; dropped: number; kept: number }
107
- | { kind: "nothing"; reason: string };
108
-
109
- /** the surface's half of the loop's CompactionCtx (loop.ts:215) — what it wires for automatic compaction */
110
- export interface CompactDeps { model?: CompactionCtx["model"]; summarize?: CompactionCtx["summarize"]; native?: CompactionCtx["native"]; signal?: AbortSignal }
111
-
112
- /** The on-disk form of copied parts: a hydrated sidecar path (absolute, under <session>/attachments)
113
- * goes back to the canonical `attachments/<file>` — a persisted absolute path is dropped at load
114
- * (session-images.ts F3), so re-appending the in-memory form would silently lose the image. */
115
- function persistableParts(parts: readonly MessagePart[]): MessagePart[] {
116
- return parts.map((p) => p.kind === "image" && p.path !== undefined && isAbsolute(p.path) ? { ...p, path: `${ATTACHMENTS_DIR}/${basename(p.path)}` } : p);
117
- }
118
-
119
- /** Compact the store's active path NOW through the ONE strategy set and make the result durable
120
- * (header). Pure over (store, cfg, deps): no renderer, so the --plain REPL shares it. */
121
- export async function compactSession(store: SessionStore, cfg: RunConfig, deps: CompactDeps = {}): Promise<CompactOutcome> {
122
- const history = store.messages();
123
- const n = history.length;
124
- if (n < MIN_COMPACT_MESSAGES) return { kind: "nothing", reason: `nothing to compact — ${n} message${n === 1 ? "" : "s"} on the active path (compaction needs at least ${MIN_COMPACT_MESSAGES})` };
125
- const tokenText = (m: Message): string => partsTokenText(m.parts);
126
- const tokensOf = (ms: readonly Message[]): number => ms.reduce((t, m) => t + estimateTokens(tokenText(m)), 0);
127
- // PORT NOTE: aion passes trigger "manual"; rovecode's CompactionTrigger is "speculative" |
128
- // "emergency" (aion's "manual" arrives with #68's core work, owned by 2d). "speculative" is the
129
- // honest stand-in: a threshold-triggered plan — and the marker this writes says strategy + token
130
- // counts only (compactionNote), so no surface reads the trigger word.
131
- const cctx: CompactionCtx = { trigger: "speculative", tokenText, summarize: deps.summarize, native: deps.native, model: deps.model, signal: deps.signal };
132
- const plan = planCompaction(history, cfg, cctx);
133
- const out = plan ? await applyCompaction(history, plan, cfg, cctx) : null;
134
- if (!out) return { kind: "nothing", reason: `nothing to compact — ${cfg.compactionStrategy ?? DEFAULT_COMPACTION_STRATEGY} found nothing droppable (${tokensOf(history)} tokens)` };
135
- const tokensBefore = tokensOf(history);
136
- const tokensAfter = tokensOf(out.history);
137
- // durable branch: fresh-id copies (the same ids would be duplicate-id corruption) chained under a
138
- // new root; the stage is parked meanwhile — append() folds staged attachments into ANY user message
139
- const staged = store.stagedAttachments;
140
- store.stageAttachments([]);
141
- const prevLeaf = history.at(-1)?.id;
142
- let parentId: string | null = null;
143
- try {
144
- for (const m of out.history) {
145
- const copy: Message = { ...m, id: randomUUID(), parentId, parts: persistableParts(m.parts) };
146
- store.append(copy);
147
- parentId = copy.id;
148
- }
149
- if (parentId !== null) store.branch(parentId); // durable leaf (meta.json), like /new — a resume lands on the compacted branch
150
- } catch (e) {
151
- if (prevLeaf !== undefined) store.branch(prevLeaf); // a half-written branch is abandoned, the old path stays active
152
- throw e;
153
- } finally {
154
- store.stageAttachments(staged);
155
- }
156
- // the marker, exactly as loop.ts persists the automatic one (rovecode's CompactionEvent has no
157
- // fallback tag/usage fields — those arrive with #68; the marker already shows before → after tokens)
158
- // the marker, exactly as loop.ts persists the automatic one (loop.ts:216-217). trigger is omitted:
159
- // rovecode's RunEvent types it "speculative" | "emergency" only, and a manual compaction has no
160
- // honest word in that union yet (#68) — absent beats a false one, and compactionNote renders
161
- // strategy + counts, never the trigger.
162
- const event: CompactionEvent = { type: "compaction", strategy: out.strategy, tokensBefore, tokensAfter };
163
- store.appendEvent(event);
164
- const original = new Set(history.map((m) => m.id));
165
- const kept = out.history.filter((m) => original.has(m.id)).length;
166
- return { kind: "compacted", event, dropped: n - kept, kept, ...(out.fallbackFrom ? { fallbackFrom: out.fallbackFrom } : {}) };
167
- }
168
-
169
- /** The detail line after the marker's before/after line. rovecode's plan-time fallback carries
170
- * `fallbackFrom` ("head-summarize" when no summarizer is wired — THIS TREE'S normal case) but no
171
- * reason text; the wording names the wiring, which is the thing missing here (aion #68 adds the
172
- * apply-time reason with the fenced summarizer). */
173
- export function compactDetails(res: Extract<CompactOutcome, { kind: "compacted" }>, focus: string): string {
174
- const s = (k: number): string => (k === 1 ? "" : "s");
175
- return `${res.dropped} message${s(res.dropped)} dropped, ${res.kept} kept — the earlier turns stay in the session file on the previous branch` +
176
- (res.fallbackFrom ? ` · ${res.fallbackFrom} could not run on this surface (no summarizer wired) — ${res.event.strategy} ran instead` : "") +
177
- (focus ? ` · focus "${focus}" not applied — the compaction strategies take no instructions` : "");
178
- }
179
-
180
- /** Both lines for a surface without replay (the --plain REPL): the marker wording, then the details. */
181
- export function compactNoteLines(res: CompactOutcome, focus: string): string[] {
182
- return res.kind === "nothing" ? [res.reason] : [compactionNote(res.event), compactDetails(res, focus)];
183
- }
184
-
185
- /** /compact [focus] — busy-gated, and it HOLDS the busy flag while it runs (a concurrent submit queues
186
- * as steering instead of starting a run — a future summarizer would be a model call) without a
187
- * renderer spinner: there is no run for the surface to settle (the sextant would mark one "stopped").
188
- * The transcript is replayed from the compacted path, so what the user sees is what the model gets:
189
- * the summary/marker note, the kept turns, then the persisted marker line (before → after tokens). */
190
- export async function cmdCompact(ctx: ContextCmdCtx, focus: string): Promise<void> {
191
- if (ctx.busy()) { ctx.renderer.addSystemNote(BUSY_WARN, "warn"); return; }
192
- const store = ctx.store();
193
- ctx.state.busy = true;
194
- const ac = new AbortController(); // Esc / ⌃c abort the in-flight summarize call — bound like `!cmd` (shell-cmd.ts), cleared after
195
- ctx.bindAbort?.(ac);
196
- let res: CompactOutcome;
197
- try {
198
- res = await compactSession(store, ctx.buildCfg(), { model: ctx.modes.modelFor(), summarize: ctx.summarize, signal: ac.signal }); // the runtime's summarizer, the command's signal
199
- } catch (e) {
200
- res = { kind: "nothing", reason: `compaction failed: ${e instanceof Error ? e.message : String(e)} — the session is unchanged` };
201
- } finally {
202
- ctx.bindAbort?.(null);
203
- ctx.state.busy = false;
204
- }
205
- if (res.kind === "nothing") { ctx.renderer.addSystemNote(res.reason, res.reason.startsWith("compaction failed") ? "error" : "info"); ctx.pushStatus(); return; }
206
- // a mode entry that lived in the dropped head would make a resume land in the default mode:
207
- // re-record the live mode on the new branch (durable entry, same shape flushModeSwitch writes)
208
- const resumesAs = modeFromEntries(store.messages()) ?? ctx.defaultMode;
209
- if (resumesAs !== ctx.modes.mode) store.append(buildModeChangeEntry({ from: resumesAs, to: ctx.modes.mode }, store.messages().at(-1)?.id ?? null));
210
- ctx.replayHistory(); ctx.refreshUsage(); ctx.pushStatus();
211
- ctx.renderer.addSystemNote(compactDetails(res, focus));
212
- }
213
-
214
- // ---------- /clear ----------
215
-
216
- /** /clear — a fresh session in place. switchSession restores the CONFIG default mode for a session
217
- * with no entries; a clear keeps the LIVE mode, recorded the way /plan records it (a pending notice
218
- * the next submit — or quit — flushes as a durable entry), and the in-memory model slots carry over. */
219
- export function cmdClear(ctx: ContextCmdCtx): void {
220
- if (ctx.busy()) { ctx.renderer.addSystemNote(BUSY_WARN, "warn"); return; }
221
- const old = ctx.store().id;
222
- const mode = ctx.modes.mode;
223
- const id = randomUUID();
224
- ctx.switchSession(id, false);
225
- if (ctx.modes.mode !== mode) {
226
- ctx.modes.toggle(mode);
227
- const cur = ctx.modes.modelFor();
228
- ctx.state.mode = mode; ctx.state.model = cur.model; ctx.state.provider = cur.provider;
229
- ctx.pushStatus();
230
- }
231
- ctx.renderer.addSystemNote(`cleared — fresh session ${id.slice(0, 8)} in the same directory; session ${old.slice(0, 8)} kept (/resume ${old.slice(0, 8)})`);
232
- }
233
-
234
- // ---------- /init ----------
235
-
236
- /** /init — the built-in AGENTS.md prompt through the plain user-turn path. Busy-gated like a custom
237
- * command (runCustomCommand): a built-in prompt is never queued as steering. */
238
- export async function cmdInit(ctx: ContextCmdCtx): Promise<void> {
239
- if (ctx.busy()) { ctx.renderer.addSystemNote(BUSY_WARN, "warn"); return; }
240
- const prompt = expandBuiltinSlash("/init", ctx.cwd);
241
- if (prompt === undefined) return; // unreachable: "init" is in BUILTIN_PROMPTS
242
- ctx.renderer.addSystemNote(existsSync(join(ctx.cwd, INIT_TARGET))
243
- ? `${INIT_TARGET} exists — asking the agent for targeted improvements (edits go through the usual approval)`
244
- : `analysing the repository to write ${INIT_TARGET} (the write goes through the usual approval)`);
245
- await ctx.submit(prompt);
246
- }
247
-
248
- // ---------- /copy ----------
249
-
250
- /** The n-th-from-the-end assistant message WITH text (tool-call-only turns do not count) → the
251
- * clipboard; one note either way, never a throw. Shared with the --plain REPL. */
252
- export async function copyAssistantMessage(messages: readonly Message[], n: number, opts: ClipboardOptions = {}): Promise<{ text: string; tone: "info" | "warn" }> {
253
- const texts = messages.filter((m) => m.role === "assistant").map((m) => partsText(m.parts)).filter((t) => t.trim() !== "");
254
- if (texts.length === 0) return { text: "nothing to copy — no assistant message with text yet", tone: "warn" };
255
- const pick = texts[texts.length - n];
256
- if (pick === undefined) return { text: `nothing to copy — only ${texts.length} assistant message${texts.length === 1 ? "" : "s"} with text (asked for number ${n} from the end)`, tone: "warn" };
257
- const res = await copyToClipboard(pick, opts);
258
- if (res.ok) return { text: `copied ${n === 1 ? "the last assistant message" : `assistant message ${n} from the end`} (${pick.length} chars) to the clipboard via ${res.tool}`, tone: "info" };
259
- return { text: `clipboard unavailable — tried ${res.tried.join(", ") || "nothing"}${res.detail ? ` (${res.detail})` : ""}; the text stays in the transcript`, tone: "warn" };
260
- }
261
-
262
- /** /copy [n] — n defaults to 1 (the last); any other argument is a usage note. */
263
- export async function cmdCopy(ctx: ContextCmdCtx, arg: string): Promise<void> {
264
- if (ctx.busy()) { ctx.renderer.addSystemNote(BUSY_WARN, "warn"); return; }
265
- const n = arg === "" ? 1 : /^[1-9]\d*$/.test(arg) ? Number(arg) : NaN;
266
- if (Number.isNaN(n)) { ctx.renderer.addSystemNote("usage: /copy [n] — copies the last assistant message; n counts back from the latest (2 = the one before)", "warn"); return; }
267
- const line = await copyAssistantMessage(ctx.store().messages(), n, ctx.clipboard ?? {});
268
- ctx.renderer.addSystemNote(line.text, line.tone);
269
- }
270
-
271
- // ---------- dispatch ----------
272
-
273
- /** handleSlash hook for the four names (CONTEXT_COMMANDS); fire-and-forget like the other async slash commands. */
274
- export async function runContextCommand(ctx: ContextCmdCtx, cmd: string, arg: string): Promise<void> {
275
- switch (cmd) {
276
- case "compact": return cmdCompact(ctx, arg);
277
- case "clear": return cmdClear(ctx);
278
- case "init": return cmdInit(ctx);
279
- case "copy": return cmdCopy(ctx, arg);
280
- default: return;
281
- }
282
- }