rovecode 0.3.3 → 0.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (491) hide show
  1. package/CHANGELOG.md +512 -0
  2. package/README.md +33 -48
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-dybnr56b.js +2 -0
  5. package/dist/cli/ask-user-p8hq4xgj.js +2 -0
  6. package/dist/cli/auth-login-ewpgw5sm.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-xv3ypwev.js +9 -0
  9. package/dist/cli/catalog-737wb2s0.js +2 -0
  10. package/dist/cli/cli-arhg40m0.js +2 -0
  11. package/dist/cli/client-cf2pxx8q.js +2 -0
  12. package/dist/cli/commands-3p7e4xxs.js +2 -0
  13. package/dist/cli/connect-3q93d7cb.js +2 -0
  14. package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
  15. package/dist/cli/context-report-hbw9zfes.js +2 -0
  16. package/dist/cli/count-remote-mby98cd0.js +2 -0
  17. package/dist/cli/design-122y0axd.js +2 -0
  18. package/dist/cli/dispatch-b4egzvvh.js +2 -0
  19. package/dist/cli/doctor-x4jkv72e.js +3 -0
  20. package/dist/cli/executor-ftvg6tsy.js +2 -0
  21. package/dist/cli/export-pdgdhkch.js +2 -0
  22. package/dist/cli/files-cez9a96p.js +2 -0
  23. package/dist/cli/gauntlet-r3xxaszc.js +2 -0
  24. package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
  25. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
  26. package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
  27. package/dist/cli/hashline-ewg5hbe3.js +2 -0
  28. package/dist/cli/http-n0kehsk8.js +5 -0
  29. package/dist/cli/index-z5qt1s76.js +2 -0
  30. package/dist/cli/install-80mp63kx.js +2 -0
  31. package/dist/cli/loop-12twjcat.js +2 -0
  32. package/dist/cli/main-01pv9206.js +4 -0
  33. package/dist/cli/main-0jys2ccn.js +3 -0
  34. package/dist/cli/main-1ztz6fkj.js +10 -0
  35. package/dist/cli/main-23q7cmww.js +9 -0
  36. package/dist/cli/main-2rzbexn2.js +3 -0
  37. package/dist/cli/main-2wyax8k9.js +9 -0
  38. package/dist/cli/main-2yeveeve.js +6 -0
  39. package/dist/cli/main-2z3dek0b.js +3 -0
  40. package/dist/cli/main-2zgsknth.js +3 -0
  41. package/dist/cli/main-45ejth3a.js +4 -0
  42. package/dist/cli/main-45rn3trk.js +22 -0
  43. package/dist/cli/main-4p4e2w7x.js +4 -0
  44. package/dist/cli/main-4y0tnfpa.js +16 -0
  45. package/dist/cli/main-5py0rkmc.js +4 -0
  46. package/dist/cli/main-6dtqmbt6.js +7 -0
  47. package/dist/cli/main-6h9x282m.js +4 -0
  48. package/dist/cli/main-6vjeds42.js +3 -0
  49. package/dist/cli/main-78gq4bt9.js +6 -0
  50. package/dist/cli/main-7jd5vh3x.js +4 -0
  51. package/dist/cli/main-7kt6r53y.js +4 -0
  52. package/dist/cli/main-8c1tbazx.js +58 -0
  53. package/dist/cli/main-9a9rnh47.js +19 -0
  54. package/dist/cli/main-9ht36z12.js +3 -0
  55. package/dist/cli/main-a2yfvcy9.js +7 -0
  56. package/dist/cli/main-a3f51n0x.js +5 -0
  57. package/dist/cli/main-b8zq261k.js +3 -0
  58. package/dist/cli/main-bxtvnf6d.js +13 -0
  59. package/dist/cli/main-edxc3yzt.js +4 -0
  60. package/dist/cli/main-evgz4mp5.js +21 -0
  61. package/dist/cli/main-f33fc5je.js +9 -0
  62. package/dist/cli/main-fvnpq46y.js +12 -0
  63. package/dist/cli/main-gbbty4d4.js +3 -0
  64. package/dist/cli/main-gth53dnt.js +25 -0
  65. package/dist/cli/main-hqbz10aw.js +9 -0
  66. package/dist/cli/main-hrrvcfan.js +38 -0
  67. package/dist/cli/main-hzwtsb2m.js +5 -0
  68. package/dist/cli/main-j7ttv0sd.js +34 -0
  69. package/dist/cli/main-jak598k9.js +5 -0
  70. package/dist/cli/main-kba6zeyd.js +6 -0
  71. package/dist/cli/main-kwwsz6rq.js +3 -0
  72. package/dist/cli/main-m8vm17zq.js +3 -0
  73. package/dist/cli/main-mg4f96e1.js +3 -0
  74. package/dist/cli/main-mg9b20ac.js +18 -0
  75. package/dist/cli/main-mgb9ccnx.js +3 -0
  76. package/dist/cli/main-mjt2p7aj.js +3 -0
  77. package/dist/cli/main-n6qrdbmy.js +3 -0
  78. package/dist/cli/main-na7wse0x.js +5 -0
  79. package/dist/cli/main-nqveez48.js +4 -0
  80. package/dist/cli/main-ntqef02r.js +10 -0
  81. package/dist/cli/main-nvc3yjay.js +136 -0
  82. package/dist/cli/main-p0cfn6nr.js +16 -0
  83. package/dist/cli/main-qj2djy17.js +19 -0
  84. package/dist/cli/main-qsevpgsv.js +3 -0
  85. package/dist/cli/main-qvarybsp.js +3 -0
  86. package/dist/cli/main-rebtt91r.js +5 -0
  87. package/dist/cli/main-rpg7h8mb.js +3 -0
  88. package/dist/cli/main-rsy72qmw.js +15 -0
  89. package/dist/cli/main-rvetps99.js +18 -0
  90. package/dist/cli/main-s4bb0jav.js +3 -0
  91. package/dist/cli/main-s9v8k74e.js +3 -0
  92. package/dist/cli/main-tjvwmscs.js +3 -0
  93. package/dist/cli/main-tkgarpjj.js +4 -0
  94. package/dist/cli/main-v8y60bb2.js +3 -0
  95. package/dist/cli/main-vhrrq337.js +3 -0
  96. package/dist/cli/main-vp2dfb7s.js +4 -0
  97. package/dist/cli/main-vqbr22sz.js +8 -0
  98. package/dist/cli/main-vxnwe5xx.js +18 -0
  99. package/dist/cli/main-wgph00xf.js +5 -0
  100. package/dist/cli/main-wk2csfnj.js +5 -0
  101. package/dist/cli/main-wm997zjx.js +3 -0
  102. package/dist/cli/main-wpkyraxh.js +3 -0
  103. package/dist/cli/main-wqt32p5x.js +4 -0
  104. package/dist/cli/main-x9ct6y1a.js +3 -0
  105. package/dist/cli/main-xfekqh9m.js +7 -0
  106. package/dist/cli/main-xt9zc3n6.js +7 -0
  107. package/dist/cli/main-xx2z3zh5.js +4 -0
  108. package/dist/cli/main-y5c82rxr.js +3 -0
  109. package/dist/cli/main-yrjt2sqt.js +14 -0
  110. package/dist/cli/main-ys6zj3yr.js +3 -0
  111. package/dist/cli/main-ywbxshqc.js +8 -0
  112. package/dist/cli/main-z13755t8.js +25 -0
  113. package/dist/cli/main-zc7pyrbj.js +4 -0
  114. package/dist/cli/main.js +279 -0
  115. package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
  116. package/dist/cli/mcp-login-bthtfpt7.js +2 -0
  117. package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
  118. package/dist/cli/notify-54v5z9dz.js +2 -0
  119. package/dist/cli/oauth-g5gme95c.js +2 -0
  120. package/dist/cli/output-satndjap.js +16 -0
  121. package/dist/cli/profiles-sfhpbq3m.js +2 -0
  122. package/dist/cli/provider-config-hv3xtdt4.js +2 -0
  123. package/dist/cli/provider-kwzq6g84.js +2 -0
  124. package/dist/cli/registry-fh0hdnyn.js +2 -0
  125. package/dist/cli/registry-y1y8e94r.js +2 -0
  126. package/dist/cli/repl-t4z03mqq.js +11 -0
  127. package/dist/cli/resume-fqt4chg8.js +2 -0
  128. package/dist/cli/run-flags-rysbag9t.js +2 -0
  129. package/dist/cli/runtime-j19fjbsa.js +2 -0
  130. package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
  131. package/dist/cli/server-r0b6bksk.js +5 -0
  132. package/dist/cli/session-arg-txmn5g4x.js +2 -0
  133. package/dist/cli/session-ed250d9j.js +2 -0
  134. package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
  135. package/dist/cli/settings-y9rzcqx8.js +2 -0
  136. package/dist/cli/setup-jmbr11j0.js +2 -0
  137. package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
  138. package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
  139. package/dist/cli/smoke-1bg937kx.js +8 -0
  140. package/dist/cli/start-chat-p01cdks3.js +12 -0
  141. package/dist/cli/stream-4wmyaypz.js +2 -0
  142. package/dist/cli/task-eg4s093s.js +2 -0
  143. package/dist/cli/tasks-12v9rr9k.js +2 -0
  144. package/dist/cli/thinking-a5ngvqyh.js +2 -0
  145. package/dist/cli/todo-1wxpcecx.js +2 -0
  146. package/dist/cli/tools-2ftsya7w.js +2 -0
  147. package/dist/cli/tools-x1tj4fxm.js +2 -0
  148. package/dist/cli/trust-cmd-hccxehzb.js +2 -0
  149. package/dist/cli/update-check-ygt3vd7m.js +2 -0
  150. package/dist/cli/update-cmd-v23qhr8c.js +2 -0
  151. package/dist/cli/voice-g1gtck92.js +2 -0
  152. package/dist/cli/webfetch-0nnrjgb5.js +2 -0
  153. package/dist/cli/websearch-f0vr2p7d.js +2 -0
  154. package/dist/cli/workspace-9rq1w4ta.js +2 -0
  155. package/dist/lib/index.js +62 -0
  156. package/dist/lib/models-index.json +1 -0
  157. package/dist/lib/plugins.js +6 -0
  158. package/dist/lib/providers.js +17 -0
  159. package/dist/lib/public-api.js +20 -0
  160. package/dist/rovecode.exe +4 -0
  161. package/package.json +83 -72
  162. package/bin/rovecode.ts +0 -21
  163. package/src/acp/server.ts +0 -374
  164. package/src/cli/auth-login.ts +0 -122
  165. package/src/cli/connect.ts +0 -244
  166. package/src/cli/context-cmd.ts +0 -199
  167. package/src/cli/dispatch.ts +0 -82
  168. package/src/cli/doctor.ts +0 -362
  169. package/src/cli/export.ts +0 -276
  170. package/src/cli/help.ts +0 -293
  171. package/src/cli/is-tui-invocation.ts +0 -8
  172. package/src/cli/main.ts +0 -583
  173. package/src/cli/market-cmd.ts +0 -658
  174. package/src/cli/mcp-login.ts +0 -141
  175. package/src/cli/mcp-market-cmd.ts +0 -302
  176. package/src/cli/output.ts +0 -382
  177. package/src/cli/repl.ts +0 -250
  178. package/src/cli/repomap-root.ts +0 -14
  179. package/src/cli/resume.ts +0 -57
  180. package/src/cli/run-flags.ts +0 -43
  181. package/src/cli/run-limits.ts +0 -78
  182. package/src/cli/runtime.ts +0 -931
  183. package/src/cli/session-arg.ts +0 -30
  184. package/src/cli/sessions-cmd.ts +0 -145
  185. package/src/cli/setup.ts +0 -153
  186. package/src/cli/skills-cmd.ts +0 -194
  187. package/src/cli/start-chat.ts +0 -65
  188. package/src/cli/trust-cmd.ts +0 -52
  189. package/src/coding/bash.ts +0 -148
  190. package/src/coding/checkpoints.ts +0 -327
  191. package/src/coding/diff.ts +0 -138
  192. package/src/coding/files.ts +0 -341
  193. package/src/coding/hashline.ts +0 -274
  194. package/src/coding/lsp-gate.ts +0 -254
  195. package/src/coding/lsp-servers.ts +0 -147
  196. package/src/coding/lsp.ts +0 -283
  197. package/src/coding/repomap-cache.ts +0 -99
  198. package/src/coding/repomap-files.ts +0 -192
  199. package/src/coding/repomap.ts +0 -481
  200. package/src/core/agents.ts +0 -255
  201. package/src/core/compaction.ts +0 -259
  202. package/src/core/config.ts +0 -289
  203. package/src/core/context-report.ts +0 -228
  204. package/src/core/context.ts +0 -60
  205. package/src/core/count-remote.ts +0 -107
  206. package/src/core/execpolicy-rules.ts +0 -196
  207. package/src/core/execpolicy.ts +0 -385
  208. package/src/core/executor.ts +0 -454
  209. package/src/core/guardrails.ts +0 -400
  210. package/src/core/hooks.ts +0 -411
  211. package/src/core/images.ts +0 -230
  212. package/src/core/intro.ts +0 -266
  213. package/src/core/loop.ts +0 -567
  214. package/src/core/modes.ts +0 -372
  215. package/src/core/orchestrator.ts +0 -245
  216. package/src/core/proc-group.ts +0 -48
  217. package/src/core/project-trust.ts +0 -98
  218. package/src/core/reflection.ts +0 -165
  219. package/src/core/sandbox-config.ts +0 -186
  220. package/src/core/session-id.ts +0 -24
  221. package/src/core/session-images.ts +0 -73
  222. package/src/core/session-ops.ts +0 -183
  223. package/src/core/session-text.ts +0 -29
  224. package/src/core/session.ts +0 -469
  225. package/src/core/settings.ts +0 -170
  226. package/src/core/tasks.ts +0 -646
  227. package/src/core/token-scale.ts +0 -108
  228. package/src/core/tools.ts +0 -309
  229. package/src/core/trust.ts +0 -104
  230. package/src/core/types.ts +0 -330
  231. package/src/core/update-check.ts +0 -171
  232. package/src/core/usage.ts +0 -204
  233. package/src/core/validate.ts +0 -121
  234. package/src/core/verify-gate.ts +0 -159
  235. package/src/core/verify.ts +0 -236
  236. package/src/core/voice.ts +0 -158
  237. package/src/core/win-job.ts +0 -183
  238. package/src/core/workspace.ts +0 -184
  239. package/src/design/audit.ts +0 -797
  240. package/src/design/direction.ts +0 -190
  241. package/src/design/rules.ts +0 -157
  242. package/src/eval/bench.ts +0 -150
  243. package/src/eval/gauntlet-runner.ts +0 -215
  244. package/src/eval/gauntlet-support.ts +0 -84
  245. package/src/eval/gauntlet-wave3.ts +0 -269
  246. package/src/eval/gauntlet-wave4.ts +0 -217
  247. package/src/eval/gauntlet.ts +0 -253
  248. package/src/index.ts +0 -17
  249. package/src/lanes/agy.ts +0 -95
  250. package/src/lanes/approval.ts +0 -24
  251. package/src/lanes/claude.ts +0 -129
  252. package/src/lanes/codex.ts +0 -127
  253. package/src/lanes/events.ts +0 -130
  254. package/src/lanes/job.ts +0 -142
  255. package/src/lanes/opencode.ts +0 -122
  256. package/src/lanes/process.ts +0 -184
  257. package/src/lanes/progress.ts +0 -183
  258. package/src/lanes/registry.ts +0 -178
  259. package/src/lanes/runner.ts +0 -124
  260. package/src/lanes/types.ts +0 -112
  261. package/src/market/catalogs/mcp-docs.json +0 -111
  262. package/src/market/catalogs/plugins.json +0 -111
  263. package/src/market/catalogs/skills.json +0 -478
  264. package/src/market/clone.ts +0 -72
  265. package/src/market/context-cost.ts +0 -121
  266. package/src/market/digest.ts +0 -106
  267. package/src/market/index.ts +0 -22
  268. package/src/market/install.ts +0 -578
  269. package/src/market/manifest.ts +0 -187
  270. package/src/market/prereq.ts +0 -145
  271. package/src/market/registry.ts +0 -363
  272. package/src/market/resolve.ts +0 -111
  273. package/src/market/types.ts +0 -236
  274. package/src/market/validate.ts +0 -227
  275. package/src/mcp/client.ts +0 -449
  276. package/src/mcp/config.ts +0 -252
  277. package/src/mcp/local-package.ts +0 -211
  278. package/src/mcp/market-catalog.ts +0 -84
  279. package/src/mcp/market-install.ts +0 -289
  280. package/src/mcp/market.ts +0 -362
  281. package/src/mcp/oauth.ts +0 -251
  282. package/src/mcp/prompts-resources.ts +0 -249
  283. package/src/mcp/shared.ts +0 -149
  284. package/src/mcp/status.ts +0 -67
  285. package/src/mcp/tools.ts +0 -275
  286. package/src/mcp/transport.ts +0 -122
  287. package/src/mcp/trust.ts +0 -25
  288. package/src/memory/blocks.ts +0 -278
  289. package/src/memory/recall.ts +0 -355
  290. package/src/memory/scope.ts +0 -182
  291. package/src/memory/store.ts +0 -105
  292. package/src/memory/tools.ts +0 -99
  293. package/src/plugins/cli.ts +0 -119
  294. package/src/plugins/discover.ts +0 -108
  295. package/src/plugins/index.ts +0 -50
  296. package/src/plugins/install.ts +0 -184
  297. package/src/plugins/load.ts +0 -124
  298. package/src/plugins/manifest.ts +0 -92
  299. package/src/plugins/state.ts +0 -83
  300. package/src/providers/auth.ts +0 -408
  301. package/src/providers/cache.ts +0 -223
  302. package/src/providers/catalog-local.ts +0 -160
  303. package/src/providers/catalog.ts +0 -421
  304. package/src/providers/middleware-context.ts +0 -86
  305. package/src/providers/middleware.ts +0 -373
  306. package/src/providers/model-list.ts +0 -23
  307. package/src/providers/oauth/common.ts +0 -105
  308. package/src/providers/oauth/device-code.ts +0 -107
  309. package/src/providers/oauth/github-copilot.ts +0 -146
  310. package/src/providers/oauth/loopback.ts +0 -158
  311. package/src/providers/oauth/openai.ts +0 -163
  312. package/src/providers/oauth/openrouter.ts +0 -89
  313. package/src/providers/oauth/pkce.ts +0 -45
  314. package/src/providers/oauth/registry.ts +0 -39
  315. package/src/providers/oauth/seam.ts +0 -89
  316. package/src/providers/profile-glm53.ts +0 -111
  317. package/src/providers/profile-sonnet5-persona.ts +0 -65
  318. package/src/providers/profile-sonnet5-voice.ts +0 -23
  319. package/src/providers/profiles.ts +0 -156
  320. package/src/providers/provider-config.ts +0 -311
  321. package/src/providers/registry.ts +0 -333
  322. package/src/providers/responses.ts +0 -209
  323. package/src/providers/retry.ts +0 -234
  324. package/src/providers/router.ts +0 -294
  325. package/src/providers/sse.ts +0 -26
  326. package/src/providers/stream-errors.ts +0 -117
  327. package/src/providers/stream.ts +0 -566
  328. package/src/providers/thinking.ts +0 -189
  329. package/src/providers/wire-messages.ts +0 -129
  330. package/src/providers/wire-responses.ts +0 -79
  331. package/src/providers/wire-select.ts +0 -53
  332. package/src/server/http.ts +0 -291
  333. package/src/server/openapi.ts +0 -246
  334. package/src/sextant/card-hits.ts +0 -102
  335. package/src/sextant/card-keys.ts +0 -55
  336. package/src/sextant/context-source.ts +0 -157
  337. package/src/sextant/crew-cards.ts +0 -350
  338. package/src/sextant/draw-agents.ts +0 -273
  339. package/src/sextant/draw-code.ts +0 -388
  340. package/src/sextant/draw-context.ts +0 -222
  341. package/src/sextant/draw-frame.ts +0 -164
  342. package/src/sextant/draw-market.ts +0 -573
  343. package/src/sextant/draw-messages.ts +0 -386
  344. package/src/sextant/draw-pet.ts +0 -230
  345. package/src/sextant/draw-plan.ts +0 -187
  346. package/src/sextant/draw-tabs.ts +0 -85
  347. package/src/sextant/draw-util.ts +0 -65
  348. package/src/sextant/draw-wizard.ts +0 -378
  349. package/src/sextant/engine.ts +0 -230
  350. package/src/sextant/frame-hits.ts +0 -25
  351. package/src/sextant/frame.ts +0 -101
  352. package/src/sextant/git-status.ts +0 -197
  353. package/src/sextant/grid.ts +0 -59
  354. package/src/sextant/input.ts +0 -119
  355. package/src/sextant/keys.ts +0 -521
  356. package/src/sextant/layout.ts +0 -86
  357. package/src/sextant/local-commands.ts +0 -169
  358. package/src/sextant/market-source.ts +0 -287
  359. package/src/sextant/mentions.ts +0 -200
  360. package/src/sextant/message-hits.ts +0 -26
  361. package/src/sextant/model.ts +0 -387
  362. package/src/sextant/overlays.ts +0 -456
  363. package/src/sextant/panel-hits.ts +0 -38
  364. package/src/sextant/pet.ts +0 -399
  365. package/src/sextant/screen.ts +0 -324
  366. package/src/sextant/scroll-hits.ts +0 -66
  367. package/src/sextant/scrollbar.ts +0 -82
  368. package/src/sextant/sextant-bridge.ts +0 -174
  369. package/src/sextant/sextant-cards.ts +0 -142
  370. package/src/sextant/sextant-diff-base.ts +0 -63
  371. package/src/sextant/sextant-files.ts +0 -154
  372. package/src/sextant/sextant-frame-loop.ts +0 -335
  373. package/src/sextant/sextant-renderer.ts +0 -574
  374. package/src/sextant/sextant-repo.ts +0 -140
  375. package/src/sextant/theme.ts +0 -66
  376. package/src/sextant/tool-rows.ts +0 -189
  377. package/src/sextant/types.ts +0 -493
  378. package/src/skills/index.ts +0 -387
  379. package/src/skills/pack.ts +0 -220
  380. package/src/skills/spec.ts +0 -162
  381. package/src/skills/tools.ts +0 -69
  382. package/src/skills/versioned.ts +0 -227
  383. package/src/telemetry/otel-export.ts +0 -122
  384. package/src/telemetry/otel-lanes.ts +0 -89
  385. package/src/telemetry/otel-logs.ts +0 -131
  386. package/src/telemetry/otel-metrics.ts +0 -136
  387. package/src/telemetry/otel.ts +0 -397
  388. package/src/telemetry/otlp.ts +0 -76
  389. package/src/tools/ask-user.ts +0 -156
  390. package/src/tools/bash-bg.ts +0 -94
  391. package/src/tools/bash-jobs.ts +0 -237
  392. package/src/tools/design.ts +0 -151
  393. package/src/tools/evalcell.ts +0 -338
  394. package/src/tools/html-text.ts +0 -139
  395. package/src/tools/provider.ts +0 -149
  396. package/src/tools/task.ts +0 -250
  397. package/src/tools/todo.ts +0 -320
  398. package/src/tools/webfetch.ts +0 -332
  399. package/src/tools/websearch.ts +0 -359
  400. package/src/tui/agents-cmd.ts +0 -41
  401. package/src/tui/app.ts +0 -749
  402. package/src/tui/attach.ts +0 -127
  403. package/src/tui/boot-notes.ts +0 -41
  404. package/src/tui/builtin-prompts.ts +0 -59
  405. package/src/tui/checkpoints-cmd.ts +0 -70
  406. package/src/tui/clipboard-image.ts +0 -81
  407. package/src/tui/clipboard.ts +0 -78
  408. package/src/tui/commands.ts +0 -283
  409. package/src/tui/config-view.ts +0 -53
  410. package/src/tui/context-cmds.ts +0 -282
  411. package/src/tui/cost.ts +0 -108
  412. package/src/tui/crash-guard.ts +0 -173
  413. package/src/tui/focus-terminal.ts +0 -34
  414. package/src/tui/git-cmds.ts +0 -273
  415. package/src/tui/git-plain.ts +0 -58
  416. package/src/tui/info-cmd.ts +0 -150
  417. package/src/tui/input-plain.ts +0 -76
  418. package/src/tui/mcp-cmd.ts +0 -128
  419. package/src/tui/memory-note.ts +0 -77
  420. package/src/tui/modes-cmd.ts +0 -45
  421. package/src/tui/notify-seq.ts +0 -100
  422. package/src/tui/notify.ts +0 -318
  423. package/src/tui/overlays.ts +0 -97
  424. package/src/tui/pi-renderer.ts +0 -428
  425. package/src/tui/providers-cmd.ts +0 -377
  426. package/src/tui/reasoning-view.ts +0 -56
  427. package/src/tui/renderer.ts +0 -128
  428. package/src/tui/replay-marker.ts +0 -29
  429. package/src/tui/session-cmd.ts +0 -148
  430. package/src/tui/session-manage.ts +0 -95
  431. package/src/tui/sextant-attach.ts +0 -102
  432. package/src/tui/sextant-io.ts +0 -202
  433. package/src/tui/sextant-smoke.ts +0 -110
  434. package/src/tui/shell-cmd.ts +0 -158
  435. package/src/tui/smoke.ts +0 -72
  436. package/src/tui/staged-terminal.ts +0 -50
  437. package/src/tui/startup.ts +0 -12
  438. package/src/tui/theme.ts +0 -59
  439. package/src/tui/todo-label.ts +0 -7
  440. package/src/tui/trust-card.ts +0 -107
  441. package/src/tui/tui-commands.ts +0 -87
  442. package/tsconfig.json +0 -30
  443. package/vendor/pi-tui/LICENSE +0 -21
  444. package/vendor/pi-tui/PATCHES.md +0 -12
  445. package/vendor/pi-tui/PROVENANCE.md +0 -12
  446. package/vendor/pi-tui/README.upstream.md +0 -854
  447. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  448. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  449. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  450. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  451. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  452. package/vendor/pi-tui/src/components/box.ts +0 -138
  453. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  454. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  455. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  456. package/vendor/pi-tui/src/components/image.ts +0 -128
  457. package/vendor/pi-tui/src/components/input.ts +0 -448
  458. package/vendor/pi-tui/src/components/loader.ts +0 -93
  459. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  460. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  461. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  462. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  463. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  464. package/vendor/pi-tui/src/components/stack.ts +0 -155
  465. package/vendor/pi-tui/src/components/text.ts +0 -108
  466. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  467. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  468. package/vendor/pi-tui/src/editor-component.ts +0 -75
  469. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  470. package/vendor/pi-tui/src/index.ts +0 -149
  471. package/vendor/pi-tui/src/keybindings.ts +0 -321
  472. package/vendor/pi-tui/src/keys.ts +0 -1402
  473. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  474. package/vendor/pi-tui/src/latex.ts +0 -1381
  475. package/vendor/pi-tui/src/layout-node.ts +0 -52
  476. package/vendor/pi-tui/src/layout.ts +0 -411
  477. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  478. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  479. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  480. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  481. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  482. package/vendor/pi-tui/src/terminal.ts +0 -554
  483. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  484. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  485. package/vendor/pi-tui/src/tui.ts +0 -1264
  486. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  487. package/vendor/pi-tui/src/utils.ts +0 -1327
  488. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  489. package/vendor/pi-tui/test/test-themes.ts +0 -39
  490. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  491. /package/{src/providers → dist/cli}/models-index.json +0 -0
@@ -1,854 +0,0 @@
1
- # @earendil-works/pi-tui
2
-
3
- Minimal terminal UI framework with differential rendering and synchronized output for flicker-free interactive CLI applications.
4
-
5
- ## Features
6
-
7
- - **Interchangeable Renderers**: Shared `TUI` interface with main-screen and alternate-screen implementations
8
- - **Differential Rendering**: Updates only changed lines or viewport rows
9
- - **Application-owned Scrolling**: Alternate-screen viewport supports mouse, trackpad, and keyboard navigation
10
- - **Synchronized Output**: Uses CSI 2026 for atomic screen updates (no flicker)
11
- - **Bracketed Paste Mode**: Handles large pastes correctly with markers for >10 line pastes
12
- - **Component-based**: Simple Component interface with render() method
13
- - **Theme Support**: Components accept theme interfaces for customizable styling
14
- - **Built-in Components**: Text, TruncatedText, Input, Editor, Markdown, Loader, SelectList, SettingsList, Spacer, Image, Box, Container, VStack, HStack, ScrollView
15
- - **Inline Images**: Renders images in terminals that support Kitty or iTerm2 graphics protocols
16
- - **Autocomplete Support**: File paths and slash commands
17
-
18
- ## Quick Start
19
-
20
- ```typescript
21
- import { type TUI, Text, Editor, ProcessTerminal, TuiMainScreen, matchesKey } from "@earendil-works/pi-tui";
22
-
23
- // Create terminal
24
- const terminal = new ProcessTerminal();
25
-
26
- // Create the default main-screen renderer through the shared TUI interface
27
- const tui: TUI = new TuiMainScreen(terminal);
28
-
29
- // Add components
30
- tui.addChild(new Text("Welcome to my app!"));
31
-
32
- import { defaultEditorTheme as editorTheme } from './test/test-themes.ts';
33
- const editor = new Editor(tui, editorTheme);
34
- editor.onSubmit = (text) => {
35
- console.log("Submitted:", text);
36
- tui.addChild(new Text(`You said: ${text}`));
37
- };
38
- tui.addChild(editor);
39
-
40
- // Focus the editor so it receives keyboard input
41
- tui.setFocus(editor);
42
-
43
- // In raw mode Ctrl+C doesn't send SIGINT — intercept it here to allow exit
44
- tui.addInputListener((data) => {
45
- if (matchesKey(data, 'ctrl+c')) {
46
- tui.stop();
47
- process.exit(0);
48
- }
49
- });
50
-
51
- // Start
52
- tui.start();
53
- ```
54
-
55
- ## Core API
56
-
57
- ### TUI interface and renderers
58
-
59
- `TUI` is the shared interface for component management, focus, overlays, input, lifecycle, terminal queries, and rendering. Choose a concrete renderer only when constructing the application:
60
-
61
- - `TuiMainScreen` renders into the main terminal buffer and preserves terminal scrollback.
62
- - `TuiAltScreen` renders a fixed-height viewport in the alternate terminal buffer with application-owned scrolling. When stopped, it restores the main buffer and prints the complete final document.
63
-
64
- ```typescript
65
- import { type TUI, TuiAltScreen, TuiMainScreen } from "@earendil-works/pi-tui";
66
-
67
- const tui: TUI = new TuiMainScreen(terminal);
68
- // To use an application-owned viewport in the alternate terminal buffer instead:
69
- // const tui: TUI = new TuiAltScreen(terminal);
70
-
71
- tui.addChild(component);
72
- tui.removeChild(component);
73
- tui.start();
74
- tui.stop();
75
- tui.requestRender(); // Request a re-render
76
-
77
- // Global debug key handler (Shift+Ctrl+D)
78
- tui.onDebug = () => console.log("Debug triggered");
79
- ```
80
-
81
- ### Alternate-screen viewport layouts
82
-
83
- `TuiAltScreen` can render an explicit terminal-height layout. `VStack` and `HStack` allocate constrained regions, while `ScrollView` owns scrolling for one region. These semantics are intentionally unavailable on `TuiMainScreen`, where the terminal owns scrollback.
84
-
85
- ```typescript
86
- import {
87
- Container,
88
- isViewportTUI,
89
- ScrollView,
90
- Text,
91
- VStack,
92
- } from "@earendil-works/pi-tui";
93
-
94
- const transcript = new Container();
95
- transcript.addChild(new Text("History"));
96
-
97
- const editorAndFooter = new VStack([
98
- editor,
99
- new Text("status"),
100
- ]);
101
-
102
- if (isViewportTUI(tui)) {
103
- tui.setLayoutRoot(new VStack([
104
- {
105
- component: new ScrollView(transcript, {
106
- follow: "end",
107
- primary: true,
108
- overscroll: "chain",
109
- }),
110
- basis: 0,
111
- grow: 1,
112
- minSize: 1,
113
- },
114
- {
115
- component: editorAndFooter,
116
- basis: "auto",
117
- shrink: 1,
118
- minSize: 1,
119
- },
120
- ]));
121
- }
122
- ```
123
-
124
- Stack entries support `basis`, `grow`, `shrink`, `minSize`, `maxSize`, and responsive `visible` callbacks. Mouse-wheel input targets the scroll view under the pointer and unused delta chains to outer scroll views by default. The primary scroll view receives the alternate-screen keyboard navigation actions and wheel input over non-scrollable regions. It can also jump between OSC 133 semantic prompt markers, matching common terminal prompt-navigation shortcuts. Press `Ctrl+Shift+F` to search its rendered content, `Enter`/`Ctrl+G` and `Shift+Enter`/`Ctrl+Shift+G` to move between matches, and `Escape` to close search. `TuiAltScreenOptions.searchMatchStyle` and `searchCurrentMatchStyle` customize match highlighting.
125
-
126
- Layout geometry is rebuilt for each requested frame. Stateful components are retained, and their existing rendered-line caches remain effective. Calling `render(width)` directly on these layout components produces an unbounded document, which is also used when alt mode restores the main screen.
127
-
128
- ### Overlays
129
-
130
- Overlays render components on top of existing content without replacing it. Useful for dialogs, menus, and modal UI.
131
-
132
- ```typescript
133
- // Show overlay with default options (centered, max 80 cols)
134
- const handle = tui.showOverlay(component);
135
-
136
- // Show overlay with custom positioning and sizing
137
- // Values can be numbers (absolute) or percentage strings (e.g., "50%")
138
- const handle = tui.showOverlay(component, {
139
- // Sizing
140
- width: 60, // Fixed width in columns
141
- width: "80%", // Width as percentage of terminal
142
- minWidth: 40, // Minimum width floor
143
- maxHeight: 20, // Maximum height in rows
144
- maxHeight: "50%", // Maximum height as percentage of terminal
145
-
146
- // Anchor-based positioning (default: 'center')
147
- anchor: 'bottom-right', // Position relative to anchor point
148
- offsetX: 2, // Horizontal offset from anchor
149
- offsetY: -1, // Vertical offset from anchor
150
-
151
- // Percentage-based positioning (alternative to anchor)
152
- row: "25%", // Vertical position (0%=top, 100%=bottom)
153
- col: "50%", // Horizontal position (0%=left, 100%=right)
154
-
155
- // Absolute positioning (overrides anchor/percent)
156
- row: 5, // Exact row position
157
- col: 10, // Exact column position
158
-
159
- // Margin from terminal edges
160
- margin: 2, // All sides
161
- margin: { top: 1, right: 2, bottom: 1, left: 2 },
162
-
163
- // Responsive visibility
164
- visible: (termWidth, termHeight) => termWidth >= 100 // Hide on narrow terminals
165
-
166
- // Focus behavior
167
- nonCapturing: true // Don't auto-focus when shown
168
- });
169
-
170
- // OverlayHandle methods
171
- handle.hide(); // Permanently remove the overlay
172
- handle.setHidden(true); // Temporarily hide (can show again)
173
- handle.setHidden(false); // Show again after hiding
174
- handle.isHidden(); // Check if temporarily hidden
175
- handle.focus(); // Focus and bring to visual front
176
- handle.unfocus(); // Release focus to normal fallback
177
- handle.unfocus({ target: baseComponent }); // Release this overlay to a specific component
178
- handle.unfocus({ target: null }); // Release this overlay and leave focus empty
179
- handle.isFocused(); // Check if overlay has focus
180
-
181
- handle.unfocus();
182
- // Overlay loses focus; TUI falls back to another visible capturing overlay or the previous focus target.
183
-
184
- handle.unfocus({ target: null });
185
- // Overlay loses focus; no component receives input until focus is set again.
186
-
187
- // A focused visible overlay reclaims keyboard input after temporary replacement UI
188
- // releases focus. If you want a specific component to receive input while overlays remain
189
- // visible, call handle.unfocus({ target: component }).
190
-
191
- // Hide topmost overlay
192
- tui.hideOverlay();
193
-
194
- // Check if any visible overlay is active
195
- tui.hasOverlay();
196
- ```
197
-
198
- **Anchor values**: `'center'`, `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`, `'top-center'`, `'bottom-center'`, `'left-center'`, `'right-center'`
199
-
200
- **Resolution order**:
201
- 1. `minWidth` is applied as a floor after width calculation
202
- 2. For position: absolute `row`/`col` > percentage `row`/`col` > `anchor`
203
- 3. `margin` clamps final position to stay within terminal bounds
204
- 4. `visible` callback controls whether overlay renders (called each frame)
205
-
206
- ### Component Interface
207
-
208
- All components implement:
209
-
210
- ```typescript
211
- interface Component {
212
- render(width: number): string[];
213
- handleInput?(data: string): void;
214
- invalidate?(): void;
215
- }
216
- ```
217
-
218
- | Method | Description |
219
- |--------|-------------|
220
- | `render(width)` | Returns an array of strings, one per line. Each line **must not exceed `width`** or the TUI will error. Use `truncateToWidth()` or manual wrapping to ensure this. |
221
- | `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences). |
222
- | `invalidate?()` | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call. |
223
-
224
- The TUI appends a full SGR reset and OSC 8 reset at the end of each rendered line. Styles do not carry across lines. If you emit multi-line text with styling, reapply styles per line or use `wrapTextWithAnsi()` so styles are preserved for each wrapped line.
225
-
226
- ### Focusable Interface (IME Support)
227
-
228
- Components that display a text cursor and need IME (Input Method Editor) support should implement the `Focusable` interface:
229
-
230
- ```typescript
231
- import { CURSOR_MARKER, type Component, type Focusable } from "@earendil-works/pi-tui";
232
-
233
- class MyInput implements Component, Focusable {
234
- focused: boolean = false; // Set by TUI when focus changes
235
-
236
- render(width: number): string[] {
237
- const marker = this.focused ? CURSOR_MARKER : "";
238
- // Emit marker right before the fake cursor
239
- return [`> ${beforeCursor}${marker}\x1b[7m${atCursor}\x1b[27m${afterCursor}`];
240
- }
241
- }
242
- ```
243
-
244
- When a `Focusable` component has focus, TUI:
245
- 1. Sets `focused = true` on the component
246
- 2. Scans rendered output for `CURSOR_MARKER` (a zero-width APC escape sequence)
247
- 3. Positions the hardware terminal cursor at that location
248
- 4. Shows the hardware cursor only when `showHardwareCursor` is enabled
249
-
250
- The cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with the renderer constructor's `showHardwareCursor` argument, `setShowHardwareCursor(true)`, or `PI_HARDWARE_CURSOR=1`. The `Editor` and `Input` built-in components already implement this interface.
251
-
252
- **Container components with embedded inputs:** When a container component (dialog, selector, etc.) contains an `Input` or `Editor` child, the container must implement `Focusable` and propagate the focus state to the child:
253
-
254
- ```typescript
255
- import { Container, type Focusable, Input } from "@earendil-works/pi-tui";
256
-
257
- class SearchDialog extends Container implements Focusable {
258
- private searchInput: Input;
259
-
260
- // Propagate focus to child input for IME cursor positioning
261
- private _focused = false;
262
- get focused(): boolean { return this._focused; }
263
- set focused(value: boolean) {
264
- this._focused = value;
265
- this.searchInput.focused = value;
266
- }
267
-
268
- constructor() {
269
- super();
270
- this.searchInput = new Input();
271
- this.addChild(this.searchInput);
272
- }
273
- }
274
- ```
275
-
276
- Without this propagation, typing with an IME (Chinese, Japanese, Korean, etc.) will show the candidate window in the wrong position.
277
-
278
- ## Built-in Components
279
-
280
- ### Container
281
-
282
- Groups child components.
283
-
284
- ```typescript
285
- const container = new Container();
286
- container.addChild(component);
287
- container.removeChild(component);
288
- ```
289
-
290
- ### Box
291
-
292
- Container that applies padding and background color to all children.
293
-
294
- ```typescript
295
- const box = new Box(
296
- 1, // paddingX (default: 1)
297
- 1, // paddingY (default: 1)
298
- (text) => chalk.bgGray(text) // optional background function
299
- );
300
- box.addChild(new Text("Content"));
301
- box.setBgFn((text) => chalk.bgBlue(text)); // Change background dynamically
302
- ```
303
-
304
- ### Text
305
-
306
- Displays multi-line text with word wrapping and padding.
307
-
308
- ```typescript
309
- const text = new Text(
310
- "Hello World", // text content
311
- 1, // paddingX (default: 1)
312
- 1, // paddingY (default: 1)
313
- (text) => chalk.bgGray(text) // optional background function
314
- );
315
- text.setText("Updated text");
316
- text.setCustomBgFn((text) => chalk.bgBlue(text));
317
- ```
318
-
319
- ### TruncatedText
320
-
321
- Single-line text that truncates to fit viewport width. Useful for status lines and headers.
322
-
323
- ```typescript
324
- const truncated = new TruncatedText(
325
- "This is a very long line that will be truncated...",
326
- 0, // paddingX (default: 0)
327
- 0 // paddingY (default: 0)
328
- );
329
- ```
330
-
331
- ### Input
332
-
333
- Single-line text input with horizontal scrolling.
334
-
335
- ```typescript
336
- const input = new Input();
337
- input.onSubmit = (value) => console.log(value);
338
- input.setValue("initial");
339
- input.getValue();
340
- ```
341
-
342
- **Key Bindings:**
343
- - `Enter` - Submit
344
- - `Ctrl+A` / `Ctrl+E` - Line start/end
345
- - `Ctrl+W` or `Alt+Backspace` - Delete word backwards
346
- - `Ctrl+U` - Delete to start of line
347
- - `Ctrl+K` - Delete to end of line
348
- - `Ctrl+Left` / `Ctrl+Right` - Word navigation
349
- - `Alt+Left` / `Alt+Right` - Word navigation
350
- - Arrow keys, Backspace, Delete work as expected
351
-
352
- ### Editor
353
-
354
- Multi-line text editor with autocomplete, file completion, paste handling, and vertical scrolling when content exceeds terminal height.
355
-
356
- ```typescript
357
- interface EditorTheme {
358
- borderColor: (str: string) => string;
359
- selectList: SelectListTheme;
360
- }
361
-
362
- interface EditorOptions {
363
- paddingX?: number; // Horizontal padding (default: 0)
364
- }
365
-
366
- const editor = new Editor(tui, theme, options?); // tui is required for height-aware scrolling
367
- editor.onSubmit = (text) => console.log(text);
368
- editor.onChange = (text) => console.log("Changed:", text);
369
- editor.disableSubmit = true; // Disable submit temporarily
370
- editor.setAutocompleteProvider(provider);
371
- editor.borderColor = (s) => chalk.blue(s); // Change border dynamically
372
- editor.setPaddingX(1); // Update horizontal padding dynamically
373
- editor.getPaddingX(); // Get current padding
374
- ```
375
-
376
- **Features:**
377
- - Multi-line editing with word wrap
378
- - Slash command autocomplete (type `/`)
379
- - File path autocomplete (press `Tab`)
380
- - Large paste handling (>10 lines creates `[paste #1 +50 lines]` marker)
381
- - Horizontal lines above/below editor
382
- - Fake cursor rendering (hidden real cursor)
383
-
384
- **Key Bindings:**
385
- - `Enter` - Submit
386
- - `Shift+Enter`, `Ctrl+Enter`, or `Alt+Enter` - New line (terminal-dependent, Alt+Enter most reliable)
387
- - `Tab` - Autocomplete
388
- - `Ctrl+K` - Delete to end of line
389
- - `Ctrl+U` - Delete to start of line
390
- - `Ctrl+W` or `Alt+Backspace` - Delete word backwards
391
- - `Alt+D` or `Alt+Delete` - Delete word forwards
392
- - `Ctrl+A` / `Ctrl+E` - Line start/end
393
- - `Ctrl+]` - Jump forward to character (awaits next keypress, then moves cursor to first occurrence)
394
- - `Ctrl+Alt+]` - Jump backward to character
395
- - Arrow keys, Backspace, Delete work as expected
396
-
397
- ### Markdown
398
-
399
- Renders markdown with syntax highlighting and theming support.
400
-
401
- ```typescript
402
- interface MarkdownTheme {
403
- heading: (text: string) => string;
404
- link: (text: string) => string;
405
- linkUrl: (text: string) => string;
406
- code: (text: string) => string;
407
- codeBlock: (text: string) => string;
408
- codeBlockBorder: (text: string) => string;
409
- quote: (text: string) => string;
410
- quoteBorder: (text: string) => string;
411
- hr: (text: string) => string;
412
- listBullet: (text: string) => string;
413
- bold: (text: string) => string;
414
- italic: (text: string) => string;
415
- strikethrough: (text: string) => string;
416
- underline: (text: string) => string;
417
- highlightCode?: (code: string, lang?: string) => string[];
418
- }
419
-
420
- interface DefaultTextStyle {
421
- color?: (text: string) => string;
422
- bgColor?: (text: string) => string;
423
- bold?: boolean;
424
- italic?: boolean;
425
- strikethrough?: boolean;
426
- underline?: boolean;
427
- }
428
-
429
- const md = new Markdown(
430
- "# Hello\n\nSome **bold** text",
431
- 1, // paddingX
432
- 1, // paddingY
433
- theme, // MarkdownTheme
434
- defaultStyle // optional DefaultTextStyle
435
- );
436
- md.setText("Updated markdown");
437
- ```
438
-
439
- **Features:**
440
- - Headings, bold, italic, code blocks, lists, links, blockquotes
441
- - HTML tags rendered as plain text
442
- - Optional syntax highlighting via `highlightCode`
443
- - Padding support
444
- - Render caching for performance
445
-
446
- ### Loader
447
-
448
- Animated loading spinner.
449
-
450
- ```typescript
451
- const loader = new Loader(
452
- tui, // TUI instance for render updates
453
- (s) => chalk.cyan(s), // spinner color function
454
- (s) => chalk.gray(s), // message color function
455
- "Loading..." // message (default: "Loading...")
456
- );
457
- loader.start();
458
- loader.setMessage("Still loading...");
459
- loader.stop();
460
- ```
461
-
462
- ### CancellableLoader
463
-
464
- Extends Loader with Escape key handling and an AbortSignal for cancelling async operations.
465
-
466
- ```typescript
467
- const loader = new CancellableLoader(
468
- tui, // TUI instance for render updates
469
- (s) => chalk.cyan(s), // spinner color function
470
- (s) => chalk.gray(s), // message color function
471
- "Working..." // message
472
- );
473
- loader.onAbort = () => done(null); // Called when user presses Escape
474
- doAsyncWork(loader.signal).then(done);
475
- ```
476
-
477
- **Properties:**
478
- - `signal: AbortSignal` - Aborted when user presses Escape
479
- - `aborted: boolean` - Whether the loader was aborted
480
- - `onAbort?: () => void` - Callback when user presses Escape
481
-
482
- ### SelectList
483
-
484
- Interactive selection list with keyboard navigation.
485
-
486
- ```typescript
487
- interface SelectItem {
488
- value: string;
489
- label: string;
490
- description?: string;
491
- }
492
-
493
- interface SelectListTheme {
494
- selectedPrefix: (text: string) => string;
495
- selectedText: (text: string) => string;
496
- description: (text: string) => string;
497
- scrollInfo: (text: string) => string;
498
- noMatch: (text: string) => string;
499
- }
500
-
501
- const list = new SelectList(
502
- [
503
- { value: "opt1", label: "Option 1", description: "First option" },
504
- { value: "opt2", label: "Option 2", description: "Second option" },
505
- ],
506
- 5, // maxVisible
507
- theme // SelectListTheme
508
- );
509
-
510
- list.onSelect = (item) => console.log("Selected:", item);
511
- list.onCancel = () => console.log("Cancelled");
512
- list.onSelectionChange = (item) => console.log("Highlighted:", item);
513
- list.setFilter("opt"); // Filter items
514
- ```
515
-
516
- **Controls:**
517
- - Arrow keys: Navigate
518
- - Enter: Select
519
- - Escape: Cancel
520
-
521
- ### SettingsList
522
-
523
- Settings panel with value cycling and submenus.
524
-
525
- ```typescript
526
- interface SettingItem {
527
- id: string;
528
- label: string;
529
- description?: string;
530
- currentValue: string;
531
- values?: string[]; // If provided, Enter/Space cycles through these
532
- submenu?: (currentValue: string, done: (selectedValue?: string) => void) => Component;
533
- }
534
-
535
- interface SettingsListTheme {
536
- label: (text: string, selected: boolean) => string;
537
- value: (text: string, selected: boolean) => string;
538
- description: (text: string) => string;
539
- cursor: string;
540
- hint: (text: string) => string;
541
- }
542
-
543
- const settings = new SettingsList(
544
- [
545
- { id: "theme", label: "Theme", currentValue: "dark", values: ["dark", "light"] },
546
- { id: "model", label: "Model", currentValue: "gpt-4", submenu: (val, done) => modelSelector },
547
- ],
548
- 10, // maxVisible
549
- theme, // SettingsListTheme
550
- (id, newValue) => console.log(`${id} changed to ${newValue}`),
551
- () => console.log("Cancelled")
552
- );
553
- settings.updateValue("theme", "light");
554
- ```
555
-
556
- **Controls:**
557
- - Arrow keys: Navigate
558
- - Enter/Space: Activate (cycle value or open submenu)
559
- - Escape: Cancel
560
-
561
- ### Spacer
562
-
563
- Empty lines for vertical spacing.
564
-
565
- ```typescript
566
- const spacer = new Spacer(2); // 2 empty lines (default: 1)
567
- ```
568
-
569
- ### Image
570
-
571
- Renders images inline for terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm) or iTerm2 inline images. Falls back to a text placeholder on unsupported terminals.
572
-
573
- ```typescript
574
- interface ImageTheme {
575
- fallbackColor: (str: string) => string;
576
- }
577
-
578
- interface ImageOptions {
579
- maxWidthCells?: number;
580
- maxHeightCells?: number;
581
- filename?: string;
582
- }
583
-
584
- const image = new Image(
585
- base64Data, // base64-encoded image data
586
- "image/png", // MIME type
587
- theme, // ImageTheme
588
- options // optional ImageOptions
589
- );
590
- tui.addChild(image);
591
- ```
592
-
593
- Supported formats: PNG, JPEG, GIF, WebP. Dimensions are parsed from the image headers automatically.
594
-
595
- #### Alternate-screen image compatibility
596
-
597
- `TuiAltScreen` supports inline images and partial viewport cropping in terminals that implement the Kitty graphics protocol, including Kitty and Ghostty. iTerm2's inline-image protocol does not provide operations to delete an existing placement or crop its source while scrolling. To prevent stale images from remaining over repainted content, `TuiAltScreen` renders image components as text placeholders in iTerm2. `TuiMainScreen` continues to render iTerm2 inline images normally.
598
-
599
- ## Autocomplete
600
-
601
- ### CombinedAutocompleteProvider
602
-
603
- Supports both slash commands and file paths.
604
-
605
- ```typescript
606
- import { CombinedAutocompleteProvider } from "@earendil-works/pi-tui";
607
-
608
- const provider = new CombinedAutocompleteProvider(
609
- [
610
- { name: "help", description: "Show help" },
611
- { name: "clear", description: "Clear screen" },
612
- { name: "delete", description: "Delete last message" },
613
- ],
614
- process.cwd() // base path for file completion
615
- );
616
-
617
- editor.setAutocompleteProvider(provider);
618
- ```
619
-
620
- **Features:**
621
- - Type `/` to see slash commands
622
- - Press `Tab` for file path completion
623
- - Works with `~/`, `./`, `../`, and `@` prefix
624
- - Filters to attachable files for `@` prefix
625
-
626
- ## Key Detection
627
-
628
- Use `matchesKey()` with the `Key` helper for detecting keyboard input (supports Kitty keyboard protocol):
629
-
630
- ```typescript
631
- import { matchesKey, Key } from "@earendil-works/pi-tui";
632
-
633
- if (matchesKey(data, Key.ctrl("c"))) {
634
- process.exit(0);
635
- }
636
-
637
- if (matchesKey(data, Key.enter)) {
638
- submit();
639
- } else if (matchesKey(data, Key.escape)) {
640
- cancel();
641
- } else if (matchesKey(data, Key.up)) {
642
- moveUp();
643
- }
644
- ```
645
-
646
- **Key identifiers** (use `Key.*` for autocomplete, or string literals):
647
- - Basic keys: `Key.enter`, `Key.escape`, `Key.tab`, `Key.space`, `Key.backspace`, `Key.delete`, `Key.home`, `Key.end`
648
- - Arrow keys: `Key.up`, `Key.down`, `Key.left`, `Key.right`
649
- - With modifiers: `Key.ctrl("c")`, `Key.shift("tab")`, `Key.alt("left")`, `Key.ctrlShift("p")`
650
- - String format also works: `"enter"`, `"ctrl+c"`, `"shift+tab"`, `"ctrl+shift+p"`
651
-
652
- ## Rendering modes
653
-
654
- `TuiMainScreen` uses three rendering strategies:
655
-
656
- 1. **First Render**: Output all lines without clearing scrollback
657
- 2. **Width Changed or Change Above Viewport**: Clear screen and fully re-render
658
- 3. **Normal Update**: Move the cursor to the first changed line, clear to the end, and render changed lines
659
-
660
- `TuiAltScreen` owns a terminal-height viewport. Without an explicit layout root it preserves the legacy single-document scrolling behavior. With `setLayoutRoot()`, `VStack`, `HStack`, and nested `ScrollView` components can reserve fixed regions and independently scroll constrained regions. It updates changed viewport rows in place, follows streaming output while at the bottom, and preserves a manually selected scroll position while content grows. Mouse-wheel and configurable keyboard navigation scroll without modifying terminal scrollback, including jumps between OSC 133 semantic prompt markers. Clicking an OSC 8 hyperlink opens it with the configured URL handler. Dragging with the primary mouse button selects text and, unless `TuiAltScreenOptions.copyOnSelect` is `false`, copies it to the clipboard with OSC 52; holding the drag at a scroll view's top or bottom edge auto-scrolls and extends the selection into off-screen content. Kitty images support vertical viewport cropping; iTerm2 inline images fall back to text because the iTerm2 protocol cannot delete or crop placements during viewport repainting.
661
-
662
- Both renderers wrap updates in **synchronized output** (`\x1b[?2026h` ... `\x1b[?2026l`) for atomic, flicker-free rendering.
663
-
664
- ## Terminal Interface
665
-
666
- The TUI works with any object implementing the `Terminal` interface:
667
-
668
- ```typescript
669
- interface Terminal {
670
- start(onInput: (data: string) => void, onResize: () => void): void;
671
- stop(): void;
672
- write(data: string): void;
673
- get columns(): number;
674
- get rows(): number;
675
- moveBy(lines: number): void;
676
- hideCursor(): void;
677
- showCursor(): void;
678
- clearLine(): void;
679
- clearFromCursor(): void;
680
- clearScreen(): void;
681
- }
682
- ```
683
-
684
- **Built-in implementations:**
685
- - `ProcessTerminal` - Uses `process.stdin/stdout`
686
- - `VirtualTerminal` - For testing (uses `@xterm/headless`)
687
-
688
- ## Utilities
689
-
690
- ```typescript
691
- import { visibleWidth, truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
692
-
693
- // Get visible width of string (ignoring ANSI codes)
694
- const width = visibleWidth("\x1b[31mHello\x1b[0m"); // 5
695
-
696
- // Truncate string to width (preserving ANSI codes, adds ellipsis)
697
- const truncated = truncateToWidth("Hello World", 8); // "Hello..."
698
-
699
- // Truncate without ellipsis
700
- const truncatedNoEllipsis = truncateToWidth("Hello World", 8, ""); // "Hello Wo"
701
-
702
- // Wrap text to width (preserving ANSI codes across line breaks)
703
- const lines = wrapTextWithAnsi("This is a long line that needs wrapping", 20);
704
- // ["This is a long line", "that needs wrapping"]
705
- ```
706
-
707
- ## Creating Custom Components
708
-
709
- When creating custom components, **each line returned by `render()` must not exceed the `width` parameter**. The TUI will error if any line is wider than the terminal.
710
-
711
- ### Handling Input
712
-
713
- Use `matchesKey()` with the `Key` helper for keyboard input:
714
-
715
- ```typescript
716
- import { matchesKey, Key, truncateToWidth } from "@earendil-works/pi-tui";
717
- import type { Component } from "@earendil-works/pi-tui";
718
-
719
- class MyInteractiveComponent implements Component {
720
- private selectedIndex = 0;
721
- private items = ["Option 1", "Option 2", "Option 3"];
722
-
723
- public onSelect?: (index: number) => void;
724
- public onCancel?: () => void;
725
-
726
- handleInput(data: string): void {
727
- if (matchesKey(data, Key.up)) {
728
- this.selectedIndex = Math.max(0, this.selectedIndex - 1);
729
- } else if (matchesKey(data, Key.down)) {
730
- this.selectedIndex = Math.min(this.items.length - 1, this.selectedIndex + 1);
731
- } else if (matchesKey(data, Key.enter)) {
732
- this.onSelect?.(this.selectedIndex);
733
- } else if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
734
- this.onCancel?.();
735
- }
736
- }
737
-
738
- render(width: number): string[] {
739
- return this.items.map((item, i) => {
740
- const prefix = i === this.selectedIndex ? "> " : " ";
741
- return truncateToWidth(prefix + item, width);
742
- });
743
- }
744
- }
745
- ```
746
-
747
- ### Handling Line Width
748
-
749
- Use the provided utilities to ensure lines fit:
750
-
751
- ```typescript
752
- import { visibleWidth, truncateToWidth } from "@earendil-works/pi-tui";
753
- import type { Component } from "@earendil-works/pi-tui";
754
-
755
- class MyComponent implements Component {
756
- private text: string;
757
-
758
- constructor(text: string) {
759
- this.text = text;
760
- }
761
-
762
- render(width: number): string[] {
763
- // Option 1: Truncate long lines
764
- return [truncateToWidth(this.text, width)];
765
-
766
- // Option 2: Check and pad to exact width
767
- const line = this.text;
768
- const visible = visibleWidth(line);
769
- if (visible > width) {
770
- return [truncateToWidth(line, width)];
771
- }
772
- // Pad to exact width (optional, for backgrounds)
773
- return [line + " ".repeat(width - visible)];
774
- }
775
- }
776
- ```
777
-
778
- ### ANSI Code Considerations
779
-
780
- Both `visibleWidth()` and `truncateToWidth()` correctly handle ANSI escape codes:
781
-
782
- - `visibleWidth()` ignores ANSI codes when calculating width
783
- - `truncateToWidth()` preserves ANSI codes and properly closes them when truncating
784
-
785
- ```typescript
786
- import chalk from "chalk";
787
-
788
- const styled = chalk.red("Hello") + " " + chalk.blue("World");
789
- const width = visibleWidth(styled); // 11 (not counting ANSI codes)
790
- const truncated = truncateToWidth(styled, 8); // Red "Hello" + " W..." with proper reset
791
- ```
792
-
793
- ### Caching
794
-
795
- For performance, components should cache their rendered output and only re-render when necessary:
796
-
797
- ```typescript
798
- class CachedComponent implements Component {
799
- private text: string;
800
- private cachedWidth?: number;
801
- private cachedLines?: string[];
802
-
803
- render(width: number): string[] {
804
- if (this.cachedLines && this.cachedWidth === width) {
805
- return this.cachedLines;
806
- }
807
-
808
- const lines = [truncateToWidth(this.text, width)];
809
-
810
- this.cachedWidth = width;
811
- this.cachedLines = lines;
812
- return lines;
813
- }
814
-
815
- invalidate(): void {
816
- this.cachedWidth = undefined;
817
- this.cachedLines = undefined;
818
- }
819
- }
820
- ```
821
-
822
- ## Example
823
-
824
- See `test/chat-simple.ts` for a complete chat interface example with:
825
- - Markdown messages with custom background colors
826
- - Loading spinner during responses
827
- - Editor with autocomplete and slash commands
828
- - Spacers between messages
829
-
830
- Run it:
831
- ```bash
832
- npx tsx test/chat-simple.ts
833
- ```
834
-
835
- ## Development
836
-
837
- ```bash
838
- # Install dependencies (from monorepo root)
839
- npm install
840
-
841
- # Run type checking
842
- npm run check
843
-
844
- # Run the demo
845
- npx tsx test/chat-simple.ts
846
- ```
847
-
848
- ### Debug logging
849
-
850
- Set `PI_TUI_WRITE_LOG` to capture the raw ANSI stream written to stdout.
851
-
852
- ```bash
853
- PI_TUI_WRITE_LOG=/tmp/tui-ansi.log npx tsx test/chat-simple.ts
854
- ```