rovecode 0.3.2 → 0.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (491) hide show
  1. package/CHANGELOG.md +512 -0
  2. package/README.md +33 -45
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-dybnr56b.js +2 -0
  5. package/dist/cli/ask-user-p8hq4xgj.js +2 -0
  6. package/dist/cli/auth-login-ewpgw5sm.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-xv3ypwev.js +9 -0
  9. package/dist/cli/catalog-737wb2s0.js +2 -0
  10. package/dist/cli/cli-arhg40m0.js +2 -0
  11. package/dist/cli/client-cf2pxx8q.js +2 -0
  12. package/dist/cli/commands-3p7e4xxs.js +2 -0
  13. package/dist/cli/connect-3q93d7cb.js +2 -0
  14. package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
  15. package/dist/cli/context-report-hbw9zfes.js +2 -0
  16. package/dist/cli/count-remote-mby98cd0.js +2 -0
  17. package/dist/cli/design-122y0axd.js +2 -0
  18. package/dist/cli/dispatch-b4egzvvh.js +2 -0
  19. package/dist/cli/doctor-x4jkv72e.js +3 -0
  20. package/dist/cli/executor-ftvg6tsy.js +2 -0
  21. package/dist/cli/export-pdgdhkch.js +2 -0
  22. package/dist/cli/files-cez9a96p.js +2 -0
  23. package/dist/cli/gauntlet-r3xxaszc.js +2 -0
  24. package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
  25. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
  26. package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
  27. package/dist/cli/hashline-ewg5hbe3.js +2 -0
  28. package/dist/cli/http-n0kehsk8.js +5 -0
  29. package/dist/cli/index-z5qt1s76.js +2 -0
  30. package/dist/cli/install-80mp63kx.js +2 -0
  31. package/dist/cli/loop-12twjcat.js +2 -0
  32. package/dist/cli/main-01pv9206.js +4 -0
  33. package/dist/cli/main-0jys2ccn.js +3 -0
  34. package/dist/cli/main-1ztz6fkj.js +10 -0
  35. package/dist/cli/main-23q7cmww.js +9 -0
  36. package/dist/cli/main-2rzbexn2.js +3 -0
  37. package/dist/cli/main-2wyax8k9.js +9 -0
  38. package/dist/cli/main-2yeveeve.js +6 -0
  39. package/dist/cli/main-2z3dek0b.js +3 -0
  40. package/dist/cli/main-2zgsknth.js +3 -0
  41. package/dist/cli/main-45ejth3a.js +4 -0
  42. package/dist/cli/main-45rn3trk.js +22 -0
  43. package/dist/cli/main-4p4e2w7x.js +4 -0
  44. package/dist/cli/main-4y0tnfpa.js +16 -0
  45. package/dist/cli/main-5py0rkmc.js +4 -0
  46. package/dist/cli/main-6dtqmbt6.js +7 -0
  47. package/dist/cli/main-6h9x282m.js +4 -0
  48. package/dist/cli/main-6vjeds42.js +3 -0
  49. package/dist/cli/main-78gq4bt9.js +6 -0
  50. package/dist/cli/main-7jd5vh3x.js +4 -0
  51. package/dist/cli/main-7kt6r53y.js +4 -0
  52. package/dist/cli/main-8c1tbazx.js +58 -0
  53. package/dist/cli/main-9a9rnh47.js +19 -0
  54. package/dist/cli/main-9ht36z12.js +3 -0
  55. package/dist/cli/main-a2yfvcy9.js +7 -0
  56. package/dist/cli/main-a3f51n0x.js +5 -0
  57. package/dist/cli/main-b8zq261k.js +3 -0
  58. package/dist/cli/main-bxtvnf6d.js +13 -0
  59. package/dist/cli/main-edxc3yzt.js +4 -0
  60. package/dist/cli/main-evgz4mp5.js +21 -0
  61. package/dist/cli/main-f33fc5je.js +9 -0
  62. package/dist/cli/main-fvnpq46y.js +12 -0
  63. package/dist/cli/main-gbbty4d4.js +3 -0
  64. package/dist/cli/main-gth53dnt.js +25 -0
  65. package/dist/cli/main-hqbz10aw.js +9 -0
  66. package/dist/cli/main-hrrvcfan.js +38 -0
  67. package/dist/cli/main-hzwtsb2m.js +5 -0
  68. package/dist/cli/main-j7ttv0sd.js +34 -0
  69. package/dist/cli/main-jak598k9.js +5 -0
  70. package/dist/cli/main-kba6zeyd.js +6 -0
  71. package/dist/cli/main-kwwsz6rq.js +3 -0
  72. package/dist/cli/main-m8vm17zq.js +3 -0
  73. package/dist/cli/main-mg4f96e1.js +3 -0
  74. package/dist/cli/main-mg9b20ac.js +18 -0
  75. package/dist/cli/main-mgb9ccnx.js +3 -0
  76. package/dist/cli/main-mjt2p7aj.js +3 -0
  77. package/dist/cli/main-n6qrdbmy.js +3 -0
  78. package/dist/cli/main-na7wse0x.js +5 -0
  79. package/dist/cli/main-nqveez48.js +4 -0
  80. package/dist/cli/main-ntqef02r.js +10 -0
  81. package/dist/cli/main-nvc3yjay.js +136 -0
  82. package/dist/cli/main-p0cfn6nr.js +16 -0
  83. package/dist/cli/main-qj2djy17.js +19 -0
  84. package/dist/cli/main-qsevpgsv.js +3 -0
  85. package/dist/cli/main-qvarybsp.js +3 -0
  86. package/dist/cli/main-rebtt91r.js +5 -0
  87. package/dist/cli/main-rpg7h8mb.js +3 -0
  88. package/dist/cli/main-rsy72qmw.js +15 -0
  89. package/dist/cli/main-rvetps99.js +18 -0
  90. package/dist/cli/main-s4bb0jav.js +3 -0
  91. package/dist/cli/main-s9v8k74e.js +3 -0
  92. package/dist/cli/main-tjvwmscs.js +3 -0
  93. package/dist/cli/main-tkgarpjj.js +4 -0
  94. package/dist/cli/main-v8y60bb2.js +3 -0
  95. package/dist/cli/main-vhrrq337.js +3 -0
  96. package/dist/cli/main-vp2dfb7s.js +4 -0
  97. package/dist/cli/main-vqbr22sz.js +8 -0
  98. package/dist/cli/main-vxnwe5xx.js +18 -0
  99. package/dist/cli/main-wgph00xf.js +5 -0
  100. package/dist/cli/main-wk2csfnj.js +5 -0
  101. package/dist/cli/main-wm997zjx.js +3 -0
  102. package/dist/cli/main-wpkyraxh.js +3 -0
  103. package/dist/cli/main-wqt32p5x.js +4 -0
  104. package/dist/cli/main-x9ct6y1a.js +3 -0
  105. package/dist/cli/main-xfekqh9m.js +7 -0
  106. package/dist/cli/main-xt9zc3n6.js +7 -0
  107. package/dist/cli/main-xx2z3zh5.js +4 -0
  108. package/dist/cli/main-y5c82rxr.js +3 -0
  109. package/dist/cli/main-yrjt2sqt.js +14 -0
  110. package/dist/cli/main-ys6zj3yr.js +3 -0
  111. package/dist/cli/main-ywbxshqc.js +8 -0
  112. package/dist/cli/main-z13755t8.js +25 -0
  113. package/dist/cli/main-zc7pyrbj.js +4 -0
  114. package/dist/cli/main.js +279 -0
  115. package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
  116. package/dist/cli/mcp-login-bthtfpt7.js +2 -0
  117. package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
  118. package/dist/cli/notify-54v5z9dz.js +2 -0
  119. package/dist/cli/oauth-g5gme95c.js +2 -0
  120. package/dist/cli/output-satndjap.js +16 -0
  121. package/dist/cli/profiles-sfhpbq3m.js +2 -0
  122. package/dist/cli/provider-config-hv3xtdt4.js +2 -0
  123. package/dist/cli/provider-kwzq6g84.js +2 -0
  124. package/dist/cli/registry-fh0hdnyn.js +2 -0
  125. package/dist/cli/registry-y1y8e94r.js +2 -0
  126. package/dist/cli/repl-t4z03mqq.js +11 -0
  127. package/dist/cli/resume-fqt4chg8.js +2 -0
  128. package/dist/cli/run-flags-rysbag9t.js +2 -0
  129. package/dist/cli/runtime-j19fjbsa.js +2 -0
  130. package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
  131. package/dist/cli/server-r0b6bksk.js +5 -0
  132. package/dist/cli/session-arg-txmn5g4x.js +2 -0
  133. package/dist/cli/session-ed250d9j.js +2 -0
  134. package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
  135. package/dist/cli/settings-y9rzcqx8.js +2 -0
  136. package/dist/cli/setup-jmbr11j0.js +2 -0
  137. package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
  138. package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
  139. package/dist/cli/smoke-1bg937kx.js +8 -0
  140. package/dist/cli/start-chat-p01cdks3.js +12 -0
  141. package/dist/cli/stream-4wmyaypz.js +2 -0
  142. package/dist/cli/task-eg4s093s.js +2 -0
  143. package/dist/cli/tasks-12v9rr9k.js +2 -0
  144. package/dist/cli/thinking-a5ngvqyh.js +2 -0
  145. package/dist/cli/todo-1wxpcecx.js +2 -0
  146. package/dist/cli/tools-2ftsya7w.js +2 -0
  147. package/dist/cli/tools-x1tj4fxm.js +2 -0
  148. package/dist/cli/trust-cmd-hccxehzb.js +2 -0
  149. package/dist/cli/update-check-ygt3vd7m.js +2 -0
  150. package/dist/cli/update-cmd-v23qhr8c.js +2 -0
  151. package/dist/cli/voice-g1gtck92.js +2 -0
  152. package/dist/cli/webfetch-0nnrjgb5.js +2 -0
  153. package/dist/cli/websearch-f0vr2p7d.js +2 -0
  154. package/dist/cli/workspace-9rq1w4ta.js +2 -0
  155. package/dist/lib/index.js +62 -0
  156. package/dist/lib/models-index.json +1 -0
  157. package/dist/lib/plugins.js +6 -0
  158. package/dist/lib/providers.js +17 -0
  159. package/dist/lib/public-api.js +20 -0
  160. package/dist/rovecode.exe +4 -0
  161. package/package.json +38 -11
  162. package/bin/rovecode.ts +0 -21
  163. package/src/acp/server.ts +0 -374
  164. package/src/cli/auth-login.ts +0 -122
  165. package/src/cli/connect.ts +0 -244
  166. package/src/cli/context-cmd.ts +0 -199
  167. package/src/cli/dispatch.ts +0 -82
  168. package/src/cli/doctor.ts +0 -362
  169. package/src/cli/export.ts +0 -276
  170. package/src/cli/help.ts +0 -293
  171. package/src/cli/is-tui-invocation.ts +0 -8
  172. package/src/cli/main.ts +0 -583
  173. package/src/cli/market-cmd.ts +0 -658
  174. package/src/cli/mcp-login.ts +0 -141
  175. package/src/cli/mcp-market-cmd.ts +0 -302
  176. package/src/cli/output.ts +0 -382
  177. package/src/cli/repl.ts +0 -250
  178. package/src/cli/repomap-root.ts +0 -14
  179. package/src/cli/resume.ts +0 -57
  180. package/src/cli/run-flags.ts +0 -43
  181. package/src/cli/run-limits.ts +0 -78
  182. package/src/cli/runtime.ts +0 -931
  183. package/src/cli/session-arg.ts +0 -30
  184. package/src/cli/sessions-cmd.ts +0 -145
  185. package/src/cli/setup.ts +0 -153
  186. package/src/cli/skills-cmd.ts +0 -194
  187. package/src/cli/start-chat.ts +0 -65
  188. package/src/cli/trust-cmd.ts +0 -52
  189. package/src/coding/bash.ts +0 -148
  190. package/src/coding/checkpoints.ts +0 -327
  191. package/src/coding/diff.ts +0 -138
  192. package/src/coding/files.ts +0 -341
  193. package/src/coding/hashline.ts +0 -274
  194. package/src/coding/lsp-gate.ts +0 -254
  195. package/src/coding/lsp-servers.ts +0 -147
  196. package/src/coding/lsp.ts +0 -283
  197. package/src/coding/repomap-cache.ts +0 -99
  198. package/src/coding/repomap-files.ts +0 -192
  199. package/src/coding/repomap.ts +0 -481
  200. package/src/core/agents.ts +0 -255
  201. package/src/core/compaction.ts +0 -259
  202. package/src/core/config.ts +0 -289
  203. package/src/core/context-report.ts +0 -228
  204. package/src/core/context.ts +0 -60
  205. package/src/core/count-remote.ts +0 -107
  206. package/src/core/execpolicy-rules.ts +0 -196
  207. package/src/core/execpolicy.ts +0 -385
  208. package/src/core/executor.ts +0 -454
  209. package/src/core/guardrails.ts +0 -400
  210. package/src/core/hooks.ts +0 -411
  211. package/src/core/images.ts +0 -230
  212. package/src/core/intro.ts +0 -266
  213. package/src/core/loop.ts +0 -567
  214. package/src/core/modes.ts +0 -372
  215. package/src/core/orchestrator.ts +0 -245
  216. package/src/core/proc-group.ts +0 -48
  217. package/src/core/project-trust.ts +0 -98
  218. package/src/core/reflection.ts +0 -165
  219. package/src/core/sandbox-config.ts +0 -186
  220. package/src/core/session-id.ts +0 -24
  221. package/src/core/session-images.ts +0 -73
  222. package/src/core/session-ops.ts +0 -183
  223. package/src/core/session-text.ts +0 -29
  224. package/src/core/session.ts +0 -469
  225. package/src/core/settings.ts +0 -170
  226. package/src/core/tasks.ts +0 -646
  227. package/src/core/token-scale.ts +0 -108
  228. package/src/core/tools.ts +0 -309
  229. package/src/core/trust.ts +0 -104
  230. package/src/core/types.ts +0 -330
  231. package/src/core/update-check.ts +0 -171
  232. package/src/core/usage.ts +0 -204
  233. package/src/core/validate.ts +0 -121
  234. package/src/core/verify-gate.ts +0 -159
  235. package/src/core/verify.ts +0 -236
  236. package/src/core/voice.ts +0 -158
  237. package/src/core/win-job.ts +0 -183
  238. package/src/core/workspace.ts +0 -184
  239. package/src/design/audit.ts +0 -797
  240. package/src/design/direction.ts +0 -190
  241. package/src/design/rules.ts +0 -157
  242. package/src/eval/bench.ts +0 -150
  243. package/src/eval/gauntlet-runner.ts +0 -215
  244. package/src/eval/gauntlet-support.ts +0 -84
  245. package/src/eval/gauntlet-wave3.ts +0 -269
  246. package/src/eval/gauntlet-wave4.ts +0 -217
  247. package/src/eval/gauntlet.ts +0 -253
  248. package/src/index.ts +0 -17
  249. package/src/lanes/agy.ts +0 -95
  250. package/src/lanes/approval.ts +0 -24
  251. package/src/lanes/claude.ts +0 -129
  252. package/src/lanes/codex.ts +0 -127
  253. package/src/lanes/events.ts +0 -130
  254. package/src/lanes/job.ts +0 -142
  255. package/src/lanes/opencode.ts +0 -122
  256. package/src/lanes/process.ts +0 -184
  257. package/src/lanes/progress.ts +0 -183
  258. package/src/lanes/registry.ts +0 -178
  259. package/src/lanes/runner.ts +0 -124
  260. package/src/lanes/types.ts +0 -112
  261. package/src/market/catalogs/mcp-docs.json +0 -111
  262. package/src/market/catalogs/plugins.json +0 -111
  263. package/src/market/catalogs/skills.json +0 -478
  264. package/src/market/clone.ts +0 -72
  265. package/src/market/context-cost.ts +0 -121
  266. package/src/market/digest.ts +0 -106
  267. package/src/market/index.ts +0 -22
  268. package/src/market/install.ts +0 -578
  269. package/src/market/manifest.ts +0 -187
  270. package/src/market/prereq.ts +0 -145
  271. package/src/market/registry.ts +0 -363
  272. package/src/market/resolve.ts +0 -111
  273. package/src/market/types.ts +0 -236
  274. package/src/market/validate.ts +0 -227
  275. package/src/mcp/client.ts +0 -449
  276. package/src/mcp/config.ts +0 -252
  277. package/src/mcp/local-package.ts +0 -211
  278. package/src/mcp/market-catalog.ts +0 -84
  279. package/src/mcp/market-install.ts +0 -289
  280. package/src/mcp/market.ts +0 -362
  281. package/src/mcp/oauth.ts +0 -251
  282. package/src/mcp/prompts-resources.ts +0 -249
  283. package/src/mcp/shared.ts +0 -149
  284. package/src/mcp/status.ts +0 -67
  285. package/src/mcp/tools.ts +0 -275
  286. package/src/mcp/transport.ts +0 -122
  287. package/src/mcp/trust.ts +0 -25
  288. package/src/memory/blocks.ts +0 -278
  289. package/src/memory/recall.ts +0 -355
  290. package/src/memory/scope.ts +0 -182
  291. package/src/memory/store.ts +0 -105
  292. package/src/memory/tools.ts +0 -99
  293. package/src/plugins/cli.ts +0 -119
  294. package/src/plugins/discover.ts +0 -108
  295. package/src/plugins/index.ts +0 -50
  296. package/src/plugins/install.ts +0 -184
  297. package/src/plugins/load.ts +0 -124
  298. package/src/plugins/manifest.ts +0 -92
  299. package/src/plugins/state.ts +0 -83
  300. package/src/providers/auth.ts +0 -408
  301. package/src/providers/cache.ts +0 -223
  302. package/src/providers/catalog-local.ts +0 -160
  303. package/src/providers/catalog.ts +0 -421
  304. package/src/providers/middleware-context.ts +0 -86
  305. package/src/providers/middleware.ts +0 -373
  306. package/src/providers/model-list.ts +0 -23
  307. package/src/providers/oauth/common.ts +0 -105
  308. package/src/providers/oauth/device-code.ts +0 -107
  309. package/src/providers/oauth/github-copilot.ts +0 -146
  310. package/src/providers/oauth/loopback.ts +0 -158
  311. package/src/providers/oauth/openai.ts +0 -163
  312. package/src/providers/oauth/openrouter.ts +0 -89
  313. package/src/providers/oauth/pkce.ts +0 -45
  314. package/src/providers/oauth/registry.ts +0 -39
  315. package/src/providers/oauth/seam.ts +0 -89
  316. package/src/providers/profile-glm53.ts +0 -111
  317. package/src/providers/profile-sonnet5-persona.ts +0 -65
  318. package/src/providers/profile-sonnet5-voice.ts +0 -23
  319. package/src/providers/profiles.ts +0 -156
  320. package/src/providers/provider-config.ts +0 -311
  321. package/src/providers/registry.ts +0 -333
  322. package/src/providers/responses.ts +0 -209
  323. package/src/providers/retry.ts +0 -234
  324. package/src/providers/router.ts +0 -294
  325. package/src/providers/sse.ts +0 -26
  326. package/src/providers/stream-errors.ts +0 -117
  327. package/src/providers/stream.ts +0 -566
  328. package/src/providers/thinking.ts +0 -189
  329. package/src/providers/wire-messages.ts +0 -129
  330. package/src/providers/wire-responses.ts +0 -79
  331. package/src/providers/wire-select.ts +0 -53
  332. package/src/server/http.ts +0 -291
  333. package/src/server/openapi.ts +0 -246
  334. package/src/sextant/card-hits.ts +0 -102
  335. package/src/sextant/card-keys.ts +0 -55
  336. package/src/sextant/context-source.ts +0 -157
  337. package/src/sextant/crew-cards.ts +0 -350
  338. package/src/sextant/draw-agents.ts +0 -273
  339. package/src/sextant/draw-code.ts +0 -388
  340. package/src/sextant/draw-context.ts +0 -222
  341. package/src/sextant/draw-frame.ts +0 -164
  342. package/src/sextant/draw-market.ts +0 -573
  343. package/src/sextant/draw-messages.ts +0 -386
  344. package/src/sextant/draw-pet.ts +0 -230
  345. package/src/sextant/draw-plan.ts +0 -187
  346. package/src/sextant/draw-tabs.ts +0 -85
  347. package/src/sextant/draw-util.ts +0 -65
  348. package/src/sextant/draw-wizard.ts +0 -378
  349. package/src/sextant/engine.ts +0 -230
  350. package/src/sextant/frame-hits.ts +0 -25
  351. package/src/sextant/frame.ts +0 -101
  352. package/src/sextant/git-status.ts +0 -197
  353. package/src/sextant/grid.ts +0 -59
  354. package/src/sextant/input.ts +0 -119
  355. package/src/sextant/keys.ts +0 -521
  356. package/src/sextant/layout.ts +0 -86
  357. package/src/sextant/local-commands.ts +0 -169
  358. package/src/sextant/market-source.ts +0 -287
  359. package/src/sextant/mentions.ts +0 -200
  360. package/src/sextant/message-hits.ts +0 -26
  361. package/src/sextant/model.ts +0 -387
  362. package/src/sextant/overlays.ts +0 -456
  363. package/src/sextant/panel-hits.ts +0 -38
  364. package/src/sextant/pet.ts +0 -399
  365. package/src/sextant/screen.ts +0 -324
  366. package/src/sextant/scroll-hits.ts +0 -66
  367. package/src/sextant/scrollbar.ts +0 -82
  368. package/src/sextant/sextant-bridge.ts +0 -174
  369. package/src/sextant/sextant-cards.ts +0 -142
  370. package/src/sextant/sextant-diff-base.ts +0 -63
  371. package/src/sextant/sextant-files.ts +0 -154
  372. package/src/sextant/sextant-frame-loop.ts +0 -335
  373. package/src/sextant/sextant-renderer.ts +0 -574
  374. package/src/sextant/sextant-repo.ts +0 -140
  375. package/src/sextant/theme.ts +0 -66
  376. package/src/sextant/tool-rows.ts +0 -189
  377. package/src/sextant/types.ts +0 -493
  378. package/src/skills/index.ts +0 -387
  379. package/src/skills/pack.ts +0 -220
  380. package/src/skills/spec.ts +0 -162
  381. package/src/skills/tools.ts +0 -69
  382. package/src/skills/versioned.ts +0 -227
  383. package/src/telemetry/otel-export.ts +0 -122
  384. package/src/telemetry/otel-lanes.ts +0 -89
  385. package/src/telemetry/otel-logs.ts +0 -131
  386. package/src/telemetry/otel-metrics.ts +0 -136
  387. package/src/telemetry/otel.ts +0 -397
  388. package/src/telemetry/otlp.ts +0 -76
  389. package/src/tools/ask-user.ts +0 -156
  390. package/src/tools/bash-bg.ts +0 -94
  391. package/src/tools/bash-jobs.ts +0 -237
  392. package/src/tools/design.ts +0 -151
  393. package/src/tools/evalcell.ts +0 -338
  394. package/src/tools/html-text.ts +0 -139
  395. package/src/tools/provider.ts +0 -149
  396. package/src/tools/task.ts +0 -250
  397. package/src/tools/todo.ts +0 -320
  398. package/src/tools/webfetch.ts +0 -332
  399. package/src/tools/websearch.ts +0 -359
  400. package/src/tui/agents-cmd.ts +0 -41
  401. package/src/tui/app.ts +0 -749
  402. package/src/tui/attach.ts +0 -127
  403. package/src/tui/boot-notes.ts +0 -41
  404. package/src/tui/builtin-prompts.ts +0 -59
  405. package/src/tui/checkpoints-cmd.ts +0 -70
  406. package/src/tui/clipboard-image.ts +0 -81
  407. package/src/tui/clipboard.ts +0 -78
  408. package/src/tui/commands.ts +0 -283
  409. package/src/tui/config-view.ts +0 -53
  410. package/src/tui/context-cmds.ts +0 -282
  411. package/src/tui/cost.ts +0 -108
  412. package/src/tui/crash-guard.ts +0 -173
  413. package/src/tui/focus-terminal.ts +0 -34
  414. package/src/tui/git-cmds.ts +0 -273
  415. package/src/tui/git-plain.ts +0 -58
  416. package/src/tui/info-cmd.ts +0 -150
  417. package/src/tui/input-plain.ts +0 -76
  418. package/src/tui/mcp-cmd.ts +0 -128
  419. package/src/tui/memory-note.ts +0 -77
  420. package/src/tui/modes-cmd.ts +0 -45
  421. package/src/tui/notify-seq.ts +0 -100
  422. package/src/tui/notify.ts +0 -318
  423. package/src/tui/overlays.ts +0 -97
  424. package/src/tui/pi-renderer.ts +0 -428
  425. package/src/tui/providers-cmd.ts +0 -377
  426. package/src/tui/reasoning-view.ts +0 -56
  427. package/src/tui/renderer.ts +0 -128
  428. package/src/tui/replay-marker.ts +0 -29
  429. package/src/tui/session-cmd.ts +0 -148
  430. package/src/tui/session-manage.ts +0 -95
  431. package/src/tui/sextant-attach.ts +0 -102
  432. package/src/tui/sextant-io.ts +0 -202
  433. package/src/tui/sextant-smoke.ts +0 -110
  434. package/src/tui/shell-cmd.ts +0 -158
  435. package/src/tui/smoke.ts +0 -72
  436. package/src/tui/staged-terminal.ts +0 -50
  437. package/src/tui/startup.ts +0 -12
  438. package/src/tui/theme.ts +0 -59
  439. package/src/tui/todo-label.ts +0 -7
  440. package/src/tui/trust-card.ts +0 -107
  441. package/src/tui/tui-commands.ts +0 -87
  442. package/tsconfig.json +0 -30
  443. package/vendor/pi-tui/LICENSE +0 -21
  444. package/vendor/pi-tui/PATCHES.md +0 -12
  445. package/vendor/pi-tui/PROVENANCE.md +0 -12
  446. package/vendor/pi-tui/README.upstream.md +0 -854
  447. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  448. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  449. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  450. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  451. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  452. package/vendor/pi-tui/src/components/box.ts +0 -138
  453. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  454. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  455. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  456. package/vendor/pi-tui/src/components/image.ts +0 -128
  457. package/vendor/pi-tui/src/components/input.ts +0 -448
  458. package/vendor/pi-tui/src/components/loader.ts +0 -93
  459. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  460. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  461. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  462. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  463. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  464. package/vendor/pi-tui/src/components/stack.ts +0 -155
  465. package/vendor/pi-tui/src/components/text.ts +0 -108
  466. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  467. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  468. package/vendor/pi-tui/src/editor-component.ts +0 -75
  469. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  470. package/vendor/pi-tui/src/index.ts +0 -149
  471. package/vendor/pi-tui/src/keybindings.ts +0 -321
  472. package/vendor/pi-tui/src/keys.ts +0 -1402
  473. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  474. package/vendor/pi-tui/src/latex.ts +0 -1381
  475. package/vendor/pi-tui/src/layout-node.ts +0 -52
  476. package/vendor/pi-tui/src/layout.ts +0 -411
  477. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  478. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  479. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  480. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  481. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  482. package/vendor/pi-tui/src/terminal.ts +0 -554
  483. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  484. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  485. package/vendor/pi-tui/src/tui.ts +0 -1264
  486. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  487. package/vendor/pi-tui/src/utils.ts +0 -1327
  488. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  489. package/vendor/pi-tui/test/test-themes.ts +0 -39
  490. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  491. /package/{src/providers → dist/cli}/models-index.json +0 -0
@@ -1,236 +0,0 @@
1
- /** One market, three kinds. `rovecode market …`, the sextant's /market and the site's market page all read
2
- * the SAME item type: an MCP server, a skill and a plugin differ in what installing them means, not in how
3
- * they are found, described or shown.
4
- *
5
- * Design rules this file exists to hold:
6
- * - MCP is WRAPPED, never rewritten: `install.kind === "mcp"` carries the existing `MarketEntry`
7
- * (src/mcp/market.ts) untouched, so the curated shelf, the registry re-typing and the install planner
8
- * keep working exactly as they do today. A new kind is a new arm of `InstallSpec`, not a new pipeline.
9
- * - Everything a UI needs to draw a row is on the item itself (title, publisher, one line, tags, version,
10
- * source). Anything that needs the local filesystem — installed? update available? — is a separate
11
- * `InstalledState`, because it changes without the catalog changing.
12
- * - Catalog data is UNTRUSTED. These types describe what a validator PRODUCES (registry.ts), never what a
13
- * response happens to contain. The caps live here so every source is bounded the same way.
14
- * - Nothing here reads a file, spawns a process or touches the network: types + pure helpers only, so
15
- * importing this module costs nothing at boot (the lazy-import rule). */
16
-
17
- import type { MarketEntry } from "../mcp/market.ts";
18
-
19
- export type MarketKind = "mcp" | "skill" | "plugin";
20
- /** where the item was found. "curated" = built into rovecode, hand-checked; "registry" = a live remote
21
- * index (the MCP registry today); "catalog" = a rovecode-published catalog file for skills and plugins */
22
- export type MarketSource = "curated" | "registry" | "catalog";
23
- /** user = ~/.rovecode (every project sees it) · project = this repo (trust-gated, committed with the repo) */
24
- export type MarketScope = "user" | "project";
25
-
26
- /** Hard ceilings on anything a source contributes. A field past its cap is truncated (strings) or dropped
27
- * (list items) — never an error, because one bad row must not blank a whole catalog. */
28
- export const LIMITS = {
29
- /** response body accepted from a remote catalog */
30
- body: 2 * 1024 * 1024,
31
- /** items accepted from one source */
32
- items: 200,
33
- /** any short string (id, title, publisher, version, url) */
34
- str: 300,
35
- /** the one-line description a row shows */
36
- desc: 500,
37
- /** tags per item, and files per skill */
38
- list: 32,
39
- /** environment variables one item may declare */
40
- env: 24,
41
- /** an item's documentation body: markdown, not a label. Nothing else here is remotely this big, which is
42
- * exactly why it needs its own cap — run a 24 KB body through `str()` and it comes back silently cut to
43
- * 300 characters, still a valid string, and nobody notices. */
44
- docs: 24 * 1024,
45
- } as const;
46
-
47
- /** An item's own documentation, fetched at BUILD time and carried in the catalog.
48
- *
49
- * Never fetched at runtime. The market has to work offline and the site's page is static, and pulling
50
- * third-party text into a UI at display time is not something worth doing for a docs pane. The body is
51
- * carried as TEXT and nothing more: it is never executed, never evaluated, never parsed as HTML here —
52
- * each surface renders it safely on its own side.
53
- *
54
- * Absent when upstream had no readable doc. A row without docs is a normal row, not a broken one. */
55
- export interface ItemDocs {
56
- /** the exact URL it was read from, so a reader can go to the original */
57
- source: string;
58
- format: "markdown";
59
- /** the body's length in bytes BEFORE truncation, so a UI can say how much is missing */
60
- bytes: number;
61
- truncated: boolean;
62
- /** Markdown text, capped at LIMITS.docs. OPTIONAL on purpose: the search path does not carry bodies
63
- * (they are ~200 KB the search never reads), so a row from `searchMarket` has the metadata and
64
- * `body === undefined`. `undefined` means "not carried on this call" and is not the same as an empty
65
- * document — a UI must send the reader to `market docs <id>` rather than say there is nothing. */
66
- body?: string;
67
- }
68
-
69
- /** An environment variable an item needs. Same shape the MCP side already uses (mcp/market.ts EnvSpec):
70
- * `secret: true` is asked masked on a shell and NEVER written into a project file — it is written as
71
- * `${NAME}` and read from the environment at launch. */
72
- export interface MarketEnv {
73
- name: string;
74
- description?: string;
75
- required: boolean;
76
- secret: boolean;
77
- /** a value the catalog already knows (a default port, a public URL): not a question */
78
- default?: string;
79
- }
80
-
81
- /** What installing this item actually does. One arm per kind — the arm carries whatever the kind's existing
82
- * installer needs, so `install.ts` can delegate without re-deriving anything. */
83
- export type InstallSpec =
84
- /** the MCP entry as mcp/market.ts produced it; mcp/market-install.ts plans and writes it */
85
- | { kind: "mcp"; entry: MarketEntry }
86
- /** a plugin folder or git URL; src/plugins/install.ts addPlugin() clones and copies it.
87
- * `subfolder` is where the plugin.json lives inside that repo — real plugins live in a monorepo
88
- * (plugins/safety-net), so a repo root is the exception, not the rule. Same escape rules as a skill's. */
89
- | { kind: "plugin"; source: string; /** true when `source` is a git URL rather than a local folder */ git: boolean; subfolder?: string }
90
- /** a skill: a git repo (optionally a subfolder) or a set of files written verbatim.
91
- * `files` is the offline form — a curated skill ships its SKILL.md in the catalog itself. */
92
- | { kind: "skill"; source?: { git: string; subfolder?: string }; files?: { path: string; text: string }[] };
93
-
94
- /** One row in the market, whatever its kind. */
95
- export interface MarketItem {
96
- /** what `market install <id>` takes. Unique per kind, not across kinds: `mcp:git` and `skill:git` may
97
- * both exist, which is why resolve.ts accepts `kind:id` and lists candidates when a bare id is ambiguous. */
98
- id: string;
99
- kind: MarketKind;
100
- /** display name; falls back to `id` when a source has none */
101
- title: string;
102
- /** who publishes it — a namespace, an org, or "unknown" */
103
- publisher: string;
104
- /** one line, already capped at LIMITS.desc */
105
- description: string;
106
- source: MarketSource;
107
- version?: string;
108
- repository?: string;
109
- homepage?: string;
110
- /** free-form, lowercase, capped; a UI may filter on these but must not depend on any particular tag */
111
- tags: string[];
112
- /** the source's own status when it is anything but normal ("deprecated", "experimental") */
113
- status?: string;
114
- /** the licence as the publisher states it ("Apache-2.0", "source-available, not open source", "none stated").
115
- * A field, not a note: a market that installs other people's work has to show this before the yes, and a
116
- * UI will want to filter on it. Free text — an SPDX id is common but not required. */
117
- license?: string;
118
- /** the variables this item needs before it can run. Empty for most skills and plugins. */
119
- env: MarketEnv[];
120
- install: InstallSpec;
121
- /** the item's own documentation, carried in the catalog — see ItemDocs */
122
- docs?: ItemDocs;
123
- /** extra lines a catalog wants in the approval preview, in its own words ("copies one SKILL.md, runs
124
- * nothing"). install.ts writes the standard plan from `install`; these are appended verbatim, capped
125
- * like every other list. A catalog that has nothing special to say omits them. */
126
- planNote?: string[];
127
- /** what a plugin brings, as data rather than as a sentence: "commands", "skills", "agents", "hooks",
128
- * "mcpServers", "tools". The catalog has carried this since the generator was written, but only the
129
- * prose form ("contributes: commands, skills") ever reached a reader — so a caller wanting to filter
130
- * on it had to parse English. The prose stays for the approval preview, which is written for a
131
- * person; this is the same fact for anything that has to choose between two candidates. It describes
132
- * what the catalog CLAIMS, exactly like every other field here, and is no more verified than they are. */
133
- contributes?: string[];
134
- }
135
-
136
- /** Local truth about one item, computed from disk (never from a catalog). Kept apart from `MarketItem`
137
- * because it changes when the user installs something, not when the catalog updates. Its PRESENCE on a
138
- * row means installed — `row.installed === undefined` is the not-installed case, so a UI draws the badge
139
- * from one check and never from a boolean that could disagree with the path. */
140
- export interface InstalledState {
141
- /** where it landed: the mcp.json, the plugin folder, the skill folder */
142
- path: string;
143
- scope: MarketScope;
144
- /** the version on disk when it is knowable (a plugin manifest, an mcp entry's recorded version) */
145
- version?: string;
146
- /** true when the catalog's version is a different string from the installed one. Not a semver compare:
147
- * a UI says "update available", the human reads both numbers. */
148
- updateAvailable?: boolean;
149
- /** project-scope items only: the trust gate's answer. `undefined` for user scope, which is never gated. */
150
- trusted?: boolean;
151
- }
152
-
153
- /** A market row as a UI receives it: the catalog item plus what this machine knows about it.
154
- * `installed` present = on disk here; absent = not installed. */
155
- export interface MarketRow extends MarketItem {
156
- installed?: InstalledState;
157
- }
158
-
159
- // ---------------------------------------------------------------- results, never exceptions
160
-
161
- /** Why a source produced nothing. A UI draws these differently, so they are distinguishable values and not
162
- * one error string: `offline` and `stale` still carry rows, `failed` carries the reason. */
163
- export type SourceStatus =
164
- | { ok: true; from: "live" }
165
- /** the network was not tried (--offline, no network): rows come from the cache or the curated shelf */
166
- | { ok: true; from: "cache"; ageMs: number }
167
- | { ok: true; from: "curated" }
168
- /** the source was not consulted at all (an empty query never asks the registry): no rows, no failure */
169
- | { ok: true; from: "skipped"; why: string }
170
- /** the fetch failed and there was nothing cached: this source contributed no rows */
171
- | { ok: false; reason: string };
172
-
173
- /** What every listing call returns. Never throws for a data problem — a caller that must distinguish
174
- * "nothing matched" from "the registry is down" reads `sources`. */
175
- export interface MarketResult {
176
- items: MarketItem[];
177
- /** one entry per source consulted, keyed by a stable name ("mcp:curated", "mcp:registry", "skills", "plugins") */
178
- sources: Record<string, SourceStatus>;
179
- /** human-readable notes worth showing once (a capped field, a dropped row, a stale cache) */
180
- notes: string[];
181
- }
182
-
183
- /** The three-step visibility the MCP market established, generalised: a plan is built and SHOWN, the human
184
- * says yes, and only then is anything written. `preview` is the whole plan in the human's words. */
185
- export interface InstallPlanView {
186
- item: MarketItem;
187
- /** where the write lands (an mcp.json, a plugins folder, a skills folder) */
188
- target: string;
189
- scope: MarketScope;
190
- /** the lines a CLI prints or a card shows — everything that must be seen before a yes */
191
- preview: string[];
192
- /** variables that will be asked (shell: masked for secrets) or written as `${NAME}` */
193
- asks: MarketEnv[];
194
- /** required values nobody can fill for the human (a directory to expose, a database URL) */
195
- pending: string[];
196
- /** true when this install replaces something already there */
197
- replaces?: string;
198
- }
199
-
200
- export type InstallOutcome =
201
- | { ok: true; item: MarketItem; target: string; scope: MarketScope;
202
- /** variable names the written config now refers to; the human must export them before a restart */
203
- envNames: string[];
204
- /** project scope only: whether the write was recorded as trusted */
205
- trusted?: boolean;
206
- /** MCP only: `<…>` placeholders the written entry still carries because nobody answered for them; the
207
- * loader skips the server until a hand replaces them, so `next` says that instead of "restart" */
208
- fillIn?: string[];
209
- /** MCP install-once only (mcp/local-package.ts): the package npm put on disk, as recorded in
210
- * installed.json — `missing` names anything the record could not carry (an integrity hash the lockfile
211
- * did not supply), so a caller can say so rather than print a clean line over a hole */
212
- package?: { name: string; version: string; prefix: string; bin: string; integrity?: string; missing?: string[] };
213
- /** what the human must do for it to take effect ("restart rovecode — servers are read once per process") */
214
- next?: string }
215
- | { ok: false; error: string };
216
-
217
- // ---------------------------------------------------------------- pure helpers (no I/O)
218
-
219
- /** `mcp:filesystem` → {kind, id}; a bare word → {id} with no kind. Anything else → null. */
220
- export function parseQualifiedId(text: string): { kind?: MarketKind; id: string } | null {
221
- const t = text.trim();
222
- if (t.length === 0 || t.length > LIMITS.str) return null;
223
- const m = /^(mcp|skill|plugin):(.+)$/.exec(t);
224
- if (m) return { kind: m[1] as MarketKind, id: m[2]!.trim() };
225
- return { id: t };
226
- }
227
-
228
- export const qualify = (item: Pick<MarketItem, "kind" | "id">): string => `${item.kind}:${item.id}`;
229
-
230
- /** The one-line row every surface shows. Kept here so the CLI, the TUI and the site cannot drift. */
231
- export function itemLine(item: MarketItem): string {
232
- const bits = [item.kind.padEnd(6), item.title];
233
- if (item.version) bits.push(item.version);
234
- bits.push(`· ${item.publisher}`);
235
- return `${bits.join(" ")} — ${item.description}`;
236
- }
@@ -1,227 +0,0 @@
1
- /** Check a catalog before anyone is asked to trust it.
2
- *
3
- * `--check` in CI answers "did OUR generator produce this?", which is the wrong question for someone
4
- * else's file. A person writing a private catalog for their team, or preparing one to send us, needs the
5
- * question answered the other way round: *given this file, what would rovecode do with it, and which of my
6
- * fields would not survive?* That is what this reports, field by field.
7
- *
8
- * THE INVARIANT THAT MATTERS: this must never call a row valid that `registry.ts` would drop. A validator
9
- * that disagrees with the loader is worse than none — it hands out a clean bill for a catalog that shows
10
- * up empty. So every row is also run through `itemFromCatalog`, the real reader, and a row it refuses
11
- * while we found nothing to say produces an error anyway. The checks below exist to explain the refusal in
12
- * the author's terms, not to be the authority on it.
13
- *
14
- * Errors and warnings are genuinely different and are not collapsed:
15
- * error — the row (or the file) is dropped. The market will not show it.
16
- * warning — the row loads, but not as written: a field was truncated, a list item past the cap was
17
- * dropped, a key was not recognised. Silent in production, and exactly what an author wants
18
- * to hear before they publish.
19
- */
20
-
21
- import { itemFromCatalog } from "./registry.ts";
22
- import { LIMITS } from "./types.ts";
23
-
24
- export type Severity = "error" | "warning";
25
-
26
- export interface Finding {
27
- /** where it is, in the author's terms: `items[3].description` */
28
- path: string;
29
- message: string;
30
- severity: Severity;
31
- }
32
-
33
- export interface ValidationReport {
34
- ok: boolean;
35
- kind: "skill" | "plugin";
36
- /** rows that would load */
37
- accepted: number;
38
- /** rows the market would drop */
39
- dropped: number;
40
- findings: Finding[];
41
- }
42
-
43
- const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === "object" && v !== null && !Array.isArray(v);
44
- const ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
45
-
46
- /** The keys each kind's rows may carry. An unrecognised key is a warning, not an error, because a catalog
47
- * may legitimately carry more than we read — but `descriptoin` is a typo that would otherwise cost the
48
- * author a silent empty description, and that is worth a line. */
49
- const KNOWN_KEYS: Record<"skill" | "plugin", ReadonlySet<string>> = {
50
- skill: new Set(["id", "name", "title", "publisher", "description", "version", "license", "tags", "repository",
51
- "homepage", "source", "files", "bytes", "docs", "env", "status", "planNote"]),
52
- plugin: new Set(["id", "name", "title", "publisher", "description", "version", "apiVersion", "license", "tags",
53
- "repository", "homepage", "install", "contributes", "docs", "env", "status", "planNote"]),
54
- };
55
-
56
- /** a subfolder that climbs out of its clone, by the same rules the installers apply */
57
- function escapes(sub: unknown): boolean {
58
- if (typeof sub !== "string" || sub === "") return false;
59
- const s = sub.split("\\").join("/");
60
- return s.startsWith("/") || /^[A-Za-z]:/.test(s) || s.split("/").includes("..");
61
- }
62
-
63
- export interface ValidateOptions {
64
- /** `skill` or `plugin`; when absent it is read from the document's own `kind`, else inferred from a name */
65
- kind?: "skill" | "plugin";
66
- /** the file name, used only to infer the kind when nothing else says */
67
- filename?: string;
68
- }
69
-
70
- export function inferKind(doc: unknown, opts: ValidateOptions = {}): "skill" | "plugin" | undefined {
71
- if (opts.kind !== undefined) return opts.kind;
72
- if (isRecord(doc) && (doc.kind === "skill" || doc.kind === "plugin")) return doc.kind;
73
- const n = (opts.filename ?? "").toLowerCase();
74
- if (n.includes("skill")) return "skill";
75
- if (n.includes("plugin")) return "plugin";
76
- return undefined;
77
- }
78
-
79
- /** Validate one catalog document. `text` is the raw file — parsing is part of what is being checked. */
80
- export function validateCatalog(text: string, opts: ValidateOptions = {}): ValidationReport {
81
- const findings: Finding[] = [];
82
- const add = (path: string, message: string, severity: Severity = "error") => findings.push({ path, message, severity });
83
-
84
- if (text.length > LIMITS.body) {
85
- return { ok: false, kind: opts.kind ?? "skill", accepted: 0, dropped: 0,
86
- findings: [{ path: "", message: `the file is ${Math.round(text.length / 1024)} KB; rovecode reads at most ${LIMITS.body / 1024} KB`, severity: "error" }] };
87
- }
88
-
89
- let doc: unknown;
90
- try { doc = JSON.parse(text); }
91
- catch (e) { return { ok: false, kind: opts.kind ?? "skill", accepted: 0, dropped: 0,
92
- findings: [{ path: "", message: `not valid JSON: ${e instanceof Error ? e.message : String(e)}`, severity: "error" }] }; }
93
-
94
- const kind = inferKind(doc, opts);
95
- if (kind === undefined) {
96
- return { ok: false, kind: "skill", accepted: 0, dropped: 0,
97
- findings: [{ path: "", message: `cannot tell whether this is a skill or a plugin catalog — pass --kind skill|plugin, or give the document a "kind" field`, severity: "error" }] };
98
- }
99
-
100
- if (!isRecord(doc)) {
101
- return { ok: false, kind, accepted: 0, dropped: 0, findings: [{ path: "", message: "the top level must be an object", severity: "error" }] };
102
- }
103
- if (doc.version === undefined) add("version", "missing; rovecode assumes 1", "warning");
104
- else if (doc.version !== 1) add("version", `is ${JSON.stringify(doc.version)}; this rovecode reads version 1`);
105
- if (!Array.isArray(doc.items)) {
106
- add("items", "missing or not an array — a catalog is { version, items: [...] }");
107
- return { ok: false, kind, accepted: 0, dropped: 0, findings };
108
- }
109
-
110
- const rows = doc.items;
111
- if (rows.length === 0) add("items", "no rows: this catalog would show nothing", "warning");
112
- if (rows.length > LIMITS.items) add("items", `${rows.length} rows; rovecode reads the first ${LIMITS.items} and ignores the rest`, "warning");
113
-
114
- const seen = new Map<string, number>();
115
- let accepted = 0, dropped = 0;
116
-
117
- rows.forEach((raw: unknown, i: number) => {
118
- const at = `items[${i}]`;
119
- const before = findings.length;
120
-
121
- if (!isRecord(raw)) { add(at, "not an object"); dropped++; return; }
122
-
123
- const id = typeof raw.id === "string" ? raw.id : typeof raw.name === "string" ? raw.name : undefined;
124
- if (id === undefined) add(`${at}.id`, "missing (a row needs an id, or a name to use as one)");
125
- else if (!ID.test(id)) add(`${at}.id`, `"${id}" is not a bare slug — lowercase letters, digits, dot, dash, underscore, at most 64`);
126
- else {
127
- const first = seen.get(id);
128
- if (first !== undefined) add(`${at}.id`, `"${id}" is already used by items[${first}]; ids are unique within a kind`);
129
- else seen.set(id, i);
130
- }
131
-
132
- if (typeof raw.description !== "string" || raw.description.trim() === "") add(`${at}.description`, "missing (a row needs one line describing it)");
133
- else if (raw.description.length > LIMITS.desc) add(`${at}.description`, `${raw.description.length} characters; will be cut to ${LIMITS.desc}`, "warning");
134
-
135
- for (const k of ["title", "publisher", "version", "license", "repository", "homepage"] as const) {
136
- const v = raw[k];
137
- if (v !== undefined && typeof v !== "string") add(`${at}.${k}`, `must be a string, not ${Array.isArray(v) ? "an array" : typeof v}`, "warning");
138
- else if (typeof v === "string" && v.length > LIMITS.str) add(`${at}.${k}`, `${v.length} characters; will be cut to ${LIMITS.str}`, "warning");
139
- }
140
- if (raw.publisher === undefined) add(`${at}.publisher`, 'missing; the row will say "unknown"', "warning");
141
-
142
- if (raw.tags !== undefined) {
143
- if (!Array.isArray(raw.tags)) add(`${at}.tags`, "must be an array of strings", "warning");
144
- else if (raw.tags.length > LIMITS.list) add(`${at}.tags`, `${raw.tags.length} tags; only the first ${LIMITS.list} are read`, "warning");
145
- }
146
-
147
- validateInstall(kind, raw, at, add);
148
- validateDocs(raw.docs, `${at}.docs`, add);
149
-
150
- for (const key of Object.keys(raw)) {
151
- if (!KNOWN_KEYS[kind].has(key)) add(`${at}.${key}`, "not a field rovecode reads — a typo, or something it will ignore", "warning");
152
- }
153
-
154
- // The authority, not us: whatever we found or missed, the real reader decides whether this row loads.
155
- const notes: string[] = [];
156
- const item = itemFromCatalog(kind, raw, notes);
157
- const errorsHere = findings.slice(before).some((f) => f.severity === "error");
158
- if (item === null) {
159
- dropped++;
160
- if (!errorsHere) add(at, `rovecode drops this row${notes.length ? `: ${notes[notes.length - 1]}` : ""} — the checks above did not explain why, which is a gap in this validator`);
161
- } else {
162
- if (errorsHere) add(at, "the row loads despite the errors above; treat them as the more careful reading", "warning");
163
- accepted++;
164
- }
165
- });
166
-
167
- return { ok: findings.every((f) => f.severity !== "error"), kind, accepted, dropped, findings };
168
- }
169
-
170
- function validateInstall(kind: "skill" | "plugin", raw: Record<string, unknown>, at: string,
171
- add: (p: string, m: string, s?: Severity) => void): void {
172
- if (kind === "skill") {
173
- const source = isRecord(raw.source) ? raw.source : undefined;
174
- const files = Array.isArray(raw.files) ? raw.files : undefined;
175
- if (source === undefined && files === undefined) {
176
- add(`${at}.source`, "missing: a skill needs either source.git (a repository) or files (the text itself)");
177
- return;
178
- }
179
- if (source !== undefined) {
180
- if (typeof source.git !== "string" || !/^https?:\/\//.test(source.git)) add(`${at}.source.git`, "must be an http(s) repository URL");
181
- if (escapes(source.subfolder)) add(`${at}.source.subfolder`, `"${String(source.subfolder)}" climbs out of the clone`);
182
- }
183
- files?.forEach((f: unknown, j: number) => {
184
- const p = isRecord(f) ? f.path : undefined;
185
- if (typeof p !== "string" || p === "") add(`${at}.files[${j}].path`, "missing");
186
- else if (escapes(p)) add(`${at}.files[${j}].path`, `"${p}" writes outside the skill's folder`);
187
- if (isRecord(f) && typeof f.text !== "string") add(`${at}.files[${j}].text`, "missing (the file's contents)");
188
- });
189
- if (files !== undefined && files.length > LIMITS.list) add(`${at}.files`, `${files.length} files; only the first ${LIMITS.list} are read`, "warning");
190
- return;
191
- }
192
-
193
- const install = isRecord(raw.install) ? raw.install : undefined;
194
- if (install === undefined) { add(`${at}.install`, "missing: a plugin needs install.source (a repository URL or a folder)"); return; }
195
- if (typeof install.source !== "string" || install.source === "") { add(`${at}.install.source`, "missing"); return; }
196
- if (install.git === true && !/^https?:\/\//.test(install.source)) add(`${at}.install.source`, `"${install.source}" is marked git: true but is not an http(s) URL`);
197
- if (escapes(install.subfolder)) add(`${at}.install.subfolder`, `"${String(install.subfolder)}" climbs out of the clone`);
198
- // Not an error, and said plainly: rovecode's own three plugins all live in a subfolder, so a row without
199
- // one is claiming the manifest sits at the repository root — right sometimes, a mistake more often.
200
- if (install.subfolder === undefined) add(`${at}.install.subfolder`, "absent: the plugin.json must then be at the repository root", "warning");
201
- }
202
-
203
- function validateDocs(docs: unknown, at: string, add: (p: string, m: string, s?: Severity) => void): void {
204
- if (docs === undefined) return; // a row without documentation is a normal row
205
- if (!isRecord(docs)) { add(at, "must be an object", "warning"); return; }
206
- if (typeof docs.source !== "string" || !/^https?:\/\//.test(docs.source)) add(`${at}.source`, "must be the http(s) URL the document was read from — the docs are dropped without it", "warning");
207
- if (docs.format !== "markdown") add(`${at}.format`, `must be "markdown"; the docs are dropped otherwise`, "warning");
208
- if (typeof docs.body !== "string" || docs.body.trim() === "") add(`${at}.body`, "empty: the docs are dropped and the row keeps no documentation", "warning");
209
- else if (Buffer.byteLength(docs.body, "utf8") > LIMITS.docs) add(`${at}.body`, `${Math.round(Buffer.byteLength(docs.body, "utf8") / 1024)} KB; will be cut to ${LIMITS.docs / 1024} KB`, "warning");
210
- if (docs.truncated === true && typeof docs.bytes === "number" && typeof docs.body === "string"
211
- && docs.bytes <= Buffer.byteLength(docs.body, "utf8")) {
212
- add(`${at}.bytes`, "marked truncated, but bytes is not larger than the body — bytes should be the size BEFORE truncation", "warning");
213
- }
214
- }
215
-
216
- /** The report as lines, for a terminal. Errors first: an author fixes those, warnings are a second pass. */
217
- export function reportLines(r: ValidationReport, where: string): string[] {
218
- const errors = r.findings.filter((f) => f.severity === "error");
219
- const warnings = r.findings.filter((f) => f.severity === "warning");
220
- const lines = [`${where}: ${r.kind} catalog — ${r.ok ? "valid" : "INVALID"}`];
221
- lines.push(` ${r.accepted} row${r.accepted === 1 ? "" : "s"} would load, ${r.dropped} dropped, ${errors.length} error${errors.length === 1 ? "" : "s"}, ${warnings.length} warning${warnings.length === 1 ? "" : "s"}`);
222
- for (const f of [...errors, ...warnings]) {
223
- lines.push(` ${f.severity === "error" ? "error " : "warning"} ${f.path === "" ? "(file)" : f.path}: ${f.message}`);
224
- }
225
- if (r.ok && warnings.length > 0) lines.push(" warnings do not stop a catalog loading; they are fields that will not survive as written");
226
- return lines;
227
- }