@reticlehq/server 2.4.0 → 2.6.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 (472) hide show
  1. package/SKILL.md +719 -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} +13 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +95 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
  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/cli-doctor.d.ts +1 -0
  20. package/dist/cli/cli-doctor.js +135 -0
  21. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  22. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  23. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  24. package/dist/cli/cli-launch.js +171 -0
  25. package/dist/cli/cli-parse-feedback.d.ts +31 -0
  26. package/dist/cli/cli-parse-feedback.js +56 -0
  27. package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
  28. package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
  29. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
  30. package/dist/{cli-port.js → cli/cli-port.js} +9 -9
  31. package/dist/cli/cli-update-commands.d.ts +4 -0
  32. package/dist/cli/cli-update-commands.js +93 -0
  33. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  34. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  35. package/dist/cli/doctor-daemon-line.d.ts +37 -0
  36. package/dist/cli/doctor-daemon-line.js +35 -0
  37. package/dist/cli/port-holder.d.ts +43 -0
  38. package/dist/cli/port-holder.js +99 -0
  39. package/dist/cli.d.ts +2 -2
  40. package/dist/cli.js +302 -100
  41. package/dist/cloud/cloud-config.d.ts +0 -1
  42. package/dist/cloud/cloud-config.js +9 -9
  43. package/dist/cloud/cloud-sync.d.ts +1 -7
  44. package/dist/cloud/cloud-sync.js +12 -12
  45. package/dist/crawl/crawl-empty.d.ts +16 -0
  46. package/dist/crawl/crawl-empty.js +24 -0
  47. package/dist/crawl/crawl-tools.js +4 -1
  48. package/dist/crawl/crawl.js +26 -7
  49. package/dist/daemon/daemon-resilience.d.ts +73 -0
  50. package/dist/daemon/daemon-resilience.js +279 -0
  51. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  52. package/dist/daemon/daemon-usefulness.js +62 -0
  53. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  54. package/dist/{daemon.js → daemon/daemon.js} +105 -48
  55. package/dist/daemon/heartbeat.d.ts +115 -0
  56. package/dist/daemon/heartbeat.js +141 -0
  57. package/dist/daemon/idle-grace.d.ts +32 -0
  58. package/dist/daemon/idle-grace.js +41 -0
  59. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
  60. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
  61. package/dist/daemon/port-presence.d.ts +76 -0
  62. package/dist/daemon/port-presence.js +93 -0
  63. package/dist/daemon/state-dir.d.ts +7 -0
  64. package/dist/daemon/state-dir.js +59 -0
  65. package/dist/daemon/unclean-predecessor.d.ts +23 -0
  66. package/dist/daemon/unclean-predecessor.js +27 -0
  67. package/dist/domain/domain-model.js +2 -2
  68. package/dist/events/body-failures.js +8 -8
  69. package/dist/events/contradictions.d.ts +1 -1
  70. package/dist/events/contradictions.js +60 -10
  71. package/dist/events/echo-mismatch.js +10 -10
  72. package/dist/events/event-filters.js +6 -6
  73. package/dist/events/json-salvage.d.ts +2 -1
  74. package/dist/events/json-salvage.js +6 -6
  75. package/dist/events/observed-in-window.d.ts +12 -0
  76. package/dist/events/observed-in-window.js +21 -0
  77. package/dist/events/predicate-eval.d.ts +53 -20
  78. package/dist/events/predicate-eval.js +179 -55
  79. package/dist/events/predicate-parse.d.ts +23 -0
  80. package/dist/events/predicate-parse.js +54 -0
  81. package/dist/events/predicate.d.ts +7 -2
  82. package/dist/events/predicate.js +126 -35
  83. package/dist/events/reconcile.js +5 -5
  84. package/dist/events/ring-buffer.js +2 -17
  85. package/dist/events/stale-response.js +6 -6
  86. package/dist/events/testid-near-miss.d.ts +22 -0
  87. package/dist/events/testid-near-miss.js +33 -0
  88. package/dist/events/unit-mismatch.js +8 -8
  89. package/dist/flows/affected-tools.js +19 -4
  90. package/dist/flows/affected.js +1 -1
  91. package/dist/flows/annotate-target.d.ts +1 -0
  92. package/dist/flows/annotate-target.js +20 -0
  93. package/dist/flows/annotate-tools.js +25 -3
  94. package/dist/flows/annotate.js +17 -4
  95. package/dist/flows/assertion-integrity.d.ts +2 -1
  96. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  97. package/dist/flows/assertion-tiers-store.js +2 -2
  98. package/dist/flows/attributed-failure.d.ts +17 -0
  99. package/dist/flows/attributed-failure.js +20 -0
  100. package/dist/flows/buddy-status.d.ts +2 -1
  101. package/dist/flows/buddy-status.js +2 -2
  102. package/dist/flows/coverage.d.ts +3 -2
  103. package/dist/flows/coverage.js +2 -2
  104. package/dist/flows/decision.js +24 -9
  105. package/dist/flows/empty-flow.d.ts +6 -0
  106. package/dist/flows/empty-flow.js +24 -0
  107. package/dist/flows/flake-store.d.ts +0 -1
  108. package/dist/flows/flake-store.js +1 -1
  109. package/dist/flows/flake.d.ts +2 -1
  110. package/dist/flows/flake.js +2 -2
  111. package/dist/flows/flow-classify.js +12 -0
  112. package/dist/flows/flow-replay-run.js +4 -4
  113. package/dist/flows/flow-replay.d.ts +6 -8
  114. package/dist/flows/flow-replay.js +132 -48
  115. package/dist/flows/flow-report.js +1 -1
  116. package/dist/flows/flow-scope.d.ts +1 -6
  117. package/dist/flows/flow-step-runners.d.ts +26 -6
  118. package/dist/flows/flow-step-runners.js +156 -6
  119. package/dist/flows/flow-success.js +20 -15
  120. package/dist/flows/flow-tools.js +63 -21
  121. package/dist/flows/flows.js +15 -11
  122. package/dist/flows/gate.d.ts +4 -3
  123. package/dist/flows/gate.js +1 -1
  124. package/dist/flows/heal-run.d.ts +0 -8
  125. package/dist/flows/heal-run.js +5 -5
  126. package/dist/flows/heal.js +3 -3
  127. package/dist/flows/predicate-to-expect.d.ts +38 -0
  128. package/dist/flows/predicate-to-expect.js +95 -0
  129. package/dist/flows/replay.d.ts +16 -1
  130. package/dist/flows/replay.js +58 -22
  131. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  132. package/dist/flows/role-anchor-nearest.js +45 -0
  133. package/dist/flows/role-drift-reason.d.ts +20 -0
  134. package/dist/flows/role-drift-reason.js +39 -0
  135. package/dist/flows/server-verify.d.ts +1 -1
  136. package/dist/flows/server-verify.js +5 -5
  137. package/dist/flows/suite-flakes.d.ts +33 -0
  138. package/dist/flows/suite-flakes.js +32 -0
  139. package/dist/flows/verify-change-tools.js +32 -10
  140. package/dist/flows/verify-change-verdict.d.ts +15 -0
  141. package/dist/flows/verify-change-verdict.js +25 -0
  142. package/dist/flows/watch-batcher.d.ts +3 -2
  143. package/dist/honesty/already-true.d.ts +2 -0
  144. package/dist/honesty/already-true.js +31 -0
  145. package/dist/honesty/blind-spots.d.ts +35 -3
  146. package/dist/honesty/blind-spots.js +57 -12
  147. package/dist/honesty/honesty.d.ts +3 -2
  148. package/dist/honesty/honesty.js +4 -4
  149. package/dist/honesty/uncaptured-bodies.js +1 -1
  150. package/dist/honesty/unread-outcome.js +2 -2
  151. package/dist/honesty/verified.d.ts +31 -3
  152. package/dist/honesty/verified.js +64 -8
  153. package/dist/http-server.js +23 -7
  154. package/dist/hunt/hunt-report.d.ts +2 -1
  155. package/dist/hunt/hunt-report.js +2 -2
  156. package/dist/index.d.ts +9 -6
  157. package/dist/index.js +41 -25
  158. package/dist/init/agent-rules.d.ts +20 -1
  159. package/dist/init/agent-rules.js +64 -8
  160. package/dist/init/app-choice.d.ts +20 -0
  161. package/dist/init/app-choice.js +25 -0
  162. package/dist/init/astro-host.d.ts +21 -0
  163. package/dist/init/astro-host.js +27 -0
  164. package/dist/init/astro-patch.d.ts +16 -0
  165. package/dist/init/astro-patch.js +240 -0
  166. package/dist/init/capabilities.js +4 -1
  167. package/dist/init/cra.d.ts +50 -0
  168. package/dist/init/cra.js +106 -0
  169. package/dist/init/cursor.js +39 -4
  170. package/dist/init/desktop-doctor.d.ts +3 -2
  171. package/dist/init/desktop-doctor.js +4 -4
  172. package/dist/init/detect.d.ts +9 -2
  173. package/dist/init/detect.js +24 -5
  174. package/dist/init/mcp-clients.d.ts +81 -0
  175. package/dist/init/mcp-clients.js +248 -0
  176. package/dist/init/mcp-registered.d.ts +12 -0
  177. package/dist/init/mcp-registered.js +14 -0
  178. package/dist/init/mcp.d.ts +27 -2
  179. package/dist/init/mcp.js +40 -4
  180. package/dist/init/next-patch.d.ts +2 -4
  181. package/dist/init/next-patch.js +109 -19
  182. package/dist/init/node-io.js +29 -6
  183. package/dist/init/plan-framework.d.ts +16 -1
  184. package/dist/init/plan-framework.js +166 -8
  185. package/dist/init/plan.d.ts +85 -6
  186. package/dist/init/plan.js +196 -33
  187. package/dist/init/project-id.d.ts +5 -9
  188. package/dist/init/project-id.js +10 -22
  189. package/dist/init/run.d.ts +23 -1
  190. package/dist/init/run.js +338 -47
  191. package/dist/init/slash-command.d.ts +9 -0
  192. package/dist/init/slash-command.js +10 -1
  193. package/dist/init/snippets.d.ts +7 -10
  194. package/dist/init/snippets.js +44 -15
  195. package/dist/init/vite-config.js +1 -1
  196. package/dist/init/windows-quote.d.ts +38 -0
  197. package/dist/init/windows-quote.js +70 -0
  198. package/dist/init/workspace-apps.d.ts +23 -0
  199. package/dist/init/workspace-apps.js +68 -0
  200. package/dist/input/driven-redaction.js +1 -1
  201. package/dist/input/network-detail.js +8 -8
  202. package/dist/input/network-mock-tools.js +2 -2
  203. package/dist/input/network-mock.js +4 -4
  204. package/dist/input/real-input.js +8 -3
  205. package/dist/input/scroll-find.js +2 -2
  206. package/dist/input/scroll-tools.d.ts +0 -5
  207. package/dist/input/scroll-tools.js +11 -4
  208. package/dist/input/viewport-tools.js +2 -2
  209. package/dist/journal/ambient-store.d.ts +0 -2
  210. package/dist/journal/ambient-store.js +1 -1
  211. package/dist/journal/ambient.js +1 -1
  212. package/dist/journal/attach-journal.d.ts +2 -1
  213. package/dist/journal/deviation-report.js +6 -6
  214. package/dist/journal/deviation-service.js +1 -1
  215. package/dist/journal/envelope-store.d.ts +0 -2
  216. package/dist/journal/envelope-store.js +1 -1
  217. package/dist/journal/envelope.d.ts +2 -3
  218. package/dist/journal/envelope.js +3 -3
  219. package/dist/journal/journal-query.js +1 -1
  220. package/dist/journal/journal-recorder.d.ts +2 -1
  221. package/dist/journal/journal-recorder.js +2 -2
  222. package/dist/journal/rollups.js +9 -9
  223. package/dist/journal/session-end.d.ts +2 -1
  224. package/dist/journal/session-journal.js +4 -4
  225. package/dist/license/license.d.ts +0 -14
  226. package/dist/license/license.js +8 -8
  227. package/dist/log.d.ts +10 -0
  228. package/dist/log.js +11 -1
  229. package/dist/mcp/mcp-is-error.d.ts +13 -0
  230. package/dist/mcp/mcp-is-error.js +18 -0
  231. package/dist/mcp/mcp-outage.d.ts +32 -0
  232. package/dist/mcp/mcp-outage.js +55 -0
  233. package/dist/mcp/mcp-proxy.d.ts +134 -0
  234. package/dist/mcp/mcp-proxy.js +720 -0
  235. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  236. package/dist/{mcp.js → mcp/mcp.js} +139 -37
  237. package/dist/mcp/proxy-handshake.d.ts +3 -0
  238. package/dist/mcp/proxy-handshake.js +54 -0
  239. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  240. package/dist/mcp/proxy-lifecycle.js +70 -0
  241. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  242. package/dist/mcp/tool-catalog-cache.js +55 -0
  243. package/dist/oracles/propose-consequences.d.ts +2 -1
  244. package/dist/oracles/propose-consequences.js +16 -11
  245. package/dist/oracles/self-instrument.d.ts +2 -1
  246. package/dist/oracles/self-instrument.js +1 -1
  247. package/dist/phenomena/phenomena.js +3 -3
  248. package/dist/platform.d.ts +16 -0
  249. package/dist/platform.js +16 -0
  250. package/dist/pool/browser-pool.d.ts +9 -0
  251. package/dist/pool/browser-pool.js +38 -9
  252. package/dist/pool/playwright-launcher.d.ts +15 -2
  253. package/dist/pool/playwright-launcher.js +21 -2
  254. package/dist/project/fs-port.js +1 -1
  255. package/dist/project/project-tools.js +2 -2
  256. package/dist/runs/build-verification-run.js +1 -1
  257. package/dist/runs/repair-prompt.d.ts +0 -6
  258. package/dist/runs/repair-prompt.js +3 -3
  259. package/dist/runs/risk-classify.js +1 -1
  260. package/dist/runs/run-diff.d.ts +0 -2
  261. package/dist/runs/run-diff.js +3 -3
  262. package/dist/runs/run-tools.js +2 -2
  263. package/dist/runs/verification-sync.js +2 -2
  264. package/dist/runs/verify-http.js +1 -1
  265. package/dist/runs/verify-server.js +1 -1
  266. package/dist/session/attachment-history.d.ts +46 -0
  267. package/dist/session/attachment-history.js +65 -0
  268. package/dist/session/command-timeout.d.ts +11 -3
  269. package/dist/session/command-timeout.js +2 -2
  270. package/dist/session/control-envelope.d.ts +6 -0
  271. package/dist/session/control-envelope.js +7 -1
  272. package/dist/session/dev-server-probe.d.ts +13 -0
  273. package/dist/session/dev-server-probe.js +52 -0
  274. package/dist/session/last-act.d.ts +17 -3
  275. package/dist/session/last-act.js +16 -4
  276. package/dist/session/live-control-tools.js +7 -2
  277. package/dist/session/live-control.js +1 -1
  278. package/dist/session/looks-like-dev-server.d.ts +14 -0
  279. package/dist/session/looks-like-dev-server.js +25 -0
  280. package/dist/session/no-session-diagnosis.d.ts +45 -0
  281. package/dist/session/no-session-diagnosis.js +120 -0
  282. package/dist/session/no-session-watch.d.ts +21 -0
  283. package/dist/session/no-session-watch.js +69 -0
  284. package/dist/session/observed-state.d.ts +4 -0
  285. package/dist/session/observed-state.js +19 -1
  286. package/dist/session/output-budget.js +1 -1
  287. package/dist/session/pending-commands.js +1 -0
  288. package/dist/session/session-health.d.ts +8 -1
  289. package/dist/session/session-health.js +8 -7
  290. package/dist/session/session-info.d.ts +43 -8
  291. package/dist/session/session-info.js +46 -1
  292. package/dist/session/session-lease.d.ts +0 -11
  293. package/dist/session/session-lease.js +1 -1
  294. package/dist/session/session-manager.d.ts +6 -0
  295. package/dist/session/session-manager.js +124 -12
  296. package/dist/session/session-readiness.d.ts +1 -1
  297. package/dist/session/session-readiness.js +6 -1
  298. package/dist/session/session-reaper.js +1 -1
  299. package/dist/session/session-reconnect.d.ts +32 -0
  300. package/dist/session/session-reconnect.js +42 -0
  301. package/dist/session/session.d.ts +15 -2
  302. package/dist/session/session.js +77 -26
  303. package/dist/session/state-select.d.ts +1 -1
  304. package/dist/session/state-select.js +1 -1
  305. package/dist/status-payload.d.ts +19 -0
  306. package/dist/status-payload.js +11 -0
  307. package/dist/telemetry/argument-shape.js +10 -1
  308. package/dist/telemetry/browser-mode.d.ts +27 -0
  309. package/dist/telemetry/browser-mode.js +33 -0
  310. package/dist/telemetry/bug-found.d.ts +17 -21
  311. package/dist/telemetry/bug-found.js +120 -8
  312. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  313. package/dist/telemetry/cli-telemetry.js +27 -4
  314. package/dist/telemetry/crash-cause.d.ts +47 -0
  315. package/dist/telemetry/crash-cause.js +104 -0
  316. package/dist/telemetry/daemon-telemetry.d.ts +38 -1
  317. package/dist/telemetry/daemon-telemetry.js +37 -12
  318. package/dist/telemetry/dev-repo.js +4 -4
  319. package/dist/telemetry/error-class.d.ts +26 -0
  320. package/dist/telemetry/error-class.js +52 -0
  321. package/dist/telemetry/error-fingerprint.js +21 -2
  322. package/dist/telemetry/feedback-cli.d.ts +8 -3
  323. package/dist/telemetry/feedback-cli.js +52 -8
  324. package/dist/telemetry/feedback-context.d.ts +23 -6
  325. package/dist/telemetry/feedback-context.js +105 -5
  326. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  327. package/dist/telemetry/feedback-delivery.js +30 -0
  328. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  329. package/dist/telemetry/feedback-outbox.js +87 -0
  330. package/dist/telemetry/feedback.d.ts +22 -1
  331. package/dist/telemetry/feedback.js +67 -8
  332. package/dist/telemetry/git-facts.d.ts +7 -0
  333. package/dist/telemetry/git-facts.js +5 -5
  334. package/dist/telemetry/project-profile.js +2 -1
  335. package/dist/telemetry/session-metrics.d.ts +47 -3
  336. package/dist/telemetry/session-metrics.js +315 -15
  337. package/dist/telemetry/telemetry.d.ts +45 -2
  338. package/dist/telemetry/telemetry.js +150 -37
  339. package/dist/telemetry/verification-of.d.ts +22 -0
  340. package/dist/telemetry/verification-of.js +84 -0
  341. package/dist/tools/act-capsule.js +2 -2
  342. package/dist/tools/act-danger.d.ts +12 -0
  343. package/dist/tools/act-danger.js +20 -3
  344. package/dist/tools/act-helpers.js +4 -4
  345. package/dist/tools/act-tools.d.ts +0 -8
  346. package/dist/tools/act-tools.js +114 -118
  347. package/dist/tools/act-view.js +7 -7
  348. package/dist/tools/alias-args.d.ts +17 -0
  349. package/dist/tools/alias-args.js +25 -0
  350. package/dist/tools/assert-grade.js +9 -9
  351. package/dist/tools/assert-verdict.d.ts +9 -1
  352. package/dist/tools/assert-verdict.js +21 -6
  353. package/dist/tools/browser-tools.js +36 -11
  354. package/dist/tools/contract-tools.d.ts +0 -5
  355. package/dist/tools/contract-tools.js +27 -1
  356. package/dist/tools/coverage-identity.d.ts +34 -0
  357. package/dist/tools/coverage-identity.js +46 -0
  358. package/dist/tools/coverage-tools.js +10 -14
  359. package/dist/tools/dynamic-tools.d.ts +2 -1
  360. package/dist/tools/dynamic-tools.js +96 -19
  361. package/dist/tools/error-recovery.d.ts +34 -2
  362. package/dist/tools/error-recovery.js +255 -4
  363. package/dist/tools/feedback-invite.d.ts +48 -0
  364. package/dist/tools/feedback-invite.js +71 -0
  365. package/dist/tools/feedback-tools.d.ts +12 -1
  366. package/dist/tools/feedback-tools.js +28 -5
  367. package/dist/tools/invoke-tool.d.ts +0 -12
  368. package/dist/tools/invoke-tool.js +126 -33
  369. package/dist/tools/lease-hint.d.ts +17 -0
  370. package/dist/tools/lease-hint.js +24 -0
  371. package/dist/tools/lease-tools.js +6 -5
  372. package/dist/tools/merge-tools.d.ts +10 -1
  373. package/dist/tools/merge-tools.js +9 -3
  374. package/dist/tools/merged-name-redirect.d.ts +13 -0
  375. package/dist/tools/merged-name-redirect.js +55 -0
  376. package/dist/tools/navigate-arrival.d.ts +26 -0
  377. package/dist/tools/navigate-arrival.js +60 -0
  378. package/dist/tools/navigate-result.d.ts +31 -0
  379. package/dist/tools/navigate-result.js +38 -0
  380. package/dist/tools/observe-tools.js +72 -20
  381. package/dist/tools/observed-nothing.d.ts +25 -0
  382. package/dist/tools/observed-nothing.js +33 -0
  383. package/dist/tools/query-paginate.js +2 -2
  384. package/dist/tools/query-shape.d.ts +18 -0
  385. package/dist/tools/query-shape.js +39 -0
  386. package/dist/tools/read-tools.js +81 -20
  387. package/dist/tools/real-input-attempt.d.ts +28 -0
  388. package/dist/tools/real-input-attempt.js +93 -0
  389. package/dist/tools/reconcile-tools.js +7 -7
  390. package/dist/tools/reload-result.d.ts +14 -0
  391. package/dist/tools/reload-result.js +29 -0
  392. package/dist/tools/snapshot-delta.d.ts +4 -5
  393. package/dist/tools/snapshot-delta.js +0 -0
  394. package/dist/tools/tool-kit.d.ts +16 -0
  395. package/dist/tools/tool-surface.d.ts +83 -0
  396. package/dist/tools/tool-surface.js +217 -0
  397. package/dist/tools/tools-helpers.js +6 -6
  398. package/dist/tools/tools.d.ts +15 -0
  399. package/dist/tools/tools.js +53 -17
  400. package/dist/tools/unadvertised-help.d.ts +5 -0
  401. package/dist/tools/unadvertised-help.js +39 -0
  402. package/dist/trace.d.ts +69 -0
  403. package/dist/trace.js +150 -0
  404. package/dist/update/nudge-credit.d.ts +4 -0
  405. package/dist/update/nudge-credit.js +53 -0
  406. package/dist/update/sdk-sync.d.ts +21 -0
  407. package/dist/update/sdk-sync.js +42 -0
  408. package/dist/update/update-checker.js +3 -3
  409. package/dist/update/update-nudge.d.ts +63 -6
  410. package/dist/update/update-nudge.js +128 -15
  411. package/dist/update/updater.d.ts +1 -9
  412. package/dist/update/updater.js +13 -8
  413. package/dist/version/peer-announce.d.ts +4 -0
  414. package/dist/version/peer-announce.js +30 -0
  415. package/dist/{server-version.js → version/server-version.js} +5 -1
  416. package/dist/version/version-nudge.d.ts +28 -0
  417. package/dist/version/version-nudge.js +40 -0
  418. package/dist/version/version-skew.d.ts +66 -0
  419. package/dist/version/version-skew.js +71 -0
  420. package/dist/visual/visual-diff.js +2 -2
  421. package/dist/visual/visual-tools.js +4 -4
  422. package/docs/README.md +55 -0
  423. package/docs/agent-cheatsheet.md +85 -0
  424. package/docs/architecture.md +113 -0
  425. package/docs/benchmarks.md +255 -0
  426. package/docs/debugging.md +102 -0
  427. package/docs/deploy-checks.md +69 -0
  428. package/docs/desktop-apps.md +256 -0
  429. package/docs/enterprise.md +62 -0
  430. package/docs/fixtures-dispatch-receiver.yml +76 -0
  431. package/docs/fixtures.md +45 -0
  432. package/docs/flows.md +166 -0
  433. package/docs/gate-plan.md +98 -0
  434. package/docs/gates.md +117 -0
  435. package/docs/getting-started.md +469 -0
  436. package/docs/human-control.md +63 -0
  437. package/docs/integration-patterns.md +171 -0
  438. package/docs/local-registry.md +126 -0
  439. package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
  440. package/docs/matrix/2.5.0/codex-machine.json +20 -0
  441. package/docs/matrix/2.5.0/cursor-machine.json +22 -0
  442. package/docs/matrix/2.5.0/gemini-machine.json +22 -0
  443. package/docs/matrix/2.5.0/opencode-machine.json +22 -0
  444. package/docs/matrix/2.5.0/vscode-machine.json +22 -0
  445. package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
  446. package/docs/matrix/MATRIX.md +26 -0
  447. package/docs/matrix/README.md +59 -0
  448. package/docs/mint.json +61 -0
  449. package/docs/multi-agent-testing.md +55 -0
  450. package/docs/platform-integration.md +122 -0
  451. package/docs/system-map.md +152 -0
  452. package/docs/telemetry-contract.md +214 -0
  453. package/docs/telemetry.md +191 -0
  454. package/docs/testing.md +82 -0
  455. package/docs/token-efficiency.md +80 -0
  456. package/docs/usage.md +1046 -0
  457. package/package.json +8 -5
  458. package/dist/cli-doctor.d.ts +0 -6
  459. package/dist/cli-doctor.js +0 -60
  460. package/dist/cli-launch.js +0 -112
  461. package/dist/daemon-resilience.d.ts +0 -22
  462. package/dist/daemon-resilience.js +0 -85
  463. package/dist/mcp-proxy.d.ts +0 -67
  464. package/dist/mcp-proxy.js +0 -337
  465. package/dist/tools/profiles.d.ts +0 -37
  466. package/dist/tools/profiles.js +0 -139
  467. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  468. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  469. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  470. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  471. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  472. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Pure, conservative patchers for an Astro app: the config gets the token/root `define` and the
3
+ * raised build target, one layout gets the dev-only connect `<script>`.
4
+ *
5
+ * Astro was the last gated framework where `init` printed a correct recipe and applied none of it —
6
+ * the only ⚠ left on a supported stack, and the user's first session was two hand-copied snippets
7
+ * away. Astro is Vite-based but renders its own HTML, so the Vite plugin's injection never fires and
8
+ * there is no entry module to wire: the connect has to live in a page or layout `<script>`, and the
9
+ * token has to be inlined by the config because nothing else is in the page's path to inject it.
10
+ *
11
+ * Both patchers bail to `manual` (the printed recipe) on any shape they do not fully recognise.
12
+ * Half-editing a build config is worse than a documented manual step.
13
+ */
14
+ import { RETICLE_DEFAULT_PORT, bridgeWsUrl } from '@reticlehq/core';
15
+ import { PatchKind } from './patch-kind.js';
16
+ /** Present in a patched config AND in a hand-followed recipe — so both count as already wired. */
17
+ const CONFIG_MARKER = '__RETICLE_TOKEN__';
18
+ /** Present in a patched layout and in the printed recipe's script. */
19
+ const LAYOUT_MARKER = 'reticle.connect';
20
+ /**
21
+ * The SDK, declared so Vite pre-bundles it BEFORE the first page load.
22
+ *
23
+ * The connect script does `await import('@reticlehq/react')`. Undeclared, Vite meets that import
24
+ * mid-load, pre-bundles it, and the hashed `/node_modules/.vite/deps/@reticlehq_react.js?v=…` URL
25
+ * the browser already requested stops existing — the import rejects with "Failed to fetch
26
+ * dynamically imported module", connect() never runs, and the page looks entirely normal. Measured
27
+ * on astro-nanostores, intermittent on whether the dep cache was warm. The Vite plugin has declared
28
+ * the SDK for this exact reason since the bug was first found on React; Astro is hand-patched and
29
+ * never got it.
30
+ */
31
+ const SDK_INCLUDE_LITERAL = `'@reticlehq/react'`;
32
+ /**
33
+ * An `include:` the app already declared — ours joins that array instead of adding a second key.
34
+ * Deliberately not anchored to a newline: `optimizeDeps: { include: ['x'] }` on one line is the
35
+ * common way to write it, and requiring a line break made the merge miss it and duplicate the key.
36
+ */
37
+ const EXISTING_INCLUDE = /(\s*include\s*:\s*\[)/;
38
+ const DEFINE_CONFIG = /defineConfig\s*\(\s*\{/;
39
+ /** A `vite:` key whose value is an object LITERAL — the one shape we can merge into safely. */
40
+ const VITE_OBJECT_KEY = /(^\s*vite\s*:\s*\{)/m;
41
+ /** Any `vite:` key at all. One that is not an object literal (a spread, an imported config) is ours to refuse. */
42
+ const ANY_VITE_KEY = /^\s*vite\s*:/m;
43
+ const BODY_CLOSE = '</body>';
44
+ const HELPER = `
45
+ function reticleToken() {
46
+ const dir = process.env['RETICLE_PAIRING_TOKEN_DIR'] || join(homedir(), '.reticle');
47
+ try { return readFileSync(join(dir, 'pairing-token'), 'utf8').trim(); } catch { return ''; }
48
+ }
49
+ `;
50
+ const HELPER_IMPORTS = `import { readFileSync } from 'node:fs';
51
+ import { homedir } from 'node:os';
52
+ import { join } from 'node:path';
53
+ `;
54
+ /**
55
+ * The keys we add inside `vite:`, each with what it must contain.
56
+ *
57
+ * Kept as a list rather than one blob because a config that already sets one of these must be merged
58
+ * INTO, not duplicated: the real astro-nanostores config has `build: { chunkSizeWarningLimit }`, and
59
+ * inserting our own `build` beside it gave the object two — where the last one wins, so
60
+ * `target: 'es2022'` was silently discarded while init reported success. Losing that target is not
61
+ * cosmetic: Astro's default down-levels the modern SDK bundle and dies on a destructuring transform.
62
+ */
63
+ const VITE_KEYS = [
64
+ { key: 'build', inner: `\n target: 'es2022',` },
65
+ {
66
+ key: 'optimizeDeps',
67
+ inner: `\n include: [${SDK_INCLUDE_LITERAL}],\n esbuildOptions: { target: 'es2022' },`,
68
+ },
69
+ {
70
+ key: 'define',
71
+ inner: `\n __RETICLE_TOKEN__: JSON.stringify(reticleToken()),\n __RETICLE_ROOT__: JSON.stringify(process.cwd()),`,
72
+ },
73
+ ];
74
+ /** Whole-key form, for a `vite:` block that does not have the key at all. */
75
+ const WHOLE = {
76
+ build: `\n build: { target: 'es2022' },`,
77
+ optimizeDeps: `\n optimizeDeps: { include: [${SDK_INCLUDE_LITERAL}], esbuildOptions: { target: 'es2022' } },`,
78
+ define: `\n define: {\n __RETICLE_TOKEN__: JSON.stringify(reticleToken()),\n __RETICLE_ROOT__: JSON.stringify(process.cwd()),\n },`,
79
+ };
80
+ /**
81
+ * Add our keys to an existing `vite: { ... }` block, merging into any the app already set.
82
+ *
83
+ * Returns null when a colliding key is not an object literal (`build: sharedConfig`) — there is no
84
+ * brace to merge into, and duplicating or replacing it would corrupt someone's build.
85
+ */
86
+ function mergeIntoViteBlock(source, braceAt) {
87
+ let out = source;
88
+ for (const { key, inner } of VITE_KEYS) {
89
+ // Scoped to the vite block by searching from its opening brace: a `build:` under `markdown:` or
90
+ // at the top level is somebody else's key and must not be touched.
91
+ const existing = new RegExp(`(\\n\\s*${key}\\s*:\\s*)([\\{A-Za-z_])`).exec(out.slice(braceAt));
92
+ if (existing?.index === undefined) {
93
+ out = `${out.slice(0, braceAt)}${WHOLE[key] ?? ''}${out.slice(braceAt)}`;
94
+ continue;
95
+ }
96
+ // The character after the colon decides: `{` is a literal we can open; anything else is a
97
+ // reference we must not touch.
98
+ if (existing[2] !== '{')
99
+ return null;
100
+ const at = braceAt + existing.index + existing[0].length; // just past the `{`
101
+ // An `include:` the app already wrote is an ARRAY, and a second `include:` key in the same
102
+ // object literal is not a merge — the last one wins and one of the two silently disappears.
103
+ // That is how `build.target` was lost while init still reported success. Join their array.
104
+ // Scoped to THIS block's braces: an `include:` under a later key is somebody else's array, and
105
+ // appending the SDK to it would both miss the target and edit a config we were not asked to.
106
+ const own = 'optimizeDeps' === key ? EXISTING_INCLUDE.exec(blockAfter(out, at)) : null;
107
+ if (own?.index === undefined) {
108
+ out = insertAt(out, at, inner);
109
+ continue;
110
+ }
111
+ // Their array first, then the rest of our keys at the block's start — inserting at the LOWER
112
+ // offset second keeps the first offset valid.
113
+ out = insertAt(out, at + own.index + own[0].length, `${SDK_INCLUDE_LITERAL}, `);
114
+ out = insertAt(out, at, withoutInclude(inner));
115
+ }
116
+ return out;
117
+ }
118
+ function insertAt(source, at, text) {
119
+ return `${source.slice(0, at)}${text}${source.slice(at)}`;
120
+ }
121
+ /**
122
+ * The text from just inside an opening brace to its match — a brace count, not a parser.
123
+ *
124
+ * Enough for the one question asked of it (does THIS object literal already declare `include`), and
125
+ * honest about its limit: a `{` or `}` inside a string literal in someone's Vite config would fool
126
+ * it. That only ever shortens or lengthens the window we search for `include:` in, so the worst case
127
+ * is the same duplicate-key merge we would have done before, never a corrupted file.
128
+ */
129
+ function blockAfter(source, at) {
130
+ let depth = 1;
131
+ for (let i = at; i < source.length; i++) {
132
+ if ('{' === source[i])
133
+ depth += 1;
134
+ else if ('}' === source[i]) {
135
+ depth -= 1;
136
+ if (0 === depth)
137
+ return source.slice(at, i);
138
+ }
139
+ }
140
+ return source.slice(at);
141
+ }
142
+ /** The optimizeDeps inner minus our `include:` line, for when the app already has one to join. */
143
+ function withoutInclude(inner) {
144
+ return inner.replace(`\n include: [${SDK_INCLUDE_LITERAL}],`, '');
145
+ }
146
+ /**
147
+ * The `vite:` block Astro needs. `build.target` is raised because Astro's default down-levels the
148
+ * modern SDK bundle and dies on a destructuring transform; `__RETICLE_ROOT__` is defined because
149
+ * without it every source pointer comes back as an absolute path from the machine that ran `init`.
150
+ */
151
+ const VITE_BLOCK = ` vite: {
152
+ build: { target: 'es2022' },
153
+ optimizeDeps: { include: [${SDK_INCLUDE_LITERAL}], esbuildOptions: { target: 'es2022' } },
154
+ define: {
155
+ __RETICLE_TOKEN__: JSON.stringify(reticleToken()),
156
+ __RETICLE_ROOT__: JSON.stringify(process.cwd()),
157
+ },
158
+ },
159
+ `;
160
+ export function patchAstroConfig(source) {
161
+ if (source.includes(CONFIG_MARKER))
162
+ return { kind: PatchKind.ALREADY };
163
+ // A `vite: { ... }` block is an object literal, so our keys can go straight after the brace and
164
+ // whatever is already in there is untouched. Refusing outright was the only genuine install defect
165
+ // left in the gate: the config bailed while the LAYOUT patch applied anyway, leaving an app with a
166
+ // connect snippet, no inlined token and no raised build target — which cannot connect, reported as
167
+ // one OK step and one warning.
168
+ const viteObject = VITE_OBJECT_KEY.exec(source);
169
+ if (viteObject?.index !== undefined) {
170
+ const at = viteObject.index + viteObject[0].length;
171
+ const merged = mergeIntoViteBlock(source, at);
172
+ if (merged !== null) {
173
+ return {
174
+ kind: PatchKind.APPLY,
175
+ code: `${HELPER_IMPORTS}${merged.trimStart()}\n${HELPER}`.trimEnd() + '\n',
176
+ };
177
+ }
178
+ return {
179
+ kind: PatchKind.MANUAL,
180
+ reason: 'this config sets a `vite` key Reticle needs (build / optimizeDeps / define) to something ' +
181
+ 'other than an object literal — merging into it is your call, not a text edit Reticle should make',
182
+ };
183
+ }
184
+ if (ANY_VITE_KEY.test(source)) {
185
+ // `vite: sharedConfig` — not an object literal, so there is no brace to merge into and guessing
186
+ // would corrupt a build config. The recipe is the honest answer here.
187
+ return {
188
+ kind: PatchKind.MANUAL,
189
+ reason: 'this config sets `vite:` to something other than an object literal — merging into it is your call, not a text edit Reticle should make',
190
+ };
191
+ }
192
+ const opening = DEFINE_CONFIG.exec(source);
193
+ if (opening?.index === undefined) {
194
+ return {
195
+ kind: PatchKind.MANUAL,
196
+ reason: "couldn't find a `defineConfig({ ... })` call to extend",
197
+ };
198
+ }
199
+ const at = opening.index + opening[0].length;
200
+ const withBlock = `${source.slice(0, at)}\n${VITE_BLOCK}${source.slice(at)}`;
201
+ return {
202
+ kind: PatchKind.APPLY,
203
+ code: `${HELPER_IMPORTS}${withBlock.trimStart()}\n${HELPER}`.trimEnd() + '\n',
204
+ };
205
+ }
206
+ /** The dev-only connect that goes inside the layout's `<body>`. */
207
+ function astroConnectScript(port, projectId) {
208
+ const url = port !== undefined && port !== RETICLE_DEFAULT_PORT
209
+ ? `\n url: '${bridgeWsUrl(port)}',`
210
+ : '';
211
+ const id = projectId !== undefined && projectId.length > 0 ? `\n projectId: '${projectId}',` : '';
212
+ return ` <script>
213
+ if (import.meta.env.DEV) {
214
+ const token = typeof __RETICLE_TOKEN__ !== 'undefined' ? __RETICLE_TOKEN__ : '';
215
+ const root = typeof __RETICLE_ROOT__ !== 'undefined' ? __RETICLE_ROOT__ : '';
216
+ const { reticle, install } = await import('@reticlehq/react');
217
+ install();
218
+ reticle.connect({${id}${url}
219
+ ...(token.length > 0 ? { token } : {}),
220
+ ...(root.length > 0 ? { root } : {}),
221
+ });
222
+ }
223
+ </script>
224
+ `;
225
+ }
226
+ export function patchAstroLayout(source, port, projectId) {
227
+ if (source.includes(LAYOUT_MARKER))
228
+ return { kind: PatchKind.ALREADY };
229
+ const at = source.lastIndexOf(BODY_CLOSE);
230
+ if (at < 0) {
231
+ return {
232
+ kind: PatchKind.MANUAL,
233
+ reason: "couldn't find a `</body>` to place the connect script before",
234
+ };
235
+ }
236
+ return {
237
+ kind: PatchKind.APPLY,
238
+ code: `${source.slice(0, at)}${astroConnectScript(port, projectId)}${source.slice(at)}`,
239
+ };
240
+ }
@@ -35,7 +35,10 @@ export function scanTestids(sources) {
35
35
  */
36
36
  const STORE_LIBRARIES = [
37
37
  ['@tanstack/react-query', "registerStore('queries', tanstackQueryStore(queryClient))"],
38
- ['zustand', "registerStore('app', useStore) // pass the store itself, not () => store.getState()"],
38
+ [
39
+ 'zustand',
40
+ "registerStore('app', useStore) // pass the store itself, not () => store.getState()",
41
+ ],
39
42
  ['@reduxjs/toolkit', "registerStore('app', store)"],
40
43
  ['redux', "registerStore('app', store)"],
41
44
  ['jotai', "registerStore('app', jotaiStore(getDefaultStore(), { cart, user }))"],
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Wiring Reticle into a Create React App project.
3
+ *
4
+ * CRA had no automated path: init reported `⚠ Connect snippet → index.html`, a target that cannot
5
+ * work. CRA's `public/index.html` is a static template the bundler never processes for modules, so a
6
+ * bare import in it resolves to nothing. `src/index.tsx` is what gets bundled, and that is where the
7
+ * connect has to arrive.
8
+ *
9
+ * The token is the second half of the problem. Every other stack has somewhere to inline it —
10
+ * Vite defines `__RETICLE_TOKEN__`, the Astro and HTML snippets read the file in Node. `src/index.tsx`
11
+ * is browser code inside a bundler that inlines exactly one thing: environment variables prefixed
12
+ * `REACT_APP_`. So it travels through `.env.development.local`, which is CRA's own documented
13
+ * mechanism and is gitignored by CRA's own template.
14
+ */
15
+ /** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
16
+ export declare const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
17
+ export declare const CRA_DEV_MODULE_PATH = "src/reticle-dev.ts";
18
+ export declare const CRA_ENV_PATH = ".env.development.local";
19
+ export declare const TOKEN_VAR = "REACT_APP_RETICLE_TOKEN";
20
+ /**
21
+ * What the app itself says when the token is not there.
22
+ *
23
+ * The token is a per-machine secret and CRA's own template gitignores the file it lives in, so it
24
+ * CANNOT travel with the repo — that part is correct and must stay. What was wrong is that it failed
25
+ * silently: a teammate clones, runs `npm start`, and the app connects with an empty token, so the
26
+ * only signal is the bridge's generic `authentication failed` in a console nobody had open. Naming
27
+ * the variable, the gitignored file and the one command that fixes it turns a dead app into a
28
+ * one-line repair. Every other stack reads the token in Node at dev-server start (the Vite plugin,
29
+ * withReticle); CRA has no such hook without ejecting, so this message is the fix.
30
+ */
31
+ export declare const CRA_TOKEN_MISSING_NOTE: string;
32
+ /**
33
+ * Said as its own NOTICE line, beside the write — not inside it.
34
+ *
35
+ * The caveat has always been in the write step's `detail`, and the write step renders `[✓]`. But
36
+ * `SKILL.md` tells whoever reads the report: *"If every line is `✓`, `·` or `–`, skip to Step 4 and
37
+ * validate."* So the one fact that makes this install conditional sat in the one place the reading
38
+ * protocol says to ignore — which is exactly how it was reported to us as "4 OK marks and no
39
+ * warning". The words were on screen; the reader was following instructions.
40
+ *
41
+ * It cannot simply be promoted: `run.ts` only writes steps whose status is APPLY, so demoting the
42
+ * token step to NOTICE would stop it writing the token and break the install outright.
43
+ */
44
+ export declare const CRA_TOKEN_PER_MACHINE_NOTICE: string;
45
+ /** Add the import after the last existing one, or null when it is already present. */
46
+ export declare function craImportPatch(source: string): string | null;
47
+ /** Set the token variable, or null when nothing needs to change. */
48
+ export declare function craEnvPatch(existing: string | null, token: string): string | null;
49
+ /** The dev-only connect module imported from `src/index.tsx`. */
50
+ export declare function craDevModuleFile(port: number | undefined, projectId?: string): string;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Wiring Reticle into a Create React App project.
3
+ *
4
+ * CRA had no automated path: init reported `⚠ Connect snippet → index.html`, a target that cannot
5
+ * work. CRA's `public/index.html` is a static template the bundler never processes for modules, so a
6
+ * bare import in it resolves to nothing. `src/index.tsx` is what gets bundled, and that is where the
7
+ * connect has to arrive.
8
+ *
9
+ * The token is the second half of the problem. Every other stack has somewhere to inline it —
10
+ * Vite defines `__RETICLE_TOKEN__`, the Astro and HTML snippets read the file in Node. `src/index.tsx`
11
+ * is browser code inside a bundler that inlines exactly one thing: environment variables prefixed
12
+ * `REACT_APP_`. So it travels through `.env.development.local`, which is CRA's own documented
13
+ * mechanism and is gitignored by CRA's own template.
14
+ */
15
+ /** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
16
+ export const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
17
+ export const CRA_DEV_MODULE_PATH = 'src/reticle-dev.ts';
18
+ export const CRA_ENV_PATH = '.env.development.local';
19
+ export const TOKEN_VAR = 'REACT_APP_RETICLE_TOKEN';
20
+ /**
21
+ * What the app itself says when the token is not there.
22
+ *
23
+ * The token is a per-machine secret and CRA's own template gitignores the file it lives in, so it
24
+ * CANNOT travel with the repo — that part is correct and must stay. What was wrong is that it failed
25
+ * silently: a teammate clones, runs `npm start`, and the app connects with an empty token, so the
26
+ * only signal is the bridge's generic `authentication failed` in a console nobody had open. Naming
27
+ * the variable, the gitignored file and the one command that fixes it turns a dead app into a
28
+ * one-line repair. Every other stack reads the token in Node at dev-server start (the Vite plugin,
29
+ * withReticle); CRA has no such hook without ejecting, so this message is the fix.
30
+ */
31
+ export const CRA_TOKEN_MISSING_NOTE = `${TOKEN_VAR} is not set, so Reticle cannot pair with the daemon. ` +
32
+ `The pairing token is per-machine and ${CRA_ENV_PATH} is gitignored by CRA's template, ` +
33
+ 'so it does not survive a clone. Run `npx reticle init` in this project to write it for this machine.';
34
+ /**
35
+ * Said as its own NOTICE line, beside the write — not inside it.
36
+ *
37
+ * The caveat has always been in the write step's `detail`, and the write step renders `[✓]`. But
38
+ * `SKILL.md` tells whoever reads the report: *"If every line is `✓`, `·` or `–`, skip to Step 4 and
39
+ * validate."* So the one fact that makes this install conditional sat in the one place the reading
40
+ * protocol says to ignore — which is exactly how it was reported to us as "4 OK marks and no
41
+ * warning". The words were on screen; the reader was following instructions.
42
+ *
43
+ * It cannot simply be promoted: `run.ts` only writes steps whose status is APPLY, so demoting the
44
+ * token step to NOTICE would stop it writing the token and break the install outright.
45
+ */
46
+ export const CRA_TOKEN_PER_MACHINE_NOTICE = `${CRA_ENV_PATH} is in CRA's own .gitignore, so the token just written does not travel. ` +
47
+ 'It works on THIS machine and nowhere else: a teammate cloning the repo, CI, or a container ' +
48
+ 'gets an app that boots, never pairs, and reports it only in the browser console. ' +
49
+ 'Each developer runs `npx reticle init` once locally.';
50
+ /** Add the import after the last existing one, or null when it is already present. */
51
+ export function craImportPatch(source) {
52
+ if (source.includes(CRA_DEV_MODULE_IMPORT))
53
+ return null;
54
+ const lines = source.split('\n');
55
+ // After the LAST import: React's own imports must still run first, and a side-effect import placed
56
+ // above them would connect before the app exists.
57
+ let lastImport = -1;
58
+ for (const [index, line] of lines.entries()) {
59
+ if (/^\s*import\s/.test(line))
60
+ lastImport = index;
61
+ }
62
+ lines.splice(lastImport + 1, 0, CRA_DEV_MODULE_IMPORT);
63
+ return lines.join('\n');
64
+ }
65
+ /** Set the token variable, or null when nothing needs to change. */
66
+ export function craEnvPatch(existing, token) {
67
+ if ('' === token)
68
+ return null;
69
+ const line = `${TOKEN_VAR}=${token}`;
70
+ if (null === existing || '' === existing.trim())
71
+ return `${line}\n`;
72
+ if (existing.includes(line))
73
+ return null;
74
+ // Replace rather than append: two assignments of one variable is a silent coin flip on which wins.
75
+ if (new RegExp(`^${TOKEN_VAR}=`, 'm').test(existing)) {
76
+ return existing.replace(new RegExp(`^${TOKEN_VAR}=.*$`, 'm'), line);
77
+ }
78
+ return `${existing.endsWith('\n') ? existing : `${existing}\n`}${line}\n`;
79
+ }
80
+ /** The dev-only connect module imported from `src/index.tsx`. */
81
+ export function craDevModuleFile(port, projectId) {
82
+ const fields = [];
83
+ if (port !== undefined)
84
+ fields.push(`url: 'ws://127.0.0.1:${String(port)}/reticle'`);
85
+ if (projectId !== undefined && projectId.length > 0)
86
+ fields.push(`projectId: '${projectId}'`);
87
+ const inline = fields.length > 0 ? `${fields.join(', ')}, ` : '';
88
+ return `// Dev-only: connect Reticle. Imported for its side effect from src/index.tsx.
89
+ //
90
+ // CRA's public/index.html is a static template the bundler never processes for modules, so the
91
+ // connect cannot live there. The pairing token arrives through REACT_APP_RETICLE_TOKEN because
92
+ // REACT_APP_* is the only thing CRA inlines into browser code.
93
+ if (process.env.NODE_ENV === 'development') {
94
+ void import('@reticlehq/react').then(({ reticle, install }) => {
95
+ install();
96
+ const token = process.env.${TOKEN_VAR} ?? '';
97
+ // Loud on purpose: without this the only symptom is the bridge's generic auth failure.
98
+ if (token.length === 0) console.error(${JSON.stringify(`[reticle] ${CRA_TOKEN_MISSING_NOTE}`)});
99
+ // Still attempt it — a bridge running without a token pairs fine.
100
+ reticle.connect({ ${inline}...(token.length > 0 ? { token } : {}) });
101
+ });
102
+ }
103
+
104
+ export {};
105
+ `;
106
+ }
@@ -5,6 +5,7 @@
5
5
  * (jsonc/comment) files bail to manual rather than being rewritten.
6
6
  */
7
7
  import { NPX, MCP_SERVER_NAME, npxServerArgs } from './mcp.js';
8
+ import { RETICLE_NPM_PACKAGE } from '../version/server-version.js';
8
9
  /** Path of Cursor's global MCP config, relative to the user's home directory. */
9
10
  export const CURSOR_MCP_RELPATH = '.cursor/mcp.json';
10
11
  /** The directory whose presence signals Cursor is installed for this user. */
@@ -18,18 +19,47 @@ export function cursorServerEntry() {
18
19
  return { command: NPX, args: npxServerArgs() };
19
20
  }
20
21
  function parseConfig(existing) {
21
- if (existing === null || existing.trim().length === 0)
22
+ if (null === existing || 0 === existing.trim().length)
22
23
  return { ok: true, config: {} };
23
24
  try {
24
25
  const parsed = JSON.parse(existing);
25
- if (typeof parsed !== 'object' || parsed === null)
26
- return { ok: true, config: {} };
26
+ // Valid JSON that is not a PLAIN OBJECT — `[]`, `3`, `"x"`, `null` — used to fall through to an
27
+ // empty config, and the file was then rewritten wholesale. Unparseable JSON was already handled
28
+ // conservatively; this adjacent case destroyed the file instead. Whatever is in there is the
29
+ // user's, and we do not understand it, so we do not touch it.
30
+ if (typeof parsed !== 'object' || null === parsed || Array.isArray(parsed))
31
+ return { ok: false };
27
32
  return { ok: true, config: parsed };
28
33
  }
29
34
  catch {
30
35
  return { ok: false };
31
36
  }
32
37
  }
38
+ /**
39
+ * Should this entry be left exactly as it is?
40
+ *
41
+ * Two different reasons to leave one alone, and only one of them is "it is correct":
42
+ * - it already matches what we would write; or
43
+ * - it is not an entry WE wrote. Somebody pointing `reticle` at their own local build is a
44
+ * deliberate choice, and silently replacing it with the published npx command would break their
45
+ * setup to fix a problem they do not have.
46
+ *
47
+ * What IS repaired is a stale entry of our own shape — an old pin, a command that has moved — which
48
+ * presence-only idempotency reported as "already registered" and never fixed, so an upgrade could
49
+ * not repair the thing an upgrade exists to repair.
50
+ */
51
+ function leaveEntryAlone(existing) {
52
+ if (JSON.stringify(existing) === JSON.stringify(cursorServerEntry()))
53
+ return true;
54
+ if (typeof existing !== 'object' || null === existing)
55
+ return true;
56
+ const command = existing.command;
57
+ const args = existing.args;
58
+ const ours = command === NPX &&
59
+ Array.isArray(args) &&
60
+ args.some((arg) => 'string' === typeof arg && arg.includes(RETICLE_NPM_PACKAGE));
61
+ return !ours;
62
+ }
33
63
  export function mergeCursorConfig(existing) {
34
64
  const parsed = parseConfig(existing);
35
65
  if (!parsed.ok) {
@@ -37,7 +67,12 @@ export function mergeCursorConfig(existing) {
37
67
  }
38
68
  const config = parsed.config;
39
69
  const servers = config.mcpServers ?? {};
40
- if (Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME)) {
70
+ // Presence alone used to mean "already registered", so a `reticle` entry left by an older release —
71
+ // a command that no longer exists, or a stale pin — was reported done and never repaired. An
72
+ // upgrade could not fix a bad entry, which is exactly when it needs to. Only OUR key is replaced;
73
+ // every other server and top-level key is carried through untouched by the spread below.
74
+ if (Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME) &&
75
+ leaveEntryAlone(servers[MCP_SERVER_NAME])) {
41
76
  return { status: CursorMergeStatus.ALREADY, content: existing ?? '' };
42
77
  }
43
78
  const merged = {
@@ -23,7 +23,7 @@ export declare const DesktopFinding: {
23
23
  readonly ELECTRON_CAPTURE_MISSING: "electron-capture-missing";
24
24
  };
25
25
  export type DesktopFinding = (typeof DesktopFinding)[keyof typeof DesktopFinding];
26
- export interface DesktopDiagnosis {
26
+ interface DesktopDiagnosis {
27
27
  code: DesktopFinding;
28
28
  /** Which file to look at. */
29
29
  file: string;
@@ -33,8 +33,9 @@ export interface DesktopDiagnosis {
33
33
  fix: string;
34
34
  }
35
35
  /** Reads a project-relative path. Undefined means the file does not exist. */
36
- export type ReadFile = (path: string) => string | undefined;
36
+ type ReadFile = (path: string) => string | undefined;
37
37
  /** Is this project a desktop shell at all? Used to print a positive result instead of silence. */
38
38
  export declare function isDesktopProject(read: ReadFile): boolean;
39
39
  /** Which desktop shell (if any) this project is, and what is missing from its Reticle wiring. */
40
40
  export declare function diagnoseDesktop(read: ReadFile, port: number): DesktopDiagnosis[];
41
+ export {};
@@ -31,7 +31,7 @@ function parseJson(text) {
31
31
  return undefined;
32
32
  try {
33
33
  const parsed = JSON.parse(text);
34
- return typeof parsed === 'object' && parsed !== null
34
+ return 'object' === typeof parsed && parsed !== null
35
35
  ? parsed
36
36
  : undefined;
37
37
  }
@@ -40,7 +40,7 @@ function parseJson(text) {
40
40
  }
41
41
  }
42
42
  function record(value) {
43
- return typeof value === 'object' && value !== null ? value : {};
43
+ return 'object' === typeof value && value !== null ? value : {};
44
44
  }
45
45
  /**
46
46
  * Does this CSP permit the bridge WebSocket on `port`?
@@ -72,7 +72,7 @@ function diagnoseTauri(read, port) {
72
72
  if (conf === undefined)
73
73
  return [];
74
74
  const security = record(record(conf['app'])['security']);
75
- const csp = typeof security['csp'] === 'string' ? security['csp'] : undefined;
75
+ const csp = 'string' === typeof security['csp'] ? security['csp'] : undefined;
76
76
  const findings = [];
77
77
  if (!cspAllowsBridge(csp, port)) {
78
78
  findings.push({
@@ -193,6 +193,6 @@ export function diagnoseDesktop(read, port) {
193
193
  const deps = { ...record(pkg['dependencies']), ...record(pkg['devDependencies']) };
194
194
  if (deps['electron'] === undefined)
195
195
  return [];
196
- const main = typeof pkg['main'] === 'string' ? pkg['main'] : undefined;
196
+ const main = 'string' === typeof pkg['main'] ? pkg['main'] : undefined;
197
197
  return main === undefined ? [] : diagnoseElectron(read, main);
198
198
  }
@@ -7,6 +7,8 @@ export declare const Framework: {
7
7
  readonly VITE: "vite";
8
8
  readonly SVELTEKIT: "sveltekit";
9
9
  readonly ASTRO: "astro";
10
+ /** Create React App. No config file exists, so `react-scripts` in the dependencies is the signal. */
11
+ readonly CRA: "cra";
10
12
  readonly HTML: "html";
11
13
  };
12
14
  export type Framework = (typeof Framework)[keyof typeof Framework];
@@ -60,8 +62,13 @@ export interface Detection {
60
62
  }
61
63
  /** Extract the leading major version from a semver range like "^19.0.0" or "19.1.1". */
62
64
  export declare function parseMajor(range: string | undefined): number | undefined;
63
- /** Marker basenames present inside the project's `node_modules`, if it has one. */
64
- export declare function packageManagerFromNodeModules(markers: ReadonlySet<string>): PackageManager | undefined;
65
+ /**
66
+ * Whether an installed tree identifies the manager that built it. Callers use this to decide whether
67
+ * they still need weaker evidence — see `resolveLockfiles`, which stops inheriting an ancestor
68
+ * lockfile once the project's own tree can answer the question.
69
+ */
70
+ export declare function namesAPackageManager(markers: ReadonlySet<string>): boolean;
71
+ export declare function detectPackageManager(lockfiles: ReadonlySet<string>, nodeModulesMarkers: ReadonlySet<string>): PackageManager;
65
72
  export declare function detect(input: DetectInput): Detection;
66
73
  interface InstallCommand {
67
74
  command: string;
@@ -7,6 +7,8 @@ export const Framework = {
7
7
  VITE: 'vite',
8
8
  SVELTEKIT: 'sveltekit',
9
9
  ASTRO: 'astro',
10
+ /** Create React App. No config file exists, so `react-scripts` in the dependencies is the signal. */
11
+ CRA: 'cra',
10
12
  HTML: 'html',
11
13
  };
12
14
  export const PackageManager = {
@@ -30,7 +32,12 @@ export const UiLibrary = {
30
32
  const NEXT_CONFIGS = ['next.config.js', 'next.config.mjs', 'next.config.ts', 'next.config.cjs'];
31
33
  const VITE_CONFIGS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.mts'];
32
34
  const SVELTE_CONFIGS = ['svelte.config.js', 'svelte.config.ts', 'svelte.config.mjs'];
33
- const ASTRO_CONFIGS = ['astro.config.mjs', 'astro.config.js', 'astro.config.ts', 'astro.config.cjs'];
35
+ const ASTRO_CONFIGS = [
36
+ 'astro.config.mjs',
37
+ 'astro.config.js',
38
+ 'astro.config.ts',
39
+ 'astro.config.cjs',
40
+ ];
34
41
  function depVersion(pkg, name) {
35
42
  return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name];
36
43
  }
@@ -42,7 +49,7 @@ export function parseMajor(range) {
42
49
  if (range === undefined)
43
50
  return undefined;
44
51
  const match = range.match(/(\d+)/);
45
- if (match === null || match[1] === undefined)
52
+ if (null === match || match[1] === undefined)
46
53
  return undefined;
47
54
  const major = parseInt(match[1], 10);
48
55
  return isNaN(major) ? undefined : major;
@@ -62,13 +69,21 @@ const NODE_MODULES_MARKERS = [
62
69
  ['.package-lock.json', PackageManager.NPM],
63
70
  ];
64
71
  /** Marker basenames present inside the project's `node_modules`, if it has one. */
65
- export function packageManagerFromNodeModules(markers) {
72
+ function packageManagerFromNodeModules(markers) {
66
73
  for (const [name, pm] of NODE_MODULES_MARKERS)
67
74
  if (markers.has(name))
68
75
  return pm;
69
76
  return undefined;
70
77
  }
71
- function detectPackageManager(lockfiles, nodeModulesMarkers) {
78
+ /**
79
+ * Whether an installed tree identifies the manager that built it. Callers use this to decide whether
80
+ * they still need weaker evidence — see `resolveLockfiles`, which stops inheriting an ancestor
81
+ * lockfile once the project's own tree can answer the question.
82
+ */
83
+ export function namesAPackageManager(markers) {
84
+ return packageManagerFromNodeModules(markers) !== undefined;
85
+ }
86
+ export function detectPackageManager(lockfiles, nodeModulesMarkers) {
72
87
  if (lockfiles.has('pnpm-lock.yaml'))
73
88
  return PackageManager.PNPM;
74
89
  if (lockfiles.has('yarn.lock'))
@@ -97,6 +112,10 @@ function detectFramework(input) {
97
112
  if (depVersion(pkg, 'vite') !== undefined || hasAnyConfig(configFiles, VITE_CONFIGS)) {
98
113
  return Framework.VITE;
99
114
  }
115
+ // Checked after Vite, never before: a project migrating off CRA can carry both, and the Vite path
116
+ // is the one that works. CRA has no config file at all, so the dependency is the only signal.
117
+ if (depVersion(pkg, 'react-scripts') !== undefined)
118
+ return Framework.CRA;
100
119
  return Framework.HTML;
101
120
  }
102
121
  /**
@@ -136,7 +155,7 @@ const INSTALL_ARGS = {
136
155
  };
137
156
  /** Build a dev-dependency install command for one or more packages (e.g. the kit + its build plugin). */
138
157
  export function installCommandParts(pm, pkgs) {
139
- const list = typeof pkgs === 'string' ? [pkgs] : pkgs;
158
+ const list = 'string' === typeof pkgs ? [pkgs] : pkgs;
140
159
  return { command: pm, args: [...INSTALL_ARGS[pm], ...list] };
141
160
  }
142
161
  export function installCommand(pm, pkgs) {