rovecode 0.3.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 (334) hide show
  1. package/LICENSE +662 -0
  2. package/README.md +737 -0
  3. package/THIRD_PARTY_NOTICES.md +268 -0
  4. package/bin/rovecode.ts +21 -0
  5. package/package.json +56 -0
  6. package/src/acp/server.ts +374 -0
  7. package/src/cli/auth-login.ts +122 -0
  8. package/src/cli/connect.ts +244 -0
  9. package/src/cli/context-cmd.ts +199 -0
  10. package/src/cli/dispatch.ts +82 -0
  11. package/src/cli/doctor.ts +362 -0
  12. package/src/cli/export.ts +276 -0
  13. package/src/cli/help.ts +293 -0
  14. package/src/cli/is-tui-invocation.ts +8 -0
  15. package/src/cli/main.ts +583 -0
  16. package/src/cli/market-cmd.ts +658 -0
  17. package/src/cli/mcp-login.ts +141 -0
  18. package/src/cli/mcp-market-cmd.ts +302 -0
  19. package/src/cli/output.ts +382 -0
  20. package/src/cli/repl.ts +250 -0
  21. package/src/cli/repomap-root.ts +14 -0
  22. package/src/cli/resume.ts +57 -0
  23. package/src/cli/run-flags.ts +43 -0
  24. package/src/cli/run-limits.ts +78 -0
  25. package/src/cli/runtime.ts +931 -0
  26. package/src/cli/session-arg.ts +30 -0
  27. package/src/cli/sessions-cmd.ts +145 -0
  28. package/src/cli/setup.ts +153 -0
  29. package/src/cli/skills-cmd.ts +194 -0
  30. package/src/cli/start-chat.ts +65 -0
  31. package/src/cli/trust-cmd.ts +52 -0
  32. package/src/coding/bash.ts +148 -0
  33. package/src/coding/checkpoints.ts +327 -0
  34. package/src/coding/diff.ts +138 -0
  35. package/src/coding/files.ts +341 -0
  36. package/src/coding/hashline.ts +274 -0
  37. package/src/coding/lsp-gate.ts +254 -0
  38. package/src/coding/lsp-servers.ts +147 -0
  39. package/src/coding/lsp.ts +283 -0
  40. package/src/coding/repomap-cache.ts +99 -0
  41. package/src/coding/repomap-files.ts +192 -0
  42. package/src/coding/repomap.ts +481 -0
  43. package/src/core/agents.ts +255 -0
  44. package/src/core/compaction.ts +259 -0
  45. package/src/core/config.ts +289 -0
  46. package/src/core/context-report.ts +228 -0
  47. package/src/core/context.ts +60 -0
  48. package/src/core/count-remote.ts +107 -0
  49. package/src/core/execpolicy-rules.ts +196 -0
  50. package/src/core/execpolicy.ts +385 -0
  51. package/src/core/executor.ts +454 -0
  52. package/src/core/guardrails.ts +400 -0
  53. package/src/core/hooks.ts +411 -0
  54. package/src/core/images.ts +230 -0
  55. package/src/core/intro.ts +266 -0
  56. package/src/core/loop.ts +567 -0
  57. package/src/core/modes.ts +372 -0
  58. package/src/core/orchestrator.ts +245 -0
  59. package/src/core/proc-group.ts +48 -0
  60. package/src/core/project-trust.ts +98 -0
  61. package/src/core/reflection.ts +165 -0
  62. package/src/core/sandbox-config.ts +186 -0
  63. package/src/core/session-id.ts +24 -0
  64. package/src/core/session-images.ts +73 -0
  65. package/src/core/session-ops.ts +183 -0
  66. package/src/core/session-text.ts +29 -0
  67. package/src/core/session.ts +469 -0
  68. package/src/core/settings.ts +170 -0
  69. package/src/core/tasks.ts +646 -0
  70. package/src/core/token-scale.ts +108 -0
  71. package/src/core/tools.ts +309 -0
  72. package/src/core/trust.ts +104 -0
  73. package/src/core/types.ts +330 -0
  74. package/src/core/update-check.ts +171 -0
  75. package/src/core/usage.ts +204 -0
  76. package/src/core/validate.ts +121 -0
  77. package/src/core/verify-gate.ts +159 -0
  78. package/src/core/verify.ts +236 -0
  79. package/src/core/voice.ts +158 -0
  80. package/src/core/win-job.ts +183 -0
  81. package/src/core/workspace.ts +184 -0
  82. package/src/design/audit.ts +797 -0
  83. package/src/design/direction.ts +190 -0
  84. package/src/design/rules.ts +157 -0
  85. package/src/eval/bench.ts +150 -0
  86. package/src/eval/gauntlet-runner.ts +215 -0
  87. package/src/eval/gauntlet-support.ts +84 -0
  88. package/src/eval/gauntlet-wave3.ts +269 -0
  89. package/src/eval/gauntlet-wave4.ts +217 -0
  90. package/src/eval/gauntlet.ts +253 -0
  91. package/src/index.ts +17 -0
  92. package/src/lanes/agy.ts +95 -0
  93. package/src/lanes/approval.ts +24 -0
  94. package/src/lanes/claude.ts +129 -0
  95. package/src/lanes/codex.ts +127 -0
  96. package/src/lanes/events.ts +130 -0
  97. package/src/lanes/job.ts +142 -0
  98. package/src/lanes/opencode.ts +122 -0
  99. package/src/lanes/process.ts +184 -0
  100. package/src/lanes/progress.ts +183 -0
  101. package/src/lanes/registry.ts +178 -0
  102. package/src/lanes/runner.ts +124 -0
  103. package/src/lanes/types.ts +112 -0
  104. package/src/market/catalogs/mcp-docs.json +111 -0
  105. package/src/market/catalogs/plugins.json +111 -0
  106. package/src/market/catalogs/skills.json +478 -0
  107. package/src/market/clone.ts +72 -0
  108. package/src/market/context-cost.ts +121 -0
  109. package/src/market/digest.ts +106 -0
  110. package/src/market/index.ts +22 -0
  111. package/src/market/install.ts +578 -0
  112. package/src/market/manifest.ts +187 -0
  113. package/src/market/prereq.ts +145 -0
  114. package/src/market/registry.ts +363 -0
  115. package/src/market/resolve.ts +111 -0
  116. package/src/market/types.ts +236 -0
  117. package/src/market/validate.ts +227 -0
  118. package/src/mcp/client.ts +449 -0
  119. package/src/mcp/config.ts +252 -0
  120. package/src/mcp/local-package.ts +211 -0
  121. package/src/mcp/market-catalog.ts +84 -0
  122. package/src/mcp/market-install.ts +289 -0
  123. package/src/mcp/market.ts +362 -0
  124. package/src/mcp/oauth.ts +251 -0
  125. package/src/mcp/prompts-resources.ts +249 -0
  126. package/src/mcp/shared.ts +149 -0
  127. package/src/mcp/status.ts +67 -0
  128. package/src/mcp/tools.ts +275 -0
  129. package/src/mcp/transport.ts +122 -0
  130. package/src/mcp/trust.ts +25 -0
  131. package/src/memory/blocks.ts +278 -0
  132. package/src/memory/recall.ts +355 -0
  133. package/src/memory/scope.ts +182 -0
  134. package/src/memory/store.ts +105 -0
  135. package/src/memory/tools.ts +99 -0
  136. package/src/plugins/cli.ts +119 -0
  137. package/src/plugins/discover.ts +108 -0
  138. package/src/plugins/index.ts +50 -0
  139. package/src/plugins/install.ts +184 -0
  140. package/src/plugins/load.ts +124 -0
  141. package/src/plugins/manifest.ts +92 -0
  142. package/src/plugins/state.ts +83 -0
  143. package/src/providers/auth.ts +408 -0
  144. package/src/providers/cache.ts +223 -0
  145. package/src/providers/catalog-local.ts +160 -0
  146. package/src/providers/catalog.ts +421 -0
  147. package/src/providers/middleware-context.ts +86 -0
  148. package/src/providers/middleware.ts +373 -0
  149. package/src/providers/model-list.ts +23 -0
  150. package/src/providers/models-index.json +1 -0
  151. package/src/providers/oauth/common.ts +105 -0
  152. package/src/providers/oauth/device-code.ts +107 -0
  153. package/src/providers/oauth/github-copilot.ts +146 -0
  154. package/src/providers/oauth/loopback.ts +158 -0
  155. package/src/providers/oauth/openai.ts +163 -0
  156. package/src/providers/oauth/openrouter.ts +89 -0
  157. package/src/providers/oauth/pkce.ts +45 -0
  158. package/src/providers/oauth/registry.ts +39 -0
  159. package/src/providers/oauth/seam.ts +89 -0
  160. package/src/providers/profile-glm53.ts +111 -0
  161. package/src/providers/profile-sonnet5-persona.ts +65 -0
  162. package/src/providers/profile-sonnet5-voice.ts +23 -0
  163. package/src/providers/profiles.ts +156 -0
  164. package/src/providers/provider-config.ts +311 -0
  165. package/src/providers/registry.ts +333 -0
  166. package/src/providers/responses.ts +209 -0
  167. package/src/providers/retry.ts +234 -0
  168. package/src/providers/router.ts +294 -0
  169. package/src/providers/sse.ts +26 -0
  170. package/src/providers/stream-errors.ts +117 -0
  171. package/src/providers/stream.ts +566 -0
  172. package/src/providers/thinking.ts +189 -0
  173. package/src/providers/wire-messages.ts +129 -0
  174. package/src/providers/wire-responses.ts +79 -0
  175. package/src/providers/wire-select.ts +53 -0
  176. package/src/server/http.ts +291 -0
  177. package/src/server/openapi.ts +246 -0
  178. package/src/sextant/card-hits.ts +102 -0
  179. package/src/sextant/card-keys.ts +55 -0
  180. package/src/sextant/context-source.ts +157 -0
  181. package/src/sextant/crew-cards.ts +350 -0
  182. package/src/sextant/draw-agents.ts +273 -0
  183. package/src/sextant/draw-code.ts +388 -0
  184. package/src/sextant/draw-context.ts +222 -0
  185. package/src/sextant/draw-frame.ts +164 -0
  186. package/src/sextant/draw-market.ts +573 -0
  187. package/src/sextant/draw-messages.ts +386 -0
  188. package/src/sextant/draw-pet.ts +230 -0
  189. package/src/sextant/draw-plan.ts +187 -0
  190. package/src/sextant/draw-tabs.ts +85 -0
  191. package/src/sextant/draw-util.ts +65 -0
  192. package/src/sextant/draw-wizard.ts +378 -0
  193. package/src/sextant/engine.ts +230 -0
  194. package/src/sextant/frame-hits.ts +25 -0
  195. package/src/sextant/frame.ts +101 -0
  196. package/src/sextant/git-status.ts +197 -0
  197. package/src/sextant/grid.ts +59 -0
  198. package/src/sextant/input.ts +119 -0
  199. package/src/sextant/keys.ts +521 -0
  200. package/src/sextant/layout.ts +86 -0
  201. package/src/sextant/local-commands.ts +169 -0
  202. package/src/sextant/market-source.ts +287 -0
  203. package/src/sextant/mentions.ts +200 -0
  204. package/src/sextant/message-hits.ts +26 -0
  205. package/src/sextant/model.ts +387 -0
  206. package/src/sextant/overlays.ts +456 -0
  207. package/src/sextant/panel-hits.ts +38 -0
  208. package/src/sextant/pet.ts +399 -0
  209. package/src/sextant/screen.ts +324 -0
  210. package/src/sextant/scroll-hits.ts +66 -0
  211. package/src/sextant/scrollbar.ts +82 -0
  212. package/src/sextant/sextant-bridge.ts +174 -0
  213. package/src/sextant/sextant-cards.ts +142 -0
  214. package/src/sextant/sextant-diff-base.ts +63 -0
  215. package/src/sextant/sextant-files.ts +154 -0
  216. package/src/sextant/sextant-frame-loop.ts +335 -0
  217. package/src/sextant/sextant-renderer.ts +574 -0
  218. package/src/sextant/sextant-repo.ts +140 -0
  219. package/src/sextant/theme.ts +66 -0
  220. package/src/sextant/tool-rows.ts +189 -0
  221. package/src/sextant/types.ts +493 -0
  222. package/src/skills/index.ts +387 -0
  223. package/src/skills/pack.ts +220 -0
  224. package/src/skills/spec.ts +162 -0
  225. package/src/skills/tools.ts +69 -0
  226. package/src/skills/versioned.ts +227 -0
  227. package/src/telemetry/otel-export.ts +122 -0
  228. package/src/telemetry/otel-lanes.ts +89 -0
  229. package/src/telemetry/otel-logs.ts +131 -0
  230. package/src/telemetry/otel-metrics.ts +136 -0
  231. package/src/telemetry/otel.ts +397 -0
  232. package/src/telemetry/otlp.ts +76 -0
  233. package/src/tools/ask-user.ts +156 -0
  234. package/src/tools/bash-bg.ts +94 -0
  235. package/src/tools/bash-jobs.ts +237 -0
  236. package/src/tools/design.ts +151 -0
  237. package/src/tools/evalcell.ts +338 -0
  238. package/src/tools/html-text.ts +139 -0
  239. package/src/tools/provider.ts +149 -0
  240. package/src/tools/task.ts +250 -0
  241. package/src/tools/todo.ts +320 -0
  242. package/src/tools/webfetch.ts +332 -0
  243. package/src/tools/websearch.ts +359 -0
  244. package/src/tui/agents-cmd.ts +41 -0
  245. package/src/tui/app.ts +749 -0
  246. package/src/tui/attach.ts +127 -0
  247. package/src/tui/boot-notes.ts +41 -0
  248. package/src/tui/builtin-prompts.ts +59 -0
  249. package/src/tui/checkpoints-cmd.ts +70 -0
  250. package/src/tui/clipboard-image.ts +81 -0
  251. package/src/tui/clipboard.ts +78 -0
  252. package/src/tui/commands.ts +283 -0
  253. package/src/tui/config-view.ts +53 -0
  254. package/src/tui/context-cmds.ts +282 -0
  255. package/src/tui/cost.ts +108 -0
  256. package/src/tui/crash-guard.ts +173 -0
  257. package/src/tui/focus-terminal.ts +34 -0
  258. package/src/tui/git-cmds.ts +273 -0
  259. package/src/tui/git-plain.ts +58 -0
  260. package/src/tui/info-cmd.ts +150 -0
  261. package/src/tui/input-plain.ts +76 -0
  262. package/src/tui/mcp-cmd.ts +128 -0
  263. package/src/tui/memory-note.ts +77 -0
  264. package/src/tui/modes-cmd.ts +45 -0
  265. package/src/tui/notify-seq.ts +100 -0
  266. package/src/tui/notify.ts +318 -0
  267. package/src/tui/overlays.ts +97 -0
  268. package/src/tui/pi-renderer.ts +428 -0
  269. package/src/tui/providers-cmd.ts +377 -0
  270. package/src/tui/reasoning-view.ts +56 -0
  271. package/src/tui/renderer.ts +128 -0
  272. package/src/tui/replay-marker.ts +29 -0
  273. package/src/tui/session-cmd.ts +148 -0
  274. package/src/tui/session-manage.ts +95 -0
  275. package/src/tui/sextant-attach.ts +102 -0
  276. package/src/tui/sextant-io.ts +202 -0
  277. package/src/tui/sextant-smoke.ts +110 -0
  278. package/src/tui/shell-cmd.ts +158 -0
  279. package/src/tui/smoke.ts +72 -0
  280. package/src/tui/staged-terminal.ts +50 -0
  281. package/src/tui/startup.ts +12 -0
  282. package/src/tui/theme.ts +59 -0
  283. package/src/tui/todo-label.ts +7 -0
  284. package/src/tui/trust-card.ts +107 -0
  285. package/src/tui/tui-commands.ts +87 -0
  286. package/tsconfig.json +30 -0
  287. package/vendor/pi-tui/LICENSE +21 -0
  288. package/vendor/pi-tui/PATCHES.md +12 -0
  289. package/vendor/pi-tui/PROVENANCE.md +12 -0
  290. package/vendor/pi-tui/README.upstream.md +854 -0
  291. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  292. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  293. package/vendor/pi-tui/src/alt-screen-search.ts +158 -0
  294. package/vendor/pi-tui/src/autocomplete.ts +827 -0
  295. package/vendor/pi-tui/src/components/alt-screen-flash.ts +52 -0
  296. package/vendor/pi-tui/src/components/box.ts +138 -0
  297. package/vendor/pi-tui/src/components/cancellable-loader.ts +41 -0
  298. package/vendor/pi-tui/src/components/editor.ts +2364 -0
  299. package/vendor/pi-tui/src/components/h-stack.ts +45 -0
  300. package/vendor/pi-tui/src/components/image.ts +128 -0
  301. package/vendor/pi-tui/src/components/input.ts +448 -0
  302. package/vendor/pi-tui/src/components/loader.ts +93 -0
  303. package/vendor/pi-tui/src/components/markdown.ts +1016 -0
  304. package/vendor/pi-tui/src/components/scroll-view.ts +217 -0
  305. package/vendor/pi-tui/src/components/select-list.ts +230 -0
  306. package/vendor/pi-tui/src/components/settings-list.ts +277 -0
  307. package/vendor/pi-tui/src/components/spacer.ts +29 -0
  308. package/vendor/pi-tui/src/components/stack.ts +155 -0
  309. package/vendor/pi-tui/src/components/text.ts +108 -0
  310. package/vendor/pi-tui/src/components/truncated-text.ts +66 -0
  311. package/vendor/pi-tui/src/components/v-stack.ts +34 -0
  312. package/vendor/pi-tui/src/editor-component.ts +75 -0
  313. package/vendor/pi-tui/src/fuzzy.ts +138 -0
  314. package/vendor/pi-tui/src/index.ts +149 -0
  315. package/vendor/pi-tui/src/keybindings.ts +321 -0
  316. package/vendor/pi-tui/src/keys.ts +1402 -0
  317. package/vendor/pi-tui/src/kill-ring.ts +47 -0
  318. package/vendor/pi-tui/src/latex.ts +1381 -0
  319. package/vendor/pi-tui/src/layout-node.ts +52 -0
  320. package/vendor/pi-tui/src/layout.ts +411 -0
  321. package/vendor/pi-tui/src/native-modifiers.ts +60 -0
  322. package/vendor/pi-tui/src/native-module-path.ts +32 -0
  323. package/vendor/pi-tui/src/stdin-buffer.ts +445 -0
  324. package/vendor/pi-tui/src/terminal-colors.ts +74 -0
  325. package/vendor/pi-tui/src/terminal-image.ts +701 -0
  326. package/vendor/pi-tui/src/terminal.ts +554 -0
  327. package/vendor/pi-tui/src/tui-alt-screen.ts +1379 -0
  328. package/vendor/pi-tui/src/tui-main-screen.ts +655 -0
  329. package/vendor/pi-tui/src/tui.ts +1264 -0
  330. package/vendor/pi-tui/src/undo-stack.ts +29 -0
  331. package/vendor/pi-tui/src/utils.ts +1327 -0
  332. package/vendor/pi-tui/src/word-navigation.ts +118 -0
  333. package/vendor/pi-tui/test/test-themes.ts +39 -0
  334. package/vendor/pi-tui/test/virtual-terminal.ts +219 -0
@@ -0,0 +1,57 @@
1
+ /** Which session `rovecode` reopens at boot, from argv.
2
+ *
3
+ * Three spellings, one rule — an explicit id always wins:
4
+ * rovecode --resume <id> that session (a unique prefix resolves in the TUI, session-cmd.ts resolveBootSession)
5
+ * rovecode --resume the newest session that holds something
6
+ * rovecode --continue the same — the flag every CLI that got this right converged on
7
+ * `--resume` followed by another flag (`rovecode --resume --yolo`) is `--resume` with no id, never "resume the
8
+ * session named --yolo".
9
+ *
10
+ * Two things changed on 2026-09-07 (the aion sessions port, and Berkay's complaint that a wrong id silently gave
11
+ * him an empty session): a typed id that is invalid (`../x`), unknown or ambiguous is REFUSED before the TUI boots
12
+ * — exit 2, one stderr line with the reason, through the one resolver every session verb uses (cli/session-arg.ts →
13
+ * core/session-ops.ts). It used to start a brand-new session under the typed name. And with nothing to continue
14
+ * from, the TUI still starts fresh (there is nothing else to do) but SAYS so on the startup card (`note`) instead of
15
+ * looking exactly like a resumed session that happened to be empty. */
16
+
17
+ import { newestSession } from "../core/session.ts";
18
+ import { resolveSessionArg } from "./session-arg.ts";
19
+
20
+ export interface ResumeRequest {
21
+ /** the id or prefix the user named */
22
+ id?: string;
23
+ /** `--continue`, or `--resume` with no id: reopen the newest non-empty session */
24
+ newest: boolean;
25
+ }
26
+
27
+ export function parseResume(argv: readonly string[]): ResumeRequest {
28
+ const ix = argv.indexOf("--resume");
29
+ const arg = ix !== -1 ? argv[ix + 1] : undefined;
30
+ const id = arg !== undefined && !arg.startsWith("-") ? arg : undefined;
31
+ return { ...(id !== undefined ? { id } : {}), newest: id === undefined && (ix !== -1 || argv.includes("--continue")) };
32
+ }
33
+
34
+ export interface BootSession {
35
+ /** the session to open; undefined = a fresh one */
36
+ id?: string;
37
+ /** what the startup card says when the request could not be honoured as a resume (nothing to continue from) */
38
+ note?: string;
39
+ }
40
+
41
+ export const NOTHING_TO_CONTINUE = "nothing to continue from — this is a new session";
42
+
43
+ /** The session to boot with. A typed id goes through the one resolver (validity, exact, unique prefix; anything
44
+ * else is `fail`, which by default exits 2 with one stderr line). `--continue` / bare `--resume` with no session
45
+ * holding anything starts fresh WITH a note. Reads only: nothing is created here. */
46
+ export function resolveBoot(argv: readonly string[], sessionsDir: string, fail?: (msg: string) => never): BootSession {
47
+ const r = parseResume(argv);
48
+ if (r.id !== undefined) return { id: resolveSessionArg("--resume", sessionsDir, r.id, fail) };
49
+ if (!r.newest) return {};
50
+ const newest = newestSession(sessionsDir);
51
+ return newest ? { id: newest.id } : { note: NOTHING_TO_CONTINUE };
52
+ }
53
+
54
+ /** The session id to boot with, or undefined for a fresh one (the id half of resolveBoot). */
55
+ export function resolveResume(argv: readonly string[], sessionsDir: string, fail?: (msg: string) => never): string | undefined {
56
+ return resolveBoot(argv, sessionsDir, fail).id;
57
+ }
@@ -0,0 +1,43 @@
1
+ /** `--add-dir <dir>` (ported from the Nimbus harness, PORT #81; the rest of Nimbus's run-flags.ts — --continue, --resume,
2
+ * --cwd, --model — rovecode already has in cli/resume.ts, cli/run-limits.ts and main.ts, so only the flag that was
3
+ * missing lives here).
4
+ *
5
+ * Repeatable, `--add-dir=<dir>` form too, EVERY occurrence of argv (unlike the last-wins value flags), each resolved
6
+ * against `base` — the LAUNCH dir — and validated NOW, before any boot: a missing path or a file is
7
+ * `--add-dir "<v>" is not a directory`, a filesystem root the "would disable the workspace boundary — pass --yolo"
8
+ * line (core/workspace.ts rootProblem), a missing or flag-shaped value `--add-dir needs a value`. Exact duplicates
9
+ * collapse here; canonical duplicates / nesting are dropped with a note by resolveRoots at boot. A usage error is
10
+ * exit 2 and leaves no session dir behind; `fail` is injectable for tests. `--add-dir` must also sit in
11
+ * cli/dispatch.ts VALUE_FLAGS so its value never becomes the command or a prompt word — the --max-cost bug class. */
12
+
13
+ import { resolve } from "node:path";
14
+ import { rootProblem } from "../core/workspace.ts";
15
+
16
+ export const ADD_DIR_FLAG = "--add-dir";
17
+ export const ADD_DIR_USAGE = 'usage: rovecode [run] [--add-dir <dir>]… — an extra workspace root beside the cwd (repeatable; --add-dir=<dir> too)';
18
+
19
+ const usageExit = (msg: string): never => {
20
+ process.stderr.write(`error: ${msg} — ${ADD_DIR_USAGE}\n`); // ONE stderr line, like the sibling usage errors
21
+ return process.exit(2);
22
+ };
23
+
24
+ /** a token that is a flag, not a value: `-x`, `--x` (a lone `-` is stdin, a value) */
25
+ const isFlagToken = (a: string): boolean => a.length > 1 && a.startsWith("-");
26
+
27
+ export function parseAddDirs(argv: readonly string[], fail: (msg: string) => never = usageExit, base = process.cwd()): string[] {
28
+ const args = argv.slice(2);
29
+ const out: string[] = [];
30
+ for (let i = 0; i < args.length; i++) {
31
+ const a = args[i]!;
32
+ let v: string | undefined;
33
+ if (a === ADD_DIR_FLAG) { v = args[i + 1]; if (v === undefined || isFlagToken(v)) return fail(`${ADD_DIR_FLAG} needs a value`); i++; }
34
+ else if (a.startsWith(`${ADD_DIR_FLAG}=`)) v = a.slice(ADD_DIR_FLAG.length + 1);
35
+ else continue;
36
+ if (v.trim().length === 0) return fail(`${ADD_DIR_FLAG} needs a directory`);
37
+ const abs = resolve(base, v);
38
+ const problem = rootProblem(abs, v);
39
+ if (problem !== undefined) return fail(problem);
40
+ if (!out.includes(abs)) out.push(abs);
41
+ }
42
+ return out;
43
+ }
@@ -0,0 +1,78 @@
1
+ /** The three ceilings on one run — turns, wall-clock seconds and dollars — from flags and environment, in one
2
+ * place so `rovecode run` (flags + a headless default) and the runtime (env, every surface) agree.
3
+ * Why a wall clock at all: a headless run that has finished its files can keep verifying — screenshots,
4
+ * probe pages, pixel measurements — until something external kills it, and a killed run leaves no result
5
+ * object. The loop checks the clock at each turn boundary (core/loop.ts) and ends with status "budget". */
6
+
7
+ export interface RunLimits {
8
+ /** RunConfig.maxTurns override; unset = the runtime default (60) */
9
+ maxTurns?: number;
10
+ /** RunConfig.maxSeconds; unset = no clock */
11
+ maxSeconds?: number;
12
+ /** RunConfig.maxCostUsd; unset = no spend cap */
13
+ maxCostUsd?: number;
14
+ }
15
+
16
+ /** a positive whole number, or undefined for anything else (unset, junk, zero, negative) */
17
+ export function positiveInt(v: string | undefined): number | undefined {
18
+ if (v === undefined) return undefined;
19
+ const n = Number(v.trim());
20
+ return Number.isInteger(n) && n > 0 ? n : undefined;
21
+ }
22
+
23
+ /** a positive amount of money — `0.50`, `2`, `$1.25` (a leading $ is forgiven: it is what people type) — or
24
+ * undefined for anything else. Zero is not an amount: a bare "0" is "off" (the shared OFF set, as for
25
+ * --max-seconds) and "0.00" is a usage error — neither may become a cap that ends every run before its
26
+ * first priced turn, which nobody means. */
27
+ export function positiveUsd(v: string | undefined): number | undefined {
28
+ if (v === undefined) return undefined;
29
+ const n = Number(v.trim().replace(/^\$/, ""));
30
+ return Number.isFinite(n) && n > 0 ? n : undefined;
31
+ }
32
+
33
+ const OFF = new Set(["off", "none", "0", "false"]);
34
+ const flagValue = (argv: readonly string[], name: string): string | undefined => { const i = argv.indexOf(name); return i === -1 ? undefined : argv[i + 1]; };
35
+
36
+ /** `--max-turns N` · `--max-seconds S|off` · `--max-cost D|off`, then ROVECODE_MAX_TURNS / ROVECODE_MAX_SECONDS /
37
+ * ROVECODE_MAX_COST, then `defaultSeconds` (the headless default; omit it for surfaces that want no clock).
38
+ * A flag that is present but not a positive number is a usage error, not a silent default. */
39
+ export function parseRunLimits(argv: readonly string[], env: Record<string, string | undefined>, opts: { defaultSeconds?: number } = {}): RunLimits | { error: string } {
40
+ const out: RunLimits = {};
41
+ // the spend cap first: it has no default and no interaction with the other two
42
+ const cost = flagValue(argv, "--max-cost");
43
+ if (cost !== undefined || argv.includes("--max-cost")) {
44
+ if (cost === undefined || !OFF.has(cost.trim().toLowerCase())) {
45
+ const n = positiveUsd(cost);
46
+ if (n === undefined) return { error: `--max-cost wants a positive amount in dollars (or "off"), not "${cost ?? ""}"` };
47
+ out.maxCostUsd = n;
48
+ }
49
+ } else {
50
+ const envCost = env["ROVECODE_MAX_COST"];
51
+ if (envCost !== undefined && !OFF.has(envCost.trim().toLowerCase())) {
52
+ const n = positiveUsd(envCost);
53
+ if (n !== undefined) out.maxCostUsd = n;
54
+ }
55
+ }
56
+ const turns = flagValue(argv, "--max-turns");
57
+ if (turns !== undefined || argv.includes("--max-turns")) {
58
+ const n = positiveInt(turns);
59
+ if (n === undefined) return { error: `--max-turns wants a positive whole number, not "${turns ?? ""}"` };
60
+ out.maxTurns = n;
61
+ } else {
62
+ const n = positiveInt(env["ROVECODE_MAX_TURNS"]);
63
+ if (n !== undefined) out.maxTurns = n;
64
+ }
65
+ const secs = flagValue(argv, "--max-seconds");
66
+ if (secs !== undefined || argv.includes("--max-seconds")) {
67
+ if (secs !== undefined && OFF.has(secs.trim().toLowerCase())) return out; // an explicit "no clock"
68
+ const n = positiveInt(secs);
69
+ if (n === undefined) return { error: `--max-seconds wants a positive whole number of seconds (or "off"), not "${secs ?? ""}"` };
70
+ out.maxSeconds = n;
71
+ return out;
72
+ }
73
+ const envSecs = env["ROVECODE_MAX_SECONDS"];
74
+ if (envSecs !== undefined && OFF.has(envSecs.trim().toLowerCase())) return out;
75
+ const n = positiveInt(envSecs) ?? opts.defaultSeconds;
76
+ if (n !== undefined) out.maxSeconds = n;
77
+ return out;
78
+ }