@reticlehq/server 2.11.0 → 2.13.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 (513) hide show
  1. package/SKILL.md +93 -111
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -14
  5. package/dist/bridge/bridge.js +248 -165
  6. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  7. package/dist/bridge/bridge.test-harness.js +8 -1
  8. package/dist/bridge/pairing-token.d.ts +5 -0
  9. package/dist/bridge/pairing-token.js +28 -0
  10. package/dist/bridge/protocol-skew.d.ts +37 -0
  11. package/dist/bridge/protocol-skew.js +45 -0
  12. package/dist/capsule/capsule-store.d.ts +8 -7
  13. package/dist/capsule/capsule-store.js +1 -1
  14. package/dist/capsule/causal-summary.d.ts +2 -2
  15. package/dist/capsule/causal-summary.js +7 -2
  16. package/dist/chromium-launch-options.d.ts +28 -0
  17. package/dist/chromium-launch-options.js +29 -0
  18. package/dist/cli/cli-doctor.js +39 -1
  19. package/dist/cli/cli-flow-commands.d.ts +3 -1
  20. package/dist/cli/cli-flow-commands.js +34 -4
  21. package/dist/cli/cli-kill.d.ts +2 -1
  22. package/dist/cli/cli-launch.d.ts +11 -1
  23. package/dist/cli/cli-launch.js +59 -10
  24. package/dist/cli/cli-parse.d.ts +17 -1
  25. package/dist/cli/cli-parse.js +238 -12
  26. package/dist/cli/cli-update-commands.js +20 -1
  27. package/dist/cli/cli-verify.d.ts +2 -1
  28. package/dist/cli/cli-verify.js +5 -4
  29. package/dist/cli/cloud-cli.js +428 -253
  30. package/dist/cli/cloud-keystore.d.ts +37 -0
  31. package/dist/cli/cloud-keystore.js +97 -0
  32. package/dist/cli/cloud-kit.d.ts +37 -0
  33. package/dist/cli/cloud-kit.js +121 -0
  34. package/dist/cli/cloud-login.d.ts +17 -0
  35. package/dist/cli/cloud-login.js +183 -0
  36. package/dist/cli/cloud-session.d.ts +48 -0
  37. package/dist/cli/cloud-session.js +53 -0
  38. package/dist/cli/config-discovery.d.ts +2 -1
  39. package/dist/cli/config-discovery.js +1 -0
  40. package/dist/cli/daemon-start-options.d.ts +32 -0
  41. package/dist/cli/daemon-start-options.js +38 -0
  42. package/dist/cli/device-flow.d.ts +76 -0
  43. package/dist/cli/device-flow.js +56 -0
  44. package/dist/cli/doctor-rows.d.ts +12 -1
  45. package/dist/cli/doctor-rows.js +12 -1
  46. package/dist/cli/drive-attach.d.ts +13 -0
  47. package/dist/cli/drive-attach.js +18 -0
  48. package/dist/cli/drive-command.js +7 -3
  49. package/dist/cli/gate-exit.d.ts +21 -0
  50. package/dist/cli/gate-exit.js +21 -0
  51. package/dist/cli/gate-hook-message.d.ts +33 -0
  52. package/dist/cli/gate-hook-message.js +46 -0
  53. package/dist/cli/mcp-command.d.ts +25 -0
  54. package/dist/cli/mcp-command.js +174 -0
  55. package/dist/cli/project-name.d.ts +30 -0
  56. package/dist/cli/project-name.js +44 -0
  57. package/dist/cli/sibling-ports.d.ts +37 -0
  58. package/dist/cli/sibling-ports.js +56 -0
  59. package/dist/cli/status-next-action.d.ts +14 -0
  60. package/dist/cli/status-next-action.js +4 -4
  61. package/dist/cli.d.ts +1 -0
  62. package/dist/cli.js +167 -186
  63. package/dist/cloud/cloud-config.d.ts +24 -2
  64. package/dist/cloud/cloud-config.js +64 -5
  65. package/dist/cloud/cloud-sync.d.ts +16 -5
  66. package/dist/cloud/cloud-sync.js +64 -1
  67. package/dist/cloud/sync-cycle.d.ts +110 -0
  68. package/dist/cloud/sync-cycle.js +270 -0
  69. package/dist/cloud/sync-daemon.d.ts +69 -0
  70. package/dist/cloud/sync-daemon.js +260 -0
  71. package/dist/cloud/sync-disk.d.ts +9 -0
  72. package/dist/cloud/sync-disk.js +191 -0
  73. package/dist/cloud/sync-hash.d.ts +2 -0
  74. package/dist/cloud/sync-hash.js +35 -0
  75. package/dist/crawl/crawl-tools.js +5 -3
  76. package/dist/crawl/crawl.d.ts +12 -13
  77. package/dist/crawl/crawl.js +25 -3
  78. package/dist/daemon/daemon-resolve.d.ts +122 -0
  79. package/dist/daemon/daemon-resolve.js +187 -0
  80. package/dist/daemon/dev-servers.d.ts +15 -0
  81. package/dist/daemon/dev-servers.js +68 -0
  82. package/dist/daemon/free-port.d.ts +29 -0
  83. package/dist/daemon/free-port.js +55 -0
  84. package/dist/daemon/heartbeat.d.ts +0 -4
  85. package/dist/daemon/heartbeat.js +2 -2
  86. package/dist/daemon/wake-decision.d.ts +32 -0
  87. package/dist/daemon/wake-decision.js +38 -0
  88. package/dist/dev/stale-issue-guard.d.ts +0 -2
  89. package/dist/dev/stale-issue-guard.js +1 -1
  90. package/dist/domain/domain-tools.js +2 -2
  91. package/dist/events/contradictions.d.ts +59 -0
  92. package/dist/events/contradictions.js +253 -27
  93. package/dist/events/declared.d.ts +17 -0
  94. package/dist/events/declared.js +43 -1
  95. package/dist/events/echo-mismatch.js +65 -1
  96. package/dist/events/event-filters.d.ts +8 -0
  97. package/dist/events/event-filters.js +19 -2
  98. package/dist/events/observed-in-window.d.ts +12 -0
  99. package/dist/events/observed-in-window.js +17 -0
  100. package/dist/events/predicate-asks.d.ts +13 -0
  101. package/dist/events/predicate-asks.js +22 -0
  102. package/dist/events/predicate-eval.js +117 -27
  103. package/dist/events/predicate-parse.d.ts +0 -6
  104. package/dist/events/predicate-parse.js +43 -3
  105. package/dist/events/predicate-precheck.d.ts +8 -0
  106. package/dist/events/predicate-precheck.js +41 -0
  107. package/dist/events/predicate-route.d.ts +14 -0
  108. package/dist/events/predicate-route.js +36 -8
  109. package/dist/events/predicate-schema.d.ts +44 -0
  110. package/dist/events/predicate-schema.js +126 -23
  111. package/dist/events/predicate.d.ts +20 -0
  112. package/dist/events/predicate.js +77 -6
  113. package/dist/events/split-text-miss.d.ts +30 -0
  114. package/dist/events/split-text-miss.js +44 -0
  115. package/dist/flows/coverage.d.ts +0 -8
  116. package/dist/flows/coverage.js +1 -1
  117. package/dist/flows/decision.d.ts +5 -1
  118. package/dist/flows/decision.js +33 -7
  119. package/dist/flows/flake.d.ts +6 -8
  120. package/dist/flows/flake.js +1 -1
  121. package/dist/flows/flow-intent.d.ts +52 -0
  122. package/dist/flows/flow-intent.js +123 -0
  123. package/dist/flows/flow-memory-consult.d.ts +55 -0
  124. package/dist/flows/flow-memory-consult.js +38 -0
  125. package/dist/flows/flow-replay-run.d.ts +18 -7
  126. package/dist/flows/flow-replay-run.js +263 -23
  127. package/dist/flows/flow-replay.js +11 -2
  128. package/dist/flows/flow-step-runners.d.ts +8 -0
  129. package/dist/flows/flow-step-runners.js +8 -2
  130. package/dist/flows/flow-store-for-session.d.ts +31 -0
  131. package/dist/flows/flow-store-for-session.js +33 -0
  132. package/dist/flows/flow-success.js +15 -3
  133. package/dist/flows/flow-tools.js +87 -33
  134. package/dist/flows/flows.d.ts +14 -1
  135. package/dist/flows/flows.js +502 -24
  136. package/dist/flows/heal-run.js +5 -2
  137. package/dist/flows/predicate-to-expect.d.ts +17 -12
  138. package/dist/flows/predicate-to-expect.js +36 -15
  139. package/dist/flows/recordings.d.ts +13 -1
  140. package/dist/flows/recordings.js +17 -2
  141. package/dist/flows/replay.d.ts +22 -2
  142. package/dist/flows/replay.js +28 -2
  143. package/dist/flows/server-verify.js +16 -2
  144. package/dist/flows/verify-change-tools.js +9 -3
  145. package/dist/honesty/blind-spots.d.ts +51 -15
  146. package/dist/honesty/blind-spots.js +64 -4
  147. package/dist/honesty/feature-capture.d.ts +150 -0
  148. package/dist/honesty/feature-capture.js +233 -0
  149. package/dist/honesty/honesty.d.ts +24 -0
  150. package/dist/honesty/honesty.js +25 -1
  151. package/dist/honesty/instrumentation-gaps.d.ts +39 -6
  152. package/dist/honesty/instrumentation-gaps.js +88 -5
  153. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  154. package/dist/honesty/tool-hit-rate.js +90 -0
  155. package/dist/honesty/undeclared-change.d.ts +51 -0
  156. package/dist/honesty/undeclared-change.js +56 -0
  157. package/dist/honesty/verified.d.ts +16 -0
  158. package/dist/honesty/verified.js +84 -5
  159. package/dist/http-server.d.ts +0 -1
  160. package/dist/http-server.js +13 -1
  161. package/dist/hunt/hunt-report.d.ts +0 -2
  162. package/dist/hunt/hunt-report.js +1 -1
  163. package/dist/impact/impact-recorder.d.ts +31 -8
  164. package/dist/impact/impact-recorder.js +104 -21
  165. package/dist/impact/impact-store.d.ts +13 -4
  166. package/dist/impact/impact-store.js +44 -3
  167. package/dist/index.d.ts +4 -1
  168. package/dist/index.js +113 -7
  169. package/dist/init/agent-rules.d.ts +30 -6
  170. package/dist/init/agent-rules.js +54 -13
  171. package/dist/init/astro-patch.d.ts +16 -0
  172. package/dist/init/astro-patch.js +28 -0
  173. package/dist/init/capabilities.d.ts +4 -3
  174. package/dist/init/capabilities.js +23 -3
  175. package/dist/init/closing-hint.d.ts +36 -0
  176. package/dist/init/closing-hint.js +120 -0
  177. package/dist/init/confirm.d.ts +27 -28
  178. package/dist/init/confirm.js +51 -11
  179. package/dist/init/cra.d.ts +34 -5
  180. package/dist/init/cra.js +96 -25
  181. package/dist/init/csp-check.js +17 -2
  182. package/dist/init/csp-doctor.d.ts +8 -1
  183. package/dist/init/csp-doctor.js +8 -3
  184. package/dist/init/dev-script.d.ts +50 -0
  185. package/dist/init/dev-script.js +66 -0
  186. package/dist/init/format-generated.d.ts +18 -0
  187. package/dist/init/format-generated.js +39 -0
  188. package/dist/init/install-hint.d.ts +9 -0
  189. package/dist/init/install-hint.js +29 -0
  190. package/dist/init/mcp-clients.d.ts +7 -0
  191. package/dist/init/mcp-clients.js +7 -0
  192. package/dist/init/mcp.d.ts +17 -6
  193. package/dist/init/mcp.js +27 -11
  194. package/dist/init/node-io.js +11 -1
  195. package/dist/init/plan-framework.d.ts +0 -5
  196. package/dist/init/plan-framework.js +147 -37
  197. package/dist/init/plan.d.ts +30 -37
  198. package/dist/init/plan.js +50 -60
  199. package/dist/init/preflight.d.ts +28 -0
  200. package/dist/init/preflight.js +38 -0
  201. package/dist/init/refresh-rules.d.ts +39 -0
  202. package/dist/init/refresh-rules.js +51 -0
  203. package/dist/init/run.d.ts +32 -35
  204. package/dist/init/run.js +151 -202
  205. package/dist/init/slash-command.d.ts +1 -1
  206. package/dist/init/snippets.js +90 -31
  207. package/dist/init/vite-config.d.ts +1 -1
  208. package/dist/init/vite-config.js +39 -25
  209. package/dist/init/workspace-apps.d.ts +17 -10
  210. package/dist/init/workspace-apps.js +110 -0
  211. package/dist/input/network-mock-tools.js +3 -6
  212. package/dist/input/real-input.d.ts +2 -0
  213. package/dist/input/real-input.js +28 -7
  214. package/dist/input/scroll-find.d.ts +7 -4
  215. package/dist/input/scroll-find.js +63 -25
  216. package/dist/input/scroll-tools.js +4 -6
  217. package/dist/input/viewport-tools.js +5 -4
  218. package/dist/intent/inline-intent.d.ts +37 -0
  219. package/dist/intent/inline-intent.js +193 -0
  220. package/dist/intent/intent-shard-store.d.ts +72 -0
  221. package/dist/intent/intent-shard-store.js +203 -0
  222. package/dist/intent/intent-shard.d.ts +411 -0
  223. package/dist/intent/intent-shard.js +176 -0
  224. package/dist/intent/intent-store.d.ts +49 -0
  225. package/dist/intent/intent-store.js +133 -0
  226. package/dist/intent/intent-subject.d.ts +45 -0
  227. package/dist/intent/intent-subject.js +177 -0
  228. package/dist/intent/intent-tools.d.ts +2 -0
  229. package/dist/intent/intent-tools.js +143 -0
  230. package/dist/intent/open-intents.d.ts +42 -0
  231. package/dist/intent/open-intents.js +40 -0
  232. package/dist/journal/envelope.d.ts +26 -26
  233. package/dist/journal/journal-recorder.d.ts +23 -0
  234. package/dist/journal/journal-recorder.js +35 -0
  235. package/dist/license/license-env.d.ts +29 -0
  236. package/dist/license/license-env.js +148 -0
  237. package/dist/loopback-agent.d.ts +31 -0
  238. package/dist/loopback-agent.js +57 -0
  239. package/dist/mcp/attach-memory.d.ts +83 -0
  240. package/dist/mcp/attach-memory.js +185 -0
  241. package/dist/mcp/client-identity.d.ts +31 -0
  242. package/dist/mcp/client-identity.js +51 -0
  243. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  244. package/dist/mcp/mcp-post-transport.js +109 -0
  245. package/dist/mcp/mcp-proxy.d.ts +2 -8
  246. package/dist/mcp/mcp-proxy.js +64 -62
  247. package/dist/mcp/mcp.d.ts +2 -0
  248. package/dist/mcp/mcp.js +52 -24
  249. package/dist/mcp/proxy-backoff.d.ts +11 -0
  250. package/dist/mcp/proxy-backoff.js +13 -0
  251. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  252. package/dist/mcp/proxy-daemon-probe.js +7 -1
  253. package/dist/mcp/proxy-handshake.d.ts +23 -0
  254. package/dist/mcp/proxy-handshake.js +31 -0
  255. package/dist/mcp/server-instructions.js +45 -3
  256. package/dist/mcp/tool-catalog-cache.d.ts +16 -0
  257. package/dist/mcp/tool-catalog-cache.js +22 -0
  258. package/dist/memory/memory-tools.d.ts +2 -0
  259. package/dist/memory/memory-tools.js +82 -0
  260. package/dist/memory/project-memory.d.ts +34 -0
  261. package/dist/memory/project-memory.js +96 -0
  262. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  263. package/dist/oracles/flow-instrument-gaps.js +4 -5
  264. package/dist/oracles/propose-consequences.js +12 -3
  265. package/dist/oracles/self-instrument.d.ts +9 -9
  266. package/dist/oracles/self-instrument.js +19 -13
  267. package/dist/pool/browser-pool.d.ts +75 -5
  268. package/dist/pool/browser-pool.js +144 -5
  269. package/dist/pool/playwright-launcher.js +12 -1
  270. package/dist/project/fs-port.d.ts +3 -0
  271. package/dist/project/fs-port.js +3 -2
  272. package/dist/project/memory-fs.js +6 -1
  273. package/dist/project/project-tools.js +6 -35
  274. package/dist/project/reticle-dir.d.ts +17 -2
  275. package/dist/project/reticle-dir.js +22 -4
  276. package/dist/runs/context-tools.d.ts +2 -0
  277. package/dist/runs/context-tools.js +89 -0
  278. package/dist/runs/run-context.d.ts +40 -0
  279. package/dist/runs/run-context.js +158 -0
  280. package/dist/runs/run-diff.d.ts +2 -1
  281. package/dist/runs/run-store.d.ts +10 -0
  282. package/dist/runs/run-store.js +10 -0
  283. package/dist/runs/verification-sync.d.ts +0 -11
  284. package/dist/runs/verification-sync.js +24 -3
  285. package/dist/runs/verify-server.d.ts +8 -1
  286. package/dist/runs/verify-server.js +14 -2
  287. package/dist/session/command-timeout.d.ts +5 -2
  288. package/dist/session/command-timeout.js +5 -4
  289. package/dist/session/dev-server-probe.d.ts +0 -8
  290. package/dist/session/dev-server-probe.js +1 -1
  291. package/dist/session/last-act.d.ts +7 -1
  292. package/dist/session/last-act.js +2 -2
  293. package/dist/session/lease-visibility.d.ts +67 -0
  294. package/dist/session/lease-visibility.js +76 -0
  295. package/dist/session/live-control-tools.js +1 -42
  296. package/dist/session/no-session-diagnosis.d.ts +33 -0
  297. package/dist/session/no-session-diagnosis.js +82 -24
  298. package/dist/session/no-session-next-action.d.ts +12 -0
  299. package/dist/session/no-session-next-action.js +77 -6
  300. package/dist/session/no-session-watch.d.ts +18 -6
  301. package/dist/session/no-session-watch.js +125 -75
  302. package/dist/session/output-budget.js +4 -1
  303. package/dist/session/pending-commands.d.ts +11 -1
  304. package/dist/session/pending-commands.js +12 -2
  305. package/dist/session/resolve-within.d.ts +34 -0
  306. package/dist/session/resolve-within.js +53 -0
  307. package/dist/session/session-health.d.ts +9 -0
  308. package/dist/session/session-health.js +23 -0
  309. package/dist/session/session-info.d.ts +18 -0
  310. package/dist/session/session-info.js +11 -0
  311. package/dist/session/session-manager.d.ts +14 -0
  312. package/dist/session/session-manager.js +111 -4
  313. package/dist/session/session-mirror.d.ts +16 -0
  314. package/dist/session/session-mirror.js +22 -0
  315. package/dist/session/session-recommendation.d.ts +19 -5
  316. package/dist/session/session-recommendation.js +25 -9
  317. package/dist/session/session-successor.d.ts +37 -0
  318. package/dist/session/session-successor.js +113 -0
  319. package/dist/session/session-tools.js +2 -2
  320. package/dist/session/session.d.ts +53 -3
  321. package/dist/session/session.js +162 -9
  322. package/dist/session/wall-clock.d.ts +12 -0
  323. package/dist/session/wall-clock.js +14 -0
  324. package/dist/setup/agent-configs.d.ts +96 -0
  325. package/dist/setup/agent-configs.js +333 -0
  326. package/dist/setup/agent-io.d.ts +9 -0
  327. package/dist/setup/agent-io.js +16 -0
  328. package/dist/setup/agent-writer.d.ts +51 -0
  329. package/dist/setup/agent-writer.js +115 -0
  330. package/dist/setup/approval-migration.d.ts +47 -0
  331. package/dist/setup/approval-migration.js +68 -0
  332. package/dist/setup/auto-approve.d.ts +85 -0
  333. package/dist/setup/auto-approve.js +150 -0
  334. package/dist/setup/bridge-port.d.ts +15 -0
  335. package/dist/setup/bridge-port.js +24 -0
  336. package/dist/setup/desktop-shape.d.ts +57 -0
  337. package/dist/setup/desktop-shape.js +63 -0
  338. package/dist/setup/dev-server-wait.d.ts +60 -0
  339. package/dist/setup/dev-server-wait.js +65 -0
  340. package/dist/setup/drive-agent.d.ts +71 -0
  341. package/dist/setup/drive-agent.js +171 -0
  342. package/dist/setup/drive-plan.d.ts +68 -0
  343. package/dist/setup/drive-plan.js +130 -0
  344. package/dist/setup/ensure-daemon.d.ts +29 -0
  345. package/dist/setup/ensure-daemon.js +58 -0
  346. package/dist/setup/init-runtime.d.ts +47 -0
  347. package/dist/setup/init-runtime.js +160 -0
  348. package/dist/setup/interrupt.d.ts +25 -0
  349. package/dist/setup/interrupt.js +40 -0
  350. package/dist/setup/license-key.d.ts +39 -0
  351. package/dist/setup/license-key.js +72 -0
  352. package/dist/setup/listeners.d.ts +37 -0
  353. package/dist/setup/listeners.js +85 -0
  354. package/dist/setup/node-effects.d.ts +60 -0
  355. package/dist/setup/node-effects.js +210 -0
  356. package/dist/setup/page-probe.d.ts +39 -0
  357. package/dist/setup/page-probe.js +49 -0
  358. package/dist/setup/relaunch.d.ts +36 -0
  359. package/dist/setup/relaunch.js +57 -0
  360. package/dist/setup/remaining-steps.d.ts +19 -0
  361. package/dist/setup/remaining-steps.js +31 -0
  362. package/dist/setup/run-setup.d.ts +99 -0
  363. package/dist/setup/run-setup.js +187 -0
  364. package/dist/setup/session-pick.d.ts +47 -0
  365. package/dist/setup/session-pick.js +51 -0
  366. package/dist/setup/setup-command.d.ts +45 -0
  367. package/dist/setup/setup-command.js +199 -0
  368. package/dist/setup/setup-options.d.ts +74 -0
  369. package/dist/setup/setup-options.js +67 -0
  370. package/dist/setup/transcripts.d.ts +16 -0
  371. package/dist/setup/transcripts.js +43 -0
  372. package/dist/status-payload.d.ts +14 -1
  373. package/dist/status-payload.js +23 -1
  374. package/dist/telemetry/automation-hint.d.ts +2 -1
  375. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  376. package/dist/telemetry/daemon-telemetry.js +52 -5
  377. package/dist/telemetry/error-class.js +1 -1
  378. package/dist/telemetry/feedback-context.d.ts +9 -1
  379. package/dist/telemetry/feedback-context.js +60 -29
  380. package/dist/telemetry/feedback-local.d.ts +15 -0
  381. package/dist/telemetry/feedback-local.js +56 -0
  382. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  383. package/dist/telemetry/feedback.d.ts +11 -0
  384. package/dist/telemetry/feedback.js +19 -6
  385. package/dist/telemetry/init-telemetry.d.ts +2 -0
  386. package/dist/telemetry/init-telemetry.js +2 -0
  387. package/dist/telemetry/install-source.d.ts +14 -0
  388. package/dist/telemetry/install-source.js +30 -0
  389. package/dist/telemetry/license-activation.d.ts +7 -1
  390. package/dist/telemetry/license-activation.js +9 -5
  391. package/dist/telemetry/mcp-connection.js +7 -0
  392. package/dist/telemetry/project-profile.d.ts +12 -9
  393. package/dist/telemetry/project-profile.js +12 -7
  394. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  395. package/dist/telemetry/proxy-telemetry.js +21 -0
  396. package/dist/telemetry/run-telemetry.d.ts +0 -2
  397. package/dist/telemetry/run-telemetry.js +1 -1
  398. package/dist/telemetry/session-metrics.d.ts +12 -0
  399. package/dist/telemetry/session-metrics.js +35 -1
  400. package/dist/telemetry/telemetry.js +17 -2
  401. package/dist/telemetry/tool-refused.d.ts +3 -1
  402. package/dist/telemetry/tool-refused.js +34 -2
  403. package/dist/telemetry/verification-of.js +2 -2
  404. package/dist/tools/act-capsule.d.ts +10 -0
  405. package/dist/tools/act-capsule.js +8 -2
  406. package/dist/tools/act-danger.d.ts +2 -1
  407. package/dist/tools/act-danger.js +11 -3
  408. package/dist/tools/act-observation.d.ts +41 -0
  409. package/dist/tools/act-observation.js +36 -0
  410. package/dist/tools/act-preflight.d.ts +20 -0
  411. package/dist/tools/act-preflight.js +98 -0
  412. package/dist/tools/act-sequence-retry.d.ts +45 -0
  413. package/dist/tools/act-sequence-retry.js +58 -0
  414. package/dist/tools/act-sequence-tool.d.ts +13 -0
  415. package/dist/tools/act-sequence-tool.js +135 -0
  416. package/dist/tools/act-target.d.ts +14 -0
  417. package/dist/tools/act-target.js +47 -0
  418. package/dist/tools/act-tools.d.ts +21 -1
  419. package/dist/tools/act-tools.js +196 -204
  420. package/dist/tools/assert-source.d.ts +22 -0
  421. package/dist/tools/assert-source.js +53 -0
  422. package/dist/tools/assert-verdict.d.ts +20 -3
  423. package/dist/tools/assert-verdict.js +58 -20
  424. package/dist/tools/browser-tools.js +6 -22
  425. package/dist/tools/complete-snapshot.d.ts +48 -0
  426. package/dist/tools/complete-snapshot.js +75 -0
  427. package/dist/tools/coverage-tools.d.ts +10 -1
  428. package/dist/tools/coverage-tools.js +123 -69
  429. package/dist/tools/dynamic-tools.js +53 -13
  430. package/dist/tools/error-recovery.d.ts +25 -14
  431. package/dist/tools/error-recovery.js +78 -9
  432. package/dist/tools/feedback-tools.d.ts +1 -1
  433. package/dist/tools/feedback-tools.js +2 -2
  434. package/dist/tools/invoke-tool.js +121 -11
  435. package/dist/tools/lease-hint.d.ts +10 -0
  436. package/dist/tools/lease-hint.js +53 -4
  437. package/dist/tools/lease-tools.d.ts +35 -5
  438. package/dist/tools/lease-tools.js +191 -13
  439. package/dist/tools/merged-name-redirect.d.ts +13 -0
  440. package/dist/tools/merged-name-redirect.js +22 -0
  441. package/dist/tools/navigate-arrival.d.ts +0 -2
  442. package/dist/tools/navigate-arrival.js +1 -1
  443. package/dist/tools/numeric-bounds.d.ts +100 -0
  444. package/dist/tools/numeric-bounds.js +125 -0
  445. package/dist/tools/observe-tools.js +109 -65
  446. package/dist/tools/read-tools.js +31 -8
  447. package/dist/tools/real-input-attempt.d.ts +32 -0
  448. package/dist/tools/real-input-attempt.js +212 -2
  449. package/dist/tools/reconcile-tools.js +22 -8
  450. package/dist/tools/resolve-target.d.ts +0 -7
  451. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  452. package/dist/tools/sdk-marker-probe.js +57 -0
  453. package/dist/tools/snapshot-delta.d.ts +1 -1
  454. package/dist/tools/tool-kit.d.ts +24 -0
  455. package/dist/tools/tool-kit.js +21 -0
  456. package/dist/tools/tool-names.d.ts +5 -0
  457. package/dist/tools/tool-names.js +5 -0
  458. package/dist/tools/tool-surface.d.ts +85 -6
  459. package/dist/tools/tool-surface.js +122 -2
  460. package/dist/tools/tools-helpers.d.ts +19 -0
  461. package/dist/tools/tools-helpers.js +46 -0
  462. package/dist/tools/tools.d.ts +2 -0
  463. package/dist/tools/tools.js +78 -12
  464. package/dist/tools/unadvertised-help.js +4 -4
  465. package/dist/tools/verify-next-baton.d.ts +53 -0
  466. package/dist/tools/verify-next-baton.js +52 -0
  467. package/dist/update/reticle-deps.d.ts +9 -0
  468. package/dist/update/reticle-deps.js +27 -0
  469. package/dist/update/sdk-sync.d.ts +1 -2
  470. package/dist/update/sdk-sync.js +1 -21
  471. package/dist/update/update-nudge.d.ts +25 -10
  472. package/dist/update/update-nudge.js +32 -3
  473. package/dist/update/updater.d.ts +1 -1
  474. package/dist/version/peer-announce.js +4 -2
  475. package/dist/version/sdk-fix.d.ts +47 -0
  476. package/dist/version/sdk-fix.js +127 -0
  477. package/dist/version/version-nudge.d.ts +14 -0
  478. package/dist/version/version-nudge.js +21 -0
  479. package/dist/version/version-skew.d.ts +38 -3
  480. package/dist/version/version-skew.js +83 -7
  481. package/dist/visual/visual-store.d.ts +6 -6
  482. package/dist/visual/visual-store.js +18 -16
  483. package/dist/visual/visual-tools.js +57 -12
  484. package/docs/README.md +2 -1
  485. package/docs/cli/cloud.mdx +1 -1
  486. package/docs/cli/doctor.mdx +2 -0
  487. package/docs/cli/serve.mdx +4 -2
  488. package/docs/cli/verify.mdx +4 -5
  489. package/docs/cli.mdx +7 -3
  490. package/docs/desktop.mdx +1 -1
  491. package/docs/docs.json +2 -2
  492. package/docs/gates.md +1 -1
  493. package/docs/getting-started.md +19 -3
  494. package/docs/index.mdx +9 -6
  495. package/docs/install-agentic.mdx +38 -12
  496. package/docs/install-manual.mdx +1 -1
  497. package/docs/packages/browser.mdx +2 -2
  498. package/docs/packages/server.mdx +1 -1
  499. package/docs/packages.mdx +8 -6
  500. package/docs/platform-integration.md +17 -0
  501. package/docs/predicates.mdx +8 -0
  502. package/docs/reticle-manual.md +106 -0
  503. package/docs/skill-file.mdx +3 -3
  504. package/docs/telemetry-contract.md +108 -4
  505. package/docs/tools-act-sequence.mdx +16 -0
  506. package/docs/tools-tools-and-run.mdx +2 -4
  507. package/docs/troubleshooting.mdx +9 -0
  508. package/docs/usage.md +4 -3
  509. package/package.json +4 -3
  510. package/dist/init/cursor.d.ts +0 -24
  511. package/dist/init/cursor.js +0 -80
  512. package/dist/session/session-readiness.d.ts +0 -34
  513. package/dist/session/session-readiness.js +0 -36
@@ -1,13 +1,17 @@
1
- import { type ImpactCounts, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
1
+ import { type ImpactCounts, type ImpactDefect, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
2
2
  export interface ImpactPaths {
3
3
  project: string;
4
4
  global: string;
5
5
  }
6
6
  /**
7
- * `reticleRoot` is the project's own `.reticle` directory (what every other store here is handed);
8
- * the global scope always lives beside the daemon's own state in `~/.reticle`.
7
+ * Where this project's dashboard lives, if `reticle link` recorded one.
8
+ *
9
+ * Read from the link file rather than derived from the API url, because the API origin and the
10
+ * console origin are different hosts in every deployment that is not a laptop. Synchronous and
11
+ * best-effort, like every other read in this file: a missing or malformed link file means no link
12
+ * in the HUD, never a failed tool call.
9
13
  */
10
- export declare function impactPaths(reticleRoot: string): ImpactPaths;
14
+ export declare function readDashboardUrl(reticleRoot: string): string | undefined;
11
15
  /** Read a scope, tolerating absence and corruption: a broken file starts over rather than throwing. */
12
16
  export declare function readScope(path: string, now: number): ImpactScope;
13
17
  /** YYYY-MM-DD in local time - the day boundary a person recognises, not UTC's. */
@@ -22,6 +26,11 @@ export declare function isoDay(now: number): string;
22
26
  */
23
27
  export interface ImpactFoldMeta {
24
28
  runMs?: number;
29
+ /**
30
+ * The defect this call caught, when it caught one. Carried beside the counters rather than in
31
+ * them because it is not a tally: `counts.failed` says how many, this says which.
32
+ */
33
+ defect?: ImpactDefect;
25
34
  }
26
35
  /** Fold one delta into a scope: totals, today's bucket, records and streak. Pure. */
27
36
  export declare function applyDelta(scope: ImpactScope, delta: Partial<ImpactCounts>, now: number, meta?: ImpactFoldMeta): ImpactScope;
@@ -1,7 +1,7 @@
1
1
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
- import { ReticleDir, IMPACT_DAILY_BUCKETS, IMPACT_SCHEMA_VERSION, ImpactScopeSchema, addImpactCounts, emptyImpactCounts, emptyImpactRecords, estimateImpactSavings, } from '@reticlehq/core';
4
+ import { ReticleDir, IMPACT_DAILY_BUCKETS, IMPACT_DEFECT_LIMIT, IMPACT_SCHEMA_VERSION, ImpactScopeSchema, addImpactCounts, emptyImpactCounts, emptyImpactRecords, estimateImpactSavings, } from '@reticlehq/core';
5
5
  /**
6
6
  * Where Reticle keeps the record of what it has done for you.
7
7
  *
@@ -14,16 +14,38 @@ import { ReticleDir, IMPACT_DAILY_BUCKETS, IMPACT_SCHEMA_VERSION, ImpactScopeSch
14
14
  */
15
15
  /** Writes are debounced: a verification loop is 50-200 calls, and each one is a counter bump. */
16
16
  const WRITE_DEBOUNCE_MS = 800;
17
+ /** The project's cloud binding, written by `reticle link`. Absent for an unlinked project. */
18
+ const CLOUD_LINK_FILE = 'cloud.json';
17
19
  /**
18
20
  * `reticleRoot` is the project's own `.reticle` directory (what every other store here is handed);
19
21
  * the global scope always lives beside the daemon's own state in `~/.reticle`.
20
22
  */
21
- export function impactPaths(reticleRoot) {
23
+ function impactPaths(reticleRoot) {
22
24
  return {
23
25
  project: join(reticleRoot, ReticleDir.IMPACT_FILE),
24
26
  global: join(homedir(), ReticleDir.ROOT, ReticleDir.IMPACT_FILE),
25
27
  };
26
28
  }
29
+ /**
30
+ * Where this project's dashboard lives, if `reticle link` recorded one.
31
+ *
32
+ * Read from the link file rather than derived from the API url, because the API origin and the
33
+ * console origin are different hosts in every deployment that is not a laptop. Synchronous and
34
+ * best-effort, like every other read in this file: a missing or malformed link file means no link
35
+ * in the HUD, never a failed tool call.
36
+ */
37
+ export function readDashboardUrl(reticleRoot) {
38
+ try {
39
+ const raw = JSON.parse(readFileSync(join(reticleRoot, CLOUD_LINK_FILE), 'utf8'));
40
+ if ('object' !== typeof raw || null === raw)
41
+ return undefined;
42
+ const value = raw['dashboardUrl'];
43
+ return 'string' === typeof value && value.length > 0 ? value : undefined;
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
27
49
  function emptyScope(now) {
28
50
  const counts = emptyImpactCounts();
29
51
  return {
@@ -32,6 +54,7 @@ function emptyScope(now) {
32
54
  records: emptyImpactRecords(),
33
55
  savings: estimateImpactSavings(counts),
34
56
  since: now,
57
+ defects: [],
35
58
  };
36
59
  }
37
60
  /** Read a scope, tolerating absence and corruption: a broken file starts over rather than throwing. */
@@ -105,7 +128,21 @@ export function applyDelta(scope, delta, now, meta = {}) {
105
128
  }
106
129
  records.bestStreakDays = Math.max(records.bestStreakDays, records.streakDays);
107
130
  records.longestRunMs = Math.max(records.longestRunMs, meta.runMs ?? 0);
108
- return { counts, days, records, savings: estimateImpactSavings(counts), since: scope.since };
131
+ /*
132
+ * Newest first, capped. Prepending is what makes the HUD's short list the CURRENT breakage rather
133
+ * than the first ten things that ever broke — which, after a week, is a list about the past.
134
+ */
135
+ const defects = meta.defect === undefined
136
+ ? scope.defects
137
+ : [meta.defect, ...scope.defects].slice(0, IMPACT_DEFECT_LIMIT);
138
+ return {
139
+ counts,
140
+ days,
141
+ records,
142
+ savings: estimateImpactSavings(counts),
143
+ since: scope.since,
144
+ defects,
145
+ };
109
146
  }
110
147
  /**
111
148
  * The live impact record for one project.
@@ -117,6 +154,7 @@ export class ImpactStore {
117
154
  #paths;
118
155
  #now;
119
156
  #projectName;
157
+ #dashboardUrl;
120
158
  #project;
121
159
  #global;
122
160
  #timer;
@@ -125,6 +163,7 @@ export class ImpactStore {
125
163
  this.#paths = impactPaths(opts.reticleRoot);
126
164
  this.#now = opts.now ?? (() => Date.now());
127
165
  this.#projectName = opts.projectName;
166
+ this.#dashboardUrl = readDashboardUrl(opts.reticleRoot);
128
167
  const now = this.#now();
129
168
  this.#project = readScope(this.#paths.project, now);
130
169
  this.#global = readScope(this.#paths.global, now);
@@ -148,6 +187,8 @@ export class ImpactStore {
148
187
  };
149
188
  if (this.#projectName !== undefined)
150
189
  snap.projectName = this.#projectName;
190
+ if (this.#dashboardUrl !== undefined)
191
+ snap.dashboardUrl = this.#dashboardUrl;
151
192
  return snap;
152
193
  }
153
194
  /** Write now (process exit, tests). */
package/dist/index.d.ts CHANGED
@@ -117,7 +117,10 @@ export interface StartOptions {
117
117
  pairingTokenDir?: string;
118
118
  /** injectable clock for contract.json's generatedAt stamp. Defaults to Date.now. */
119
119
  now?: () => number;
120
- /** 'core' exposes the lean tool surface. Defaults to env RETICLE_TOOL_PROFILE, else 'full'. */
120
+ /**
121
+ * Retired profile name (`core`/`full`/…). Old values still map. The live switch is
122
+ * RETICLE_ADVERTISE_ALL_TOOLS=1; the default is the lean surface.
123
+ */
121
124
  toolProfile?: string;
122
125
  /** Start the OEM/CI verify HTTP endpoint alongside the daemon (`reticle serve --http`). */
123
126
  httpVerify?: boolean;
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { join } from 'node:path';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
- import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry } from '@reticlehq/core';
4
+ import { resolveProjectCloud } from './cloud/cloud-config.js';
5
+ import { startSyncDaemon } from './cloud/sync-daemon.js';
6
+ import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, projectCandidates, } from '@reticlehq/core';
5
7
  import { discoverProjectConfigs } from './cli/config-discovery.js';
6
8
  import { projectCandidatesFrom, resolveArtifactRoot, } from './project/artifact-root.js';
7
9
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -14,6 +16,8 @@ import { openLoopbackAlias } from './daemon/loopback-alias.js';
14
16
  import { reportAppInstrumented } from './telemetry/app-instrumented.js';
15
17
  import { resolveBridgeSecurityWithAutoToken } from './bridge/bridge-security.js';
16
18
  import { Bridge } from './bridge/bridge.js';
19
+ import { sdkFixForDirectory } from './version/sdk-fix.js';
20
+ import { SERVER_VERSION } from './version/server-version.js';
17
21
  import { BaselineStore } from './project/baselines.js';
18
22
  import { RecordingStore } from './flows/recordings.js';
19
23
  import { initImpact } from './impact/impact-recorder.js';
@@ -39,6 +43,7 @@ import { statusPayload } from './status-payload.js';
39
43
  import { CdpRealInputProvider, LaunchedRealInputProvider } from './input/real-input.js';
40
44
  import { cpus } from 'node:os';
41
45
  import { BrowserPool } from './pool/browser-pool.js';
46
+ import { AGENT_ALREADY_DRIVING_ELSEWHERE, shouldGreetWithLeaseNotice, } from './session/lease-visibility.js';
42
47
  import { playwrightLauncher, resolveMaxContexts } from './pool/playwright-launcher.js';
43
48
  import { LeaseReaper } from './pool/lease-reaper.js';
44
49
  import { readJournalEnabled, readProjectId } from './cli/cli-port.js';
@@ -187,7 +192,14 @@ function makeNetworkDetailRouter(bridge, driveUrl) {
187
192
  function attachJournal(bridge, deps) {
188
193
  const journalAttach = makeJournalAttach(deps);
189
194
  const ambientStore = new AmbientStore(deps.fs, deps.reticleRoot);
195
+ // Built once, not per session: the resolver walks config discovery and the user-level registry,
196
+ // and neither changes between two tabs connecting a second apart.
197
+ const resolveArtifactRoot = artifactRootResolver(deps.reticleRoot);
190
198
  bridge.attachSessionCreate((session) => {
199
+ // Stamp the project's own `.reticle` before ANY counter fires for this session. Without it every
200
+ // verdict is recorded against wherever the daemon was started, which is how one app's evidence
201
+ // reached a different account's production dashboard.
202
+ session.artifactRoot = resolveArtifactRoot(session.projectId).root;
191
203
  journalAttach(session);
192
204
  // Seed the learned ambient map so a fresh session starts knowing which regions churn, instead of
193
205
  // re-learning from zero. Best-effort + async: a late seed still helps, a failure is silent.
@@ -256,6 +268,14 @@ async function resolveRealInput(options, onNavigateError, onNetworkDetail) {
256
268
  return {};
257
269
  }
258
270
  /** Start the Reticle bridge (browser WS endpoint) and, by default, the MCP stdio server. */
271
+ /**
272
+ * The SDK-upgrade sentence for THIS project's package.json, evaluated at each HELLO so a
273
+ * just-edited manifest is what we name. Falls back to the framework-neutral sensor when cwd is
274
+ * not an app.
275
+ */
276
+ function sdkFixForCwd() {
277
+ return sdkFixForDirectory(SERVER_VERSION, process.cwd());
278
+ }
259
279
  /**
260
280
  * Resolve a session's artifact root, from everything this machine knows about where projects live.
261
281
  *
@@ -264,6 +284,39 @@ async function resolveRealInput(options, onNavigateError, onNetworkDetail) {
264
284
  * this daemon is up, and a resolution that used a startup snapshot would keep answering with a map
265
285
  * from before the project the agent is now driving existed.
266
286
  */
287
+ /**
288
+ * Every `.reticle` root on this machine that sync should consider, from the same two sources the
289
+ * artifact resolver uses.
290
+ *
291
+ * Read on every call rather than snapshotted, for the same reason: `reticle link` can run in
292
+ * another terminal while this daemon is up, and a startup snapshot would keep that repo silent
293
+ * until somebody restarted a process they have no reason to suspect.
294
+ *
295
+ * Returns directories that MIGHT be linked; the caller resolves each and skips the ones that are
296
+ * not. Deciding that here would mean reading every cloud.json on every tick.
297
+ */
298
+ function knownProjectRoots() {
299
+ const roots = new Set();
300
+ try {
301
+ const path = join(homedir(), ReticleDir.ROOT, PROJECT_REGISTRY_FILE);
302
+ if (existsSync(path)) {
303
+ const registry = parseProjectRegistry(JSON.parse(readFileSync(path, 'utf8')));
304
+ for (const candidate of projectCandidates(registry))
305
+ roots.add(join(candidate.directory, ReticleDir.ROOT));
306
+ }
307
+ }
308
+ catch {
309
+ // A registry that cannot be read is an empty one — never a reason to stop syncing.
310
+ }
311
+ try {
312
+ for (const config of discoverProjectConfigs(process.cwd()).found)
313
+ roots.add(join(config.directory, ReticleDir.ROOT));
314
+ }
315
+ catch {
316
+ // Same: a diagnostic walk that throws must not take the sync loop with it.
317
+ }
318
+ return [...roots];
319
+ }
267
320
  function artifactRootResolver(daemonRoot) {
268
321
  return (projectId) => {
269
322
  let registry = emptyProjectRegistry();
@@ -298,7 +351,7 @@ export async function start(options = {}) {
298
351
  // "nothing recorded yet" over a month of history on disk is the worst version of this feature.
299
352
  initImpact({ reticleRoot: options.reticleRoot ?? join(process.cwd(), ReticleDir.ROOT) });
300
353
  const security = await resolveBridgeSecurityWithAutoToken(options);
301
- const bridge = new Bridge({ port, ...security });
354
+ const bridge = new Bridge({ port, sdkFix: sdkFixForCwd, ...security });
302
355
  // Server-authoritative liveness: a Node-side reaper (immune to browser throttling) ends sessions
303
356
  // whose agent has gone idle, so a forgotten/crashed agent never leaves the HUD "running" forever.
304
357
  const reaper = new SessionReaper(bridge.sessions);
@@ -332,6 +385,25 @@ export async function start(options = {}) {
332
385
  pool = createBrowserPool(options.headless ?? true);
333
386
  leaseReaper = new LeaseReaper(pool);
334
387
  leaseReaper.start();
388
+ /*
389
+ * Tell a tab that ARRIVES during a lease why its HUD is silent.
390
+ *
391
+ * The acquire-time notice only reaches tabs already connected, so opening the app — or merely
392
+ * reloading it — while an agent held a lease landed somebody on a dark HUD with nothing
393
+ * explaining it. That is the more common way to hit it, because a person opens the dashboard
394
+ * precisely when they want to watch. Reported as "dashboard is being watched, but the agent chat
395
+ * shows nothing".
396
+ *
397
+ * Registered separately from the journal hook rather than folded into it: this one needs the
398
+ * pool, which does not exist on the no-drive path, and session-create handlers are additive.
399
+ */
400
+ const leasePool = pool;
401
+ bridge.attachSessionCreate((session) => {
402
+ const leasedIds = leasePool.leasedSessionIds();
403
+ const leasedProjects = leasedIds.map((id) => bridge.sessions.get(id)?.projectId);
404
+ if (shouldGreetWithLeaseNotice(session, leasedIds, leasedProjects))
405
+ session.pushNarration(AGENT_ALREADY_DRIVING_ELSEWHERE);
406
+ });
335
407
  const deps = {
336
408
  sessions: bridge.sessions,
337
409
  pool,
@@ -391,14 +463,19 @@ export async function startDaemon(options = {}) {
391
463
  initImpact({ reticleRoot: options.reticleRoot ?? join(process.cwd(), ReticleDir.ROOT) });
392
464
  const security = await resolveBridgeSecurityWithAutoToken(options);
393
465
  const shared = createSharedServer(security.token === undefined ? {} : { token: security.token });
394
- const bridge = new Bridge({ port, server: shared.httpServer, ...security });
466
+ const bridge = new Bridge({ port, server: shared.httpServer, sdkFix: sdkFixForCwd, ...security });
395
467
  // The daemon owns listen (below), so the real bind error is reported there; absorb bridge.ready's
396
468
  // mirror rejection so a port collision can't surface as an unhandled promise rejection.
397
469
  void bridge.ready.catch(() => undefined);
470
+ // Declared before attachStatus so the closure below can report it; assigned further down, before
471
+ // listen — the first status request cannot arrive until after the bind.
472
+ let verifyHttp;
398
473
  // `reticle status` GETs this for a live, at-a-glance view of connected tabs + their health.
399
474
  // The same diagnosis agents get on an empty `reticle_sessions`, so `reticle status` — the
400
475
  // most-run command in the field — stops answering "sessionCount: 0" and nothing else.
401
- shared.attachStatus(() => statusPayload(bridge.sessions.count(), bridge.sessions.list(), bridge.sessions.noSessionHint()));
476
+ // `verifyPort` rides along so a later `serve --http` can tell whether this daemon already honours
477
+ // the requested `--http-port` instead of silently ignoring the flag (#687).
478
+ shared.attachStatus(() => statusPayload(bridge.sessions.count(), bridge.sessions.list(), bridge.sessions.noSessionHint(), verifyHttp?.port));
402
479
  // Agent-independent presence: the daemon outlives any single agent, so when the LAST agent's MCP
403
480
  // connection drops (it stopped, or is waiting on the human), end every session and push a clear
404
481
  // "go to your terminal" notice to the panel — the human is on the browser and must not lose a typed
@@ -426,6 +503,23 @@ export async function startDaemon(options = {}) {
426
503
  const pool = createBrowserPool(options.headless ?? true);
427
504
  const leaseReaper = new LeaseReaper(pool);
428
505
  leaseReaper.start();
506
+ /*
507
+ * Automatic cloud sync, for the session nobody thinks about.
508
+ *
509
+ * The daemon outlives every individual tool call and is already running for the whole session, so
510
+ * it is the only place "keep the dashboard current while work is happening" costs nobody an extra
511
+ * process to remember. Safe for an UNLINKED project: it resolves the link on every tick and does
512
+ * nothing until one exists, so `reticle link` takes effect without a restart.
513
+ */
514
+ const cloudSync = startSyncDaemon({
515
+ reticleRoot,
516
+ cloud: () => resolveProjectCloud(fs, reticleRoot, homedir(), process.env),
517
+ // Every OTHER linked repo on this machine, not just the directory the daemon was started in.
518
+ // One daemon serves many projects, and pushing only its own root left the rest silently
519
+ // reporting nothing — indistinguishable, on the dashboard, from nobody having verified anything.
520
+ otherRoots: () => Promise.resolve(knownProjectRoots()),
521
+ cloudFor: (root) => resolveProjectCloud(fs, root, homedir(), process.env),
522
+ });
429
523
  // Scope auto-selection to the active project (from .reticle.json) so a stray tab from another app is
430
524
  // never picked when the agent omits a sessionId. Explicit per-call scope/sessionId still overrides.
431
525
  // Scope + the no-session diagnosis: "no browser session connected" is the error that ends most
@@ -433,7 +527,7 @@ export async function startDaemon(options = {}) {
433
527
  // Ordered AFTER the pool: the watch may open a browser itself the moment it finds a listening dev
434
528
  // server for a wired project (see no-session-watch.ts), and it opens it through the pool — the same
435
529
  // path reticle_lease takes, in this process, binding nothing.
436
- wireSessionScope(bridge.sessions, readProjectId(process.cwd()), port, () => pool.reapedLeaseCount(), (url) => pool.acquire(url));
530
+ wireSessionScope(bridge.sessions, readProjectId(process.cwd()), port, (sessionId) => pool.wasReapedLease(sessionId), (url) => pool.acquire(url));
437
531
  const deps = {
438
532
  sessions: bridge.sessions,
439
533
  pool,
@@ -444,9 +538,18 @@ export async function startDaemon(options = {}) {
444
538
  project,
445
539
  fs,
446
540
  reticleRoot,
541
+ // The long-lived daemon needs this MORE than the standalone MCP process does, not less: it is
542
+ // the one that outlives a single project and serves every app on the machine. Omitting it here
543
+ // silently disabled per-session artifact resolution for every agent that attaches to a running
544
+ // daemon — the common case — so flows were listed, loaded and healed against wherever the
545
+ // daemon happened to be launched. Wired in only one of the two places, a resolver is a resolver
546
+ // that does not run.
547
+ artifactRootFor: artifactRootResolver(reticleRoot),
447
548
  now,
448
549
  bridgePort: port,
449
550
  browserProbe: probeChromium,
551
+ // A finished verification should not sit behind a one-minute timer — see ToolDeps.onRunPersisted.
552
+ onRunPersisted: () => cloudSync.nudge(),
450
553
  };
451
554
  const profile = resolveToolSurface(options.toolProfile);
452
555
  const effectiveDeps = realInput !== undefined ? { ...deps, realInput } : deps;
@@ -461,9 +564,11 @@ export async function startDaemon(options = {}) {
461
564
  // Optional OEM/CI verify endpoint: a host platform POSTs to /verify and gets an ReticleVerificationRun,
462
565
  // driving the same flow-replay machinery the agent uses — no MCP stdio, no human. Each verdict is
463
566
  // persisted via RunStore. Localhost-bound + token-guarded. Off unless `reticle serve --http`.
464
- let verifyHttp;
567
+ // (`verifyHttp` itself is declared above attachStatus, which reports its port.)
465
568
  if (true === options.httpVerify) {
466
- const runStore = new RunStore(fs, reticleRoot);
569
+ // Wakes cloud sync when the HTTP verify server persists a run — that path does not push
570
+ // inline the way the MCP one does, so without this its runs waited for the timer.
571
+ const runStore = new RunStore(fs, reticleRoot, { onWrote: () => cloudSync.nudge() });
467
572
  const runner = new ReticleRunner(createRunnerPort(effectiveDeps));
468
573
  const token = options.httpVerifyToken ?? process.env[ReticleEnv.VERIFY_TOKEN] ?? '';
469
574
  verifyHttp = await startVerifyServer({ runner, token, persist: (run) => runStore.write(run) }, options.httpVerifyPort ?? RETICLE_VERIFY_DEFAULT_PORT);
@@ -552,6 +657,7 @@ export async function startDaemon(options = {}) {
552
657
  if (vh !== undefined)
553
658
  await new Promise((resolve) => vh.server.close(() => resolve()));
554
659
  leaseReaper.stop();
660
+ cloudSync.stop();
555
661
  await loopbackAlias.close?.();
556
662
  await pool.shutdown();
557
663
  await owned?.dispose();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `reticle init` writes a behavioral rule into the coding agent's instruction file so the agent
3
3
  * VERIFIES a feature with Reticle after building it — not only when the user remembers to say "test
4
- * it with reticle". Registering the MCP server (mcp.ts / cursor.ts) only makes the tools AVAILABLE;
4
+ * it with reticle". Registering the MCP server (mcp.ts / mcp-clients.ts) only makes the tools AVAILABLE;
5
5
  * this is the piece that makes the agent reach for them, every session, unprompted.
6
6
  *
7
7
  * It carries a SECOND standing instruction, and that one is not about this project at all: send
@@ -15,7 +15,7 @@
15
15
  * written into whichever instruction file the detected agent reads: `CLAUDE.md` (Claude Code),
16
16
  * `.cursor/rules/reticle.mdc` (Cursor, `alwaysApply` so it stays in context), or `AGENTS.md` as the
17
17
  * cross-agent fallback. Idempotent via a marker block — safe to append to an existing file and a
18
- * re-run is a no-op. Mirrors the apply/already result shape used across init (see cursor.ts).
18
+ * re-run is a no-op. Mirrors the apply/already result shape used across init (see mcp-clients.ts).
19
19
  */
20
20
  /** Project-root instruction files the agent re-reads every session. */
21
21
  export declare const CLAUDE_MD_PATH = "CLAUDE.md";
@@ -36,6 +36,15 @@ export declare const AGENTS_MD_PATH = "AGENTS.md";
36
36
  export declare const RETICLE_MD_PATH = "RETICLE.md";
37
37
  /** Cursor project rule; the frontmatter `alwaysApply` keeps it in context on every turn. */
38
38
  export declare const CURSOR_RULE_PATH = ".cursor/rules/reticle.mdc";
39
+ /**
40
+ * Does this file already carry the managed block?
41
+ *
42
+ * Exported so callers cannot invent their own looser test. The begin marker carries explanatory
43
+ * text inside the comment, so a substring check for `reticle:begin` matches files that
44
+ * `mergeMarkedInstruction` would treat as having NO block — and the two disagreeing means a refresh
45
+ * silently appends a second copy instead of updating the first.
46
+ */
47
+ export declare function hasManagedBlock(text: string): boolean;
39
48
  /**
40
49
  * How the agent must INVOKE the CLI.
41
50
  *
@@ -60,13 +69,28 @@ export declare const CLI: string;
60
69
  * inside a host that asks the human for permission — so a dev server it starts is visible in the
61
70
  * transcript, attributable, and stoppable.
62
71
  *
63
- * The guards are the whole reason this is safe, so no renderer may drop them: adopt what is already
64
- * listening, read the command rather than invent one, never kill, background it and SAY so, and leave
65
- * the permission prompt to the host.
72
+ * The guards are the whole reason this is safe, so no renderer may drop them: one server at a time,
73
+ * read the command rather than invent one, never kill anything you did not start, and leave the
74
+ * permission prompt to the host. "Background it and say so" is no longer among them because the
75
+ * branch above now states it at the point of action, and this text is read every turn — a rule
76
+ * stated twice costs every agent tokens forever and is the first thing to drift.
77
+ *
78
+ * The RESTART branch outranks the rest and is stated first. It used to be an aside, while "if a dev
79
+ * server is already listening, use it" was a numbered guard — so an agent resolving the two
80
+ * reasonably reused a server started before `init` edited the build config, served a bundle with no
81
+ * SDK in it, and landed on an empty session list with every other step done correctly.
66
82
  *
67
83
  * Exported because slash-command.ts renders the same rule; there must not be two versions of it.
68
84
  */
69
- export declare const DEV_SERVER_POLICY = "**If no dev server is listening, start one yourself.** Read the project's own dev script out of `package.json` (`dev`, `start`, whatever this project calls it), run it in the BACKGROUND, tell the user in one line that it is running and how to stop it, then carry on. Stopping to ask is how a verification turn ends with nothing verified.\n\nFive guards, none optional:\n\n1. **Never start a second one.** If something is already listening on the app's port, use it.\n2. **Never guess the command.** It comes from `package.json` scripts. If there is no recognisable dev script, say so and stop rather than inventing one.\n3. **Never kill anything.** Not a dev server, not a daemon, not a port holder \u2014 including one you started.\n4. **Background it, and say so.** The user must know a server is running and how to stop it. A dev server the human does not know about is the same failure one step later.\n5. **The permission prompt belongs to your host.** Never try to bypass, suppress or auto-approve it, and take a refusal as the answer.";
85
+ export declare const DEV_SERVER_POLICY = "**A dev server already running when `reticle init` ran does not have Reticle in its bundle.** It read the build config at boot; `init` edited it afterwards. It serves the old bundle and no session appears. In order:\n\n1. **A dev server was already running?** Restart it, then hard-reload the tab. \"Something is listening\" does not mean the right bundle is served.\n2. **Nothing was running?** Start it in the BACKGROUND and say so in one line. `reticle_sessions` gives you this project's own dev command in `next_action`; use that, never compose one. Started after `init`, it needs no restart.\n\nStopping to ask is how a verification turn ends with nothing verified.\n\nFour guards, none optional:\n\n1. **Never run two at once.** One dev server on the app's port. Restarting a stale one means stopping it first, not starting a second alongside it.\n2. **Never guess the command.** It comes from `package.json` scripts. No recognisable dev script means say so and stop, not invent one.\n3. **Never kill anything you did not start**, and never a daemon or a port holder. The one exception is the restart above, and say in one line that you did it.\n4. **The permission prompt belongs to your host.** Never bypass, suppress or auto-approve it, and take a refusal as the answer.";
86
+ /**
87
+ * The operative rules, carried by every file that loads on every turn.
88
+ *
89
+ * Short on purpose. This is the text that has to CHANGE BEHAVIOUR, so it states when to verify, when
90
+ * NOT to, how, and the guards against calling something proven when it is not. Everything an agent
91
+ * needs only at the moment it hits a specific situation lives in RETICLE.md, named at the end.
92
+ */
93
+ export declare const RULE_BODY: string;
70
94
  /** The full-rules file, written verbatim (no markers: `init` owns this file entirely). */
71
95
  export declare function reticleMdFile(): string;
72
96
  export declare const AgentRuleStatus: {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `reticle init` writes a behavioral rule into the coding agent's instruction file so the agent
3
3
  * VERIFIES a feature with Reticle after building it — not only when the user remembers to say "test
4
- * it with reticle". Registering the MCP server (mcp.ts / cursor.ts) only makes the tools AVAILABLE;
4
+ * it with reticle". Registering the MCP server (mcp.ts / mcp-clients.ts) only makes the tools AVAILABLE;
5
5
  * this is the piece that makes the agent reach for them, every session, unprompted.
6
6
  *
7
7
  * It carries a SECOND standing instruction, and that one is not about this project at all: send
@@ -15,7 +15,7 @@
15
15
  * written into whichever instruction file the detected agent reads: `CLAUDE.md` (Claude Code),
16
16
  * `.cursor/rules/reticle.mdc` (Cursor, `alwaysApply` so it stays in context), or `AGENTS.md` as the
17
17
  * cross-agent fallback. Idempotent via a marker block — safe to append to an existing file and a
18
- * re-run is a no-op. Mirrors the apply/already result shape used across init (see cursor.ts).
18
+ * re-run is a no-op. Mirrors the apply/already result shape used across init (see mcp-clients.ts).
19
19
  */
20
20
  import { NPX } from './mcp.js';
21
21
  import { RETICLE_NPM_PACKAGE } from '../version/server-version.js';
@@ -41,6 +41,17 @@ export const CURSOR_RULE_PATH = '.cursor/rules/reticle.mdc';
41
41
  /** Markers delimiting the managed block so a re-run is idempotent and the block stays updatable. */
42
42
  const RULE_BEGIN = '<!-- reticle:begin (managed by `reticle init` — edit outside these markers) -->';
43
43
  const RULE_END = '<!-- reticle:end -->';
44
+ /**
45
+ * Does this file already carry the managed block?
46
+ *
47
+ * Exported so callers cannot invent their own looser test. The begin marker carries explanatory
48
+ * text inside the comment, so a substring check for `reticle:begin` matches files that
49
+ * `mergeMarkedInstruction` would treat as having NO block — and the two disagreeing means a refresh
50
+ * silently appends a second copy instead of updating the first.
51
+ */
52
+ export function hasManagedBlock(text) {
53
+ return text.includes(RULE_BEGIN);
54
+ }
44
55
  /**
45
56
  * How the agent must INVOKE the CLI.
46
57
  *
@@ -65,21 +76,32 @@ export const CLI = `${NPX} ${RETICLE_NPM_PACKAGE}`;
65
76
  * inside a host that asks the human for permission — so a dev server it starts is visible in the
66
77
  * transcript, attributable, and stoppable.
67
78
  *
68
- * The guards are the whole reason this is safe, so no renderer may drop them: adopt what is already
69
- * listening, read the command rather than invent one, never kill, background it and SAY so, and leave
70
- * the permission prompt to the host.
79
+ * The guards are the whole reason this is safe, so no renderer may drop them: one server at a time,
80
+ * read the command rather than invent one, never kill anything you did not start, and leave the
81
+ * permission prompt to the host. "Background it and say so" is no longer among them because the
82
+ * branch above now states it at the point of action, and this text is read every turn — a rule
83
+ * stated twice costs every agent tokens forever and is the first thing to drift.
84
+ *
85
+ * The RESTART branch outranks the rest and is stated first. It used to be an aside, while "if a dev
86
+ * server is already listening, use it" was a numbered guard — so an agent resolving the two
87
+ * reasonably reused a server started before `init` edited the build config, served a bundle with no
88
+ * SDK in it, and landed on an empty session list with every other step done correctly.
71
89
  *
72
90
  * Exported because slash-command.ts renders the same rule; there must not be two versions of it.
73
91
  */
74
- export const DEV_SERVER_POLICY = `**If no dev server is listening, start one yourself.** Read the project's own dev script out of \`package.json\` (\`dev\`, \`start\`, whatever this project calls it), run it in the BACKGROUND, tell the user in one line that it is running and how to stop it, then carry on. Stopping to ask is how a verification turn ends with nothing verified.
92
+ export const DEV_SERVER_POLICY = `**A dev server already running when \`reticle init\` ran does not have Reticle in its bundle.** It read the build config at boot; \`init\` edited it afterwards. It serves the old bundle and no session appears. In order:
93
+
94
+ 1. **A dev server was already running?** Restart it, then hard-reload the tab. "Something is listening" does not mean the right bundle is served.
95
+ 2. **Nothing was running?** Start it in the BACKGROUND and say so in one line. \`reticle_sessions\` gives you this project's own dev command in \`next_action\`; use that, never compose one. Started after \`init\`, it needs no restart.
75
96
 
76
- Five guards, none optional:
97
+ Stopping to ask is how a verification turn ends with nothing verified.
77
98
 
78
- 1. **Never start a second one.** If something is already listening on the app's port, use it.
79
- 2. **Never guess the command.** It comes from \`package.json\` scripts. If there is no recognisable dev script, say so and stop rather than inventing one.
80
- 3. **Never kill anything.** Not a dev server, not a daemon, not a port holder — including one you started.
81
- 4. **Background it, and say so.** The user must know a server is running and how to stop it. A dev server the human does not know about is the same failure one step later.
82
- 5. **The permission prompt belongs to your host.** Never try to bypass, suppress or auto-approve it, and take a refusal as the answer.`;
99
+ Four guards, none optional:
100
+
101
+ 1. **Never run two at once.** One dev server on the app's port. Restarting a stale one means stopping it first, not starting a second alongside it.
102
+ 2. **Never guess the command.** It comes from \`package.json\` scripts. No recognisable dev script means say so and stop, not invent one.
103
+ 3. **Never kill anything you did not start**, and never a daemon or a port holder. The one exception is the restart above, and say in one line that you did it.
104
+ 4. **The permission prompt belongs to your host.** Never bypass, suppress or auto-approve it, and take a refusal as the answer.`;
83
105
  /**
84
106
  * The operative rules, carried by every file that loads on every turn.
85
107
  *
@@ -87,7 +109,7 @@ Five guards, none optional:
87
109
  * NOT to, how, and the guards against calling something proven when it is not. Everything an agent
88
110
  * needs only at the moment it hits a specific situation lives in RETICLE.md, named at the end.
89
111
  */
90
- const RULE_BODY = `## Verifying with Reticle
112
+ export const RULE_BODY = `## Verifying with Reticle
91
113
 
92
114
  This app is instrumented by **Reticle**, an in-app verification layer exposed as \`reticle_*\` MCP tools and the \`${CLI}\` CLI (always through npx: Reticle's server is not installed into this project). Verifying is part of "done", not an optional extra.
93
115
 
@@ -103,21 +125,40 @@ This app is instrumented by **Reticle**, an in-app verification layer exposed as
103
125
  - **Only \`reticle_act_and_wait\` and \`reticle_assert\` produce a verdict.** \`reticle_act\` and everything else move or read the app and prove nothing, so a session ending without one of those two has no result however many tools it used.
104
126
  - Covered flows: \`${CLI} gate\` reports which recorded flows the changed files affect and whether they still pass.
105
127
 
128
+ **Setting Reticle up, or just restarted? You are mid-sequence — do not stop until a verdict exists.**
129
+ The whole of it is: instrument the app → get a dev server running → open the app in a browser →
130
+ drive one flow → report the verdict. Every step is yours to do, and none of them needs the user.
131
+ The ONE legitimate pause is a client restart, because a client reads its server list at startup;
132
+ when you come back, you are not finished, you are on the next step — resume it without being asked.
133
+ Stopping anywhere else leaves an app that looks installed and can verify nothing, which is the
134
+ single commonest way this goes wrong. \`/reticle\` carries the full recovery ladder when a step
135
+ does not work; use it rather than guessing, and never report the install as finished without a
136
+ verdict to point at.
137
+
106
138
  **Nothing connected? Get the app running.**
107
139
 
108
140
  ${DEV_SERVER_POLICY}
109
141
 
110
142
  A dev server that is already running does not pick up an edited build config or a newly created plugin file — restart it and hard-reload the tab. And if a server IS listening and still nothing connects, the cause is the SDK not loading in the page, not a missing dev server; do not tell the user to start one they are already running.
111
143
 
144
+ **Finish \`src/reticle-dev.ts\` before you claim setup is done.** \`init\` writes it and cannot always fill it in: a store that needs an argument only reading the code supplies (Jotai atoms, an XState actor, a TanStack \`queryClient\`) is left as a commented \`registerStore\` line. A file that registers nothing looks exactly like a finished one, and \`reticle_state\` then returns empty forever — which is indistinguishable from an app that has nothing to report, so it reads as success. Uncomment the line, complete it, and prove it by driving one flow and seeing your keys come back. If \`init\` told you to restart your client, this is the job waiting for you on the other side of that restart.
145
+
146
+ **Verify each feature as you finish it, not all of them at the end.** Asked for four, build one, drive it, get a verdict, then start the second. A red verdict after four builds has four suspects; after one it has none.
147
+
148
+ **Capture what a change is FOR while you are building it, not afterwards.** \`reticle_intent\` records the business outcome a change is meant to produce, and the only moment anybody knows it is while the change is being made. Pass \`intent\` when you save a flow, so the saved flow carries the reason it exists. A flow without one replays for months and then reports "step 3 failed" instead of what stopped being true for a user.
149
+
112
150
  **Honesty, which is the whole point:**
113
151
 
114
152
  - **\`verified: "unknown"\` is not a pass.** It means Reticle drove the app and could not tell what happened; \`verifiedReason\` says which clause decided that. Report it as unknown, never as working.
153
+ - **\`verified: "no-fault"\` is not a pass either.** It means nothing was DECLARED to prove: the page settled and no channel complained, but you asserted nothing, so there is no verification. You get it whenever \`until\` is omitted. Name a consequence the action changes — a signal, a request, a route, or store state — and call again.
115
154
  - **Never weaken a check to make it green.** Downgrading, skipping or deleting an assertion is a finding, not a fix.
116
155
  - **If Reticle cannot run** (no daemon, or this is not a running web app), say so. Do not skip verification silently.
117
156
  - **Setup is not finished until one real flow has been driven and produced a verdict.** \`init\` exiting 0, the tools appearing, and a session being listed are all things that happen before anything has been verified.
118
157
 
119
158
  **The \`/reticle\` skill runs this whole loop for you** — detect, connect, drive one flow, report. If your client does not have it, install it once: \`/plugin marketplace add reticlehq/reticle\` then \`/plugin install reticle@reticlehq\` in Claude Code, or \`npx skills add reticlehq/reticle\` anywhere the skills CLI works.
120
159
 
160
+ **A tool you need is missing? It exists.** The default surface advertises a subset; reach any other by name with \`reticle_run { tool, args }\`, and list them with \`reticle_tools\`. Two worth knowing: \`reticle_context\` returns this run's own memory — what is established, what is proven, what is still unverified — which is what you want after a compaction or when picking up work you did not start; \`reticle_intent\` records what a change was MEANT to do, while somebody still knows.
161
+
121
162
  **Report Reticle's own defects with \`reticle_feedback\` the moment you notice**, then carry on with your task. You are the user Reticle is built for and the only one who can say what it cost you, and that knowledge is gone when your context is.
122
163
 
123
164
  📄 **The rest is in [RETICLE.md](./${RETICLE_MD_PATH}): what to do when the tools are missing, when a result carries \`version_skew\` or \`update_available\`, when \`reticle_state\` comes back empty, and how to write a feedback report that can be acted on. Read it when you hit one of those, not before.**`;
@@ -15,3 +15,19 @@ import { type SourcePatch } from './patch-kind.js';
15
15
  import { UiLibrary } from './detect.js';
16
16
  export declare function patchAstroConfig(source: string): SourcePatch;
17
17
  export declare function patchAstroLayout(source: string, port: number | undefined, projectId: string | undefined, uiLibrary?: UiLibrary): SourcePatch;
18
+ /** Where Astro keeps ambient types — `create-astro` ships this file. */
19
+ export declare const ASTRO_ENV_DTS_PATH = "src/env.d.ts";
20
+ /**
21
+ * The declarations `astro check` needs for the Vite `define` names (#677).
22
+ *
23
+ * Matching SvelteKit's `hooks.client.ts` typing: `string | undefined`, because the define can be
24
+ * absent when the pairing token file is missing.
25
+ */
26
+ export declare const ASTRO_ENV_DTS_DECLARES: string;
27
+ /**
28
+ * Append the Vite-define ambient declarations to `src/env.d.ts`, or create the file.
29
+ *
30
+ * Without this, `create-astro`'s default `"astro check && astro build"` fails with four
31
+ * `ts(2304) Cannot find name '__RETICLE_TOKEN__'` errors after a clean init (#677).
32
+ */
33
+ export declare function patchAstroEnvDts(existing: string | null): SourcePatch;
@@ -270,3 +270,31 @@ export function patchAstroLayout(source, port, projectId, uiLibrary = UiLibrary.
270
270
  code: `${source.slice(0, at)}${astroConnectScript(port, projectId, uiLibrary)}${source.slice(at)}`,
271
271
  };
272
272
  }
273
+ /** Where Astro keeps ambient types — `create-astro` ships this file. */
274
+ export const ASTRO_ENV_DTS_PATH = 'src/env.d.ts';
275
+ /**
276
+ * The declarations `astro check` needs for the Vite `define` names (#677).
277
+ *
278
+ * Matching SvelteKit's `hooks.client.ts` typing: `string | undefined`, because the define can be
279
+ * absent when the pairing token file is missing.
280
+ */
281
+ export const ASTRO_ENV_DTS_DECLARES = 'declare const __RETICLE_TOKEN__: string | undefined;\n' +
282
+ 'declare const __RETICLE_ROOT__: string | undefined;\n';
283
+ /** Present once either declare is in the file — both are written together. */
284
+ const ENV_DTS_MARKER = '__RETICLE_TOKEN__';
285
+ /**
286
+ * Append the Vite-define ambient declarations to `src/env.d.ts`, or create the file.
287
+ *
288
+ * Without this, `create-astro`'s default `"astro check && astro build"` fails with four
289
+ * `ts(2304) Cannot find name '__RETICLE_TOKEN__'` errors after a clean init (#677).
290
+ */
291
+ export function patchAstroEnvDts(existing) {
292
+ if (null !== existing && existing.includes(ENV_DTS_MARKER)) {
293
+ return { kind: PatchKind.ALREADY };
294
+ }
295
+ if (null === existing || '' === existing.trim()) {
296
+ return { kind: PatchKind.APPLY, code: ASTRO_ENV_DTS_DECLARES };
297
+ }
298
+ const base = existing.endsWith('\n') ? existing : `${existing}\n`;
299
+ return { kind: PatchKind.APPLY, code: `${base}${ASTRO_ENV_DTS_DECLARES}` };
300
+ }