@reticlehq/server 2.3.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 (424) 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} +105 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -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/cli-parse.d.ts +143 -0
  25. package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
  26. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
  27. package/dist/{cli-port.js → cli/cli-port.js} +29 -8
  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 +182 -93
  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.d.ts +0 -6
  88. package/dist/flows/decision.js +64 -8
  89. package/dist/flows/empty-flow.d.ts +6 -0
  90. package/dist/flows/empty-flow.js +24 -0
  91. package/dist/flows/flake-store.d.ts +0 -1
  92. package/dist/flows/flake-store.js +1 -1
  93. package/dist/flows/flake.d.ts +2 -1
  94. package/dist/flows/flake.js +2 -2
  95. package/dist/flows/flow-classify.js +12 -0
  96. package/dist/flows/flow-replay-run.js +4 -4
  97. package/dist/flows/flow-replay.d.ts +5 -7
  98. package/dist/flows/flow-replay.js +114 -47
  99. package/dist/flows/flow-report.js +1 -1
  100. package/dist/flows/flow-scope.d.ts +1 -6
  101. package/dist/flows/flow-step-runners.d.ts +26 -6
  102. package/dist/flows/flow-step-runners.js +157 -6
  103. package/dist/flows/flow-success.js +20 -15
  104. package/dist/flows/flow-tools.js +67 -13
  105. package/dist/flows/flows.js +15 -11
  106. package/dist/flows/gate.d.ts +4 -3
  107. package/dist/flows/gate.js +1 -1
  108. package/dist/flows/heal-run.d.ts +0 -8
  109. package/dist/flows/heal-run.js +5 -5
  110. package/dist/flows/heal.js +3 -3
  111. package/dist/flows/predicate-to-expect.d.ts +38 -0
  112. package/dist/flows/predicate-to-expect.js +95 -0
  113. package/dist/flows/replay.d.ts +16 -1
  114. package/dist/flows/replay.js +58 -22
  115. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  116. package/dist/flows/role-anchor-nearest.js +45 -0
  117. package/dist/flows/server-verify.d.ts +1 -1
  118. package/dist/flows/server-verify.js +5 -5
  119. package/dist/flows/verify-change-tools.js +32 -10
  120. package/dist/flows/verify-change-verdict.d.ts +15 -0
  121. package/dist/flows/verify-change-verdict.js +25 -0
  122. package/dist/flows/watch-batcher.d.ts +3 -2
  123. package/dist/honesty/already-true.d.ts +2 -0
  124. package/dist/honesty/already-true.js +31 -0
  125. package/dist/honesty/blind-spots.d.ts +16 -3
  126. package/dist/honesty/blind-spots.js +24 -12
  127. package/dist/honesty/honesty.d.ts +3 -2
  128. package/dist/honesty/honesty.js +4 -4
  129. package/dist/honesty/uncaptured-bodies.js +1 -1
  130. package/dist/honesty/unread-outcome.js +2 -2
  131. package/dist/honesty/verified.d.ts +22 -3
  132. package/dist/honesty/verified.js +48 -8
  133. package/dist/http-server.js +14 -7
  134. package/dist/hunt/hunt-report.d.ts +2 -1
  135. package/dist/hunt/hunt-report.js +2 -2
  136. package/dist/index.d.ts +8 -6
  137. package/dist/index.js +32 -25
  138. package/dist/init/agent-rules.d.ts +2 -1
  139. package/dist/init/agent-rules.js +45 -7
  140. package/dist/init/app-choice.d.ts +20 -0
  141. package/dist/init/app-choice.js +25 -0
  142. package/dist/init/astro-host.d.ts +21 -0
  143. package/dist/init/astro-host.js +27 -0
  144. package/dist/init/astro-patch.d.ts +16 -0
  145. package/dist/init/astro-patch.js +240 -0
  146. package/dist/init/capabilities.d.ts +17 -0
  147. package/dist/init/capabilities.js +54 -0
  148. package/dist/init/cra.d.ts +37 -0
  149. package/dist/init/cra.js +90 -0
  150. package/dist/init/cursor.js +39 -4
  151. package/dist/init/desktop-doctor.d.ts +3 -2
  152. package/dist/init/desktop-doctor.js +4 -4
  153. package/dist/init/detect.d.ts +26 -0
  154. package/dist/init/detect.js +78 -5
  155. package/dist/init/mcp-registered.d.ts +12 -0
  156. package/dist/init/mcp-registered.js +14 -0
  157. package/dist/init/mcp.d.ts +27 -2
  158. package/dist/init/mcp.js +40 -4
  159. package/dist/init/next-patch.d.ts +39 -0
  160. package/dist/init/next-patch.js +196 -0
  161. package/dist/init/node-io.js +48 -6
  162. package/dist/init/patch-kind.d.ts +20 -0
  163. package/dist/init/patch-kind.js +10 -0
  164. package/dist/init/plan-framework.d.ts +50 -0
  165. package/dist/init/plan-framework.js +341 -0
  166. package/dist/init/plan.d.ts +119 -6
  167. package/dist/init/plan.js +230 -131
  168. package/dist/init/project-id.d.ts +5 -9
  169. package/dist/init/project-id.js +10 -22
  170. package/dist/init/run.d.ts +22 -0
  171. package/dist/init/run.js +393 -28
  172. package/dist/init/slash-command.d.ts +27 -0
  173. package/dist/init/slash-command.js +61 -0
  174. package/dist/init/snippets.d.ts +41 -3
  175. package/dist/init/snippets.js +185 -10
  176. package/dist/init/vite-config.d.ts +4 -10
  177. package/dist/init/vite-config.js +15 -7
  178. package/dist/init/workspace-apps.d.ts +23 -0
  179. package/dist/init/workspace-apps.js +68 -0
  180. package/dist/input/driven-redaction.d.ts +15 -0
  181. package/dist/input/driven-redaction.js +76 -0
  182. package/dist/input/network-detail.d.ts +9 -3
  183. package/dist/input/network-detail.js +43 -23
  184. package/dist/input/network-mock-tools.js +2 -2
  185. package/dist/input/network-mock.js +4 -4
  186. package/dist/input/real-input.js +8 -3
  187. package/dist/input/scroll-find.js +2 -2
  188. package/dist/input/scroll-tools.d.ts +0 -5
  189. package/dist/input/scroll-tools.js +11 -4
  190. package/dist/input/viewport-tools.js +2 -2
  191. package/dist/journal/ambient-store.d.ts +0 -2
  192. package/dist/journal/ambient-store.js +1 -1
  193. package/dist/journal/ambient.js +1 -1
  194. package/dist/journal/attach-journal.d.ts +2 -1
  195. package/dist/journal/deviation-report.js +6 -6
  196. package/dist/journal/deviation-service.js +1 -1
  197. package/dist/journal/envelope-store.d.ts +0 -2
  198. package/dist/journal/envelope-store.js +1 -1
  199. package/dist/journal/envelope.d.ts +2 -3
  200. package/dist/journal/envelope.js +3 -3
  201. package/dist/journal/journal-query.js +1 -1
  202. package/dist/journal/journal-recorder.d.ts +2 -1
  203. package/dist/journal/journal-recorder.js +2 -2
  204. package/dist/journal/rollups.js +9 -9
  205. package/dist/journal/session-end.d.ts +2 -1
  206. package/dist/journal/session-journal.js +4 -4
  207. package/dist/license/license.d.ts +0 -14
  208. package/dist/license/license.js +8 -8
  209. package/dist/log.d.ts +10 -0
  210. package/dist/log.js +11 -1
  211. package/dist/mcp/mcp-is-error.d.ts +13 -0
  212. package/dist/mcp/mcp-is-error.js +18 -0
  213. package/dist/mcp/mcp-outage.d.ts +31 -0
  214. package/dist/mcp/mcp-outage.js +48 -0
  215. package/dist/mcp/mcp-proxy.d.ts +124 -0
  216. package/dist/mcp/mcp-proxy.js +697 -0
  217. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  218. package/dist/{mcp.js → mcp/mcp.js} +110 -35
  219. package/dist/mcp/proxy-handshake.d.ts +3 -0
  220. package/dist/mcp/proxy-handshake.js +54 -0
  221. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  222. package/dist/mcp/proxy-lifecycle.js +70 -0
  223. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  224. package/dist/mcp/tool-catalog-cache.js +55 -0
  225. package/dist/oracles/propose-consequences.d.ts +2 -1
  226. package/dist/oracles/propose-consequences.js +16 -11
  227. package/dist/oracles/self-instrument.d.ts +2 -1
  228. package/dist/oracles/self-instrument.js +1 -1
  229. package/dist/phenomena/phenomena.js +3 -3
  230. package/dist/platform.d.ts +16 -0
  231. package/dist/platform.js +16 -0
  232. package/dist/pool/playwright-launcher.d.ts +15 -2
  233. package/dist/pool/playwright-launcher.js +21 -2
  234. package/dist/project/fs-port.js +1 -1
  235. package/dist/project/project-tools.js +2 -2
  236. package/dist/runs/build-verification-run.js +1 -1
  237. package/dist/runs/repair-prompt.d.ts +0 -6
  238. package/dist/runs/repair-prompt.js +3 -3
  239. package/dist/runs/risk-classify.js +1 -1
  240. package/dist/runs/run-diff.d.ts +0 -2
  241. package/dist/runs/run-diff.js +3 -3
  242. package/dist/runs/run-tools.js +2 -2
  243. package/dist/runs/verification-sync.js +2 -2
  244. package/dist/runs/verify-http.js +1 -1
  245. package/dist/runs/verify-server.js +1 -1
  246. package/dist/session/command-timeout.d.ts +11 -3
  247. package/dist/session/command-timeout.js +2 -2
  248. package/dist/session/control-envelope.d.ts +6 -0
  249. package/dist/session/control-envelope.js +7 -1
  250. package/dist/session/dev-server-probe.d.ts +13 -0
  251. package/dist/session/dev-server-probe.js +52 -0
  252. package/dist/session/last-act.d.ts +17 -3
  253. package/dist/session/last-act.js +16 -4
  254. package/dist/session/live-control-tools.js +7 -2
  255. package/dist/session/live-control.js +1 -1
  256. package/dist/session/looks-like-dev-server.d.ts +14 -0
  257. package/dist/session/looks-like-dev-server.js +25 -0
  258. package/dist/session/no-session-diagnosis.d.ts +38 -0
  259. package/dist/session/no-session-diagnosis.js +70 -0
  260. package/dist/session/no-session-watch.d.ts +19 -0
  261. package/dist/session/no-session-watch.js +61 -0
  262. package/dist/session/observed-state.d.ts +4 -0
  263. package/dist/session/observed-state.js +19 -1
  264. package/dist/session/output-budget.js +1 -1
  265. package/dist/session/session-health.d.ts +8 -1
  266. package/dist/session/session-health.js +8 -7
  267. package/dist/session/session-info.d.ts +26 -7
  268. package/dist/session/session-info.js +42 -1
  269. package/dist/session/session-lease.d.ts +0 -11
  270. package/dist/session/session-lease.js +1 -1
  271. package/dist/session/session-manager.d.ts +3 -0
  272. package/dist/session/session-manager.js +58 -9
  273. package/dist/session/session-readiness.d.ts +1 -1
  274. package/dist/session/session-readiness.js +6 -1
  275. package/dist/session/session-reaper.js +1 -1
  276. package/dist/session/session-reconnect.d.ts +32 -0
  277. package/dist/session/session-reconnect.js +42 -0
  278. package/dist/session/session.d.ts +20 -2
  279. package/dist/session/session.js +76 -26
  280. package/dist/session/state-select.d.ts +1 -1
  281. package/dist/session/state-select.js +1 -1
  282. package/dist/status-payload.d.ts +19 -0
  283. package/dist/status-payload.js +11 -0
  284. package/dist/telemetry/argument-shape.js +10 -1
  285. package/dist/telemetry/browser-mode.d.ts +27 -0
  286. package/dist/telemetry/browser-mode.js +33 -0
  287. package/dist/telemetry/bug-found.d.ts +17 -21
  288. package/dist/telemetry/bug-found.js +120 -8
  289. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  290. package/dist/telemetry/cli-telemetry.js +27 -4
  291. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  292. package/dist/telemetry/daemon-telemetry.js +12 -2
  293. package/dist/telemetry/dev-repo.js +4 -4
  294. package/dist/telemetry/error-fingerprint.js +21 -2
  295. package/dist/telemetry/feedback-cli.d.ts +1 -1
  296. package/dist/telemetry/feedback-cli.js +4 -4
  297. package/dist/telemetry/feedback-context.d.ts +22 -0
  298. package/dist/telemetry/feedback-context.js +41 -1
  299. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  300. package/dist/telemetry/feedback-delivery.js +30 -0
  301. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  302. package/dist/telemetry/feedback-outbox.js +87 -0
  303. package/dist/telemetry/feedback.d.ts +22 -1
  304. package/dist/telemetry/feedback.js +67 -8
  305. package/dist/telemetry/git-facts.js +1 -1
  306. package/dist/telemetry/session-metrics.d.ts +2 -0
  307. package/dist/telemetry/session-metrics.js +68 -6
  308. package/dist/telemetry/telemetry.d.ts +27 -2
  309. package/dist/telemetry/telemetry.js +119 -29
  310. package/dist/telemetry/verification-of.d.ts +22 -0
  311. package/dist/telemetry/verification-of.js +84 -0
  312. package/dist/tools/act-capsule.js +2 -2
  313. package/dist/tools/act-danger.d.ts +12 -0
  314. package/dist/tools/act-danger.js +20 -3
  315. package/dist/tools/act-helpers.js +4 -4
  316. package/dist/tools/act-tools.d.ts +0 -8
  317. package/dist/tools/act-tools.js +84 -115
  318. package/dist/tools/act-view.js +7 -7
  319. package/dist/tools/alias-args.d.ts +17 -0
  320. package/dist/tools/alias-args.js +25 -0
  321. package/dist/tools/assert-grade.js +9 -9
  322. package/dist/tools/assert-verdict.d.ts +3 -1
  323. package/dist/tools/assert-verdict.js +8 -5
  324. package/dist/tools/browser-tools.js +30 -11
  325. package/dist/tools/contract-tools.js +1 -1
  326. package/dist/tools/coverage-identity.d.ts +34 -0
  327. package/dist/tools/coverage-identity.js +46 -0
  328. package/dist/tools/coverage-tools.js +10 -14
  329. package/dist/tools/dynamic-tools.d.ts +2 -1
  330. package/dist/tools/dynamic-tools.js +90 -18
  331. package/dist/tools/error-recovery.d.ts +20 -2
  332. package/dist/tools/error-recovery.js +179 -4
  333. package/dist/tools/feedback-tools.d.ts +12 -1
  334. package/dist/tools/feedback-tools.js +28 -5
  335. package/dist/tools/invoke-tool.d.ts +0 -6
  336. package/dist/tools/invoke-tool.js +57 -32
  337. package/dist/tools/lease-hint.d.ts +17 -0
  338. package/dist/tools/lease-hint.js +24 -0
  339. package/dist/tools/lease-tools.js +6 -5
  340. package/dist/tools/merge-tools.d.ts +10 -1
  341. package/dist/tools/merge-tools.js +9 -3
  342. package/dist/tools/merged-name-redirect.d.ts +13 -0
  343. package/dist/tools/merged-name-redirect.js +55 -0
  344. package/dist/tools/navigate-result.d.ts +17 -0
  345. package/dist/tools/navigate-result.js +31 -0
  346. package/dist/tools/observe-tools.js +54 -20
  347. package/dist/tools/observed-nothing.d.ts +25 -0
  348. package/dist/tools/observed-nothing.js +33 -0
  349. package/dist/tools/query-paginate.js +2 -2
  350. package/dist/tools/query-shape.d.ts +18 -0
  351. package/dist/tools/query-shape.js +39 -0
  352. package/dist/tools/read-tools.js +85 -20
  353. package/dist/tools/real-input-attempt.d.ts +28 -0
  354. package/dist/tools/real-input-attempt.js +88 -0
  355. package/dist/tools/reconcile-tools.js +7 -7
  356. package/dist/tools/reload-result.d.ts +14 -0
  357. package/dist/tools/reload-result.js +29 -0
  358. package/dist/tools/snapshot-delta.d.ts +4 -5
  359. package/dist/tools/snapshot-delta.js +0 -0
  360. package/dist/tools/tool-kit.d.ts +8 -0
  361. package/dist/tools/tool-surface.d.ts +83 -0
  362. package/dist/tools/tool-surface.js +204 -0
  363. package/dist/tools/tools-helpers.js +6 -6
  364. package/dist/tools/tools.d.ts +15 -0
  365. package/dist/tools/tools.js +53 -17
  366. package/dist/tools/unadvertised-help.d.ts +5 -0
  367. package/dist/tools/unadvertised-help.js +39 -0
  368. package/dist/trace.d.ts +69 -0
  369. package/dist/trace.js +150 -0
  370. package/dist/update/nudge-credit.d.ts +4 -0
  371. package/dist/update/nudge-credit.js +53 -0
  372. package/dist/update/sdk-sync.d.ts +21 -0
  373. package/dist/update/sdk-sync.js +42 -0
  374. package/dist/update/update-checker.js +3 -3
  375. package/dist/update/update-nudge.d.ts +63 -6
  376. package/dist/update/update-nudge.js +128 -15
  377. package/dist/update/updater.d.ts +1 -9
  378. package/dist/update/updater.js +13 -8
  379. package/dist/version/peer-announce.d.ts +4 -0
  380. package/dist/version/peer-announce.js +30 -0
  381. package/dist/{server-version.js → version/server-version.js} +5 -1
  382. package/dist/version/version-nudge.d.ts +28 -0
  383. package/dist/version/version-nudge.js +40 -0
  384. package/dist/version/version-skew.d.ts +66 -0
  385. package/dist/version/version-skew.js +71 -0
  386. package/dist/visual/visual-diff.js +2 -2
  387. package/dist/visual/visual-tools.js +4 -4
  388. package/docs/agent-cheatsheet.md +85 -0
  389. package/docs/architecture.md +113 -0
  390. package/docs/benchmarks.md +255 -0
  391. package/docs/debugging.md +102 -0
  392. package/docs/deploy-checks.md +69 -0
  393. package/docs/desktop-apps.md +256 -0
  394. package/docs/enterprise.md +62 -0
  395. package/docs/fixtures.md +28 -0
  396. package/docs/flows.md +166 -0
  397. package/docs/getting-started.md +469 -0
  398. package/docs/human-control.md +63 -0
  399. package/docs/integration-patterns.md +171 -0
  400. package/docs/local-registry.md +126 -0
  401. package/docs/mint.json +61 -0
  402. package/docs/multi-agent-testing.md +55 -0
  403. package/docs/platform-integration.md +122 -0
  404. package/docs/telemetry-contract.md +214 -0
  405. package/docs/telemetry.md +184 -0
  406. package/docs/testing.md +82 -0
  407. package/docs/token-efficiency.md +80 -0
  408. package/docs/usage.md +1046 -0
  409. package/package.json +8 -5
  410. package/dist/cli-launch.js +0 -112
  411. package/dist/cli-parse.d.ts +0 -140
  412. package/dist/daemon-resilience.d.ts +0 -22
  413. package/dist/daemon-resilience.js +0 -85
  414. package/dist/mcp-proxy.d.ts +0 -35
  415. package/dist/mcp-proxy.js +0 -190
  416. package/dist/tools/profiles.d.ts +0 -37
  417. package/dist/tools/profiles.js +0 -139
  418. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  419. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  420. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  421. /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
  422. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  423. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  424. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -5,8 +5,12 @@
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)
9
- reticle serve [--port N] [--drive <url>] [--headed] [--http] [--http-port N] [--http-token T]
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.
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]
12
16
  reticle doctor [--port N] (diagnose setup: Chromium, daemon, port — one command)
@@ -15,8 +19,8 @@ export const CLI_USAGE = `usage:
15
19
  reticle affected [--since <ref>] [file...] (which saved flows must re-verify for the changed files)
16
20
  reticle gate [--since <ref>] [file...] (exit non-zero unless passing artifacts cover the affected flows)
17
21
  reticle watch [url] (on save, report which saved flows must re-verify)
18
- reticle drive <url> [--headed] (foreground mode — for debugging)
19
- reticle mcp [--port N] [--drive <url>] [--headed] (MCP stdio proxy — auto-starts daemon if needed)
22
+ reticle drive <url> [--headless] (foreground mode — for debugging)
23
+ reticle mcp [--port N] [--drive <url>] [--headless] (MCP stdio proxy — auto-starts daemon if needed)
20
24
  reticle update (install the latest server version and restart)
21
25
  reticle rollback (restore the previous server version and restart)
22
26
  reticle license (show enterprise license status: active | eval | missing)
@@ -32,7 +36,10 @@ Cloud (link this project to Reticle Cloud — runs/flows recorded on the dashboa
32
36
  reticle project <ls|create <name>> (list or create cloud projects)
33
37
  reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]
34
38
  reticle push (send local run artifacts to the dashboard)
35
- reticle runs | regression | share <runId> (read cloud state; regression exits 3 if any flow broke)`;
39
+ reticle runs | regression | share <runId> (read cloud state; regression exits 3 if any flow broke)
40
+
41
+ 'drive' shows the browser; everything else is hidden (serve/mcp own the pool behind leases, replay
42
+ and the spec runner — batch work). --headed opts any of them in, --headless hides drive, CI hides it.`;
36
43
  const INIT_COMMAND = 'init';
37
44
  const SERVE_COMMAND = 'serve';
38
45
  const STOP_COMMAND = 'stop';
@@ -53,10 +60,10 @@ const VERSION_COMMAND = 'version';
53
60
  const TELEMETRY_COMMAND = 'telemetry';
54
61
  const FEEDBACK_COMMAND = 'feedback';
55
62
  const IDENTIFY_COMMAND = 'identify';
56
- export const COMPANY_FLAG = '--company';
57
- export const EMAIL_FLAG = '--email';
58
- export const CONTEXT_FLAG = '--context';
59
- 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';
60
67
  /** `reticle feedback --rating 4 "the words"` — the human half of the channel. */
61
68
  export const RATING_FLAG = '--rating';
62
69
  export const BUG_FLAG = '--bug';
@@ -115,24 +122,51 @@ const KNOWN_COMMANDS = new Set([
115
122
  ]);
116
123
  /** The subcommand name if we recognize it, else `unknown`. Bare `reticle` reports `help`. */
117
124
  export function knownCommand(arg) {
118
- if (arg === undefined || arg === '')
125
+ if (arg === undefined || '' === arg)
119
126
  return 'help';
120
127
  return KNOWN_COMMANDS.has(arg) ? arg : UNKNOWN_COMMAND;
121
128
  }
122
129
  export const HEADED_FLAG = '--headed';
130
+ /**
131
+ * Force a hidden browser. The default is now HEADED: a run nobody can see is a run nobody trusts,
132
+ * and every "did it actually do anything?" question cost a round-trip. CI passes this (or just sets
133
+ * CI, which flips the default) because there is no display there to be headed on.
134
+ */
135
+ const HEADLESS_FLAG = '--headless';
123
136
  export const PORT_FLAG = '--port';
124
137
  export const DRIVE_FLAG = '--drive';
125
138
  const QUIET_FLAG = '--quiet';
126
139
  const DRY_RUN_FLAG = '--dry-run';
127
140
  const YES_FLAG = '--yes';
128
141
  const NO_MCP_FLAG = '--no-mcp';
142
+ const APP_FLAG = '--app';
129
143
  const NO_INSTALL_FLAG = '--no-install';
130
144
  export const HTTP_FLAG = '--http';
131
145
  export const HTTP_PORT_FLAG = '--http-port';
132
146
  export const HTTP_TOKEN_FLAG = '--http-token';
133
147
  const TIMEOUT_FLAG = '--timeout';
134
148
  const STORAGE_STATE_FLAG = '--storage-state';
135
- function parseServeFlags(args, defaultPort) {
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
+ });
169
+ function parseServeFlags(args, defaultPort, _defaultHeadless) {
136
170
  let port = defaultPort;
137
171
  let driveUrl;
138
172
  let headless = true;
@@ -142,25 +176,32 @@ function parseServeFlags(args, defaultPort) {
142
176
  let i = 0;
143
177
  while (i < args.length) {
144
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;
145
183
  if (arg === PORT_FLAG) {
146
184
  i++;
147
185
  const n = args[i];
148
186
  if (n === undefined)
149
- return { kind: 'error', message: CLI_USAGE };
187
+ return missingValue(PORT_FLAG);
150
188
  const parsed = parseInt(n, 10);
151
189
  if (isNaN(parsed))
152
- return { kind: 'error', message: CLI_USAGE };
190
+ return notANumber(PORT_FLAG, n);
153
191
  port = parsed;
154
192
  }
155
193
  else if (arg === DRIVE_FLAG) {
156
194
  i++;
157
195
  driveUrl = args[i];
158
196
  if (driveUrl === undefined)
159
- return { kind: 'error', message: CLI_USAGE };
197
+ return missingValue(DRIVE_FLAG);
160
198
  }
161
199
  else if (arg === HEADED_FLAG) {
162
200
  headless = false;
163
201
  }
202
+ else if (arg === HEADLESS_FLAG) {
203
+ headless = true;
204
+ }
164
205
  else if (arg === HTTP_FLAG) {
165
206
  http = true;
166
207
  }
@@ -168,20 +209,20 @@ function parseServeFlags(args, defaultPort) {
168
209
  i++;
169
210
  const n = args[i];
170
211
  if (n === undefined)
171
- return { kind: 'error', message: CLI_USAGE };
212
+ return missingValue(HTTP_PORT_FLAG);
172
213
  const parsed = parseInt(n, 10);
173
214
  if (isNaN(parsed))
174
- return { kind: 'error', message: CLI_USAGE };
215
+ return notANumber(HTTP_PORT_FLAG, n);
175
216
  httpPort = parsed;
176
217
  }
177
218
  else if (arg === HTTP_TOKEN_FLAG) {
178
219
  i++;
179
220
  httpToken = args[i];
180
221
  if (httpToken === undefined)
181
- return { kind: 'error', message: CLI_USAGE };
222
+ return missingValue(HTTP_TOKEN_FLAG);
182
223
  }
183
224
  else {
184
- return { kind: 'error', message: CLI_USAGE };
225
+ return unknownArgument(arg);
185
226
  }
186
227
  i++;
187
228
  }
@@ -197,7 +238,7 @@ function parseServeFlags(args, defaultPort) {
197
238
  }
198
239
  function parsePortFlag(args, defaultPort) {
199
240
  const idx = args.indexOf(PORT_FLAG);
200
- if (idx === -1)
241
+ if (-1 === idx)
201
242
  return defaultPort;
202
243
  const n = args[idx + 1];
203
244
  if (n === undefined)
@@ -205,25 +246,28 @@ function parsePortFlag(args, defaultPort) {
205
246
  const parsed = parseInt(n, 10);
206
247
  return isNaN(parsed) ? defaultPort : parsed;
207
248
  }
208
- function parseDriveSuffix(args, port) {
209
- let headless = true;
249
+ function parseDriveSuffix(args, port, defaultHeadless) {
250
+ let headless = defaultHeadless;
210
251
  let driveUrl;
211
252
  for (const arg of args) {
212
253
  if (arg === HEADED_FLAG) {
213
254
  headless = false;
214
255
  }
256
+ else if (arg === HEADLESS_FLAG) {
257
+ headless = true;
258
+ }
215
259
  else if (arg.startsWith('--')) {
216
- return { kind: 'error', message: CLI_USAGE };
260
+ return unknownArgument(arg);
217
261
  }
218
262
  else if (driveUrl === undefined) {
219
263
  driveUrl = arg;
220
264
  }
221
265
  else {
222
- return { kind: 'error', message: CLI_USAGE };
266
+ return unknownArgument(arg);
223
267
  }
224
268
  }
225
269
  if (driveUrl === undefined)
226
- return { kind: 'error', message: CLI_USAGE };
270
+ return missingOperand(DRIVE_COMMAND, 'a url');
227
271
  return { kind: 'ok', port, driveUrl, headless };
228
272
  }
229
273
  /**
@@ -247,32 +291,32 @@ function parseVerifySuffix(args) {
247
291
  i++;
248
292
  const n = args[i];
249
293
  if (n === undefined)
250
- return { kind: 'error', message: CLI_USAGE };
294
+ return missingValue(TIMEOUT_FLAG);
251
295
  const parsed = parseInt(n, 10);
252
296
  if (isNaN(parsed))
253
- return { kind: 'error', message: CLI_USAGE };
297
+ return notANumber(TIMEOUT_FLAG, n);
254
298
  timeoutMs = parsed;
255
299
  }
256
300
  else if (arg === STORAGE_STATE_FLAG) {
257
301
  i++;
258
302
  const v = args[i];
259
303
  if (v === undefined)
260
- return { kind: 'error', message: CLI_USAGE };
304
+ return missingValue(STORAGE_STATE_FLAG);
261
305
  storageState = v;
262
306
  }
263
307
  else if (arg.startsWith('--')) {
264
- return { kind: 'error', message: CLI_USAGE };
308
+ return unknownArgument(arg);
265
309
  }
266
310
  else if (url === undefined) {
267
311
  url = arg;
268
312
  }
269
313
  else {
270
- return { kind: 'error', message: CLI_USAGE };
314
+ return unknownArgument(arg);
271
315
  }
272
316
  i++;
273
317
  }
274
318
  if (url === undefined)
275
- return { kind: 'error', message: CLI_USAGE };
319
+ return missingOperand(VERIFY_COMMAND, 'a url');
276
320
  return {
277
321
  kind: 'ok',
278
322
  url,
@@ -286,19 +330,31 @@ function parseInitFlags(args) {
286
330
  let mcp = true;
287
331
  let dryRun = false;
288
332
  let install = true;
333
+ let app;
289
334
  let i = 0;
290
335
  while (i < args.length) {
291
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;
292
341
  if (arg === PORT_FLAG) {
293
342
  i++;
294
343
  const n = args[i];
295
344
  if (n === undefined)
296
- return { kind: 'error', message: CLI_USAGE };
345
+ return missingValue(PORT_FLAG);
297
346
  const parsed = parseInt(n, 10);
298
347
  if (isNaN(parsed))
299
- return { kind: 'error', message: CLI_USAGE };
348
+ return notANumber(PORT_FLAG, n);
300
349
  port = parsed;
301
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
+ }
302
358
  else if (arg === NO_MCP_FLAG) {
303
359
  mcp = false;
304
360
  }
@@ -312,15 +368,29 @@ function parseInitFlags(args) {
312
368
  // Accepted for scripting/CI; init has no interactive prompts today.
313
369
  }
314
370
  else {
315
- return { kind: 'error', message: CLI_USAGE };
371
+ return unknownArgument(arg);
316
372
  }
317
373
  i++;
318
374
  }
319
- return { kind: 'ok', port, mcp, dryRun, install };
375
+ return { kind: 'ok', port, mcp, dryRun, install, app };
320
376
  }
321
377
  /** Pure CLI arg parser — exported for unit tests. argv = process.argv.slice(2). */
322
378
  const SINCE_FLAG = '--since';
323
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
+ }
324
394
  function parseTargetArgs(rest) {
325
395
  const files = [];
326
396
  let since;
@@ -336,28 +406,45 @@ function parseTargetArgs(rest) {
336
406
  }
337
407
  return since === undefined ? { files } : { files, since };
338
408
  }
339
- export function parseCliArgs(argv, defaultPort) {
340
- if (argv.length === 0)
409
+ /**
410
+ * @param defaultHeadless Whether a browser Reticle launches should be hidden when no flag says
411
+ * otherwise. Injected rather than read from the environment here, because this module is pure —
412
+ * `cli.ts` decides it from `CI`. The product default is FALSE: showing the run is what makes it
413
+ * trustworthy, and asking people to opt into seeing their own app was backwards.
414
+ */
415
+ export function parseCliArgs(argv, defaultPort, defaultHeadless = false) {
416
+ // Bare `reticle` is `serve` — a pool-owning command, so headless like the rest of that family.
417
+ if (0 === argv.length)
341
418
  return { kind: 'serve', port: defaultPort, headless: true, http: false };
342
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 };
343
423
  // `version` (or the conventional -v/--version flags) prints the running version — the diagnostic the
344
424
  // troubleshooting docs lean on to confirm which npx-resolved build is actually executing.
345
- if (cmd === VERSION_COMMAND || cmd === '--version' || cmd === '-v')
425
+ if (cmd === VERSION_COMMAND || '--version' === cmd || '-v' === cmd)
346
426
  return { kind: 'version' };
347
427
  // `help` (and the conventional -h/--help) print usage to stdout and exit 0 — the universal first move
348
428
  // for a new user, which otherwise fell through to a JSON error with exit 1.
349
- if (cmd === 'help' || cmd === '--help' || cmd === '-h')
429
+ if ('help' === cmd || '--help' === cmd || '-h' === cmd)
350
430
  return { kind: 'help' };
351
431
  switch (cmd) {
352
432
  case INIT_COMMAND: {
353
433
  const r = parseInitFlags(rest);
354
- if (r.kind === 'error')
434
+ if ('error' === r.kind)
355
435
  return r;
356
- 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
+ };
357
444
  }
358
445
  case SERVE_COMMAND: {
359
- const r = parseServeFlags(rest, defaultPort);
360
- if (r.kind === 'error')
446
+ const r = parseServeFlags(rest, defaultPort, defaultHeadless);
447
+ if ('error' === r.kind)
361
448
  return r;
362
449
  return {
363
450
  kind: 'serve',
@@ -396,7 +483,7 @@ export function parseCliArgs(argv, defaultPort) {
396
483
  case IDENTIFY_COMMAND: {
397
484
  const flag = (name) => {
398
485
  const at = rest.indexOf(name);
399
- return at === -1 ? undefined : rest[at + 1];
486
+ return -1 === at ? undefined : rest[at + 1];
400
487
  };
401
488
  const context = flag(CONTEXT_FLAG);
402
489
  const company = flag(COMPANY_FLAG);
@@ -411,13 +498,13 @@ export function parseCliArgs(argv, defaultPort) {
411
498
  }
412
499
  case FEEDBACK_COMMAND: {
413
500
  const ratingAt = rest.indexOf(RATING_FLAG);
414
- const rating = ratingAt === -1 ? undefined : Number(rest[ratingAt + 1]);
501
+ const rating = -1 === ratingAt ? undefined : Number(rest[ratingAt + 1]);
415
502
  if (rating !== undefined && (!Number.isInteger(rating) || rating < 1 || rating > 5)) {
416
503
  return { kind: 'error', message: `${RATING_FLAG} takes a whole number from 1 to 5` };
417
504
  }
418
505
  // Everything that is not a flag or the rating's value is the message. Quoting is the user's
419
506
  // job for shell reasons, but joining the remainder means an unquoted sentence still works.
420
- const consumed = new Set(ratingAt === -1 ? [] : [ratingAt, ratingAt + 1]);
507
+ const consumed = new Set(-1 === ratingAt ? [] : [ratingAt, ratingAt + 1]);
421
508
  const text = rest
422
509
  .filter((arg, i) => !consumed.has(i) && !arg.startsWith('--'))
423
510
  .join(' ')
@@ -436,14 +523,14 @@ export function parseCliArgs(argv, defaultPort) {
436
523
  return url !== undefined ? { kind: 'open', port, url } : { kind: 'open', port };
437
524
  }
438
525
  case DRIVE_COMMAND: {
439
- const r = parseDriveSuffix(rest, defaultPort);
440
- if (r.kind === 'error')
526
+ const r = parseDriveSuffix(rest, defaultPort, defaultHeadless);
527
+ if ('error' === r.kind)
441
528
  return r;
442
529
  return { kind: 'drive', port: r.port, driveUrl: r.driveUrl, headless: r.headless };
443
530
  }
444
531
  case DAEMON_INNER_COMMAND: {
445
- const r = parseServeFlags(rest, defaultPort);
446
- if (r.kind === 'error')
532
+ const r = parseServeFlags(rest, defaultPort, defaultHeadless);
533
+ if ('error' === r.kind)
447
534
  return r;
448
535
  return {
449
536
  kind: '_daemon',
@@ -457,7 +544,7 @@ export function parseCliArgs(argv, defaultPort) {
457
544
  }
458
545
  case VERIFY_COMMAND: {
459
546
  const r = parseVerifySuffix(rest);
460
- if (r.kind === 'error')
547
+ if ('error' === r.kind)
461
548
  return r;
462
549
  return {
463
550
  kind: 'verify',
@@ -477,21 +564,13 @@ export function parseCliArgs(argv, defaultPort) {
477
564
  }
478
565
  case AFFECTED_COMMAND: {
479
566
  const t = parseTargetArgs(rest);
480
- if (t.files.length === 0 && t.since === undefined) {
481
- return { kind: 'error', message: 'usage: reticle affected [--since <ref>] [file...]' };
482
- }
483
- return {
484
- kind: 'affected',
485
- files: t.files,
486
- ...(t.since === undefined ? {} : { since: t.since }),
487
- };
567
+ const since = t.since ?? implicitSince(t.files);
568
+ return { kind: 'affected', files: t.files, ...(since === undefined ? {} : { since }) };
488
569
  }
489
570
  case GATE_COMMAND: {
490
571
  const t = parseTargetArgs(rest);
491
- if (t.files.length === 0 && t.since === undefined) {
492
- return { kind: 'error', message: 'usage: reticle gate [--since <ref>] [file...]' };
493
- }
494
- 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 }) };
495
574
  }
496
575
  case WATCH_COMMAND: {
497
576
  // `reticle watch [url]` — on file save, report which saved flows must re-verify.
@@ -503,8 +582,8 @@ export function parseCliArgs(argv, defaultPort) {
503
582
  case ROLLBACK_COMMAND:
504
583
  return { kind: 'rollback' };
505
584
  case MCP_COMMAND: {
506
- const r = parseServeFlags(rest, defaultPort);
507
- if (r.kind === 'error')
585
+ const r = parseServeFlags(rest, defaultPort, defaultHeadless);
586
+ if ('error' === r.kind)
508
587
  return r;
509
588
  return {
510
589
  kind: 'mcp',
@@ -517,6 +596,8 @@ export function parseCliArgs(argv, defaultPort) {
517
596
  };
518
597
  }
519
598
  default:
520
- 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);
521
602
  }
522
603
  }
@@ -7,6 +7,21 @@
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>;
22
+ export declare function isLikelyDevServerPort(port: number): boolean;
23
+ /** The warning printed when a project pins the bridge to something that looks like its dev server. */
24
+ export declare function devServerPortWarning(port: number): string;
10
25
  /**
11
26
  * Read the port stored in the project's .reticle.json (written by `reticle init`).
12
27
  * Returns undefined if the file is absent, unreadable, or has no valid numeric port.
@@ -8,6 +8,27 @@
8
8
  * 4. RETICLE_DEFAULT_PORT (4400)
9
9
  */
10
10
  import { readFileSync } from 'node:fs';
11
+ /**
12
+ * Ports that belong to a DEV SERVER, not to Reticle's bridge. Next defaults to 3000, Vite to 5173,
13
+ * Astro to 4321, and so on.
14
+ *
15
+ * `.reticle.json`'s `port` is the bridge — the daemon ↔ SDK channel, default 4400 — and it has
16
+ * nothing to do with the port the app is served on. Setting it to the dev-server port makes the
17
+ * daemon try to bind the port the app already holds; what the user sees is either a bind failure or
18
+ * a daemon that never connects, neither of which mentions the actual mistake. The old setup skill
19
+ * asked "what port does your dev server run on?" a few lines before showing this field, which is how
20
+ * the confusion got manufactured in the first place.
21
+ */
22
+ export const DEV_SERVER_PORTS = new Set([
23
+ 3000, 3001, 4200, 4321, 5000, 5173, 5174, 8000, 8080, 8100, 9000,
24
+ ]);
25
+ export function isLikelyDevServerPort(port) {
26
+ return DEV_SERVER_PORTS.has(port);
27
+ }
28
+ /** The warning printed when a project pins the bridge to something that looks like its dev server. */
29
+ export function devServerPortWarning(port) {
30
+ return `[reticle] .reticle.json sets "port": ${port}, which is a common DEV-SERVER port. That field is the Reticle BRIDGE port (default 4400), not the port your app is served on — they must be different, or the daemon fights your dev server for it. Remove "port" unless you are running several apps at once.`;
31
+ }
11
32
  /**
12
33
  * Read the port stored in the project's .reticle.json (written by `reticle init`).
13
34
  * Returns undefined if the file is absent, unreadable, or has no valid numeric port.
@@ -16,9 +37,9 @@ export function readProjectPort(cwd) {
16
37
  try {
17
38
  const raw = readFileSync(`${cwd}/.reticle.json`, 'utf8');
18
39
  const config = JSON.parse(raw);
19
- if (typeof config === 'object' && config !== null) {
40
+ if ('object' === typeof config && config !== null) {
20
41
  const p = config['port'];
21
- if (typeof p === 'number' && Number.isInteger(p) && p > 0 && p < 65536)
42
+ if ('number' === typeof p && Number.isInteger(p) && p > 0 && p < 65536)
22
43
  return p;
23
44
  }
24
45
  }
@@ -36,9 +57,9 @@ export function readProjectId(cwd) {
36
57
  try {
37
58
  const raw = readFileSync(`${cwd}/.reticle.json`, 'utf8');
38
59
  const config = JSON.parse(raw);
39
- if (typeof config === 'object' && config !== null) {
60
+ if ('object' === typeof config && config !== null) {
40
61
  const id = config['projectId'];
41
- if (typeof id === 'string' && id.length > 0)
62
+ if ('string' === typeof id && id.length > 0)
42
63
  return id;
43
64
  }
44
65
  }
@@ -55,16 +76,16 @@ export function readProjectId(cwd) {
55
76
  export function readJournalEnabled(cwd, env) {
56
77
  if (env !== undefined) {
57
78
  const v = env.trim().toLowerCase();
58
- if (v === '0' || v === 'false' || v === 'off')
79
+ if ('0' === v || 'false' === v || 'off' === v)
59
80
  return false;
60
- if (v === '1' || v === 'true' || v === 'on')
81
+ if ('1' === v || 'true' === v || 'on' === v)
61
82
  return true;
62
83
  }
63
84
  try {
64
85
  const raw = readFileSync(`${cwd}/.reticle.json`, 'utf8');
65
86
  const config = JSON.parse(raw);
66
- if (typeof config === 'object' && config !== null) {
67
- if (config['journal'] === false)
87
+ if ('object' === typeof config && config !== null) {
88
+ if (false === config['journal'])
68
89
  return false;
69
90
  }
70
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
+ }