rovecode 0.3.3 → 0.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (491) hide show
  1. package/CHANGELOG.md +512 -0
  2. package/README.md +33 -48
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-dybnr56b.js +2 -0
  5. package/dist/cli/ask-user-p8hq4xgj.js +2 -0
  6. package/dist/cli/auth-login-ewpgw5sm.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-xv3ypwev.js +9 -0
  9. package/dist/cli/catalog-737wb2s0.js +2 -0
  10. package/dist/cli/cli-arhg40m0.js +2 -0
  11. package/dist/cli/client-cf2pxx8q.js +2 -0
  12. package/dist/cli/commands-3p7e4xxs.js +2 -0
  13. package/dist/cli/connect-3q93d7cb.js +2 -0
  14. package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
  15. package/dist/cli/context-report-hbw9zfes.js +2 -0
  16. package/dist/cli/count-remote-mby98cd0.js +2 -0
  17. package/dist/cli/design-122y0axd.js +2 -0
  18. package/dist/cli/dispatch-b4egzvvh.js +2 -0
  19. package/dist/cli/doctor-x4jkv72e.js +3 -0
  20. package/dist/cli/executor-ftvg6tsy.js +2 -0
  21. package/dist/cli/export-pdgdhkch.js +2 -0
  22. package/dist/cli/files-cez9a96p.js +2 -0
  23. package/dist/cli/gauntlet-r3xxaszc.js +2 -0
  24. package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
  25. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
  26. package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
  27. package/dist/cli/hashline-ewg5hbe3.js +2 -0
  28. package/dist/cli/http-n0kehsk8.js +5 -0
  29. package/dist/cli/index-z5qt1s76.js +2 -0
  30. package/dist/cli/install-80mp63kx.js +2 -0
  31. package/dist/cli/loop-12twjcat.js +2 -0
  32. package/dist/cli/main-01pv9206.js +4 -0
  33. package/dist/cli/main-0jys2ccn.js +3 -0
  34. package/dist/cli/main-1ztz6fkj.js +10 -0
  35. package/dist/cli/main-23q7cmww.js +9 -0
  36. package/dist/cli/main-2rzbexn2.js +3 -0
  37. package/dist/cli/main-2wyax8k9.js +9 -0
  38. package/dist/cli/main-2yeveeve.js +6 -0
  39. package/dist/cli/main-2z3dek0b.js +3 -0
  40. package/dist/cli/main-2zgsknth.js +3 -0
  41. package/dist/cli/main-45ejth3a.js +4 -0
  42. package/dist/cli/main-45rn3trk.js +22 -0
  43. package/dist/cli/main-4p4e2w7x.js +4 -0
  44. package/dist/cli/main-4y0tnfpa.js +16 -0
  45. package/dist/cli/main-5py0rkmc.js +4 -0
  46. package/dist/cli/main-6dtqmbt6.js +7 -0
  47. package/dist/cli/main-6h9x282m.js +4 -0
  48. package/dist/cli/main-6vjeds42.js +3 -0
  49. package/dist/cli/main-78gq4bt9.js +6 -0
  50. package/dist/cli/main-7jd5vh3x.js +4 -0
  51. package/dist/cli/main-7kt6r53y.js +4 -0
  52. package/dist/cli/main-8c1tbazx.js +58 -0
  53. package/dist/cli/main-9a9rnh47.js +19 -0
  54. package/dist/cli/main-9ht36z12.js +3 -0
  55. package/dist/cli/main-a2yfvcy9.js +7 -0
  56. package/dist/cli/main-a3f51n0x.js +5 -0
  57. package/dist/cli/main-b8zq261k.js +3 -0
  58. package/dist/cli/main-bxtvnf6d.js +13 -0
  59. package/dist/cli/main-edxc3yzt.js +4 -0
  60. package/dist/cli/main-evgz4mp5.js +21 -0
  61. package/dist/cli/main-f33fc5je.js +9 -0
  62. package/dist/cli/main-fvnpq46y.js +12 -0
  63. package/dist/cli/main-gbbty4d4.js +3 -0
  64. package/dist/cli/main-gth53dnt.js +25 -0
  65. package/dist/cli/main-hqbz10aw.js +9 -0
  66. package/dist/cli/main-hrrvcfan.js +38 -0
  67. package/dist/cli/main-hzwtsb2m.js +5 -0
  68. package/dist/cli/main-j7ttv0sd.js +34 -0
  69. package/dist/cli/main-jak598k9.js +5 -0
  70. package/dist/cli/main-kba6zeyd.js +6 -0
  71. package/dist/cli/main-kwwsz6rq.js +3 -0
  72. package/dist/cli/main-m8vm17zq.js +3 -0
  73. package/dist/cli/main-mg4f96e1.js +3 -0
  74. package/dist/cli/main-mg9b20ac.js +18 -0
  75. package/dist/cli/main-mgb9ccnx.js +3 -0
  76. package/dist/cli/main-mjt2p7aj.js +3 -0
  77. package/dist/cli/main-n6qrdbmy.js +3 -0
  78. package/dist/cli/main-na7wse0x.js +5 -0
  79. package/dist/cli/main-nqveez48.js +4 -0
  80. package/dist/cli/main-ntqef02r.js +10 -0
  81. package/dist/cli/main-nvc3yjay.js +136 -0
  82. package/dist/cli/main-p0cfn6nr.js +16 -0
  83. package/dist/cli/main-qj2djy17.js +19 -0
  84. package/dist/cli/main-qsevpgsv.js +3 -0
  85. package/dist/cli/main-qvarybsp.js +3 -0
  86. package/dist/cli/main-rebtt91r.js +5 -0
  87. package/dist/cli/main-rpg7h8mb.js +3 -0
  88. package/dist/cli/main-rsy72qmw.js +15 -0
  89. package/dist/cli/main-rvetps99.js +18 -0
  90. package/dist/cli/main-s4bb0jav.js +3 -0
  91. package/dist/cli/main-s9v8k74e.js +3 -0
  92. package/dist/cli/main-tjvwmscs.js +3 -0
  93. package/dist/cli/main-tkgarpjj.js +4 -0
  94. package/dist/cli/main-v8y60bb2.js +3 -0
  95. package/dist/cli/main-vhrrq337.js +3 -0
  96. package/dist/cli/main-vp2dfb7s.js +4 -0
  97. package/dist/cli/main-vqbr22sz.js +8 -0
  98. package/dist/cli/main-vxnwe5xx.js +18 -0
  99. package/dist/cli/main-wgph00xf.js +5 -0
  100. package/dist/cli/main-wk2csfnj.js +5 -0
  101. package/dist/cli/main-wm997zjx.js +3 -0
  102. package/dist/cli/main-wpkyraxh.js +3 -0
  103. package/dist/cli/main-wqt32p5x.js +4 -0
  104. package/dist/cli/main-x9ct6y1a.js +3 -0
  105. package/dist/cli/main-xfekqh9m.js +7 -0
  106. package/dist/cli/main-xt9zc3n6.js +7 -0
  107. package/dist/cli/main-xx2z3zh5.js +4 -0
  108. package/dist/cli/main-y5c82rxr.js +3 -0
  109. package/dist/cli/main-yrjt2sqt.js +14 -0
  110. package/dist/cli/main-ys6zj3yr.js +3 -0
  111. package/dist/cli/main-ywbxshqc.js +8 -0
  112. package/dist/cli/main-z13755t8.js +25 -0
  113. package/dist/cli/main-zc7pyrbj.js +4 -0
  114. package/dist/cli/main.js +279 -0
  115. package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
  116. package/dist/cli/mcp-login-bthtfpt7.js +2 -0
  117. package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
  118. package/dist/cli/notify-54v5z9dz.js +2 -0
  119. package/dist/cli/oauth-g5gme95c.js +2 -0
  120. package/dist/cli/output-satndjap.js +16 -0
  121. package/dist/cli/profiles-sfhpbq3m.js +2 -0
  122. package/dist/cli/provider-config-hv3xtdt4.js +2 -0
  123. package/dist/cli/provider-kwzq6g84.js +2 -0
  124. package/dist/cli/registry-fh0hdnyn.js +2 -0
  125. package/dist/cli/registry-y1y8e94r.js +2 -0
  126. package/dist/cli/repl-t4z03mqq.js +11 -0
  127. package/dist/cli/resume-fqt4chg8.js +2 -0
  128. package/dist/cli/run-flags-rysbag9t.js +2 -0
  129. package/dist/cli/runtime-j19fjbsa.js +2 -0
  130. package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
  131. package/dist/cli/server-r0b6bksk.js +5 -0
  132. package/dist/cli/session-arg-txmn5g4x.js +2 -0
  133. package/dist/cli/session-ed250d9j.js +2 -0
  134. package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
  135. package/dist/cli/settings-y9rzcqx8.js +2 -0
  136. package/dist/cli/setup-jmbr11j0.js +2 -0
  137. package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
  138. package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
  139. package/dist/cli/smoke-1bg937kx.js +8 -0
  140. package/dist/cli/start-chat-p01cdks3.js +12 -0
  141. package/dist/cli/stream-4wmyaypz.js +2 -0
  142. package/dist/cli/task-eg4s093s.js +2 -0
  143. package/dist/cli/tasks-12v9rr9k.js +2 -0
  144. package/dist/cli/thinking-a5ngvqyh.js +2 -0
  145. package/dist/cli/todo-1wxpcecx.js +2 -0
  146. package/dist/cli/tools-2ftsya7w.js +2 -0
  147. package/dist/cli/tools-x1tj4fxm.js +2 -0
  148. package/dist/cli/trust-cmd-hccxehzb.js +2 -0
  149. package/dist/cli/update-check-ygt3vd7m.js +2 -0
  150. package/dist/cli/update-cmd-v23qhr8c.js +2 -0
  151. package/dist/cli/voice-g1gtck92.js +2 -0
  152. package/dist/cli/webfetch-0nnrjgb5.js +2 -0
  153. package/dist/cli/websearch-f0vr2p7d.js +2 -0
  154. package/dist/cli/workspace-9rq1w4ta.js +2 -0
  155. package/dist/lib/index.js +62 -0
  156. package/dist/lib/models-index.json +1 -0
  157. package/dist/lib/plugins.js +6 -0
  158. package/dist/lib/providers.js +17 -0
  159. package/dist/lib/public-api.js +20 -0
  160. package/dist/rovecode.exe +4 -0
  161. package/package.json +83 -72
  162. package/bin/rovecode.ts +0 -21
  163. package/src/acp/server.ts +0 -374
  164. package/src/cli/auth-login.ts +0 -122
  165. package/src/cli/connect.ts +0 -244
  166. package/src/cli/context-cmd.ts +0 -199
  167. package/src/cli/dispatch.ts +0 -82
  168. package/src/cli/doctor.ts +0 -362
  169. package/src/cli/export.ts +0 -276
  170. package/src/cli/help.ts +0 -293
  171. package/src/cli/is-tui-invocation.ts +0 -8
  172. package/src/cli/main.ts +0 -583
  173. package/src/cli/market-cmd.ts +0 -658
  174. package/src/cli/mcp-login.ts +0 -141
  175. package/src/cli/mcp-market-cmd.ts +0 -302
  176. package/src/cli/output.ts +0 -382
  177. package/src/cli/repl.ts +0 -250
  178. package/src/cli/repomap-root.ts +0 -14
  179. package/src/cli/resume.ts +0 -57
  180. package/src/cli/run-flags.ts +0 -43
  181. package/src/cli/run-limits.ts +0 -78
  182. package/src/cli/runtime.ts +0 -931
  183. package/src/cli/session-arg.ts +0 -30
  184. package/src/cli/sessions-cmd.ts +0 -145
  185. package/src/cli/setup.ts +0 -153
  186. package/src/cli/skills-cmd.ts +0 -194
  187. package/src/cli/start-chat.ts +0 -65
  188. package/src/cli/trust-cmd.ts +0 -52
  189. package/src/coding/bash.ts +0 -148
  190. package/src/coding/checkpoints.ts +0 -327
  191. package/src/coding/diff.ts +0 -138
  192. package/src/coding/files.ts +0 -341
  193. package/src/coding/hashline.ts +0 -274
  194. package/src/coding/lsp-gate.ts +0 -254
  195. package/src/coding/lsp-servers.ts +0 -147
  196. package/src/coding/lsp.ts +0 -283
  197. package/src/coding/repomap-cache.ts +0 -99
  198. package/src/coding/repomap-files.ts +0 -192
  199. package/src/coding/repomap.ts +0 -481
  200. package/src/core/agents.ts +0 -255
  201. package/src/core/compaction.ts +0 -259
  202. package/src/core/config.ts +0 -289
  203. package/src/core/context-report.ts +0 -228
  204. package/src/core/context.ts +0 -60
  205. package/src/core/count-remote.ts +0 -107
  206. package/src/core/execpolicy-rules.ts +0 -196
  207. package/src/core/execpolicy.ts +0 -385
  208. package/src/core/executor.ts +0 -454
  209. package/src/core/guardrails.ts +0 -400
  210. package/src/core/hooks.ts +0 -411
  211. package/src/core/images.ts +0 -230
  212. package/src/core/intro.ts +0 -266
  213. package/src/core/loop.ts +0 -567
  214. package/src/core/modes.ts +0 -372
  215. package/src/core/orchestrator.ts +0 -245
  216. package/src/core/proc-group.ts +0 -48
  217. package/src/core/project-trust.ts +0 -98
  218. package/src/core/reflection.ts +0 -165
  219. package/src/core/sandbox-config.ts +0 -186
  220. package/src/core/session-id.ts +0 -24
  221. package/src/core/session-images.ts +0 -73
  222. package/src/core/session-ops.ts +0 -183
  223. package/src/core/session-text.ts +0 -29
  224. package/src/core/session.ts +0 -469
  225. package/src/core/settings.ts +0 -170
  226. package/src/core/tasks.ts +0 -646
  227. package/src/core/token-scale.ts +0 -108
  228. package/src/core/tools.ts +0 -309
  229. package/src/core/trust.ts +0 -104
  230. package/src/core/types.ts +0 -330
  231. package/src/core/update-check.ts +0 -171
  232. package/src/core/usage.ts +0 -204
  233. package/src/core/validate.ts +0 -121
  234. package/src/core/verify-gate.ts +0 -159
  235. package/src/core/verify.ts +0 -236
  236. package/src/core/voice.ts +0 -158
  237. package/src/core/win-job.ts +0 -183
  238. package/src/core/workspace.ts +0 -184
  239. package/src/design/audit.ts +0 -797
  240. package/src/design/direction.ts +0 -190
  241. package/src/design/rules.ts +0 -157
  242. package/src/eval/bench.ts +0 -150
  243. package/src/eval/gauntlet-runner.ts +0 -215
  244. package/src/eval/gauntlet-support.ts +0 -84
  245. package/src/eval/gauntlet-wave3.ts +0 -269
  246. package/src/eval/gauntlet-wave4.ts +0 -217
  247. package/src/eval/gauntlet.ts +0 -253
  248. package/src/index.ts +0 -17
  249. package/src/lanes/agy.ts +0 -95
  250. package/src/lanes/approval.ts +0 -24
  251. package/src/lanes/claude.ts +0 -129
  252. package/src/lanes/codex.ts +0 -127
  253. package/src/lanes/events.ts +0 -130
  254. package/src/lanes/job.ts +0 -142
  255. package/src/lanes/opencode.ts +0 -122
  256. package/src/lanes/process.ts +0 -184
  257. package/src/lanes/progress.ts +0 -183
  258. package/src/lanes/registry.ts +0 -178
  259. package/src/lanes/runner.ts +0 -124
  260. package/src/lanes/types.ts +0 -112
  261. package/src/market/catalogs/mcp-docs.json +0 -111
  262. package/src/market/catalogs/plugins.json +0 -111
  263. package/src/market/catalogs/skills.json +0 -478
  264. package/src/market/clone.ts +0 -72
  265. package/src/market/context-cost.ts +0 -121
  266. package/src/market/digest.ts +0 -106
  267. package/src/market/index.ts +0 -22
  268. package/src/market/install.ts +0 -578
  269. package/src/market/manifest.ts +0 -187
  270. package/src/market/prereq.ts +0 -145
  271. package/src/market/registry.ts +0 -363
  272. package/src/market/resolve.ts +0 -111
  273. package/src/market/types.ts +0 -236
  274. package/src/market/validate.ts +0 -227
  275. package/src/mcp/client.ts +0 -449
  276. package/src/mcp/config.ts +0 -252
  277. package/src/mcp/local-package.ts +0 -211
  278. package/src/mcp/market-catalog.ts +0 -84
  279. package/src/mcp/market-install.ts +0 -289
  280. package/src/mcp/market.ts +0 -362
  281. package/src/mcp/oauth.ts +0 -251
  282. package/src/mcp/prompts-resources.ts +0 -249
  283. package/src/mcp/shared.ts +0 -149
  284. package/src/mcp/status.ts +0 -67
  285. package/src/mcp/tools.ts +0 -275
  286. package/src/mcp/transport.ts +0 -122
  287. package/src/mcp/trust.ts +0 -25
  288. package/src/memory/blocks.ts +0 -278
  289. package/src/memory/recall.ts +0 -355
  290. package/src/memory/scope.ts +0 -182
  291. package/src/memory/store.ts +0 -105
  292. package/src/memory/tools.ts +0 -99
  293. package/src/plugins/cli.ts +0 -119
  294. package/src/plugins/discover.ts +0 -108
  295. package/src/plugins/index.ts +0 -50
  296. package/src/plugins/install.ts +0 -184
  297. package/src/plugins/load.ts +0 -124
  298. package/src/plugins/manifest.ts +0 -92
  299. package/src/plugins/state.ts +0 -83
  300. package/src/providers/auth.ts +0 -408
  301. package/src/providers/cache.ts +0 -223
  302. package/src/providers/catalog-local.ts +0 -160
  303. package/src/providers/catalog.ts +0 -421
  304. package/src/providers/middleware-context.ts +0 -86
  305. package/src/providers/middleware.ts +0 -373
  306. package/src/providers/model-list.ts +0 -23
  307. package/src/providers/oauth/common.ts +0 -105
  308. package/src/providers/oauth/device-code.ts +0 -107
  309. package/src/providers/oauth/github-copilot.ts +0 -146
  310. package/src/providers/oauth/loopback.ts +0 -158
  311. package/src/providers/oauth/openai.ts +0 -163
  312. package/src/providers/oauth/openrouter.ts +0 -89
  313. package/src/providers/oauth/pkce.ts +0 -45
  314. package/src/providers/oauth/registry.ts +0 -39
  315. package/src/providers/oauth/seam.ts +0 -89
  316. package/src/providers/profile-glm53.ts +0 -111
  317. package/src/providers/profile-sonnet5-persona.ts +0 -65
  318. package/src/providers/profile-sonnet5-voice.ts +0 -23
  319. package/src/providers/profiles.ts +0 -156
  320. package/src/providers/provider-config.ts +0 -311
  321. package/src/providers/registry.ts +0 -333
  322. package/src/providers/responses.ts +0 -209
  323. package/src/providers/retry.ts +0 -234
  324. package/src/providers/router.ts +0 -294
  325. package/src/providers/sse.ts +0 -26
  326. package/src/providers/stream-errors.ts +0 -117
  327. package/src/providers/stream.ts +0 -566
  328. package/src/providers/thinking.ts +0 -189
  329. package/src/providers/wire-messages.ts +0 -129
  330. package/src/providers/wire-responses.ts +0 -79
  331. package/src/providers/wire-select.ts +0 -53
  332. package/src/server/http.ts +0 -291
  333. package/src/server/openapi.ts +0 -246
  334. package/src/sextant/card-hits.ts +0 -102
  335. package/src/sextant/card-keys.ts +0 -55
  336. package/src/sextant/context-source.ts +0 -157
  337. package/src/sextant/crew-cards.ts +0 -350
  338. package/src/sextant/draw-agents.ts +0 -273
  339. package/src/sextant/draw-code.ts +0 -388
  340. package/src/sextant/draw-context.ts +0 -222
  341. package/src/sextant/draw-frame.ts +0 -164
  342. package/src/sextant/draw-market.ts +0 -573
  343. package/src/sextant/draw-messages.ts +0 -386
  344. package/src/sextant/draw-pet.ts +0 -230
  345. package/src/sextant/draw-plan.ts +0 -187
  346. package/src/sextant/draw-tabs.ts +0 -85
  347. package/src/sextant/draw-util.ts +0 -65
  348. package/src/sextant/draw-wizard.ts +0 -378
  349. package/src/sextant/engine.ts +0 -230
  350. package/src/sextant/frame-hits.ts +0 -25
  351. package/src/sextant/frame.ts +0 -101
  352. package/src/sextant/git-status.ts +0 -197
  353. package/src/sextant/grid.ts +0 -59
  354. package/src/sextant/input.ts +0 -119
  355. package/src/sextant/keys.ts +0 -521
  356. package/src/sextant/layout.ts +0 -86
  357. package/src/sextant/local-commands.ts +0 -169
  358. package/src/sextant/market-source.ts +0 -287
  359. package/src/sextant/mentions.ts +0 -200
  360. package/src/sextant/message-hits.ts +0 -26
  361. package/src/sextant/model.ts +0 -387
  362. package/src/sextant/overlays.ts +0 -456
  363. package/src/sextant/panel-hits.ts +0 -38
  364. package/src/sextant/pet.ts +0 -399
  365. package/src/sextant/screen.ts +0 -324
  366. package/src/sextant/scroll-hits.ts +0 -66
  367. package/src/sextant/scrollbar.ts +0 -82
  368. package/src/sextant/sextant-bridge.ts +0 -174
  369. package/src/sextant/sextant-cards.ts +0 -142
  370. package/src/sextant/sextant-diff-base.ts +0 -63
  371. package/src/sextant/sextant-files.ts +0 -154
  372. package/src/sextant/sextant-frame-loop.ts +0 -335
  373. package/src/sextant/sextant-renderer.ts +0 -574
  374. package/src/sextant/sextant-repo.ts +0 -140
  375. package/src/sextant/theme.ts +0 -66
  376. package/src/sextant/tool-rows.ts +0 -189
  377. package/src/sextant/types.ts +0 -493
  378. package/src/skills/index.ts +0 -387
  379. package/src/skills/pack.ts +0 -220
  380. package/src/skills/spec.ts +0 -162
  381. package/src/skills/tools.ts +0 -69
  382. package/src/skills/versioned.ts +0 -227
  383. package/src/telemetry/otel-export.ts +0 -122
  384. package/src/telemetry/otel-lanes.ts +0 -89
  385. package/src/telemetry/otel-logs.ts +0 -131
  386. package/src/telemetry/otel-metrics.ts +0 -136
  387. package/src/telemetry/otel.ts +0 -397
  388. package/src/telemetry/otlp.ts +0 -76
  389. package/src/tools/ask-user.ts +0 -156
  390. package/src/tools/bash-bg.ts +0 -94
  391. package/src/tools/bash-jobs.ts +0 -237
  392. package/src/tools/design.ts +0 -151
  393. package/src/tools/evalcell.ts +0 -338
  394. package/src/tools/html-text.ts +0 -139
  395. package/src/tools/provider.ts +0 -149
  396. package/src/tools/task.ts +0 -250
  397. package/src/tools/todo.ts +0 -320
  398. package/src/tools/webfetch.ts +0 -332
  399. package/src/tools/websearch.ts +0 -359
  400. package/src/tui/agents-cmd.ts +0 -41
  401. package/src/tui/app.ts +0 -749
  402. package/src/tui/attach.ts +0 -127
  403. package/src/tui/boot-notes.ts +0 -41
  404. package/src/tui/builtin-prompts.ts +0 -59
  405. package/src/tui/checkpoints-cmd.ts +0 -70
  406. package/src/tui/clipboard-image.ts +0 -81
  407. package/src/tui/clipboard.ts +0 -78
  408. package/src/tui/commands.ts +0 -283
  409. package/src/tui/config-view.ts +0 -53
  410. package/src/tui/context-cmds.ts +0 -282
  411. package/src/tui/cost.ts +0 -108
  412. package/src/tui/crash-guard.ts +0 -173
  413. package/src/tui/focus-terminal.ts +0 -34
  414. package/src/tui/git-cmds.ts +0 -273
  415. package/src/tui/git-plain.ts +0 -58
  416. package/src/tui/info-cmd.ts +0 -150
  417. package/src/tui/input-plain.ts +0 -76
  418. package/src/tui/mcp-cmd.ts +0 -128
  419. package/src/tui/memory-note.ts +0 -77
  420. package/src/tui/modes-cmd.ts +0 -45
  421. package/src/tui/notify-seq.ts +0 -100
  422. package/src/tui/notify.ts +0 -318
  423. package/src/tui/overlays.ts +0 -97
  424. package/src/tui/pi-renderer.ts +0 -428
  425. package/src/tui/providers-cmd.ts +0 -377
  426. package/src/tui/reasoning-view.ts +0 -56
  427. package/src/tui/renderer.ts +0 -128
  428. package/src/tui/replay-marker.ts +0 -29
  429. package/src/tui/session-cmd.ts +0 -148
  430. package/src/tui/session-manage.ts +0 -95
  431. package/src/tui/sextant-attach.ts +0 -102
  432. package/src/tui/sextant-io.ts +0 -202
  433. package/src/tui/sextant-smoke.ts +0 -110
  434. package/src/tui/shell-cmd.ts +0 -158
  435. package/src/tui/smoke.ts +0 -72
  436. package/src/tui/staged-terminal.ts +0 -50
  437. package/src/tui/startup.ts +0 -12
  438. package/src/tui/theme.ts +0 -59
  439. package/src/tui/todo-label.ts +0 -7
  440. package/src/tui/trust-card.ts +0 -107
  441. package/src/tui/tui-commands.ts +0 -87
  442. package/tsconfig.json +0 -30
  443. package/vendor/pi-tui/LICENSE +0 -21
  444. package/vendor/pi-tui/PATCHES.md +0 -12
  445. package/vendor/pi-tui/PROVENANCE.md +0 -12
  446. package/vendor/pi-tui/README.upstream.md +0 -854
  447. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  448. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  449. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  450. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  451. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  452. package/vendor/pi-tui/src/components/box.ts +0 -138
  453. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  454. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  455. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  456. package/vendor/pi-tui/src/components/image.ts +0 -128
  457. package/vendor/pi-tui/src/components/input.ts +0 -448
  458. package/vendor/pi-tui/src/components/loader.ts +0 -93
  459. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  460. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  461. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  462. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  463. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  464. package/vendor/pi-tui/src/components/stack.ts +0 -155
  465. package/vendor/pi-tui/src/components/text.ts +0 -108
  466. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  467. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  468. package/vendor/pi-tui/src/editor-component.ts +0 -75
  469. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  470. package/vendor/pi-tui/src/index.ts +0 -149
  471. package/vendor/pi-tui/src/keybindings.ts +0 -321
  472. package/vendor/pi-tui/src/keys.ts +0 -1402
  473. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  474. package/vendor/pi-tui/src/latex.ts +0 -1381
  475. package/vendor/pi-tui/src/layout-node.ts +0 -52
  476. package/vendor/pi-tui/src/layout.ts +0 -411
  477. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  478. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  479. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  480. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  481. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  482. package/vendor/pi-tui/src/terminal.ts +0 -554
  483. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  484. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  485. package/vendor/pi-tui/src/tui.ts +0 -1264
  486. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  487. package/vendor/pi-tui/src/utils.ts +0 -1327
  488. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  489. package/vendor/pi-tui/test/test-themes.ts +0 -39
  490. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  491. /package/{src/providers → dist/cli}/models-index.json +0 -0
@@ -1,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
- }