rovecode 0.3.2 → 0.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (491) hide show
  1. package/CHANGELOG.md +512 -0
  2. package/README.md +33 -45
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-dybnr56b.js +2 -0
  5. package/dist/cli/ask-user-p8hq4xgj.js +2 -0
  6. package/dist/cli/auth-login-ewpgw5sm.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-xv3ypwev.js +9 -0
  9. package/dist/cli/catalog-737wb2s0.js +2 -0
  10. package/dist/cli/cli-arhg40m0.js +2 -0
  11. package/dist/cli/client-cf2pxx8q.js +2 -0
  12. package/dist/cli/commands-3p7e4xxs.js +2 -0
  13. package/dist/cli/connect-3q93d7cb.js +2 -0
  14. package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
  15. package/dist/cli/context-report-hbw9zfes.js +2 -0
  16. package/dist/cli/count-remote-mby98cd0.js +2 -0
  17. package/dist/cli/design-122y0axd.js +2 -0
  18. package/dist/cli/dispatch-b4egzvvh.js +2 -0
  19. package/dist/cli/doctor-x4jkv72e.js +3 -0
  20. package/dist/cli/executor-ftvg6tsy.js +2 -0
  21. package/dist/cli/export-pdgdhkch.js +2 -0
  22. package/dist/cli/files-cez9a96p.js +2 -0
  23. package/dist/cli/gauntlet-r3xxaszc.js +2 -0
  24. package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
  25. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
  26. package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
  27. package/dist/cli/hashline-ewg5hbe3.js +2 -0
  28. package/dist/cli/http-n0kehsk8.js +5 -0
  29. package/dist/cli/index-z5qt1s76.js +2 -0
  30. package/dist/cli/install-80mp63kx.js +2 -0
  31. package/dist/cli/loop-12twjcat.js +2 -0
  32. package/dist/cli/main-01pv9206.js +4 -0
  33. package/dist/cli/main-0jys2ccn.js +3 -0
  34. package/dist/cli/main-1ztz6fkj.js +10 -0
  35. package/dist/cli/main-23q7cmww.js +9 -0
  36. package/dist/cli/main-2rzbexn2.js +3 -0
  37. package/dist/cli/main-2wyax8k9.js +9 -0
  38. package/dist/cli/main-2yeveeve.js +6 -0
  39. package/dist/cli/main-2z3dek0b.js +3 -0
  40. package/dist/cli/main-2zgsknth.js +3 -0
  41. package/dist/cli/main-45ejth3a.js +4 -0
  42. package/dist/cli/main-45rn3trk.js +22 -0
  43. package/dist/cli/main-4p4e2w7x.js +4 -0
  44. package/dist/cli/main-4y0tnfpa.js +16 -0
  45. package/dist/cli/main-5py0rkmc.js +4 -0
  46. package/dist/cli/main-6dtqmbt6.js +7 -0
  47. package/dist/cli/main-6h9x282m.js +4 -0
  48. package/dist/cli/main-6vjeds42.js +3 -0
  49. package/dist/cli/main-78gq4bt9.js +6 -0
  50. package/dist/cli/main-7jd5vh3x.js +4 -0
  51. package/dist/cli/main-7kt6r53y.js +4 -0
  52. package/dist/cli/main-8c1tbazx.js +58 -0
  53. package/dist/cli/main-9a9rnh47.js +19 -0
  54. package/dist/cli/main-9ht36z12.js +3 -0
  55. package/dist/cli/main-a2yfvcy9.js +7 -0
  56. package/dist/cli/main-a3f51n0x.js +5 -0
  57. package/dist/cli/main-b8zq261k.js +3 -0
  58. package/dist/cli/main-bxtvnf6d.js +13 -0
  59. package/dist/cli/main-edxc3yzt.js +4 -0
  60. package/dist/cli/main-evgz4mp5.js +21 -0
  61. package/dist/cli/main-f33fc5je.js +9 -0
  62. package/dist/cli/main-fvnpq46y.js +12 -0
  63. package/dist/cli/main-gbbty4d4.js +3 -0
  64. package/dist/cli/main-gth53dnt.js +25 -0
  65. package/dist/cli/main-hqbz10aw.js +9 -0
  66. package/dist/cli/main-hrrvcfan.js +38 -0
  67. package/dist/cli/main-hzwtsb2m.js +5 -0
  68. package/dist/cli/main-j7ttv0sd.js +34 -0
  69. package/dist/cli/main-jak598k9.js +5 -0
  70. package/dist/cli/main-kba6zeyd.js +6 -0
  71. package/dist/cli/main-kwwsz6rq.js +3 -0
  72. package/dist/cli/main-m8vm17zq.js +3 -0
  73. package/dist/cli/main-mg4f96e1.js +3 -0
  74. package/dist/cli/main-mg9b20ac.js +18 -0
  75. package/dist/cli/main-mgb9ccnx.js +3 -0
  76. package/dist/cli/main-mjt2p7aj.js +3 -0
  77. package/dist/cli/main-n6qrdbmy.js +3 -0
  78. package/dist/cli/main-na7wse0x.js +5 -0
  79. package/dist/cli/main-nqveez48.js +4 -0
  80. package/dist/cli/main-ntqef02r.js +10 -0
  81. package/dist/cli/main-nvc3yjay.js +136 -0
  82. package/dist/cli/main-p0cfn6nr.js +16 -0
  83. package/dist/cli/main-qj2djy17.js +19 -0
  84. package/dist/cli/main-qsevpgsv.js +3 -0
  85. package/dist/cli/main-qvarybsp.js +3 -0
  86. package/dist/cli/main-rebtt91r.js +5 -0
  87. package/dist/cli/main-rpg7h8mb.js +3 -0
  88. package/dist/cli/main-rsy72qmw.js +15 -0
  89. package/dist/cli/main-rvetps99.js +18 -0
  90. package/dist/cli/main-s4bb0jav.js +3 -0
  91. package/dist/cli/main-s9v8k74e.js +3 -0
  92. package/dist/cli/main-tjvwmscs.js +3 -0
  93. package/dist/cli/main-tkgarpjj.js +4 -0
  94. package/dist/cli/main-v8y60bb2.js +3 -0
  95. package/dist/cli/main-vhrrq337.js +3 -0
  96. package/dist/cli/main-vp2dfb7s.js +4 -0
  97. package/dist/cli/main-vqbr22sz.js +8 -0
  98. package/dist/cli/main-vxnwe5xx.js +18 -0
  99. package/dist/cli/main-wgph00xf.js +5 -0
  100. package/dist/cli/main-wk2csfnj.js +5 -0
  101. package/dist/cli/main-wm997zjx.js +3 -0
  102. package/dist/cli/main-wpkyraxh.js +3 -0
  103. package/dist/cli/main-wqt32p5x.js +4 -0
  104. package/dist/cli/main-x9ct6y1a.js +3 -0
  105. package/dist/cli/main-xfekqh9m.js +7 -0
  106. package/dist/cli/main-xt9zc3n6.js +7 -0
  107. package/dist/cli/main-xx2z3zh5.js +4 -0
  108. package/dist/cli/main-y5c82rxr.js +3 -0
  109. package/dist/cli/main-yrjt2sqt.js +14 -0
  110. package/dist/cli/main-ys6zj3yr.js +3 -0
  111. package/dist/cli/main-ywbxshqc.js +8 -0
  112. package/dist/cli/main-z13755t8.js +25 -0
  113. package/dist/cli/main-zc7pyrbj.js +4 -0
  114. package/dist/cli/main.js +279 -0
  115. package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
  116. package/dist/cli/mcp-login-bthtfpt7.js +2 -0
  117. package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
  118. package/dist/cli/notify-54v5z9dz.js +2 -0
  119. package/dist/cli/oauth-g5gme95c.js +2 -0
  120. package/dist/cli/output-satndjap.js +16 -0
  121. package/dist/cli/profiles-sfhpbq3m.js +2 -0
  122. package/dist/cli/provider-config-hv3xtdt4.js +2 -0
  123. package/dist/cli/provider-kwzq6g84.js +2 -0
  124. package/dist/cli/registry-fh0hdnyn.js +2 -0
  125. package/dist/cli/registry-y1y8e94r.js +2 -0
  126. package/dist/cli/repl-t4z03mqq.js +11 -0
  127. package/dist/cli/resume-fqt4chg8.js +2 -0
  128. package/dist/cli/run-flags-rysbag9t.js +2 -0
  129. package/dist/cli/runtime-j19fjbsa.js +2 -0
  130. package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
  131. package/dist/cli/server-r0b6bksk.js +5 -0
  132. package/dist/cli/session-arg-txmn5g4x.js +2 -0
  133. package/dist/cli/session-ed250d9j.js +2 -0
  134. package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
  135. package/dist/cli/settings-y9rzcqx8.js +2 -0
  136. package/dist/cli/setup-jmbr11j0.js +2 -0
  137. package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
  138. package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
  139. package/dist/cli/smoke-1bg937kx.js +8 -0
  140. package/dist/cli/start-chat-p01cdks3.js +12 -0
  141. package/dist/cli/stream-4wmyaypz.js +2 -0
  142. package/dist/cli/task-eg4s093s.js +2 -0
  143. package/dist/cli/tasks-12v9rr9k.js +2 -0
  144. package/dist/cli/thinking-a5ngvqyh.js +2 -0
  145. package/dist/cli/todo-1wxpcecx.js +2 -0
  146. package/dist/cli/tools-2ftsya7w.js +2 -0
  147. package/dist/cli/tools-x1tj4fxm.js +2 -0
  148. package/dist/cli/trust-cmd-hccxehzb.js +2 -0
  149. package/dist/cli/update-check-ygt3vd7m.js +2 -0
  150. package/dist/cli/update-cmd-v23qhr8c.js +2 -0
  151. package/dist/cli/voice-g1gtck92.js +2 -0
  152. package/dist/cli/webfetch-0nnrjgb5.js +2 -0
  153. package/dist/cli/websearch-f0vr2p7d.js +2 -0
  154. package/dist/cli/workspace-9rq1w4ta.js +2 -0
  155. package/dist/lib/index.js +62 -0
  156. package/dist/lib/models-index.json +1 -0
  157. package/dist/lib/plugins.js +6 -0
  158. package/dist/lib/providers.js +17 -0
  159. package/dist/lib/public-api.js +20 -0
  160. package/dist/rovecode.exe +4 -0
  161. package/package.json +38 -11
  162. package/bin/rovecode.ts +0 -21
  163. package/src/acp/server.ts +0 -374
  164. package/src/cli/auth-login.ts +0 -122
  165. package/src/cli/connect.ts +0 -244
  166. package/src/cli/context-cmd.ts +0 -199
  167. package/src/cli/dispatch.ts +0 -82
  168. package/src/cli/doctor.ts +0 -362
  169. package/src/cli/export.ts +0 -276
  170. package/src/cli/help.ts +0 -293
  171. package/src/cli/is-tui-invocation.ts +0 -8
  172. package/src/cli/main.ts +0 -583
  173. package/src/cli/market-cmd.ts +0 -658
  174. package/src/cli/mcp-login.ts +0 -141
  175. package/src/cli/mcp-market-cmd.ts +0 -302
  176. package/src/cli/output.ts +0 -382
  177. package/src/cli/repl.ts +0 -250
  178. package/src/cli/repomap-root.ts +0 -14
  179. package/src/cli/resume.ts +0 -57
  180. package/src/cli/run-flags.ts +0 -43
  181. package/src/cli/run-limits.ts +0 -78
  182. package/src/cli/runtime.ts +0 -931
  183. package/src/cli/session-arg.ts +0 -30
  184. package/src/cli/sessions-cmd.ts +0 -145
  185. package/src/cli/setup.ts +0 -153
  186. package/src/cli/skills-cmd.ts +0 -194
  187. package/src/cli/start-chat.ts +0 -65
  188. package/src/cli/trust-cmd.ts +0 -52
  189. package/src/coding/bash.ts +0 -148
  190. package/src/coding/checkpoints.ts +0 -327
  191. package/src/coding/diff.ts +0 -138
  192. package/src/coding/files.ts +0 -341
  193. package/src/coding/hashline.ts +0 -274
  194. package/src/coding/lsp-gate.ts +0 -254
  195. package/src/coding/lsp-servers.ts +0 -147
  196. package/src/coding/lsp.ts +0 -283
  197. package/src/coding/repomap-cache.ts +0 -99
  198. package/src/coding/repomap-files.ts +0 -192
  199. package/src/coding/repomap.ts +0 -481
  200. package/src/core/agents.ts +0 -255
  201. package/src/core/compaction.ts +0 -259
  202. package/src/core/config.ts +0 -289
  203. package/src/core/context-report.ts +0 -228
  204. package/src/core/context.ts +0 -60
  205. package/src/core/count-remote.ts +0 -107
  206. package/src/core/execpolicy-rules.ts +0 -196
  207. package/src/core/execpolicy.ts +0 -385
  208. package/src/core/executor.ts +0 -454
  209. package/src/core/guardrails.ts +0 -400
  210. package/src/core/hooks.ts +0 -411
  211. package/src/core/images.ts +0 -230
  212. package/src/core/intro.ts +0 -266
  213. package/src/core/loop.ts +0 -567
  214. package/src/core/modes.ts +0 -372
  215. package/src/core/orchestrator.ts +0 -245
  216. package/src/core/proc-group.ts +0 -48
  217. package/src/core/project-trust.ts +0 -98
  218. package/src/core/reflection.ts +0 -165
  219. package/src/core/sandbox-config.ts +0 -186
  220. package/src/core/session-id.ts +0 -24
  221. package/src/core/session-images.ts +0 -73
  222. package/src/core/session-ops.ts +0 -183
  223. package/src/core/session-text.ts +0 -29
  224. package/src/core/session.ts +0 -469
  225. package/src/core/settings.ts +0 -170
  226. package/src/core/tasks.ts +0 -646
  227. package/src/core/token-scale.ts +0 -108
  228. package/src/core/tools.ts +0 -309
  229. package/src/core/trust.ts +0 -104
  230. package/src/core/types.ts +0 -330
  231. package/src/core/update-check.ts +0 -171
  232. package/src/core/usage.ts +0 -204
  233. package/src/core/validate.ts +0 -121
  234. package/src/core/verify-gate.ts +0 -159
  235. package/src/core/verify.ts +0 -236
  236. package/src/core/voice.ts +0 -158
  237. package/src/core/win-job.ts +0 -183
  238. package/src/core/workspace.ts +0 -184
  239. package/src/design/audit.ts +0 -797
  240. package/src/design/direction.ts +0 -190
  241. package/src/design/rules.ts +0 -157
  242. package/src/eval/bench.ts +0 -150
  243. package/src/eval/gauntlet-runner.ts +0 -215
  244. package/src/eval/gauntlet-support.ts +0 -84
  245. package/src/eval/gauntlet-wave3.ts +0 -269
  246. package/src/eval/gauntlet-wave4.ts +0 -217
  247. package/src/eval/gauntlet.ts +0 -253
  248. package/src/index.ts +0 -17
  249. package/src/lanes/agy.ts +0 -95
  250. package/src/lanes/approval.ts +0 -24
  251. package/src/lanes/claude.ts +0 -129
  252. package/src/lanes/codex.ts +0 -127
  253. package/src/lanes/events.ts +0 -130
  254. package/src/lanes/job.ts +0 -142
  255. package/src/lanes/opencode.ts +0 -122
  256. package/src/lanes/process.ts +0 -184
  257. package/src/lanes/progress.ts +0 -183
  258. package/src/lanes/registry.ts +0 -178
  259. package/src/lanes/runner.ts +0 -124
  260. package/src/lanes/types.ts +0 -112
  261. package/src/market/catalogs/mcp-docs.json +0 -111
  262. package/src/market/catalogs/plugins.json +0 -111
  263. package/src/market/catalogs/skills.json +0 -478
  264. package/src/market/clone.ts +0 -72
  265. package/src/market/context-cost.ts +0 -121
  266. package/src/market/digest.ts +0 -106
  267. package/src/market/index.ts +0 -22
  268. package/src/market/install.ts +0 -578
  269. package/src/market/manifest.ts +0 -187
  270. package/src/market/prereq.ts +0 -145
  271. package/src/market/registry.ts +0 -363
  272. package/src/market/resolve.ts +0 -111
  273. package/src/market/types.ts +0 -236
  274. package/src/market/validate.ts +0 -227
  275. package/src/mcp/client.ts +0 -449
  276. package/src/mcp/config.ts +0 -252
  277. package/src/mcp/local-package.ts +0 -211
  278. package/src/mcp/market-catalog.ts +0 -84
  279. package/src/mcp/market-install.ts +0 -289
  280. package/src/mcp/market.ts +0 -362
  281. package/src/mcp/oauth.ts +0 -251
  282. package/src/mcp/prompts-resources.ts +0 -249
  283. package/src/mcp/shared.ts +0 -149
  284. package/src/mcp/status.ts +0 -67
  285. package/src/mcp/tools.ts +0 -275
  286. package/src/mcp/transport.ts +0 -122
  287. package/src/mcp/trust.ts +0 -25
  288. package/src/memory/blocks.ts +0 -278
  289. package/src/memory/recall.ts +0 -355
  290. package/src/memory/scope.ts +0 -182
  291. package/src/memory/store.ts +0 -105
  292. package/src/memory/tools.ts +0 -99
  293. package/src/plugins/cli.ts +0 -119
  294. package/src/plugins/discover.ts +0 -108
  295. package/src/plugins/index.ts +0 -50
  296. package/src/plugins/install.ts +0 -184
  297. package/src/plugins/load.ts +0 -124
  298. package/src/plugins/manifest.ts +0 -92
  299. package/src/plugins/state.ts +0 -83
  300. package/src/providers/auth.ts +0 -408
  301. package/src/providers/cache.ts +0 -223
  302. package/src/providers/catalog-local.ts +0 -160
  303. package/src/providers/catalog.ts +0 -421
  304. package/src/providers/middleware-context.ts +0 -86
  305. package/src/providers/middleware.ts +0 -373
  306. package/src/providers/model-list.ts +0 -23
  307. package/src/providers/oauth/common.ts +0 -105
  308. package/src/providers/oauth/device-code.ts +0 -107
  309. package/src/providers/oauth/github-copilot.ts +0 -146
  310. package/src/providers/oauth/loopback.ts +0 -158
  311. package/src/providers/oauth/openai.ts +0 -163
  312. package/src/providers/oauth/openrouter.ts +0 -89
  313. package/src/providers/oauth/pkce.ts +0 -45
  314. package/src/providers/oauth/registry.ts +0 -39
  315. package/src/providers/oauth/seam.ts +0 -89
  316. package/src/providers/profile-glm53.ts +0 -111
  317. package/src/providers/profile-sonnet5-persona.ts +0 -65
  318. package/src/providers/profile-sonnet5-voice.ts +0 -23
  319. package/src/providers/profiles.ts +0 -156
  320. package/src/providers/provider-config.ts +0 -311
  321. package/src/providers/registry.ts +0 -333
  322. package/src/providers/responses.ts +0 -209
  323. package/src/providers/retry.ts +0 -234
  324. package/src/providers/router.ts +0 -294
  325. package/src/providers/sse.ts +0 -26
  326. package/src/providers/stream-errors.ts +0 -117
  327. package/src/providers/stream.ts +0 -566
  328. package/src/providers/thinking.ts +0 -189
  329. package/src/providers/wire-messages.ts +0 -129
  330. package/src/providers/wire-responses.ts +0 -79
  331. package/src/providers/wire-select.ts +0 -53
  332. package/src/server/http.ts +0 -291
  333. package/src/server/openapi.ts +0 -246
  334. package/src/sextant/card-hits.ts +0 -102
  335. package/src/sextant/card-keys.ts +0 -55
  336. package/src/sextant/context-source.ts +0 -157
  337. package/src/sextant/crew-cards.ts +0 -350
  338. package/src/sextant/draw-agents.ts +0 -273
  339. package/src/sextant/draw-code.ts +0 -388
  340. package/src/sextant/draw-context.ts +0 -222
  341. package/src/sextant/draw-frame.ts +0 -164
  342. package/src/sextant/draw-market.ts +0 -573
  343. package/src/sextant/draw-messages.ts +0 -386
  344. package/src/sextant/draw-pet.ts +0 -230
  345. package/src/sextant/draw-plan.ts +0 -187
  346. package/src/sextant/draw-tabs.ts +0 -85
  347. package/src/sextant/draw-util.ts +0 -65
  348. package/src/sextant/draw-wizard.ts +0 -378
  349. package/src/sextant/engine.ts +0 -230
  350. package/src/sextant/frame-hits.ts +0 -25
  351. package/src/sextant/frame.ts +0 -101
  352. package/src/sextant/git-status.ts +0 -197
  353. package/src/sextant/grid.ts +0 -59
  354. package/src/sextant/input.ts +0 -119
  355. package/src/sextant/keys.ts +0 -521
  356. package/src/sextant/layout.ts +0 -86
  357. package/src/sextant/local-commands.ts +0 -169
  358. package/src/sextant/market-source.ts +0 -287
  359. package/src/sextant/mentions.ts +0 -200
  360. package/src/sextant/message-hits.ts +0 -26
  361. package/src/sextant/model.ts +0 -387
  362. package/src/sextant/overlays.ts +0 -456
  363. package/src/sextant/panel-hits.ts +0 -38
  364. package/src/sextant/pet.ts +0 -399
  365. package/src/sextant/screen.ts +0 -324
  366. package/src/sextant/scroll-hits.ts +0 -66
  367. package/src/sextant/scrollbar.ts +0 -82
  368. package/src/sextant/sextant-bridge.ts +0 -174
  369. package/src/sextant/sextant-cards.ts +0 -142
  370. package/src/sextant/sextant-diff-base.ts +0 -63
  371. package/src/sextant/sextant-files.ts +0 -154
  372. package/src/sextant/sextant-frame-loop.ts +0 -335
  373. package/src/sextant/sextant-renderer.ts +0 -574
  374. package/src/sextant/sextant-repo.ts +0 -140
  375. package/src/sextant/theme.ts +0 -66
  376. package/src/sextant/tool-rows.ts +0 -189
  377. package/src/sextant/types.ts +0 -493
  378. package/src/skills/index.ts +0 -387
  379. package/src/skills/pack.ts +0 -220
  380. package/src/skills/spec.ts +0 -162
  381. package/src/skills/tools.ts +0 -69
  382. package/src/skills/versioned.ts +0 -227
  383. package/src/telemetry/otel-export.ts +0 -122
  384. package/src/telemetry/otel-lanes.ts +0 -89
  385. package/src/telemetry/otel-logs.ts +0 -131
  386. package/src/telemetry/otel-metrics.ts +0 -136
  387. package/src/telemetry/otel.ts +0 -397
  388. package/src/telemetry/otlp.ts +0 -76
  389. package/src/tools/ask-user.ts +0 -156
  390. package/src/tools/bash-bg.ts +0 -94
  391. package/src/tools/bash-jobs.ts +0 -237
  392. package/src/tools/design.ts +0 -151
  393. package/src/tools/evalcell.ts +0 -338
  394. package/src/tools/html-text.ts +0 -139
  395. package/src/tools/provider.ts +0 -149
  396. package/src/tools/task.ts +0 -250
  397. package/src/tools/todo.ts +0 -320
  398. package/src/tools/webfetch.ts +0 -332
  399. package/src/tools/websearch.ts +0 -359
  400. package/src/tui/agents-cmd.ts +0 -41
  401. package/src/tui/app.ts +0 -749
  402. package/src/tui/attach.ts +0 -127
  403. package/src/tui/boot-notes.ts +0 -41
  404. package/src/tui/builtin-prompts.ts +0 -59
  405. package/src/tui/checkpoints-cmd.ts +0 -70
  406. package/src/tui/clipboard-image.ts +0 -81
  407. package/src/tui/clipboard.ts +0 -78
  408. package/src/tui/commands.ts +0 -283
  409. package/src/tui/config-view.ts +0 -53
  410. package/src/tui/context-cmds.ts +0 -282
  411. package/src/tui/cost.ts +0 -108
  412. package/src/tui/crash-guard.ts +0 -173
  413. package/src/tui/focus-terminal.ts +0 -34
  414. package/src/tui/git-cmds.ts +0 -273
  415. package/src/tui/git-plain.ts +0 -58
  416. package/src/tui/info-cmd.ts +0 -150
  417. package/src/tui/input-plain.ts +0 -76
  418. package/src/tui/mcp-cmd.ts +0 -128
  419. package/src/tui/memory-note.ts +0 -77
  420. package/src/tui/modes-cmd.ts +0 -45
  421. package/src/tui/notify-seq.ts +0 -100
  422. package/src/tui/notify.ts +0 -318
  423. package/src/tui/overlays.ts +0 -97
  424. package/src/tui/pi-renderer.ts +0 -428
  425. package/src/tui/providers-cmd.ts +0 -377
  426. package/src/tui/reasoning-view.ts +0 -56
  427. package/src/tui/renderer.ts +0 -128
  428. package/src/tui/replay-marker.ts +0 -29
  429. package/src/tui/session-cmd.ts +0 -148
  430. package/src/tui/session-manage.ts +0 -95
  431. package/src/tui/sextant-attach.ts +0 -102
  432. package/src/tui/sextant-io.ts +0 -202
  433. package/src/tui/sextant-smoke.ts +0 -110
  434. package/src/tui/shell-cmd.ts +0 -158
  435. package/src/tui/smoke.ts +0 -72
  436. package/src/tui/staged-terminal.ts +0 -50
  437. package/src/tui/startup.ts +0 -12
  438. package/src/tui/theme.ts +0 -59
  439. package/src/tui/todo-label.ts +0 -7
  440. package/src/tui/trust-card.ts +0 -107
  441. package/src/tui/tui-commands.ts +0 -87
  442. package/tsconfig.json +0 -30
  443. package/vendor/pi-tui/LICENSE +0 -21
  444. package/vendor/pi-tui/PATCHES.md +0 -12
  445. package/vendor/pi-tui/PROVENANCE.md +0 -12
  446. package/vendor/pi-tui/README.upstream.md +0 -854
  447. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  448. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  449. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  450. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  451. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  452. package/vendor/pi-tui/src/components/box.ts +0 -138
  453. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  454. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  455. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  456. package/vendor/pi-tui/src/components/image.ts +0 -128
  457. package/vendor/pi-tui/src/components/input.ts +0 -448
  458. package/vendor/pi-tui/src/components/loader.ts +0 -93
  459. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  460. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  461. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  462. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  463. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  464. package/vendor/pi-tui/src/components/stack.ts +0 -155
  465. package/vendor/pi-tui/src/components/text.ts +0 -108
  466. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  467. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  468. package/vendor/pi-tui/src/editor-component.ts +0 -75
  469. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  470. package/vendor/pi-tui/src/index.ts +0 -149
  471. package/vendor/pi-tui/src/keybindings.ts +0 -321
  472. package/vendor/pi-tui/src/keys.ts +0 -1402
  473. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  474. package/vendor/pi-tui/src/latex.ts +0 -1381
  475. package/vendor/pi-tui/src/layout-node.ts +0 -52
  476. package/vendor/pi-tui/src/layout.ts +0 -411
  477. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  478. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  479. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  480. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  481. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  482. package/vendor/pi-tui/src/terminal.ts +0 -554
  483. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  484. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  485. package/vendor/pi-tui/src/tui.ts +0 -1264
  486. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  487. package/vendor/pi-tui/src/utils.ts +0 -1327
  488. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  489. package/vendor/pi-tui/test/test-themes.ts +0 -39
  490. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  491. /package/{src/providers → dist/cli}/models-index.json +0 -0
package/src/tui/notify.ts DELETED
@@ -1,318 +0,0 @@
1
- /** Notifications for the interactive TUI: ONE decorator over the Renderer seam rings the terminal (BEL / OSC 9 / OSC 777)
2
- * and/or spawns a desktop hook when a run ends or a card needs the user — and ONLY while the terminal is unfocused, because
3
- * the whole value is telling someone who looked away; a bell on every turn is a bell nobody hears. Both surfaces, one
4
- * place: the renderers themselves no longer ring (their `ring()` moved here on 2026-09-07).
5
- *
6
- * Pattern source: openai/codex (Apache-2.0, pattern level; no code copied) — codex-rs/tui/src/tui.rs (the unfocused | always
7
- * gate; terminal_focused starts true; a write error warns once and disables the backend for the session),
8
- * codex-rs/tui/src/chatwidget/notifications.rs (the event set: turn complete / approval requested / elicitation),
9
- * codex-rs/hooks/src/legacy_notify.rs (the `notify` argv hook: payload appended as the LAST argument, stdin/stdout/stderr
10
- * nulled, never awaited; the agent-turn-complete payload shape — the approval / question payloads are rovecode's own).
11
- * Deviations: codex disables focus reporting on Windows — Windows Terminal ≥ 1.14 reports focus through VT input, so the
12
- * tracker is installed on every TTY; codex fires the hook regardless of focus — here the hook obeys the SAME gate, because a
13
- * desktop toast while the person is looking at the terminal is the same noise wearing a different coat.
14
- *
15
- * Knobs (core/settings.ts; env beats file, project beats user, read directly at boot):
16
- * bell (existing) true (default) | false — the on/off switch for terminal notifications
17
- * notify auto (default) | bell | osc9 | osc777 — the method; auto = OSC 9 toast on Ghostty / iTerm2 / kitty /
18
- * Warp / WezTerm, BEL everywhere else. ROVECODE_NOTIFY=off is the env spelling of `bell: false`.
19
- * notify_when unfocused (default) | always ROVECODE_NOTIFY_WHEN
20
- * notify_command argv — a JSON string array or whitespace-split words, NEVER a shell ROVECODE_NOTIFY_COMMAND
21
- *
22
- * notify_command's permission path (Berkay's 2026-09-04 trust decision applied): from the env or the USER file it is the
23
- * person's own configuration; from the PROJECT file it is a repo-supplied command and applies only when that file is
24
- * approved in the digest trust store (mcp/trust.ts — the same store as project mcp.json; any edit re-asks), else it is
25
- * dropped with one note naming the key. The argv then goes through the same classification bash commands get (port #9:
26
- * execpolicy forbidden / dangerous-command → the hook is off, with a note). It is spawned as argv with no shell, every
27
- * stdio nulled, never awaited; the run's text reaches it ONLY as one JSON object in argv[last]. Not through the sandbox
28
- * rung: the hook is a desktop-side program (notify-send, a toast exe) — under docker/wsl the toast would appear inside the
29
- * container. Headless surfaces (`run`, `serve`, `acp`, `--plain`) never construct any of this. */
30
-
31
- import { dangerousCommandMatch, defaultExecPolicy } from "../core/execpolicy.ts";
32
- import { loadSettingsScoped, type ScopedSettings, type Settings } from "../core/settings.ts";
33
- import { FocusTerminal } from "./focus-terminal.ts";
34
- import { FOCUS_OFF, FOCUS_ON, FocusTracker, autoMethod, isArgvError, isTmux, message, parseArgv, sequenceFor, type Env, type NotifyDetail, type NotifyKind, type NotifyMethod } from "./notify-seq.ts";
35
- import type { AssistantView, Renderer } from "./renderer.ts";
36
- import { pickRenderer, type RawStdin, type RawStdout } from "./sextant-io.ts";
37
-
38
- export type NotifyWhen = "unfocused" | "always";
39
- export const NOTIFY_METHODS = ["auto", "bell", "osc9", "osc777"] as const;
40
- export type NotifyMethodSetting = (typeof NOTIFY_METHODS)[number];
41
- export const NOTIFY_WHENS: readonly NotifyWhen[] = ["unfocused", "always"];
42
-
43
- /** everything fire() needs, decided once at boot */
44
- export interface NotifyConfig {
45
- /** the terminal method; undefined = terminal notifications off (`bell: false` / ROVECODE_NOTIFY=off) */
46
- method?: NotifyMethod;
47
- when: NotifyWhen;
48
- /** the hook argv, validated and classified; undefined = no hook */
49
- hook?: string[];
50
- /** where the hook came from, for the notes (`ROVECODE_NOTIFY_COMMAND` or a settings file path) */
51
- hookSource?: string;
52
- /** OSC sequences ride a DCS passthrough inside tmux */
53
- tmux: boolean;
54
- /** one-shot warn notes: what was dropped and why (shown once the surface is live) */
55
- notes: string[];
56
- }
57
-
58
- export interface ResolveDeps {
59
- /** default process.env */
60
- env?: Env;
61
- /** the two settings files, the project layer already gated (default: core/settings.ts loadSettingsScoped(cwd)) */
62
- settings?: ScopedSettings;
63
- }
64
-
65
- const norm = (v: string | undefined): string => (v ?? "").trim().toLowerCase();
66
-
67
- /** the knobs → one config. Never throws; every refusal is a note. */
68
- export function resolveNotifyConfig(cwd: string, d: ResolveDeps = {}): NotifyConfig {
69
- const env = d.env ?? process.env;
70
- const scoped = d.settings ?? loadSettingsScoped(cwd);
71
- const merged: Settings = { ...scoped.user, ...scoped.project };
72
- const notes: string[] = [];
73
-
74
- // the terminal method: ROVECODE_NOTIFY (off | auto | bell | osc9 | osc777) beats the files; `bell: false` is off
75
- let method: NotifyMethodSetting | undefined;
76
- const envMethod = norm(env.ROVECODE_NOTIFY);
77
- if (envMethod === "off") method = undefined;
78
- else if ((NOTIFY_METHODS as readonly string[]).includes(envMethod)) method = envMethod as NotifyMethodSetting;
79
- else {
80
- if (envMethod !== "") notes.push(`notify: unknown value "${envMethod}" in ROVECODE_NOTIFY — off | auto | bell | osc9 | osc777; using the settings files`);
81
- method = merged.bell === false ? undefined : (merged.notify ?? "auto");
82
- }
83
- const resolved: NotifyMethod | undefined = method === "auto" ? autoMethod(env) : method;
84
-
85
- // the gate: ROVECODE_NOTIFY_WHEN beats the files; unfocused unless told otherwise
86
- let when: NotifyWhen = merged.notify_when ?? "unfocused";
87
- const envWhen = norm(env.ROVECODE_NOTIFY_WHEN);
88
- if ((NOTIFY_WHENS as readonly string[]).includes(envWhen)) when = envWhen as NotifyWhen;
89
- else if (envWhen !== "") notes.push(`notify_when: unknown value "${envWhen}" in ROVECODE_NOTIFY_WHEN — unfocused | always; using ${when}`);
90
-
91
- // the hook: env → the project file (trust-gated) → the user file
92
- let raw: string | undefined, source: string | undefined;
93
- const envCmd = env.ROVECODE_NOTIFY_COMMAND;
94
- if (envCmd !== undefined && envCmd.trim() !== "") { raw = envCmd; source = "ROVECODE_NOTIFY_COMMAND"; }
95
- else {
96
- // the project layer arrives GATED (core/settings.ts COMMAND_KEYS): an untrusted file's notify_command is already gone
97
- // and named in `dropped` — the same store and the same yes as hooks.ts, sandbox.json and mcp.json (`rovecode trust`)
98
- if (scoped.project.notify_command !== undefined) { raw = scoped.project.notify_command; source = scoped.projectPath; }
99
- else if (scoped.dropped.includes("notify_command")) notes.push(`notify_command: set by ${scoped.projectPath}, a repository file this machine has not approved — ignored (rovecode trust show · rovecode trust; in the TUI: /trust); set it in ~/.rovecode/settings.json or ROVECODE_NOTIFY_COMMAND to use your own`);
100
- if (raw === undefined && scoped.user.notify_command !== undefined) { raw = scoped.user.notify_command; source = "~/.rovecode/settings.json"; }
101
- }
102
- let hook: string[] | undefined;
103
- if (raw !== undefined) {
104
- const argv = parseArgv(raw);
105
- if (isArgvError(argv)) notes.push(`notify_command (${source}): ${argv.error} — the hook is off`);
106
- else {
107
- const danger = dangerousCommandMatch(argv);
108
- const decision = defaultExecPolicy().check(argv).decision;
109
- if (danger !== null) notes.push(`notify_command (${source}): "${argv.join(" ")}" is a dangerous command (${danger}) — the hook is off`);
110
- else if (decision === "forbidden") notes.push(`notify_command (${source}): execpolicy forbids "${argv.join(" ")}" — the hook is off`);
111
- else hook = argv;
112
- }
113
- }
114
- return { ...(resolved !== undefined ? { method: resolved } : {}), when, ...(hook !== undefined ? { hook, hookSource: source } : {}), tmux: isTmux(env), notes };
115
- }
116
-
117
- /** the last completed assistant text kept for the preview / payload */
118
- const PREVIEW_MAX = 400;
119
-
120
- /** the JSON handed to `notify_command` as its LAST argument (codex legacy-notify shape for the turn; rovecode's own for the cards) */
121
- export type NotifyPayload =
122
- | { type: "agent-turn-complete"; cwd: string; "last-assistant-message": string | null }
123
- | { type: "approval-requested"; cwd: string; tool: string; "args-preview": string }
124
- | { type: "question-requested"; cwd: string; question: string };
125
-
126
- export function payloadFor(kind: NotifyKind, d: NotifyDetail, cwd: string): NotifyPayload {
127
- if (kind === "approval") return { type: "approval-requested", cwd, tool: d.tool ?? "", "args-preview": d.argsPreview ?? "" };
128
- if (kind === "question") return { type: "question-requested", cwd, question: d.question ?? "" };
129
- return { type: "agent-turn-complete", cwd, "last-assistant-message": d.lastText ?? null };
130
- }
131
-
132
- /** the Bun.spawn slice the default spawner needs (tests inject a recorder) */
133
- export interface SpawnLike { (argv: string[], opts: { stdio: ["ignore", "ignore", "ignore"] }): { unref(): void } }
134
- const bunSpawn: SpawnLike = (argv, opts) => Bun.spawn(argv, opts);
135
-
136
- /** the default hook spawner: argv, no shell, every stdio nulled, `unref()` so a hung hook never pins the event loop at
137
- * quit, never awaited */
138
- export function defaultSpawner(spawnFn: SpawnLike = bunSpawn): (argv: string[]) => void {
139
- return (argv) => { spawnFn(argv, { stdio: ["ignore", "ignore", "ignore"] }).unref(); };
140
- }
141
-
142
- export interface NotifierDeps {
143
- config: NotifyConfig;
144
- /** payload cwd (default process.cwd()) */
145
- cwd?: string;
146
- /** terminal sequences go here (default: nowhere — the composition root wires stdout when it is a TTY) */
147
- write?: (s: string) => void;
148
- /** the FocusTracker's flag (default: always focused = watched = silent under `unfocused`) */
149
- focused?: () => boolean;
150
- /** the hook spawner (default defaultSpawner()) */
151
- spawn?: (argv: string[]) => void;
152
- /** the one-shot warn notes (default: dropped) */
153
- note?: (text: string, tone: "warn") => void;
154
- }
155
-
156
- const reason = (e: unknown): string => (e instanceof Error ? e.message : String(e));
157
-
158
- export class Notifier {
159
- readonly config: NotifyConfig;
160
- private readonly cwd: string;
161
- private readonly write: (s: string) => void;
162
- private readonly focused: () => boolean;
163
- private readonly spawn: (argv: string[]) => void;
164
- private readonly note: (text: string, tone: "warn") => void;
165
- private seqOff = false;
166
- private hookOff = false;
167
- private notesShown = false;
168
- private decset = false;
169
-
170
- constructor(d: NotifierDeps) {
171
- this.config = d.config;
172
- this.cwd = d.cwd ?? process.cwd();
173
- this.write = d.write ?? (() => {});
174
- this.focused = d.focused ?? (() => true);
175
- this.spawn = d.spawn ?? defaultSpawner();
176
- this.note = d.note ?? (() => {});
177
- }
178
-
179
- /** can anything fire — a terminal method or a hook */
180
- active(): boolean { return this.config.method !== undefined || this.config.hook !== undefined; }
181
-
182
- /** the config's notes, once, the first time the surface can show them */
183
- private showNotes(): void {
184
- if (this.notesShown) return;
185
- this.notesShown = true;
186
- for (const n of this.config.notes) this.note(n, "warn");
187
- }
188
-
189
- /** DECSET 1004 — written by withNotifications right after the surface's start writes; true when it was written */
190
- focusOn(): boolean {
191
- this.showNotes();
192
- if (!this.active()) return false;
193
- this.decset = true;
194
- this.emit(FOCUS_ON);
195
- return true;
196
- }
197
- /** DECRST 1004 — right before the surface's stop writes; a no-op unless a focusOn() wrote (never an unpaired `?1004l`) */
198
- focusOff(): void {
199
- if (!this.decset) return;
200
- this.decset = false;
201
- this.emit(FOCUS_OFF);
202
- }
203
-
204
- /** one trigger → the sequence and/or the hook, ONLY when `when` is always or the terminal is unfocused. Never throws. */
205
- fire(kind: NotifyKind, d: NotifyDetail): void {
206
- this.showNotes();
207
- if (!(this.config.when === "always" || !this.focused())) return; // the person is watching: nothing to tell them
208
- const { method, hook } = this.config;
209
- if (method !== undefined) this.emit(sequenceFor(method, message(kind, d), this.config.tmux));
210
- if (hook !== undefined && !this.hookOff) this.runHook(hook, payloadFor(kind, d, this.cwd));
211
- }
212
-
213
- private emit(seq: string): void {
214
- if (this.seqOff) return;
215
- try { this.write(seq); } catch (e) {
216
- this.seqOff = true;
217
- this.note(`notify: the terminal write failed (${reason(e)}) — terminal notifications are off for this session`, "warn");
218
- }
219
- }
220
-
221
- private runHook(argv: string[], payload: NotifyPayload): void {
222
- try { this.spawn([...argv, JSON.stringify(payload)]); } catch (e) {
223
- this.hookOff = true;
224
- this.note(`notify_command: could not start ${argv[0]} (${reason(e)}) — the hook is off for this session`, "warn");
225
- }
226
- }
227
- }
228
-
229
- type Overridden = Pick<Renderer, "start" | "stop" | "askApproval" | "askQuestion" | "setBusy" | "beginAssistant">;
230
-
231
- /** The decorator: a Proxy over the inner renderer that adds NO member the inner lacks (`"onEvent" in wrapped` follows the
232
- * inner; `instanceof` follows the inner's prototype) and forwards every other member bound to the inner. Overridden: start
233
- * (DECSET 1004 after the surface's own start writes, when anything can fire) · stop (DECRST 1004 before its stop writes) ·
234
- * askApproval / askQuestion (a trigger each, from ANY caller) · setBusy (run end = a label-less `false` after a `true` —
235
- * never a labelled outcome, never a second `false`) · beginAssistant (keeps the last completed text for the preview / payload). */
236
- export function withNotifications<R extends Renderer>(inner: R, n: Notifier): R {
237
- let busy = false, started = false, decset = false;
238
- let generation = 0;
239
- let lastText: string | undefined;
240
- const over: Overridden = {
241
- start(hooks, options) {
242
- const epoch = generation;
243
- const starting = inner.start(hooks, options);
244
- const activated = () => { if (epoch === generation && !started) { started = true; decset = n.focusOn(); } };
245
- if (starting) return starting.then(activated);
246
- activated();
247
- },
248
- stop() {
249
- generation++; // a prepared renderer resolving after cancellation must not re-enable focus reporting
250
- if (decset) { decset = false; n.focusOff(); }
251
- started = false; // after leave nothing rings: a run's finally racing the user's exit has no terminal to reach
252
- inner.stop();
253
- },
254
- askApproval(tool, argsPreview, detail) {
255
- if (started) n.fire("approval", { tool, argsPreview });
256
- return inner.askApproval(tool, argsPreview, detail);
257
- },
258
- askQuestion(q, signal) {
259
- if (started) n.fire("question", { question: q.question });
260
- return inner.askQuestion(q, signal);
261
- },
262
- setBusy(b, label) {
263
- inner.setBusy(b, label);
264
- if (b) { busy = true; lastText = undefined; return; }
265
- const ended = busy && label === undefined;
266
- busy = false;
267
- if (ended && started) n.fire("run_end", { ...(lastText !== undefined ? { lastText } : {}) }); // a surface that never started has no terminal to hear it
268
- },
269
- beginAssistant(): AssistantView {
270
- const v = inner.beginAssistant();
271
- let buf = "";
272
- return {
273
- append(delta) { v.append(delta); if (buf.length < PREVIEW_MAX) buf = (buf + delta).slice(0, PREVIEW_MAX); },
274
- done() { v.done(); lastText = buf; },
275
- };
276
- },
277
- };
278
- return new Proxy(inner, {
279
- get(target, prop) {
280
- if (typeof prop === "string" && Object.hasOwn(over, prop)) return over[prop as keyof Overridden];
281
- const v = Reflect.get(target, prop, target);
282
- return typeof v === "function" ? (v as (...a: unknown[]) => unknown).bind(target) : v;
283
- },
284
- });
285
- }
286
-
287
- /** main.ts's ONE composition root for the interactive TUI: the surface pickRenderer would have chosen (sextant on a capable
288
- * TTY, else the classic PiTuiRenderer with `cwd` — app.ts parity), its raw-input path carrying a FocusTracker on every TTY
289
- * (classic: FocusTerminal around ProcessTerminal; sextant: the ProcessIO option), wrapped in withNotifications over a
290
- * Notifier that writes to `stdout` only when it is a TTY. */
291
- export function interactiveRenderer(cli: { classic: boolean; pet?: string }, env: Env = process.env, stdout: RawStdout = process.stdout, stdin: RawStdin = process.stdin, cwd: string = process.cwd()): Renderer {
292
- const tty = stdout.isTTY === true;
293
- const tracker = tty ? new FocusTracker() : undefined;
294
- let inner: Renderer | undefined = pickRenderer(cli, env, stdout, { stdin, ...(tracker ? { focus: tracker } : {}) });
295
- if (!inner) {
296
- // The classic surface is optional: do not load its editor/markdown graph into a sextant session.
297
- const { PiTuiRenderer } = require("./pi-renderer.ts") as typeof import("./pi-renderer.ts");
298
- const { ProcessTerminal } = require("../../vendor/pi-tui/src/terminal.ts") as typeof import("../../vendor/pi-tui/src/terminal.ts");
299
- inner = new PiTuiRenderer({ cwd, terminal: tracker ? new FocusTerminal(new ProcessTerminal(), tracker) : new ProcessTerminal() });
300
- }
301
- // EOF on stdin (the pipe or wrapper that launched us went away). The classic surface listens for
302
- // "data" only and the vendored terminal never sees an end, so a piped launch that loses its writer
303
- // hangs forever — measured 60 s+ under a test timeout. The sextant side handles this in ProcessIO
304
- // (sextant-io.ts, same ETX shape); the classic side cannot be reached through its own seams, so
305
- // the fallback here is direct: exit the PROCESS, after restoring the terminal through the crash
306
- // guard's own path. A TUI whose input is gone has nothing left to wait for, and its terminal
307
- // state is the only thing that must not be lost on the way out. Skipped for a TTY — a TTY does not end.
308
- if ((stdin as { isTTY?: boolean }).isTTY !== true) {
309
- stdin.on?.("end", () => { try { process.exit(0); } catch { /* already going */ } });
310
- }
311
- const notifier = new Notifier({
312
- config: resolveNotifyConfig(cwd, { env }), cwd,
313
- write: tty ? (s) => { stdout.write(s); } : () => {},
314
- focused: () => tracker?.focused ?? true,
315
- note: (text, tone) => inner!.addSystemNote(text, tone),
316
- });
317
- return withNotifications(inner, notifier);
318
- }
@@ -1,97 +0,0 @@
1
- /** Overlay bodies for PiTuiRenderer: the port #24 approval diff card and the port #33 question
2
- * card. Each is a pi-tui Component that routes keys to the SelectList (or Input) it wraps, so
3
- * the renderer's overlay lifecycle (show → settle → hide → refocus editor) is shared and the
4
- * wrapped list's bindings are untouched. Only pi-renderer.ts, this module and theme.ts may
5
- * import from vendor/pi-tui. */
6
-
7
- import {
8
- type Component,
9
- Input,
10
- type SelectList,
11
- truncateToWidth,
12
- visibleWidth,
13
- wrapTextWithAnsi,
14
- } from "../../vendor/pi-tui/src/index.ts";
15
- import { pal, st } from "./theme.ts";
16
-
17
- /** One overlay row: leading space, clipped to width-2, padded so the right gutter stays clean. */
18
- export function fitLine(s: string, width: number): string {
19
- const t = truncateToWidth(s, width - 2, "…");
20
- return ` ${t}${" ".repeat(Math.max(0, width - 1 - visibleWidth(t)))}`;
21
- }
22
-
23
- /** Port #24: body of an edit/write approval overlay — title, the bounded unified diff
24
- * (+ green, - red, headers/@@ dim), a spacer, then the verdict list. Keys go straight
25
- * to the list, so verdicts and bindings are identical to the plain approval overlay. */
26
- const MORE_RE = /^… \+(\d+) more line/;
27
- export class ApprovalCard implements Component {
28
- constructor(
29
- private readonly title: string,
30
- private readonly diff: string[],
31
- private readonly list: SelectList,
32
- private readonly rows: () => number,
33
- ) {}
34
- handleInput(data: string): void { this.list.handleInput(data); }
35
- invalidate(): void { this.list.invalidate(); }
36
- render(width: number): string[] {
37
- // physical bound: title, list and some transcript must stay visible. previewDiff already
38
- // clipped logically — fold its marker's count into ours rather than stacking two markers.
39
- const max = Math.max(4, this.rows() - 12);
40
- let lines = this.diff;
41
- if (lines.length > max) {
42
- const tail = MORE_RE.exec(lines[lines.length - 1]!);
43
- const hidden = lines.length - max + (tail ? Number(tail[1]) - 1 : 0);
44
- lines = [...lines.slice(0, max), `… +${hidden} more line${hidden === 1 ? "" : "s"}`];
45
- }
46
- return [fitLine(pal.warn(this.title), width), ...lines.map((l, i) => fitLine(paintDiff(l, i), width)), " ".repeat(width), ...this.list.render(width)];
47
- }
48
- }
49
-
50
- function paintDiff(line: string, idx: number): string {
51
- if ((idx === 0 && line.startsWith("--- ")) || (idx === 1 && line.startsWith("+++ "))) return st.dim(line);
52
- const c = line[0];
53
- return c === "+" ? pal.ok(line) : c === "-" ? pal.err(line) : c === " " ? line : st.dim(line);
54
- }
55
-
56
- /** SelectList value of the "type an answer…" entry — can never collide with an option index. */
57
- export const FREE_TEXT = "\u0000free-text";
58
-
59
- /** SelectList value of the "skip this question" entry: the NON-destructive decline (resolves null →
60
- * the tool reports "user declined to answer"), so a busy run — where Escape means "stop the run" —
61
- * can still be left unanswered without killing it (port #33 critic LOW). Same escape idiom as
62
- * FREE_TEXT: an option index can never look like this. */
63
- export const SKIP_QUESTION = "\u0000skip-question";
64
-
65
- /** Port #33: body of a question overlay — title, the wrapped question (bounded by the terminal),
66
- * a spacer, then either the option list (options + the free-text entry + skip) or a one-line
67
- * Input for a typed answer, each with a key hint. Keys route to whichever is active; the renderer
68
- * wires the list/input callbacks (select, submit, escape) and flips `typing`. */
69
- export class QuestionCard implements Component {
70
- private typing: boolean;
71
- readonly input = new Input();
72
- constructor(
73
- private readonly question: string,
74
- private readonly list: SelectList,
75
- private readonly optionCount: number,
76
- private readonly freeText: boolean,
77
- private readonly rows: () => number,
78
- /** true while a run is in flight — decides what Escape means in the hint */
79
- private readonly busy: () => boolean,
80
- ) { this.typing = optionCount === 0; } // no options: straight to the input
81
- get isTyping(): boolean { return this.typing; }
82
- setTyping(on: boolean): void { this.typing = on; }
83
- handleInput(data: string): void { if (this.typing) this.input.handleInput(data); else this.list.handleInput(data); }
84
- invalidate(): void { this.list.invalidate(); this.input.invalidate(); }
85
- render(width: number): string[] {
86
- const bodyRows = this.typing ? 1 : this.optionCount + (this.freeText ? 1 : 0) + 1; // + the skip entry
87
- // physical bound: title, body, hint and some transcript must stay visible
88
- const max = Math.max(2, this.rows() - 9 - bodyRows);
89
- let lines = wrapTextWithAnsi(this.question, width - 2);
90
- if (lines.length > max) lines = [...lines.slice(0, max - 1), st.dim(`… +${lines.length - max + 1} more lines`)];
91
- const esc = this.busy() ? "Esc stop the run" : "Esc skip";
92
- const body = this.typing
93
- ? [fitLine(this.input.render(width - 2)[0] ?? "", width), fitLine(st.dim(this.optionCount > 0 ? "Enter sends · Esc back to the options" : `Enter sends · ${esc}`), width)]
94
- : [...this.list.render(width), fitLine(st.dim(`↑↓ choose · Enter answer · ${esc}`), width)];
95
- return [fitLine(pal.warn("question"), width), ...lines.map((l) => fitLine(l, width)), " ".repeat(width), ...body];
96
- }
97
- }