@reticlehq/server 2.12.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 (386) hide show
  1. package/SKILL.md +67 -109
  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 -1
  5. package/dist/bridge/bridge.js +247 -164
  6. package/dist/bridge/pairing-token.d.ts +5 -0
  7. package/dist/bridge/pairing-token.js +28 -0
  8. package/dist/bridge/protocol-skew.d.ts +37 -0
  9. package/dist/bridge/protocol-skew.js +45 -0
  10. package/dist/capsule/capsule-store.d.ts +6 -6
  11. package/dist/capsule/causal-summary.js +7 -2
  12. package/dist/cli/cli-doctor.js +31 -1
  13. package/dist/cli/cli-launch.d.ts +11 -1
  14. package/dist/cli/cli-launch.js +59 -10
  15. package/dist/cli/cli-parse.d.ts +15 -1
  16. package/dist/cli/cli-parse.js +212 -8
  17. package/dist/cli/cli-verify.d.ts +1 -0
  18. package/dist/cli/cli-verify.js +3 -2
  19. package/dist/cli/cloud-cli.js +428 -259
  20. package/dist/cli/cloud-keystore.d.ts +37 -0
  21. package/dist/cli/cloud-keystore.js +97 -0
  22. package/dist/cli/cloud-kit.d.ts +37 -0
  23. package/dist/cli/cloud-kit.js +121 -0
  24. package/dist/cli/cloud-login.d.ts +17 -0
  25. package/dist/cli/cloud-login.js +183 -0
  26. package/dist/cli/cloud-session.d.ts +48 -0
  27. package/dist/cli/cloud-session.js +53 -0
  28. package/dist/cli/config-discovery.js +1 -0
  29. package/dist/cli/daemon-start-options.d.ts +32 -0
  30. package/dist/cli/daemon-start-options.js +38 -0
  31. package/dist/cli/device-flow.d.ts +76 -0
  32. package/dist/cli/device-flow.js +56 -0
  33. package/dist/cli/doctor-rows.d.ts +9 -0
  34. package/dist/cli/doctor-rows.js +9 -0
  35. package/dist/cli/drive-attach.d.ts +13 -0
  36. package/dist/cli/drive-attach.js +18 -0
  37. package/dist/cli/drive-command.js +7 -3
  38. package/dist/cli/mcp-command.js +51 -20
  39. package/dist/cli/project-name.d.ts +30 -0
  40. package/dist/cli/project-name.js +44 -0
  41. package/dist/cli/status-next-action.d.ts +14 -0
  42. package/dist/cli/status-next-action.js +4 -4
  43. package/dist/cli.d.ts +1 -0
  44. package/dist/cli.js +134 -80
  45. package/dist/cloud/cloud-config.d.ts +22 -1
  46. package/dist/cloud/cloud-config.js +64 -5
  47. package/dist/cloud/cloud-sync.d.ts +5 -5
  48. package/dist/cloud/cloud-sync.js +33 -2
  49. package/dist/cloud/sync-cycle.d.ts +110 -0
  50. package/dist/cloud/sync-cycle.js +270 -0
  51. package/dist/cloud/sync-daemon.d.ts +69 -0
  52. package/dist/cloud/sync-daemon.js +260 -0
  53. package/dist/cloud/sync-disk.d.ts +9 -0
  54. package/dist/cloud/sync-disk.js +191 -0
  55. package/dist/cloud/sync-hash.d.ts +2 -0
  56. package/dist/cloud/sync-hash.js +35 -0
  57. package/dist/crawl/crawl-tools.js +5 -3
  58. package/dist/crawl/crawl.d.ts +3 -0
  59. package/dist/crawl/crawl.js +5 -0
  60. package/dist/daemon/daemon-resolve.d.ts +43 -0
  61. package/dist/daemon/daemon-resolve.js +69 -0
  62. package/dist/daemon/dev-servers.d.ts +15 -0
  63. package/dist/daemon/dev-servers.js +68 -0
  64. package/dist/daemon/wake-decision.d.ts +32 -0
  65. package/dist/daemon/wake-decision.js +38 -0
  66. package/dist/domain/domain-tools.js +2 -2
  67. package/dist/events/contradictions.d.ts +18 -0
  68. package/dist/events/contradictions.js +134 -21
  69. package/dist/events/declared.d.ts +7 -4
  70. package/dist/events/declared.js +15 -6
  71. package/dist/events/echo-mismatch.js +64 -14
  72. package/dist/events/event-filters.d.ts +8 -0
  73. package/dist/events/event-filters.js +19 -2
  74. package/dist/events/predicate-eval.js +53 -5
  75. package/dist/events/predicate-precheck.d.ts +8 -0
  76. package/dist/events/predicate-precheck.js +41 -0
  77. package/dist/events/predicate-route.d.ts +14 -0
  78. package/dist/events/predicate-route.js +36 -8
  79. package/dist/events/predicate.d.ts +5 -0
  80. package/dist/events/predicate.js +37 -5
  81. package/dist/events/split-text-miss.d.ts +30 -0
  82. package/dist/events/split-text-miss.js +44 -0
  83. package/dist/flows/flake.d.ts +6 -6
  84. package/dist/flows/flow-memory-consult.d.ts +55 -0
  85. package/dist/flows/flow-memory-consult.js +38 -0
  86. package/dist/flows/flow-replay-run.d.ts +18 -7
  87. package/dist/flows/flow-replay-run.js +239 -22
  88. package/dist/flows/flow-replay.js +11 -2
  89. package/dist/flows/flow-step-runners.d.ts +8 -0
  90. package/dist/flows/flow-step-runners.js +8 -2
  91. package/dist/flows/flow-store-for-session.d.ts +31 -0
  92. package/dist/flows/flow-store-for-session.js +33 -0
  93. package/dist/flows/flow-tools.js +58 -32
  94. package/dist/flows/flows.d.ts +8 -0
  95. package/dist/flows/flows.js +468 -2
  96. package/dist/flows/heal-run.js +5 -2
  97. package/dist/flows/predicate-to-expect.d.ts +17 -12
  98. package/dist/flows/predicate-to-expect.js +30 -12
  99. package/dist/flows/recordings.d.ts +13 -1
  100. package/dist/flows/recordings.js +17 -2
  101. package/dist/flows/replay.d.ts +22 -2
  102. package/dist/flows/replay.js +28 -2
  103. package/dist/flows/verify-change-tools.js +5 -2
  104. package/dist/honesty/blind-spots.d.ts +55 -2
  105. package/dist/honesty/blind-spots.js +63 -3
  106. package/dist/honesty/honesty.d.ts +24 -0
  107. package/dist/honesty/honesty.js +25 -1
  108. package/dist/honesty/instrumentation-gaps.d.ts +9 -0
  109. package/dist/honesty/instrumentation-gaps.js +29 -2
  110. package/dist/honesty/verified.d.ts +2 -1
  111. package/dist/honesty/verified.js +16 -1
  112. package/dist/http-server.js +12 -0
  113. package/dist/impact/impact-recorder.d.ts +31 -8
  114. package/dist/impact/impact-recorder.js +104 -21
  115. package/dist/impact/impact-store.d.ts +15 -1
  116. package/dist/impact/impact-store.js +43 -2
  117. package/dist/index.js +113 -7
  118. package/dist/init/agent-rules.d.ts +13 -6
  119. package/dist/init/agent-rules.js +23 -12
  120. package/dist/init/astro-patch.d.ts +16 -0
  121. package/dist/init/astro-patch.js +28 -0
  122. package/dist/init/capabilities.d.ts +4 -1
  123. package/dist/init/capabilities.js +22 -2
  124. package/dist/init/confirm.d.ts +31 -7
  125. package/dist/init/confirm.js +48 -8
  126. package/dist/init/cra.d.ts +20 -4
  127. package/dist/init/cra.js +57 -17
  128. package/dist/init/csp-check.js +17 -2
  129. package/dist/init/csp-doctor.d.ts +8 -1
  130. package/dist/init/csp-doctor.js +8 -3
  131. package/dist/init/dev-script.d.ts +3 -1
  132. package/dist/init/dev-script.js +8 -5
  133. package/dist/init/format-generated.d.ts +18 -0
  134. package/dist/init/format-generated.js +39 -0
  135. package/dist/init/install-hint.d.ts +9 -0
  136. package/dist/init/install-hint.js +29 -0
  137. package/dist/init/mcp-clients.d.ts +7 -0
  138. package/dist/init/mcp-clients.js +7 -0
  139. package/dist/init/mcp.d.ts +1 -1
  140. package/dist/init/mcp.js +1 -1
  141. package/dist/init/node-io.js +11 -1
  142. package/dist/init/plan-framework.js +81 -45
  143. package/dist/init/plan.d.ts +20 -7
  144. package/dist/init/plan.js +20 -45
  145. package/dist/init/preflight.d.ts +28 -0
  146. package/dist/init/preflight.js +38 -0
  147. package/dist/init/run.d.ts +32 -9
  148. package/dist/init/run.js +141 -119
  149. package/dist/init/slash-command.d.ts +1 -1
  150. package/dist/init/snippets.js +41 -25
  151. package/dist/init/vite-config.d.ts +1 -1
  152. package/dist/init/vite-config.js +39 -25
  153. package/dist/init/workspace-apps.d.ts +17 -10
  154. package/dist/init/workspace-apps.js +110 -0
  155. package/dist/input/network-mock-tools.js +3 -6
  156. package/dist/input/real-input.js +25 -5
  157. package/dist/input/scroll-tools.js +4 -6
  158. package/dist/input/viewport-tools.js +5 -4
  159. package/dist/intent/inline-intent.d.ts +10 -19
  160. package/dist/intent/inline-intent.js +91 -1
  161. package/dist/intent/intent-shard-store.d.ts +72 -0
  162. package/dist/intent/intent-shard-store.js +203 -0
  163. package/dist/intent/intent-shard.d.ts +411 -0
  164. package/dist/intent/intent-shard.js +176 -0
  165. package/dist/intent/intent-store.d.ts +12 -0
  166. package/dist/intent/intent-store.js +21 -0
  167. package/dist/intent/intent-subject.d.ts +45 -0
  168. package/dist/intent/intent-subject.js +177 -0
  169. package/dist/intent/intent-tools.js +65 -2
  170. package/dist/intent/open-intents.d.ts +21 -0
  171. package/dist/intent/open-intents.js +15 -0
  172. package/dist/license/license-env.d.ts +29 -0
  173. package/dist/license/license-env.js +148 -0
  174. package/dist/loopback-agent.d.ts +31 -0
  175. package/dist/loopback-agent.js +57 -0
  176. package/dist/mcp/attach-memory.d.ts +18 -2
  177. package/dist/mcp/attach-memory.js +50 -7
  178. package/dist/mcp/client-identity.d.ts +31 -0
  179. package/dist/mcp/client-identity.js +51 -0
  180. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  181. package/dist/mcp/mcp-post-transport.js +109 -0
  182. package/dist/mcp/mcp-proxy.d.ts +2 -1
  183. package/dist/mcp/mcp-proxy.js +52 -62
  184. package/dist/mcp/mcp.js +7 -1
  185. package/dist/mcp/proxy-backoff.d.ts +11 -0
  186. package/dist/mcp/proxy-backoff.js +13 -0
  187. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  188. package/dist/mcp/proxy-daemon-probe.js +7 -1
  189. package/dist/mcp/proxy-handshake.d.ts +23 -0
  190. package/dist/mcp/proxy-handshake.js +31 -0
  191. package/dist/mcp/tool-catalog-cache.d.ts +8 -0
  192. package/dist/mcp/tool-catalog-cache.js +12 -0
  193. package/dist/memory/memory-tools.d.ts +2 -0
  194. package/dist/memory/memory-tools.js +82 -0
  195. package/dist/memory/project-memory.d.ts +34 -0
  196. package/dist/memory/project-memory.js +96 -0
  197. package/dist/oracles/propose-consequences.js +12 -3
  198. package/dist/pool/browser-pool.d.ts +63 -4
  199. package/dist/pool/browser-pool.js +113 -4
  200. package/dist/pool/playwright-launcher.js +9 -0
  201. package/dist/project/project-tools.js +3 -3
  202. package/dist/project/reticle-dir.d.ts +15 -2
  203. package/dist/project/reticle-dir.js +21 -4
  204. package/dist/runs/run-store.d.ts +10 -0
  205. package/dist/runs/run-store.js +10 -0
  206. package/dist/runs/verification-sync.d.ts +0 -11
  207. package/dist/runs/verification-sync.js +22 -1
  208. package/dist/runs/verify-server.d.ts +8 -1
  209. package/dist/runs/verify-server.js +14 -2
  210. package/dist/session/command-timeout.d.ts +5 -2
  211. package/dist/session/command-timeout.js +5 -4
  212. package/dist/session/lease-visibility.d.ts +67 -0
  213. package/dist/session/lease-visibility.js +76 -0
  214. package/dist/session/no-session-diagnosis.d.ts +23 -0
  215. package/dist/session/no-session-diagnosis.js +58 -17
  216. package/dist/session/no-session-next-action.d.ts +7 -0
  217. package/dist/session/no-session-next-action.js +42 -3
  218. package/dist/session/no-session-watch.d.ts +10 -6
  219. package/dist/session/no-session-watch.js +51 -19
  220. package/dist/session/pending-commands.d.ts +11 -1
  221. package/dist/session/pending-commands.js +12 -2
  222. package/dist/session/resolve-within.d.ts +34 -0
  223. package/dist/session/resolve-within.js +53 -0
  224. package/dist/session/session-info.d.ts +18 -0
  225. package/dist/session/session-info.js +11 -0
  226. package/dist/session/session-manager.d.ts +14 -0
  227. package/dist/session/session-manager.js +61 -3
  228. package/dist/session/session-mirror.d.ts +16 -0
  229. package/dist/session/session-mirror.js +22 -0
  230. package/dist/session/session-recommendation.d.ts +19 -5
  231. package/dist/session/session-recommendation.js +25 -9
  232. package/dist/session/session-successor.js +36 -1
  233. package/dist/session/session-tools.js +2 -2
  234. package/dist/session/session.d.ts +22 -2
  235. package/dist/session/session.js +87 -7
  236. package/dist/session/wall-clock.d.ts +12 -0
  237. package/dist/session/wall-clock.js +14 -0
  238. package/dist/setup/agent-configs.d.ts +96 -0
  239. package/dist/setup/agent-configs.js +333 -0
  240. package/dist/setup/agent-io.d.ts +9 -0
  241. package/dist/setup/agent-io.js +16 -0
  242. package/dist/setup/agent-writer.d.ts +51 -0
  243. package/dist/setup/agent-writer.js +115 -0
  244. package/dist/setup/approval-migration.d.ts +47 -0
  245. package/dist/setup/approval-migration.js +68 -0
  246. package/dist/setup/auto-approve.d.ts +85 -0
  247. package/dist/setup/auto-approve.js +150 -0
  248. package/dist/setup/bridge-port.d.ts +15 -0
  249. package/dist/setup/bridge-port.js +24 -0
  250. package/dist/setup/desktop-shape.d.ts +57 -0
  251. package/dist/setup/desktop-shape.js +63 -0
  252. package/dist/setup/dev-server-wait.d.ts +60 -0
  253. package/dist/setup/dev-server-wait.js +65 -0
  254. package/dist/setup/drive-agent.d.ts +71 -0
  255. package/dist/setup/drive-agent.js +171 -0
  256. package/dist/setup/drive-plan.d.ts +68 -0
  257. package/dist/setup/drive-plan.js +130 -0
  258. package/dist/setup/ensure-daemon.d.ts +29 -0
  259. package/dist/setup/ensure-daemon.js +58 -0
  260. package/dist/setup/init-runtime.d.ts +47 -0
  261. package/dist/setup/init-runtime.js +160 -0
  262. package/dist/setup/interrupt.d.ts +25 -0
  263. package/dist/setup/interrupt.js +40 -0
  264. package/dist/setup/license-key.d.ts +39 -0
  265. package/dist/setup/license-key.js +72 -0
  266. package/dist/setup/listeners.d.ts +37 -0
  267. package/dist/setup/listeners.js +85 -0
  268. package/dist/setup/node-effects.d.ts +60 -0
  269. package/dist/setup/node-effects.js +210 -0
  270. package/dist/setup/page-probe.d.ts +39 -0
  271. package/dist/setup/page-probe.js +49 -0
  272. package/dist/setup/relaunch.d.ts +36 -0
  273. package/dist/setup/relaunch.js +57 -0
  274. package/dist/setup/remaining-steps.d.ts +19 -0
  275. package/dist/setup/remaining-steps.js +31 -0
  276. package/dist/setup/run-setup.d.ts +99 -0
  277. package/dist/setup/run-setup.js +187 -0
  278. package/dist/setup/session-pick.d.ts +47 -0
  279. package/dist/setup/session-pick.js +51 -0
  280. package/dist/setup/setup-command.d.ts +45 -0
  281. package/dist/setup/setup-command.js +199 -0
  282. package/dist/setup/setup-options.d.ts +74 -0
  283. package/dist/setup/setup-options.js +67 -0
  284. package/dist/setup/transcripts.d.ts +16 -0
  285. package/dist/setup/transcripts.js +43 -0
  286. package/dist/status-payload.d.ts +14 -1
  287. package/dist/status-payload.js +23 -1
  288. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  289. package/dist/telemetry/daemon-telemetry.js +52 -5
  290. package/dist/telemetry/error-class.js +1 -1
  291. package/dist/telemetry/feedback-context.d.ts +9 -1
  292. package/dist/telemetry/feedback-context.js +60 -29
  293. package/dist/telemetry/feedback-local.d.ts +15 -0
  294. package/dist/telemetry/feedback-local.js +56 -0
  295. package/dist/telemetry/feedback.d.ts +11 -0
  296. package/dist/telemetry/feedback.js +19 -6
  297. package/dist/telemetry/init-telemetry.d.ts +2 -0
  298. package/dist/telemetry/init-telemetry.js +2 -0
  299. package/dist/telemetry/install-source.d.ts +14 -0
  300. package/dist/telemetry/install-source.js +30 -0
  301. package/dist/telemetry/license-activation.d.ts +7 -1
  302. package/dist/telemetry/license-activation.js +9 -5
  303. package/dist/telemetry/mcp-connection.js +7 -0
  304. package/dist/telemetry/project-profile.d.ts +12 -1
  305. package/dist/telemetry/project-profile.js +11 -6
  306. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  307. package/dist/telemetry/proxy-telemetry.js +21 -0
  308. package/dist/telemetry/session-metrics.d.ts +12 -0
  309. package/dist/telemetry/session-metrics.js +35 -1
  310. package/dist/telemetry/telemetry.js +17 -2
  311. package/dist/telemetry/tool-refused.d.ts +3 -1
  312. package/dist/telemetry/tool-refused.js +34 -2
  313. package/dist/tools/act-capsule.d.ts +10 -0
  314. package/dist/tools/act-capsule.js +8 -2
  315. package/dist/tools/act-danger.d.ts +2 -1
  316. package/dist/tools/act-danger.js +11 -3
  317. package/dist/tools/act-observation.d.ts +41 -0
  318. package/dist/tools/act-observation.js +36 -0
  319. package/dist/tools/act-preflight.d.ts +20 -0
  320. package/dist/tools/act-preflight.js +98 -0
  321. package/dist/tools/act-sequence-retry.d.ts +45 -0
  322. package/dist/tools/act-sequence-retry.js +58 -0
  323. package/dist/tools/act-sequence-tool.d.ts +13 -0
  324. package/dist/tools/act-sequence-tool.js +135 -0
  325. package/dist/tools/act-target.d.ts +14 -0
  326. package/dist/tools/act-target.js +47 -0
  327. package/dist/tools/act-tools.d.ts +21 -1
  328. package/dist/tools/act-tools.js +100 -213
  329. package/dist/tools/assert-verdict.js +22 -16
  330. package/dist/tools/browser-tools.js +6 -2
  331. package/dist/tools/dynamic-tools.js +24 -5
  332. package/dist/tools/error-recovery.d.ts +1 -0
  333. package/dist/tools/error-recovery.js +8 -0
  334. package/dist/tools/invoke-tool.js +59 -9
  335. package/dist/tools/lease-tools.d.ts +30 -4
  336. package/dist/tools/lease-tools.js +169 -5
  337. package/dist/tools/merged-name-redirect.d.ts +13 -0
  338. package/dist/tools/merged-name-redirect.js +22 -0
  339. package/dist/tools/numeric-bounds.d.ts +100 -0
  340. package/dist/tools/numeric-bounds.js +125 -0
  341. package/dist/tools/observe-tools.js +47 -40
  342. package/dist/tools/read-tools.js +28 -7
  343. package/dist/tools/real-input-attempt.d.ts +10 -0
  344. package/dist/tools/real-input-attempt.js +59 -1
  345. package/dist/tools/reconcile-tools.js +4 -4
  346. package/dist/tools/tool-kit.d.ts +12 -0
  347. package/dist/tools/tool-kit.js +6 -1
  348. package/dist/tools/tool-names.d.ts +2 -0
  349. package/dist/tools/tool-names.js +2 -0
  350. package/dist/tools/tools-helpers.d.ts +19 -0
  351. package/dist/tools/tools-helpers.js +46 -0
  352. package/dist/tools/tools.d.ts +2 -0
  353. package/dist/tools/tools.js +56 -8
  354. package/dist/update/reticle-deps.d.ts +9 -0
  355. package/dist/update/reticle-deps.js +27 -0
  356. package/dist/update/sdk-sync.d.ts +1 -2
  357. package/dist/update/sdk-sync.js +1 -21
  358. package/dist/update/update-nudge.d.ts +18 -0
  359. package/dist/update/update-nudge.js +20 -0
  360. package/dist/version/peer-announce.js +4 -2
  361. package/dist/version/sdk-fix.d.ts +47 -0
  362. package/dist/version/sdk-fix.js +127 -0
  363. package/dist/version/version-nudge.d.ts +14 -0
  364. package/dist/version/version-nudge.js +21 -0
  365. package/dist/version/version-skew.d.ts +38 -3
  366. package/dist/version/version-skew.js +83 -7
  367. package/dist/visual/visual-store.d.ts +6 -6
  368. package/dist/visual/visual-store.js +18 -16
  369. package/dist/visual/visual-tools.js +57 -12
  370. package/docs/cli/doctor.mdx +1 -0
  371. package/docs/cli/serve.mdx +4 -2
  372. package/docs/cli.mdx +7 -3
  373. package/docs/desktop.mdx +1 -1
  374. package/docs/docs.json +1 -1
  375. package/docs/getting-started.md +19 -3
  376. package/docs/index.mdx +9 -6
  377. package/docs/install-agentic.mdx +38 -12
  378. package/docs/install-manual.mdx +1 -1
  379. package/docs/packages.mdx +1 -1
  380. package/docs/skill-file.mdx +3 -3
  381. package/docs/telemetry-contract.md +108 -4
  382. package/docs/tools-act-sequence.mdx +16 -0
  383. package/docs/usage.md +1 -1
  384. package/package.json +4 -3
  385. package/dist/init/cursor.d.ts +0 -24
  386. package/dist/init/cursor.js +0 -80
@@ -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
+ }
@@ -13,7 +13,10 @@
13
13
  export declare const MAX_TESTIDS = 60;
14
14
  /** Every distinct `data-testid` literal in the given sources, in first-seen order. */
15
15
  export declare function scanTestids(sources: readonly string[]): string[];
16
- /** The store libraries this app depends on, as ready-to-uncomment registration lines. */
16
+ /**
17
+ * The store libraries this app depends on AND the running app cannot reveal on its own, as
18
+ * ready-to-uncomment registration lines.
19
+ */
17
20
  export declare function storeHints(deps: ReadonlySet<string>): string[];
18
21
  /** A store instance we found in the app's own source, with everything needed to import and register it. */
19
22
  export interface FoundStore {
@@ -49,9 +49,29 @@ const STORE_LIBRARIES = [
49
49
  ['pinia', "registerStore('cart', piniaStore(useCartStore()))"],
50
50
  ['svelte', "registerStore('cart', svelteStore(cartStore))"],
51
51
  ];
52
- /** The store libraries this app depends on, as ready-to-uncomment registration lines. */
52
+ /**
53
+ * Libraries that hand their store to React through a context provider, and therefore need no hint.
54
+ *
55
+ * `<Provider store>` and `<QueryClientProvider client>` put the store instance in the fiber tree as
56
+ * a prop, so the React adapter finds and registers it on the first commit — see `auto-stores.ts` in
57
+ * `@reticlehq/react`. Offering a commented `registerStore` line for these was asking someone to do
58
+ * by hand, and be nagged about, work that is already done by the time they read the report.
59
+ *
60
+ * Everything absent from this set is module-scope (Zustand, Valtio, a MobX singleton) or needs an
61
+ * argument only the source supplies (Jotai atoms, an XState actor). Nothing in the running app
62
+ * points at those, so they still need the app to hand them over, and the hint still earns its place.
63
+ */
64
+ const AUTO_DISCOVERED_DEPS = new Set([
65
+ '@tanstack/react-query',
66
+ '@reduxjs/toolkit',
67
+ 'redux',
68
+ ]);
69
+ /**
70
+ * The store libraries this app depends on AND the running app cannot reveal on its own, as
71
+ * ready-to-uncomment registration lines.
72
+ */
53
73
  export function storeHints(deps) {
54
- return STORE_LIBRARIES.filter(([dep]) => deps.has(dep)).map(([, hint]) => hint);
74
+ return STORE_LIBRARIES.filter(([dep]) => deps.has(dep) && !AUTO_DISCOVERED_DEPS.has(dep)).map(([, hint]) => hint);
55
75
  }
56
76
  /**
57
77
  * Store factories whose EXPORTED result is the store instance itself — the form `registerStore`
@@ -19,17 +19,29 @@
19
19
  * keeps today's cost and today's exit behaviour, and blocking never happens where blocking is
20
20
  * wrong. The signal is the standard "am I being piped" one and needs no argument parsing, which
21
21
  * also keeps `init`'s documented flag surface where it is.
22
- * - It never starts anything. A dev server started by a long-lived daemon is invisible to the
23
- * person whose machine it runs on and orphans when the daemon exits, so the daemon stays out of
24
- * it — the AGENT starts one, under the guards in agent-rules.ts, because it is attributable and
25
- * stoppable there. So the honest shape here is: confirm the app if it connects, otherwise name
26
- * the ONE command that is outstanding and the one that proves it worked.
22
+ * - The DAEMON never starts anything. A dev server started by a long-lived daemon is invisible to
23
+ * the person whose machine it runs on and orphans when the daemon exits, so the daemon stays out
24
+ * of it. Note what that reasoning is actually about: attributability, not abstinence. A
25
+ * foreground `init` somebody ran satisfies it — the command is in their transcript, and it stops
26
+ * what it started if setup fails or is interrupted — which is why the runtime phase is allowed
27
+ * to boot one where the daemon is not. This function keeps its own shape either way: confirm the
28
+ * app if it connects, otherwise name the ONE command that is outstanding and the one that proves
29
+ * it worked.
27
30
  */
28
31
  import { InitConfirmation } from '@reticlehq/core';
29
- import type { InitOutcome } from '@reticlehq/core';
32
+ import type { DevServerEntry, InitOutcome } from '@reticlehq/core';
30
33
  export interface ConfirmDeps {
31
34
  /** Session ids connected right now, or null when nothing is listening on the bridge port. */
32
35
  listSessionIds: () => Promise<readonly string[] | null>;
36
+ /**
37
+ * The dev servers that have announced themselves, read at the END of the wait rather than the
38
+ * start: a dev server booting alongside `init` is the common case, and asking before it has
39
+ * finished listening would report the state we are trying to move away from.
40
+ *
41
+ * Optional so every existing construction of these deps — and any embedder's — keeps working and
42
+ * simply gets today's less specific message.
43
+ */
44
+ listDevServers?: () => readonly DevServerEntry[];
33
45
  now: () => number;
34
46
  sleep: (ms: number) => Promise<void>;
35
47
  windowMs?: number;
@@ -68,7 +80,19 @@ export declare function confirmAppConnected(deps: ConfirmDeps): Promise<InitConf
68
80
  * there is a command outstanding. A missing daemon is not a defect in the install — the daemon comes
69
81
  * up when the agent loads the tools — so it is a notice.
70
82
  */
71
- export declare function confirmationMessage(confirmation: InitConfirmation, port: number): string;
83
+ /**
84
+ * Narrow "no app connected" using what the dev servers themselves announced.
85
+ *
86
+ * `confirmAppConnected` can only observe the bridge, so its NO_SESSION covers two situations with
87
+ * OPPOSITE fixes: the dev server is not running Reticle at all, or it is and no page has been
88
+ * opened. An announced entry is proof the plugin is loaded in the process that is actually running
89
+ * — which means the config is right and it has already been restarted.
90
+ *
91
+ * Only the unexplained answer is sharpened. CONNECTED is the finished state and NO_DAEMON is a
92
+ * different fact; neither becomes more true because a dev server is up.
93
+ */
94
+ export declare function sharpenWithDevServers(confirmation: InitConfirmation, running: readonly DevServerEntry[]): InitConfirmation;
95
+ export declare function confirmationMessage(confirmation: InitConfirmation, port: number, running?: readonly DevServerEntry[]): string;
72
96
  /**
73
97
  * Finish `init` on evidence rather than on a write.
74
98
  *
@@ -19,14 +19,21 @@
19
19
  * keeps today's cost and today's exit behaviour, and blocking never happens where blocking is
20
20
  * wrong. The signal is the standard "am I being piped" one and needs no argument parsing, which
21
21
  * also keeps `init`'s documented flag surface where it is.
22
- * - It never starts anything. A dev server started by a long-lived daemon is invisible to the
23
- * person whose machine it runs on and orphans when the daemon exits, so the daemon stays out of
24
- * it — the AGENT starts one, under the guards in agent-rules.ts, because it is attributable and
25
- * stoppable there. So the honest shape here is: confirm the app if it connects, otherwise name
26
- * the ONE command that is outstanding and the one that proves it worked.
22
+ * - The DAEMON never starts anything. A dev server started by a long-lived daemon is invisible to
23
+ * the person whose machine it runs on and orphans when the daemon exits, so the daemon stays out
24
+ * of it. Note what that reasoning is actually about: attributability, not abstinence. A
25
+ * foreground `init` somebody ran satisfies it — the command is in their transcript, and it stops
26
+ * what it started if setup fails or is interrupted — which is why the runtime phase is allowed
27
+ * to boot one where the daemon is not. This function keeps its own shape either way: confirm the
28
+ * app if it connects, otherwise name the ONE command that is outstanding and the one that proves
29
+ * it worked.
27
30
  */
28
31
  import { InitConfirmation, RETICLE_DEFAULT_PORT } from '@reticlehq/core';
29
32
  import { fetchStatus, summarizeStatus } from '../cli/cli-launch.js';
33
+ import { readDevServers } from '../daemon/dev-servers.js';
34
+ import { devServersForProject } from '@reticlehq/core';
35
+ import { readProjectId } from '../cli/cli-port.js';
36
+ import { reticleStateHome } from '../daemon/daemon.js';
30
37
  import { reportInitOutcome } from '../telemetry/init-telemetry.js';
31
38
  /**
32
39
  * How long a human is asked to wait.
@@ -78,7 +85,23 @@ const PROVE_COMMAND = '`npx @reticlehq/server status`';
78
85
  * there is a command outstanding. A missing daemon is not a defect in the install — the daemon comes
79
86
  * up when the agent loads the tools — so it is a notice.
80
87
  */
81
- export function confirmationMessage(confirmation, port) {
88
+ /**
89
+ * Narrow "no app connected" using what the dev servers themselves announced.
90
+ *
91
+ * `confirmAppConnected` can only observe the bridge, so its NO_SESSION covers two situations with
92
+ * OPPOSITE fixes: the dev server is not running Reticle at all, or it is and no page has been
93
+ * opened. An announced entry is proof the plugin is loaded in the process that is actually running
94
+ * — which means the config is right and it has already been restarted.
95
+ *
96
+ * Only the unexplained answer is sharpened. CONNECTED is the finished state and NO_DAEMON is a
97
+ * different fact; neither becomes more true because a dev server is up.
98
+ */
99
+ export function sharpenWithDevServers(confirmation, running) {
100
+ if (confirmation !== InitConfirmation.NO_SESSION)
101
+ return confirmation;
102
+ return 0 === running.length ? InitConfirmation.NO_SESSION : InitConfirmation.NO_PAGE;
103
+ }
104
+ export function confirmationMessage(confirmation, port, running = []) {
82
105
  if (confirmation === InitConfirmation.CONNECTED) {
83
106
  return (' [✓] an app is connected — that is the install finished, not just written.\n' +
84
107
  ' Ask your agent to drive a flow.');
@@ -89,6 +112,15 @@ export function confirmationMessage(confirmation, port) {
89
112
  ' The daemon starts on its own when your agent loads the Reticle tools. After that, ' +
90
113
  `${PROVE_COMMAND} confirms the app.`);
91
114
  }
115
+ if (confirmation === InitConfirmation.NO_PAGE) {
116
+ // Deliberately never says "restart". The announcement is proof they already did — telling them
117
+ // again is how a correct instruction reads as the tool not knowing what is going on.
118
+ const where = running.map((s) => ` ${s.url}`).join('\n');
119
+ return (' [⚠] your dev server has Reticle loaded, and no page has connected yet — so the only step ' +
120
+ 'left is opening the app.\n' +
121
+ ` Open it in a browser:\n${where}\n` +
122
+ ` ${PROVE_COMMAND} then confirms it.`);
123
+ }
92
124
  return (' [⚠] no app has connected yet — the files are written, the page half of the install is not ' +
93
125
  'done.\n' +
94
126
  ' Restart your dev server and load the app in a browser — or ask your agent to, which it is ' +
@@ -142,13 +174,21 @@ export async function confirmInstall(result, io, deps, report = reportInitOutcom
142
174
  }
143
175
  io.print('');
144
176
  io.print(WATCHING(deps.windowMs ?? CONFIRM_WINDOW_MS));
145
- const confirmation = await confirmAppConnected(deps);
146
- io.print(confirmationMessage(confirmation, deps.port));
177
+ const observed = await confirmAppConnected(deps);
178
+ const running = deps.listDevServers?.() ?? [];
179
+ const confirmation = sharpenWithDevServers(observed, running);
180
+ io.print(confirmationMessage(confirmation, deps.port, running));
147
181
  report({ ...outcome, confirmation });
148
182
  }
149
183
  /** The real deps: the daemon's own status endpoint, and whether a human is watching. */
150
184
  export function nodeConfirmDeps(port = RETICLE_DEFAULT_PORT) {
151
185
  return {
186
+ // Scoped to the project being set up. The registry is machine-wide, so unscoped it reported a
187
+ // sibling app's dev server as this one's — see devServersForProject.
188
+ listDevServers: () => devServersForProject(readDevServers(reticleStateHome()), {
189
+ projectId: readProjectId(process.cwd()),
190
+ root: process.cwd(),
191
+ }),
152
192
  listSessionIds: async () => {
153
193
  const payload = await fetchStatus(port);
154
194
  // `fetchStatus` resolves undefined when nothing answered, which is precisely the "no daemon"
@@ -12,9 +12,17 @@
12
12
  * `REACT_APP_`. So it travels through `.env.development.local`, which is CRA's own documented
13
13
  * mechanism and is gitignored by CRA's own template.
14
14
  */
15
- /** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
15
+ /** The line added to `src/index.tsx` / `src/index.js`. Side-effect import: the module guards itself on NODE_ENV. */
16
16
  export declare const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
17
- export declare const CRA_DEV_MODULE_PATH = "src/reticle-dev.ts";
17
+ /**
18
+ * Where the connect module lands — `.ts` only when the app already speaks TypeScript.
19
+ *
20
+ * A JavaScript CRA project has no tsconfig and cannot resolve `.ts` (#675). Emitting `.ts` there
21
+ * makes the first compile after init fail while the plan still reports `[✓]`.
22
+ */
23
+ export declare function craDevModulePath(typescript: boolean): string;
24
+ /** TypeScript default path — prefer `craDevModulePath` when the project's language is known. */
25
+ export declare const CRA_DEV_MODULE_PATH: string;
18
26
  export declare const CRA_ENV_PATH = ".env.development.local";
19
27
  export declare const TOKEN_VAR = "REACT_APP_RETICLE_TOKEN";
20
28
  /**
@@ -59,5 +67,13 @@ export declare const CRA_TOKEN_PER_MACHINE_NOTICE: string;
59
67
  export declare function craImportPatch(source: string): string | null;
60
68
  /** Set the token variable, or null when nothing needs to change. */
61
69
  export declare function craEnvPatch(existing: string | null, token: string, url?: string): string | null;
62
- /** The dev-only connect module imported from `src/index.tsx`. */
63
- export declare function craDevModuleFile(port: number | undefined, projectId?: string): string;
70
+ /** Options for the CRA connect module. */
71
+ export interface CraDevModuleOptions {
72
+ /**
73
+ * When false, emit a `.js`-shaped body (no `export {}`). Default true for callers that predate
74
+ * the language branch.
75
+ */
76
+ typescript?: boolean;
77
+ }
78
+ /** The dev-only connect module imported from the app entry. */
79
+ export declare function craDevModuleFile(port: number | undefined, projectId?: string, options?: CraDevModuleOptions): string;
package/dist/init/cra.js CHANGED
@@ -14,9 +14,19 @@
14
14
  */
15
15
  import { bridgeWsUrl } from '@reticlehq/core';
16
16
  import { CLI } from './agent-rules.js';
17
- /** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
17
+ /** The line added to `src/index.tsx` / `src/index.js`. Side-effect import: the module guards itself on NODE_ENV. */
18
18
  export const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
19
- export const CRA_DEV_MODULE_PATH = 'src/reticle-dev.ts';
19
+ /**
20
+ * Where the connect module lands — `.ts` only when the app already speaks TypeScript.
21
+ *
22
+ * A JavaScript CRA project has no tsconfig and cannot resolve `.ts` (#675). Emitting `.ts` there
23
+ * makes the first compile after init fail while the plan still reports `[✓]`.
24
+ */
25
+ export function craDevModulePath(typescript) {
26
+ return typescript ? 'src/reticle-dev.ts' : 'src/reticle-dev.js';
27
+ }
28
+ /** TypeScript default path — prefer `craDevModulePath` when the project's language is known. */
29
+ export const CRA_DEV_MODULE_PATH = craDevModulePath(true);
20
30
  export const CRA_ENV_PATH = '.env.development.local';
21
31
  export const TOKEN_VAR = 'REACT_APP_RETICLE_TOKEN';
22
32
  /**
@@ -101,8 +111,9 @@ export function craEnvPatch(existing, token, url) {
101
111
  }
102
112
  return next === (existing ?? '') ? null : next;
103
113
  }
104
- /** The dev-only connect module imported from `src/index.tsx`. */
105
- export function craDevModuleFile(port, projectId) {
114
+ /** The dev-only connect module imported from the app entry. */
115
+ export function craDevModuleFile(port, projectId, options = {}) {
116
+ const typescript = false !== options.typescript;
106
117
  const fields = [];
107
118
  // `bridgeWsUrl`, not a hand-written string. This was the only client URL in the product spelling
108
119
  // the host as `127.0.0.1` while every other generator said `localhost` — same endpoint, but a
@@ -112,27 +123,56 @@ export function craDevModuleFile(port, projectId) {
112
123
  fields.push(`url: '${bridgeWsUrl(port)}'`);
113
124
  if (projectId !== undefined && projectId.length > 0)
114
125
  fields.push(`projectId: '${projectId}'`);
115
- const inline = fields.length > 0 ? `${fields.join(', ')}, ` : '';
126
+ // Multiline on purpose (#684): a single-line connect + console.error fails CRA boilerplate
127
+ // Prettier (printWidth 80) and the project's own lint blocks a clean install.
128
+ const connectFields = [
129
+ ...fields.map((f) => ` ${f},`),
130
+ ' ...(url.length > 0 ? { url } : {}),',
131
+ ' ...(token.length > 0 ? { token } : {}),',
132
+ ].join('\n');
133
+ // Split so every emitted line stays under a CRA boilerplate printWidth of 80 (#684). A single
134
+ // JSON.stringify of the whole note is ~290 characters and fails prettier-as-eslint on install.
135
+ // Chunks are joined with `+` in the emitted file; together they equal `[reticle] ${CRA_TOKEN_MISSING_NOTE}`.
136
+ const missingChunks = [
137
+ `[reticle] ${TOKEN_VAR} is not set, so Reticle `,
138
+ 'cannot pair with the daemon. The pairing token is ',
139
+ `per-machine and ${CRA_ENV_PATH} is gitignored by `,
140
+ "CRA's template, so it does not survive a clone. Run ",
141
+ `\`${CLI} init\` in this project to write it for this `,
142
+ 'machine.',
143
+ ].map((c) => JSON.stringify(c));
144
+ // Eight spaces match the indent inside `console.error(` below.
145
+ const missingExpr = missingChunks.join(' +\n ');
146
+ // `export {}` is a TypeScript empty-module marker. A `.js` file with it is fine under Babel, but
147
+ // the workaround reporters used was to drop it when renaming to `.js` — keep the JS emit clean.
148
+ const trailer = typescript ? '\nexport {};\n' : '\n';
116
149
  return `// Dev-only: connect Reticle. Imported for its side effect from src/index.tsx.
117
150
  //
118
- // CRA's public/index.html is a static template the bundler never processes for modules, so the
119
- // connect cannot live there. The pairing token arrives through REACT_APP_RETICLE_TOKEN because
120
- // REACT_APP_* is the only thing CRA inlines into browser code.
151
+ // CRA's public/index.html is a static template the bundler never processes for
152
+ // modules, so the connect cannot live there. The pairing token arrives through
153
+ // REACT_APP_RETICLE_TOKEN because REACT_APP_* is the only thing CRA inlines
154
+ // into browser code.
121
155
  if (process.env.NODE_ENV === 'development') {
122
156
  void import('@reticlehq/react').then(({ reticle, install }) => {
123
157
  install();
124
158
  const token = process.env.${TOKEN_VAR} ?? '';
125
- // Written by \`reticle init\` from the daemon that was live when it ran, and refreshed by
126
- // re-running it. CRA gives us no hook to resolve this at dev-server start, so if the daemon
127
- // moves, re-run \`reticle init\` rather than editing the url below.
159
+ // Written by \`reticle init\` from the daemon that was live when it ran, and
160
+ // refreshed by re-running it. CRA gives us no hook to resolve this at
161
+ // dev-server start, so if the daemon moves, re-run \`reticle init\` rather
162
+ // than editing the url below.
128
163
  const url = process.env.${URL_VAR} ?? '';
129
- // Loud on purpose: without this the only symptom is the bridge's generic auth failure.
130
- if (token.length === 0) console.error(${JSON.stringify(`[reticle] ${CRA_TOKEN_MISSING_NOTE}`)});
164
+ // Loud on purpose: without this the only symptom is the bridge's generic
165
+ // auth failure.
166
+ if (token.length === 0) {
167
+ console.error(
168
+ ${missingExpr},
169
+ );
170
+ }
131
171
  // Still attempt it — a bridge running without a token pairs fine.
132
- reticle.connect({ ${inline}...(url.length > 0 ? { url } : {}), ...(token.length > 0 ? { token } : {}) });
172
+ reticle.connect({
173
+ ${connectFields}
174
+ });
133
175
  });
134
176
  }
135
-
136
- export {};
137
- `;
177
+ ${trailer}`;
138
178
  }
@@ -30,10 +30,10 @@ function bridgeOrigins(port) {
30
30
  * Matches to the end of the directive (`;`) or the end of the enclosing quoted string, which is how
31
31
  * these are written in both a Next `headers()` value and a `<meta content="...">`.
32
32
  */
33
- function connectSrcSources(text) {
33
+ function directiveSources(text, directive) {
34
34
  // Stops at the directive separator or the closing double quote of the enclosing string. Single
35
35
  // quotes are NOT terminators: `'self'` is a source, not the end of the list.
36
- const match = /connect-src([^;"]*)/i.exec(text);
36
+ const match = new RegExp(`${directive}([^;"]*)`, 'i').exec(text);
37
37
  const list = match?.[1];
38
38
  if (list === undefined)
39
39
  return undefined;
@@ -42,6 +42,21 @@ function connectSrcSources(text) {
42
42
  .map((source) => source.trim())
43
43
  .filter((source) => source.length > 0);
44
44
  }
45
+ /**
46
+ * What this policy allows a WebSocket to reach — `connect-src`, or `default-src` when it is absent.
47
+ *
48
+ * The fallback is the CSP spec's, and leaving it out made the check blind to the commonest strict
49
+ * policy there is. MarkText, a production Electron editor, declares
50
+ * `default-src 'self'; script-src 'self'; …` with NO `connect-src`: its WebSockets are restricted to
51
+ * 'self', the bridge is blocked, and this returned "no problem" because it was looking for a
52
+ * directive that was not written. Every fetch-directive falls back to `default-src`; that is what
53
+ * `default` means, and it is why an author does not repeat themselves.
54
+ *
55
+ * Still undefined when NEITHER is present — a policy that constrains neither is not blocking us.
56
+ */
57
+ function connectSrcSources(text) {
58
+ return directiveSources(text, 'connect-src') ?? directiveSources(text, 'default-src');
59
+ }
45
60
  /**
46
61
  * The problem with this app's `connect-src`, or undefined if there is not one.
47
62
  *
@@ -21,5 +21,12 @@ export interface CspDiagnosis {
21
21
  /** Exactly what to add — copy-pasteable, with the port actually in use. */
22
22
  fix: string;
23
23
  }
24
- export declare function diagnoseWebCsp(read: ReadFile, port: number): CspDiagnosis[];
24
+ /**
25
+ * The places a Content-Security-Policy gets written in a JS app.
26
+ *
27
+ * Next config and middleware cover `headers()` and the edge-middleware style; the layouts and the
28
+ * plain `index.html` cover the `<meta http-equiv>` style. Both reported cases were in this list.
29
+ */
30
+ export declare const CSP_FILES: readonly string[];
31
+ export declare function diagnoseWebCsp(read: ReadFile, port: number, alsoCheck?: readonly string[]): CspDiagnosis[];
25
32
  export {};
@@ -18,7 +18,7 @@ import { cspConnectSrcProblem, devCspAddition } from './csp-check.js';
18
18
  * Next config and middleware cover `headers()` and the edge-middleware style; the layouts and the
19
19
  * plain `index.html` cover the `<meta http-equiv>` style. Both reported cases were in this list.
20
20
  */
21
- const CSP_FILES = [
21
+ export const CSP_FILES = [
22
22
  'next.config.js',
23
23
  'next.config.mjs',
24
24
  'next.config.ts',
@@ -31,12 +31,17 @@ const CSP_FILES = [
31
31
  'pages/_document.js',
32
32
  'index.html',
33
33
  'public/index.html',
34
+ // Vite puts the entry HTML at the project root; electron-vite and Tauri put the RENDERER's one
35
+ // under src/. MarkText declares its policy in `src/renderer/index.html`, and a desktop app is the
36
+ // likeliest place to meet a strict CSP at all — Electron's own security guidance asks for one.
37
+ 'src/index.html',
38
+ 'src/renderer/index.html',
34
39
  'vercel.json',
35
40
  'netlify.toml',
36
41
  ];
37
- export function diagnoseWebCsp(read, port) {
42
+ export function diagnoseWebCsp(read, port, alsoCheck = []) {
38
43
  const findings = [];
39
- for (const file of CSP_FILES) {
44
+ for (const file of [...alsoCheck, ...CSP_FILES]) {
40
45
  const source = read(file);
41
46
  if (source === undefined)
42
47
  continue;
@@ -14,6 +14,8 @@
14
14
  * which script, and whether to touch anything at all — and that is what wants testing. Spawning a
15
15
  * process is not.
16
16
  */
17
+ /** Script names a JS project uses to run itself, in the order we would pick them. */
18
+ export declare const DEV_SCRIPT_NAMES: readonly ["dev", "start", "serve"];
17
19
  export declare const DevScriptChoice: {
18
20
  /** Something already answers on the port. Use it; never replace a server the user is running. */
19
21
  readonly ALREADY_SERVING: "already-serving";
@@ -45,4 +47,4 @@ export declare function planDevScript(scripts: Readonly<Record<string, string>>,
45
47
  * parsing attached, and `run.ts` is at its cohesion limit. Returns undefined for anything it cannot
46
48
  * read: this phrasing decides nothing, so it must never be able to break an install.
47
49
  */
48
- export declare function devCommandFrom(pkgRaw: string, packageManager: string): string | undefined;
50
+ export declare function devCommandFrom(pkg: unknown, packageManager: string): string | undefined;
@@ -15,7 +15,8 @@
15
15
  * process is not.
16
16
  */
17
17
  /** Script names a JS project uses to run itself, in the order we would pick them. */
18
- const CANDIDATES = ['dev', 'start', 'serve'];
18
+ export const DEV_SCRIPT_NAMES = ['dev', 'start', 'serve'];
19
+ const CANDIDATES = DEV_SCRIPT_NAMES;
19
20
  export const DevScriptChoice = {
20
21
  /** Something already answers on the port. Use it; never replace a server the user is running. */
21
22
  ALREADY_SERVING: 'already-serving',
@@ -49,11 +50,13 @@ export function planDevScript(scripts, packageManager, serving) {
49
50
  * parsing attached, and `run.ts` is at its cohesion limit. Returns undefined for anything it cannot
50
51
  * read: this phrasing decides nothing, so it must never be able to break an install.
51
52
  */
52
- export function devCommandFrom(pkgRaw, packageManager) {
53
+ export function devCommandFrom(pkg, packageManager) {
53
54
  try {
54
- const parsed = JSON.parse(pkgRaw);
55
- const scripts = 'object' === typeof parsed && null !== parsed
56
- ? (parsed.scripts ?? {})
55
+ // Takes the PARSED manifest. It used to take the raw string and parse it a fourth time — the
56
+ // caller now reads the file once, through a guard, so there is one place a malformed manifest
57
+ // can be noticed and it is not this one.
58
+ const scripts = 'object' === typeof pkg && null !== pkg
59
+ ? (pkg.scripts ?? {})
57
60
  : {};
58
61
  return planDevScript(scripts, packageManager, false).command;
59
62
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Format a file `init` is about to write, using the project's own Prettier when it is installed.
3
+ *
4
+ * Reported from the field (#684): a clean CRA install failed the project's own lint because
5
+ * `src/reticle-dev.ts` was emitted as one long line. The project already had Prettier; init never
6
+ * asked it. Best-effort: resolve Prettier from the app (not from ours — we do not ship it), apply
7
+ * its config, and on any failure return the original content so init never fails because of a
8
+ * formatter. Sync only — `applyEffects` is sync, and Prettier 3's async `format` cannot join here.
9
+ */
10
+ /** True when this write is a connect/dev module lint will treat as project source. */
11
+ export declare function isGeneratedSourcePath(relPath: string): boolean;
12
+ /**
13
+ * Format `content` for `relPath` under `projectRoot`, or return it unchanged.
14
+ *
15
+ * Only the sync Prettier API is used. Prettier 3's async `format` is skipped (returns the original)
16
+ * rather than holding init open on a promise `applyEffects` cannot await.
17
+ */
18
+ export declare function formatGeneratedSource(content: string, relPath: string, projectRoot: string): string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Format a file `init` is about to write, using the project's own Prettier when it is installed.
3
+ *
4
+ * Reported from the field (#684): a clean CRA install failed the project's own lint because
5
+ * `src/reticle-dev.ts` was emitted as one long line. The project already had Prettier; init never
6
+ * asked it. Best-effort: resolve Prettier from the app (not from ours — we do not ship it), apply
7
+ * its config, and on any failure return the original content so init never fails because of a
8
+ * formatter. Sync only — `applyEffects` is sync, and Prettier 3's async `format` cannot join here.
9
+ */
10
+ import { createRequire } from 'node:module';
11
+ import { basename, join } from 'node:path';
12
+ /** Basenames of full-file generators that a project's lint will see. */
13
+ const GENERATED_SOURCE_NAMES = new Set(['reticle-dev.ts', 'reticle-dev.tsx', 'hooks.client.ts']);
14
+ /** True when this write is a connect/dev module lint will treat as project source. */
15
+ export function isGeneratedSourcePath(relPath) {
16
+ return GENERATED_SOURCE_NAMES.has(basename(relPath));
17
+ }
18
+ /**
19
+ * Format `content` for `relPath` under `projectRoot`, or return it unchanged.
20
+ *
21
+ * Only the sync Prettier API is used. Prettier 3's async `format` is skipped (returns the original)
22
+ * rather than holding init open on a promise `applyEffects` cannot await.
23
+ */
24
+ export function formatGeneratedSource(content, relPath, projectRoot) {
25
+ if (!isGeneratedSourcePath(relPath))
26
+ return content;
27
+ try {
28
+ const require = createRequire(join(projectRoot, 'package.json'));
29
+ const prettier = require('prettier');
30
+ const filePath = join(projectRoot, relPath);
31
+ const resolved = prettier.resolveConfig.sync?.(filePath) ?? {};
32
+ const formatted = prettier.format(content, { ...resolved, filepath: filePath });
33
+ // Prettier 2 returns a string; Prettier 3 returns a Promise we cannot join from sync code.
34
+ return 'string' === typeof formatted ? formatted : content;
35
+ }
36
+ catch {
37
+ return content;
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * What to say when the dependency install fails.
3
+ *
4
+ * Split out of plan.ts, which was at the 1000-line backstop. A cohesive unit rather than an
5
+ * arbitrary cut: it is prose about one step's failure modes, and the only thing it needs is which
6
+ * package manager was used.
7
+ */
8
+ import { PackageManager } from './detect.js';
9
+ export declare function installFailureHint(pm: PackageManager): string;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * What to say when the dependency install fails.
3
+ *
4
+ * Split out of plan.ts, which was at the 1000-line backstop. A cohesive unit rather than an
5
+ * arbitrary cut: it is prose about one step's failure modes, and the only thing it needs is which
6
+ * package manager was used.
7
+ */
8
+ import { PackageManager } from './detect.js';
9
+ /**
10
+ * Every package manager fetches, so every one of them can fail for a reason that is nothing to do
11
+ * with the project: offline, a proxy that blocks npmjs, a corporate mirror that is down.
12
+ *
13
+ * The hint used to name only version pinning and pnpm's maturity window. Both are real causes, and
14
+ * neither is that one — so a blocked registry sent the reader through their own dependency versions
15
+ * looking for a problem that was entirely about reachability.
16
+ */
17
+ const REGISTRY_HINT = 'If it could not reach the registry at all (offline, a proxy, a mirror that is down), that is ' +
18
+ 'about reachability and not about this project: check `npm config get registry` and whether this ' +
19
+ 'machine can reach it.';
20
+ export function installFailureHint(pm) {
21
+ if (pm !== PackageManager.PNPM) {
22
+ return `If the version was refused, install the SDK yourself. ${REGISTRY_HINT}`;
23
+ }
24
+ return ('If pnpm reported ERR_PNPM_NO_MATURE_MATCHING_VERSION, its minimumReleaseAge setting is holding ' +
25
+ 'this release back. Either wait out the window, or allow these packages explicitly:\n' +
26
+ ' pnpm config set minimumReleaseAgeExclude "@reticlehq/*"\n' +
27
+ 'Do NOT drop the version pin — unpinned, pnpm installs an older SDK against a newer daemon, and ' +
28
+ `that mismatch surfaces as a -32000 with nothing naming a version.\n${REGISTRY_HINT}`);
29
+ }
@@ -78,5 +78,12 @@ export declare function clientSnippet(spec: ClientSpec): string;
78
78
  * because a fresh Cursor profile has not written `~/.cursor` yet.
79
79
  */
80
80
  export declare function clientMarkerRelPath(spec: ClientSpec): string;
81
+ /**
82
+ * Project-relative directory whose presence signals Cursor is in use for this repo.
83
+ *
84
+ * A fresh Cursor profile has not written `~/.cursor` yet, so the home marker alone misses real
85
+ * users. A project-level `.cursor/` is unambiguous evidence somebody uses Cursor here.
86
+ */
87
+ export declare const CURSOR_PROJECT_MARKER = ".cursor";
81
88
  /** Clients `init` can wire by writing a file — everything but the CLI-registered one. */
82
89
  export declare function fileBackedClients(): readonly ClientSpec[];
@@ -286,6 +286,13 @@ export function clientMarkerRelPath(spec) {
286
286
  const parts = spec.relPath.split('/');
287
287
  return parts.length > 1 ? parts.slice(0, -1).join('/') : spec.relPath;
288
288
  }
289
+ /**
290
+ * Project-relative directory whose presence signals Cursor is in use for this repo.
291
+ *
292
+ * A fresh Cursor profile has not written `~/.cursor` yet, so the home marker alone misses real
293
+ * users. A project-level `.cursor/` is unambiguous evidence somebody uses Cursor here.
294
+ */
295
+ export const CURSOR_PROJECT_MARKER = '.cursor';
289
296
  /** Clients `init` can wire by writing a file — everything but the CLI-registered one. */
290
297
  export function fileBackedClients() {
291
298
  return MCP_CLIENTS.filter((spec) => spec.scope !== ConfigScope.CLI);
@@ -43,7 +43,7 @@ export declare const NPX = "npx";
43
43
  *
44
44
  * So it stays unpinned, and the skew is handled where it actually shows up: the contract fingerprint
45
45
  * makes a real mismatch loud on the next tool result (see version-skew), and a stale entry of our own
46
- * shape is now repaired on re-run rather than reported "already registered" (see cursor.ts).
46
+ * shape is now repaired on re-run rather than reported "already registered" (see mcp-clients.ts).
47
47
  */
48
48
  export declare function npxServerArgs(): string[];
49
49
  /**