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,521 +0,0 @@
1
- /** Sextant input (port #43): keys, mouse, focus, prompt editing and the overlay key routes.
2
- * Ported from the user's own sextant v0.4.0 prototype — src/app.js:1438-1584 (submit, insertText,
3
- * FOCUS_ORDER, onKey incl. the ctrl map, esc / esc-esc, tab focus, per-focus enter, files nav,
4
- * code scroll/mode, message paging, history/suggestion ↑↓, line editing; onMouse with wheel 64/65
5
- * and the reverse hit order) and :1024-1029 (runSlash history push). Dropped: intent matching, the
6
- * mock shell, /spawn /crew; /undo /permissions /mode become notes. Split for the line budget: the
7
- * card key route is card-keys.ts; the renderer-local slash commands, those notes and the
8
- * state-then-hook effect helpers are local-commands.ts.
9
- * `handleInput` is a PURE state mutator: `now` is a parameter (esc-esc window), no Date.now(),
10
- * timers or process access; everything that leaves the state goes through KeyCtx.hooks (rovecode's
11
- * RendererHooks) or KeyCtx.local (renderer-owned effects: theme rebuild, file/diff loading,
12
- * toasts); a state field is written BEFORE its local hook fires, so the renderer only loads
13
- * content / rebuilds the palette.
14
- * Deviations from the prototype, all deliberate: Enter runs the highlighted slash row only for a
15
- * ≥ 2-letter prefix stem (`/hel` → /help) or a ↓-picked row — a fuzzy-only top row (`/x` → /exit,
16
- * `/exot` → /export) or a one-letter stem (`/e` → /exit would quit) submits the line verbatim so
17
- * handleSlash reports the unknown command (the #43 bar); ⇧Tab also completes while the box is
18
- * open (README table); Esc closes a visible box first (sgSel = -1 until the text changes) before
19
- * arming the interrupt or clearing the line; ↑↓ keep browsing history once started, and ↓ past the
20
- * newest yields "" (faithful — no draft field; a draft is a types.ts question for #44); ⌃c with a
21
- * pending card while a run is live denies/nulls the card AND interrupts (the prototype only
22
- * answered the card); End re-sticks the messages tail but wheel-down never does — only the drawer
23
- * knows maxScroll (#44 handoff: re-stick in the messages drawer at the tail); /help opens the
24
- * keys card AND reaches handleSlash; a left-button drag never clicks; a click on a panel body with
25
- * no hit zone focuses it; ←/→, Backspace and Delete step by code point (😀 is never split); the
26
- * mouse goes through the #40 mouseKind, so a shift/alt/ctrl + wheel scrolls and never clicks.
27
- * NOTE for #44: once sextant is the default, app.ts discoverCommands `reserved` should include the
28
- * renderer-local names (theme open diff focus agents) so a custom command file cannot shadow them. */
29
-
30
- import {
31
- THEME_ORDER, type CodeMode, type Focus, type HitZone, type InputEvent, type KeyEvent, type Layout, type MouseEvent, type Page,
32
- type Rect, type SextantState, type ThemeName, type TreeRow,
33
- } from "./types.ts";
34
- import { type Fuzzy, type Suggestion, onPaletteKey, openPalette, parseInput, resolveFile, suggestions } from "./overlays.ts";
35
- import { onMarketKey, type MarketViewRow } from "./draw-market.ts";
36
- import { onWizardKey, wizardPaste } from "./draw-wizard.ts";
37
- import { onContextKey } from "./draw-context.ts";
38
- import { dispatch, openFile, runAction, setFocus, setMode, setTheme, showAgents } from "./local-commands.ts";
39
- import { dismissCard, onCardKey } from "./card-keys.ts";
40
- import { gridFor } from "./draw-agents.ts";
41
- import { mouseKind } from "./input.ts";
42
- import { mainPage, nextPage } from "./draw-tabs.ts";
43
- import { openNotices, openOverlays } from "./overlays.ts";
44
-
45
- // ------------------------------------------------------------------ contract
46
-
47
- /** a click zone a drawer registered this frame; the LAST registered zone under the pointer wins (contract: types.ts) */
48
- export type { HitZone } from "./types.ts";
49
-
50
- export interface KeyCtx {
51
- layout: Layout;
52
- /** rovecode RendererHooks (tui/renderer.ts): the controller's submit / interrupt / exit */
53
- hooks: { onSubmit(text: string): void; onInterrupt(): void; onExit(): void };
54
- /** renderer-owned effects; the state field is already written when these fire */
55
- local: {
56
- setTheme(name: ThemeName): void; setMode(mode: CodeMode): void; openFile(path: string): void; toast(text: string): void;
57
- /** port #54: an @image mention reached dispatch — stage it for the next message (tui/attach.ts's seam,
58
- * the store's stage: caps, the vision note and the chip are that seam's, nothing duplicated here) */
59
- attachImage(abs: string): void;
60
- /** open /market: the renderer loads the catalog (async) and fills s.market when it answers */
61
- openMarket(): void;
62
- /** open /context: the renderer asks the LIVE runtime for the system prompt and tool schemas — the two
63
- * rows a transcript cannot know — and fills s.context when the count is done */
64
- openContext(): void;
65
- /** open the connect wizard (/connect, /setup): the provider rows come from the attach seam's
66
- * wizardProviders; without it (no runtime behind the surface) the call degrades to a toast */
67
- openWizard(): void;
68
- /** the human pressed Enter on a row (and answered the install-once chooser for an npx row: `local`):
69
- * build the plan and write it into s.market.plan */
70
- marketPlan(row: MarketViewRow, local?: boolean): void;
71
- /** the human confirmed the plan card: run THAT plan (its `local` included) and write the outcome back onto the card */
72
- marketInstall(row: MarketViewRow, local?: boolean): void;
73
- /** the docs pane opened on a row whose body has not been read: fetch it and fill the row's lines */
74
- marketDocs(row: MarketViewRow): void;
75
- /** the connect wizard (draw-wizard.ts): register a url door's endpoint (reg.add), store the pasted
76
- * key for `provider` (credentials file, not the transcript), load the model list for the chosen
77
- * provider, pin the checked rows as the active list */
78
- wizardRegister(id: string, url: string, protocol: "openai" | "anthropic"): void;
79
- wizardStoreKey(provider: string, secret: string): void;
80
- wizardModel(model: string): void;
81
- /** the model step OPENED: fetch the endpoint's model list through the wizardModels seam (the
82
- * renderer answers with wizardModelConfirmed; empty or failed → the typed fallback stays) */
83
- wizardLoadModels(): void;
84
- /** the model step's ACTIVATE answer (confirmModels): pin `models` as the provider's active list
85
- * (registry setModels) and make `makeDefault` the default when one is named */
86
- wizardActivate(provider: string, models: readonly string[], makeDefault?: string): void;
87
- };
88
- /** click zones the drawers registered while painting the current frame */
89
- hits: readonly HitZone[];
90
- /** the zone grabbed by the last click that had an onDrag (a scrollbar thumb); owned by the frame loop
91
- * so it outlives the per-frame `hits` list — drag events go to it until the button is released */
92
- drag: { zone: HitZone | null; y0: number };
93
- /** the flattened files tree as drawn this frame (cursor / Enter / ←→ act on these rows) */
94
- rows: readonly TreeRow[];
95
- /** #40 engine.fuzzy when wired; defaults to the ported scorer in overlays.ts */
96
- fuzzy?: Fuzzy;
97
- }
98
-
99
- /** what the renderer must do after a key: repaint. A union of one today, kept extensible. */
100
- export type KeyEffect = "render";
101
-
102
- export const ESC_WINDOW_MS = 1500;
103
- export const FOCUS_ORDER: readonly Focus[] = ["messages", "code", "files"];
104
- /** ←/→ in the code panel (app.js:1547); `search` joins the cycle only while a result is shown */
105
- export const CODE_MODE_CYCLE: readonly CodeMode[] = ["code", "diff", "run", "agents"];
106
- /** "scroll to the tail" sentinel — the drawer clamps to the last row */
107
- export const SCROLL_TAIL = 1e9;
108
-
109
- const R = (): KeyEffect[] => ["render"], NONE = (): KeyEffect[] => [];
110
- const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(v, hi));
111
- const inRect = (x: number, y: number, r: Rect) => x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h;
112
-
113
- /** messages→code→files, files only while the layout shows the column (app.js:1520) */
114
- /** files is in the Tab cycle when its panel is on screen — its own column, or paged into the main slot */
115
- export function focusOrder(L: Layout, page: Page = "code"): Focus[] {
116
- return FOCUS_ORDER.filter((f) => f !== "files" || L.files !== null || page === "files");
117
- }
118
-
119
- // ------------------------------------------------------------------ entry
120
-
121
- export function handleInput(s: SextantState, ev: InputEvent, ctx: KeyCtx, now: number): KeyEffect[] {
122
- if (ev.type === "mouse") return onMouse(s, ev, ctx, now);
123
- if (ev.type === "paste") return onPaste(s, ev.text, ctx);
124
- if (s.palette) {
125
- if (ev.ctrl && ev.name === "c") return ctrlC(s, ctx);
126
- onPaletteKey(s, ev, (a) => runAction(s, a, ctx), ctx.fuzzy);
127
- return R();
128
- }
129
- if (s.market) {
130
- if (ev.ctrl && ev.name === "c") return ctrlC(s, ctx);
131
- const req = onMarketKey(s, ev, ctx.fuzzy);
132
- // the overlay decides WHAT should happen; the renderer owns the market module and does it
133
- if (req.kind === "plan") ctx.local.marketPlan(req.row, req.local);
134
- else if (req.kind === "docs") ctx.local.marketDocs(req.row);
135
- // the plan card names an item; installing re-read the SELECTION instead, and the two can differ —
136
- // the plan arrives asynchronously, so an arrow key (or a click on a list row that outlived the card)
137
- // moved the selection while the card still showed the first item. Install what the human approved.
138
- else if (req.kind === "install") ctx.local.marketInstall(req.plan.row, req.plan.local);
139
- return R();
140
- }
141
- if (s.context) {
142
- if (ev.ctrl && ev.name === "c") return ctrlC(s, ctx);
143
- // a page is the drawn body height; the panel is centred and capped, so this matches drawContext
144
- onContextKey(s, ev, Math.max(4, Math.min(ctx.layout.h - 4, 24)));
145
- return R();
146
- }
147
- if (s.wizard) {
148
- if (ev.ctrl && ev.name === "c") return ctrlC(s, ctx);
149
- // the overlay decides WHAT should happen; the renderer owns the registry and runs it
150
- const req = onWizardKey(s, ev);
151
- if (req.kind === "registerEndpoint") ctx.local.wizardRegister(req.id, req.url, req.protocol);
152
- else if (req.kind === "storeKey") ctx.local.wizardStoreKey(req.provider, req.secret);
153
- else if (req.kind === "confirmModels") ctx.local.wizardActivate(req.provider, req.models, req.makeDefault === undefined ? undefined : req.makeDefault);
154
- else if (req.kind === "confirmModel") ctx.local.wizardModel(req.model);
155
- else if (req.kind === "loadModels") ctx.local.wizardLoadModels();
156
- return R();
157
- }
158
- if (s.help) { // app.js:1488 — the card swallows every key; the usual closers dismiss it
159
- const closer = ev.name === "escape" || ev.name === "enter" || ev.name === "space" || (ev.ctrl && ev.name === "c");
160
- if (closer || (ev.ch && !ev.ctrl)) s.help = false;
161
- return R();
162
- }
163
- if (ev.ctrl) return onCtrl(s, ev, ctx);
164
- if (s.card && onCardKey(s, s.card, ev)) return R();
165
- const { name } = ev;
166
- if (name === "escape") return onEscape(s, ctx, now);
167
- if (name === "tab" || name === "shift-tab") return onTab(s, name, ctx);
168
- if (name === "enter") return onEnter(s, ctx);
169
- if (s.focus === "files") { const fx = onFilesKey(s, name, ctx); if (fx) return fx; }
170
- if (s.focus === "code") { const fx = onCodeKey(s, name, ctx); if (fx) return fx; }
171
- return onPromptKey(s, ev, ctx);
172
- }
173
-
174
- // ------------------------------------------------------------------ ctrl map (app.js:1489-1502)
175
-
176
- /** The chords that put an overlay on the screen. A card is modal, and an overlay painted over it also
177
- * registers its hit zones first — so the card underneath stops answering the mouse and the tool call
178
- * waiting on it is stuck until the user finds their way back out. ⌃c was special-cased for this from
179
- * the start; the rest fell through by omission. Typing still reaches the prompt while a card waits
180
- * (onCardKey returning false is deliberate) — this only stops a second surface from covering it. */
181
- const OPENS_OVERLAY = new Set(["k", "p", "b", "m", "g"]);
182
-
183
- function onCtrl(s: SextantState, ev: KeyEvent, ctx: KeyCtx): KeyEffect[] {
184
- if (s.card && ev.name !== undefined && OPENS_OVERLAY.has(ev.name)) return R();
185
- switch (ev.name) {
186
- case "c": return ctrlC(s, ctx);
187
- case "k": case "p": openPalette(s); return R();
188
- case "g": ctx.local.openContext(); return R();
189
- case "t": {
190
- const next: ThemeName = THEME_ORDER[(THEME_ORDER.indexOf(s.theme) + 1) % THEME_ORDER.length]!;
191
- setTheme(s, ctx, next);
192
- return R();
193
- }
194
- case "e": setFocus(s, ctx, "files"); return R();
195
- // ⌃o: cycle the tab strip (draw-tabs.ts) — code → files → plan on a narrow terminal; inert when wide
196
- case "o": {
197
- const next = nextPage(ctx.layout, s);
198
- if (next !== s.page) { s.page = next; s.focus = next === "files" ? "files" : "code"; }
199
- return R();
200
- }
201
- case "b": openNotices(s); return R(); // the notification history (overlays.ts openNotices)
202
- case "m": ctx.local.openMarket(); return R(); // the market overlay (draw-market.ts)
203
- // ⌃v with an IMAGE on the clipboard: the terminal sends no paste for it, only this key — ask the
204
- // OS for the image through /paste (tui/attach.ts cmdPasteImage). Text pastes never arrive here.
205
- case "v": ctx.hooks.onSubmit("/paste"); return R();
206
- case "s": setMode(s, ctx, "code"); s.focus = "code"; return R();
207
- case "d": setMode(s, ctx, s.code.mode === "diff" ? "code" : "diff"); return R();
208
- case "r": setMode(s, ctx, "run"); return R();
209
- case "a": showAgents(s, ctx); return R();
210
- case "n": ctx.hooks.onSubmit("/new"); return R();
211
- case "u": clearInput(s); return R();
212
- case "left": case "right":
213
- s.input.cur = wordJump(s.input.text, s.input.cur, ev.name === "left" ? -1 : 1);
214
- return R();
215
- default: return NONE();
216
- }
217
- }
218
-
219
- /** ⌃c: a pending card is dismissed (deny / null) AND a running turn is interrupted (the prototype
220
- * only answered the card); idle → exit, so the second ⌃c after a run was stopped leaves (app.js:1491) */
221
- function ctrlC(s: SextantState, ctx: KeyCtx): KeyEffect[] {
222
- if (s.card) dismissCard(s);
223
- if (s.running) ctx.hooks.onInterrupt(); else ctx.hooks.onExit();
224
- return R();
225
- }
226
-
227
- function clearInput(s: SextantState): void { s.input.text = ""; s.input.cur = 0; s.input.sgSel = 0; s.input.histIdx = -1; }
228
-
229
- // ------------------------------------------------------------------ esc / tab / enter
230
-
231
- function onEscape(s: SextantState, ctx: KeyCtx, now: number): KeyEffect[] {
232
- if (s.focus !== "messages") { // a panel: close its open lane first, then hand focus back to the prompt
233
- if (s.code.laneOpen) s.code.laneOpen = false; else s.focus = "messages";
234
- return R();
235
- }
236
- if (openSuggestions(s, ctx).length) { s.input.sgSel = -1; return R(); } // close the box, keep the text
237
- if (s.running) { // app.js:1513 — the first Esc arms the window, the second inside it interrupts
238
- if (now < s.escUntil) { s.escUntil = 0; ctx.hooks.onInterrupt(); }
239
- else s.escUntil = now + ESC_WINDOW_MS;
240
- return R();
241
- }
242
- if (s.input.text) { clearInput(s); return R(); }
243
- return NONE();
244
- }
245
-
246
- function onTab(s: SextantState, name: string, ctx: KeyCtx): KeyEffect[] {
247
- const sugs = openSuggestions(s, ctx);
248
- if (s.focus === "messages" && sugs.length) { applySuggestion(s, sugs[clamp(s.input.sgSel, 0, sugs.length - 1)]!); return R(); }
249
- const order = focusOrder(ctx.layout, s.page), i = Math.max(0, order.indexOf(s.focus));
250
- s.focus = order[(i + (name === "tab" ? 1 : order.length - 1)) % order.length]!;
251
- return R();
252
- }
253
-
254
- function onEnter(s: SextantState, ctx: KeyCtx): KeyEffect[] {
255
- if (s.focus === "files") {
256
- const r = ctx.rows[s.files.cursor];
257
- if (r?.dir) { // toggle the fold
258
- if (s.files.expanded.has(r.path)) s.files.expanded.delete(r.path); else s.files.expanded.add(r.path);
259
- } else if (r) openFile(s, ctx, r.path);
260
- return R();
261
- }
262
- if (s.focus === "code") {
263
- if (s.code.mode === "agents" && s.crew.length) { // open / close the selected lane; opened = at its tail
264
- s.code.laneOpen = !s.code.laneOpen;
265
- s.code.scroll = s.code.laneOpen ? SCROLL_TAIL : 0;
266
- }
267
- return R();
268
- }
269
- const sugs = openSuggestions(s, ctx);
270
- if (sugs.length) {
271
- const pick = sugs[clamp(s.input.sgSel, 0, sugs.length - 1)]!;
272
- if (!autoRuns(s, pick)) return submitLine(s, s.input.text, ctx);
273
- applySuggestion(s, pick);
274
- if (pick.enter === "complete") return R();
275
- }
276
- return submitLine(s, s.input.text, ctx);
277
- }
278
-
279
- /** Enter takes the highlighted row when the user picked it with ↓, when it is an argument or mention
280
- * row, or when the typed stem is a ≥ 2-letter prefix of the command (`/hel` → /help); a fuzzy-only
281
- * top row (`/x` → /exit) or a one-letter stem (`/e`) would run a command the user never named */
282
- function autoRuns(s: SextantState, pick: Suggestion): boolean {
283
- if (pick.kind !== "slash" || s.input.sgSel !== 0) return true;
284
- const stem = (parseInput(s.input.text).cmd ?? "").toLowerCase();
285
- return stem.length >= 2 && pick.label.slice(1).toLowerCase().startsWith(stem);
286
- }
287
-
288
- /** app.js:1438-1457 minus the mock paths: trimmed non-empty → history, clear, dispatch */
289
- function submitLine(s: SextantState, raw: string, ctx: KeyCtx): KeyEffect[] {
290
- const text = raw.trim();
291
- if (!text) return NONE();
292
- s.input.history.push(text);
293
- clearInput(s);
294
- dispatch(s, text, ctx);
295
- return R();
296
- }
297
-
298
- // ------------------------------------------------------------------ files / code panels
299
-
300
- function onFilesKey(s: SextantState, name: string, ctx: KeyCtx): KeyEffect[] | null {
301
- const rows = ctx.rows, F = s.files;
302
- if (name === "up" || name === "down") {
303
- F.cursor = clamp(F.cursor + (name === "up" ? -1 : 1), 0, Math.max(0, rows.length - 1));
304
- const body = Math.max(1, (ctx.layout.files?.h ?? 3) - 2); // panel() body = rect minus borders
305
- if (F.cursor < F.scroll) F.scroll = F.cursor; else if (F.cursor >= F.scroll + body) F.scroll = F.cursor - body + 1;
306
- const r = rows[F.cursor];
307
- if (r && !r.dir) openFile(s, ctx, r.path);
308
- return R();
309
- }
310
- if (name === "left" || name === "right") {
311
- const r = rows[F.cursor];
312
- if (r?.dir) { if (name === "right") F.expanded.add(r.path); else F.expanded.delete(r.path); }
313
- return R();
314
- }
315
- return null;
316
- }
317
-
318
- /** the agents board's body width: drawCode's inner rect (w − 4) minus the 3-wide rail and its 2-cell gap */
319
- const agentsBodyWidth = (code: Rect): number => code.w - 9;
320
-
321
- function onCodeKey(s: SextantState, name: string, ctx: KeyCtx): KeyEffect[] | null {
322
- const c = s.code, n = s.crew.length;
323
- if (c.mode === "agents" && n && !c.laneOpen) { // app.js:1535-1541 — lane grid navigation
324
- const { cols } = gridFor(agentsBodyWidth(ctx.layout.code), n); // the board's own grid (#46), not a lane count
325
- const step: Record<string, number> = { left: n - 1, right: 1, up: n - cols, down: cols };
326
- // own keys only: a key named "constructor" or "__proto__" is not a move (`in` would make lane NaN)
327
- if (Object.hasOwn(step, name)) { c.lane = (c.lane + step[name]!) % n; return R(); }
328
- }
329
- if (name === "up") { c.scroll = Math.max(0, c.scroll - 1); return R(); }
330
- if (name === "down") { c.scroll += 1; return R(); }
331
- if (name === "pageup") { c.scroll = Math.max(0, c.scroll - 10); return R(); }
332
- if (name === "pagedown") { c.scroll += 10; return R(); }
333
- if (name === "left" || name === "right") {
334
- const modes = c.search ? [...CODE_MODE_CYCLE, "search" as CodeMode] : [...CODE_MODE_CYCLE];
335
- const i = Math.max(0, modes.indexOf(c.mode));
336
- setMode(s, ctx, modes[(i + (name === "right" ? 1 : modes.length - 1)) % modes.length]!);
337
- return R();
338
- }
339
- return null;
340
- }
341
-
342
- // ------------------------------------------------------------------ prompt (app.js:1549-1562)
343
-
344
- function onPromptKey(s: SextantState, ev: KeyEvent, ctx: KeyCtx): KeyEffect[] {
345
- const { name } = ev, I = s.input;
346
- if (name === "pageup") { s.stick = false; s.msgScroll = Math.max(0, s.msgScroll - 5); return R(); }
347
- if (name === "pagedown") { s.msgScroll += 5; return R(); }
348
- if (name === "up" || name === "down") return onHistoryKey(s, name === "up" ? -1 : 1, ctx);
349
- if (name === "left" || name === "right") {
350
- const d = name === "left" ? -1 : 1;
351
- if (ev.alt) I.cur = wordJump(I.text, I.cur, d);
352
- else I.cur = clamp(d < 0 ? I.cur - cpBefore(I.text, I.cur) : I.cur + cpAt(I.text, I.cur), 0, I.text.length);
353
- return R();
354
- }
355
- if (name === "home") { I.cur = 0; return R(); }
356
- if (name === "end") { I.cur = I.text.length; s.stick = true; return R(); }
357
- if (name === "backspace") {
358
- if (I.cur > 0) { // the whole code point before the cursor goes (😀 is two UTF-16 units)
359
- const n = cpBefore(I.text, I.cur);
360
- I.text = I.text.slice(0, I.cur - n) + I.text.slice(I.cur);
361
- I.cur -= n; I.sgSel = 0; I.histIdx = -1;
362
- }
363
- s.focus = "messages";
364
- return R();
365
- }
366
- if (name === "delete") {
367
- if (I.cur < I.text.length) { // the whole code point under the cursor goes
368
- I.text = I.text.slice(0, I.cur) + I.text.slice(I.cur + cpAt(I.text, I.cur));
369
- I.sgSel = 0;
370
- }
371
- return R();
372
- }
373
- const ch = name === "space" ? " " : ev.ch;
374
- if (ch && !ev.alt) { insertText(s, ch); return R(); }
375
- return NONE();
376
- }
377
-
378
- /** ↑↓: the open suggestion box first (wrap over its rows), else browse history (app.js:1556) */
379
- function onHistoryKey(s: SextantState, d: -1 | 1, ctx: KeyCtx): KeyEffect[] {
380
- const I = s.input, sugs = I.histIdx < 0 ? openSuggestions(s, ctx) : [];
381
- if (sugs.length) { // no browse running and the box is open: move its highlight
382
- I.sgSel = (clamp(I.sgSel, 0, sugs.length - 1) + d + sugs.length) % sugs.length;
383
- return R();
384
- }
385
- const h = I.history;
386
- if (!h.length) return NONE();
387
- if (I.histIdx < 0) I.histIdx = h.length; // a browse starts just past the newest entry
388
- I.histIdx = clamp(I.histIdx + d, 0, h.length);
389
- I.text = h[I.histIdx] ?? ""; // past the newest again: an empty line (no draft is kept)
390
- I.cur = I.text.length;
391
- I.sgSel = 0;
392
- return R();
393
- }
394
-
395
- /** a pasted line that is ONE image path — what a terminal delivers when a file is dragged onto it
396
- * (Windows Terminal quotes paths with spaces). Syntactic only: keys.ts is pure and never touches the
397
- * disk; app.ts's /attach loads the file, sniffs the magic bytes and reports a non-image as a note. */
398
- export function pastedImagePath(text: string): string | null {
399
- const t = text.trim();
400
- if (t.length === 0 || t.includes("\n")) return null;
401
- const m = /^(?:"([^"]+)"|'([^']+)'|(\S+))$/.exec(t);
402
- const p = m?.[1] ?? m?.[2] ?? m?.[3];
403
- return p !== undefined && /\.(png|jpe?g|gif|webp)$/i.test(p) ? p : null;
404
- }
405
-
406
- function onPaste(s: SextantState, text: string, ctx: KeyCtx): KeyEffect[] {
407
- if (s.help) return NONE();
408
- if (s.palette) { s.palette.query += text.replace(/\s+/g, " "); s.palette.sel = 0; return R(); }
409
- // the connect wizard's KEY step takes its whole input as paste (a key is one paste event, not
410
- // keystrokes) — and a paste it does not take is swallowed, never typed into the composer under it
411
- if (s.wizard) { wizardPaste(s, text); return R(); }
412
- // a dropped image file attaches instead of landing in the prompt as a path string
413
- if (!s.card) {
414
- const img = pastedImagePath(text);
415
- if (img !== null) { ctx.hooks.onSubmit(`/attach "${img}"`); return R(); }
416
- }
417
- const c = s.card;
418
- if (c?.kind === "question" && c.prompt.allowFreeText !== false) {
419
- const onFreeText = c.selected === (c.prompt.options?.length ?? 0); // index options.length = the free-text row
420
- if (onFreeText) { c.freeText += text; return R(); }
421
- }
422
- insertText(s, text);
423
- return R();
424
- }
425
-
426
- /** app.js:1476-1482 — typing pulls focus back to the prompt and drops a history browse */
427
- function insertText(s: SextantState, t: string): void {
428
- const I = s.input;
429
- I.text = I.text.slice(0, I.cur) + t + I.text.slice(I.cur); I.cur += t.length;
430
- I.sgSel = 0; I.histIdx = -1; s.focus = "messages";
431
- }
432
-
433
- function applySuggestion(s: SextantState, sg: Suggestion): void {
434
- s.input.text = sg.apply.text; s.input.cur = sg.apply.cur; s.input.sgSel = 0; s.input.histIdx = -1;
435
- }
436
-
437
- /** the visible suggestion rows: none once Esc dismissed the box (sgSel = -1) */
438
- function openSuggestions(s: SextantState, ctx: KeyCtx): Suggestion[] {
439
- return s.input.sgSel < 0 ? [] : suggestions(s, s.files.paths, ctx.fuzzy);
440
- }
441
-
442
- /** UTF-16 length of the code point that ends at `cur` — 2 for an astral pair such as 😀 */
443
- function cpBefore(text: string, cur: number): number { return cur >= 2 && text.codePointAt(cur - 2)! > 0xffff ? 2 : 1; }
444
- /** UTF-16 length of the code point that starts at `cur` (1 past the end — callers clamp) */
445
- function cpAt(text: string, cur: number): number { return (text.codePointAt(cur) ?? 0) > 0xffff ? 2 : 1; }
446
-
447
- function wordJump(text: string, cur: number, d: -1 | 1): number {
448
- let i = cur;
449
- if (d < 0) { while (i > 0 && /\s/.test(text[i - 1]!)) i--; while (i > 0 && !/\s/.test(text[i - 1]!)) i--; }
450
- else { while (i < text.length && /\s/.test(text[i]!)) i++; while (i < text.length && !/\s/.test(text[i]!)) i++; }
451
- return i;
452
- }
453
-
454
- // ------------------------------------------------------------------ mouse (app.js:1575-1584)
455
-
456
- /** the button kinds come from the #40 parser's mouseKind, which masks the modifier bits (4 shift, 8 meta,
457
- * 16 ctrl) first: shift/alt/ctrl + wheel (b 68/69, 72/73, 80/81) is a wheel — the prototype's raw
458
- * `b === 64 || b === 65` let those fall through into the click walk, so shift+wheel over the `/exit`
459
- * suggestion row ran it; a modified or non-left button, a drag and a release never click */
460
- function onMouse(s: SextantState, ev: MouseEvent, ctx: KeyCtx, now: number): KeyEffect[] {
461
- const kind = mouseKind(ev);
462
- const L = ctx.layout;
463
- if (kind === "wheel-up" || kind === "wheel-down") return onWheel(s, ev, L, kind === "wheel-up" ? -1 : 1, ctx);
464
- // a grabbed zone (a scrollbar thumb) follows the pointer until the button is released
465
- if (kind === "release") { const had = ctx.drag.zone !== null; ctx.drag.zone = null; return had ? R() : NONE(); }
466
- if (kind === "drag") { const z = ctx.drag.zone; if (z?.onDrag) { z.onDrag(ev.y, ev.y - ctx.drag.y0); return R(); } return NONE(); }
467
- if (kind !== "click") return NONE();
468
- for (let i = ctx.hits.length - 1; i >= 0; i--) {
469
- const z = ctx.hits[i]!;
470
- if (!inRect(ev.x, ev.y, z.rect)) continue;
471
- ctx.drag.zone = z.onDrag ? z : null;
472
- ctx.drag.y0 = ev.y;
473
- z.onClick();
474
- if (z.key) handleInput(s, z.key, ctx, now);
475
- return R();
476
- }
477
- // ANY open overlay swallows the fall-through, not just the two that existed when this was written:
478
- // `market` and `context` were added later and never added here, so a click that missed every hit
479
- // zone still moved focus in the cockpit behind the box. openOverlays is the invariant table — asking
480
- // it means the next overlay is covered on the day it is added rather than the day someone notices.
481
- if (openOverlays(s).length > 0) return NONE();
482
- // the main slot is code unless the layout hid the paged panel and it is showing there instead
483
- const main = mainPage(L, s);
484
- const panels: [Focus, Rect | null][] = [
485
- ["messages", L.messages],
486
- ["code", main === "code" ? L.code : null],
487
- ["files", L.files ?? (main === "files" ? L.code : null)],
488
- ];
489
- for (const [f, r] of panels) {
490
- if (r && inRect(ev.x, ev.y, r)) { s.focus = f; return R(); }
491
- }
492
- return NONE();
493
- }
494
-
495
- /** the panel under the pointer scrolls: messages by 2 (wheel-up unsticks the tail), code by 3,
496
- * files by 2; wheel-down never re-sticks — only the drawer knows maxScroll (#44 handoff) */
497
- /** the wheel scrolls the panel under the pointer. Messages: up unsticks (the frame loop re-sticks when the
498
- * view reaches the tail again — scroll-hits.ts followTailIfAtEnd); code clamps in codeScrollTop; the files
499
- * tree — in its panel, or paged into the main slot on a narrow terminal — clamps to its rows here and
500
- * pulls the cursor along, because drawFiles snaps the window back to the cursor otherwise. */
501
- function onWheel(s: SextantState, ev: MouseEvent, L: Layout, d: -1 | 1, ctx: KeyCtx): KeyEffect[] {
502
- // the wheel never reaches the hit zones at all — it does its own rect maths against the panels — so
503
- // with the market open, scrolling over the box scrolled the transcript UNDERNEATH it, invisibly.
504
- // Same fix as onMouse: ask the overlay table rather than naming two of the four by hand.
505
- if (openOverlays(s).length > 0) return NONE();
506
- if (inRect(ev.x, ev.y, L.messages)) {
507
- s.msgScroll = Math.max(0, s.msgScroll + d * 2);
508
- if (d < 0) s.stick = false;
509
- return R();
510
- }
511
- const main = mainPage(L, s);
512
- const filesRect = L.files ?? (main === "files" ? L.code : null);
513
- if (filesRect && inRect(ev.x, ev.y, filesRect)) {
514
- const h = Math.max(1, filesRect.h - 2), f = s.files;
515
- f.scroll = Math.max(0, Math.min(f.scroll + d * 2, Math.max(0, ctx.rows.length - h)));
516
- f.cursor = Math.max(f.scroll, Math.min(f.cursor, f.scroll + h - 1));
517
- return R();
518
- }
519
- if (main === "code" && inRect(ev.x, ev.y, L.code)) { s.code.scroll = Math.max(0, s.code.scroll + d * 3); return R(); }
520
- return NONE();
521
- }
@@ -1,86 +0,0 @@
1
- /** ported from the user's sextant v0.4.0 prototype, src/app.js lines 184-220 (layout, panel) */
2
- /* Panel geometry for the sextant surface and the rounded panel frame. Pure: no clock, no I/O.
3
- layout() mirrors app.js layout() number for number (sextant-layout.test.ts pins nine sizes
4
- computed from the prototype); panel() takes the screen and theme explicitly instead of closing
5
- over them, and measures title/extra widths in cells (the prototype used string length). This is
6
- the ONE panel() of the surface (#44 folded the #41 draw-frame and #42 draw-util copies into it):
7
- the interior is cleared to the theme background and an extra that would not fit is clipped with
8
- an ellipsis before it is dropped — text-identical to the prototype for every extra that fits. */
9
-
10
- import { MIN_COLS, MIN_ROWS, strWidth } from "./screen.ts";
11
- import { st } from "./theme.ts";
12
- import { ATTR, type Layout, type LayoutOptions, type Rect, type ScreenLike, type Seg, type Theme } from "./types.ts";
13
-
14
- /** files column at w ≥ 140 (30 wide from 150, else 26); right column at w ≥ 110 (34 / 28);
15
- * messages = max(8, round(contentH · 0.34)) rows; usage 5 rows; pet 14 rows when contentH ≥ 36 —
16
- * under files when present, else under plan; the size is clamped to the 40×12 floor like Screen. */
17
- export function layout(w: number, h: number, opts: LayoutOptions): Layout {
18
- const W = Math.max(MIN_COLS, w), H = Math.max(MIN_ROWS, h);
19
- const frame: Rect = { x: 0, y: 0, w: W - 1, h: H };
20
- const x0 = 2, x1 = W - 4, y0 = 1, y1 = H - 2;
21
- const showFiles = W >= 140, showRight = W >= 110;
22
- const filesW = showFiles ? (W >= 150 ? 30 : 26) : 0;
23
- const rightW = showRight ? (W >= 150 ? 34 : 28) : 0;
24
- const contentH = y1 - y0 + 1;
25
- const files: Rect | null = showFiles ? { x: x0, y: y0, w: filesW, h: contentH } : null;
26
- const right: Rect | null = showRight ? { x: x1 - rightW + 1, y: y0, w: rightW, h: contentH } : null;
27
- const cx = x0 + (showFiles ? filesW + 1 : 0);
28
- const cw = (right ? right.x - 1 : x1 + 1) - cx;
29
- const msgH = Math.max(8, Math.round(contentH * 0.34));
30
- const code: Rect = { x: cx, y: y0, w: cw, h: contentH - msgH };
31
- const messages: Rect = { x: cx, y: y0 + code.h, w: cw, h: msgH };
32
- const petH = opts.pet && contentH >= 36 ? 14 : 0;
33
- let plan: Rect | null = null, usage: Rect | null = null, pet: Rect | null = null;
34
- if (right) {
35
- const usageH = 5;
36
- plan = { x: right.x, y: y0, w: rightW, h: contentH - usageH };
37
- usage = { x: right.x, y: y0 + plan.h, w: rightW, h: usageH };
38
- }
39
- if (petH && files) { files.h = contentH - petH; pet = { x: x0, y: y0 + files.h, w: filesW, h: petH }; }
40
- else if (petH && right && plan) { plan.h -= petH; pet = { x: right.x, y: y0 + plan.h, w: rightW, h: petH }; }
41
- return { w: W, h: H, frame, files, code, messages, plan, usage, pet };
42
- }
43
-
44
- /** cells a segment list occupies (East-Asian-width aware, like the borders) */
45
- export const segWidth = (segs: readonly Seg[]): number => segs.reduce((n, [t]) => n + strWidth(t), 0);
46
-
47
- /** the longest prefix of `s` that fits `w` cells */
48
- function headCells(s: string, w: number): string {
49
- let out = "", used = 0;
50
- for (const c of s) { const cw = strWidth(c); if (used + cw > w) break; out += c; used += cw; }
51
- return out;
52
- }
53
-
54
- /** clip the widest segment (with an ellipsis) until the run fits `room` cells; [] when hopeless
55
- * (room under 6 cells, or fewer than 3 cells of the clipped text would survive) */
56
- export function fitSegs(segs: readonly Seg[], room: number): Seg[] {
57
- let out: Seg[] = segs.filter(([t]) => t.length > 0);
58
- let ew = segWidth(out);
59
- if (ew <= room) return out;
60
- if (room < 6) return [];
61
- let widest = 0;
62
- out.forEach(([t], i) => { if (strWidth(t) > strWidth(out[widest]?.[0] ?? "")) widest = i; });
63
- const [text, style] = out[widest]!;
64
- const keep = strWidth(text) - (ew - room) - 1;
65
- if (keep < 3) return [];
66
- out = out.map((sg, i) => (i === widest ? [headCells(text, keep) + "…", style] : sg));
67
- ew = segWidth(out);
68
- return ew <= room ? out : [];
69
- }
70
-
71
- /** rounded panel with the title in the top border and right-aligned extra segments; returns the inner rect.
72
- * Interior cleared to the theme background; the title is clipped to the box; extras must leave a
73
- * `─` between themselves and the title (the prototype's strict `ex > x + 4 + titleW`) — when they do
74
- * not fit, the widest one is clipped with an ellipsis (fitSegs), else they are dropped.
75
- * Border/title colors: `color` when given, else accent when focused, else frame (border) / fg2 (title). */
76
- export function panel(scr: ScreenLike, P: Rect, title: string, focused: boolean, extra: readonly Seg[] | undefined, C: Theme, color?: number): Rect {
77
- const col = color ?? (focused ? C.accent : C.frame);
78
- scr.box(P.x, P.y, P.w, P.h, st(col), C.bg);
79
- if (title) scr.text(P.x + 2, P.y, [[" ", st(-1)], [title, st(color ?? (focused ? C.accent : C.fg2), -1, ATTR.BOLD)], [" ", st(-1)]], Math.max(0, P.w - 4));
80
- if (extra && extra.length) {
81
- const segs = fitSegs(extra, P.w - 8 - strWidth(title));
82
- const ew = segWidth(segs);
83
- if (ew > 0) scr.text(P.x + P.w - 3 - ew, P.y, [[" ", st(-1)], ...segs, [" ", st(-1)]]);
84
- }
85
- return { x: P.x + 2, y: P.y + 1, w: P.w - 4, h: P.h - 2 };
86
- }