rovecode 0.3.2 → 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 -45
  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 +38 -11
  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,55 +0,0 @@
1
- /** Sextant card keys (port #43): the key route of the ONE modal card — ←→↑↓ (and Tab) move the
2
- * selection, Enter resolves it (approval: once / always / deny; question: an option, the free-text
3
- * row or skip), Esc dismisses (deny / null) and the free-text row takes typing and Backspace.
4
- * Ported from the user's own sextant v0.4.0 prototype app.js:1503-1510 and :1434; split out of
5
- * keys.ts for the line budget. Pure state mutation, no clock: the card leaves the state before
6
- * its promise settles, so a re-entrant hook never sees a stale card. */
7
-
8
- import type { CardState, KeyEvent, SextantState } from "./types.ts";
9
-
10
- /** false = not a card key: it falls through to the prompt (typing a follow-up while a card waits) */
11
- export function onCardKey(s: SextantState, c: CardState, ev: KeyEvent): boolean {
12
- const { name } = ev;
13
- const n = c.kind === "approval" ? c.verdicts.length // allow · always · [all edits] · deny
14
- : (c.prompt.options?.length ?? 0) + (c.prompt.allowFreeText !== false ? 1 : 0) + 1; // options · free text · skip
15
- if (name === "left" || name === "up") return move(c, n, -1);
16
- if (name === "right" || name === "down" || name === "tab") return move(c, n, 1);
17
- if (name === "escape") { dismissCard(s); return true; }
18
- if (c.kind === "approval") {
19
- if (name !== "enter") return false;
20
- s.card = null;
21
- c.resolve(c.verdicts[c.selected] ?? "deny");
22
- return true;
23
- }
24
- const opts = c.prompt.options?.length ?? 0, freeIdx = c.prompt.allowFreeText !== false ? opts : -1;
25
- if (name === "enter") {
26
- if (c.selected === n - 1) { dismissCard(s); return true; } // skip
27
- if (c.selected !== freeIdx) { s.card = null; c.resolve({ kind: "option", index: c.selected }); return true; }
28
- const t = c.freeText.trim();
29
- if (t) { s.card = null; c.resolve({ kind: "text", text: t }); }
30
- return true; // an empty free-text row is not an answer yet
31
- }
32
- if (c.selected !== freeIdx) return false;
33
- if (name === "backspace") { // by code point: 😀 is one glyph, two UTF-16 units
34
- c.freeText = [...c.freeText].slice(0, -1).join("");
35
- return true;
36
- }
37
- const ch = name === "space" ? " " : ev.ch;
38
- if (!ch || ev.alt) return false;
39
- c.freeText += ch;
40
- return true;
41
- }
42
-
43
- /** wrap the selection over the card's n rows */
44
- function move(c: CardState, n: number, d: -1 | 1): true {
45
- const v = (c.selected + d + n) % n;
46
- c.selected = v;
47
- return true;
48
- }
49
-
50
- /** approval → deny, question → null; the card leaves the state before the promise settles */
51
- export function dismissCard(s: SextantState): void {
52
- const c = s.card;
53
- s.card = null;
54
- if (c?.kind === "approval") c.resolve("deny"); else c?.resolve(null);
55
- }
@@ -1,157 +0,0 @@
1
- /** The seam between the context overlay and core/context-report.ts.
2
- *
3
- * draw-context.ts is pure over `ContextState` and never counts anything. This file is the only place that
4
- * knows the report module exists: it lazily imports it (the boot rule — nothing in that dependency tree is
5
- * loaded until someone opens /context), asks the LIVE runtime for the two rows a transcript cannot know,
6
- * and maps the report onto the overlay's flat view.
7
- *
8
- * The live runtime is the whole reason this overlay is worth having. `rovecode context` on a shell has to
9
- * BUILD a runtime to learn the system prompt and the tool schemas, and in a fresh session those two are
10
- * most of the window; here they are already in memory, so the number is complete without constructing
11
- * anything. What is still missing is MCP tools — their schemas do not exist until a server is connected —
12
- * and the panel says so on its own line rather than letting a complete-looking total imply otherwise.
13
- *
14
- * Two numbers travel, never one. `raw` is what o200k counted; `estimated` is that corrected towards the
15
- * model's own tokenizer. Presenting only the second would be presenting a corrected figure as a
16
- * measurement, and someone comparing this panel against a provider's dashboard needs to know which is
17
- * which. `scale.note` says where the factor came from, including when nobody has measured this model.
18
- *
19
- * Nothing here throws. A runtime that cannot answer, a model the catalog does not know, an empty
20
- * transcript — each is a state the overlay draws, because a panel that reports on the session must not be
21
- * the thing that ends it. */
22
-
23
- import type { ContextState } from "./draw-context.ts";
24
-
25
- /** What the live runtime can tell us that the transcript cannot. Both optional: a session whose runtime
26
- * is mid-rebuild still opens the panel, and `live: false` makes the total read as a floor. */
27
- export interface ContextFixed {
28
- /** the system prompt a real turn would send — buildDef's, not the base alone */
29
- system?: string;
30
- /** rovecode's own tool schemas, serialized. MCP tools are deliberately absent. */
31
- toolSchemas?: string;
32
- }
33
-
34
- /** the runtime shape this file needs, structurally — declared here so the overlay never imports the
35
- * runtime's types and so a test can hand in three lines instead of a process */
36
- export interface LiveRuntime {
37
- registry: { list(): { schema: unknown }[] };
38
- // method syntax, and `unknown` for the prompt: the real AgentDefinition types `systemPrompt` as a string
39
- // or a function over its own AgentVars, and pinning either shape here would make this file depend on the
40
- // runtime's types — which is exactly what the overlay must not do. It is narrowed where it is read.
41
- buildDef(ref: { provider: string; model: string; effort: "auto" }): { systemPrompt: unknown };
42
- /** optional: the market overlay uses it after installing an MCP server (types.ts SextantAttach) */
43
- reloadMcp?(): Promise<{ added: string[]; removed: string[]; failed: { name: string; error: string }[]; skipped: string[] }>;
44
- }
45
-
46
- /** Pull the two fixed rows out of a runtime that is ALREADY running.
47
- *
48
- * Deliberately not `createRuntime`: the CLI builds one because it has none, and paid for that decision
49
- * with a session directory it had to pass in to avoid creating a stray empty session. Inside the TUI the
50
- * runtime is the one the next turn will use, so this is a read, not a construction — and it cannot leave
51
- * anything behind.
52
- *
53
- * `buildDef`, not `systemPrompt()`: the prompt a run actually sends is the base plus the model profile,
54
- * the design section, and — for a model without native tool calling — the tool block. Asking for the base
55
- * alone under-counts the largest fixed row by everything that makes it large. */
56
- export function fixedFrom(rt: LiveRuntime | null | undefined, ref: { provider: string; model: string }): ContextFixed {
57
- if (!rt) return {};
58
- try {
59
- const schemas = rt.registry.list().map((t) => t.schema);
60
- const def = rt.buildDef({ ...ref, effort: "auto" });
61
- const sp = def.systemPrompt;
62
- const system = typeof sp === "string" ? sp : typeof sp === "function" ? String((sp as (x: Record<string, unknown>) => unknown)({})) : undefined;
63
- if (system === undefined) return {};
64
- return { system, toolSchemas: JSON.stringify(schemas) };
65
- } catch {
66
- // a runtime that cannot describe itself is a panel that says so, not a crashed frame
67
- return {};
68
- }
69
- }
70
-
71
- /** the report module's shape, structurally: this file must compile and be testable without importing it */
72
- interface Slice { label: string; tokens: number; share: number; note?: string }
73
- interface Drift { estimated: number; reported: number; delta: number; fraction: number; beyondTolerance: boolean }
74
- interface Report {
75
- model: { provider: string; model: string };
76
- window?: number;
77
- estimated: number;
78
- corrected: number;
79
- scale: { factor: number; measured: boolean; note: string };
80
- slices: Slice[];
81
- remaining?: number;
82
- fraction?: number;
83
- nearLimit?: boolean;
84
- drift?: Drift;
85
- totals: { inputTokens?: number; outputTokens?: number; cacheReadTokens?: number; cacheWriteTokens?: number };
86
- costUsd?: number;
87
- unpricedTurns: number;
88
- images: number;
89
- }
90
-
91
- export interface ContextLoadInput {
92
- messages: readonly unknown[];
93
- current: { provider: string; model: string };
94
- /** window + pricing, however the caller has them */
95
- lookup: (r: { provider: string; model: string }) => unknown;
96
- fixed: ContextFixed;
97
- /** the tolerance drift is judged against; printed by the panel, never assumed by a reader */
98
- tolerance?: number;
99
- /** tests inject the report function; production lazily imports the real one */
100
- report?: (input: unknown) => Report;
101
- }
102
-
103
- /** the fraction past which drift is called out rather than merely shown. Matches core/context-report.ts;
104
- * it travels in the state so the panel can print it instead of asserting a number the reader cannot see. */
105
- const TOLERANCE = 0.05;
106
-
107
- /** Build the overlay's state. Never throws: every failure becomes something the panel can draw. */
108
- export async function loadContext(input: ContextLoadInput): Promise<Omit<ContextState, "scroll">> {
109
- const tolerance = input.tolerance ?? TOLERANCE;
110
- const model = `${input.current.provider}/${input.current.model}`;
111
-
112
- let report: Report;
113
- try {
114
- const fn = input.report ?? (await import("../core/context-report.ts")).contextReport as unknown as (i: unknown) => Report;
115
- report = fn({
116
- messages: input.messages,
117
- current: input.current,
118
- lookup: input.lookup,
119
- ...(input.fixed.system !== undefined ? { system: input.fixed.system } : {}),
120
- ...(input.fixed.toolSchemas !== undefined ? { toolSchemas: input.fixed.toolSchemas } : {}),
121
- });
122
- } catch {
123
- // the panel opens and says it has nothing, which is more useful than a frame that died counting
124
- return {
125
- model, raw: 0, estimated: 0, scale: { factor: 1, measured: false, note: "the context report could not be built" },
126
- slices: [], images: 0, billed: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
127
- unpricedTurns: 0, live: false, tolerance,
128
- };
129
- }
130
-
131
- const t = report.totals;
132
- return {
133
- model,
134
- ...(report.window !== undefined ? { window: report.window } : {}),
135
- // `estimated` on the panel is the CORRECTED number, because that is what the window and the bill are
136
- // computed from; `raw` carries what o200k counted so the panel can show both
137
- raw: report.estimated,
138
- estimated: report.corrected,
139
- scale: report.scale,
140
- ...(report.remaining !== undefined ? { remaining: report.remaining } : {}),
141
- ...(report.fraction !== undefined ? { fraction: report.fraction } : {}),
142
- ...(report.nearLimit !== undefined ? { nearLimit: report.nearLimit } : {}),
143
- slices: report.slices.map((s) => ({ label: s.label, tokens: s.tokens, share: s.share, ...(s.note ? { note: s.note } : {}) })),
144
- images: report.images,
145
- ...(report.drift ? { drift: { ...report.drift } } : {}),
146
- billed: {
147
- input: t.inputTokens ?? 0, output: t.outputTokens ?? 0,
148
- cacheRead: t.cacheReadTokens ?? 0, cacheWrite: t.cacheWriteTokens ?? 0,
149
- },
150
- ...(report.costUsd !== undefined ? { costUsd: report.costUsd } : {}),
151
- unpricedTurns: report.unpricedTurns,
152
- // "live" is about the two rows a transcript cannot know. The system prompt is the one that matters:
153
- // tool schemas can legitimately be an empty list, so their absence proves nothing.
154
- live: input.fixed.system !== undefined,
155
- tolerance,
156
- };
157
- }
@@ -1,350 +0,0 @@
1
- /** The CREW CARD (Berkay's pick, 2026-09-07): the plan panel's crew section as titled cards — one card per
2
- * WORKFLOW rather than one row per task, with a chip per agent showing which CLI opened.
3
- *
4
- * crew 3 working
5
- * test-project-scan ›
6
- * 3 agents · 01s
7
- * ▪▪▫ codex claude codex
8
- *
9
- * WHAT A WORKFLOW IS HERE. rovecode has no workflow concept and this file does not invent one: `s.crew` is a flat
10
- * TaskInfo[], and the only grouping that exists in the data is "tasks started under the same run" — TaskManager
11
- * stamps `batch` / `batchLabel` from bindRun (core/tasks.ts, nimbus-5f's slice), which every surface already calls
12
- * once per run. No heuristic: no time buckets, no label-prefix matching. A task with no batch (started outside a
13
- * bound run) gets its own single-task card titled by its own label, which is exactly what the flat list showed.
14
- *
15
- * THE CHIP RULE — a chip must never claim a CLI ran when it did not:
16
- * - queued is a HOLLOW chip and a dim name; only running/done chips are filled. "codex is queued" and "codex is
17
- * working" cannot look the same, because the difference is whether a process exists.
18
- * - only an EXTERNAL lane (TaskInfo.kind "external") shows a CLI name, from its `lane` adapter id. One of our own
19
- * subagents shows its AGENT name; if such a task were ever named after an adapter (the ids are reserved in
20
- * tasks.ts, so this is a defence rather than a case) it renders as `agent:<name>` and never as a bare CLI name.
21
- *
22
- * WHAT GOES FIRST WHEN IT DOES NOT FIT (the panel is 32 cells; stated rather than discovered by clipping):
23
- * 1. the blank row between cards — spacing carries no information;
24
- * 2. the names row collapses: repeats become counts (`codex ×2`), then a `+N` tail. Whole names are dropped, never
25
- * clipped mid-word — half a CLI's name is a different CLI's name;
26
- * 3. the title is truncated with an ellipsis, at a word boundary when there is one;
27
- * 4. the chips cap at what fits and the rest become `+N`;
28
- * 5. whole cards go, newest kept, with a `+N more` footer.
29
- * NEVER dropped: the agent count and the clock. A card that cannot show those two is not drawn at all — a title
30
- * alone would say a workflow exists without saying whether anything is running.
31
- *
32
- * PURE: `now` is a parameter (the clock and the card's age), no timers, no process access, no state writes. The
33
- * painter at the bottom is the only part that touches a screen. */
34
-
35
- import type { TaskInfo } from "../core/tasks.ts";
36
- import { isAdapterId } from "../lanes/types.ts";
37
- import { laneElapsed } from "./draw-agents.ts";
38
- import { fmtK } from "./model.ts";
39
- import { st } from "./theme.ts";
40
- import { ATTR, type ScreenLike, type Seg, type Theme } from "./types.ts";
41
-
42
- /** TaskInfo as the card reads it. `batch` / `batchLabel` / `progress` / `files` are all on TaskInfo now (core/tasks.ts);
43
- * the alias stays as this module's own name for what it consumes. */
44
- export type CrewTask = TaskInfo;
45
-
46
- export type CardTone = "fg" | "fg2" | "muted" | "dim" | "ok" | "err" | "accent" | "warn";
47
- export interface CardSpan { text: string; tone: CardTone; bold?: boolean }
48
- /** one painted row; `right` is anchored to the card's right edge (the `›` affordance) */
49
- export interface CardLine { spans: CardSpan[]; right?: CardSpan }
50
-
51
- export interface CrewGroup {
52
- /** the batch id, or `task:<id>` for a task that has none */
53
- key: string;
54
- label: string;
55
- tasks: CrewTask[];
56
- /** min(startedAt ?? createdAt) over the group */
57
- startedAt: number;
58
- /** max(finishedAt) when EVERY task has finished; undefined while any is live (the clock still runs) */
59
- finishedAt?: number;
60
- working: number;
61
- }
62
-
63
- const oneLine = (s: string): string => s.replace(/\s+/g, " ").trim();
64
- const cps = (s: string): string[] => [...s];
65
- const width = (s: string): number => cps(s).length;
66
-
67
- // ------------------------------------------------------------------ grouping
68
-
69
- /** Group by `batch`, first-seen order kept (the crew list is already in start order). A task without a batch is its
70
- * own group. The label is the batch label when the surface set one, else the first task's label — never invented. */
71
- export function groupCrew(crew: readonly CrewTask[]): CrewGroup[] {
72
- const out: CrewGroup[] = [];
73
- const byKey = new Map<string, CrewGroup>();
74
- for (const t of crew) {
75
- const key = t.batch ?? `task:${t.id}`;
76
- let g = byKey.get(key);
77
- if (g === undefined) {
78
- g = { key, label: oneLine(t.batchLabel ?? t.label) || "(no label)", tasks: [], startedAt: t.startedAt ?? t.createdAt, working: 0 };
79
- byKey.set(key, g);
80
- out.push(g);
81
- }
82
- g.tasks.push(t);
83
- g.startedAt = Math.min(g.startedAt, t.startedAt ?? t.createdAt);
84
- }
85
- for (const g of out) {
86
- g.working = g.tasks.filter((t) => t.status === "queued" || t.status === "running").length;
87
- const ends = g.tasks.map((t) => t.finishedAt);
88
- if (ends.every((e): e is number => e !== undefined)) g.finishedAt = Math.max(...ends);
89
- }
90
- return out;
91
- }
92
-
93
- /** the card's age: the WHOLE workflow's span, live until every task in it has finished */
94
- export function groupElapsed(g: CrewGroup, now: number): number {
95
- if (g.finishedAt !== undefined) return Math.max(0, g.finishedAt - g.startedAt);
96
- return Math.max(0, now - g.startedAt, ...g.tasks.map((t) => laneElapsed(t, now)));
97
- }
98
-
99
- /** `01s` · `04m` · `1h02m` — the compact age from the card Berkay picked; mm:ss (laneClock) is the per-task form */
100
- export function compactAge(ms: number): string {
101
- const s = Math.max(0, Math.floor(ms / 1000));
102
- if (s < 60) return `${String(s).padStart(2, "0")}s`;
103
- const m = Math.floor(s / 60);
104
- if (m < 60) return `${String(m).padStart(2, "0")}m`;
105
- return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
106
- }
107
-
108
- // ------------------------------------------------------------------ chips
109
-
110
- /** ▫ queued (nothing has run) · ▪ running · ◆ done · × failed · · cancelled */
111
- export function chipGlyph(status: TaskInfo["status"]): string {
112
- switch (status) {
113
- case "queued": return "▫";
114
- case "running": return "▪";
115
- case "done": return "◆";
116
- case "failed": return "×";
117
- default: return "·";
118
- }
119
- }
120
- export function chipTone(status: TaskInfo["status"]): CardTone {
121
- switch (status) {
122
- case "queued": return "dim";
123
- case "running": return "accent";
124
- case "done": return "ok";
125
- case "failed": return "err";
126
- default: return "muted";
127
- }
128
- }
129
-
130
- /** the name under a chip: a CLI id ONLY for an external lane; our own agents show their agent name. An external
131
- * lane with no adapter id recorded says `cli?` rather than borrowing a name we do not have. */
132
- export function chipName(t: CrewTask): string {
133
- if (t.kind === "external") return t.lane ?? "cli?";
134
- return isAdapterId(t.agent) ? `agent:${t.agent}` : t.agent;
135
- }
136
-
137
- /** the chip row's glyphs, one per task in order; `+N` when more tasks than fit in `w` cells */
138
- export function chipRow(tasks: readonly CrewTask[], w: number): CardSpan[] {
139
- if (w <= 0 || tasks.length === 0) return [];
140
- const spans: CardSpan[] = [];
141
- let used = 0;
142
- for (let i = 0; i < tasks.length; i++) {
143
- const rest = tasks.length - i;
144
- const tail = rest > 1 ? `+${rest}` : "";
145
- if (used + 1 + width(tail) > w && i > 0) { spans.push({ text: `+${rest}`, tone: "dim" }); return spans; }
146
- if (used + 1 > w) return spans;
147
- const t = tasks[i]!;
148
- spans.push({ text: chipGlyph(t.status), tone: chipTone(t.status), bold: t.status === "running" });
149
- used += 1;
150
- }
151
- return spans;
152
- }
153
-
154
- /** The names, collapsed to fit: `codex claude codex` → `codex ×2 claude` when repeats cost less, then a `+N` tail.
155
- * Whole names only — a clipped name is a lie about which CLI ran. Empty when nothing fits. */
156
- export function nameRow(tasks: readonly CrewTask[], w: number): CardSpan[] {
157
- if (w <= 0 || tasks.length === 0) return [];
158
- const runs: { name: string; count: number; tone: CardTone }[] = [];
159
- for (const t of tasks) {
160
- const name = chipName(t), tone = chipTone(t.status);
161
- const last = runs[runs.length - 1];
162
- if (last !== undefined && last.name === name && last.tone === tone) last.count++;
163
- else runs.push({ name, count: 1, tone });
164
- }
165
- const label = (r: { name: string; count: number }): string => (r.count > 1 ? `${r.name} ×${r.count}` : r.name);
166
- const spans: CardSpan[] = [];
167
- let used = 0;
168
- for (let i = 0; i < runs.length; i++) {
169
- const r = runs[i]!;
170
- const text = label(r);
171
- // the tail counts the AGENTS still unnamed, not the runs: "+2" when three agents are hidden would be a wrong number
172
- const left = runs.slice(i).reduce((n, x) => n + x.count, 0);
173
- const tail = ` +${left}`;
174
- const sep = spans.length > 0 ? 1 : 0;
175
- const more = i < runs.length - 1 ? width(` +${left - r.count}`) : 0;
176
- if (used + sep + width(text) + more > w) {
177
- if (spans.length > 0 && used + width(tail) <= w) spans.push({ text: tail, tone: "dim" });
178
- return spans;
179
- }
180
- if (sep) { spans.push({ text: " ", tone: "dim" }); used += 1; }
181
- spans.push({ text, tone: r.tone });
182
- used += width(text);
183
- }
184
- return spans;
185
- }
186
-
187
- // ------------------------------------------------------------------ card rows
188
-
189
- /** ellipsis truncation at a word boundary when there is one in the last third of the space */
190
- export function fitTitle(title: string, w: number): string {
191
- const c = cps(oneLine(title));
192
- if (w <= 0) return "";
193
- if (c.length <= w) return c.join("");
194
- if (w === 1) return "…";
195
- const head = c.slice(0, w - 1).join("");
196
- const cut = head.lastIndexOf(" ");
197
- return (cut >= Math.floor((w - 1) * 0.67) ? head.slice(0, cut) : head) + "…";
198
- }
199
-
200
- /** the card: title (+ `›`), `N agents · age`, then the chips and the names on one row. The meta row is the
201
- * honesty core — it is never dropped, so a card is only produced when `w` can hold it. */
202
- export function cardLines(g: CrewGroup, w: number, now: number): CardLine[] {
203
- const count = `${g.tasks.length} agent${g.tasks.length === 1 ? "" : "s"}`;
204
- const meta = `${count} · ${compactAge(groupElapsed(g, now))}`;
205
- if (w < width(meta)) return [];
206
- const lines: CardLine[] = [];
207
- const arrow = w >= width(meta) + 2 ? { text: "›", tone: "dim" as CardTone } : undefined;
208
- lines.push({ spans: [{ text: fitTitle(g.label, w - (arrow ? 2 : 0)), tone: g.working > 0 ? "fg" : "fg2", bold: g.working > 0 }], ...(arrow ? { right: arrow } : {}) });
209
- lines.push({ spans: [{ text: meta, tone: "dim" }] });
210
- const chips = chipRow(g.tasks, Math.max(0, Math.min(g.tasks.length + 2, Math.floor(w / 2))));
211
- const chipW = chips.reduce((n, s) => n + width(s.text), 0);
212
- const names = nameRow(g.tasks, w - chipW - 2);
213
- if (chips.length > 0) lines.push({ spans: names.length > 0 ? [...chips, { text: " ", tone: "dim" }, ...names] : chips });
214
- const progress = progressRow(g, w);
215
- if (progress.length > 0) lines.push({ spans: progress });
216
- return lines;
217
- }
218
-
219
- /** THE PROGRESS ROW — `wrote 4 files · 12 calls · 4.2k`, summed over a workflow's tasks, and it is the second thing
220
- * dropped at a narrow width (after the blank separator): the chips and the CLI names are what the card exists for.
221
- * Four rules, all of them about not overstating what a CLI did (lanes/progress.ts, nimbus-24's measurements):
222
- * - a RUNNING lane with nothing confirmed shows no file segment at all. `filesWritten` is what the CLI has
223
- * confirmed so far, and one of the four (agy) confirms nothing until it ends — it sits at zero for minutes and
224
- * then jumps to the measured count. "wrote 0 files" for a lane that is writing would be a wrong statement, and
225
- * treating that zero as a stall would be wrong about exactly one CLI.
226
- * - a FINISHED task's list is the worktree diff, so there zero is a measurement and is worth printing.
227
- * - a task whose patch never reached the tree did not WRITE anything the person has: its count is reported as
228
- * `discarded` / `so far`, never as `wrote`. The signal is `progress.applied` (tri-state, written by the code
229
- * that does the applying): absent = nothing offered to the tree yet, true = merged back, false = the writes
230
- * stayed in a worktree that was deleted. NOT `patchLines`, and not the status either: a lane that finishes
231
- * cleanly whose patch git refuses — a concurrent task touched the same lines — still reports ok AND gets a
232
- * patchLines, so both of those would print `wrote 4 files` about a tree that has none of them (nimbus-24's
233
- * lanes-job tests cover the case). One of OUR isolated children has no progress object, and orchestrator.ts
234
- * has the same hole — so the signal there is `files`, which tasks.ts sets only for a patch that applied
235
- * (5f gated it on progress.applied, else the runner's own "patch-apply-failed" marker). `patchLines` is a
236
- * SIZE and never a verdict; the one thing it still settles is the empty patch, where 0 lines means there was
237
- * nothing to apply and nothing that could fail.
238
- * - `usage` ABSENT is not zero. A lane we could not read reports nothing; a lane that burned nothing reports
239
- * zeros. Absent contributes no token segment; explicit zeros show `0`.
240
- * `calls` counts every tool the CLI reported, reads included, so the word stays `calls`. Only lanes report them,
241
- * so a mixed workflow's row says what its lanes did without implying the native agents did nothing. */
242
- /** Did this task's work reach the person's tree? `true` landed · `false` written and thrown away · `undefined`
243
- * nothing offered yet. A lane answers from `progress.applied` (the code that applies, not the code beside it); one
244
- * of our own children answers from `files`, which exists only for a patch that applied — except for the empty
245
- * patch, where `patchLines === 0` means there was nothing to apply, and that counts as landed. */
246
- export function landedOf(t: CrewTask): boolean | undefined {
247
- if (t.progress !== undefined && "applied" in t.progress) return t.progress.applied;
248
- if (t.status === "running" || t.status === "queued") return undefined;
249
- if (t.files !== undefined) return true;
250
- return t.status === "done" && t.patchLines === 0;
251
- }
252
-
253
- export function progressRow(g: CrewGroup, w: number): CardSpan[] {
254
- let calls = 0, applied = 0, pending = 0, discarded = 0, tokens = 0, sawUsage = false;
255
- for (const t of g.tasks) {
256
- calls += t.progress?.toolCalls ?? 0;
257
- const n = t.files?.length ?? t.progress?.filesWrittenTotal ?? 0;
258
- const landed = landedOf(t);
259
- if (landed === true) applied += n;
260
- else if (landed === undefined) pending += n;
261
- else discarded += n;
262
- const u = t.progress?.usage ?? t.usage;
263
- if (u !== undefined) { sawUsage = true; tokens += u.input + u.output; }
264
- }
265
- const parts: { text: string; tone: CardTone }[] = [];
266
- if (applied > 0) parts.push({ text: `wrote ${applied} file${applied === 1 ? "" : "s"}`, tone: "ok" });
267
- if (pending > 0) parts.push({ text: `${pending} file${pending === 1 ? "" : "s"} so far`, tone: "dim" });
268
- if (discarded > 0) parts.push({ text: `${discarded} discarded`, tone: "muted" });
269
- // every task settled and nothing landed: that zero IS the measurement, and saying so beats an empty row
270
- const merged = g.tasks.some((t) => landedOf(t) === true);
271
- if (parts.length === 0 && g.working === 0 && merged) parts.push({ text: "wrote 0 files", tone: "dim" });
272
- if (calls > 0) parts.push({ text: `${calls} call${calls === 1 ? "" : "s"}`, tone: "dim" });
273
- if (sawUsage) parts.push({ text: fmtK(tokens), tone: "dim" });
274
- if (parts.length === 0) return [];
275
- const spans: CardSpan[] = [];
276
- let used = 0;
277
- for (const part of parts) {
278
- const sep = spans.length > 0 ? 3 : 0; // " · "
279
- if (used + sep + width(part.text) > w) break;
280
- if (sep) { spans.push({ text: " · ", tone: "dim" }); used += 3; }
281
- spans.push(part);
282
- used += width(part.text);
283
- }
284
- return spans;
285
- }
286
-
287
- export interface CrewCardBlock {
288
- lines: CardLine[];
289
- /** cards that did not fit — the `+N more` footer is already the last line when this is > 0 */
290
- hidden: number;
291
- }
292
-
293
- /** The card order: WORKING workflows first, newest of them at the top, then the settled ones newest first. The
294
- * section's header says "2 working"; a panel that then shows two finished cards and hides the running one is
295
- * answering a different question from the one it just asked. Within each half the newest card is first because
296
- * the crew list is start-ordered, so its tail is the live work. */
297
- export function crewCardOrder(groups: readonly CrewGroup[]): CrewGroup[] {
298
- const newestFirst = [...groups].reverse();
299
- return [...newestFirst.filter((g) => g.working > 0), ...newestFirst.filter((g) => g.working === 0)];
300
- }
301
-
302
- /** Pack the cards into `rows` rows in crewCardOrder. Blank separators are dropped before any card is. */
303
- export function crewCardBlock(groups: readonly CrewGroup[], w: number, rows: number, now: number): CrewCardBlock {
304
- if (rows <= 0 || w <= 0 || groups.length === 0) return { lines: [], hidden: groups.length };
305
- const order = crewCardOrder(groups);
306
- const cards = order.map((g) => cardLines(g, w, now)).filter((c) => c.length > 0);
307
- const pack = (gap: boolean): CrewCardBlock & { shown: number } => {
308
- const lines: CardLine[] = [];
309
- let used = 0, shown = 0;
310
- for (const card of cards) {
311
- const need = card.length + (gap && shown > 0 ? 1 : 0);
312
- const footer = cards.length - shown - 1 > 0 ? 1 : 0;
313
- if (used + need + footer > rows) break;
314
- if (gap && shown > 0) { lines.push({ spans: [] }); used++; }
315
- lines.push(...card);
316
- used += card.length;
317
- shown++;
318
- }
319
- const hidden = groups.length - shown;
320
- if (hidden > 0 && shown > 0 && used < rows) lines.push({ spans: [{ text: `+${hidden} more`, tone: "dim" }] });
321
- return { lines: shown > 0 ? lines : [], hidden: shown > 0 ? hidden : groups.length, shown };
322
- };
323
- // separators are spacing; a card is information. Take the spaced packing only when it costs no card.
324
- const spaced = pack(true), dense = pack(false);
325
- return dense.shown > spaced.shown ? { lines: dense.lines, hidden: dense.hidden } : { lines: spaced.lines, hidden: spaced.hidden };
326
- }
327
-
328
- /** rows a full render of these groups would take (cards + separators), for a panel deciding its budget */
329
- export function crewCardHeight(groups: readonly CrewGroup[], w: number, now: number): number {
330
- const cards = groups.map((g) => cardLines(g, w, now)).filter((c) => c.length > 0);
331
- return cards.reduce((n, c) => n + c.length, 0) + Math.max(0, cards.length - 1);
332
- }
333
-
334
- // ------------------------------------------------------------------ painter
335
-
336
- const TONE: Record<CardTone, keyof Theme> = { fg: "fg", fg2: "fg2", muted: "muted", dim: "dim", ok: "ok", err: "err", accent: "accent", warn: "warn" };
337
-
338
- /** paint `lines` at (x, y) in `w` cells — the only impure part, and it writes nothing but those rows */
339
- export function paintCrewCards(scr: ScreenLike, x: number, y: number, w: number, lines: readonly CardLine[], theme: Theme): void {
340
- lines.forEach((line, i) => {
341
- const row = y + i;
342
- if (line.right !== undefined) {
343
- const r = line.right;
344
- scr.put(x + w - width(r.text), row, r.text, st(theme[TONE[r.tone]] as number), width(r.text));
345
- }
346
- if (line.spans.length === 0) return;
347
- const segs: Seg[] = line.spans.map((s) => [s.text, st(theme[TONE[s.tone]] as number, -1, s.bold ? ATTR.BOLD : 0)]);
348
- scr.text(x, row, segs, w - (line.right ? width(line.right.text) + 1 : 0));
349
- });
350
- }