rovecode 0.4.0-beta.3 → 0.4.0

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 (432) hide show
  1. package/README.md +57 -72
  2. package/THIRD_PARTY_NOTICES.md +0 -44
  3. package/bin/rovecode.ts +21 -0
  4. package/package.json +16 -38
  5. package/src/account/keys.ts +97 -0
  6. package/src/account/login.ts +158 -0
  7. package/src/account/provision.ts +47 -0
  8. package/src/account/store.ts +63 -0
  9. package/src/acp/server.ts +373 -0
  10. package/src/cli/account-cmd.ts +116 -0
  11. package/src/cli/connect.ts +244 -0
  12. package/src/cli/context-cmd.ts +199 -0
  13. package/src/cli/dispatch.ts +109 -0
  14. package/src/cli/doctor.ts +324 -0
  15. package/src/cli/export.ts +278 -0
  16. package/src/cli/help.ts +240 -0
  17. package/src/cli/is-tui-invocation.ts +8 -0
  18. package/src/cli/main.ts +599 -0
  19. package/src/cli/market-cmd.ts +658 -0
  20. package/src/cli/mcp-market-cmd.ts +299 -0
  21. package/src/cli/output.ts +382 -0
  22. package/src/cli/repl.ts +172 -0
  23. package/src/cli/resume.ts +32 -0
  24. package/src/cli/run-limits.ts +78 -0
  25. package/src/cli/runtime.ts +792 -0
  26. package/src/cli/setup.ts +187 -0
  27. package/src/cli/update-cmd.ts +78 -0
  28. package/src/cli/workflow-cmd.ts +100 -0
  29. package/src/coding/checkpoints.ts +270 -0
  30. package/src/coding/diff.ts +136 -0
  31. package/src/coding/files.ts +339 -0
  32. package/src/coding/hashline.ts +319 -0
  33. package/src/coding/lsp.ts +406 -0
  34. package/src/coding/repomap-cache.ts +99 -0
  35. package/src/coding/repomap-files.ts +110 -0
  36. package/src/coding/repomap.ts +392 -0
  37. package/src/core/compaction.ts +399 -0
  38. package/src/core/config.ts +289 -0
  39. package/src/core/context-report.ts +228 -0
  40. package/src/core/context.ts +60 -0
  41. package/src/core/count-remote.ts +107 -0
  42. package/src/core/execpolicy-rules.ts +196 -0
  43. package/src/core/execpolicy.ts +385 -0
  44. package/src/core/executor.ts +397 -0
  45. package/src/core/guardrails.ts +400 -0
  46. package/src/core/hooks.ts +398 -0
  47. package/src/core/images.ts +230 -0
  48. package/src/core/intro.ts +236 -0
  49. package/src/core/loop.ts +621 -0
  50. package/src/core/modes.ts +372 -0
  51. package/src/core/orchestrator.ts +207 -0
  52. package/src/core/reflection.ts +165 -0
  53. package/src/core/sandbox-config.ts +167 -0
  54. package/src/core/session-images.ts +73 -0
  55. package/src/core/session.ts +398 -0
  56. package/src/core/settings.ts +98 -0
  57. package/src/core/stuck-detector.ts +273 -0
  58. package/src/core/tasks.ts +374 -0
  59. package/src/core/token-scale.ts +108 -0
  60. package/src/core/tool-output-budget.ts +166 -0
  61. package/src/core/tools.ts +288 -0
  62. package/src/core/types.ts +330 -0
  63. package/src/core/update-check.ts +171 -0
  64. package/src/core/update.ts +158 -0
  65. package/src/core/usage.ts +204 -0
  66. package/src/core/validate.ts +121 -0
  67. package/src/core/verify-gate.ts +159 -0
  68. package/src/core/verify.ts +237 -0
  69. package/src/core/voice.ts +158 -0
  70. package/src/core/win-job.ts +183 -0
  71. package/src/design/audit.ts +797 -0
  72. package/src/design/direction.ts +190 -0
  73. package/src/design/rules.ts +157 -0
  74. package/src/eval/bench.ts +150 -0
  75. package/src/eval/gauntlet-runner.ts +218 -0
  76. package/src/eval/gauntlet.ts +226 -0
  77. package/src/eval/grader.ts +186 -0
  78. package/src/eval/record.ts +202 -0
  79. package/src/eval/redact.ts +141 -0
  80. package/src/eval/replay.ts +147 -0
  81. package/src/eval/trajectory.ts +373 -0
  82. package/src/index.ts +17 -0
  83. package/src/market/catalogs/mcp-docs.json +111 -0
  84. package/src/market/catalogs/plugins.json +111 -0
  85. package/src/market/catalogs/skills.json +478 -0
  86. package/src/market/clone.ts +72 -0
  87. package/src/market/context-cost.ts +121 -0
  88. package/src/market/digest.ts +106 -0
  89. package/src/market/index.ts +22 -0
  90. package/src/market/install.ts +578 -0
  91. package/src/market/manifest.ts +187 -0
  92. package/src/market/prereq.ts +145 -0
  93. package/src/market/registry.ts +363 -0
  94. package/src/market/resolve.ts +111 -0
  95. package/src/market/types.ts +236 -0
  96. package/src/market/validate.ts +227 -0
  97. package/src/mcp/client.ts +431 -0
  98. package/src/mcp/config.ts +239 -0
  99. package/src/mcp/local-package.ts +211 -0
  100. package/src/mcp/market-catalog.ts +84 -0
  101. package/src/mcp/market-install.ts +289 -0
  102. package/src/mcp/market.ts +0 -0
  103. package/src/mcp/tools.ts +131 -0
  104. package/src/mcp/trust.ts +49 -0
  105. package/src/memory/blocks.ts +175 -0
  106. package/src/memory/recall.ts +355 -0
  107. package/src/memory/store.ts +105 -0
  108. package/src/memory/tools.ts +99 -0
  109. package/src/plugins/cli.ts +123 -0
  110. package/src/plugins/discover.ts +108 -0
  111. package/src/plugins/index.ts +50 -0
  112. package/src/plugins/init.ts +140 -0
  113. package/src/plugins/install.ts +184 -0
  114. package/src/plugins/load.ts +149 -0
  115. package/src/plugins/manifest.ts +106 -0
  116. package/src/plugins/state.ts +83 -0
  117. package/src/providers/auth.ts +293 -0
  118. package/src/providers/cache.ts +223 -0
  119. package/src/providers/catalog-local.ts +160 -0
  120. package/src/providers/catalog.ts +408 -0
  121. package/src/providers/middleware-context.ts +86 -0
  122. package/src/providers/middleware.ts +373 -0
  123. package/src/providers/profile-glm53.ts +111 -0
  124. package/src/providers/profile-sonnet5-persona.ts +65 -0
  125. package/src/providers/profile-sonnet5-voice.ts +23 -0
  126. package/src/providers/profiles.ts +156 -0
  127. package/src/providers/provider-config.ts +311 -0
  128. package/src/providers/registry.ts +302 -0
  129. package/src/providers/response-validation.ts +80 -0
  130. package/src/providers/retry.ts +234 -0
  131. package/src/providers/router.ts +294 -0
  132. package/src/providers/sse.ts +26 -0
  133. package/src/providers/stream-errors.ts +117 -0
  134. package/src/providers/stream.ts +569 -0
  135. package/src/providers/thinking.ts +189 -0
  136. package/src/providers/wire-messages.ts +129 -0
  137. package/src/sdk/client.ts +225 -0
  138. package/src/sdk/index.ts +3 -0
  139. package/src/server/dashboard.ts +144 -0
  140. package/src/server/http.ts +343 -0
  141. package/src/server/openapi.ts +246 -0
  142. package/src/sextant/card-hits.ts +102 -0
  143. package/src/sextant/card-keys.ts +55 -0
  144. package/src/sextant/context-source.ts +157 -0
  145. package/src/sextant/draw-agents.ts +273 -0
  146. package/src/sextant/draw-code.ts +388 -0
  147. package/src/sextant/draw-context.ts +222 -0
  148. package/src/sextant/draw-frame.ts +164 -0
  149. package/src/sextant/draw-market.ts +573 -0
  150. package/src/sextant/draw-messages.ts +386 -0
  151. package/src/sextant/draw-pet.ts +230 -0
  152. package/src/sextant/draw-plan.ts +159 -0
  153. package/src/sextant/draw-tabs.ts +85 -0
  154. package/src/sextant/draw-util.ts +65 -0
  155. package/src/sextant/engine.ts +230 -0
  156. package/src/sextant/frame-hits.ts +25 -0
  157. package/src/sextant/frame.ts +101 -0
  158. package/src/sextant/git-status.ts +197 -0
  159. package/src/sextant/grid.ts +59 -0
  160. package/src/sextant/input.ts +119 -0
  161. package/src/sextant/keys.ts +488 -0
  162. package/src/sextant/layout.ts +86 -0
  163. package/src/sextant/local-commands.ts +156 -0
  164. package/src/sextant/market-source.ts +287 -0
  165. package/src/sextant/mentions.ts +141 -0
  166. package/src/sextant/message-hits.ts +26 -0
  167. package/src/sextant/model.ts +387 -0
  168. package/src/sextant/overlays.ts +451 -0
  169. package/src/sextant/panel-hits.ts +38 -0
  170. package/src/sextant/pet.ts +399 -0
  171. package/src/sextant/screen.ts +324 -0
  172. package/src/sextant/scroll-hits.ts +66 -0
  173. package/src/sextant/scrollbar.ts +82 -0
  174. package/src/sextant/selection.ts +123 -0
  175. package/src/sextant/sextant-bridge.ts +174 -0
  176. package/src/sextant/sextant-cards.ts +142 -0
  177. package/src/sextant/sextant-diff-base.ts +63 -0
  178. package/src/sextant/sextant-files.ts +154 -0
  179. package/src/sextant/sextant-frame-loop.ts +314 -0
  180. package/src/sextant/sextant-renderer.ts +478 -0
  181. package/src/sextant/sextant-repo.ts +131 -0
  182. package/src/sextant/theme.ts +66 -0
  183. package/src/sextant/tool-rows.ts +189 -0
  184. package/src/sextant/types.ts +473 -0
  185. package/src/skills/index.ts +306 -0
  186. package/src/skills/tools.ts +69 -0
  187. package/src/skills/versioned.ts +227 -0
  188. package/src/telemetry/otel.ts +353 -0
  189. package/src/telemetry/otlp.ts +68 -0
  190. package/src/tools/ask-user.ts +156 -0
  191. package/src/tools/design.ts +151 -0
  192. package/src/tools/evalcell.ts +338 -0
  193. package/src/tools/html-text.ts +139 -0
  194. package/src/tools/provider.ts +149 -0
  195. package/src/tools/task.ts +216 -0
  196. package/src/tools/todo.ts +320 -0
  197. package/src/tools/webfetch.ts +331 -0
  198. package/src/tui/app.ts +608 -0
  199. package/src/tui/attach.ts +127 -0
  200. package/src/tui/checkpoints-cmd.ts +70 -0
  201. package/src/tui/clipboard-image.ts +81 -0
  202. package/src/tui/commands.ts +277 -0
  203. package/src/tui/cost.ts +108 -0
  204. package/src/tui/info-cmd.ts +144 -0
  205. package/src/tui/mcp-cmd.ts +128 -0
  206. package/src/tui/modes-cmd.ts +45 -0
  207. package/src/tui/overlays.ts +97 -0
  208. package/src/tui/pi-renderer.ts +424 -0
  209. package/src/tui/providers-cmd.ts +366 -0
  210. package/src/tui/renderer.ts +101 -0
  211. package/src/tui/replay-marker.ts +29 -0
  212. package/src/tui/session-cmd.ts +146 -0
  213. package/src/tui/sextant-attach.ts +68 -0
  214. package/src/tui/sextant-io.ts +184 -0
  215. package/src/tui/sextant-smoke.ts +110 -0
  216. package/src/tui/smoke.ts +72 -0
  217. package/src/tui/theme.ts +59 -0
  218. package/src/tui/todo-label.ts +7 -0
  219. package/src/workflow/engine.ts +266 -0
  220. package/tsconfig.json +30 -0
  221. package/vendor/pi-tui/LICENSE +21 -0
  222. package/vendor/pi-tui/PATCHES.md +12 -0
  223. package/vendor/pi-tui/PROVENANCE.md +12 -0
  224. package/vendor/pi-tui/README.upstream.md +854 -0
  225. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  226. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  227. package/vendor/pi-tui/src/alt-screen-search.ts +158 -0
  228. package/vendor/pi-tui/src/autocomplete.ts +827 -0
  229. package/vendor/pi-tui/src/components/alt-screen-flash.ts +52 -0
  230. package/vendor/pi-tui/src/components/box.ts +138 -0
  231. package/vendor/pi-tui/src/components/cancellable-loader.ts +41 -0
  232. package/vendor/pi-tui/src/components/editor.ts +2364 -0
  233. package/vendor/pi-tui/src/components/h-stack.ts +45 -0
  234. package/vendor/pi-tui/src/components/image.ts +128 -0
  235. package/vendor/pi-tui/src/components/input.ts +448 -0
  236. package/vendor/pi-tui/src/components/loader.ts +93 -0
  237. package/vendor/pi-tui/src/components/markdown.ts +1016 -0
  238. package/vendor/pi-tui/src/components/scroll-view.ts +217 -0
  239. package/vendor/pi-tui/src/components/select-list.ts +230 -0
  240. package/vendor/pi-tui/src/components/settings-list.ts +277 -0
  241. package/vendor/pi-tui/src/components/spacer.ts +29 -0
  242. package/vendor/pi-tui/src/components/stack.ts +155 -0
  243. package/vendor/pi-tui/src/components/text.ts +108 -0
  244. package/vendor/pi-tui/src/components/truncated-text.ts +66 -0
  245. package/vendor/pi-tui/src/components/v-stack.ts +34 -0
  246. package/vendor/pi-tui/src/editor-component.ts +75 -0
  247. package/vendor/pi-tui/src/fuzzy.ts +138 -0
  248. package/vendor/pi-tui/src/index.ts +149 -0
  249. package/vendor/pi-tui/src/keybindings.ts +321 -0
  250. package/vendor/pi-tui/src/keys.ts +1402 -0
  251. package/vendor/pi-tui/src/kill-ring.ts +47 -0
  252. package/vendor/pi-tui/src/latex.ts +1381 -0
  253. package/vendor/pi-tui/src/layout-node.ts +52 -0
  254. package/vendor/pi-tui/src/layout.ts +411 -0
  255. package/vendor/pi-tui/src/native-modifiers.ts +60 -0
  256. package/vendor/pi-tui/src/native-module-path.ts +32 -0
  257. package/vendor/pi-tui/src/stdin-buffer.ts +445 -0
  258. package/vendor/pi-tui/src/terminal-colors.ts +74 -0
  259. package/vendor/pi-tui/src/terminal-image.ts +701 -0
  260. package/vendor/pi-tui/src/terminal.ts +554 -0
  261. package/vendor/pi-tui/src/tui-alt-screen.ts +1379 -0
  262. package/vendor/pi-tui/src/tui-main-screen.ts +655 -0
  263. package/vendor/pi-tui/src/tui.ts +1264 -0
  264. package/vendor/pi-tui/src/undo-stack.ts +29 -0
  265. package/vendor/pi-tui/src/utils.ts +1327 -0
  266. package/vendor/pi-tui/src/word-navigation.ts +118 -0
  267. package/vendor/pi-tui/test/test-themes.ts +39 -0
  268. package/vendor/pi-tui/test/virtual-terminal.ts +219 -0
  269. package/CHANGELOG.md +0 -527
  270. package/bin/rovecode.js +0 -24
  271. package/dist/cli/app-j6gn14w3.js +0 -2
  272. package/dist/cli/ask-user-cwstt8fz.js +0 -2
  273. package/dist/cli/auth-login-9bbp9915.js +0 -2
  274. package/dist/cli/auth-m8p9grty.js +0 -2
  275. package/dist/cli/bench-16zqdms5.js +0 -9
  276. package/dist/cli/catalog-1xchffa4.js +0 -2
  277. package/dist/cli/cli-1n1zb64f.js +0 -2
  278. package/dist/cli/client-2t9gjkck.js +0 -2
  279. package/dist/cli/commands-exafvm2b.js +0 -2
  280. package/dist/cli/connect-6zde0kn3.js +0 -2
  281. package/dist/cli/context-cmd-5t43wgqt.js +0 -2
  282. package/dist/cli/context-report-kt01pw8y.js +0 -2
  283. package/dist/cli/count-remote-ap7x3vh6.js +0 -2
  284. package/dist/cli/design-ne5zszyh.js +0 -2
  285. package/dist/cli/dispatch-2r5myxye.js +0 -2
  286. package/dist/cli/doctor-ws4fh4tn.js +0 -3
  287. package/dist/cli/executor-bdrjn634.js +0 -2
  288. package/dist/cli/export-1mxb9g5p.js +0 -2
  289. package/dist/cli/files-g104xghh.js +0 -2
  290. package/dist/cli/gauntlet-07xrjpj7.js +0 -2
  291. package/dist/cli/gauntlet-runner-xvy64436.js +0 -10
  292. package/dist/cli/gauntlet-wave3-jm91yt5w.js +0 -5
  293. package/dist/cli/gauntlet-wave4-r13py7p1.js +0 -14
  294. package/dist/cli/hashline-znvrat11.js +0 -2
  295. package/dist/cli/http-xafw6fsh.js +0 -143
  296. package/dist/cli/index-1sgjm25y.js +0 -2
  297. package/dist/cli/init-g2m0tn4m.js +0 -51
  298. package/dist/cli/install-avaqjjqq.js +0 -2
  299. package/dist/cli/loop-mmpfft01.js +0 -2
  300. package/dist/cli/main-0904f6ps.js +0 -5
  301. package/dist/cli/main-0ab9fc26.js +0 -9
  302. package/dist/cli/main-0jys2ccn.js +0 -3
  303. package/dist/cli/main-0mtcdbs7.js +0 -3
  304. package/dist/cli/main-0z1w2zsg.js +0 -3
  305. package/dist/cli/main-1dchs7xv.js +0 -18
  306. package/dist/cli/main-1ereejm1.js +0 -3
  307. package/dist/cli/main-1k1kw6b5.js +0 -3
  308. package/dist/cli/main-27y4sm2k.js +0 -38
  309. package/dist/cli/main-2wwjex5j.js +0 -58
  310. package/dist/cli/main-2yeveeve.js +0 -6
  311. package/dist/cli/main-2yfck9b5.js +0 -3
  312. package/dist/cli/main-2zmzgkwh.js +0 -3
  313. package/dist/cli/main-351pz3z7.js +0 -7
  314. package/dist/cli/main-3gjqfh7a.js +0 -6
  315. package/dist/cli/main-3nf3kgve.js +0 -3
  316. package/dist/cli/main-3pjrb2hd.js +0 -3
  317. package/dist/cli/main-3rxcvgna.js +0 -19
  318. package/dist/cli/main-4b3jgy66.js +0 -19
  319. package/dist/cli/main-4wndhjdc.js +0 -7
  320. package/dist/cli/main-4xcmvxnk.js +0 -3
  321. package/dist/cli/main-5tbz0wbz.js +0 -4
  322. package/dist/cli/main-5ywnwthm.js +0 -3
  323. package/dist/cli/main-6b62vkz0.js +0 -14
  324. package/dist/cli/main-6dnk69vp.js +0 -3
  325. package/dist/cli/main-6genrmhs.js +0 -136
  326. package/dist/cli/main-73g7eff4.js +0 -15
  327. package/dist/cli/main-7c5thhjd.js +0 -5
  328. package/dist/cli/main-7rn6bqje.js +0 -3
  329. package/dist/cli/main-80haw7qk.js +0 -4
  330. package/dist/cli/main-875s60s2.js +0 -4
  331. package/dist/cli/main-8kjxbpw4.js +0 -8
  332. package/dist/cli/main-90ds1z4e.js +0 -10
  333. package/dist/cli/main-9etavkew.js +0 -3
  334. package/dist/cli/main-a9njrkk1.js +0 -3
  335. package/dist/cli/main-aecrjq2d.js +0 -12
  336. package/dist/cli/main-ck9asesq.js +0 -9
  337. package/dist/cli/main-cta9racd.js +0 -4
  338. package/dist/cli/main-ddv7j2ag.js +0 -3
  339. package/dist/cli/main-dfreez27.js +0 -10
  340. package/dist/cli/main-f7rw7des.js +0 -3
  341. package/dist/cli/main-ggcn7rd7.js +0 -5
  342. package/dist/cli/main-gzkmycnv.js +0 -3
  343. package/dist/cli/main-hq51jg8v.js +0 -18
  344. package/dist/cli/main-jft389w9.js +0 -8
  345. package/dist/cli/main-k1eqkg83.js +0 -3
  346. package/dist/cli/main-k2y8a2aw.js +0 -9
  347. package/dist/cli/main-kcpbykxz.js +0 -4
  348. package/dist/cli/main-kd488vje.js +0 -22
  349. package/dist/cli/main-kh32yvgk.js +0 -5
  350. package/dist/cli/main-kqxnqjnv.js +0 -25
  351. package/dist/cli/main-kyn0xnsg.js +0 -3
  352. package/dist/cli/main-m1kk6fp5.js +0 -21
  353. package/dist/cli/main-mv40pcr2.js +0 -4
  354. package/dist/cli/main-n0t3973w.js +0 -3
  355. package/dist/cli/main-nqveez48.js +0 -4
  356. package/dist/cli/main-pknhvrmj.js +0 -3
  357. package/dist/cli/main-pn1w7a7j.js +0 -3
  358. package/dist/cli/main-prxxs70n.js +0 -4
  359. package/dist/cli/main-q3vsesf9.js +0 -3
  360. package/dist/cli/main-qsevpgsv.js +0 -3
  361. package/dist/cli/main-rdgdw24b.js +0 -25
  362. package/dist/cli/main-rfth4tbm.js +0 -16
  363. package/dist/cli/main-rg0wn0xf.js +0 -5
  364. package/dist/cli/main-sdmxhtv8.js +0 -4
  365. package/dist/cli/main-skbp13js.js +0 -18
  366. package/dist/cli/main-t4xnd213.js +0 -7
  367. package/dist/cli/main-vqak588n.js +0 -4
  368. package/dist/cli/main-w2n1303f.js +0 -9
  369. package/dist/cli/main-wbrdspr2.js +0 -5
  370. package/dist/cli/main-wsrg79c1.js +0 -7
  371. package/dist/cli/main-x4r0fne4.js +0 -5
  372. package/dist/cli/main-xea2f3tn.js +0 -6
  373. package/dist/cli/main-xg704a3c.js +0 -3
  374. package/dist/cli/main-xvnrabfp.js +0 -16
  375. package/dist/cli/main-xy53xf0r.js +0 -4
  376. package/dist/cli/main-y1fqy60y.js +0 -3
  377. package/dist/cli/main-yn8cd281.js +0 -34
  378. package/dist/cli/main-yr0ksc0h.js +0 -4
  379. package/dist/cli/main-z2ex2vyf.js +0 -4
  380. package/dist/cli/main-z3aayzvq.js +0 -3
  381. package/dist/cli/main-zaqh35jg.js +0 -3
  382. package/dist/cli/main-zc2e8e46.js +0 -4
  383. package/dist/cli/main-zzrfw6cf.js +0 -13
  384. package/dist/cli/main.js +0 -280
  385. package/dist/cli/market-cmd-e14kmx9n.js +0 -5
  386. package/dist/cli/mcp-login-wq7ktdek.js +0 -2
  387. package/dist/cli/mcp-market-cmd-9mg3jecy.js +0 -2
  388. package/dist/cli/notify-b7qc0cjb.js +0 -2
  389. package/dist/cli/oauth-z8whcgfx.js +0 -2
  390. package/dist/cli/output-b3ewj3ps.js +0 -16
  391. package/dist/cli/profiles-6mr5he5e.js +0 -2
  392. package/dist/cli/provider-config-g7j42q8x.js +0 -2
  393. package/dist/cli/provider-jr1y8vvm.js +0 -2
  394. package/dist/cli/registry-s8yk86g0.js +0 -2
  395. package/dist/cli/registry-t6p8d4mn.js +0 -2
  396. package/dist/cli/repl-bajwe1mh.js +0 -11
  397. package/dist/cli/resume-rwn9nz7y.js +0 -2
  398. package/dist/cli/run-flags-nah7ndpt.js +0 -2
  399. package/dist/cli/runtime-n7gafzhb.js +0 -2
  400. package/dist/cli/sandbox-config-emdy18x4.js +0 -2
  401. package/dist/cli/server-b0nvs2bn.js +0 -5
  402. package/dist/cli/session-arg-y75wd4kj.js +0 -2
  403. package/dist/cli/session-j62evmjq.js +0 -2
  404. package/dist/cli/sessions-cmd-tsnwz0ns.js +0 -7
  405. package/dist/cli/settings-df10wfez.js +0 -2
  406. package/dist/cli/setup-jzvv72fg.js +0 -2
  407. package/dist/cli/sextant-smoke-37m81ke6.js +0 -5
  408. package/dist/cli/skills-cmd-gjxnxnhx.js +0 -2
  409. package/dist/cli/smoke-p7748apt.js +0 -8
  410. package/dist/cli/start-chat-s4st3mm0.js +0 -12
  411. package/dist/cli/stream-gmeyewds.js +0 -2
  412. package/dist/cli/task-gh0kkp3n.js +0 -2
  413. package/dist/cli/tasks-z1kfpe8e.js +0 -2
  414. package/dist/cli/thinking-0eqkrz6t.js +0 -2
  415. package/dist/cli/todo-5brcrt9m.js +0 -2
  416. package/dist/cli/tools-7pzm0vj9.js +0 -2
  417. package/dist/cli/tools-s635p6s8.js +0 -2
  418. package/dist/cli/trust-cmd-cjav8zgm.js +0 -2
  419. package/dist/cli/update-check-pt31bm2f.js +0 -2
  420. package/dist/cli/update-cmd-tk131s9t.js +0 -2
  421. package/dist/cli/voice-56nabd8d.js +0 -2
  422. package/dist/cli/webfetch-xd8q596m.js +0 -2
  423. package/dist/cli/websearch-5hkf98k1.js +0 -2
  424. package/dist/cli/workflow-cmd-cy3cvzjp.js +0 -4
  425. package/dist/cli/workspace-q10g5z3e.js +0 -2
  426. package/dist/lib/index.js +0 -62
  427. package/dist/lib/models-index.json +0 -1
  428. package/dist/lib/plugins.js +0 -55
  429. package/dist/lib/providers.js +0 -17
  430. package/dist/lib/public-api.js +0 -20
  431. package/dist/lib/sdk.js +0 -360
  432. /package/{dist/cli → src/providers}/models-index.json +0 -0
@@ -0,0 +1,68 @@
1
+ /** Port #44: the attach context the app hands a Renderer that implements `attach` (the sextant surface)
2
+ * — extracted from app.ts for the ADR-002 line cap. The panels read the runtime through these closures
3
+ * ONLY: the active store (swapped by /sessions, /resume and a root /rewind — read live, never captured),
4
+ * the task manager, the current mode's model, the catalog's context window and the /cost math over the
5
+ * active transcript (tui/cost.ts sessionUsage — priced per message at its origin model). */
6
+
7
+ import type { SessionStore } from "../core/session.ts";
8
+ import type { TaskManager } from "../core/tasks.ts";
9
+ import type { ModelCatalog } from "../providers/catalog.ts";
10
+ import type { SextantAttach } from "../sextant/types.ts";
11
+ import type { LiveRuntime } from "../sextant/context-source.ts";
12
+ import { sessionUsage } from "./cost.ts";
13
+
14
+ /** slash names the sextant surface handles itself before onSubmit (keys.ts runLocal — the future
15
+ * src/sextant/local-commands.ts); reserved against custom commands like the built-ins, so a
16
+ * `.rovecode/commands/theme.md` warns and loses instead of silently never being reachable. */
17
+ export const SEXTANT_LOCAL_NAMES: readonly string[] = ["theme", "open", "diff", "focus", "agents", "notices", "context"];
18
+
19
+ export interface AttachSources {
20
+ cwd: string;
21
+ sessionsDir: string;
22
+ /** the ACTIVE store, read live */
23
+ store(): SessionStore;
24
+ tasks: TaskManager;
25
+ /** the current mode's model */
26
+ model(): { provider: string; model: string };
27
+ catalog: ModelCatalog;
28
+ /** the LIVE runtime, for /context. A function rather than a value: /sessions and /resume swap what is
29
+ * underneath, and a captured runtime would report on the session the human left. Returning null is
30
+ * allowed and means the panel says its total is a floor. */
31
+ runtime?: () => LiveRuntime | null;
32
+ /** `--pet <name>` */
33
+ petName?: string;
34
+ }
35
+
36
+ export function buildSextantAttach(a: AttachSources): SextantAttach {
37
+ return {
38
+ cwd: a.cwd,
39
+ sessionsDir: a.sessionsDir,
40
+ store: () => ({ id: a.store().id }),
41
+ tasks: a.tasks,
42
+ model: a.model,
43
+ contextWindow: () => { const m = a.model(); return a.catalog.lookup(m.provider, m.model)?.contextWindow; },
44
+ usage: () => sessionUsage(a.store().messages(), a.catalog, a.model()),
45
+ // /context counts the real transcript against the real catalog, and asks the live runtime for the
46
+ // system prompt and the tool schemas — the two rows a transcript cannot know and a fresh window is
47
+ // mostly made of. The tier travels with the pricing, or a long xAI turn is billed at half rate.
48
+ contextInputs: () => ({
49
+ messages: a.store().messages(),
50
+ lookup: (r) => {
51
+ const info = a.catalog.lookup(r.provider, r.model);
52
+ if (!info) return undefined;
53
+ return {
54
+ ...(info.contextWindow !== undefined ? { contextWindow: info.contextWindow } : {}),
55
+ ...(info.pricing ? { pricing: info.pricing } : {}),
56
+ ...(info.tier ? { tier: info.tier } : {}),
57
+ };
58
+ },
59
+ runtime: a.runtime?.() ?? null,
60
+ }),
61
+ // /market installs an MCP server into a file this session already read; without this the only way to
62
+ // reach it was to quit and start again, which is a poor answer to "I just installed it"
63
+ reloadMcp: async () => { const rt = a.runtime?.(); return (await rt?.reloadMcp?.()) ?? { added: [], removed: [], failed: [], skipped: [] }; },
64
+ // the images staged for the next message (tui/attach.ts /attach, /paste) — read live, the store owns them
65
+ staged: () => a.store().stagedAttachments.map((p) => p.name ?? "image"),
66
+ ...(a.petName !== undefined ? { petName: a.petName } : {}),
67
+ };
68
+ }
@@ -0,0 +1,184 @@
1
+ /** Sextant terminal I/O + surface choice (port #44). The ONE module on the sextant path that touches
2
+ * vendor: ProcessIO drives process.stdin/stdout (raw mode, utf8, resize, and pi-tui's Windows
3
+ * ENABLE_VIRTUAL_TERMINAL_INPUT helper — without it libuv's console reader drops modifier state and
4
+ * Shift+Tab arrives as a plain \t instead of CSI Z; the helper is loaded AFTER setRawMode(true), which
5
+ * resets the console mode, exactly as vendor/pi-tui/src/terminal.ts:186-190 does). MemoryIO is the
6
+ * in-memory double tests and the smoke drive. chooseSurface() is the pure `rovecode` default rule:
7
+ * sextant only on a TTY of at least 100×30 that renders truecolor or 256 colors (the #40 quantizer
8
+ * paints the latter); `--classic` beats everything; ROVECODE_TUI=classic|sextant overrides the heuristics
9
+ * (a non-TTY still never gets sextant, nor does a TTY under the 40×12 Screen floor). */
10
+
11
+ import { createRequire } from "node:module";
12
+ import { join } from "node:path";
13
+ import { getNativeModuleCandidates } from "../../vendor/pi-tui/src/native-module-path.ts";
14
+ import { MIN_COLS, MIN_ROWS } from "../sextant/screen.ts";
15
+ import { SextantRenderer } from "../sextant/sextant-renderer.ts";
16
+ import { SEXTANT_MIN_COLS, SEXTANT_MIN_ROWS, type TerminalIO } from "../sextant/types.ts";
17
+ import type { Renderer } from "./renderer.ts";
18
+
19
+ const cjsRequire = createRequire(import.meta.url);
20
+ /** the vendored pi-tui source dir — getNativeModuleCandidates resolves `native/…` beside it */
21
+ const VENDOR_TUI_URL = new URL("../../vendor/pi-tui/src/terminal.ts", import.meta.url).href;
22
+
23
+ export type Env = Readonly<Record<string, string | undefined>>;
24
+
25
+ /** where pi-tui's prebuilt console-mode helper may live (vendor tree, or beside a compiled binary) */
26
+ export function vtInputCandidates(arch: string = process.arch, execPath: string = process.execPath): string[] {
27
+ return getNativeModuleCandidates(join("native", "win32", "prebuilds", `win32-${arch}`, "win32-console-mode.node"), { moduleUrl: VENDOR_TUI_URL, execPath });
28
+ }
29
+
30
+ /** pi-tui terminal.ts enableWindowsVTInput: flip ENABLE_VIRTUAL_TERMINAL_INPUT on the stdin console;
31
+ * false when the helper is unavailable (Shift+Tab is then indistinguishable from Tab) */
32
+ export function enableWindowsVTInput(arch: string = process.arch): boolean {
33
+ if (arch !== "x64" && arch !== "arm64") return false;
34
+ for (const p of vtInputCandidates(arch)) {
35
+ try {
36
+ const helper = cjsRequire(p) as { enableVirtualTerminalInput?: () => boolean };
37
+ return helper.enableVirtualTerminalInput?.() ?? false;
38
+ } catch { /* try the next packaging location */ }
39
+ }
40
+ return false;
41
+ }
42
+
43
+ /** the slice of process.stdin the surface uses (tests inject a fake) */
44
+ export interface RawStdin {
45
+ isRaw?: boolean;
46
+ setRawMode?(mode: boolean): unknown;
47
+ setEncoding(enc: BufferEncoding): unknown;
48
+ resume(): unknown;
49
+ pause(): unknown;
50
+ on(event: "data", cb: (chunk: string | Buffer) => void): unknown;
51
+ off(event: "data", cb: (chunk: string | Buffer) => void): unknown;
52
+ }
53
+ /** the slice of process.stdout the surface uses */
54
+ export interface RawStdout {
55
+ write(s: string): unknown;
56
+ isTTY?: boolean;
57
+ columns?: number;
58
+ rows?: number;
59
+ on(event: "resize", cb: () => void): unknown;
60
+ off(event: "resize", cb: () => void): unknown;
61
+ }
62
+ export interface ProcessIOOptions {
63
+ /** default process.platform; the VT helper runs on win32 only */
64
+ platform?: string;
65
+ /** the VT-input helper (default enableWindowsVTInput) */
66
+ vtInput?: () => boolean;
67
+ }
68
+
69
+ export class ProcessIO implements TerminalIO {
70
+ private wasRaw = false;
71
+ constructor(
72
+ private readonly stdin: RawStdin = process.stdin,
73
+ private readonly stdout: RawStdout = process.stdout,
74
+ readonly env: Env = process.env,
75
+ private readonly opts: ProcessIOOptions = {},
76
+ ) {}
77
+ write(s: string): void { this.stdout.write(s); }
78
+ onInput(cb: (chunk: string) => void): () => void {
79
+ const h = (d: string | Buffer): void => cb(typeof d === "string" ? d : d.toString("utf8"));
80
+ this.stdin.on("data", h);
81
+ return () => { this.stdin.off("data", h); };
82
+ }
83
+ onResize(cb: (cols: number, rows: number) => void): () => void {
84
+ const h = (): void => { const { cols, rows } = this.size(); cb(cols, rows); };
85
+ this.stdout.on("resize", h);
86
+ return () => { this.stdout.off("resize", h); };
87
+ }
88
+ size(): { cols: number; rows: number } { return { cols: this.stdout.columns || 80, rows: this.stdout.rows || 24 }; }
89
+ /** raw mode + utf8 + flowing, then the Windows VT-input helper (must follow setRawMode, which resets the console flags) */
90
+ enterRaw(): void {
91
+ this.wasRaw = this.stdin.isRaw ?? false;
92
+ this.stdin.setRawMode?.(true);
93
+ this.stdin.setEncoding("utf8");
94
+ this.stdin.resume();
95
+ if ((this.opts.platform ?? process.platform) === "win32") (this.opts.vtInput ?? enableWindowsVTInput)();
96
+ }
97
+ /** restore the previous raw state and pause stdin so buffered bytes never reach the parent shell */
98
+ leaveRaw(): void {
99
+ this.stdin.setRawMode?.(this.wasRaw);
100
+ this.stdin.pause();
101
+ }
102
+ }
103
+
104
+ /** in-memory TerminalIO: records writes, replays fed input, emulates resizes (tests + `smoke-tui --sextant`) */
105
+ export class MemoryIO implements TerminalIO {
106
+ readonly writes: string[] = [];
107
+ raw = false;
108
+ private readonly inputs = new Set<(chunk: string) => void>();
109
+ private readonly resizes = new Set<(cols: number, rows: number) => void>();
110
+ constructor(public cols = 160, public rows = 44, readonly env: Env = {}) {}
111
+ write(s: string): void { this.writes.push(s); }
112
+ onInput(cb: (chunk: string) => void): () => void { this.inputs.add(cb); return () => { this.inputs.delete(cb); }; }
113
+ onResize(cb: (cols: number, rows: number) => void): () => void { this.resizes.add(cb); return () => { this.resizes.delete(cb); }; }
114
+ size(): { cols: number; rows: number } { return { cols: this.cols, rows: this.rows }; }
115
+ enterRaw(): void { this.raw = true; }
116
+ leaveRaw(): void { this.raw = false; }
117
+ /** deliver raw terminal bytes (keys, mouse, paste) */
118
+ feed(chunk: string): void { for (const cb of [...this.inputs]) cb(chunk); }
119
+ resize(cols: number, rows: number): void { this.cols = cols; this.rows = rows; for (const cb of [...this.resizes]) cb(cols, rows); }
120
+ /** everything written so far */
121
+ output(): string { return this.writes.join(""); }
122
+ /** live input + resize subscriptions (0 after stop) */
123
+ get listeners(): number { return this.inputs.size + this.resizes.size; }
124
+ }
125
+
126
+ // ------------------------------------------------------------------ surface choice
127
+
128
+ const TC_PROGRAMS = new Set(["vscode", "iTerm.app", "WezTerm", "ghostty"]);
129
+
130
+ /** the terminal renders 24-bit color: COLORTERM truecolor/24bit, Windows Terminal (WT_SESSION), a
131
+ * known TERM_PROGRAM (vscode, iTerm.app, WezTerm, ghostty) or a kitty/`-direct` TERM */
132
+ export function truecolor(env: Env): boolean {
133
+ const ct = (env.COLORTERM ?? "").toLowerCase();
134
+ if (ct === "truecolor" || ct === "24bit") return true;
135
+ if (env.WT_SESSION) return true;
136
+ if (TC_PROGRAMS.has(env.TERM_PROGRAM ?? "")) return true;
137
+ return /kitty|direct/.test(env.TERM ?? "");
138
+ }
139
+
140
+ export type ColorDepth = "truecolor" | "256" | "none";
141
+
142
+ /** what the terminal renders: truecolor per the heuristics above; else 256 colors when TERM says so
143
+ * (xterm-256color, screen-/tmux-256color) and COLORTERM is silent — the surface then paints through the
144
+ * #40 quantizer (SGR 38;5); else nothing sextant can use */
145
+ export function colorDepth(env: Env): ColorDepth {
146
+ if (truecolor(env)) return "truecolor";
147
+ if (!(env.COLORTERM ?? "").trim() && /256color/.test(env.TERM ?? "")) return "256";
148
+ return "none";
149
+ }
150
+
151
+ export interface StdoutInfo { isTTY?: boolean; columns?: number; rows?: number }
152
+
153
+ /** the heuristic: a TTY of at least SEXTANT_MIN_COLS×SEXTANT_MIN_ROWS with truecolor or 256 colors */
154
+ export function sextantOk(env: Env, stdout: StdoutInfo): boolean {
155
+ return !!stdout.isTTY && (stdout.columns ?? 0) >= SEXTANT_MIN_COLS && (stdout.rows ?? 0) >= SEXTANT_MIN_ROWS && colorDepth(env) !== "none";
156
+ }
157
+
158
+ /** the Screen floor (screen.ts MIN_COLS×MIN_ROWS): below it the cell buffer is larger than the terminal
159
+ * (39-cell rows into a 30-column window), so even a forced sextant yields; an unknown size counts as
160
+ * the 80×24 ProcessIO.size() falls back to */
161
+ export function fitsFloor(stdout: StdoutInfo): boolean {
162
+ return (stdout.columns ?? 80) >= MIN_COLS && (stdout.rows ?? 24) >= MIN_ROWS;
163
+ }
164
+
165
+ export type Surface = "sextant" | "classic";
166
+
167
+ /** `--classic` wins; ROVECODE_TUI=classic forces classic, ROVECODE_TUI=sextant forces sextant on any TTY at or
168
+ * above the 40×12 floor (the 100×30 and color heuristics skipped); otherwise sextantOk(). A non-TTY
169
+ * stdout never gets sextant. */
170
+ export function chooseSurface(cli: { classic: boolean }, env: Env, stdout: StdoutInfo): Surface {
171
+ if (cli.classic) return "classic";
172
+ const force = (env.ROVECODE_TUI ?? "").trim().toLowerCase();
173
+ if (force === "classic") return "classic";
174
+ if (force === "sextant") return stdout.isTTY && fitsFloor(stdout) ? "sextant" : "classic";
175
+ return sextantOk(env, stdout) ? "sextant" : "classic";
176
+ }
177
+
178
+ /** main.ts: the renderer to hand runTui — a SextantRenderer over the process terminal (truecolor SGR, or
179
+ * the 256-color quantizer when that is all the terminal declares), or undefined (runTui then builds the
180
+ * classic PiTuiRenderer exactly as before) */
181
+ export function pickRenderer(cli: { classic: boolean; pet?: string }, env: Env = process.env, stdout: RawStdout = process.stdout): Renderer | undefined {
182
+ if (chooseSurface(cli, env, stdout) !== "sextant") return undefined;
183
+ return new SextantRenderer({ io: new ProcessIO(process.stdin, stdout, env), truecolor: colorDepth(env) === "truecolor", ...(cli.pet !== undefined ? { pet: cli.pet } : {}), ...(env.ROVECODE_THEME ? { theme: env.ROVECODE_THEME } : {}) });
184
+ }
@@ -0,0 +1,110 @@
1
+ /** `rovecode smoke-tui --sextant` (port #44): the sextant surface end-to-end through the real pipeline —
2
+ * agentLoop → SextantRenderer → Screen → an in-memory TerminalIO at 160×44 (no @xterm/headless, so it
3
+ * also runs from an installed tree or the compiled binary). Gated session: a scripted write, then an
4
+ * anchored edit of that file, each raise the approval card and are allowed once the card is on
5
+ * screen; a text turn closes the run; ⌃c quits and the terminal must be restored (leave sequence in
6
+ * the output tail, frame interval cleared). Prints the edit's card frame and the final frame; the CLI
7
+ * wrapper exits 0 iff every stage rendered. */
8
+
9
+ import { mkdtempSync, rmSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { fileTag, lineHash } from "../coding/hashline.ts";
13
+ import { mockStream, textTurn, toolTurn } from "../providers/stream.ts";
14
+ import { SextantRenderer } from "../sextant/sextant-renderer.ts";
15
+ import { runTui } from "./app.ts";
16
+ import { MemoryIO } from "./sextant-io.ts";
17
+
18
+ export interface SextantSmokeResult {
19
+ ok: boolean;
20
+ reasons: string[];
21
+ /** the frame with the edit's approval card on screen */
22
+ cardFrame: string;
23
+ /** the final frame */
24
+ frame: string;
25
+ /** the last 400 bytes written to the terminal (the leave sequence lives here) */
26
+ tail: string;
27
+ }
28
+
29
+ const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));
30
+ /** the two approval cards, in order: the write's, then the edit's */
31
+ const CARDS: readonly RegExp[] = [/needs your permission\s+write\b/, /needs your permission\s+edit\b/];
32
+ /** what quitting must leave in the output: main screen, cursor shown, SGR mouse off, bracketed paste off */
33
+ const LEAVE: readonly string[] = ["\x1b[?1049l", "\x1b[?25h", "\x1b[?1006l", "\x1b[?2004l"];
34
+
35
+ /** drive the surface headlessly and report; never exits the process (runSextantSmoke does) */
36
+ export async function sextantSmoke(opts: { cols?: number; rows?: number; deadlineMs?: number } = {}): Promise<SextantSmokeResult> {
37
+ const cwd = mkdtempSync(join(tmpdir(), "rovecode-sextant-smoke-"));
38
+ const io = new MemoryIO(opts.cols ?? 160, opts.rows ?? 44, { COLORTERM: "truecolor" });
39
+ const renderer = new SextantRenderer({ io, cwd, pet: "rovecode" });
40
+ const probe = join(cwd, "smoke.txt");
41
+ const written = "smoke-ok\n";
42
+ const stream = mockStream({
43
+ turns: [
44
+ toolTurn([{ id: "t1", tool: "write", args: { path: probe, content: written } }]),
45
+ toolTurn([{ id: "t2", tool: "edit", args: { path: probe, edits: [{ tag: fileTag(written), anchorLine: 1, anchorHash: lineHash("smoke-ok"), newLines: ["smoke-edited"] }] } }]),
46
+ textTurn("Smoke OK — the sextant surface rendered a write, an anchored edit and this summary."),
47
+ ],
48
+ });
49
+ const reasons: string[] = [];
50
+ // The whole assertion below is "two approval cards appear", so the permission level is pinned here rather
51
+ // than inherited from the machine: a ~/.rovecode/settings.json with "permission":"auto" allowed the write
52
+ // and the edit without a card and the smoke FAILED with "approval cards seen 0/2". `yolo: false` cannot
53
+ // pin it — app.ts treats false as "no flag" and lets the settings file win; `permission` is the flag rung.
54
+ const app = runTui({ renderer, stream, cwd, permission: "ask", exitOnClose: false, model: "scripted" });
55
+ io.feed("hello rovecode\r");
56
+ const deadline = Date.now() + (opts.deadlineMs ?? 15_000);
57
+ const cardFrames: string[] = [];
58
+ let frame = "", done = false;
59
+ while (Date.now() < deadline) {
60
+ renderer.tick();
61
+ frame = renderer.frameText();
62
+ if (cardFrames.length === CARDS.length && frame.includes("Smoke OK") && !frame.includes("needs your permission")) { done = true; break; }
63
+ const want = CARDS[cardFrames.length];
64
+ if (want !== undefined && want.test(frame) && frame.includes("allow")) { cardFrames.push(frame); io.feed("\r"); } // Enter = allow once
65
+ await sleep(25);
66
+ }
67
+ // the boot reveal paints panels in 90 ms steps after bootAt (frame.ts REVEAL_STEP_MS); a fast host
68
+ // finishes the scripted run before the right column and the pet have appeared — give the reveal its time
69
+ const PANELS = ["─ files ─", "─ code ─", "─ messages ─", "─ plan ─", "─ usage ─", "─ rovecode ─"];
70
+ const shown = (p: string): boolean => frame.includes(p) || (p === "─ code ─" && frame.includes("─ diff ─"));
71
+ const revealBy = Date.now() + 2_000;
72
+ while (done && Date.now() < revealBy && !PANELS.every(shown)) { await sleep(25); renderer.tick(); frame = renderer.frameText(); }
73
+ if (!done) reasons.push(`final frame not reached (approval cards seen ${cardFrames.length}/${CARDS.length})`);
74
+ if (!/~ edit\s+smoke\.txt/.test(frame)) reasons.push("no `~ edit smoke.txt` tool row in the messages panel");
75
+ if (!/\+ write\s+smoke\.txt/.test(frame)) reasons.push("no `+ write smoke.txt` tool row in the messages panel");
76
+ for (const p of PANELS) if (!shown(p)) reasons.push(`panel ${p.trim()} missing at ${io.cols}×${io.rows}`);
77
+ const wasActive = renderer.active;
78
+ io.feed("\x03");
79
+ await app;
80
+ const tail = io.output().slice(-400);
81
+ for (const seq of LEAVE) if (!tail.includes(seq)) reasons.push(`the output tail lacks ${JSON.stringify(seq)} — terminal not restored`);
82
+ if (!wasActive) reasons.push("the frame interval was not running before quit");
83
+ if (renderer.active) reasons.push("the frame interval survived quit");
84
+ // the repo watcher's git children must be GONE before the scratch repo goes (sextant-renderer drain);
85
+ // on Windows a live `git status` holds its cwd and rmSync answers EBUSY — the tui-sextant "flake".
86
+ // The retry is the belt to that brace: handle release on Windows lags the process exit itself.
87
+ await renderer.drain().catch(() => {});
88
+ for (let attempt = 0; ; attempt++) {
89
+ try { rmSync(cwd, { recursive: true, force: true }); break; }
90
+ catch (e) {
91
+ const code = (e as NodeJS.ErrnoException).code;
92
+ if ((code !== "EBUSY" && code !== "EPERM" && code !== "ENOTEMPTY") || attempt >= 20) throw e;
93
+ await sleep(25);
94
+ }
95
+ }
96
+ return { ok: reasons.length === 0, reasons, cardFrame: cardFrames[1] ?? cardFrames[0] ?? "(no approval card rendered)", frame, tail };
97
+ }
98
+
99
+ export async function runSextantSmoke(): Promise<void> {
100
+ const r = await sextantSmoke();
101
+ console.log("── sextant: approval card for the anchored edit (160x44) ──────────────────────");
102
+ console.log(r.cardFrame);
103
+ console.log("── sextant: final frame (160x44) ────────────────────────────────────────────────");
104
+ console.log(r.frame);
105
+ console.log("─────────────────────────────────────────────────────────────────────────────────");
106
+ console.log(r.ok
107
+ ? "smoke-tui --sextant: PASS (files · code · messages · plan · usage · rovecode + two approval cards through the full pipeline; terminal restored on quit)"
108
+ : `smoke-tui --sextant: FAIL — ${r.reasons.join("; ")}`);
109
+ process.exit(r.ok ? 0 : 1);
110
+ }
@@ -0,0 +1,72 @@
1
+ /** `rovecode smoke-tui`: end-to-end render check through the real pipeline —
2
+ * agentLoop → Renderer → pi-tui → ANSI → @xterm/headless terminal emulator.
3
+ * Gated session: a scripted write, then an anchored edit of that file, each raise the
4
+ * approval overlay with its diff card (port #24) and are approved once the card is on
5
+ * screen; a markdown turn closes the run. Prints the edit's diff-card screen and the
6
+ * final 80x24 screen; exits 0 iff every stage rendered. */
7
+
8
+ import { VirtualTerminal } from "../../vendor/pi-tui/test/virtual-terminal.ts";
9
+ import { PiTuiRenderer } from "./pi-renderer.ts";
10
+ import { runTui } from "./app.ts";
11
+ import { mockStream, textTurn, toolTurn } from "../providers/stream.ts";
12
+ import { fileTag, lineHash } from "../coding/hashline.ts";
13
+ import { mkdtempSync, rmSync } from "node:fs";
14
+ import { tmpdir } from "node:os";
15
+ import { join } from "node:path";
16
+
17
+ export async function runTuiSmoke(): Promise<void> {
18
+ const cwd = mkdtempSync(join(tmpdir(), "rovecode-tui-smoke-"));
19
+ const term = new VirtualTerminal(80, 24);
20
+ const renderer = new PiTuiRenderer({ terminal: term, cwd });
21
+ const probe = join(cwd, "smoke.txt");
22
+ const written = "smoke-ok\n";
23
+ const stream = mockStream({
24
+ turns: [
25
+ toolTurn([{ id: "t1", tool: "write", args: { path: probe, content: written } }]),
26
+ // port #24: anchored edit of the file just written → the approval card shows -old/+new
27
+ toolTurn([{ id: "t2", tool: "edit", args: { path: probe, edits: [
28
+ { tag: fileTag(written), anchorLine: 1, anchorHash: lineHash("smoke-ok"), newLines: ["smoke-edited"] },
29
+ ] } }]),
30
+ textTurn("# Smoke OK\n\nrendered **markdown**, a tool card, and the status line."),
31
+ ],
32
+ });
33
+
34
+ // pinned, not inherited: this smoke asserts approval cards, and a ~/.rovecode/settings.json with
35
+ // "permission":"auto" would allow everything silently (`yolo: false` is "no flag" to app.ts, not "ask")
36
+ const app = runTui({ renderer, stream, cwd, permission: "ask", exitOnClose: false, model: "scripted" });
37
+
38
+ // type a prompt into the (focused) editor and submit
39
+ term.sendInput("hello rovecode");
40
+ term.sendInput("\r");
41
+
42
+ // each approval overlay must show its diff card before Enter (= allow once) approves it:
43
+ // the write is an all-adds create diff, the edit a modify diff with the removed line
44
+ const cards = ["+smoke-ok", "-smoke-ok"];
45
+ const cardScreens: string[][] = [];
46
+ const deadline = Date.now() + 10_000;
47
+ let screen: string[] = [];
48
+ let ok = false;
49
+ while (Date.now() < deadline) {
50
+ screen = await term.flushAndGetViewport();
51
+ const text = screen.join("\n");
52
+ if (text.includes("Smoke OK") && text.includes("write") && cardScreens.length === cards.length) { ok = true; break; }
53
+ const want = cards[cardScreens.length];
54
+ if (want !== undefined && text.includes("allow once") && text.includes(want)) {
55
+ cardScreens.push(screen);
56
+ term.sendInput("\r");
57
+ }
58
+ await new Promise((r) => setTimeout(r, 50));
59
+ }
60
+
61
+ term.sendInput("\x03"); // Ctrl+C → clean exit path
62
+ await app;
63
+ rmSync(cwd, { recursive: true, force: true });
64
+
65
+ console.log("── approval overlay with diff card (edit) ──────────────────────────────");
66
+ for (const line of cardScreens[1] ?? ["(diff card never rendered)"]) console.log(line.trimEnd());
67
+ console.log("── emulated 80x24 screen ──────────────────────────────────────────────");
68
+ for (const line of screen) console.log(line.trimEnd());
69
+ console.log("───────────────────────────────────────────────────────────────────────");
70
+ console.log(ok ? "smoke-tui: PASS (markdown + tool card + diff card rendered through full pipeline)" : `smoke-tui: FAIL (diff cards seen: ${cardScreens.length}/${cards.length})`);
71
+ process.exit(ok ? 0 : 1);
72
+ }
@@ -0,0 +1,59 @@
1
+ /** Rovecode TUI theme: raw ANSI styling (no chalk dep) + theme objects for vendored pi-tui
2
+ * components (EditorTheme / MarkdownTheme / SelectListTheme shapes from vendor). */
3
+
4
+ import type { EditorTheme, MarkdownTheme, SelectListTheme } from "../../vendor/pi-tui/src/index.ts";
5
+
6
+ const esc = (open: string, close: string) => (s: string) => `\x1b[${open}m${s}\x1b[${close}m`;
7
+
8
+ export const st = {
9
+ bold: esc("1", "22"),
10
+ dim: esc("2", "22"),
11
+ italic: esc("3", "23"),
12
+ underline: esc("4", "24"),
13
+ inverse: esc("7", "27"),
14
+ strike: esc("9", "29"),
15
+ fg: (n: number) => esc(`38;5;${n}`, "39"),
16
+ bg: (n: number) => esc(`48;5;${n}`, "49"),
17
+ };
18
+
19
+ // palette (256-color): calm blues/teals for chrome, amber for warnings
20
+ export const pal = {
21
+ accent: st.fg(75), // sky blue — headings, borders
22
+ accentDim: st.fg(67),
23
+ ok: st.fg(114), // green
24
+ warn: st.fg(215), // amber
25
+ err: st.fg(203), // red
26
+ muted: st.dim,
27
+ code: st.fg(180),
28
+ link: st.fg(75),
29
+ };
30
+
31
+ export const rovecodeSelectListTheme: SelectListTheme = {
32
+ selectedPrefix: (t) => pal.accent(t),
33
+ selectedText: (t) => st.bold(t),
34
+ description: (t) => st.dim(t),
35
+ scrollInfo: (t) => st.dim(t),
36
+ noMatch: (t) => st.dim(t),
37
+ };
38
+
39
+ export const rovecodeEditorTheme: EditorTheme = {
40
+ borderColor: (t) => pal.accentDim(t),
41
+ selectList: rovecodeSelectListTheme,
42
+ };
43
+
44
+ export const rovecodeMarkdownTheme: MarkdownTheme = {
45
+ heading: (t) => st.bold(pal.accent(t)),
46
+ link: (t) => pal.link(st.underline(t)),
47
+ linkUrl: (t) => st.dim(t),
48
+ code: (t) => pal.code(t),
49
+ codeBlock: (t) => pal.code(t),
50
+ codeBlockBorder: (t) => st.dim(t),
51
+ quote: (t) => st.italic(st.dim(t)),
52
+ quoteBorder: (t) => st.dim(t),
53
+ hr: (t) => st.dim(t),
54
+ listBullet: (t) => pal.accent(t),
55
+ bold: (t) => st.bold(t),
56
+ italic: (t) => st.italic(t),
57
+ strikethrough: (t) => st.strike(t),
58
+ underline: (t) => st.underline(t),
59
+ };
@@ -0,0 +1,7 @@
1
+ /** Tiny status-bar helper split from info-cmd.ts so app.ts can import it statically
2
+ * without pulling in the full info-cmd module at startup. */
3
+ import { loadTodos, todoStatusLabel } from "../tools/todo.ts";
4
+
5
+ export function todoLabel(sessionDir: string): string | undefined {
6
+ return todoStatusLabel(loadTodos(sessionDir).items) || undefined;
7
+ }