rovecode 0.3.3 → 0.4.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (495) hide show
  1. package/CHANGELOG.md +527 -0
  2. package/README.md +46 -48
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-j6gn14w3.js +2 -0
  5. package/dist/cli/ask-user-cwstt8fz.js +2 -0
  6. package/dist/cli/auth-login-9bbp9915.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-16zqdms5.js +9 -0
  9. package/dist/cli/catalog-1xchffa4.js +2 -0
  10. package/dist/cli/cli-1n1zb64f.js +2 -0
  11. package/dist/cli/client-2t9gjkck.js +2 -0
  12. package/dist/cli/commands-exafvm2b.js +2 -0
  13. package/dist/cli/connect-6zde0kn3.js +2 -0
  14. package/dist/cli/context-cmd-5t43wgqt.js +2 -0
  15. package/dist/cli/context-report-kt01pw8y.js +2 -0
  16. package/dist/cli/count-remote-ap7x3vh6.js +2 -0
  17. package/dist/cli/design-ne5zszyh.js +2 -0
  18. package/dist/cli/dispatch-2r5myxye.js +2 -0
  19. package/dist/cli/doctor-ws4fh4tn.js +3 -0
  20. package/dist/cli/executor-bdrjn634.js +2 -0
  21. package/dist/cli/export-1mxb9g5p.js +2 -0
  22. package/dist/cli/files-g104xghh.js +2 -0
  23. package/dist/cli/gauntlet-07xrjpj7.js +2 -0
  24. package/dist/cli/gauntlet-runner-xvy64436.js +10 -0
  25. package/dist/cli/gauntlet-wave3-jm91yt5w.js +5 -0
  26. package/dist/cli/gauntlet-wave4-r13py7p1.js +14 -0
  27. package/dist/cli/hashline-znvrat11.js +2 -0
  28. package/dist/cli/http-xafw6fsh.js +143 -0
  29. package/dist/cli/index-1sgjm25y.js +2 -0
  30. package/dist/cli/init-g2m0tn4m.js +51 -0
  31. package/dist/cli/install-avaqjjqq.js +2 -0
  32. package/dist/cli/loop-mmpfft01.js +2 -0
  33. package/dist/cli/main-0904f6ps.js +5 -0
  34. package/dist/cli/main-0ab9fc26.js +9 -0
  35. package/dist/cli/main-0jys2ccn.js +3 -0
  36. package/dist/cli/main-0mtcdbs7.js +3 -0
  37. package/dist/cli/main-0z1w2zsg.js +3 -0
  38. package/dist/cli/main-1dchs7xv.js +18 -0
  39. package/dist/cli/main-1ereejm1.js +3 -0
  40. package/dist/cli/main-1k1kw6b5.js +3 -0
  41. package/dist/cli/main-27y4sm2k.js +38 -0
  42. package/dist/cli/main-2wwjex5j.js +58 -0
  43. package/dist/cli/main-2yeveeve.js +6 -0
  44. package/dist/cli/main-2yfck9b5.js +3 -0
  45. package/dist/cli/main-2zmzgkwh.js +3 -0
  46. package/dist/cli/main-351pz3z7.js +7 -0
  47. package/dist/cli/main-3gjqfh7a.js +6 -0
  48. package/dist/cli/main-3nf3kgve.js +3 -0
  49. package/dist/cli/main-3pjrb2hd.js +3 -0
  50. package/dist/cli/main-3rxcvgna.js +19 -0
  51. package/dist/cli/main-4b3jgy66.js +19 -0
  52. package/dist/cli/main-4wndhjdc.js +7 -0
  53. package/dist/cli/main-4xcmvxnk.js +3 -0
  54. package/dist/cli/main-5tbz0wbz.js +4 -0
  55. package/dist/cli/main-5ywnwthm.js +3 -0
  56. package/dist/cli/main-6b62vkz0.js +14 -0
  57. package/dist/cli/main-6dnk69vp.js +3 -0
  58. package/dist/cli/main-6genrmhs.js +136 -0
  59. package/dist/cli/main-73g7eff4.js +15 -0
  60. package/dist/cli/main-7c5thhjd.js +5 -0
  61. package/dist/cli/main-7rn6bqje.js +3 -0
  62. package/dist/cli/main-80haw7qk.js +4 -0
  63. package/dist/cli/main-875s60s2.js +4 -0
  64. package/dist/cli/main-8kjxbpw4.js +8 -0
  65. package/dist/cli/main-90ds1z4e.js +10 -0
  66. package/dist/cli/main-9etavkew.js +3 -0
  67. package/dist/cli/main-a9njrkk1.js +3 -0
  68. package/dist/cli/main-aecrjq2d.js +12 -0
  69. package/dist/cli/main-ck9asesq.js +9 -0
  70. package/dist/cli/main-cta9racd.js +4 -0
  71. package/dist/cli/main-ddv7j2ag.js +3 -0
  72. package/dist/cli/main-dfreez27.js +10 -0
  73. package/dist/cli/main-f7rw7des.js +3 -0
  74. package/dist/cli/main-ggcn7rd7.js +5 -0
  75. package/dist/cli/main-gzkmycnv.js +3 -0
  76. package/dist/cli/main-hq51jg8v.js +18 -0
  77. package/dist/cli/main-jft389w9.js +8 -0
  78. package/dist/cli/main-k1eqkg83.js +3 -0
  79. package/dist/cli/main-k2y8a2aw.js +9 -0
  80. package/dist/cli/main-kcpbykxz.js +4 -0
  81. package/dist/cli/main-kd488vje.js +22 -0
  82. package/dist/cli/main-kh32yvgk.js +5 -0
  83. package/dist/cli/main-kqxnqjnv.js +25 -0
  84. package/dist/cli/main-kyn0xnsg.js +3 -0
  85. package/dist/cli/main-m1kk6fp5.js +21 -0
  86. package/dist/cli/main-mv40pcr2.js +4 -0
  87. package/dist/cli/main-n0t3973w.js +3 -0
  88. package/dist/cli/main-nqveez48.js +4 -0
  89. package/dist/cli/main-pknhvrmj.js +3 -0
  90. package/dist/cli/main-pn1w7a7j.js +3 -0
  91. package/dist/cli/main-prxxs70n.js +4 -0
  92. package/dist/cli/main-q3vsesf9.js +3 -0
  93. package/dist/cli/main-qsevpgsv.js +3 -0
  94. package/dist/cli/main-rdgdw24b.js +25 -0
  95. package/dist/cli/main-rfth4tbm.js +16 -0
  96. package/dist/cli/main-rg0wn0xf.js +5 -0
  97. package/dist/cli/main-sdmxhtv8.js +4 -0
  98. package/dist/cli/main-skbp13js.js +18 -0
  99. package/dist/cli/main-t4xnd213.js +7 -0
  100. package/dist/cli/main-vqak588n.js +4 -0
  101. package/dist/cli/main-w2n1303f.js +9 -0
  102. package/dist/cli/main-wbrdspr2.js +5 -0
  103. package/dist/cli/main-wsrg79c1.js +7 -0
  104. package/dist/cli/main-x4r0fne4.js +5 -0
  105. package/dist/cli/main-xea2f3tn.js +6 -0
  106. package/dist/cli/main-xg704a3c.js +3 -0
  107. package/dist/cli/main-xvnrabfp.js +16 -0
  108. package/dist/cli/main-xy53xf0r.js +4 -0
  109. package/dist/cli/main-y1fqy60y.js +3 -0
  110. package/dist/cli/main-yn8cd281.js +34 -0
  111. package/dist/cli/main-yr0ksc0h.js +4 -0
  112. package/dist/cli/main-z2ex2vyf.js +4 -0
  113. package/dist/cli/main-z3aayzvq.js +3 -0
  114. package/dist/cli/main-zaqh35jg.js +3 -0
  115. package/dist/cli/main-zc2e8e46.js +4 -0
  116. package/dist/cli/main-zzrfw6cf.js +13 -0
  117. package/dist/cli/main.js +280 -0
  118. package/dist/cli/market-cmd-e14kmx9n.js +5 -0
  119. package/dist/cli/mcp-login-wq7ktdek.js +2 -0
  120. package/dist/cli/mcp-market-cmd-9mg3jecy.js +2 -0
  121. package/dist/cli/notify-b7qc0cjb.js +2 -0
  122. package/dist/cli/oauth-z8whcgfx.js +2 -0
  123. package/dist/cli/output-b3ewj3ps.js +16 -0
  124. package/dist/cli/profiles-6mr5he5e.js +2 -0
  125. package/dist/cli/provider-config-g7j42q8x.js +2 -0
  126. package/dist/cli/provider-jr1y8vvm.js +2 -0
  127. package/dist/cli/registry-s8yk86g0.js +2 -0
  128. package/dist/cli/registry-t6p8d4mn.js +2 -0
  129. package/dist/cli/repl-bajwe1mh.js +11 -0
  130. package/dist/cli/resume-rwn9nz7y.js +2 -0
  131. package/dist/cli/run-flags-nah7ndpt.js +2 -0
  132. package/dist/cli/runtime-n7gafzhb.js +2 -0
  133. package/dist/cli/sandbox-config-emdy18x4.js +2 -0
  134. package/dist/cli/server-b0nvs2bn.js +5 -0
  135. package/dist/cli/session-arg-y75wd4kj.js +2 -0
  136. package/dist/cli/session-j62evmjq.js +2 -0
  137. package/dist/cli/sessions-cmd-tsnwz0ns.js +7 -0
  138. package/dist/cli/settings-df10wfez.js +2 -0
  139. package/dist/cli/setup-jzvv72fg.js +2 -0
  140. package/dist/cli/sextant-smoke-37m81ke6.js +5 -0
  141. package/dist/cli/skills-cmd-gjxnxnhx.js +2 -0
  142. package/dist/cli/smoke-p7748apt.js +8 -0
  143. package/dist/cli/start-chat-s4st3mm0.js +12 -0
  144. package/dist/cli/stream-gmeyewds.js +2 -0
  145. package/dist/cli/task-gh0kkp3n.js +2 -0
  146. package/dist/cli/tasks-z1kfpe8e.js +2 -0
  147. package/dist/cli/thinking-0eqkrz6t.js +2 -0
  148. package/dist/cli/todo-5brcrt9m.js +2 -0
  149. package/dist/cli/tools-7pzm0vj9.js +2 -0
  150. package/dist/cli/tools-s635p6s8.js +2 -0
  151. package/dist/cli/trust-cmd-cjav8zgm.js +2 -0
  152. package/dist/cli/update-check-pt31bm2f.js +2 -0
  153. package/dist/cli/update-cmd-tk131s9t.js +2 -0
  154. package/dist/cli/voice-56nabd8d.js +2 -0
  155. package/dist/cli/webfetch-xd8q596m.js +2 -0
  156. package/dist/cli/websearch-5hkf98k1.js +2 -0
  157. package/dist/cli/workflow-cmd-cy3cvzjp.js +4 -0
  158. package/dist/cli/workspace-q10g5z3e.js +2 -0
  159. package/dist/lib/index.js +62 -0
  160. package/dist/lib/models-index.json +1 -0
  161. package/dist/lib/plugins.js +55 -0
  162. package/dist/lib/providers.js +17 -0
  163. package/dist/lib/public-api.js +20 -0
  164. package/dist/lib/sdk.js +360 -0
  165. package/package.json +84 -72
  166. package/bin/rovecode.ts +0 -21
  167. package/src/acp/server.ts +0 -374
  168. package/src/cli/auth-login.ts +0 -122
  169. package/src/cli/connect.ts +0 -244
  170. package/src/cli/context-cmd.ts +0 -199
  171. package/src/cli/dispatch.ts +0 -82
  172. package/src/cli/doctor.ts +0 -362
  173. package/src/cli/export.ts +0 -276
  174. package/src/cli/help.ts +0 -293
  175. package/src/cli/is-tui-invocation.ts +0 -8
  176. package/src/cli/main.ts +0 -583
  177. package/src/cli/market-cmd.ts +0 -658
  178. package/src/cli/mcp-login.ts +0 -141
  179. package/src/cli/mcp-market-cmd.ts +0 -302
  180. package/src/cli/output.ts +0 -382
  181. package/src/cli/repl.ts +0 -250
  182. package/src/cli/repomap-root.ts +0 -14
  183. package/src/cli/resume.ts +0 -57
  184. package/src/cli/run-flags.ts +0 -43
  185. package/src/cli/run-limits.ts +0 -78
  186. package/src/cli/runtime.ts +0 -931
  187. package/src/cli/session-arg.ts +0 -30
  188. package/src/cli/sessions-cmd.ts +0 -145
  189. package/src/cli/setup.ts +0 -153
  190. package/src/cli/skills-cmd.ts +0 -194
  191. package/src/cli/start-chat.ts +0 -65
  192. package/src/cli/trust-cmd.ts +0 -52
  193. package/src/coding/bash.ts +0 -148
  194. package/src/coding/checkpoints.ts +0 -327
  195. package/src/coding/diff.ts +0 -138
  196. package/src/coding/files.ts +0 -341
  197. package/src/coding/hashline.ts +0 -274
  198. package/src/coding/lsp-gate.ts +0 -254
  199. package/src/coding/lsp-servers.ts +0 -147
  200. package/src/coding/lsp.ts +0 -283
  201. package/src/coding/repomap-cache.ts +0 -99
  202. package/src/coding/repomap-files.ts +0 -192
  203. package/src/coding/repomap.ts +0 -481
  204. package/src/core/agents.ts +0 -255
  205. package/src/core/compaction.ts +0 -259
  206. package/src/core/config.ts +0 -289
  207. package/src/core/context-report.ts +0 -228
  208. package/src/core/context.ts +0 -60
  209. package/src/core/count-remote.ts +0 -107
  210. package/src/core/execpolicy-rules.ts +0 -196
  211. package/src/core/execpolicy.ts +0 -385
  212. package/src/core/executor.ts +0 -454
  213. package/src/core/guardrails.ts +0 -400
  214. package/src/core/hooks.ts +0 -411
  215. package/src/core/images.ts +0 -230
  216. package/src/core/intro.ts +0 -266
  217. package/src/core/loop.ts +0 -567
  218. package/src/core/modes.ts +0 -372
  219. package/src/core/orchestrator.ts +0 -245
  220. package/src/core/proc-group.ts +0 -48
  221. package/src/core/project-trust.ts +0 -98
  222. package/src/core/reflection.ts +0 -165
  223. package/src/core/sandbox-config.ts +0 -186
  224. package/src/core/session-id.ts +0 -24
  225. package/src/core/session-images.ts +0 -73
  226. package/src/core/session-ops.ts +0 -183
  227. package/src/core/session-text.ts +0 -29
  228. package/src/core/session.ts +0 -469
  229. package/src/core/settings.ts +0 -170
  230. package/src/core/tasks.ts +0 -646
  231. package/src/core/token-scale.ts +0 -108
  232. package/src/core/tools.ts +0 -309
  233. package/src/core/trust.ts +0 -104
  234. package/src/core/types.ts +0 -330
  235. package/src/core/update-check.ts +0 -171
  236. package/src/core/usage.ts +0 -204
  237. package/src/core/validate.ts +0 -121
  238. package/src/core/verify-gate.ts +0 -159
  239. package/src/core/verify.ts +0 -236
  240. package/src/core/voice.ts +0 -158
  241. package/src/core/win-job.ts +0 -183
  242. package/src/core/workspace.ts +0 -184
  243. package/src/design/audit.ts +0 -797
  244. package/src/design/direction.ts +0 -190
  245. package/src/design/rules.ts +0 -157
  246. package/src/eval/bench.ts +0 -150
  247. package/src/eval/gauntlet-runner.ts +0 -215
  248. package/src/eval/gauntlet-support.ts +0 -84
  249. package/src/eval/gauntlet-wave3.ts +0 -269
  250. package/src/eval/gauntlet-wave4.ts +0 -217
  251. package/src/eval/gauntlet.ts +0 -253
  252. package/src/index.ts +0 -17
  253. package/src/lanes/agy.ts +0 -95
  254. package/src/lanes/approval.ts +0 -24
  255. package/src/lanes/claude.ts +0 -129
  256. package/src/lanes/codex.ts +0 -127
  257. package/src/lanes/events.ts +0 -130
  258. package/src/lanes/job.ts +0 -142
  259. package/src/lanes/opencode.ts +0 -122
  260. package/src/lanes/process.ts +0 -184
  261. package/src/lanes/progress.ts +0 -183
  262. package/src/lanes/registry.ts +0 -178
  263. package/src/lanes/runner.ts +0 -124
  264. package/src/lanes/types.ts +0 -112
  265. package/src/market/catalogs/mcp-docs.json +0 -111
  266. package/src/market/catalogs/plugins.json +0 -111
  267. package/src/market/catalogs/skills.json +0 -478
  268. package/src/market/clone.ts +0 -72
  269. package/src/market/context-cost.ts +0 -121
  270. package/src/market/digest.ts +0 -106
  271. package/src/market/index.ts +0 -22
  272. package/src/market/install.ts +0 -578
  273. package/src/market/manifest.ts +0 -187
  274. package/src/market/prereq.ts +0 -145
  275. package/src/market/registry.ts +0 -363
  276. package/src/market/resolve.ts +0 -111
  277. package/src/market/types.ts +0 -236
  278. package/src/market/validate.ts +0 -227
  279. package/src/mcp/client.ts +0 -449
  280. package/src/mcp/config.ts +0 -252
  281. package/src/mcp/local-package.ts +0 -211
  282. package/src/mcp/market-catalog.ts +0 -84
  283. package/src/mcp/market-install.ts +0 -289
  284. package/src/mcp/market.ts +0 -362
  285. package/src/mcp/oauth.ts +0 -251
  286. package/src/mcp/prompts-resources.ts +0 -249
  287. package/src/mcp/shared.ts +0 -149
  288. package/src/mcp/status.ts +0 -67
  289. package/src/mcp/tools.ts +0 -275
  290. package/src/mcp/transport.ts +0 -122
  291. package/src/mcp/trust.ts +0 -25
  292. package/src/memory/blocks.ts +0 -278
  293. package/src/memory/recall.ts +0 -355
  294. package/src/memory/scope.ts +0 -182
  295. package/src/memory/store.ts +0 -105
  296. package/src/memory/tools.ts +0 -99
  297. package/src/plugins/cli.ts +0 -119
  298. package/src/plugins/discover.ts +0 -108
  299. package/src/plugins/index.ts +0 -50
  300. package/src/plugins/install.ts +0 -184
  301. package/src/plugins/load.ts +0 -124
  302. package/src/plugins/manifest.ts +0 -92
  303. package/src/plugins/state.ts +0 -83
  304. package/src/providers/auth.ts +0 -408
  305. package/src/providers/cache.ts +0 -223
  306. package/src/providers/catalog-local.ts +0 -160
  307. package/src/providers/catalog.ts +0 -421
  308. package/src/providers/middleware-context.ts +0 -86
  309. package/src/providers/middleware.ts +0 -373
  310. package/src/providers/model-list.ts +0 -23
  311. package/src/providers/oauth/common.ts +0 -105
  312. package/src/providers/oauth/device-code.ts +0 -107
  313. package/src/providers/oauth/github-copilot.ts +0 -146
  314. package/src/providers/oauth/loopback.ts +0 -158
  315. package/src/providers/oauth/openai.ts +0 -163
  316. package/src/providers/oauth/openrouter.ts +0 -89
  317. package/src/providers/oauth/pkce.ts +0 -45
  318. package/src/providers/oauth/registry.ts +0 -39
  319. package/src/providers/oauth/seam.ts +0 -89
  320. package/src/providers/profile-glm53.ts +0 -111
  321. package/src/providers/profile-sonnet5-persona.ts +0 -65
  322. package/src/providers/profile-sonnet5-voice.ts +0 -23
  323. package/src/providers/profiles.ts +0 -156
  324. package/src/providers/provider-config.ts +0 -311
  325. package/src/providers/registry.ts +0 -333
  326. package/src/providers/responses.ts +0 -209
  327. package/src/providers/retry.ts +0 -234
  328. package/src/providers/router.ts +0 -294
  329. package/src/providers/sse.ts +0 -26
  330. package/src/providers/stream-errors.ts +0 -117
  331. package/src/providers/stream.ts +0 -566
  332. package/src/providers/thinking.ts +0 -189
  333. package/src/providers/wire-messages.ts +0 -129
  334. package/src/providers/wire-responses.ts +0 -79
  335. package/src/providers/wire-select.ts +0 -53
  336. package/src/server/http.ts +0 -291
  337. package/src/server/openapi.ts +0 -246
  338. package/src/sextant/card-hits.ts +0 -102
  339. package/src/sextant/card-keys.ts +0 -55
  340. package/src/sextant/context-source.ts +0 -157
  341. package/src/sextant/crew-cards.ts +0 -350
  342. package/src/sextant/draw-agents.ts +0 -273
  343. package/src/sextant/draw-code.ts +0 -388
  344. package/src/sextant/draw-context.ts +0 -222
  345. package/src/sextant/draw-frame.ts +0 -164
  346. package/src/sextant/draw-market.ts +0 -573
  347. package/src/sextant/draw-messages.ts +0 -386
  348. package/src/sextant/draw-pet.ts +0 -230
  349. package/src/sextant/draw-plan.ts +0 -187
  350. package/src/sextant/draw-tabs.ts +0 -85
  351. package/src/sextant/draw-util.ts +0 -65
  352. package/src/sextant/draw-wizard.ts +0 -378
  353. package/src/sextant/engine.ts +0 -230
  354. package/src/sextant/frame-hits.ts +0 -25
  355. package/src/sextant/frame.ts +0 -101
  356. package/src/sextant/git-status.ts +0 -197
  357. package/src/sextant/grid.ts +0 -59
  358. package/src/sextant/input.ts +0 -119
  359. package/src/sextant/keys.ts +0 -521
  360. package/src/sextant/layout.ts +0 -86
  361. package/src/sextant/local-commands.ts +0 -169
  362. package/src/sextant/market-source.ts +0 -287
  363. package/src/sextant/mentions.ts +0 -200
  364. package/src/sextant/message-hits.ts +0 -26
  365. package/src/sextant/model.ts +0 -387
  366. package/src/sextant/overlays.ts +0 -456
  367. package/src/sextant/panel-hits.ts +0 -38
  368. package/src/sextant/pet.ts +0 -399
  369. package/src/sextant/screen.ts +0 -324
  370. package/src/sextant/scroll-hits.ts +0 -66
  371. package/src/sextant/scrollbar.ts +0 -82
  372. package/src/sextant/sextant-bridge.ts +0 -174
  373. package/src/sextant/sextant-cards.ts +0 -142
  374. package/src/sextant/sextant-diff-base.ts +0 -63
  375. package/src/sextant/sextant-files.ts +0 -154
  376. package/src/sextant/sextant-frame-loop.ts +0 -335
  377. package/src/sextant/sextant-renderer.ts +0 -574
  378. package/src/sextant/sextant-repo.ts +0 -140
  379. package/src/sextant/theme.ts +0 -66
  380. package/src/sextant/tool-rows.ts +0 -189
  381. package/src/sextant/types.ts +0 -493
  382. package/src/skills/index.ts +0 -387
  383. package/src/skills/pack.ts +0 -220
  384. package/src/skills/spec.ts +0 -162
  385. package/src/skills/tools.ts +0 -69
  386. package/src/skills/versioned.ts +0 -227
  387. package/src/telemetry/otel-export.ts +0 -122
  388. package/src/telemetry/otel-lanes.ts +0 -89
  389. package/src/telemetry/otel-logs.ts +0 -131
  390. package/src/telemetry/otel-metrics.ts +0 -136
  391. package/src/telemetry/otel.ts +0 -397
  392. package/src/telemetry/otlp.ts +0 -76
  393. package/src/tools/ask-user.ts +0 -156
  394. package/src/tools/bash-bg.ts +0 -94
  395. package/src/tools/bash-jobs.ts +0 -237
  396. package/src/tools/design.ts +0 -151
  397. package/src/tools/evalcell.ts +0 -338
  398. package/src/tools/html-text.ts +0 -139
  399. package/src/tools/provider.ts +0 -149
  400. package/src/tools/task.ts +0 -250
  401. package/src/tools/todo.ts +0 -320
  402. package/src/tools/webfetch.ts +0 -332
  403. package/src/tools/websearch.ts +0 -359
  404. package/src/tui/agents-cmd.ts +0 -41
  405. package/src/tui/app.ts +0 -749
  406. package/src/tui/attach.ts +0 -127
  407. package/src/tui/boot-notes.ts +0 -41
  408. package/src/tui/builtin-prompts.ts +0 -59
  409. package/src/tui/checkpoints-cmd.ts +0 -70
  410. package/src/tui/clipboard-image.ts +0 -81
  411. package/src/tui/clipboard.ts +0 -78
  412. package/src/tui/commands.ts +0 -283
  413. package/src/tui/config-view.ts +0 -53
  414. package/src/tui/context-cmds.ts +0 -282
  415. package/src/tui/cost.ts +0 -108
  416. package/src/tui/crash-guard.ts +0 -173
  417. package/src/tui/focus-terminal.ts +0 -34
  418. package/src/tui/git-cmds.ts +0 -273
  419. package/src/tui/git-plain.ts +0 -58
  420. package/src/tui/info-cmd.ts +0 -150
  421. package/src/tui/input-plain.ts +0 -76
  422. package/src/tui/mcp-cmd.ts +0 -128
  423. package/src/tui/memory-note.ts +0 -77
  424. package/src/tui/modes-cmd.ts +0 -45
  425. package/src/tui/notify-seq.ts +0 -100
  426. package/src/tui/notify.ts +0 -318
  427. package/src/tui/overlays.ts +0 -97
  428. package/src/tui/pi-renderer.ts +0 -428
  429. package/src/tui/providers-cmd.ts +0 -377
  430. package/src/tui/reasoning-view.ts +0 -56
  431. package/src/tui/renderer.ts +0 -128
  432. package/src/tui/replay-marker.ts +0 -29
  433. package/src/tui/session-cmd.ts +0 -148
  434. package/src/tui/session-manage.ts +0 -95
  435. package/src/tui/sextant-attach.ts +0 -102
  436. package/src/tui/sextant-io.ts +0 -202
  437. package/src/tui/sextant-smoke.ts +0 -110
  438. package/src/tui/shell-cmd.ts +0 -158
  439. package/src/tui/smoke.ts +0 -72
  440. package/src/tui/staged-terminal.ts +0 -50
  441. package/src/tui/startup.ts +0 -12
  442. package/src/tui/theme.ts +0 -59
  443. package/src/tui/todo-label.ts +0 -7
  444. package/src/tui/trust-card.ts +0 -107
  445. package/src/tui/tui-commands.ts +0 -87
  446. package/tsconfig.json +0 -30
  447. package/vendor/pi-tui/LICENSE +0 -21
  448. package/vendor/pi-tui/PATCHES.md +0 -12
  449. package/vendor/pi-tui/PROVENANCE.md +0 -12
  450. package/vendor/pi-tui/README.upstream.md +0 -854
  451. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  452. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  453. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  454. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  455. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  456. package/vendor/pi-tui/src/components/box.ts +0 -138
  457. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  458. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  459. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  460. package/vendor/pi-tui/src/components/image.ts +0 -128
  461. package/vendor/pi-tui/src/components/input.ts +0 -448
  462. package/vendor/pi-tui/src/components/loader.ts +0 -93
  463. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  464. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  465. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  466. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  467. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  468. package/vendor/pi-tui/src/components/stack.ts +0 -155
  469. package/vendor/pi-tui/src/components/text.ts +0 -108
  470. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  471. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  472. package/vendor/pi-tui/src/editor-component.ts +0 -75
  473. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  474. package/vendor/pi-tui/src/index.ts +0 -149
  475. package/vendor/pi-tui/src/keybindings.ts +0 -321
  476. package/vendor/pi-tui/src/keys.ts +0 -1402
  477. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  478. package/vendor/pi-tui/src/latex.ts +0 -1381
  479. package/vendor/pi-tui/src/layout-node.ts +0 -52
  480. package/vendor/pi-tui/src/layout.ts +0 -411
  481. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  482. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  483. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  484. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  485. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  486. package/vendor/pi-tui/src/terminal.ts +0 -554
  487. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  488. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  489. package/vendor/pi-tui/src/tui.ts +0 -1264
  490. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  491. package/vendor/pi-tui/src/utils.ts +0 -1327
  492. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  493. package/vendor/pi-tui/test/test-themes.ts +0 -39
  494. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  495. /package/{src/providers → dist/cli}/models-index.json +0 -0
@@ -1,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
- }