@reticlehq/server 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. package/SKILL.md +122 -123
  2. package/dist/command/cli/adhoc-suite.d.ts +14 -0
  3. package/dist/command/cli/adhoc-suite.js +42 -7
  4. package/dist/command/cli/adhoc-verdict.d.ts +18 -2
  5. package/dist/command/cli/adhoc-verdict.js +70 -6
  6. package/dist/command/cli/answers/gate-exit.d.ts +23 -0
  7. package/dist/command/cli/answers/gate-exit.js +45 -0
  8. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  9. package/dist/command/cli/answers/gate-hook-message.js +3 -1
  10. package/dist/command/cli/auth/device-flow.d.ts +13 -2
  11. package/dist/command/cli/auth/device-flow.js +17 -0
  12. package/dist/command/cli/cli-doctor.js +14 -0
  13. package/dist/command/cli/cli-flow-commands.d.ts +0 -11
  14. package/dist/command/cli/cli-flow-commands.js +28 -105
  15. package/dist/command/cli/cli-kill.js +3 -2
  16. package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
  17. package/dist/command/cli/cli-parse-grammar.js +75 -0
  18. package/dist/command/cli/cli-parse-verify.d.ts +2 -0
  19. package/dist/command/cli/cli-parse-verify.js +18 -0
  20. package/dist/command/cli/cli-parse.d.ts +19 -28
  21. package/dist/command/cli/cli-parse.js +82 -182
  22. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  23. package/dist/command/cli/cli-update-commands.js +61 -9
  24. package/dist/command/cli/cli-usage.d.ts +33 -0
  25. package/dist/command/cli/cli-usage.js +287 -0
  26. package/dist/command/cli/cli-verify.d.ts +34 -4
  27. package/dist/command/cli/cli-verify.js +81 -12
  28. package/dist/command/cli/cloud-cli.d.ts +13 -0
  29. package/dist/command/cli/cloud-cli.js +145 -333
  30. package/dist/command/cli/cloud-kit.d.ts +12 -0
  31. package/dist/command/cli/cloud-kit.js +41 -2
  32. package/dist/command/cli/cloud-link.d.ts +57 -0
  33. package/dist/command/cli/cloud-link.js +308 -0
  34. package/dist/command/cli/cloud-login.d.ts +15 -1
  35. package/dist/command/cli/cloud-login.js +67 -13
  36. package/dist/command/cli/context-command.d.ts +1 -0
  37. package/dist/command/cli/context-command.js +33 -0
  38. package/dist/command/cli/daemon-start-options.d.ts +3 -0
  39. package/dist/command/cli/daemon-start-options.js +24 -3
  40. package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
  41. package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
  42. package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
  43. package/dist/command/cli/doctor/doctor-project-line.js +5 -4
  44. package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
  45. package/dist/command/cli/doctor/doctor-rows.js +6 -0
  46. package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
  47. package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
  48. package/dist/command/cli/known-regression.d.ts +13 -0
  49. package/dist/command/cli/known-regression.js +34 -0
  50. package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
  51. package/dist/command/cli/launch/sdk-version-match.js +25 -0
  52. package/dist/command/cli/lifecycle/daemon-lifecycle.js +11 -2
  53. package/dist/command/cli/mcp-command.d.ts +6 -0
  54. package/dist/command/cli/mcp-command.js +13 -5
  55. package/dist/command/cli/ports/port-holder.js +1 -1
  56. package/dist/command/cli/project-name.d.ts +29 -1
  57. package/dist/command/cli/project-name.js +107 -2
  58. package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
  59. package/dist/command/cli/setup-mcp-cli.js +29 -8
  60. package/dist/command/cli/status/status-lines.d.ts +1 -0
  61. package/dist/command/cli/status/status-lines.js +47 -2
  62. package/dist/command/cli/status/workspace-links.d.ts +10 -0
  63. package/dist/command/cli/status/workspace-links.js +29 -0
  64. package/dist/command/cli/try-command.d.ts +96 -0
  65. package/dist/command/cli/try-command.js +362 -0
  66. package/dist/command/cli/tutorial.d.ts +0 -100
  67. package/dist/command/cli/tutorial.js +0 -178
  68. package/dist/command/cli/verify-results.d.ts +19 -0
  69. package/dist/command/cli/verify-results.js +29 -0
  70. package/dist/command/cli-onboarding.d.ts +0 -11
  71. package/dist/command/cli-onboarding.js +0 -15
  72. package/dist/command/cli.d.ts +14 -0
  73. package/dist/command/cli.js +103 -83
  74. package/dist/command/daemon/daemon-resilience.js +4 -4
  75. package/dist/command/daemon/daemon-resolve.d.ts +9 -1
  76. package/dist/command/daemon/daemon-resolve.js +49 -10
  77. package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
  78. package/dist/command/setup/bringup/bridge-port.js +13 -3
  79. package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
  80. package/dist/command/setup/bringup/ensure-daemon.js +9 -2
  81. package/dist/command/setup/init/agent-restart.d.ts +23 -0
  82. package/dist/command/setup/init/agent-restart.js +28 -0
  83. package/dist/command/setup/init/init-port.d.ts +10 -1
  84. package/dist/command/setup/init/init-port.js +22 -2
  85. package/dist/command/setup/init/init-runtime.d.ts +12 -2
  86. package/dist/command/setup/init/init-runtime.js +96 -37
  87. package/dist/command/setup/node-effects.d.ts +2 -0
  88. package/dist/command/setup/node-effects.js +36 -8
  89. package/dist/command/setup/probe/existing-app.d.ts +12 -0
  90. package/dist/command/setup/probe/existing-app.js +14 -0
  91. package/dist/command/setup/remaining-steps.js +1 -1
  92. package/dist/command/setup/run-setup.d.ts +29 -9
  93. package/dist/command/setup/run-setup.js +34 -28
  94. package/dist/command/setup/setup-command.d.ts +23 -0
  95. package/dist/command/setup/setup-command.js +57 -4
  96. package/dist/command/setup/setup-mcp.js +25 -2
  97. package/dist/command/setup/tauri-signing.d.ts +19 -0
  98. package/dist/command/setup/tauri-signing.js +79 -0
  99. package/dist/command/setup/terminal/listeners.d.ts +10 -1
  100. package/dist/command/setup/terminal/listeners.js +37 -6
  101. package/dist/command/update/update-nudge.d.ts +9 -0
  102. package/dist/command/update/update-nudge.js +1 -1
  103. package/dist/command/update/updater.js +11 -15
  104. package/dist/command/version/version-nudge.js +3 -0
  105. package/dist/command/version/version-skew.d.ts +20 -1
  106. package/dist/command/version/version-skew.js +38 -2
  107. package/dist/features/crawl/crawl.js +14 -1
  108. package/dist/features/exhaust/ledger.d.ts +14 -14
  109. package/dist/features/harness/drive-report.d.ts +39 -5
  110. package/dist/features/harness/drive-report.js +271 -54
  111. package/dist/features/harness/drive-runs.d.ts +120 -0
  112. package/dist/features/harness/drive-runs.js +289 -0
  113. package/dist/features/harness/drivers.d.ts +1 -1
  114. package/dist/features/harness/drivers.js +1 -1
  115. package/dist/features/harness/explore/console-pages.d.ts +15 -0
  116. package/dist/features/harness/explore/console-pages.js +146 -0
  117. package/dist/features/harness/explore/disparity.d.ts +19 -0
  118. package/dist/features/harness/explore/disparity.js +71 -0
  119. package/dist/features/harness/explore/fake-app.d.ts +16 -0
  120. package/dist/features/harness/explore/fake-app.js +49 -0
  121. package/dist/features/harness/explore/goal-search.d.ts +26 -0
  122. package/dist/features/harness/explore/goal-search.js +144 -0
  123. package/dist/features/harness/explore/page-tree.d.ts +49 -0
  124. package/dist/features/harness/explore/page-tree.js +148 -0
  125. package/dist/features/harness/explore/site-map.d.ts +34 -0
  126. package/dist/features/harness/explore/site-map.js +150 -0
  127. package/dist/features/harness/goals.d.ts +68 -7
  128. package/dist/features/harness/goals.js +289 -36
  129. package/dist/features/harness/harness.d.ts +30 -1
  130. package/dist/features/harness/harness.js +141 -7
  131. package/dist/features/harness/platform/local-apps.d.ts +36 -3
  132. package/dist/features/harness/platform/local-apps.js +81 -5
  133. package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
  134. package/dist/features/harness/platform/persona-credentials.js +69 -0
  135. package/dist/features/harness/platform/platform-drives.d.ts +61 -0
  136. package/dist/features/harness/platform/platform-drives.js +114 -0
  137. package/dist/features/harness/platform/refusal.d.ts +52 -0
  138. package/dist/features/harness/platform/refusal.js +71 -0
  139. package/dist/features/harness/platform/remote-drive.d.ts +8 -2
  140. package/dist/features/harness/platform/remote-drive.js +22 -9
  141. package/dist/features/harness/platform/script.d.ts +57 -11
  142. package/dist/features/harness/platform/script.js +144 -12
  143. package/dist/features/harness/platform/server-driver.d.ts +17 -4
  144. package/dist/features/harness/platform/server-driver.js +29 -26
  145. package/dist/features/harness/platform-config.d.ts +38 -5
  146. package/dist/features/harness/platform-config.js +93 -8
  147. package/dist/features/harness/route-guess.d.ts +13 -0
  148. package/dist/features/harness/route-guess.js +49 -0
  149. package/dist/features/harness/script/script-run.d.ts +30 -1
  150. package/dist/features/harness/script/script-run.js +355 -48
  151. package/dist/features/license/license.d.ts +2 -2
  152. package/dist/features/visual/step-shots.d.ts +31 -0
  153. package/dist/features/visual/step-shots.js +231 -0
  154. package/dist/features/visual/visual-tools.js +22 -14
  155. package/dist/hooks/client-directory.d.ts +18 -0
  156. package/dist/hooks/client-directory.js +57 -0
  157. package/dist/hooks/coding-agents.d.ts +34 -0
  158. package/dist/hooks/coding-agents.js +117 -0
  159. package/dist/hooks/driven-by.d.ts +21 -2
  160. package/dist/hooks/driven-by.js +42 -2
  161. package/dist/hooks/hook-bus.d.ts +4 -0
  162. package/dist/hooks/hook-bus.js +18 -0
  163. package/dist/index.d.ts +1 -0
  164. package/dist/index.js +26 -183
  165. package/dist/judgement/capsule/capsule-store.d.ts +2 -2
  166. package/dist/judgement/capsule/causal-summary.d.ts +15 -1
  167. package/dist/judgement/capsule/causal-summary.js +33 -0
  168. package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
  169. package/dist/judgement/runs/artifact/build-verification-run.js +20 -4
  170. package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
  171. package/dist/judgement/runs/artifact/gap-summary.js +12 -3
  172. package/dist/judgement/runs/artifact/replay-mapping.js +25 -9
  173. package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
  174. package/dist/judgement/runs/artifact/run-store.js +175 -8
  175. package/dist/judgement/runs/context-tools.js +59 -1
  176. package/dist/judgement/runs/default-run-id.d.ts +3 -0
  177. package/dist/judgement/runs/default-run-id.js +16 -0
  178. package/dist/judgement/runs/drive-run.d.ts +53 -5
  179. package/dist/judgement/runs/drive-run.js +234 -20
  180. package/dist/judgement/runs/resume.d.ts +44 -0
  181. package/dist/judgement/runs/resume.js +137 -0
  182. package/dist/judgement/runs/reticle-runner.js +5 -2
  183. package/dist/judgement/runs/runner-port.d.ts +5 -1
  184. package/dist/judgement/runs/runner-port.js +8 -2
  185. package/dist/judgement/runs/verification-sync.d.ts +2 -0
  186. package/dist/judgement/runs/verification-sync.js +36 -15
  187. package/dist/language/flows/decision.js +2 -1
  188. package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
  189. package/dist/language/flows/fields/flow-secret-field.js +25 -2
  190. package/dist/language/flows/flow-anchor.d.ts +7 -2
  191. package/dist/language/flows/flow-anchor.js +10 -3
  192. package/dist/language/flows/flow-intent.d.ts +1 -0
  193. package/dist/language/flows/flow-journey.d.ts +6 -0
  194. package/dist/language/flows/flow-journey.js +20 -0
  195. package/dist/language/flows/flow-learning.d.ts +4 -0
  196. package/dist/language/flows/flow-learning.js +37 -3
  197. package/dist/language/flows/flow-replay-run.js +16 -18
  198. package/dist/language/flows/flow-replay-types.d.ts +1 -1
  199. package/dist/language/flows/flow-replay.d.ts +18 -1
  200. package/dist/language/flows/flow-replay.js +22 -7
  201. package/dist/language/flows/flow-result.d.ts +22 -1
  202. package/dist/language/flows/flow-result.js +47 -2
  203. package/dist/language/flows/flow-step-runners.d.ts +2 -0
  204. package/dist/language/flows/flow-step-runners.js +12 -6
  205. package/dist/language/flows/flow-store-for-session.js +5 -1
  206. package/dist/language/flows/flow-tools.d.ts +10 -0
  207. package/dist/language/flows/flow-tools.js +61 -62
  208. package/dist/language/flows/flows.d.ts +16 -1
  209. package/dist/language/flows/flows.js +69 -10
  210. package/dist/language/flows/narrow-write.d.ts +7 -0
  211. package/dist/language/flows/narrow-write.js +36 -1
  212. package/dist/language/flows/recording/recording-backtrack.js +14 -1
  213. package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
  214. package/dist/language/flows/recording/tape/recordings.js +60 -10
  215. package/dist/language/flows/replay.js +29 -6
  216. package/dist/language/flows/single-replay.d.ts +7 -0
  217. package/dist/language/flows/single-replay.js +26 -0
  218. package/dist/language/flows/verify-change-tools.d.ts +1 -0
  219. package/dist/language/flows/verify-change-tools.js +200 -193
  220. package/dist/memory/cloud/cloud-config.d.ts +10 -1
  221. package/dist/memory/cloud/cloud-config.js +19 -5
  222. package/dist/memory/cloud/cloud-sync.js +3 -3
  223. package/dist/memory/cloud/harness-config.d.ts +49 -0
  224. package/dist/memory/cloud/harness-config.js +16 -20
  225. package/dist/memory/cloud/memory-scope.d.ts +17 -0
  226. package/dist/memory/cloud/memory-scope.js +22 -0
  227. package/dist/memory/cloud/sync-cycle.d.ts +32 -4
  228. package/dist/memory/cloud/sync-cycle.js +113 -12
  229. package/dist/memory/cloud/sync-daemon.d.ts +11 -0
  230. package/dist/memory/cloud/sync-daemon.js +114 -4
  231. package/dist/memory/cloud/sync-disk.js +26 -2
  232. package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
  233. package/dist/memory/cloud/sync-screenshots.js +105 -0
  234. package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
  235. package/dist/memory/cloud/unsynced-roots.js +166 -0
  236. package/dist/memory/impact/account-watch.d.ts +16 -1
  237. package/dist/memory/impact/account-watch.js +26 -1
  238. package/dist/memory/impact/impact-store.d.ts +1 -4
  239. package/dist/memory/impact/impact-store.js +32 -42
  240. package/dist/memory/intent/inline-intent.d.ts +1 -0
  241. package/dist/memory/intent/intent-shard-store.js +16 -4
  242. package/dist/memory/intent/intent-shard.d.ts +12 -0
  243. package/dist/memory/intent/intent-store.d.ts +1 -0
  244. package/dist/memory/intent/intent-tools.js +42 -2
  245. package/dist/memory/intent/open-intents.d.ts +6 -0
  246. package/dist/memory/intent/open-intents.js +10 -1
  247. package/dist/memory/intent/settled-words.d.ts +24 -0
  248. package/dist/memory/intent/settled-words.js +130 -0
  249. package/dist/memory/journal/drive-flow.d.ts +16 -0
  250. package/dist/memory/journal/drive-flow.js +34 -2
  251. package/dist/memory/journal/drive-run-flush.d.ts +2 -0
  252. package/dist/memory/journal/drive-run-flush.js +20 -5
  253. package/dist/memory/journal/journal-recorder.js +46 -3
  254. package/dist/memory/journal/on-disk/retention.d.ts +5 -0
  255. package/dist/memory/journal/on-disk/retention.js +27 -2
  256. package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
  257. package/dist/memory/journal/session-end.d.ts +15 -1
  258. package/dist/memory/journal/session-end.js +38 -7
  259. package/dist/memory/project/artifact-root-resolver.js +33 -5
  260. package/dist/memory/project/artifact-root.d.ts +4 -3
  261. package/dist/memory/project/artifact-root.js +2 -2
  262. package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
  263. package/dist/memory/project/dir/reticle-dir.js +23 -2
  264. package/dist/memory/project/link-directory.d.ts +15 -0
  265. package/dist/memory/project/link-directory.js +44 -0
  266. package/dist/memory/project/root-claims.d.ts +6 -0
  267. package/dist/memory/project/root-claims.js +39 -0
  268. package/dist/memory/project/session-root.d.ts +2 -0
  269. package/dist/memory/project/session-root.js +9 -1
  270. package/dist/memory/project/sync-status.d.ts +46 -0
  271. package/dist/memory/project/sync-status.js +135 -1
  272. package/dist/memory/recall/memory-tools.js +46 -3
  273. package/dist/memory/recall/platform-memory.d.ts +34 -0
  274. package/dist/memory/recall/platform-memory.js +139 -0
  275. package/dist/memory/recall/project-memory.js +36 -8
  276. package/dist/portal/bridge/bridge.d.ts +9 -5
  277. package/dist/portal/bridge/bridge.js +66 -23
  278. package/dist/portal/bridge/harness-request.d.ts +23 -0
  279. package/dist/portal/bridge/harness-request.js +27 -0
  280. package/dist/portal/bridge/wrong-path.d.ts +17 -0
  281. package/dist/portal/bridge/wrong-path.js +35 -0
  282. package/dist/portal/input/network-mock-tools.js +11 -2
  283. package/dist/portal/input/real-input.d.ts +2 -0
  284. package/dist/portal/input/real-input.js +13 -10
  285. package/dist/portal/pool/browser-pool.d.ts +48 -0
  286. package/dist/portal/pool/browser-pool.js +120 -2
  287. package/dist/portal/pool/context-permissions.d.ts +33 -0
  288. package/dist/portal/pool/context-permissions.js +33 -0
  289. package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
  290. package/dist/portal/pool/lease-marker-stamp.js +49 -0
  291. package/dist/portal/pool/playwright-launcher.d.ts +27 -5
  292. package/dist/portal/pool/playwright-launcher.js +59 -14
  293. package/dist/portal/pool/pool-contract.d.ts +21 -1
  294. package/dist/portal/session/agent-link.d.ts +8 -0
  295. package/dist/portal/session/agent-link.js +17 -0
  296. package/dist/portal/session/command-payload.js +6 -1
  297. package/dist/portal/session/daemon-answers.d.ts +3 -3
  298. package/dist/portal/session/daemon-answers.js +4 -2
  299. package/dist/portal/session/dev-server/dev-command.d.ts +9 -0
  300. package/dist/portal/session/dev-server/dev-command.js +27 -1
  301. package/dist/portal/session/facts/observed-state.d.ts +16 -18
  302. package/dist/portal/session/facts/observed-state.js +46 -3
  303. package/dist/portal/session/human/live-control.d.ts +9 -2
  304. package/dist/portal/session/human/live-control.js +12 -3
  305. package/dist/portal/session/live-control-tools.js +82 -13
  306. package/dist/portal/session/narration-args.d.ts +5 -0
  307. package/dist/portal/session/narration-args.js +17 -0
  308. package/dist/portal/session/no-session-diagnosis.d.ts +1 -1
  309. package/dist/portal/session/no-session-diagnosis.js +31 -9
  310. package/dist/portal/session/no-session-next-action.d.ts +20 -0
  311. package/dist/portal/session/no-session-next-action.js +134 -61
  312. package/dist/portal/session/no-session-watch.js +179 -22
  313. package/dist/portal/session/presence/session-lease.js +1 -1
  314. package/dist/portal/session/recorded-gaps.d.ts +97 -0
  315. package/dist/portal/session/recorded-gaps.js +251 -0
  316. package/dist/portal/session/session-health.d.ts +11 -0
  317. package/dist/portal/session/session-health.js +3 -1
  318. package/dist/portal/session/session-info.js +2 -1
  319. package/dist/portal/session/session-manager.d.ts +18 -0
  320. package/dist/portal/session/session-manager.js +54 -3
  321. package/dist/portal/session/session-successor.d.ts +8 -0
  322. package/dist/portal/session/session-successor.js +1 -1
  323. package/dist/portal/session/session.d.ts +15 -28
  324. package/dist/portal/session/session.js +60 -61
  325. package/dist/portal/session/timing/command-timeout.js +25 -1
  326. package/dist/portal/session/timing/wall-clock.d.ts +1 -1
  327. package/dist/portal/session/timing/wall-clock.js +1 -1
  328. package/dist/portal/session/zero-install-first-call.d.ts +31 -0
  329. package/dist/portal/session/zero-install-first-call.js +88 -0
  330. package/dist/start-options.d.ts +7 -1
  331. package/dist/start-wiring.d.ts +63 -0
  332. package/dist/start-wiring.js +169 -0
  333. package/dist/status-payload.d.ts +13 -1
  334. package/dist/status-payload.js +13 -1
  335. package/dist/surface/http-server.js +7 -1
  336. package/dist/surface/mcp/attach-memory.d.ts +1 -0
  337. package/dist/surface/mcp/attach-memory.js +12 -1
  338. package/dist/surface/mcp/mcp-proxy.d.ts +4 -0
  339. package/dist/surface/mcp/mcp-proxy.js +29 -8
  340. package/dist/surface/mcp/mcp.d.ts +23 -0
  341. package/dist/surface/mcp/mcp.js +34 -4
  342. package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
  343. package/dist/surface/mcp/proxy/drive-calls.js +27 -0
  344. package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
  345. package/dist/surface/mcp/server-instructions.js +22 -14
  346. package/dist/surface/mcp/shared-params.js +1 -1
  347. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  348. package/dist/surface/mcp/surface-vocabulary.js +1 -0
  349. package/dist/surface/tools/account-tool.d.ts +22 -0
  350. package/dist/surface/tools/account-tool.js +157 -0
  351. package/dist/surface/tools/act/act-danger.d.ts +11 -0
  352. package/dist/surface/tools/act/act-danger.js +38 -1
  353. package/dist/surface/tools/act/act-observation.d.ts +33 -0
  354. package/dist/surface/tools/act/act-observation.js +57 -0
  355. package/dist/surface/tools/act/act-preflight.js +25 -2
  356. package/dist/surface/tools/act/act-target.js +8 -3
  357. package/dist/surface/tools/act/resolve-target.d.ts +1 -1
  358. package/dist/surface/tools/act/resolve-target.js +18 -3
  359. package/dist/surface/tools/act/screen-request.d.ts +34 -0
  360. package/dist/surface/tools/act/screen-request.js +113 -0
  361. package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
  362. package/dist/surface/tools/act/sequence-grade.js +13 -2
  363. package/dist/surface/tools/act-sequence-tool.js +136 -27
  364. package/dist/surface/tools/act-tools.d.ts +0 -9
  365. package/dist/surface/tools/act-tools.js +48 -34
  366. package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
  367. package/dist/surface/tools/agent/app-notes-learning.js +80 -0
  368. package/dist/surface/tools/agent/app-notes.d.ts +4 -0
  369. package/dist/surface/tools/agent/app-notes.js +48 -0
  370. package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
  371. package/dist/surface/tools/agent/build-staleness.js +62 -0
  372. package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
  373. package/dist/surface/tools/agent/compact-envelope.js +374 -0
  374. package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
  375. package/dist/surface/tools/agent/for-the-agent.js +44 -0
  376. package/dist/surface/tools/agent/notices.d.ts +50 -0
  377. package/dist/surface/tools/agent/notices.js +152 -0
  378. package/dist/surface/tools/agent/reteach.d.ts +11 -0
  379. package/dist/surface/tools/agent/reteach.js +50 -0
  380. package/dist/surface/tools/args/alias-args.d.ts +5 -0
  381. package/dist/surface/tools/args/alias-args.js +10 -0
  382. package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
  383. package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
  384. package/dist/surface/tools/assert/assert-source.d.ts +15 -0
  385. package/dist/surface/tools/assert/assert-source.js +40 -0
  386. package/dist/surface/tools/assert/assert-verdict.js +25 -15
  387. package/dist/surface/tools/browser-tools.js +43 -1
  388. package/dist/surface/tools/chat-drives.d.ts +75 -0
  389. package/dist/surface/tools/chat-drives.js +265 -21
  390. package/dist/surface/tools/coverage-tools.d.ts +2 -10
  391. package/dist/surface/tools/coverage-tools.js +2 -53
  392. package/dist/surface/tools/dynamic-tools.js +29 -1
  393. package/dist/surface/tools/empty-session-list.d.ts +10 -0
  394. package/dist/surface/tools/empty-session-list.js +45 -5
  395. package/dist/surface/tools/error-recovery.d.ts +8 -0
  396. package/dist/surface/tools/error-recovery.js +69 -5
  397. package/dist/surface/tools/explore-tools.d.ts +23 -2
  398. package/dist/surface/tools/explore-tools.js +377 -74
  399. package/dist/surface/tools/feedback-tools.d.ts +2 -3
  400. package/dist/surface/tools/feedback-tools.js +4 -3
  401. package/dist/surface/tools/gap-novelty.d.ts +3 -1
  402. package/dist/surface/tools/gap-novelty.js +8 -1
  403. package/dist/surface/tools/harness-explore.d.ts +52 -73
  404. package/dist/surface/tools/harness-explore.js +177 -146
  405. package/dist/surface/tools/harness-gate.d.ts +88 -0
  406. package/dist/surface/tools/harness-gate.js +235 -0
  407. package/dist/surface/tools/harness-hunt.d.ts +43 -0
  408. package/dist/surface/tools/harness-hunt.js +215 -0
  409. package/dist/surface/tools/harness-plan.d.ts +5 -0
  410. package/dist/surface/tools/harness-plan.js +7 -0
  411. package/dist/surface/tools/harness-script.d.ts +22 -0
  412. package/dist/surface/tools/harness-script.js +256 -38
  413. package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
  414. package/dist/surface/tools/harness-tab-guard.js +27 -0
  415. package/dist/surface/tools/harness-toolset.d.ts +8 -0
  416. package/dist/surface/tools/harness-toolset.js +27 -4
  417. package/dist/surface/tools/human-notes.d.ts +16 -0
  418. package/dist/surface/tools/human-notes.js +27 -0
  419. package/dist/surface/tools/invoke-tool.d.ts +5 -2
  420. package/dist/surface/tools/invoke-tool.js +220 -93
  421. package/dist/surface/tools/lease-hint.js +26 -5
  422. package/dist/surface/tools/lease-permissions.d.ts +33 -0
  423. package/dist/surface/tools/lease-permissions.js +73 -0
  424. package/dist/surface/tools/lease-readiness.d.ts +38 -0
  425. package/dist/surface/tools/lease-readiness.js +57 -0
  426. package/dist/surface/tools/lease-seed.d.ts +20 -0
  427. package/dist/surface/tools/lease-seed.js +45 -1
  428. package/dist/surface/tools/lease-tools.d.ts +10 -1
  429. package/dist/surface/tools/lease-tools.js +119 -69
  430. package/dist/surface/tools/live-call-text.js +2 -2
  431. package/dist/surface/tools/merge-tools.js +42 -2
  432. package/dist/surface/tools/merged-name-redirect.js +4 -0
  433. package/dist/surface/tools/next-step.d.ts +11 -1
  434. package/dist/surface/tools/next-step.js +73 -18
  435. package/dist/surface/tools/observe-tools.js +43 -5
  436. package/dist/surface/tools/platform-moment.d.ts +60 -0
  437. package/dist/surface/tools/platform-moment.js +132 -4
  438. package/dist/surface/tools/read-tools.js +65 -14
  439. package/dist/surface/tools/reticle-result.d.ts +120 -0
  440. package/dist/surface/tools/reticle-result.js +364 -0
  441. package/dist/surface/tools/tool-kit.d.ts +68 -2
  442. package/dist/surface/tools/tool-kit.js +73 -1
  443. package/dist/surface/tools/tools.js +25 -13
  444. package/dist/telemetry/account-envelope.d.ts +7 -0
  445. package/dist/telemetry/account-envelope.js +61 -0
  446. package/dist/telemetry/agent-client.d.ts +15 -0
  447. package/dist/telemetry/agent-client.js +57 -0
  448. package/dist/telemetry/app-instrumented.js +9 -2
  449. package/dist/telemetry/argument-shape.d.ts +7 -0
  450. package/dist/telemetry/argument-shape.js +86 -1
  451. package/dist/telemetry/automation-hint.d.ts +2 -3
  452. package/dist/telemetry/automation-hint.js +33 -8
  453. package/dist/telemetry/cli-telemetry.js +7 -2
  454. package/dist/telemetry/daemon-telemetry.js +3 -0
  455. package/dist/telemetry/error-fingerprint.d.ts +10 -1
  456. package/dist/telemetry/error-fingerprint.js +61 -5
  457. package/dist/telemetry/feedback-cli.d.ts +0 -13
  458. package/dist/telemetry/feedback-cli.js +0 -77
  459. package/dist/telemetry/feedback.d.ts +13 -0
  460. package/dist/telemetry/feedback.js +183 -17
  461. package/dist/telemetry/harness-run.d.ts +18 -0
  462. package/dist/telemetry/harness-run.js +80 -0
  463. package/dist/telemetry/hud-events.d.ts +16 -0
  464. package/dist/telemetry/hud-events.js +27 -0
  465. package/dist/telemetry/hud-metrics.d.ts +27 -6
  466. package/dist/telemetry/hud-metrics.js +99 -25
  467. package/dist/telemetry/init-telemetry.js +2 -2
  468. package/dist/telemetry/install-source.d.ts +13 -25
  469. package/dist/telemetry/install-source.js +44 -3
  470. package/dist/telemetry/mcp-connection.d.ts +0 -14
  471. package/dist/telemetry/mcp-connection.js +5 -1
  472. package/dist/telemetry/onboarding-firsts.d.ts +32 -0
  473. package/dist/telemetry/onboarding-firsts.js +81 -6
  474. package/dist/telemetry/platform-linked.d.ts +5 -0
  475. package/dist/telemetry/platform-linked.js +32 -0
  476. package/dist/telemetry/project-profile.js +5 -3
  477. package/dist/telemetry/proxy-telemetry.js +4 -1
  478. package/dist/telemetry/session-metrics.d.ts +15 -4
  479. package/dist/telemetry/session-metrics.js +88 -52
  480. package/dist/telemetry/telemetry.d.ts +26 -2
  481. package/dist/telemetry/telemetry.js +102 -21
  482. package/dist/telemetry/verification-of.d.ts +3 -1
  483. package/dist/telemetry/verification-of.js +12 -4
  484. package/dist/wire-journal.d.ts +2 -2
  485. package/dist/wire-journal.js +16 -5
  486. package/docs/README.md +2 -2
  487. package/docs/actions.mdx +6 -3
  488. package/docs/adapters.md +1 -1
  489. package/docs/agent-cheatsheet.md +24 -27
  490. package/docs/architecture.md +5 -5
  491. package/docs/autodrive.md +13 -12
  492. package/docs/benchmarks.md +5 -5
  493. package/docs/best-practices.mdx +27 -9
  494. package/docs/capabilities.mdx +14 -16
  495. package/docs/cli/affected.mdx +1 -1
  496. package/docs/cli/cloud.mdx +54 -75
  497. package/docs/cli/doctor.mdx +8 -3
  498. package/docs/cli/drive.mdx +12 -12
  499. package/docs/cli/feedback.mdx +3 -3
  500. package/docs/cli/gate.mdx +7 -2
  501. package/docs/cli/help.mdx +50 -55
  502. package/docs/cli/init.mdx +32 -15
  503. package/docs/cli/license.mdx +12 -8
  504. package/docs/cli/mcp.mdx +4 -4
  505. package/docs/cli/open.mdx +3 -3
  506. package/docs/cli/report.mdx +4 -2
  507. package/docs/cli/restart.mdx +2 -2
  508. package/docs/cli/rollback.mdx +14 -8
  509. package/docs/cli/serve.mdx +4 -4
  510. package/docs/cli/setup.mdx +23 -0
  511. package/docs/cli/status.mdx +13 -4
  512. package/docs/cli/stop.mdx +67 -8
  513. package/docs/cli/try.mdx +62 -0
  514. package/docs/cli/update.mdx +4 -3
  515. package/docs/cli/verify.mdx +17 -6
  516. package/docs/cli/version.mdx +3 -2
  517. package/docs/cli.mdx +110 -41
  518. package/docs/debugging.md +3 -3
  519. package/docs/deploy-checks.md +1 -1
  520. package/docs/desktop.mdx +5 -3
  521. package/docs/enterprise.md +11 -10
  522. package/docs/environment.mdx +5 -2
  523. package/docs/faq.mdx +3 -3
  524. package/docs/first-drive.md +5 -4
  525. package/docs/flows.md +35 -27
  526. package/docs/for-agents.md +4 -5
  527. package/docs/frameworks.mdx +32 -21
  528. package/docs/getting-started.md +66 -64
  529. package/docs/human-control.md +15 -14
  530. package/docs/index.mdx +15 -43
  531. package/docs/install-agentic.mdx +22 -37
  532. package/docs/install-manual.mdx +22 -12
  533. package/docs/instrumentation.mdx +5 -3
  534. package/docs/integration-patterns.md +7 -7
  535. package/docs/license-activation.mdx +1 -1
  536. package/docs/multi-agent-testing.md +24 -6
  537. package/docs/packages/babel-plugin.mdx +4 -2
  538. package/docs/packages/browser.mdx +16 -10
  539. package/docs/packages/core.mdx +12 -5
  540. package/docs/packages/electron.mdx +3 -2
  541. package/docs/packages/engine.mdx +1 -1
  542. package/docs/packages/eslint-plugin.mdx +18 -12
  543. package/docs/packages/next.mdx +8 -7
  544. package/docs/packages/open-verification.mdx +1 -1
  545. package/docs/packages/server.mdx +5 -5
  546. package/docs/packages/tauri.mdx +8 -8
  547. package/docs/packages/test.mdx +4 -10
  548. package/docs/packages/vite-plugin.mdx +11 -4
  549. package/docs/packages.mdx +11 -15
  550. package/docs/platform-integration.md +6 -5
  551. package/docs/predicates.mdx +40 -24
  552. package/docs/quickstart.mdx +58 -29
  553. package/docs/required-checks.md +8 -7
  554. package/docs/reticle-manual.md +15 -5
  555. package/docs/skill-file.mdx +17 -79
  556. package/docs/skill-setup.md +9 -9
  557. package/docs/skill.md +200 -0
  558. package/docs/system-map.md +7 -7
  559. package/docs/telemetry.md +22 -24
  560. package/docs/testing.md +4 -4
  561. package/docs/token-efficiency.md +12 -12
  562. package/docs/tools/act-and-wait.mdx +9 -2
  563. package/docs/tools/act-sequence.mdx +6 -7
  564. package/docs/tools/act.mdx +2 -2
  565. package/docs/tools/assert.mdx +18 -17
  566. package/docs/tools/console.mdx +9 -9
  567. package/docs/tools/inspect.mdx +16 -7
  568. package/docs/tools/look.mdx +24 -0
  569. package/docs/tools/navigate.mdx +5 -3
  570. package/docs/tools/network.mdx +2 -2
  571. package/docs/tools/observe.mdx +6 -6
  572. package/docs/tools/overview.mdx +34 -33
  573. package/docs/tools/query.mdx +5 -5
  574. package/docs/tools/session-and-feedback.mdx +13 -4
  575. package/docs/tools/sessions.mdx +8 -8
  576. package/docs/tools/snapshot.mdx +6 -5
  577. package/docs/tools/state.mdx +9 -9
  578. package/docs/tools/tools-and-run.mdx +26 -24
  579. package/docs/tools/verify.mdx +28 -0
  580. package/docs/tools/wait-for.mdx +8 -4
  581. package/docs/troubleshooting.mdx +21 -6
  582. package/docs/usage.md +51 -45
  583. package/docs/use-cases.mdx +3 -3
  584. package/docs/vs-chrome-devtools-mcp.mdx +9 -6
  585. package/docs/vs-playwright-mcp.mdx +3 -1
  586. package/docs/what-is-recorded.md +7 -5
  587. package/docs/what-is-reticle.mdx +2 -2
  588. package/docs/why-reticle.mdx +1 -1
  589. package/package.json +6 -5
  590. package/dist/command/cli/demo/demo-page.d.ts +0 -78
  591. package/dist/command/cli/demo/demo-page.js +0 -249
  592. package/dist/command/cli/demo/demo-run.d.ts +0 -38
  593. package/dist/command/cli/demo/demo-run.js +0 -138
  594. package/dist/judgement/hunt/hunt-report.d.ts +0 -49
  595. package/dist/judgement/hunt/hunt-report.js +0 -84
  596. package/dist/language/flows/buddy-status.d.ts +0 -25
  597. package/dist/language/flows/buddy-status.js +0 -32
  598. package/dist/language/flows/change/watch-batcher.d.ts +0 -18
  599. package/dist/language/flows/change/watch-batcher.js +0 -19
  600. package/dist/memory/cloud/harness-offer.d.ts +0 -44
  601. package/dist/memory/cloud/harness-offer.js +0 -145
  602. package/dist/surface/tools/feature-capture.d.ts +0 -151
  603. package/dist/surface/tools/feature-capture.js +0 -235
  604. package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
  605. package/dist/surface/tools/tool-hit-rate.js +0 -90
  606. package/dist/telemetry/harness-drive.d.ts +0 -4
  607. package/dist/telemetry/harness-drive.js +0 -35
  608. package/dist/telemetry/identify.d.ts +0 -22
  609. package/dist/telemetry/identify.js +0 -73
  610. package/docs/cli/capsules.mdx +0 -70
  611. package/docs/cli/hunt.mdx +0 -73
  612. package/docs/cli/identify.mdx +0 -76
  613. package/docs/cli/kill.mdx +0 -91
  614. package/docs/cli/watch.mdx +0 -62
@@ -5,24 +5,37 @@
5
5
  * `credentials.json` (per-project api keys from `reticle link`). The non-secret repo binding + sync policy
6
6
  * is `<repo>/.reticle/cloud.json`. Auth for a command = `RETICLE_API_KEY` env (agent) OR the login token.
7
7
  */
8
+ import { CreatedProjectSchema } from './project-name.js';
9
+ import { keyRefusalAtLastSync } from '../../memory/project/sync-status.js';
8
10
  import { apiKeyFrom } from '@reticlehq/core';
9
- import { mkdir, writeFile } from 'node:fs/promises';
11
+ import { PlatformLinkVia } from '@reticlehq/core/telemetry';
12
+ import { existsSync } from 'node:fs';
13
+ import { writeFile } from 'node:fs/promises';
10
14
  import { setTimeout as sleep } from 'node:timers/promises';
11
15
  import { homedir } from 'node:os';
12
- import { basename, join } from 'node:path';
16
+ import { join } from 'node:path';
13
17
  import { z } from 'zod';
14
18
  import { createNodeFileSystem } from '../../memory/project/fs/fs-port.js';
15
19
  import { CLOUD_LINK_FILE, resolveProjectCloud } from '../../memory/cloud/cloud-config.js';
16
20
  import { memoryReadUrl, MemoryResponseScope, scopeMemoryResponse, scopeOfMemoryResponse, } from '../../memory/cloud/memory-scope.js';
17
- import { applyCredential, findCredential } from './auth/cloud-keystore.js';
18
- import { defaultProjectFor } from './project-name.js';
21
+ import { linkDirectoryFrom } from '../../memory/project/link-directory.js';
19
22
  import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
23
+ import { cmdLink, validateKey } from './cloud-link.js';
24
+ // `link` moved to its own module; the CLI entry still imports it from here.
25
+ export { cmdLink } from './cloud-link.js';
20
26
  import { normalizeUrl } from './auth/cloud-session.js';
21
- import { cmdLogin, cmdLogout } from './cloud-login.js';
22
- import { api, baseUrl, bearer, CREDENTIALS_FILE, emit, err, flags, hint, home, readJson, readSession, readSessionFor, RETICLE_DIR, } from './cloud-kit.js';
27
+ import { chooseWorkspace, cmdLogin, cmdLogout } from './cloud-login.js';
28
+ import { cmdTry } from './try-command.js';
29
+ import { CLOUD_COMMANDS } from './cli-parse-grammar.js';
30
+ import { api, baseUrl, bearer, emit, err, flags, hint, readJson, readSession, readSessionFor, RETICLE_DIR, } from './cloud-kit.js';
23
31
  import { syncRequest } from '../../memory/cloud/cloud-sync.js';
32
+ import { GateExit, gateExitWith } from './answers/gate-exit.js';
33
+ import { brokenOf } from './known-regression.js';
34
+ import { describeUnsyncedBriefly } from '../../memory/cloud/unsynced-roots.js';
35
+ import { machineUnsyncedRoots } from '../../memory/project/sync-status.js';
24
36
  import { describeSync, runSyncCycle } from '../../memory/cloud/sync-cycle.js';
25
37
  import { diskSink, diskSource, readCloudIssues, readCloudState } from '../../memory/cloud/sync-disk.js';
38
+ import { fetchPlatformRun } from '../../features/harness/platform/platform-drives.js';
26
39
  /**
27
40
  * Where `reticle login` dials when nothing says otherwise: the hosted service.
28
41
  *
@@ -42,25 +55,11 @@ import { diskSink, diskSource, readCloudIssues, readCloudState } from '../../mem
42
55
  * override resolves through. This directory is what makes staging and production hold at the same
43
56
  * time instead of clobbering each other, which is the whole reason a single file was not enough.
44
57
  */
45
- const CLOUD_COMMANDS = new Set([
46
- 'login',
47
- 'connect',
48
- 'logout',
49
- 'whoami',
50
- 'link',
51
- 'project',
52
- 'config',
53
- 'issues',
54
- 'memory',
55
- 'push',
56
- 'sync',
57
- 'runs',
58
- 'regression',
59
- 'share',
60
- ]);
61
- export const isCloudCommand = (cmd) => cmd !== undefined && CLOUD_COMMANDS.has(cmd);
58
+ const CLOUD_COMMAND_SET = new Set(CLOUD_COMMANDS);
59
+ export const isCloudCommand = (cmd) => cmd !== undefined && CLOUD_COMMAND_SET.has(cmd);
62
60
  /** `reticle sync --watch` — keep cycling instead of exiting. */
63
61
  const WATCH_FLAG = '--watch';
62
+ const MOVE_HISTORY_FLAG = '--move-history';
64
63
  /**
65
64
  * How often `--watch` cycles.
66
65
  *
@@ -70,78 +69,15 @@ const WATCH_FLAG = '--watch';
70
69
  */
71
70
  const DEFAULT_SYNC_INTERVAL_MS = 60_000;
72
71
  /**
73
- * How a key is named out loud: enough to match it against the dashboard, never enough to use.
74
- *
75
- * The same shape the web console shows, so the two name one key identically and somebody can tell
76
- * at a glance which row this repo is using.
77
- */
78
- const KEY_HINT_CHARS = 16;
79
- const keyHint = (key) => key.length <= KEY_HINT_CHARS ? key : `${key.slice(0, KEY_HINT_CHARS)}…`;
80
- /**
81
- * The api key this machine already holds for a project ON THIS CLOUD, if any.
82
- *
83
- * Two shapes, matching the resolver: `{ key, url }` is what `link` writes now and is only returned
84
- * when the URL matches, and a bare string is the legacy shape with no URL to check. Without the URL
85
- * check this would happily reuse a production key for a self-hosted link, because `link` names
86
- * every project "default" and the two collide in one slot.
87
- */
88
- const storedCredential = async (projectId, url, orgId) => findCredential(await readJson(join(home(), CREDENTIALS_FILE)), projectId, url, orgId);
89
- /**
90
- * What a key is good for, or undefined when the cloud will not accept it.
91
- *
92
- * Never throws: a revoked key, a rotated one and an unreachable cloud are all "cannot reuse this",
93
- * and the caller's answer to every one of them is the same — mint a fresh one.
94
- */
95
- const validateKey = async (url, key) => {
96
- try {
97
- return WhoamiSchema.parse(await api('GET', `${url}/v1/cloud/whoami`, key));
98
- }
99
- catch {
100
- return undefined;
101
- }
102
- };
103
- const KeySchema = z.object({ projectId: z.string(), projectName: z.string(), key: z.string() });
104
- const WhoamiSchema = z.object({
105
- projectId: z.string(),
106
- projectName: z.string(),
107
- /**
108
- * Which tenant the key belongs to. Optional for the same reason `dashboardUrl` is — an older
109
- * cloud does not send it — and when it is missing a stored key cannot be proved to be ours, which
110
- * is why the reuse path below mints instead of guessing.
111
- */
112
- orgId: z.string().optional(),
113
- /**
114
- * Where this project's dashboard lives. Optional because an older cloud does not send it, and a
115
- * CLI that refused to link against one would break the thing it is supposed to connect.
116
- */
117
- dashboardUrl: z.string().optional(),
118
- });
119
- const CreatedProjectSchema = z.object({ projectId: z.string(), name: z.string() });
120
- const ProjectsListSchema = z.object({
121
- projects: z.array(z.object({ projectId: z.string(), name: z.string() })),
122
- });
123
- /** Resolve a --project value that may be a slug id OR a display name into the canonical projectId. */
124
- /**
125
- * The id for the project the caller named, creating it if it does not exist yet.
126
- *
127
- * It used to refuse — "create it with `reticle project create`" — which made naming a project a
128
- * two-command bookkeeping ritual for the COMMON case: a first repo, whose project has of course not
129
- * been created yet. That refusal is also the only reason the magic path existed: bare `link` binds
130
- * to a project called `Default` precisely so nobody has to meet this error.
131
- *
132
- * Creating is announced rather than silent. A tool that quietly invents a durable, billable object
133
- * is its own surprise, and the whole point of this change is that the automatic path SAYS what it
134
- * did.
72
+ * The `.reticle` this folder's commands read and write: the project `link` binds (see
73
+ * `linkDirectoryFor`), so `sync` from a monorepo root reaches the app it linked — unless only this
74
+ * folder holds a link, made before `link` followed the project, which keeps working.
135
75
  */
136
- const resolveProjectId = async (url, token, wanted) => {
137
- const { projects } = ProjectsListSchema.parse(await api('GET', `${url}/v1/projects`, token));
138
- const lc = wanted.toLowerCase();
139
- const match = projects.find((p) => p.projectId === wanted || p.name.toLowerCase() === lc);
140
- if (match !== undefined)
141
- return match.projectId;
142
- const created = CreatedProjectSchema.parse(await api('POST', `${url}/v1/projects`, token, { name: wanted }));
143
- hint(`created project "${wanted}" (${created.projectId}) — it did not exist yet`);
144
- return created.projectId;
76
+ const linkedRoot = () => {
77
+ const project = join(linkDirectoryFrom(process.cwd()), RETICLE_DIR);
78
+ const here = join(process.cwd(), RETICLE_DIR);
79
+ const onlyHere = !existsSync(join(project, CLOUD_LINK_FILE)) && existsSync(join(here, CLOUD_LINK_FILE));
80
+ return onlyHere ? here : project;
145
81
  };
146
82
  /**
147
83
  * `reticle whoami` — the one call an agent (or a confused human) makes to know its state: who am I logged
@@ -150,13 +86,13 @@ const resolveProjectId = async (url, token, wanted) => {
150
86
  const cmdWhoami = async () => {
151
87
  const session = await readSession();
152
88
  const fs = createNodeFileSystem();
153
- const cloud = await resolveProjectCloud(fs, join(process.cwd(), RETICLE_DIR), homedir(), process.env);
89
+ const cloud = await resolveProjectCloud(fs, linkedRoot(), homedir(), process.env);
154
90
  /*
155
91
  * The sync half of "what is my state". Without it the honest answer to "why does the dashboard
156
92
  * look old?" was to go and read a JSON file — and the two failure modes a person actually hits
157
93
  * (nothing has synced yet, and the last attempt errored) looked identical from out here.
158
94
  */
159
- const reticleRoot = join(process.cwd(), RETICLE_DIR);
95
+ const reticleRoot = linkedRoot();
160
96
  const state = readCloudState(reticleRoot);
161
97
  const decisions = Object.keys(readCloudIssues(reticleRoot).triage).length;
162
98
  emit({
@@ -182,9 +118,31 @@ const cmdWhoami = async () => {
182
118
  if (null === cloud.config)
183
119
  hint(null === session
184
120
  ? 'this repo is not attached — run `reticle connect` (signs in and links in one step)'
185
- : 'this repo is not attached — run `reticle link`');
121
+ : 'this repo is not attached — run `reticle connect`');
186
122
  return 0;
187
123
  };
124
+ /**
125
+ * Who is signed in and which project this repo is linked to, for `reticle status`. Local files
126
+ * only: status must answer with no network.
127
+ */
128
+ export const accountStatusFields = async () => {
129
+ const session = await readSession();
130
+ const cloud = await resolveProjectCloud(createNodeFileSystem(), linkedRoot(), homedir(), process.env);
131
+ // What the last sync heard, from local state, in the platform's words when it named the reason.
132
+ const refused = keyRefusalAtLastSync(linkedRoot());
133
+ const link = await readJson(join(linkedRoot(), CLOUD_LINK_FILE));
134
+ const workspace = 'object' === typeof link && null !== link
135
+ ? link['workspace']
136
+ : undefined;
137
+ return {
138
+ signedInAs: session?.orgName ?? null,
139
+ linkedProject: null === cloud.config ? null : cloud.projectId,
140
+ ...(null !== cloud.config && 'string' === typeof workspace && 0 < workspace.length
141
+ ? { linkedWorkspace: workspace }
142
+ : {}),
143
+ ...(refused === undefined ? {} : { keyRefused: refused }),
144
+ };
145
+ };
188
146
  /** `reticle project ls` / `reticle project create <name>` — key- or session-authed. */
189
147
  const cmdProject = async (argv) => {
190
148
  const active = await readSession();
@@ -195,8 +153,8 @@ const cmdProject = async (argv) => {
195
153
  // Name BOTH hosts when there is a session for a different one. "Run reticle login" on its own is
196
154
  // baffling to somebody who just did — the useful fact is that they logged in somewhere else.
197
155
  err(null !== active && normalizeUrl(active.url) !== url
198
- ? `signed in to ${normalizeUrl(active.url)}, but this command targets ${url} — run \`reticle login --url ${url}\`, or set RETICLE_API_KEY`
199
- : `not signed in to ${url} — run \`reticle login --url ${url}\`, or set RETICLE_API_KEY`);
156
+ ? `signed in to ${normalizeUrl(active.url)}, but this command targets ${url} — run \`reticle connect --url ${url}\`, or set RETICLE_API_KEY`
157
+ : `not signed in to ${url} — run \`reticle connect --url ${url}\`, or set RETICLE_API_KEY`);
200
158
  return 2;
201
159
  }
202
160
  const sub = argv[0];
@@ -212,7 +170,7 @@ const cmdProject = async (argv) => {
212
170
  }
213
171
  const created = CreatedProjectSchema.parse(await api('POST', `${url}/v1/projects`, token, { name }));
214
172
  emit(created);
215
- hint(`next: \`reticle link --project ${created.projectId}\` to bind this repo`);
173
+ hint(`next: \`reticle connect --project ${created.projectId}\` to bind this repo`);
216
174
  return 0;
217
175
  }
218
176
  if ('rename' === sub) {
@@ -237,229 +195,24 @@ const cmdProject = async (argv) => {
237
195
  err('usage: reticle project <ls|create <name>|rename <id> <name>|rm <id>>');
238
196
  return 2;
239
197
  };
198
+ const NO_INSTALL = '--no-install';
199
+ /** Whether `reticle connect`'s init half installs the packages: not under `--no-install`. */
200
+ export const connectInstalls = (argv) => !argv.includes(NO_INSTALL);
240
201
  /**
241
- * `reticle link [--project <id>]` — bind THIS repo to a cloud project. With a login token it MINTS a
242
- * project-scoped key (no pasting); with a pre-set RETICLE_API_KEY it resolves the key's project via
243
- * whoami. Writes the non-secret binding to <repo>/.reticle/cloud.json and the secret key to
244
- * ~/.reticle/credentials.json (keyed by projectId).
202
+ * `reticle connect`: the cloud half of first-run setup in one go. A saved login is checked against
203
+ * this host first; it links once, in the workspace chosen, then sends local history.
245
204
  */
246
- const cmdLink = async (argv) => {
247
- const f = flags(argv);
248
- const url = baseUrl(await readSession(), f['url']);
249
- // Same rule as every other authed verb: the token is looked up by the host it will be sent to.
250
- const session = await readSessionFor(url);
251
- const envKey = apiKeyFrom(process.env);
252
- /*
253
- * Read the existing binding FIRST, because it decides which project a bare `link` targets.
254
- * Re-running `link` is ordinary — rotating a key, repointing an environment — and it must land in
255
- * the project this repo already reports to, or its history splits in two without saying so.
256
- */
257
- const priorLink = await readJson(join(process.cwd(), RETICLE_DIR, CLOUD_LINK_FILE));
258
- const priorProjectId = 'object' === typeof priorLink && priorLink !== null
259
- ? priorLink['projectId']
260
- : undefined;
261
- let projectId;
262
- let projectName;
263
- let key;
264
- /**
265
- * The tenant this binding belongs to, recorded so the credential can be filed and found per-org.
266
- * Undefined against an older cloud, where the slot stays cloud+project as it always was.
267
- */
268
- let orgId;
269
- /** The key this machine already had for the slot, and whether it provably belongs to another tenant. */
270
- let priorKey;
271
- let priorIsForeign = false;
272
- /*
273
- * Where this project's dashboard lives. Asked of the cloud rather than derived from `url`: the API
274
- * origin and the console origin are different hosts in every deployment that is not a laptop, so a
275
- * link the CLI guessed would be wrong exactly where it matters.
276
- */
277
- let dashboardUrl;
278
- /** Whether the key was already on this machine — so the report can say so instead of "minted". */
279
- let reusedKey = false;
280
- // Tracked separately from the value: an OLDER cloud answers whoami without a dashboardUrl, and
281
- // keying the fallback off the value would ask the same question twice every time.
282
- let askedWhoami = false;
283
- if (envKey !== undefined && envKey.length > 0) {
284
- const who = WhoamiSchema.parse(await api('GET', `${url}/v1/cloud/whoami`, envKey));
285
- projectId = who.projectId;
286
- projectName = who.projectName;
287
- dashboardUrl = who.dashboardUrl;
288
- orgId = who.orgId;
289
- askedWhoami = true;
290
- key = envKey;
291
- }
292
- else if (session !== null) {
293
- // --project accepts a slug id OR a display name; default when omitted. Resolve to the canonical id.
294
- const wanted = f['project'];
295
- /*
296
- * A repo is named after itself, not "Default".
297
- *
298
- * When every repo binds to one project called "Default", two unrelated checkouts on one account
299
- * merge their runs, issues and impact into a single bucket and the per-project view describes
300
- * nothing. It is also what lets two TENANTS collide, since a credential slot built from a
301
- * project id is only as distinct as the ids are.
302
- */
303
- const fallback = defaultProjectFor(basename(process.cwd()), 'string' === typeof priorProjectId ? priorProjectId : undefined);
304
- const targetId = wanted === undefined
305
- ? await resolveProjectId(url, session.token, fallback)
306
- : await resolveProjectId(url, session.token, wanted);
307
- /*
308
- * Reuse the key this machine already holds for the project, rather than minting another.
309
- *
310
- * `link` was idempotent about the BINDING and not about the KEY: two runs against one project
311
- * left two live `reticle-cli` keys on the account, each valid, neither identifiable to a repo.
312
- * Agents retry — that is what agents do — so it accumulates silently until somebody has a key
313
- * list they cannot reason about and revokes the wrong one.
314
- *
315
- * Validated before trusting, because a stored key can have been revoked or rotated from the
316
- * dashboard and a stale credential must not strand the repo. The check is the whoami call the
317
- * mint path already makes for `dashboardUrl`, so the common path costs no extra round trip —
318
- * and a key that fails it is replaced rather than reported.
319
- */
320
- const existing = await storedCredential(targetId, url, session.orgId);
321
- const validated = existing === undefined ? undefined : await validateKey(url, existing);
322
- /*
323
- * A valid key is not the same as OUR key.
324
- *
325
- * `validateKey` only asks whether the cloud still accepts it, and a key belonging to another
326
- * organisation on the same cloud passes that question perfectly. With every project named
327
- * "default", the slot `<url>::default` was shared across tenants — so a brand-new workspace
328
- * signing in on a machine that had linked a different account reused that account's key and
329
- * would have pushed its runs into a stranger's dashboard. Measured, not hypothesised.
330
- *
331
- * So reuse now requires PROOF of a tenant match, and treats anything less as no match: an older
332
- * cloud that omits `orgId`, or a session file written before we recorded one, both fall through
333
- * to minting. Minting a second key is a tidiness problem; pushing to the wrong tenant is a
334
- * disclosure, and only one of those is worth defaulting to.
335
- */
336
- const sameTenant = validated !== undefined &&
337
- session.orgId !== undefined &&
338
- validated.orgId !== undefined &&
339
- validated.orgId === session.orgId;
340
- const reusable = sameTenant ? validated : undefined;
341
- priorKey = existing;
342
- // Provably somebody else's: it still works on this cloud AND whoami names a different org. A key
343
- // that merely FAILED validation is our own revoked one, and overwriting that is the point.
344
- priorIsForeign =
345
- validated !== undefined &&
346
- validated.orgId !== undefined &&
347
- session.orgId !== undefined &&
348
- validated.orgId !== session.orgId;
349
- if (existing !== undefined && reusable !== undefined) {
350
- projectId = reusable.projectId;
351
- projectName = reusable.projectName;
352
- dashboardUrl = reusable.dashboardUrl;
353
- orgId = reusable.orgId;
354
- askedWhoami = true;
355
- key = existing;
356
- reusedKey = true;
357
- }
358
- else {
359
- const minted = KeySchema.parse(await api('POST', `${url}/v1/keys`, session.token, {
360
- name: 'reticle-cli',
361
- projectId: targetId,
362
- }));
363
- projectId = minted.projectId;
364
- projectName = minted.projectName;
365
- key = minted.key;
366
- // The key was minted WITH this session, so its tenant is this session's by construction.
367
- orgId = session.orgId;
368
- }
369
- }
370
- else {
371
- err('run `reticle login` first, or set RETICLE_API_KEY to link with an existing key');
372
- return 2;
373
- }
374
- const reticleDir = join(process.cwd(), RETICLE_DIR);
375
- await mkdir(reticleDir, { recursive: true });
376
- const linkPath = join(reticleDir, CLOUD_LINK_FILE);
377
- const prev = await readJson(linkPath);
378
- const prevObj = 'object' === typeof prev && prev !== null ? prev : {};
379
- // The minted-key path has not asked yet. Best-effort: a link that works is worth more than a link
380
- // that also has a link in it, and an older cloud simply does not send one.
381
- if (!askedWhoami) {
382
- try {
383
- const who = WhoamiSchema.parse(await api('GET', `${url}/v1/cloud/whoami`, key));
384
- dashboardUrl = who.dashboardUrl;
385
- // Only FILL IN a tenant we do not have. The session we minted with is the authority on which
386
- // org this key belongs to; letting a later lookup overwrite it is how the answer drifts.
387
- orgId = orgId ?? who.orgId;
388
- }
389
- catch {
390
- // Older cloud, or a transient failure. The HUD shows its list without a link.
391
- }
392
- }
393
- const cloudJson = {
394
- projectId,
395
- projectName,
396
- // Recorded so the daemon resolves the credential in the ORG slot rather than the ambiguous
397
- // cloud+project one, which two tenants on one cloud both answer to.
398
- ...(orgId === undefined ? {} : { orgId }),
399
- url,
400
- ...(dashboardUrl === undefined ? {} : { dashboardUrl }),
401
- sync: prevObj['sync'] ?? { runs: true, memory: true, flows: true },
402
- verify: prevObj['verify'] ?? 'local',
403
- };
404
- await writeFile(linkPath, `${JSON.stringify(cloudJson, null, 2)}\n`);
405
- await mkdir(home(), { recursive: true });
406
- const credPath = join(home(), CREDENTIALS_FILE);
407
- const creds = (await readJson(credPath)) ?? {};
408
- const credObj = applyCredential('object' === typeof creds && creds !== null ? creds : {}, { projectId, url, key, orgId, priorKey, priorIsForeign });
409
- await writeFile(credPath, `${JSON.stringify(credObj, null, 2)}\n`);
410
- emit({ linked: projectName, projectId, cloudJson: linkPath, credentials: credPath });
411
- /*
412
- * Say what just happened, in the vocabulary of somebody who has used other tools.
413
- *
414
- * `link` mints the key and stores it for you, which is the product's whole edge — and it is
415
- * invisible, which is its whole cost. A real report: somebody pasted a masked placeholder key
416
- * into a `.env` because their mental model said "I must make a key and put it somewhere". One had
417
- * already been minted and filed outside the repo. They did not need another step; they needed to
418
- * be told the step had happened.
419
- *
420
- * The key is identified the way the dashboard identifies it — a prefix, never the secret — so
421
- * this can be read aloud, pasted into an issue, or left in a terminal without leaking anything.
422
- */
423
- hint(`bound this repo to project "${projectName}"`);
424
- hint(reusedKey
425
- ? `reusing key ${keyHint(key)} — already stored in ${credPath}, not in your repo`
426
- : `minted key ${keyHint(key)} — stored in ${credPath}, not in your repo`);
427
- hint('to change it: `reticle link --project <other>`; to inspect: `reticle whoami`');
428
- hint('linked ✓ runs auto-push on `reticle verify`; `reticle push` sends existing local runs; `reticle whoami` shows state');
429
- /*
430
- * A binding is not a connection.
431
- *
432
- * Without `.reticle.json` the app's bundle carries no projectId, so the daemon cannot attribute a
433
- * session to this repo: its runs pool into whichever root the daemon itself was started in, and
434
- * `sync` here reports "nothing to send" however much was actually recorded. Measured on this
435
- * repo's bench app — two verdicts driven, then a sync that claimed there was nothing.
436
- *
437
- * Said AFTER the success line rather than refusing: the binding really was written and really is
438
- * correct, and the missing half is one command away. Refusing would strand somebody who links
439
- * before they install, which is a legitimate order to work in.
440
- */
441
- if (!(await createNodeFileSystem().exists(join(process.cwd(), RETICLE_CONFIG_BASENAME)))) {
442
- hint(`no ${RETICLE_CONFIG_BASENAME} here: this app's runs reach this binding only while its dev ` +
443
- 'server runs with the Reticle build plugin, which announces it. Run `reticle init` in the app ' +
444
- 'to make the binding hold without it.');
445
- }
446
- return 0;
447
- };
448
- /**
449
- * `reticle connect` finishes the cloud half of first-run setup in one invocation. The CLI entry
450
- * initializes an unwired app before calling this verb. A saved login is checked against this exact
451
- * host before reuse; an expired one opens the normal browser approval flow. Login does not auto-link
452
- * here because this command links once, to the project the caller named, then sends local history.
453
- */
454
- const cmdConnect = async (argv) => {
205
+ const cmdConnect = async (given) => {
206
+ // `--no-install` is the init half's (see `connectInstalls`); the cloud half takes it and moves on.
207
+ const argv = given.filter((arg) => NO_INSTALL !== arg);
455
208
  for (let i = 0; i < argv.length; i += 2) {
456
209
  const flag = argv[i];
457
210
  const value = argv[i + 1];
458
- if ((flag !== '--project' && flag !== '--url') ||
211
+ if ((flag !== '--project' && flag !== '--url' && flag !== '--workspace') ||
459
212
  value === undefined ||
460
213
  0 === value.length ||
461
214
  value.startsWith('--')) {
462
- err('usage: reticle connect [--project <name|id>] [--url <cloud origin>]');
215
+ err('usage: reticle connect [--project <name|id>] [--workspace <name|id>] [--url <origin>] [--no-install]');
463
216
  return 2;
464
217
  }
465
218
  }
@@ -487,12 +240,15 @@ const cmdConnect = async (argv) => {
487
240
  return code;
488
241
  }
489
242
  }
243
+ const chosen = await chooseWorkspace(url, f['workspace']);
244
+ if (chosen !== 0)
245
+ return chosen;
490
246
  const linkArgs = [
491
247
  '--url',
492
248
  url,
493
249
  ...(f['project'] === undefined ? [] : ['--project', f['project']]),
494
250
  ];
495
- const linked = await cmdLink(linkArgs);
251
+ const linked = await cmdLink(linkArgs, process.cwd(), PlatformLinkVia.CONNECT);
496
252
  if (linked !== 0)
497
253
  return linked;
498
254
  const pushed = await cmdPush();
@@ -504,10 +260,10 @@ const cmdConnect = async (argv) => {
504
260
  /** `reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]`. */
505
261
  const cmdConfig = async (argv) => {
506
262
  const f = flags(argv);
507
- const linkPath = join(process.cwd(), RETICLE_DIR, CLOUD_LINK_FILE);
263
+ const linkPath = join(linkedRoot(), CLOUD_LINK_FILE);
508
264
  const raw = await readJson(linkPath);
509
265
  if (null === raw || typeof raw !== 'object') {
510
- err('no .reticle/cloud.json here — run `reticle link` first');
266
+ err('no .reticle/cloud.json here — run `reticle connect` first');
511
267
  return 2;
512
268
  }
513
269
  const cfg = raw;
@@ -538,7 +294,7 @@ const cmdConfig = async (argv) => {
538
294
  return 0;
539
295
  };
540
296
  /**
541
- * `reticle sync [--watch]` — one full cycle: send the difference, collect what came back.
297
+ * `reticle sync [--watch] [--move-history]` — one full cycle: send the difference, collect what came back.
542
298
  *
543
299
  * This replaced a `push` that re-uploaded every run artifact on every invocation. That was fine with
544
300
  * three runs and absurd with three hundred, and it only ever went one way — so a bug somebody
@@ -549,12 +305,15 @@ const cmdConfig = async (argv) => {
549
305
  */
550
306
  const cmdSync = async (argv) => {
551
307
  const fs = createNodeFileSystem();
552
- const reticleRoot = join(process.cwd(), RETICLE_DIR);
308
+ const reticleRoot = linkedRoot();
553
309
  const cloud = await resolveProjectCloud(fs, reticleRoot, homedir(), process.env);
554
310
  if (null === cloud.config) {
555
- err(cloud.reason ?? 'cloud not attached here: run `reticle link`, or set RETICLE_API_KEY');
311
+ err(cloud.reason ?? 'cloud not attached here: run `reticle connect`, or set RETICLE_API_KEY');
312
+ await reportUnsynced(fs);
556
313
  return 1;
557
314
  }
315
+ if (cloud.note !== undefined)
316
+ err(cloud.note);
558
317
  const config = cloud.config;
559
318
  const full = diskSource(reticleRoot);
560
319
  const source = {
@@ -563,10 +322,16 @@ const cmdSync = async (argv) => {
563
322
  capsules: () => (cloud.policy.capsules ? full.capsules() : []),
564
323
  // `memory` is the project's cross-run history and the derived records that summarise it.
565
324
  derived: (kind) => cloud.policy.memory ? full.derived(kind) : undefined,
325
+ ...(cloud.policy.screenshots && full.screenshot !== undefined
326
+ ? { screenshot: full.screenshot }
327
+ : {}),
566
328
  };
567
329
  const once = async () => {
568
330
  const report = await runSyncCycle({
569
331
  config,
332
+ ...(cloud.link === undefined ? {} : { link: cloud.link }),
333
+ // Runs made under a folder's previous link go only when the person says so.
334
+ ...(argv.includes(MOVE_HISTORY_FLAG) ? { moveHistory: true } : {}),
570
335
  source,
571
336
  sink: diskSink(reticleRoot),
572
337
  state: readCloudState(reticleRoot),
@@ -590,6 +355,7 @@ const cmdSync = async (argv) => {
590
355
  ...(report.error === undefined ? {} : { error: report.error }),
591
356
  });
592
357
  hint(describeSync(report));
358
+ await reportUnsynced(fs);
593
359
  return report.ok ? 0 : 1;
594
360
  };
595
361
  const watch = argv.includes(WATCH_FLAG);
@@ -602,6 +368,14 @@ const cmdSync = async (argv) => {
602
368
  await sleep(everyMs);
603
369
  }
604
370
  };
371
+ /**
372
+ * Every other folder on this machine holding runs the platform never got, one line each. `sync` only
373
+ * ever sends the folder it runs in, so a monorepo app's unlinked `.reticle` was invisible from here.
374
+ */
375
+ const reportUnsynced = async (fs) => {
376
+ for (const line of describeUnsyncedBriefly(await machineUnsyncedRoots(fs, homedir(), process.env), process.cwd()))
377
+ hint(line);
378
+ };
605
379
  /** `reticle push` — the name people already type. One cycle, same as `reticle sync`. */
606
380
  const cmdPush = async () => cmdSync([]);
607
381
  /**
@@ -619,15 +393,32 @@ const cmdPush = async () => cmdSync([]);
619
393
  */
620
394
  const repoCloud = async () => {
621
395
  const fs = createNodeFileSystem();
622
- const cloud = await resolveProjectCloud(fs, join(process.cwd(), RETICLE_DIR), homedir(), process.env);
396
+ const cloud = await resolveProjectCloud(fs, linkedRoot(), homedir(), process.env);
623
397
  if (null === cloud.config)
624
- throw new Error(cloud.reason ?? 'cloud not attached here: run `reticle link`, or set RETICLE_API_KEY');
398
+ throw new Error(cloud.reason ?? 'cloud not attached here: run `reticle connect`, or set RETICLE_API_KEY');
625
399
  return { ...cloud.config, projectId: cloud.projectId };
626
400
  };
627
401
  /** `reticle runs` — the linked project's recent run artifacts (the key scopes it server-side). */
628
- const cmdRuns = async () => {
402
+ /**
403
+ * `reticle runs [<id>]` — the project's runs, or one of them by id.
404
+ *
405
+ * With an id it is the way back to a Harness drive whose local record is gone (another machine, a
406
+ * cleaned checkout): the platform keeps the run. A 404 is "not synced yet, or not found", never a
407
+ * crash, since a run written a minute ago may simply not have been pushed.
408
+ */
409
+ const cmdRuns = async (argv) => {
629
410
  const { url, apiKey } = await repoCloud();
630
- emit(await api('GET', `${url}/v1/runs`, apiKey));
411
+ const id = argv[0];
412
+ if (id === undefined) {
413
+ emit(await api('GET', `${url}/v1/runs`, apiKey));
414
+ return 0;
415
+ }
416
+ const got = await fetchPlatformRun({ url, apiKey }, id);
417
+ if ('error' in got) {
418
+ err(got.error);
419
+ return 1;
420
+ }
421
+ emit(got.run);
631
422
  return 0;
632
423
  };
633
424
  /**
@@ -648,7 +439,7 @@ const cmdIssues = async (argv) => {
648
439
  const fixAt = argv.indexOf('--fix');
649
440
  const wanted = -1 === fixAt ? undefined : argv[fixAt + 1];
650
441
  if (fixAt !== -1 && wanted === undefined) {
651
- err('usage: reticle issues --fix <fingerprint>');
442
+ err('usage: reticle runs issues --fix <fingerprint>');
652
443
  return 2;
653
444
  }
654
445
  const body = await api('GET', `${url}/v1/issues`, apiKey);
@@ -667,7 +458,7 @@ const cmdIssues = async (argv) => {
667
458
  }
668
459
  const found = parsed.data.issues.find((i) => i.fingerprint === wanted);
669
460
  if (found === undefined) {
670
- err(`no issue with fingerprint '${wanted}' — run \`reticle issues\` to list them`);
461
+ err(`no issue with fingerprint '${wanted}' — run \`reticle runs issues\` to list them`);
671
462
  return 2;
672
463
  }
673
464
  const prompt = found.fixPrompt;
@@ -698,7 +489,7 @@ const cmdMemory = async (argv) => {
698
489
  const at = argv.indexOf('--subject');
699
490
  const subject = -1 === at ? undefined : argv[at + 1];
700
491
  if (-1 !== at && subject === undefined) {
701
- err('usage: reticle memory [--subject <name>]');
492
+ err('usage: reticle runs memory [--subject <name>]');
702
493
  return 2;
703
494
  }
704
495
  // Scoped to the LINKED project, on the way out AND on the way back. Without the parameter
@@ -734,20 +525,35 @@ const cmdRegression = async () => {
734
525
  const { url, apiKey } = await repoCloud();
735
526
  const report = await api('GET', `${url}/v1/project/regression`, apiKey);
736
527
  emit(report);
737
- const parsed = z.object({ broken: z.array(z.unknown()) }).safeParse(report);
738
- return parsed.success && parsed.data.broken.length > 0 ? 3 : 0;
528
+ return gateExitWith(GateExit.PASS, brokenOf(report)?.length);
739
529
  };
740
530
  /** `reticle share <runId>` — mint a public proof link for one run. */
741
531
  const cmdShare = async (argv) => {
742
532
  const runId = argv[0];
743
533
  if (runId === undefined) {
744
- err('usage: reticle share <runId>');
534
+ err('usage: reticle runs share <runId>');
745
535
  return 2;
746
536
  }
747
537
  const { url, apiKey } = await repoCloud();
748
538
  emit(await api('POST', `${url}/v1/runs/${encodeURIComponent(runId)}/share`, apiKey));
749
539
  return 0;
750
540
  };
541
+ /** `reticle runs <regression|share|issues|memory>`, and `reticle runs [<runId>]` otherwise. */
542
+ const cmdRunsFamily = async (argv) => {
543
+ const [sub, ...rest] = argv;
544
+ switch (sub) {
545
+ case 'regression':
546
+ return cmdRegression();
547
+ case 'share':
548
+ return cmdShare(rest);
549
+ case 'issues':
550
+ return cmdIssues(rest);
551
+ case 'memory':
552
+ return cmdMemory(rest);
553
+ default:
554
+ return cmdRuns(argv);
555
+ }
556
+ };
751
557
  /** Dispatch a cloud subcommand. Returns the process exit code. */
752
558
  export const runCloudCommand = async (argv) => {
753
559
  const [cmd, ...rest] = argv;
@@ -759,6 +565,11 @@ export const runCloudCommand = async (argv) => {
759
565
  return await cmdLogin(rest, cmdLink);
760
566
  case 'connect':
761
567
  return await cmdConnect(rest);
568
+ case 'try':
569
+ return await cmdTry(rest, {
570
+ push: () => cmdSync([]),
571
+ dashboardUrl: async (c) => (await validateKey(c.url, c.apiKey))?.dashboardUrl,
572
+ });
762
573
  case 'logout':
763
574
  return await cmdLogout(rest);
764
575
  case 'whoami':
@@ -769,12 +580,13 @@ export const runCloudCommand = async (argv) => {
769
580
  return await cmdLink(rest);
770
581
  case 'config':
771
582
  return await cmdConfig(rest);
583
+ // `push` is the name people type, so it takes `--watch` too: the help and the README say so.
772
584
  case 'push':
773
- return await cmdPush();
585
+ return await cmdSync(rest);
774
586
  case 'sync':
775
587
  return await cmdSync(rest);
776
588
  case 'runs':
777
- return await cmdRuns();
589
+ return await cmdRunsFamily(rest);
778
590
  case 'issues':
779
591
  return await cmdIssues(rest);
780
592
  case 'memory':