@reticlehq/server 2.4.0 → 2.5.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 (416) hide show
  1. package/SKILL.md +702 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +63 -13
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +1 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +9 -9
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
  20. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  21. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  22. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  23. package/dist/cli/cli-launch.js +171 -0
  24. package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +2 -11
  25. package/dist/{cli-parse.js → cli/cli-parse.js} +113 -60
  26. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
  27. package/dist/{cli-port.js → cli/cli-port.js} +9 -9
  28. package/dist/cli/cli-update-commands.d.ts +4 -0
  29. package/dist/cli/cli-update-commands.js +93 -0
  30. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  31. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  32. package/dist/cli.d.ts +2 -2
  33. package/dist/cli.js +174 -92
  34. package/dist/cloud/cloud-config.d.ts +0 -1
  35. package/dist/cloud/cloud-config.js +9 -9
  36. package/dist/cloud/cloud-sync.d.ts +1 -7
  37. package/dist/cloud/cloud-sync.js +12 -12
  38. package/dist/crawl/crawl-empty.d.ts +16 -0
  39. package/dist/crawl/crawl-empty.js +24 -0
  40. package/dist/crawl/crawl-tools.js +4 -1
  41. package/dist/crawl/crawl.js +26 -7
  42. package/dist/daemon/daemon-resilience.d.ts +49 -0
  43. package/dist/daemon/daemon-resilience.js +191 -0
  44. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  45. package/dist/daemon/daemon-usefulness.js +62 -0
  46. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  47. package/dist/{daemon.js → daemon/daemon.js} +80 -48
  48. package/dist/daemon/idle-grace.d.ts +32 -0
  49. package/dist/daemon/idle-grace.js +41 -0
  50. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
  51. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
  52. package/dist/domain/domain-model.js +2 -2
  53. package/dist/events/body-failures.js +8 -8
  54. package/dist/events/contradictions.d.ts +1 -1
  55. package/dist/events/contradictions.js +60 -10
  56. package/dist/events/echo-mismatch.js +10 -10
  57. package/dist/events/event-filters.js +6 -6
  58. package/dist/events/json-salvage.d.ts +2 -1
  59. package/dist/events/json-salvage.js +6 -6
  60. package/dist/events/observed-in-window.d.ts +12 -0
  61. package/dist/events/observed-in-window.js +21 -0
  62. package/dist/events/predicate-eval.d.ts +43 -20
  63. package/dist/events/predicate-eval.js +159 -55
  64. package/dist/events/predicate.d.ts +2 -2
  65. package/dist/events/predicate.js +112 -35
  66. package/dist/events/reconcile.js +5 -5
  67. package/dist/events/ring-buffer.js +1 -1
  68. package/dist/events/stale-response.js +6 -6
  69. package/dist/events/testid-near-miss.d.ts +22 -0
  70. package/dist/events/testid-near-miss.js +33 -0
  71. package/dist/events/unit-mismatch.js +8 -8
  72. package/dist/flows/affected-tools.js +19 -4
  73. package/dist/flows/affected.js +1 -1
  74. package/dist/flows/annotate-target.d.ts +1 -0
  75. package/dist/flows/annotate-target.js +20 -0
  76. package/dist/flows/annotate-tools.js +25 -3
  77. package/dist/flows/annotate.js +17 -4
  78. package/dist/flows/assertion-integrity.d.ts +2 -1
  79. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  80. package/dist/flows/assertion-tiers-store.js +2 -2
  81. package/dist/flows/attributed-failure.d.ts +17 -0
  82. package/dist/flows/attributed-failure.js +20 -0
  83. package/dist/flows/buddy-status.d.ts +2 -1
  84. package/dist/flows/buddy-status.js +2 -2
  85. package/dist/flows/coverage.d.ts +3 -2
  86. package/dist/flows/coverage.js +2 -2
  87. package/dist/flows/decision.js +24 -9
  88. package/dist/flows/empty-flow.d.ts +6 -0
  89. package/dist/flows/empty-flow.js +24 -0
  90. package/dist/flows/flake-store.d.ts +0 -1
  91. package/dist/flows/flake-store.js +1 -1
  92. package/dist/flows/flake.d.ts +2 -1
  93. package/dist/flows/flake.js +2 -2
  94. package/dist/flows/flow-classify.js +12 -0
  95. package/dist/flows/flow-replay-run.js +4 -4
  96. package/dist/flows/flow-replay.d.ts +5 -7
  97. package/dist/flows/flow-replay.js +114 -47
  98. package/dist/flows/flow-report.js +1 -1
  99. package/dist/flows/flow-scope.d.ts +1 -6
  100. package/dist/flows/flow-step-runners.d.ts +26 -6
  101. package/dist/flows/flow-step-runners.js +157 -6
  102. package/dist/flows/flow-success.js +20 -15
  103. package/dist/flows/flow-tools.js +53 -7
  104. package/dist/flows/flows.js +15 -11
  105. package/dist/flows/gate.d.ts +4 -3
  106. package/dist/flows/gate.js +1 -1
  107. package/dist/flows/heal-run.d.ts +0 -8
  108. package/dist/flows/heal-run.js +5 -5
  109. package/dist/flows/heal.js +3 -3
  110. package/dist/flows/predicate-to-expect.d.ts +38 -0
  111. package/dist/flows/predicate-to-expect.js +95 -0
  112. package/dist/flows/replay.d.ts +16 -1
  113. package/dist/flows/replay.js +58 -22
  114. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  115. package/dist/flows/role-anchor-nearest.js +45 -0
  116. package/dist/flows/server-verify.d.ts +1 -1
  117. package/dist/flows/server-verify.js +5 -5
  118. package/dist/flows/verify-change-tools.js +32 -10
  119. package/dist/flows/verify-change-verdict.d.ts +15 -0
  120. package/dist/flows/verify-change-verdict.js +25 -0
  121. package/dist/flows/watch-batcher.d.ts +3 -2
  122. package/dist/honesty/already-true.d.ts +2 -0
  123. package/dist/honesty/already-true.js +31 -0
  124. package/dist/honesty/blind-spots.d.ts +16 -3
  125. package/dist/honesty/blind-spots.js +24 -12
  126. package/dist/honesty/honesty.d.ts +3 -2
  127. package/dist/honesty/honesty.js +4 -4
  128. package/dist/honesty/uncaptured-bodies.js +1 -1
  129. package/dist/honesty/unread-outcome.js +2 -2
  130. package/dist/honesty/verified.d.ts +22 -3
  131. package/dist/honesty/verified.js +48 -8
  132. package/dist/http-server.js +14 -7
  133. package/dist/hunt/hunt-report.d.ts +2 -1
  134. package/dist/hunt/hunt-report.js +2 -2
  135. package/dist/index.d.ts +8 -6
  136. package/dist/index.js +32 -25
  137. package/dist/init/agent-rules.d.ts +2 -1
  138. package/dist/init/agent-rules.js +45 -7
  139. package/dist/init/app-choice.d.ts +20 -0
  140. package/dist/init/app-choice.js +25 -0
  141. package/dist/init/astro-host.d.ts +21 -0
  142. package/dist/init/astro-host.js +27 -0
  143. package/dist/init/astro-patch.d.ts +16 -0
  144. package/dist/init/astro-patch.js +240 -0
  145. package/dist/init/capabilities.js +4 -1
  146. package/dist/init/cra.d.ts +37 -0
  147. package/dist/init/cra.js +90 -0
  148. package/dist/init/cursor.js +39 -4
  149. package/dist/init/desktop-doctor.d.ts +3 -2
  150. package/dist/init/desktop-doctor.js +4 -4
  151. package/dist/init/detect.d.ts +3 -2
  152. package/dist/init/detect.js +16 -5
  153. package/dist/init/mcp-registered.d.ts +12 -0
  154. package/dist/init/mcp-registered.js +14 -0
  155. package/dist/init/mcp.d.ts +27 -2
  156. package/dist/init/mcp.js +40 -4
  157. package/dist/init/next-patch.d.ts +2 -4
  158. package/dist/init/next-patch.js +109 -19
  159. package/dist/init/node-io.js +23 -6
  160. package/dist/init/plan-framework.d.ts +16 -1
  161. package/dist/init/plan-framework.js +132 -8
  162. package/dist/init/plan.d.ts +73 -8
  163. package/dist/init/plan.js +133 -34
  164. package/dist/init/project-id.d.ts +5 -9
  165. package/dist/init/project-id.js +10 -22
  166. package/dist/init/run.d.ts +5 -0
  167. package/dist/init/run.js +210 -36
  168. package/dist/init/slash-command.d.ts +9 -0
  169. package/dist/init/slash-command.js +10 -1
  170. package/dist/init/snippets.d.ts +7 -10
  171. package/dist/init/snippets.js +44 -15
  172. package/dist/init/vite-config.js +1 -1
  173. package/dist/init/workspace-apps.d.ts +23 -0
  174. package/dist/init/workspace-apps.js +68 -0
  175. package/dist/input/driven-redaction.js +1 -1
  176. package/dist/input/network-detail.js +8 -8
  177. package/dist/input/network-mock-tools.js +2 -2
  178. package/dist/input/network-mock.js +4 -4
  179. package/dist/input/real-input.js +8 -3
  180. package/dist/input/scroll-find.js +2 -2
  181. package/dist/input/scroll-tools.d.ts +0 -5
  182. package/dist/input/scroll-tools.js +11 -4
  183. package/dist/input/viewport-tools.js +2 -2
  184. package/dist/journal/ambient-store.d.ts +0 -2
  185. package/dist/journal/ambient-store.js +1 -1
  186. package/dist/journal/ambient.js +1 -1
  187. package/dist/journal/attach-journal.d.ts +2 -1
  188. package/dist/journal/deviation-report.js +6 -6
  189. package/dist/journal/deviation-service.js +1 -1
  190. package/dist/journal/envelope-store.d.ts +0 -2
  191. package/dist/journal/envelope-store.js +1 -1
  192. package/dist/journal/envelope.d.ts +2 -3
  193. package/dist/journal/envelope.js +3 -3
  194. package/dist/journal/journal-query.js +1 -1
  195. package/dist/journal/journal-recorder.d.ts +2 -1
  196. package/dist/journal/journal-recorder.js +2 -2
  197. package/dist/journal/rollups.js +9 -9
  198. package/dist/journal/session-end.d.ts +2 -1
  199. package/dist/journal/session-journal.js +4 -4
  200. package/dist/license/license.d.ts +0 -14
  201. package/dist/license/license.js +8 -8
  202. package/dist/log.d.ts +10 -0
  203. package/dist/log.js +11 -1
  204. package/dist/mcp/mcp-is-error.d.ts +13 -0
  205. package/dist/mcp/mcp-is-error.js +18 -0
  206. package/dist/mcp/mcp-outage.d.ts +31 -0
  207. package/dist/mcp/mcp-outage.js +48 -0
  208. package/dist/mcp/mcp-proxy.d.ts +124 -0
  209. package/dist/mcp/mcp-proxy.js +697 -0
  210. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  211. package/dist/{mcp.js → mcp/mcp.js} +110 -35
  212. package/dist/mcp/proxy-handshake.d.ts +3 -0
  213. package/dist/mcp/proxy-handshake.js +54 -0
  214. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  215. package/dist/mcp/proxy-lifecycle.js +70 -0
  216. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  217. package/dist/mcp/tool-catalog-cache.js +55 -0
  218. package/dist/oracles/propose-consequences.d.ts +2 -1
  219. package/dist/oracles/propose-consequences.js +16 -11
  220. package/dist/oracles/self-instrument.d.ts +2 -1
  221. package/dist/oracles/self-instrument.js +1 -1
  222. package/dist/phenomena/phenomena.js +3 -3
  223. package/dist/platform.d.ts +16 -0
  224. package/dist/platform.js +16 -0
  225. package/dist/pool/playwright-launcher.d.ts +15 -2
  226. package/dist/pool/playwright-launcher.js +21 -2
  227. package/dist/project/fs-port.js +1 -1
  228. package/dist/project/project-tools.js +2 -2
  229. package/dist/runs/build-verification-run.js +1 -1
  230. package/dist/runs/repair-prompt.d.ts +0 -6
  231. package/dist/runs/repair-prompt.js +3 -3
  232. package/dist/runs/risk-classify.js +1 -1
  233. package/dist/runs/run-diff.d.ts +0 -2
  234. package/dist/runs/run-diff.js +3 -3
  235. package/dist/runs/run-tools.js +2 -2
  236. package/dist/runs/verification-sync.js +2 -2
  237. package/dist/runs/verify-http.js +1 -1
  238. package/dist/runs/verify-server.js +1 -1
  239. package/dist/session/command-timeout.d.ts +11 -3
  240. package/dist/session/command-timeout.js +2 -2
  241. package/dist/session/control-envelope.d.ts +6 -0
  242. package/dist/session/control-envelope.js +7 -1
  243. package/dist/session/dev-server-probe.d.ts +13 -0
  244. package/dist/session/dev-server-probe.js +52 -0
  245. package/dist/session/last-act.d.ts +17 -3
  246. package/dist/session/last-act.js +16 -4
  247. package/dist/session/live-control-tools.js +7 -2
  248. package/dist/session/live-control.js +1 -1
  249. package/dist/session/looks-like-dev-server.d.ts +14 -0
  250. package/dist/session/looks-like-dev-server.js +25 -0
  251. package/dist/session/no-session-diagnosis.d.ts +38 -0
  252. package/dist/session/no-session-diagnosis.js +70 -0
  253. package/dist/session/no-session-watch.d.ts +19 -0
  254. package/dist/session/no-session-watch.js +61 -0
  255. package/dist/session/observed-state.d.ts +4 -0
  256. package/dist/session/observed-state.js +19 -1
  257. package/dist/session/output-budget.js +1 -1
  258. package/dist/session/session-health.d.ts +8 -1
  259. package/dist/session/session-health.js +8 -7
  260. package/dist/session/session-info.d.ts +26 -7
  261. package/dist/session/session-info.js +42 -1
  262. package/dist/session/session-lease.d.ts +0 -11
  263. package/dist/session/session-lease.js +1 -1
  264. package/dist/session/session-manager.d.ts +3 -0
  265. package/dist/session/session-manager.js +52 -9
  266. package/dist/session/session-readiness.d.ts +1 -1
  267. package/dist/session/session-readiness.js +6 -1
  268. package/dist/session/session-reaper.js +1 -1
  269. package/dist/session/session-reconnect.d.ts +32 -0
  270. package/dist/session/session-reconnect.js +42 -0
  271. package/dist/session/session.d.ts +14 -2
  272. package/dist/session/session.js +69 -26
  273. package/dist/session/state-select.d.ts +1 -1
  274. package/dist/session/state-select.js +1 -1
  275. package/dist/status-payload.d.ts +19 -0
  276. package/dist/status-payload.js +11 -0
  277. package/dist/telemetry/argument-shape.js +10 -1
  278. package/dist/telemetry/browser-mode.d.ts +27 -0
  279. package/dist/telemetry/browser-mode.js +33 -0
  280. package/dist/telemetry/bug-found.d.ts +17 -21
  281. package/dist/telemetry/bug-found.js +120 -8
  282. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  283. package/dist/telemetry/cli-telemetry.js +27 -4
  284. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  285. package/dist/telemetry/daemon-telemetry.js +12 -2
  286. package/dist/telemetry/dev-repo.js +4 -4
  287. package/dist/telemetry/error-fingerprint.js +21 -2
  288. package/dist/telemetry/feedback-cli.d.ts +1 -1
  289. package/dist/telemetry/feedback-cli.js +4 -4
  290. package/dist/telemetry/feedback-context.d.ts +22 -0
  291. package/dist/telemetry/feedback-context.js +41 -1
  292. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  293. package/dist/telemetry/feedback-delivery.js +30 -0
  294. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  295. package/dist/telemetry/feedback-outbox.js +87 -0
  296. package/dist/telemetry/feedback.d.ts +22 -1
  297. package/dist/telemetry/feedback.js +67 -8
  298. package/dist/telemetry/git-facts.js +1 -1
  299. package/dist/telemetry/session-metrics.d.ts +2 -0
  300. package/dist/telemetry/session-metrics.js +68 -6
  301. package/dist/telemetry/telemetry.d.ts +27 -2
  302. package/dist/telemetry/telemetry.js +119 -29
  303. package/dist/telemetry/verification-of.d.ts +22 -0
  304. package/dist/telemetry/verification-of.js +84 -0
  305. package/dist/tools/act-capsule.js +2 -2
  306. package/dist/tools/act-danger.d.ts +12 -0
  307. package/dist/tools/act-danger.js +20 -3
  308. package/dist/tools/act-helpers.js +4 -4
  309. package/dist/tools/act-tools.d.ts +0 -8
  310. package/dist/tools/act-tools.js +84 -115
  311. package/dist/tools/act-view.js +7 -7
  312. package/dist/tools/alias-args.d.ts +17 -0
  313. package/dist/tools/alias-args.js +25 -0
  314. package/dist/tools/assert-grade.js +9 -9
  315. package/dist/tools/assert-verdict.d.ts +3 -1
  316. package/dist/tools/assert-verdict.js +8 -5
  317. package/dist/tools/browser-tools.js +30 -11
  318. package/dist/tools/contract-tools.js +1 -1
  319. package/dist/tools/coverage-identity.d.ts +34 -0
  320. package/dist/tools/coverage-identity.js +46 -0
  321. package/dist/tools/coverage-tools.js +10 -14
  322. package/dist/tools/dynamic-tools.d.ts +2 -1
  323. package/dist/tools/dynamic-tools.js +90 -18
  324. package/dist/tools/error-recovery.d.ts +20 -2
  325. package/dist/tools/error-recovery.js +179 -4
  326. package/dist/tools/feedback-tools.d.ts +12 -1
  327. package/dist/tools/feedback-tools.js +28 -5
  328. package/dist/tools/invoke-tool.d.ts +0 -6
  329. package/dist/tools/invoke-tool.js +57 -32
  330. package/dist/tools/lease-hint.d.ts +17 -0
  331. package/dist/tools/lease-hint.js +24 -0
  332. package/dist/tools/lease-tools.js +6 -5
  333. package/dist/tools/merge-tools.d.ts +10 -1
  334. package/dist/tools/merge-tools.js +9 -3
  335. package/dist/tools/merged-name-redirect.d.ts +13 -0
  336. package/dist/tools/merged-name-redirect.js +55 -0
  337. package/dist/tools/navigate-result.d.ts +17 -0
  338. package/dist/tools/navigate-result.js +31 -0
  339. package/dist/tools/observe-tools.js +54 -20
  340. package/dist/tools/observed-nothing.d.ts +25 -0
  341. package/dist/tools/observed-nothing.js +33 -0
  342. package/dist/tools/query-paginate.js +2 -2
  343. package/dist/tools/query-shape.d.ts +18 -0
  344. package/dist/tools/query-shape.js +39 -0
  345. package/dist/tools/read-tools.js +81 -20
  346. package/dist/tools/real-input-attempt.d.ts +28 -0
  347. package/dist/tools/real-input-attempt.js +88 -0
  348. package/dist/tools/reconcile-tools.js +7 -7
  349. package/dist/tools/reload-result.d.ts +14 -0
  350. package/dist/tools/reload-result.js +29 -0
  351. package/dist/tools/snapshot-delta.d.ts +4 -5
  352. package/dist/tools/snapshot-delta.js +0 -0
  353. package/dist/tools/tool-kit.d.ts +8 -0
  354. package/dist/tools/tool-surface.d.ts +83 -0
  355. package/dist/tools/tool-surface.js +204 -0
  356. package/dist/tools/tools-helpers.js +6 -6
  357. package/dist/tools/tools.d.ts +15 -0
  358. package/dist/tools/tools.js +53 -17
  359. package/dist/tools/unadvertised-help.d.ts +5 -0
  360. package/dist/tools/unadvertised-help.js +39 -0
  361. package/dist/trace.d.ts +69 -0
  362. package/dist/trace.js +150 -0
  363. package/dist/update/nudge-credit.d.ts +4 -0
  364. package/dist/update/nudge-credit.js +53 -0
  365. package/dist/update/sdk-sync.d.ts +21 -0
  366. package/dist/update/sdk-sync.js +42 -0
  367. package/dist/update/update-checker.js +3 -3
  368. package/dist/update/update-nudge.d.ts +63 -6
  369. package/dist/update/update-nudge.js +128 -15
  370. package/dist/update/updater.d.ts +1 -9
  371. package/dist/update/updater.js +13 -8
  372. package/dist/version/peer-announce.d.ts +4 -0
  373. package/dist/version/peer-announce.js +30 -0
  374. package/dist/{server-version.js → version/server-version.js} +5 -1
  375. package/dist/version/version-nudge.d.ts +28 -0
  376. package/dist/version/version-nudge.js +40 -0
  377. package/dist/version/version-skew.d.ts +66 -0
  378. package/dist/version/version-skew.js +71 -0
  379. package/dist/visual/visual-diff.js +2 -2
  380. package/dist/visual/visual-tools.js +4 -4
  381. package/docs/agent-cheatsheet.md +85 -0
  382. package/docs/architecture.md +113 -0
  383. package/docs/benchmarks.md +255 -0
  384. package/docs/debugging.md +102 -0
  385. package/docs/deploy-checks.md +69 -0
  386. package/docs/desktop-apps.md +256 -0
  387. package/docs/enterprise.md +62 -0
  388. package/docs/fixtures.md +28 -0
  389. package/docs/flows.md +166 -0
  390. package/docs/getting-started.md +469 -0
  391. package/docs/human-control.md +63 -0
  392. package/docs/integration-patterns.md +171 -0
  393. package/docs/local-registry.md +126 -0
  394. package/docs/mint.json +61 -0
  395. package/docs/multi-agent-testing.md +55 -0
  396. package/docs/platform-integration.md +122 -0
  397. package/docs/telemetry-contract.md +214 -0
  398. package/docs/telemetry.md +184 -0
  399. package/docs/testing.md +82 -0
  400. package/docs/token-efficiency.md +80 -0
  401. package/docs/usage.md +1046 -0
  402. package/package.json +8 -5
  403. package/dist/cli-launch.js +0 -112
  404. package/dist/daemon-resilience.d.ts +0 -22
  405. package/dist/daemon-resilience.js +0 -85
  406. package/dist/mcp-proxy.d.ts +0 -67
  407. package/dist/mcp-proxy.js +0 -337
  408. package/dist/tools/profiles.d.ts +0 -37
  409. package/dist/tools/profiles.js +0 -139
  410. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  411. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  412. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  413. /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
  414. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  415. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  416. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -5,7 +5,11 @@
5
5
  * imports are unchanged.
6
6
  */
7
7
  export const CLI_USAGE = `usage:
8
- reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] (wire Reticle into the project in this directory)
8
+ reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>] (wire Reticle into the project in this directory)
9
+ --app picks WHICH app in a monorepo, when several are found
10
+ --no-mcp skips MORE than the server registration: also the agent rule files
11
+ (CLAUDE.md / AGENTS.md / .cursor) and the /reticle command, because all three
12
+ only make sense once the tools are reachable.
9
13
  reticle serve [--port N] [--drive <url>] [--headless] [--http] [--http-port N] [--http-token T]
10
14
  reticle stop [--port N] [--quiet]
11
15
  reticle status [--port N]
@@ -56,10 +60,10 @@ const VERSION_COMMAND = 'version';
56
60
  const TELEMETRY_COMMAND = 'telemetry';
57
61
  const FEEDBACK_COMMAND = 'feedback';
58
62
  const IDENTIFY_COMMAND = 'identify';
59
- export const COMPANY_FLAG = '--company';
60
- export const EMAIL_FLAG = '--email';
61
- export const CONTEXT_FLAG = '--context';
62
- export const FORGET_FLAG = '--forget';
63
+ const COMPANY_FLAG = '--company';
64
+ const EMAIL_FLAG = '--email';
65
+ const CONTEXT_FLAG = '--context';
66
+ const FORGET_FLAG = '--forget';
63
67
  /** `reticle feedback --rating 4 "the words"` — the human half of the channel. */
64
68
  export const RATING_FLAG = '--rating';
65
69
  export const BUG_FLAG = '--bug';
@@ -118,7 +122,7 @@ const KNOWN_COMMANDS = new Set([
118
122
  ]);
119
123
  /** The subcommand name if we recognize it, else `unknown`. Bare `reticle` reports `help`. */
120
124
  export function knownCommand(arg) {
121
- if (arg === undefined || arg === '')
125
+ if (arg === undefined || '' === arg)
122
126
  return 'help';
123
127
  return KNOWN_COMMANDS.has(arg) ? arg : UNKNOWN_COMMAND;
124
128
  }
@@ -128,25 +132,40 @@ export const HEADED_FLAG = '--headed';
128
132
  * and every "did it actually do anything?" question cost a round-trip. CI passes this (or just sets
129
133
  * CI, which flips the default) because there is no display there to be headed on.
130
134
  */
131
- export const HEADLESS_FLAG = '--headless';
135
+ const HEADLESS_FLAG = '--headless';
132
136
  export const PORT_FLAG = '--port';
133
137
  export const DRIVE_FLAG = '--drive';
134
138
  const QUIET_FLAG = '--quiet';
135
139
  const DRY_RUN_FLAG = '--dry-run';
136
140
  const YES_FLAG = '--yes';
137
141
  const NO_MCP_FLAG = '--no-mcp';
142
+ const APP_FLAG = '--app';
138
143
  const NO_INSTALL_FLAG = '--no-install';
139
144
  export const HTTP_FLAG = '--http';
140
145
  export const HTTP_PORT_FLAG = '--http-port';
141
146
  export const HTTP_TOKEN_FLAG = '--http-token';
142
147
  const TIMEOUT_FLAG = '--timeout';
143
148
  const STORAGE_STATE_FLAG = '--storage-state';
144
- /**
145
- * `serve` / `mcp` / `_daemon` own the browser POOL, which backs automated work — leased contexts for
146
- * parallel agents, flow replay, the spec runner. Those are batch: nobody is watching, and launching
147
- * them headed changed timing enough to break four e2e specs. The headed default belongs to the
148
- * INTERACTIVE command (`drive`), where a human asked to see the run. `--headed` still opts in here.
149
- */
149
+ const unknownArgument = (arg) => ({
150
+ kind: 'error',
151
+ message: `unknown argument '${arg}'`,
152
+ });
153
+ const missingValue = (flag) => ({
154
+ kind: 'error',
155
+ message: `${flag} needs a value`,
156
+ });
157
+ const notANumber = (flag, value) => ({
158
+ kind: 'error',
159
+ message: `${flag} expects a number, got '${value}'`,
160
+ });
161
+ const missingOperand = (command, what) => ({
162
+ kind: 'error',
163
+ message: `${command} needs ${what}`,
164
+ });
165
+ const unknownCommand = (command) => ({
166
+ kind: 'error',
167
+ message: `unknown command '${command}'`,
168
+ });
150
169
  function parseServeFlags(args, defaultPort, _defaultHeadless) {
151
170
  let port = defaultPort;
152
171
  let driveUrl;
@@ -157,21 +176,25 @@ function parseServeFlags(args, defaultPort, _defaultHeadless) {
157
176
  let i = 0;
158
177
  while (i < args.length) {
159
178
  const arg = args[i];
179
+ // `i < args.length` already guarantees this, but the index signature does not say so and the
180
+ // unknown-argument error needs a real string to name.
181
+ if (arg === undefined)
182
+ break;
160
183
  if (arg === PORT_FLAG) {
161
184
  i++;
162
185
  const n = args[i];
163
186
  if (n === undefined)
164
- return { kind: 'error', message: CLI_USAGE };
187
+ return missingValue(PORT_FLAG);
165
188
  const parsed = parseInt(n, 10);
166
189
  if (isNaN(parsed))
167
- return { kind: 'error', message: CLI_USAGE };
190
+ return notANumber(PORT_FLAG, n);
168
191
  port = parsed;
169
192
  }
170
193
  else if (arg === DRIVE_FLAG) {
171
194
  i++;
172
195
  driveUrl = args[i];
173
196
  if (driveUrl === undefined)
174
- return { kind: 'error', message: CLI_USAGE };
197
+ return missingValue(DRIVE_FLAG);
175
198
  }
176
199
  else if (arg === HEADED_FLAG) {
177
200
  headless = false;
@@ -186,20 +209,20 @@ function parseServeFlags(args, defaultPort, _defaultHeadless) {
186
209
  i++;
187
210
  const n = args[i];
188
211
  if (n === undefined)
189
- return { kind: 'error', message: CLI_USAGE };
212
+ return missingValue(HTTP_PORT_FLAG);
190
213
  const parsed = parseInt(n, 10);
191
214
  if (isNaN(parsed))
192
- return { kind: 'error', message: CLI_USAGE };
215
+ return notANumber(HTTP_PORT_FLAG, n);
193
216
  httpPort = parsed;
194
217
  }
195
218
  else if (arg === HTTP_TOKEN_FLAG) {
196
219
  i++;
197
220
  httpToken = args[i];
198
221
  if (httpToken === undefined)
199
- return { kind: 'error', message: CLI_USAGE };
222
+ return missingValue(HTTP_TOKEN_FLAG);
200
223
  }
201
224
  else {
202
- return { kind: 'error', message: CLI_USAGE };
225
+ return unknownArgument(arg);
203
226
  }
204
227
  i++;
205
228
  }
@@ -215,7 +238,7 @@ function parseServeFlags(args, defaultPort, _defaultHeadless) {
215
238
  }
216
239
  function parsePortFlag(args, defaultPort) {
217
240
  const idx = args.indexOf(PORT_FLAG);
218
- if (idx === -1)
241
+ if (-1 === idx)
219
242
  return defaultPort;
220
243
  const n = args[idx + 1];
221
244
  if (n === undefined)
@@ -234,17 +257,17 @@ function parseDriveSuffix(args, port, defaultHeadless) {
234
257
  headless = true;
235
258
  }
236
259
  else if (arg.startsWith('--')) {
237
- return { kind: 'error', message: CLI_USAGE };
260
+ return unknownArgument(arg);
238
261
  }
239
262
  else if (driveUrl === undefined) {
240
263
  driveUrl = arg;
241
264
  }
242
265
  else {
243
- return { kind: 'error', message: CLI_USAGE };
266
+ return unknownArgument(arg);
244
267
  }
245
268
  }
246
269
  if (driveUrl === undefined)
247
- return { kind: 'error', message: CLI_USAGE };
270
+ return missingOperand(DRIVE_COMMAND, 'a url');
248
271
  return { kind: 'ok', port, driveUrl, headless };
249
272
  }
250
273
  /**
@@ -268,32 +291,32 @@ function parseVerifySuffix(args) {
268
291
  i++;
269
292
  const n = args[i];
270
293
  if (n === undefined)
271
- return { kind: 'error', message: CLI_USAGE };
294
+ return missingValue(TIMEOUT_FLAG);
272
295
  const parsed = parseInt(n, 10);
273
296
  if (isNaN(parsed))
274
- return { kind: 'error', message: CLI_USAGE };
297
+ return notANumber(TIMEOUT_FLAG, n);
275
298
  timeoutMs = parsed;
276
299
  }
277
300
  else if (arg === STORAGE_STATE_FLAG) {
278
301
  i++;
279
302
  const v = args[i];
280
303
  if (v === undefined)
281
- return { kind: 'error', message: CLI_USAGE };
304
+ return missingValue(STORAGE_STATE_FLAG);
282
305
  storageState = v;
283
306
  }
284
307
  else if (arg.startsWith('--')) {
285
- return { kind: 'error', message: CLI_USAGE };
308
+ return unknownArgument(arg);
286
309
  }
287
310
  else if (url === undefined) {
288
311
  url = arg;
289
312
  }
290
313
  else {
291
- return { kind: 'error', message: CLI_USAGE };
314
+ return unknownArgument(arg);
292
315
  }
293
316
  i++;
294
317
  }
295
318
  if (url === undefined)
296
- return { kind: 'error', message: CLI_USAGE };
319
+ return missingOperand(VERIFY_COMMAND, 'a url');
297
320
  return {
298
321
  kind: 'ok',
299
322
  url,
@@ -307,19 +330,31 @@ function parseInitFlags(args) {
307
330
  let mcp = true;
308
331
  let dryRun = false;
309
332
  let install = true;
333
+ let app;
310
334
  let i = 0;
311
335
  while (i < args.length) {
312
336
  const arg = args[i];
337
+ // `i < args.length` already guarantees this, but the index signature does not say so and the
338
+ // unknown-argument error needs a real string to name.
339
+ if (arg === undefined)
340
+ break;
313
341
  if (arg === PORT_FLAG) {
314
342
  i++;
315
343
  const n = args[i];
316
344
  if (n === undefined)
317
- return { kind: 'error', message: CLI_USAGE };
345
+ return missingValue(PORT_FLAG);
318
346
  const parsed = parseInt(n, 10);
319
347
  if (isNaN(parsed))
320
- return { kind: 'error', message: CLI_USAGE };
348
+ return notANumber(PORT_FLAG, n);
321
349
  port = parsed;
322
350
  }
351
+ else if (arg === APP_FLAG) {
352
+ i++;
353
+ const value = args[i];
354
+ if (value === undefined)
355
+ return missingValue(APP_FLAG);
356
+ app = value;
357
+ }
323
358
  else if (arg === NO_MCP_FLAG) {
324
359
  mcp = false;
325
360
  }
@@ -333,15 +368,29 @@ function parseInitFlags(args) {
333
368
  // Accepted for scripting/CI; init has no interactive prompts today.
334
369
  }
335
370
  else {
336
- return { kind: 'error', message: CLI_USAGE };
371
+ return unknownArgument(arg);
337
372
  }
338
373
  i++;
339
374
  }
340
- return { kind: 'ok', port, mcp, dryRun, install };
375
+ return { kind: 'ok', port, mcp, dryRun, install, app };
341
376
  }
342
377
  /** Pure CLI arg parser — exported for unit tests. argv = process.argv.slice(2). */
343
378
  const SINCE_FLAG = '--since';
344
379
  /** Parse `[--since <ref>] [file...]` shared by `affected` and `gate`. */
380
+ /**
381
+ * What `reticle gate` / `reticle affected` mean with NOTHING after them: the working tree.
382
+ *
383
+ * They used to mean a usage error — while the rule `reticle init` writes into the agent's own
384
+ * instruction file says, in as many words, "run `reticle gate`". An instruction the tool rejects is
385
+ * worse than no instruction: the agent spends a turn on it and concludes Reticle is broken.
386
+ *
387
+ * HEAD is the ref that answers the question being asked. Explicit files or an explicit --since still
388
+ * win; this only fills the empty case.
389
+ */
390
+ const WORKING_TREE_REF = 'HEAD';
391
+ function implicitSince(files) {
392
+ return 0 === files.length ? WORKING_TREE_REF : undefined;
393
+ }
345
394
  function parseTargetArgs(rest) {
346
395
  const files = [];
347
396
  let since;
@@ -365,27 +414,37 @@ function parseTargetArgs(rest) {
365
414
  */
366
415
  export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
367
416
  // Bare `reticle` is `serve` — a pool-owning command, so headless like the rest of that family.
368
- if (argv.length === 0)
417
+ if (0 === argv.length)
369
418
  return { kind: 'serve', port: defaultPort, headless: true, http: false };
370
419
  const [cmd, ...rest] = argv;
420
+ // Guaranteed by the bare-argv check above; restated so the unknown-command error can name it.
421
+ if (cmd === undefined)
422
+ return { kind: 'error', message: CLI_USAGE };
371
423
  // `version` (or the conventional -v/--version flags) prints the running version — the diagnostic the
372
424
  // troubleshooting docs lean on to confirm which npx-resolved build is actually executing.
373
- if (cmd === VERSION_COMMAND || cmd === '--version' || cmd === '-v')
425
+ if (cmd === VERSION_COMMAND || '--version' === cmd || '-v' === cmd)
374
426
  return { kind: 'version' };
375
427
  // `help` (and the conventional -h/--help) print usage to stdout and exit 0 — the universal first move
376
428
  // for a new user, which otherwise fell through to a JSON error with exit 1.
377
- if (cmd === 'help' || cmd === '--help' || cmd === '-h')
429
+ if ('help' === cmd || '--help' === cmd || '-h' === cmd)
378
430
  return { kind: 'help' };
379
431
  switch (cmd) {
380
432
  case INIT_COMMAND: {
381
433
  const r = parseInitFlags(rest);
382
- if (r.kind === 'error')
434
+ if ('error' === r.kind)
383
435
  return r;
384
- return { kind: 'init', port: r.port, mcp: r.mcp, dryRun: r.dryRun, install: r.install };
436
+ return {
437
+ kind: 'init',
438
+ port: r.port,
439
+ mcp: r.mcp,
440
+ dryRun: r.dryRun,
441
+ install: r.install,
442
+ app: r.app,
443
+ };
385
444
  }
386
445
  case SERVE_COMMAND: {
387
446
  const r = parseServeFlags(rest, defaultPort, defaultHeadless);
388
- if (r.kind === 'error')
447
+ if ('error' === r.kind)
389
448
  return r;
390
449
  return {
391
450
  kind: 'serve',
@@ -424,7 +483,7 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
424
483
  case IDENTIFY_COMMAND: {
425
484
  const flag = (name) => {
426
485
  const at = rest.indexOf(name);
427
- return at === -1 ? undefined : rest[at + 1];
486
+ return -1 === at ? undefined : rest[at + 1];
428
487
  };
429
488
  const context = flag(CONTEXT_FLAG);
430
489
  const company = flag(COMPANY_FLAG);
@@ -439,13 +498,13 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
439
498
  }
440
499
  case FEEDBACK_COMMAND: {
441
500
  const ratingAt = rest.indexOf(RATING_FLAG);
442
- const rating = ratingAt === -1 ? undefined : Number(rest[ratingAt + 1]);
501
+ const rating = -1 === ratingAt ? undefined : Number(rest[ratingAt + 1]);
443
502
  if (rating !== undefined && (!Number.isInteger(rating) || rating < 1 || rating > 5)) {
444
503
  return { kind: 'error', message: `${RATING_FLAG} takes a whole number from 1 to 5` };
445
504
  }
446
505
  // Everything that is not a flag or the rating's value is the message. Quoting is the user's
447
506
  // job for shell reasons, but joining the remainder means an unquoted sentence still works.
448
- const consumed = new Set(ratingAt === -1 ? [] : [ratingAt, ratingAt + 1]);
507
+ const consumed = new Set(-1 === ratingAt ? [] : [ratingAt, ratingAt + 1]);
449
508
  const text = rest
450
509
  .filter((arg, i) => !consumed.has(i) && !arg.startsWith('--'))
451
510
  .join(' ')
@@ -465,13 +524,13 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
465
524
  }
466
525
  case DRIVE_COMMAND: {
467
526
  const r = parseDriveSuffix(rest, defaultPort, defaultHeadless);
468
- if (r.kind === 'error')
527
+ if ('error' === r.kind)
469
528
  return r;
470
529
  return { kind: 'drive', port: r.port, driveUrl: r.driveUrl, headless: r.headless };
471
530
  }
472
531
  case DAEMON_INNER_COMMAND: {
473
532
  const r = parseServeFlags(rest, defaultPort, defaultHeadless);
474
- if (r.kind === 'error')
533
+ if ('error' === r.kind)
475
534
  return r;
476
535
  return {
477
536
  kind: '_daemon',
@@ -485,7 +544,7 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
485
544
  }
486
545
  case VERIFY_COMMAND: {
487
546
  const r = parseVerifySuffix(rest);
488
- if (r.kind === 'error')
547
+ if ('error' === r.kind)
489
548
  return r;
490
549
  return {
491
550
  kind: 'verify',
@@ -505,21 +564,13 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
505
564
  }
506
565
  case AFFECTED_COMMAND: {
507
566
  const t = parseTargetArgs(rest);
508
- if (t.files.length === 0 && t.since === undefined) {
509
- return { kind: 'error', message: 'usage: reticle affected [--since <ref>] [file...]' };
510
- }
511
- return {
512
- kind: 'affected',
513
- files: t.files,
514
- ...(t.since === undefined ? {} : { since: t.since }),
515
- };
567
+ const since = t.since ?? implicitSince(t.files);
568
+ return { kind: 'affected', files: t.files, ...(since === undefined ? {} : { since }) };
516
569
  }
517
570
  case GATE_COMMAND: {
518
571
  const t = parseTargetArgs(rest);
519
- if (t.files.length === 0 && t.since === undefined) {
520
- return { kind: 'error', message: 'usage: reticle gate [--since <ref>] [file...]' };
521
- }
522
- return { kind: 'gate', files: t.files, ...(t.since === undefined ? {} : { since: t.since }) };
572
+ const since = t.since ?? implicitSince(t.files);
573
+ return { kind: 'gate', files: t.files, ...(since === undefined ? {} : { since }) };
523
574
  }
524
575
  case WATCH_COMMAND: {
525
576
  // `reticle watch [url]` — on file save, report which saved flows must re-verify.
@@ -532,7 +583,7 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
532
583
  return { kind: 'rollback' };
533
584
  case MCP_COMMAND: {
534
585
  const r = parseServeFlags(rest, defaultPort, defaultHeadless);
535
- if (r.kind === 'error')
586
+ if ('error' === r.kind)
536
587
  return r;
537
588
  return {
538
589
  kind: 'mcp',
@@ -545,6 +596,8 @@ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
545
596
  };
546
597
  }
547
598
  default:
548
- return { kind: 'error', message: CLI_USAGE };
599
+ // A bare `reticle` never lands here — it defaults to `serve` above — so `cmd` is always a real
600
+ // word somebody typed, and naming it is strictly more useful than reprinting the whole help.
601
+ return unknownCommand(cmd);
549
602
  }
550
603
  }
@@ -7,6 +7,18 @@
7
7
  * 3..reticle.json "port" field in the cwd ← per-project isolation
8
8
  * 4. RETICLE_DEFAULT_PORT (4400)
9
9
  */
10
+ /**
11
+ * Ports that belong to a DEV SERVER, not to Reticle's bridge. Next defaults to 3000, Vite to 5173,
12
+ * Astro to 4321, and so on.
13
+ *
14
+ * `.reticle.json`'s `port` is the bridge — the daemon ↔ SDK channel, default 4400 — and it has
15
+ * nothing to do with the port the app is served on. Setting it to the dev-server port makes the
16
+ * daemon try to bind the port the app already holds; what the user sees is either a bind failure or
17
+ * a daemon that never connects, neither of which mentions the actual mistake. The old setup skill
18
+ * asked "what port does your dev server run on?" a few lines before showing this field, which is how
19
+ * the confusion got manufactured in the first place.
20
+ */
21
+ export declare const DEV_SERVER_PORTS: ReadonlySet<number>;
10
22
  export declare function isLikelyDevServerPort(port: number): boolean;
11
23
  /** The warning printed when a project pins the bridge to something that looks like its dev server. */
12
24
  export declare function devServerPortWarning(port: number): string;
@@ -19,7 +19,7 @@ import { readFileSync } from 'node:fs';
19
19
  * asked "what port does your dev server run on?" a few lines before showing this field, which is how
20
20
  * the confusion got manufactured in the first place.
21
21
  */
22
- const DEV_SERVER_PORTS = new Set([
22
+ export const DEV_SERVER_PORTS = new Set([
23
23
  3000, 3001, 4200, 4321, 5000, 5173, 5174, 8000, 8080, 8100, 9000,
24
24
  ]);
25
25
  export function isLikelyDevServerPort(port) {
@@ -37,9 +37,9 @@ export function readProjectPort(cwd) {
37
37
  try {
38
38
  const raw = readFileSync(`${cwd}/.reticle.json`, 'utf8');
39
39
  const config = JSON.parse(raw);
40
- if (typeof config === 'object' && config !== null) {
40
+ if ('object' === typeof config && config !== null) {
41
41
  const p = config['port'];
42
- if (typeof p === 'number' && Number.isInteger(p) && p > 0 && p < 65536)
42
+ if ('number' === typeof p && Number.isInteger(p) && p > 0 && p < 65536)
43
43
  return p;
44
44
  }
45
45
  }
@@ -57,9 +57,9 @@ export function readProjectId(cwd) {
57
57
  try {
58
58
  const raw = readFileSync(`${cwd}/.reticle.json`, 'utf8');
59
59
  const config = JSON.parse(raw);
60
- if (typeof config === 'object' && config !== null) {
60
+ if ('object' === typeof config && config !== null) {
61
61
  const id = config['projectId'];
62
- if (typeof id === 'string' && id.length > 0)
62
+ if ('string' === typeof id && id.length > 0)
63
63
  return id;
64
64
  }
65
65
  }
@@ -76,16 +76,16 @@ export function readProjectId(cwd) {
76
76
  export function readJournalEnabled(cwd, env) {
77
77
  if (env !== undefined) {
78
78
  const v = env.trim().toLowerCase();
79
- if (v === '0' || v === 'false' || v === 'off')
79
+ if ('0' === v || 'false' === v || 'off' === v)
80
80
  return false;
81
- if (v === '1' || v === 'true' || v === 'on')
81
+ if ('1' === v || 'true' === v || 'on' === v)
82
82
  return true;
83
83
  }
84
84
  try {
85
85
  const raw = readFileSync(`${cwd}/.reticle.json`, 'utf8');
86
86
  const config = JSON.parse(raw);
87
- if (typeof config === 'object' && config !== null) {
88
- if (config['journal'] === false)
87
+ if ('object' === typeof config && config !== null) {
88
+ if (false === config['journal'])
89
89
  return false;
90
90
  }
91
91
  }
@@ -0,0 +1,4 @@
1
+ /** `reticle update` — install the latest server version, sync the app's SDK, and restart. */
2
+ export declare function handleUpdate(): Promise<void>;
3
+ /** `reticle rollback` — restore the previous server version and restart. */
4
+ export declare function handleRollback(): Promise<void>;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The self-update command pair — `reticle update` and `reticle rollback`.
3
+ *
4
+ * Split out of cli.ts, which sits at the 600-line cap: these two are one cohesive unit (swap the
5
+ * installed version, restart) and the file they came from is the CLI's dispatch table, which grows
6
+ * for entirely different reasons.
7
+ */
8
+ import { checkForUpdate } from '../update/update-checker.js';
9
+ import { updateTarget } from '../update/update-nudge.js';
10
+ import { applyUpdate, rollback } from '../update/updater.js';
11
+ import { SERVER_VERSION } from '../version/server-version.js';
12
+ import { log } from '../log.js';
13
+ import { readFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { reticleDepsOf, sdkSyncCommand } from '../update/sdk-sync.js';
16
+ import { detectPackageManager } from '../init/detect.js';
17
+ import { buildNodeIo } from '../init/node-io.js';
18
+ /**
19
+ * Bring the SDK in the CURRENT project to the version being installed.
20
+ *
21
+ * Best-effort and non-fatal on purpose: `reticle update` is run from wherever the human happens to
22
+ * be, which is often not an app at all. A directory with no manifest, or one that has none of our
23
+ * packages, simply has nothing to sync — that is a normal outcome, not a failure, and it must never
24
+ * stop the CLI half from happening.
25
+ */
26
+ function syncProjectSdk(target) {
27
+ const cwd = process.cwd();
28
+ let manifest;
29
+ try {
30
+ manifest = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'));
31
+ }
32
+ catch {
33
+ return; // not an app directory
34
+ }
35
+ const packages = reticleDepsOf(manifest);
36
+ const io = buildNodeIo(cwd);
37
+ const pm = detectPackageManager(new Set(io.rootFiles()), new Set(io.listDirs('node_modules')));
38
+ const cmd = sdkSyncCommand(pm, packages, target);
39
+ if (null === cmd) {
40
+ log('reticle_update_sdk', { synced: false, reason: 'no @reticlehq packages in this project' });
41
+ return;
42
+ }
43
+ const ok = io.exec(cmd.command, cmd.args);
44
+ log('reticle_update_sdk', { synced: ok, packages, to: target, packageManager: pm });
45
+ if (!ok) {
46
+ log('reticle_update_sdk_failed', {
47
+ hint: `run \`${cmd.command} ${cmd.args.join(' ')}\` by hand, then restart the dev server`,
48
+ });
49
+ }
50
+ }
51
+ /** `reticle update` — install the latest server version, sync the app's SDK, and restart. */
52
+ export async function handleUpdate() {
53
+ try {
54
+ const manifest = await checkForUpdate(SERVER_VERSION, () => Date.now());
55
+ // Direction, not inequality: the registry being DIFFERENT is not the registry being newer, and
56
+ // the old gate happily installed a downgrade — reported by a user. See updateTarget.
57
+ const target = updateTarget(manifest);
58
+ if (target === undefined) {
59
+ log('reticle_update', {
60
+ ok: false,
61
+ message: 'already on the latest version',
62
+ version: SERVER_VERSION,
63
+ });
64
+ return;
65
+ }
66
+ log('reticle_update', { ok: true, from: SERVER_VERSION, to: target });
67
+ // The app's SDK FIRST, then the CLI. `reticle update` used to swap only the CLI, so the command
68
+ // whose job is keeping an install current was itself a way to produce a version-skewed pair —
69
+ // and the skew message told people to run it to fix an outdated SDK, which it could not do.
70
+ //
71
+ // Before the CLI swap because `applyUpdate` never returns (it execs and exits). If this half
72
+ // fails the daemon is still the older one, which is the direction the HELLO check names clearly
73
+ // and a re-run fixes; the reverse would leave a new daemon talking to an old page.
74
+ syncProjectSdk(target);
75
+ await applyUpdate(target); // calls process.exit; Claude Code restarts
76
+ }
77
+ catch (error) {
78
+ log('reticle_update_failed', { error: error instanceof Error ? error.message : String(error) });
79
+ process.exitCode = 1;
80
+ }
81
+ }
82
+ /** `reticle rollback` — restore the previous server version and restart. */
83
+ export async function handleRollback() {
84
+ try {
85
+ await rollback(); // calls process.exit
86
+ }
87
+ catch (error) {
88
+ log('reticle_rollback_failed', {
89
+ error: error instanceof Error ? error.message : String(error),
90
+ });
91
+ process.exitCode = 1;
92
+ }
93
+ }
@@ -12,18 +12,18 @@
12
12
  import { join, basename } from 'node:path';
13
13
  import { randomUUID } from 'node:crypto';
14
14
  import { RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir, RunAgentKind, RunFramework, RunProfile, RunTrigger, VerdictStatus, } from '@reticlehq/core';
15
- import { start } from './index.js';
16
- import { resolveCloudConfig, syncRunToCloud, SyncOutcome } from './cloud/cloud-sync.js';
17
- import { ReticleRunner } from './runs/reticle-runner.js';
18
- import { createRunnerPort } from './runs/runner-port.js';
19
- import { RunStore } from './runs/run-store.js';
20
- import { renderRunReport } from './runs/render-report.js';
21
- import { BaselineStore } from './project/baselines.js';
22
- import { RecordingStore } from './flows/recordings.js';
23
- import { FlowStore } from './flows/flows.js';
24
- import { ProjectStore } from './project/project-store.js';
25
- import { AnnotationStore } from './flows/annotation-store.js';
26
- import { createNodeFileSystem } from './project/fs-port.js';
15
+ import { start } from '../index.js';
16
+ import { resolveCloudConfig, syncRunToCloud, SyncOutcome } from '../cloud/cloud-sync.js';
17
+ import { ReticleRunner } from '../runs/reticle-runner.js';
18
+ import { createRunnerPort } from '../runs/runner-port.js';
19
+ import { RunStore } from '../runs/run-store.js';
20
+ import { renderRunReport } from '../runs/render-report.js';
21
+ import { BaselineStore } from '../project/baselines.js';
22
+ import { RecordingStore } from '../flows/recordings.js';
23
+ import { FlowStore } from '../flows/flows.js';
24
+ import { ProjectStore } from '../project/project-store.js';
25
+ import { AnnotationStore } from '../flows/annotation-store.js';
26
+ import { createNodeFileSystem } from '../project/fs-port.js';
27
27
  const EXIT_PASS = 0;
28
28
  const EXIT_FAIL = 1;
29
29
  const DEFAULT_SESSION_TIMEOUT_MS = 15_000;
@@ -63,7 +63,7 @@ export async function runVerify(args, ports) {
63
63
  return;
64
64
  }
65
65
  const names = await conn.listFlows();
66
- if (names.length === 0) {
66
+ if (0 === names.length) {
67
67
  ports.fail(MSG_NO_FLOWS);
68
68
  ports.exit(EXIT_FAIL);
69
69
  return;
@@ -89,7 +89,7 @@ export async function runVerify(args, ports) {
89
89
  */
90
90
  async function pushRunToCloud(run, ports) {
91
91
  const config = resolveCloudConfig(process.env);
92
- if (config === null)
92
+ if (null === config)
93
93
  return;
94
94
  const result = await syncRunToCloud(run, config, (url, init) => fetch(url, init));
95
95
  if (result.outcome === SyncOutcome.SYNCED) {