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,148 +0,0 @@
1
- /** The bash tool (ports #10, #21, #55). Runs a shell command through the Executor seam — the probed rung
2
- * (direct / wsl / docker) shapes the argv, and the abort path is the rung's tree kill (Windows Job Object
3
- * + taskkill sweep; SIGTERM to the process group on POSIX).
4
- *
5
- * It lived at the end of coding/hashline.ts until #55 gave it a timeout and a background flag. That file
6
- * is the anchored-edit implementation; shell execution had simply drifted into its last fifty lines.
7
- * hashline.ts re-exports `bashTool`, so the ten files importing it from there are untouched.
8
- *
9
- * ONE TOOL, THREE SHAPES, and the reason it is one tool rather than three: policy runs UPSTREAM of
10
- * execute() — the permission rules, execpolicy, the hooks and the human approval all decide before this
11
- * function is entered. A separate `bash_background` tool would be a second door onto the same room, and
12
- * every rule written for `bash` would have to be written again for it.
13
- * plain await it, one automatic retry on a non-zero exit
14
- * timeout_ms the same, with a deadline; a timed-out run KEEPS its partial output and says so
15
- * run_in_background hand it to the job manager and return the id at once (tools/bash-jobs.ts) */
16
-
17
- import type { Tool, ToolContext, ToolOutput } from "../core/types.ts";
18
- import { getExecutor } from "../core/executor.ts";
19
- import { startBashJob, hasJobManager } from "../tools/bash-jobs.ts";
20
-
21
- /** Upper bound on `timeout_ms`, so a model cannot ask for a deadline that never arrives. Ten minutes:
22
- * past that the honest answer is a background job, which is the flag sitting next to it. */
23
- export const MAX_TIMEOUT_MS = 600_000;
24
-
25
- export const bashTool: Tool = {
26
- schema: {
27
- name: "bash",
28
- description:
29
- "Run a shell command in the workspace (cwd locked to the session cwd). One automatic retry on non-zero exit. " +
30
- "Destructive system commands are refused by a best-effort blocklist — this is NOT a sandbox. Output truncated to 10k chars. " +
31
- `\`timeout_ms\` gives up after that long and RETURNS what the command printed (max ${MAX_TIMEOUT_MS}ms); the process tree is killed. ` +
32
- "`run_in_background: true` returns a job id at once instead of waiting — for a dev server, a long build, a slow test run. " +
33
- "Read it later with bash_output (only NEW output per read), see them with bash_list, stop one with bash_kill; a finished job also posts one note here by itself.",
34
- args: {
35
- type: "object",
36
- properties: {
37
- command: { type: "string" },
38
- timeout_ms: { type: "number", description: `give up after this many ms and return the partial output (1..${MAX_TIMEOUT_MS}); a timeout is never retried` },
39
- run_in_background: { type: "boolean", description: "start it as a background job and return its id immediately (bash_output reads it)" },
40
- },
41
- required: ["command"],
42
- },
43
- },
44
- kind: "execute",
45
- sequential: true,
46
- async execute(args, ctx): Promise<ToolOutput> {
47
- const a = args as { command: string; timeout_ms?: unknown; run_in_background?: unknown };
48
- const cmd = String(a.command);
49
- const denied = deniedCommand(cmd);
50
- if (denied) return { ok: false, output: denied };
51
-
52
- // #55: the background flag is read AFTER the blocklist and BEFORE anything runs — a command we
53
- // would refuse in the foreground must not become startable by asking for it in the background.
54
- if (a.run_in_background === true) {
55
- if (!hasJobManager()) {
56
- // Deliberately a refusal, not a silent foreground run: the caller asked NOT to be blocked, and
57
- // quietly blocking it is the wrong answer to a request we cannot honour.
58
- return { ok: false, output: "background jobs are not available on this surface — drop run_in_background to run it in the foreground" };
59
- }
60
- const started = startBashJob(cmd, ctx.cwd);
61
- if (!started.ok) return { ok: false, output: started.reason };
62
- return {
63
- ok: true,
64
- output: `started background job ${started.info.id}: ${cmd}\nIt runs while you continue — do NOT poll in a loop. \`bash_output ${started.info.id}\` reads what is new; a note lands here when it finishes.`,
65
- data: started.info,
66
- };
67
- }
68
-
69
- const timeout = timeoutFrom(a.timeout_ms);
70
- if (timeout instanceof Error) return { ok: false, output: timeout.message };
71
-
72
- // port #10: shell execution goes through the Executor seam (direct/wsl/docker
73
- // rungs, probed not assumed). Direct rung is byte-compatible with the old
74
- // inline runOnce; a missing bash now returns exit=-1 instead of throwing.
75
- const run = async (): Promise<{ code: number; text: string; timedOut: boolean }> => {
76
- if (timeout === undefined) {
77
- const r = await getExecutor().run(cmd, ctx.cwd, ctx.signal);
78
- return { code: r.code, text: r.text, timedOut: false };
79
- }
80
- // The deadline aborts a controller the executor is watching, which IS the tree-kill path — the
81
- // same one Esc uses. A timeout that returned while the process kept running would be a lie in
82
- // the other direction, and on Windows it would hold the pipe open too.
83
- const ac = new AbortController();
84
- const onOuter = (): void => ac.abort();
85
- ctx.signal.addEventListener("abort", onOuter, { once: true });
86
- let timer: ReturnType<typeof setTimeout> | undefined;
87
- let fired = false;
88
- try {
89
- timer = setTimeout(() => { fired = true; ac.abort(); }, timeout);
90
- const r = await getExecutor().run(cmd, ctx.cwd, ac.signal);
91
- return { code: r.code, text: r.text, timedOut: fired };
92
- } finally {
93
- clearTimeout(timer);
94
- ctx.signal.removeEventListener("abort", onOuter);
95
- }
96
- };
97
-
98
- let r = await run();
99
- // single self-contained retry — but NEVER after an abort (port #21): the kill
100
- // makes the exit non-zero, and a blind retry would respawn the cancelled
101
- // command as a detached subprocess that outlives the run. Nor after a TIMEOUT:
102
- // a command that needed more time than it was given needs more time, not twice.
103
- if (r.code !== 0 && !r.timedOut && !ctx.signal.aborted) r = await run();
104
- if (r.timedOut) {
105
- // The partial output is the whole point. A build that timed out has usually already printed the
106
- // reason it was slow, and throwing that away leaves the model with only "it took too long".
107
- return { ok: false, output: `timed out after ${timeout}ms (process tree killed; what it printed follows)\n${r.text}` };
108
- }
109
- return { ok: r.code === 0, output: `exit=${r.code}\n${r.text}` };
110
- },
111
- };
112
-
113
- /** undefined = no deadline; an Error is a refusal to be RETURNED as tool output, never thrown. */
114
- function timeoutFrom(raw: unknown): number | undefined | Error {
115
- if (raw === undefined || raw === null) return undefined;
116
- const n = typeof raw === "number" ? raw : Number(raw);
117
- if (!Number.isFinite(n) || n <= 0) return new Error(`timeout_ms must be a positive number of milliseconds (got ${JSON.stringify(raw)})`);
118
- if (n > MAX_TIMEOUT_MS) return new Error(`timeout_ms ${n} is longer than the ${MAX_TIMEOUT_MS}ms cap — use run_in_background for work that takes longer`);
119
- return Math.floor(n);
120
- }
121
-
122
- // ---------- Bash safety (best-effort blocklist, NOT a sandbox) ----------
123
-
124
- /** Footgun guard on the raw command string. A determined agent bypasses it;
125
- * real isolation belongs at the process/OS layer. */
126
- const denyPatterns: RegExp[] = [
127
- /rm\s+(-[a-z]*\s+)*\/(\s|$)/, // rm -rf /
128
- /rm\s+(-[a-z]*\s+)*\/\*/, // rm -rf /*
129
- /rm\s+(-[a-z]*\s+)*(--no-preserve-root\s+)?\*(\s|$)/, // rm -rf *
130
- /:\(\)\s*\{/, // fork bomb body :(){ ...
131
- /\bmkfs(\.\w+)?\b/,
132
- /\b(shutdown|reboot|poweroff|halt)\b/,
133
- /(^|[;&|\s])(sudo\s+)?format\s+(\/|[c-z]:)/i, // windows format
134
- /(^|[;&|\s])(sudo\s+)?del\s+\/[fqs]/i, // windows del /f
135
- /(^|[;&|\s])(sudo\s+)?rd\s+\/[sq]/i,
136
- /(^|[;&|\s])(sudo\s+)?remove-item\s+-(rec|r|f|force)/i,
137
- /\bdd\s+[^|]*of=\/dev\/(sd|nvme|hd|disk)/, // raw disk overwrite
138
- />\s*\/dev\/(sd|nvme|hd|disk)/,
139
- /\bsudo\b.*\b(rm|mkfs|dd|shutdown|reboot|halt|format)\b/, // sudo + destructive core
140
- ];
141
-
142
- export function deniedCommand(cmd: string): string | null {
143
- const hit = denyPatterns.find((re) => re.test(cmd));
144
- return hit
145
- ? `command refused by safety blocklist (matched ${hit.source}): this tool is not a sandbox; rephrase without destructive system commands`
146
- : null;
147
- }
148
-
@@ -1,327 +0,0 @@
1
- /** Shadow-git checkpoints (PORT #11, cline port, Apache-2.0 — see THIRD_PARTY_NOTICES).
2
- *
3
- * KNOWN COST, and the design that would remove it — measured 2026-09-06, kept here so the next reader
4
- * starts from the answer rather than the question. The shadow git-dir is per SESSION, so every new
5
- * session pays `git init` + a full `add` of the workspace inside its FIRST write or bash call: 3.45–3.56 s
6
- * in this repository (init+config 160 ms, first add 1.4 s, first commit 1.5 s writing 565 loose objects
7
- * on Windows). Later calls in that session are 210–335 ms, of which ~200 ms is git. It does not amortise
8
- * across sessions because nothing is shared.
9
- *
10
- * The cut is one shadow repo per REPOSITORY with a ref per session — a new session's first snapshot
11
- * becomes an incremental add, ~80 ms measured. cline keeps one shadow repo per TASK because a task is its
12
- * unit of restore; ours is the repository, so sessions in one workspace can share an object store and
13
- * differ only by ref. It is NOT a configuration change: a shared git-dir means a shared index and a
14
- * shared HEAD, and today's `add . && commit` / `reset --hard && clean -fd` would collide on index.lock
15
- * (the second writer silently gets no checkpoint) and move HEAD under another session. It has to be
16
- * re-done on plumbing: GIT_INDEX_FILE=<shadow>/<session>.index per session, then add → write-tree →
17
- * commit-tree -p <that session's last> → update-ref refs/rovecode/<session> (atomic per ref, and the
18
- * object store is atomic per object); restore becomes read-tree --reset -u then clean -fd against that
19
- * same index, with no HEAD involved. A hash stays a hash, so restoring one session's checkpoint from
20
- * another finally works — the case per-session storage never allowed, and the one that needs its own
21
- * test, alongside a two-writers concurrency test. Sessions whose old per-session repo exists keep using
22
- * it; nothing needs migrating.
23
- *
24
- * A SECOND git repository whose git-dir lives under .rovecode/checkpoints/<session> and whose
25
- * work-tree is the WORKSPACE, so the user's own .git is never written. This is cline's
26
- * shadow-git design: the @8eb5f3d snapshot's docs still describe it (docs/core-workflows/
27
- * checkpoints.mdx:17 "shadow Git repository separate from your project's actual Git
28
- * history … Your main Git repository stays untouched"), but its v4 CODE moved to in-repo
29
- * `git stash create` + refs/cline/* (sdk/packages/core/src/hooks/checkpoint-hooks.ts:172,
30
- * sdk/packages/core/src/session/checkpoint-restore.ts:444-477), which requires a git
31
- * workspace and rewrites the user's HEAD — incompatible with this bar. The mechanics here
32
- * therefore port cline's last shadow-git implementation, v3.89.2
33
- * apps/vscode/src/integrations/checkpoints/:
34
- * - `git init` in the checkpoints dir, then core.worktree=<workspace>, commit.gpgSign
35
- * off, own identity (CheckpointGitOperations.ts:88-94); git-dir = <dir>/.git and
36
- * worktree-mismatch reuse check (CheckpointUtils.ts:20-23, GitOperations.ts:70-73)
37
- * - excludes written to <git-dir>/info/exclude, list headed by ".git/"
38
- * (CheckpointExclusions.ts:42-46 + 297-301)
39
- * - snapshot = `add . --ignore-errors` (CheckpointGitOperations.ts:213) +
40
- * `commit --allow-empty --no-verify` (CheckpointTracker.ts:251-253)
41
- * - restore = `reset --hard <hash>` (CheckpointTracker.ts:364) + `clean -fd` so files
42
- * created after the checkpoint are rewound away while ignored paths (node_modules,
43
- * .rovecode, build output) survive — the reset+clean pair of the snapshot's own restore
44
- * (checkpoint-restore.ts:458-470)
45
- * Deviations from v3.89.2: the shadow repo lives IN-WORKSPACE under .rovecode (bar) so
46
- * ".rovecode/" is excluded from itself; the nested-.git rename dance
47
- * (CheckpointGitOperations.ts:148-166, 207 ".git_disabled") is NOT ported — renaming the
48
- * user's nested .git would violate "user .git never touched", so nested repos become
49
- * inert gitlink entries instead (their contents are not checkpointed, never modified);
50
- * core.autocrlf=false is set so restores are byte-exact on Windows.
51
- *
52
- * Conversation restore returns the session entryId to branch to — the CALLER feeds it to
53
- * SessionStore.branch() (port #2 leaf machinery); this module never imports session.ts. */
54
-
55
- import { execFile } from "node:child_process";
56
- import { appendFileSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
57
- import { join, resolve } from "node:path";
58
-
59
- export interface Checkpoint {
60
- hash: string; // shadow commit hash
61
- label: string; // e.g. the mutating tool's name
62
- entryId?: string; // session entry to branch to on conversation restore
63
- createdAt: number;
64
- }
65
-
66
- export type RestoreMode = "files" | "conversation" | "both";
67
-
68
- export type RestoreResult =
69
- | { ok: true; mode: RestoreMode; checkpoint: Checkpoint; entryId?: string }
70
- | { ok: false; error: string };
71
-
72
- /** ToolKind values whose calls mutate the workspace → snapshot after each (bar:
73
- * "snapshot commit after every mutating tool call"). memory writes land under the
74
- * excluded .rovecode/; spawned children's own write/execute calls hit the same hook. */
75
- export const MUTATING_KINDS: ReadonlySet<string> = new Set(["write", "execute"]);
76
-
77
- /** Conversation-restore anchor for a snapshot: the LAST role:"user" message on the
78
- * active path. At snapshot time the tail entry is the assistant message that ISSUED
79
- * the in-flight tool call (the loop appends it pre-dispatch), so anchoring the tail
80
- * branches to a history ending in tool_calls with no tool replies → provider 400.
81
- * cline anchors the user run message instead (checkpoint-restore.ts:217-250).
82
- * Wiring contract (like MUTATING_KINDS): runtime.ts withCheckpoint computes
83
- * `anchorEntryId(activeStore.messages())`. */
84
- export function anchorEntryId(messages: ReadonlyArray<{ id: string; role: string }>): string | undefined {
85
- return messages.findLast((m) => m.role === "user")?.id;
86
- }
87
-
88
- export interface CheckpointsInit {
89
- workspace: string;
90
- sessionId: string;
91
- /** override the shadow root (default <workspace>/.rovecode/checkpoints) — tests/global mode */
92
- shadowRoot?: string;
93
- }
94
-
95
- /** cline's default exclusions (CheckpointExclusions.ts:42-70), structural entries first, then the media /
96
- * archive / binary categories. The first port kept only the structural entries, and the snapshot's
97
- * `git add .` then hashed every file the WORKSPACE tracks: in a repo carrying 165 MB of tracked video
98
- * (site/media-src) the first mutating tool call of every session took 26–35 s and left a 232 MB shadow
99
- * repo under .rovecode/checkpoints/<session> (measured 2026-09-06, scripts/probe-turn.ts). A checkpoint
100
- * exists to restore what the agent changed, and the agent does not edit videos, screenshots, archives or
101
- * compiled binaries — those are excluded by extension, like cline does. With videos alone excluded the
102
- * same repo still took 10.7 s and 79 MB: 82 MB of site/screenshots/*.png. Text of any size is still
103
- * snapshotted, and so is SVG (text a designer or the agent writes). */
104
- const EXCLUDES = [
105
- ".git/",
106
- ".rovecode/", // the shadow repo itself lives here (deviation: in-workspace)
107
- "node_modules/",
108
- "dist/",
109
- "build/",
110
- "out/",
111
- ".next/",
112
- "__pycache__/",
113
- ".venv/",
114
- "venv/",
115
- ".DS_Store",
116
- // media (cline getMediaFilePatterns): video, audio, raster images — not SVG
117
- "*.mp4", "*.m4v", "*.mov", "*.avi", "*.mkv", "*.webm", "*.wmv", "*.flv", "*.mpg", "*.mpeg",
118
- "*.mp3", "*.m4a", "*.wav", "*.flac", "*.ogg", "*.aac", "*.wma",
119
- "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp", "*.ico", "*.webp", "*.tif", "*.tiff", "*.heic", "*.avif", "*.psd",
120
- // archives and disk images (getLargeDataFilePatterns)
121
- "*.zip", "*.tar", "*.gz", "*.tgz", "*.bz2", "*.xz", "*.7z", "*.rar", "*.iso", "*.dmg",
122
- // compiled binaries and native libraries
123
- "*.exe", "*.dll", "*.so", "*.dylib", "*.node", "*.wasm", "*.o", "*.a", "*.class", "*.jar", "*.pyc",
124
- // databases and caches (getDatabaseFilePatterns / getCacheFilePatterns)
125
- "*.sqlite", "*.sqlite3", "*.db", "*.mdb", "*.log",
126
- ];
127
-
128
- /** `verb` names the failing subcommand in errors; the default suits bare invocations
129
- * like ["init"], but --git-dir'd calls must pass it (the first non-dash arg there is
130
- * the git-dir PATH — blaming a path instead of the verb misled /restore users). */
131
- function runGit(args: string[], cwd: string, verb = args.find((a) => !a.startsWith("-")) ?? "", extra: Record<string, string> = {}): Promise<string> {
132
- // Explicit env hygiene: a caller's GIT_* vars must not redirect shadow commands
133
- // at the USER repo (cline relies on simple-git cwd instead — GitOperations.ts:88).
134
- // `extra` is OUR explicit override (position()'s scratch GIT_INDEX_FILE), applied after the scrub.
135
- const env = { ...process.env };
136
- for (const k of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE", "GIT_OBJECT_DIRECTORY", "GIT_COMMON_DIR"]) delete env[k];
137
- Object.assign(env, extra);
138
- return new Promise((res, rej) => {
139
- execFile("git", args, { cwd, env, windowsHide: true, maxBuffer: 16 * 1024 * 1024 }, (err, stdout, stderr) => {
140
- if (err) rej(new Error(`git ${verb} failed: ${stderr.trim() || err.message}`));
141
- else res(stdout.trim());
142
- });
143
- });
144
- }
145
-
146
- /** Canonical form for workspace-identity compares: realpath fixes case/8.3 aliases of
147
- * EXISTING paths (C:\foo vs c:\foo reopened the shadow repo as "another workspace"
148
- * and silently disabled checkpoints); the case-fold below covers paths realpath
149
- * cannot resolve, on the case-insensitive platform only. */
150
- function canonPath(p: string): string {
151
- let r = p;
152
- try { r = (realpathSync.native ?? realpathSync)(p); } catch { /* nonexistent: compare as given */ }
153
- return process.platform === "win32" ? r.toLowerCase() : r;
154
- }
155
-
156
- /** The ONE spelling of a session's shadow directory, `<workspace>/.rovecode/checkpoints/<sanitised id>`: the id is
157
- * folded charwise to [A-Za-z0-9._-] and dot-only ids ("."/"..", which survive the charwise filter but escape or
158
- * collapse the shadow root under join()) and "" become underscores. init() creates it; `rovecode sessions delete`
159
- * (core/session-ops.ts) removes it — both through here, so a delete never builds the path a second way. */
160
- export function checkpointShadowDir(workspace: string, sessionId: string, shadowRoot?: string): string {
161
- const cleaned = sessionId.replace(/[^A-Za-z0-9._-]/g, "_");
162
- const session = /^\.*$/.test(cleaned) ? cleaned.replace(/\./g, "_") || "_" : cleaned;
163
- return join(shadowRoot ?? join(workspace, ".rovecode", "checkpoints"), session);
164
- }
165
-
166
- export class Checkpoints {
167
- /** history, oldest first (sidecar-backed: survives process restarts) */
168
- private readonly log: Checkpoint[] = [];
169
-
170
- private constructor(
171
- readonly workspace: string,
172
- /** shadow repo GIT DIR: <workspace>/.rovecode/checkpoints/<session>/.git */
173
- readonly gitDir: string,
174
- private readonly sidecar: string,
175
- ) {}
176
-
177
- /** Every shadow command names its git-dir and work-tree explicitly, so no cwd or
178
- * environment state can ever point one at the user's repo. */
179
- private git(...args: string[]): Promise<string> {
180
- return runGit(["--git-dir", this.gitDir, "--work-tree", this.workspace, ...args], this.workspace, args[0]);
181
- }
182
-
183
- /** Create or reopen the shadow repo for a session. Works whether or not the workspace
184
- * is a git repo — the shadow git-dir is entirely separate (non-git workspaces bar). */
185
- static async init(opts: CheckpointsInit): Promise<Checkpoints> {
186
- const workspace = resolve(opts.workspace);
187
- const shadowDir = checkpointShadowDir(workspace, opts.sessionId, opts.shadowRoot);
188
- const gitDir = join(shadowDir, ".git"); // cline layout: <checkpointsDir>/.git (CheckpointUtils.ts:20-23)
189
- mkdirSync(shadowDir, { recursive: true });
190
- const cp = new Checkpoints(workspace, gitDir, join(shadowDir, "checkpoints.jsonl"));
191
-
192
- if (!existsSync(join(gitDir, "HEAD"))) {
193
- // plain `git init` in the shadow dir, exactly GitOperations.ts:88
194
- await runGit(["init"], shadowDir);
195
- // GitOperations.ts:91-94 config block (identity ours; autocrlf is an rovecode addition)
196
- for (const [k, v] of [
197
- ["core.worktree", workspace],
198
- ["commit.gpgSign", "false"],
199
- ["core.autocrlf", "false"],
200
- ["user.name", "Rovecode Checkpoint"],
201
- ["user.email", "checkpoint@rovecode.local"],
202
- ] as const) await cp.git("config", k, v);
203
- } else {
204
- // reuse check: refuse a shadow repo whose recorded worktree is another path
205
- // (GitOperations.ts:70-73 "Checkpoints can only be used in the original workspace").
206
- // Compared canonically — a case-variant reopen (C:\foo vs c:\foo) is the SAME dir.
207
- const wt = await cp.git("config", "core.worktree").catch(() => "");
208
- if (canonPath(resolve(wt)) !== canonPath(workspace)) throw new Error(`checkpoints: shadow repo belongs to ${wt}, not ${workspace}`);
209
- }
210
- // (re)write excludes into the shadow git-dir every init (CheckpointExclusions.ts:297-301)
211
- mkdirSync(join(gitDir, "info"), { recursive: true });
212
- writeFileSync(join(gitDir, "info", "exclude"), EXCLUDES.join("\n") + "\n");
213
- cp.loadSidecar();
214
- return cp;
215
- }
216
-
217
- private loadSidecar(): void {
218
- if (!existsSync(this.sidecar)) return;
219
- for (const line of readFileSync(this.sidecar, "utf8").split("\n")) {
220
- if (!line.trim()) continue;
221
- try {
222
- const c = JSON.parse(line) as Checkpoint;
223
- if (typeof c.hash === "string" && typeof c.label === "string") this.log.push(c);
224
- } catch { /* corrupt sidecar line: skip, never throw (session.ts reload pattern) */ }
225
- }
226
- }
227
-
228
- /** Snapshot the whole workspace: stage-all + allow-empty commit
229
- * (GitOperations.ts:213 + CheckpointTracker.ts:251-253). Call after every mutating
230
- * tool call; `entryId` is the session entry a conversation restore should branch to. */
231
- async snapshot(label: string, entryId?: string): Promise<Checkpoint> {
232
- await this.git("add", ".", "--ignore-errors");
233
- await this.git("commit", "--allow-empty", "--no-verify", "-m", `rovecode-checkpoint: ${label}`);
234
- const hash = await this.git("rev-parse", "HEAD");
235
- const c: Checkpoint = { hash, label, createdAt: Date.now(), ...(entryId !== undefined ? { entryId } : {}) };
236
- appendFileSync(this.sidecar, JSON.stringify(c) + "\n");
237
- this.log.push(c);
238
- return c;
239
- }
240
-
241
- /** History oldest→newest. */
242
- list(): Checkpoint[] { return [...this.log]; }
243
-
244
- /** Where the workspace sits in the snapshot history (port #65 /undo). `at` — the NEWEST checkpoint whose tree
245
- * equals the work tree as it is now (staged into a SCRATCH index under the shadow git-dir, so the real index —
246
- * and changedSince's untracked list — stay untouched); null when no snapshot matches (the workspace drifted).
247
- * `previous` — the nearest ANCESTOR of `at` whose content differs (the pre-edit state; re-snapshots of an
248
- * unchanged tree are stepped over; after a restore the shadow history forks, so the parent chain is followed,
249
- * not the list order); null when nothing older differs. The git-call count does not grow with the history
250
- * (rev-parse takes the hashes in batches); any git failure degrades to `{ at: null, previous: null }`, which a
251
- * caller treats as a drifted workspace. */
252
- async position(): Promise<{ at: Checkpoint | null; previous: Checkpoint | null }> {
253
- const none = { at: null, previous: null };
254
- if (this.log.length === 0) return none;
255
- const scratch = join(this.gitDir, "rovecode-undo-index");
256
- const base = ["--git-dir", this.gitDir, "--work-tree", this.workspace];
257
- try {
258
- rmSync(scratch, { force: true });
259
- await runGit([...base, "add", ".", "--ignore-errors"], this.workspace, "add", { GIT_INDEX_FILE: scratch });
260
- const tree = await runGit([...base, "write-tree"], this.workspace, "write-tree", { GIT_INDEX_FILE: scratch });
261
- const trees: string[] = [];
262
- for (let i = 0; i < this.log.length; i += 200) {
263
- trees.push(...(await this.git("rev-parse", ...this.log.slice(i, i + 200).map((c) => `${c.hash}^{tree}`))).split("\n").map((s) => s.trim()));
264
- }
265
- const atIdx = trees.lastIndexOf(tree);
266
- if (atIdx < 0) return none;
267
- const at = this.log[atIdx]!;
268
- for (const h of (await this.git("rev-list", at.hash)).split("\n").slice(1)) {
269
- const i = this.log.findLastIndex((c) => c.hash === h.trim());
270
- if (i >= 0 && trees[i] !== tree) return { at, previous: this.log[i]! };
271
- }
272
- return { at, previous: null };
273
- } catch { return none; }
274
- finally { rmSync(scratch, { force: true }); }
275
- }
276
-
277
- /** What a "files" restore of `hash` would touch (port #65 /undo's confirmation card): tracked paths whose
278
- * work-tree content differs from the snapshot (`diff --name-status <hash>`: M rewritten, D recreated) plus
279
- * the untracked, non-excluded paths `clean -fd` would remove (`?`). Read-only; null when git fails, so a
280
- * caller falls back to a generic card instead of a wrong list. */
281
- async changedSince(hash: string): Promise<{ path: string; status: string }[] | null> {
282
- try {
283
- const out: { path: string; status: string }[] = [];
284
- for (const line of (await this.git("diff", "--name-status", hash)).split("\n")) {
285
- const m = /^([A-Z])\S*\t(.+)$/.exec(line.trim());
286
- if (m) out.push({ status: m[1]!, path: m[2]!.split("\t").at(-1)! });
287
- }
288
- for (const p of (await this.git("ls-files", "--others", "--exclude-standard")).split("\n")) if (p.trim()) out.push({ status: "?", path: p.trim() });
289
- return out;
290
- } catch { return null; }
291
- }
292
-
293
- /** Restore a checkpoint by full hash or unique prefix.
294
- * - "files": worktree → checkpoint state (reset --hard + clean -fd; ignored paths survive)
295
- * - "conversation": NO file changes; returns the entryId for SessionStore.branch()
296
- * - "both": files restored AND entryId returned
297
- * Never throws — bad refs/modes AND shadow-git failures (a stale index.lock used to
298
- * escape here and kill the TUI on unhandled rejection) come back structured, the
299
- * error naming the failing verb (reset/clean). */
300
- async restore(ref: string, mode: RestoreMode): Promise<RestoreResult> {
301
- const hits = this.log.filter((c) => c.hash === ref || c.hash.startsWith(ref));
302
- // duplicate hashes (identical content re-snapshotted) are ONE candidate — the
303
- // LATEST entry wins so its (newer) conversation anchor is the one restored
304
- const target = hits.at(-1);
305
- if (!target || ref.length < 4) return { ok: false, error: `no checkpoint matches ${ref}` };
306
- if (new Set(hits.map((h) => h.hash)).size > 1) return { ok: false, error: `ambiguous checkpoint prefix ${ref}` };
307
- // conversation restore needs a recorded entryId — reject BEFORE touching any file,
308
- // so "both" can never half-apply
309
- if (mode !== "files" && target.entryId === undefined) {
310
- return { ok: false, error: `checkpoint ${target.hash.slice(0, 8)} has no session entryId` };
311
- }
312
- if (mode !== "conversation") {
313
- try {
314
- await this.git("reset", "--hard", target.hash); // CheckpointTracker.ts:364
315
- // remove files created after the checkpoint; single -f spares nested git repos,
316
- // no -x spares ignored/excluded paths (checkpoint-restore.ts:458-470)
317
- await this.git("clean", "-fd");
318
- } catch (e) {
319
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
320
- }
321
- }
322
- return {
323
- ok: true, mode, checkpoint: target,
324
- ...(mode !== "files" && target.entryId !== undefined ? { entryId: target.entryId } : {}),
325
- };
326
- }
327
- }
@@ -1,138 +0,0 @@
1
- /** Port #24 — bounded unified-diff preview for edit/write approvals.
2
- *
3
- * The post-change content is computed IN MEMORY (edit: hashline's pure apply over the
4
- * current file; write: the proposed content), diffed against the file with jsdiff
5
- * (`diff`, BSD-3-Clause — see THIRD_PARTY_NOTICES.md), and rendered git-style, clipped
6
- * to maxLines with an exact "… +N more lines" marker. Never throws: stale anchors,
7
- * binary content, malformed args and I/O errors all come back as kind "unavailable"
8
- * carrying the reason, so the approval overlay degrades instead of blocking the ask.
9
- * Shape follows gemini-cli's confirmation diffs — structuredPatch with a small context
10
- * (packages/core/src/tools/diffOptions.ts:10-18) and CR-tolerant line comparison
11
- * (diff-utils.ts:22 splits on /\r?\n/; here jsdiff's stripTrailingCr). No code copied. */
12
-
13
- import { existsSync, readFileSync } from "node:fs";
14
- import { relative } from "node:path";
15
- import { toolPath } from "../core/workspace.ts";
16
- import { structuredPatch } from "diff";
17
- import { applyEditsToContent, type EditFailure, type EditOp } from "./hashline.ts";
18
-
19
- export type DiffKind = "modify" | "create" | "unchanged" | "unavailable";
20
- export interface DiffPreview { text: string; truncated: boolean; kind: DiffKind }
21
- export interface DiffPreviewOptions { maxLines?: number; context?: number }
22
-
23
- /** default cap on rendered lines (headers + hunks); the TUI clips further to its rows */
24
- export const DIFF_MAX_LINES = 40;
25
- const DIFF_CONTEXT = 3;
26
- /** git's buffer_is_binary heuristic: a NUL byte within the first 8000 bytes */
27
- const BINARY_SNIFF = 8000;
28
- /** Myers budget: a rewrite beyond this many edited lines is reported, not diffed (bounded CPU);
29
- * the wall-clock cap is a safety net for huge files, the edit-length cap is the deterministic one */
30
- const MAX_EDIT_LENGTH = 2000;
31
- const MAX_DIFF_MS = 1000;
32
- const MAX_CHARS = 4_000_000;
33
-
34
- /** `… +N more lines` — the exact marker appended when the output is clipped. */
35
- export function moreMarker(n: number): string { return `… +${n} more line${n === 1 ? "" : "s"}`; }
36
-
37
- /** Keep the first `max` lines; the marker accounts for every hidden line. */
38
- export function clipLines(lines: string[], max: number): string[] {
39
- return lines.length > max ? [...lines.slice(0, max), moreMarker(lines.length - max)] : lines;
40
- }
41
-
42
- /** the ladder's own spelling (core/workspace.ts toolPath): the preview names the file the tool will open */
43
- function resolvePath(cwd: string, p: string): string { return toolPath(cwd, p); }
44
-
45
- function hasNul(buf: Uint8Array): boolean {
46
- const n = Math.min(buf.length, BINARY_SNIFF);
47
- for (let i = 0; i < n; i++) if (buf[i] === 0) return true;
48
- return false;
49
- }
50
-
51
- const unavailable = (reason: string): DiffPreview => ({ text: `diff unavailable: ${reason}`, truncated: false, kind: "unavailable" });
52
-
53
- function failureReason(f: EditFailure): string {
54
- switch (f.kind) {
55
- case "tag-mismatch": return `stale read — file TAG is now ${f.actual}, the edit expects ${f.expected}`;
56
- case "hash-mismatch": return `stale anchor — line ${f.line} hash is ${f.actual}, the edit expects ${f.expected}; ${f.nearest}`;
57
- case "out-of-range": return `line ${f.line} out of range (file has ${f.lineCount} lines)`;
58
- }
59
- }
60
-
61
- /** The edit tool's args, validated (a malformed call yields "unavailable", never a throw). */
62
- function editOps(args: Record<string, unknown>, path: string): EditOp[] | null {
63
- if (!Array.isArray(args.edits)) return null;
64
- const ops: EditOp[] = [];
65
- for (const e of args.edits as unknown[]) {
66
- const o = (typeof e === "object" && e !== null ? e : {}) as Record<string, unknown>;
67
- if (typeof o.tag !== "string" || typeof o.anchorHash !== "string" || typeof o.anchorLine !== "number") return null;
68
- if (!Array.isArray(o.newLines) || !o.newLines.every((l) => typeof l === "string")) return null;
69
- ops.push({ path, tag: o.tag, anchorLine: o.anchorLine, anchorHash: o.anchorHash, newLines: o.newLines as string[] });
70
- }
71
- return ops;
72
- }
73
-
74
- /** Content → signed display lines: CRLF-tolerant, trailing newline dropped, missing EOF newline noted. */
75
- function signedLines(content: string, sign: "+" | "-"): string[] {
76
- const lines = content.replace(/\r\n/g, "\n").split("\n");
77
- const eofNewline = lines[lines.length - 1] === "";
78
- if (eofNewline) lines.pop();
79
- const out = lines.map((l) => sign + l);
80
- if (!eofNewline && out.length > 0) out.push("\");
81
- return out;
82
- }
83
-
84
- function renderUnified(rel: string, before: string | null, after: string, opts: DiffPreviewOptions): DiffPreview {
85
- const maxLines = opts.maxLines ?? DIFF_MAX_LINES;
86
- if (before === after) return { text: "", truncated: false, kind: "unchanged" };
87
- const body: string[] = [];
88
- let kind: DiffKind = "modify";
89
- if (before === null) {
90
- // new file: every line is an add — no Myers pass needed (nor its cost on big files)
91
- kind = "create";
92
- const adds = signedLines(after, "+");
93
- if (adds.length > 0) body.push(`@@ -0,0 +1,${adds.filter((l) => l[0] === "+").length} @@`, ...adds);
94
- } else {
95
- if (before.length + after.length > MAX_CHARS) return unavailable("file too large to preview");
96
- // stripTrailingCr: a CRLF file diffs by content, not by line ending (gemini-cli diff-utils.ts:22)
97
- const patch = structuredPatch(rel, rel, before, after, undefined, undefined,
98
- { context: opts.context ?? DIFF_CONTEXT, stripTrailingCr: true, maxEditLength: MAX_EDIT_LENGTH, timeout: MAX_DIFF_MS });
99
- if (patch === undefined) return unavailable(`change too large to preview (over ${MAX_EDIT_LENGTH} edited lines or ${MAX_DIFF_MS}ms)`);
100
- for (const h of patch.hunks) body.push(`@@ -${h.oldStart},${h.oldLines} +${h.newStart},${h.newLines} @@`, ...h.lines);
101
- if (body.length === 0) return { text: "no content changes (line endings only)", truncated: false, kind: "unchanged" };
102
- }
103
- const lines = [`--- ${before === null ? "/dev/null" : `a/${rel}`}`, `+++ b/${rel}`, ...body];
104
- return { text: clipLines(lines, maxLines).join("\n"), truncated: lines.length > maxLines, kind };
105
- }
106
-
107
- /** Preview what an edit/write tool call would do to its file, as a bounded unified diff.
108
- * `args` are the (revised) tool args exactly as the tool would receive them. */
109
- export function previewDiff(tool: "edit" | "write", args: unknown, cwd: string, opts: DiffPreviewOptions = {}): DiffPreview {
110
- try {
111
- const a = (typeof args === "object" && args !== null ? args : {}) as Record<string, unknown>;
112
- if (typeof a.path !== "string" || a.path === "") return unavailable("missing path");
113
- const abs = resolvePath(cwd, a.path);
114
- const rel = relative(cwd, abs).replace(/\\/g, "/") || a.path;
115
- let before: string | null = null;
116
- if (existsSync(abs)) {
117
- const buf = readFileSync(abs);
118
- if (hasNul(buf)) return unavailable("binary file");
119
- before = buf.toString("utf8");
120
- }
121
- let after: string;
122
- if (tool === "write") {
123
- if (typeof a.content !== "string") return unavailable("missing content");
124
- after = a.content;
125
- } else {
126
- if (before === null) return unavailable(`file not found: ${abs}`);
127
- const ops = editOps(a, abs);
128
- if (ops === null) return unavailable("malformed edits");
129
- const r = applyEditsToContent(before, ops, abs);
130
- if (!r.ok) return unavailable(failureReason(r.failure));
131
- after = r.content;
132
- }
133
- if (after.includes("\0")) return unavailable("binary content");
134
- return renderUnified(rel, before, after, opts);
135
- } catch (e) {
136
- return unavailable(e instanceof Error ? e.message : String(e));
137
- }
138
- }