@reticlehq/server 2.6.0 → 2.8.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 (276) hide show
  1. package/README.md +14 -5
  2. package/SKILL.md +251 -33
  3. package/dist/bridge/bridge.d.ts +6 -1
  4. package/dist/bridge/bridge.js +27 -4
  5. package/dist/capsule/causal-summary.d.ts +20 -0
  6. package/dist/capsule/causal-summary.js +31 -6
  7. package/dist/capsule/divergence.js +7 -1
  8. package/dist/cli/chromium-hint.d.ts +68 -0
  9. package/dist/cli/chromium-hint.js +145 -0
  10. package/dist/cli/cli-doctor.js +31 -36
  11. package/dist/cli/cli-kill.d.ts +58 -0
  12. package/dist/cli/cli-kill.js +162 -0
  13. package/dist/cli/cli-launch.d.ts +18 -0
  14. package/dist/cli/cli-launch.js +32 -1
  15. package/dist/cli/cli-parse.d.ts +16 -1
  16. package/dist/cli/cli-parse.js +32 -9
  17. package/dist/cli/cli-port.d.ts +8 -0
  18. package/dist/cli/cli-port.js +36 -1
  19. package/dist/cli/cli-verify.js +2 -2
  20. package/dist/cli/doctor-daemon-line.js +10 -1
  21. package/dist/cli/doctor-sessions-line.d.ts +12 -0
  22. package/dist/cli/doctor-sessions-line.js +66 -0
  23. package/dist/cli/port-holder.d.ts +10 -0
  24. package/dist/cli/port-holder.js +26 -2
  25. package/dist/cli.js +139 -23
  26. package/dist/crawl/crawl-tools.js +12 -2
  27. package/dist/crawl/crawl.d.ts +13 -0
  28. package/dist/crawl/crawl.js +50 -2
  29. package/dist/daemon/daemon-resilience.js +2 -2
  30. package/dist/daemon/loopback-alias.d.ts +32 -0
  31. package/dist/daemon/loopback-alias.js +80 -0
  32. package/dist/events/contradictions.d.ts +14 -0
  33. package/dist/events/contradictions.js +26 -17
  34. package/dist/events/predicate-eval.d.ts +26 -3
  35. package/dist/events/predicate-eval.js +284 -95
  36. package/dist/events/predicate-parse.d.ts +1 -1
  37. package/dist/events/predicate-parse.js +18 -4
  38. package/dist/events/predicate.js +170 -18
  39. package/dist/events/ring-buffer.d.ts +8 -0
  40. package/dist/events/ring-buffer.js +34 -1
  41. package/dist/flows/flow-classify.js +1 -1
  42. package/dist/flows/flow-tools.js +12 -4
  43. package/dist/flows/heal-run.js +1 -1
  44. package/dist/honesty/blind-spots.js +6 -2
  45. package/dist/honesty/honesty.d.ts +11 -0
  46. package/dist/honesty/honesty.js +9 -1
  47. package/dist/honesty/unsettled.d.ts +40 -0
  48. package/dist/honesty/unsettled.js +78 -0
  49. package/dist/honesty/verified.d.ts +7 -0
  50. package/dist/honesty/verified.js +27 -4
  51. package/dist/http-server.js +11 -1
  52. package/dist/index.js +29 -2
  53. package/dist/init/agent-rules.d.ts +16 -0
  54. package/dist/init/agent-rules.js +96 -17
  55. package/dist/init/astro-patch.d.ts +2 -1
  56. package/dist/init/astro-patch.js +11 -5
  57. package/dist/init/capabilities.d.ts +22 -0
  58. package/dist/init/capabilities.js +76 -0
  59. package/dist/init/confirm.d.ts +96 -0
  60. package/dist/init/confirm.js +138 -0
  61. package/dist/init/cra.js +3 -2
  62. package/dist/init/detect.d.ts +8 -0
  63. package/dist/init/detect.js +15 -0
  64. package/dist/init/non-js-project.d.ts +27 -0
  65. package/dist/init/non-js-project.js +84 -0
  66. package/dist/init/plan-framework.d.ts +9 -0
  67. package/dist/init/plan-framework.js +72 -16
  68. package/dist/init/plan.d.ts +40 -4
  69. package/dist/init/plan.js +183 -43
  70. package/dist/init/run.d.ts +24 -2
  71. package/dist/init/run.js +181 -46
  72. package/dist/init/slash-command.d.ts +1 -1
  73. package/dist/init/slash-command.js +46 -6
  74. package/dist/init/snippets.d.ts +64 -5
  75. package/dist/init/snippets.js +220 -31
  76. package/dist/init/vite-config.js +29 -3
  77. package/dist/input/network-mock-tools.d.ts +19 -0
  78. package/dist/input/network-mock-tools.js +29 -4
  79. package/dist/license/license.js +1 -1
  80. package/dist/mcp/mcp-proxy.d.ts +69 -3
  81. package/dist/mcp/mcp-proxy.js +215 -23
  82. package/dist/mcp/mcp.js +58 -7
  83. package/dist/mcp/proxy-lifecycle.d.ts +20 -1
  84. package/dist/mcp/proxy-lifecycle.js +22 -1
  85. package/dist/pool/browser-pool.d.ts +22 -2
  86. package/dist/pool/browser-pool.js +44 -4
  87. package/dist/pool/playwright-launcher.js +10 -2
  88. package/dist/project/learned-routes.d.ts +24 -0
  89. package/dist/project/learned-routes.js +63 -0
  90. package/dist/project/project-store.d.ts +6 -0
  91. package/dist/project/project-store.js +25 -1
  92. package/dist/session/live-control-tools.js +39 -2
  93. package/dist/session/live-control.d.ts +7 -9
  94. package/dist/session/live-control.js +34 -1
  95. package/dist/session/no-session-diagnosis.d.ts +23 -4
  96. package/dist/session/no-session-diagnosis.js +128 -44
  97. package/dist/session/no-session-watch.js +12 -0
  98. package/dist/session/ref-provenance.d.ts +54 -0
  99. package/dist/session/ref-provenance.js +61 -0
  100. package/dist/session/review-store.d.ts +19 -0
  101. package/dist/session/review-store.js +34 -5
  102. package/dist/session/session-lease.js +1 -1
  103. package/dist/session/session-manager.d.ts +9 -0
  104. package/dist/session/session-manager.js +11 -0
  105. package/dist/session/session.d.ts +8 -0
  106. package/dist/session/session.js +12 -0
  107. package/dist/status-payload.d.ts +13 -1
  108. package/dist/status-payload.js +6 -1
  109. package/dist/telemetry/app-instrumented.d.ts +37 -0
  110. package/dist/telemetry/app-instrumented.js +54 -0
  111. package/dist/telemetry/automation-hint.d.ts +17 -0
  112. package/dist/telemetry/automation-hint.js +71 -0
  113. package/dist/telemetry/bug-found.d.ts +0 -20
  114. package/dist/telemetry/bug-found.js +38 -21
  115. package/dist/telemetry/cli-telemetry.js +7 -1
  116. package/dist/telemetry/daemon-telemetry.d.ts +4 -4
  117. package/dist/telemetry/daemon-telemetry.js +8 -4
  118. package/dist/telemetry/feedback.js +1 -1
  119. package/dist/telemetry/init-telemetry.js +9 -1
  120. package/dist/telemetry/install-source.d.ts +36 -0
  121. package/dist/telemetry/install-source.js +40 -0
  122. package/dist/telemetry/run-telemetry.js +4 -1
  123. package/dist/telemetry/session-metrics.d.ts +3 -3
  124. package/dist/telemetry/session-metrics.js +9 -4
  125. package/dist/telemetry/telemetry.d.ts +7 -1
  126. package/dist/telemetry/telemetry.js +17 -2
  127. package/dist/telemetry/tool-refused.d.ts +19 -0
  128. package/dist/telemetry/tool-refused.js +55 -0
  129. package/dist/telemetry/verification-of.js +72 -8
  130. package/dist/temp-dir.d.ts +27 -0
  131. package/dist/temp-dir.js +46 -0
  132. package/dist/tools/act-tools.js +77 -11
  133. package/dist/tools/assert-grade.d.ts +1 -1
  134. package/dist/tools/assert-grade.js +27 -5
  135. package/dist/tools/assert-verdict.js +25 -1
  136. package/dist/tools/contract-tools.js +4 -0
  137. package/dist/tools/dynamic-tools.js +1 -1
  138. package/dist/tools/error-recovery.d.ts +10 -0
  139. package/dist/tools/error-recovery.js +70 -5
  140. package/dist/tools/feedback-invite.js +1 -1
  141. package/dist/tools/invoke-tool.js +149 -32
  142. package/dist/tools/lease-tools.d.ts +29 -1
  143. package/dist/tools/lease-tools.js +58 -5
  144. package/dist/tools/navigate-result.js +1 -1
  145. package/dist/tools/observe-tools.js +1 -1
  146. package/dist/tools/observed-nothing.js +1 -1
  147. package/dist/tools/react-grace.d.ts +48 -0
  148. package/dist/tools/react-grace.js +87 -0
  149. package/dist/tools/read-tools.js +20 -4
  150. package/dist/tools/settle-in-flight.d.ts +79 -0
  151. package/dist/tools/settle-in-flight.js +106 -0
  152. package/dist/tools/snapshot-delta.d.ts +7 -0
  153. package/dist/tools/snapshot-delta.js +130 -6
  154. package/dist/tools/tool-kit.d.ts +52 -5
  155. package/dist/tools/tool-kit.js +55 -24
  156. package/dist/tools/tool-surface.js +28 -9
  157. package/dist/tools/tools.js +38 -4
  158. package/dist/visual/capture-cleanup.d.ts +16 -0
  159. package/dist/visual/capture-cleanup.js +39 -0
  160. package/dist/visual/visual-tools.d.ts +18 -0
  161. package/dist/visual/visual-tools.js +24 -5
  162. package/docs/README.md +56 -11
  163. package/docs/actions.mdx +87 -0
  164. package/docs/agent-cheatsheet.md +102 -26
  165. package/docs/architecture.md +46 -35
  166. package/docs/benchmarks.md +76 -72
  167. package/docs/best-practices.mdx +211 -0
  168. package/docs/capabilities.mdx +387 -0
  169. package/docs/cli/affected.mdx +51 -0
  170. package/docs/cli/capsules.mdx +69 -0
  171. package/docs/cli/cloud.mdx +127 -0
  172. package/docs/cli/doctor.mdx +76 -0
  173. package/docs/cli/drive.mdx +55 -0
  174. package/docs/cli/feedback.mdx +67 -0
  175. package/docs/cli/gate.mdx +64 -0
  176. package/docs/cli/help.mdx +92 -0
  177. package/docs/cli/hunt.mdx +57 -0
  178. package/docs/cli/identify.mdx +62 -0
  179. package/docs/cli/init.mdx +94 -0
  180. package/docs/cli/kill.mdx +91 -0
  181. package/docs/cli/license.mdx +48 -0
  182. package/docs/cli/mcp.mdx +61 -0
  183. package/docs/cli/open.mdx +68 -0
  184. package/docs/cli/restart.mdx +60 -0
  185. package/docs/cli/rollback.mdx +35 -0
  186. package/docs/cli/serve.mdx +62 -0
  187. package/docs/cli/status.mdx +64 -0
  188. package/docs/cli/stop.mdx +56 -0
  189. package/docs/cli/telemetry.mdx +57 -0
  190. package/docs/cli/update.mdx +52 -0
  191. package/docs/cli/verify.mdx +57 -0
  192. package/docs/cli/version.mdx +39 -0
  193. package/docs/cli/watch.mdx +55 -0
  194. package/docs/cli.mdx +219 -0
  195. package/docs/debugging.md +13 -9
  196. package/docs/deploy-checks.md +15 -11
  197. package/docs/desktop-apps.md +49 -40
  198. package/docs/desktop.mdx +188 -0
  199. package/docs/docs.json +363 -0
  200. package/docs/enterprise.md +20 -16
  201. package/docs/faq.mdx +88 -0
  202. package/docs/fixtures.md +11 -7
  203. package/docs/flows.md +26 -22
  204. package/docs/for-agents.md +71 -0
  205. package/docs/frameworks.mdx +199 -0
  206. package/docs/gate-plan.md +45 -41
  207. package/docs/gates.md +23 -19
  208. package/docs/getting-started.md +76 -65
  209. package/docs/human-control.md +22 -18
  210. package/docs/index.mdx +101 -0
  211. package/docs/install-agentic.mdx +215 -0
  212. package/docs/install-manual.mdx +194 -0
  213. package/docs/instrumentation.mdx +232 -0
  214. package/docs/integration-patterns.md +38 -34
  215. package/docs/local-registry.md +16 -12
  216. package/docs/multi-agent-testing.md +16 -12
  217. package/docs/packages/babel-plugin.mdx +70 -0
  218. package/docs/packages/browser.mdx +111 -0
  219. package/docs/packages/core.mdx +106 -0
  220. package/docs/packages/electron.mdx +87 -0
  221. package/docs/packages/eslint-plugin.mdx +84 -0
  222. package/docs/packages/next.mdx +91 -0
  223. package/docs/packages/react.mdx +91 -0
  224. package/docs/packages/server.mdx +98 -0
  225. package/docs/packages/tauri.mdx +63 -0
  226. package/docs/packages/test.mdx +108 -0
  227. package/docs/packages/vite-plugin.mdx +96 -0
  228. package/docs/packages.mdx +217 -0
  229. package/docs/platform-integration.md +33 -26
  230. package/docs/predicates.mdx +179 -0
  231. package/docs/quickstart.mdx +211 -0
  232. package/docs/recipes.mdx +242 -0
  233. package/docs/skill-file.mdx +105 -0
  234. package/docs/state-management.mdx +197 -0
  235. package/docs/style.css +14 -0
  236. package/docs/system-map.md +47 -32
  237. package/docs/telemetry-contract.md +101 -46
  238. package/docs/telemetry-events.mdx +430 -0
  239. package/docs/telemetry.md +63 -50
  240. package/docs/testing.md +10 -6
  241. package/docs/token-efficiency.md +30 -26
  242. package/docs/tools-act-and-wait.mdx +123 -0
  243. package/docs/tools-act-sequence.mdx +120 -0
  244. package/docs/tools-act.mdx +82 -0
  245. package/docs/tools-assert.mdx +70 -0
  246. package/docs/tools-console.mdx +59 -0
  247. package/docs/tools-inspect.mdx +85 -0
  248. package/docs/tools-navigate.mdx +63 -0
  249. package/docs/tools-network.mdx +86 -0
  250. package/docs/tools-observe.mdx +89 -0
  251. package/docs/tools-overview.mdx +157 -0
  252. package/docs/tools-query.mdx +82 -0
  253. package/docs/tools-session-and-feedback.mdx +91 -0
  254. package/docs/tools-sessions.mdx +65 -0
  255. package/docs/tools-snapshot.mdx +75 -0
  256. package/docs/tools-state.mdx +81 -0
  257. package/docs/tools-tools-and-run.mdx +125 -0
  258. package/docs/tools-wait-for.mdx +66 -0
  259. package/docs/troubleshooting.mdx +193 -0
  260. package/docs/usage.md +175 -153
  261. package/docs/vs-chrome-devtools-mcp.mdx +82 -0
  262. package/docs/vs-playwright-mcp.mdx +85 -0
  263. package/docs/vs-screenshots.mdx +96 -0
  264. package/docs/what-is-reticle.mdx +63 -0
  265. package/docs/why-reticle.mdx +107 -0
  266. package/package.json +2 -2
  267. package/docs/matrix/2.5.0/claude-code-machine.json +0 -19
  268. package/docs/matrix/2.5.0/codex-machine.json +0 -20
  269. package/docs/matrix/2.5.0/cursor-machine.json +0 -22
  270. package/docs/matrix/2.5.0/gemini-machine.json +0 -22
  271. package/docs/matrix/2.5.0/opencode-machine.json +0 -22
  272. package/docs/matrix/2.5.0/vscode-machine.json +0 -22
  273. package/docs/matrix/2.5.0/windsurf-machine.json +0 -22
  274. package/docs/matrix/MATRIX.md +0 -26
  275. package/docs/matrix/README.md +0 -59
  276. package/docs/mint.json +0 -61
package/dist/init/run.js CHANGED
@@ -4,16 +4,17 @@
4
4
  * access goes through `InitIo` so the orchestration is unit-testable with an in-memory IO.
5
5
  */
6
6
  import { dirname, join } from 'node:path';
7
+ import { noPackageJsonMessage } from './non-js-project.js';
7
8
  import { spanSync } from '../trace.js';
8
9
  import { readFileSync } from 'node:fs';
9
10
  import { homedir } from 'node:os';
10
- import { detect, Framework, namesAPackageManager } from './detect.js';
11
+ import { detect, Framework, namesAPackageManager, UiLibrary } from './detect.js';
11
12
  import { wasMcpRegistered } from './mcp-registered.js';
12
13
  import { pickAstroHost } from './astro-host.js';
13
14
  import { workspaceParents } from './workspace-apps.js';
14
15
  import { chooseWorkspaceApp } from './app-choice.js';
15
16
  import { isConnectStep } from './plan.js';
16
- import { CLI, CURSOR_RULE_PATH } from './agent-rules.js';
17
+ import { CLI, CURSOR_RULE_PATH, RETICLE_MD_PATH } from './agent-rules.js';
17
18
  import { CRA_ENV_PATH } from './cra.js';
18
19
  /** CRA's bundled entry, in the order create-react-app itself generates them. */
19
20
  const CRA_ENTRY_CANDIDATES = ['src/index.tsx', 'src/index.jsx', 'src/index.ts', 'src/index.js'];
@@ -43,11 +44,11 @@ function readPairingToken() {
43
44
  import { DEPS_TARGET, frameworkPackages, MCP_TARGET, buildPlan, StepStatus, } from './plan.js';
44
45
  import { claudeAvailableProbe, claudeExistsProbe } from './mcp.js';
45
46
  import { reticleDevLocation } from './next-patch.js';
46
- import { scanTestids, storeHints } from './capabilities.js';
47
+ import { scanTestids, storeHints, scanStores } from './capabilities.js';
47
48
  import { CURSOR_DIR_RELPATH, CURSOR_MCP_RELPATH } from './cursor.js';
48
49
  import { fileBackedClients, clientMarkerRelPath, ConfigScope, McpClient } from './mcp-clients.js';
49
50
  import { deriveProjectId, packageName } from './project-id.js';
50
- import { VITE_DEV_MODULE_PATH } from './snippets.js';
51
+ import { VITE_DEV_MODULE_PATH, connectArgWithToken, staticPageSnippet } from './snippets.js';
51
52
  import { CLAUDE_COMMAND_PATH, CURSOR_COMMAND_PATH } from './slash-command.js';
52
53
  import { SERVER_VERSION } from '../version/server-version.js';
53
54
  import { InitFailure, reportInitOutcome } from '../telemetry/init-telemetry.js';
@@ -119,24 +120,72 @@ const NEXT_PAGES_APP_CANDIDATES = [
119
120
  'src/pages/_app.js',
120
121
  ];
121
122
  const SVELTEKIT_HOOKS = 'src/hooks.client.ts';
122
- /** Where an app's components live. Bounded on purpose — this is a hint, not an index of the repo. */
123
- const SOURCE_DIRS = ['src', 'src/components', 'src/pages', 'app', 'components'];
124
123
  const SOURCE_FILE = /\.(tsx|jsx|ts|js|svelte|vue|astro)$/;
125
124
  /** Files read for the testid scan. A capabilities block is a hint; reading a whole repo for it is not. */
126
125
  const MAX_SCANNED_FILES = 200;
127
- /** Read a bounded set of the app's source files, for the `data-testid` scan. */
126
+ /** Directories that never hold the app's own source, and are expensive or misleading to read. */
127
+ const NOT_SOURCE_DIRS = new Set([
128
+ 'node_modules',
129
+ 'dist',
130
+ 'build',
131
+ 'out',
132
+ 'coverage',
133
+ 'target',
134
+ 'public',
135
+ 'static',
136
+ '.next',
137
+ '.svelte-kit',
138
+ '.nuxt',
139
+ ]);
140
+ /** How far below the app root to look. Deep enough for `modules/users/UserList.tsx`, not a repo crawl. */
141
+ const MAX_SCAN_DEPTH = 5;
142
+ /**
143
+ * Read a bounded set of the app's source files, for the `data-testid` scan.
144
+ *
145
+ * This used to walk the fixed list `src, src/components, src/pages, app, components`. Reported from
146
+ * the field (#318) by a repo whose app is at `src/admin`: the scan read directories that were not
147
+ * the app's and correctly reported finding nothing in them, so `init` said "no data-testid values
148
+ * yet" about an app with several — which makes an agent go and write the ones already there. That
149
+ * list had already grown once for a `frontend/` app, and a third report of the same shape is what
150
+ * says the answer is not another name in a list. So: walk the app root, bounded by depth and by the
151
+ * same file cap as before.
152
+ */
128
153
  function readSourceFiles(io) {
129
154
  const out = [];
130
- for (const dir of SOURCE_DIRS) {
155
+ const read = (path) => {
156
+ const content = io.readFile(path);
157
+ if (content !== null)
158
+ out.push({ path, source: content });
159
+ };
160
+ for (const name of io.rootFiles()) {
161
+ if (out.length >= MAX_SCANNED_FILES)
162
+ return out;
163
+ if (SOURCE_FILE.test(name))
164
+ read(name);
165
+ }
166
+ const walk = (dir, depth) => {
131
167
  for (const name of io.listFiles(dir)) {
132
- if (!SOURCE_FILE.test(name))
133
- continue;
134
- const content = io.readFile(`${dir}/${name}`);
135
- if (content !== null)
136
- out.push(content);
137
168
  if (out.length >= MAX_SCANNED_FILES)
138
- return out;
169
+ return;
170
+ if (SOURCE_FILE.test(name))
171
+ read(`${dir}/${name}`);
139
172
  }
173
+ if (depth >= MAX_SCAN_DEPTH)
174
+ return;
175
+ for (const sub of io.listDirs(dir)) {
176
+ if (out.length >= MAX_SCANNED_FILES)
177
+ return;
178
+ if (sub.startsWith('.') || NOT_SOURCE_DIRS.has(sub))
179
+ continue;
180
+ walk(`${dir}/${sub}`, depth + 1);
181
+ }
182
+ };
183
+ for (const dir of io.listDirs('.')) {
184
+ if (out.length >= MAX_SCANNED_FILES)
185
+ return out;
186
+ if (dir.startsWith('.') || NOT_SOURCE_DIRS.has(dir))
187
+ continue;
188
+ walk(dir, 1);
140
189
  }
141
190
  return out;
142
191
  }
@@ -203,6 +252,17 @@ function firstPresent(files, candidates) {
203
252
  return c;
204
253
  return null;
205
254
  }
255
+ /**
256
+ * The directory the human's agent runs in, when it is not the app's directory.
257
+ *
258
+ * `undefined` for a single-package repo, which keeps every agent-file path project-relative exactly
259
+ * as it was — the two roots are the same there, which is why writing them beside the app went
260
+ * unnoticed until a repo with its app at `src/admin` reported it.
261
+ */
262
+ function agentRootOf(options) {
263
+ const root = options.agentRoot;
264
+ return root === undefined || root === options.cwd ? undefined : root;
265
+ }
206
266
  function gatherPlanInput(options, io, pkgRaw) {
207
267
  const pkg = JSON.parse(pkgRaw);
208
268
  // Stable identity derived from the app's package.json name + root, so it survives port changes.
@@ -282,6 +342,10 @@ function gatherPlanInput(options, io, pkgRaw) {
282
342
  // Where the component goes depends on WHICH router mounts it: `pages/` routes on presence, so a
283
343
  // component there becomes a broken route; `app/` routes on filename, so a sibling is inert.
284
344
  const devLocation = reticleDevLocation(layoutPath ?? 'app/layout.tsx', detection.typescript);
345
+ // Read once: both the testid scan and the store scan want the same bounded set of files.
346
+ const sourceFiles = readSourceFiles(io);
347
+ const agentRoot = agentRootOf(options);
348
+ const agentFile = (relPath) => agentRoot === undefined ? relPath : join(agentRoot, relPath);
285
349
  return {
286
350
  detection,
287
351
  claudeCli,
@@ -302,8 +366,10 @@ function gatherPlanInput(options, io, pkgRaw) {
302
366
  ? { path: layoutPath, source: layoutSource }
303
367
  : null,
304
368
  // Capabilities: scanned, never asked for. Bounded — a hint for the agent, not a repo index.
305
- testids: scanTestids(readSourceFiles(io)),
369
+ testids: scanTestids(sourceFiles.map((f) => f.source)),
306
370
  storeHints: storeHints(dependencyNames(pkg)),
371
+ foundStores: scanStores(sourceFiles, dependencyNames(pkg)),
372
+ nextFoundStores: scanStores(sourceFiles, dependencyNames(pkg), dirname(devLocation.path)),
307
373
  viteDevModuleExists: io.exists(VITE_DEV_MODULE_PATH),
308
374
  nextReticleDevPath: devLocation.path,
309
375
  nextReticleDevImport: devLocation.importSpecifier,
@@ -313,12 +379,18 @@ function gatherPlanInput(options, io, pkgRaw) {
313
379
  craEnv: io.readFile(CRA_ENV_PATH),
314
380
  pairingToken: readPairingToken(),
315
381
  reticleConfigExists: io.exists('.reticle.json'),
316
- // Read the agent instruction files so the rule merge stays idempotent across re-runs.
317
- claudeMdContent: io.readFile('CLAUDE.md'),
318
- agentsMdContent: io.readFile('AGENTS.md'),
319
- cursorRuleContent: io.readFile(CURSOR_RULE_PATH),
320
- claudeCommandContent: io.readFile(CLAUDE_COMMAND_PATH),
321
- cursorCommandContent: io.readFile(CURSOR_COMMAND_PATH),
382
+ // The CONTENT, so a config that exists can be checked rather than trusted — a `"port"` set to
383
+ // the app's own dev-server port used to survive every re-run of `init`.
384
+ reticleConfigSource: io.readFile('.reticle.json'),
385
+ // Read the agent instruction files so the rule merge stays idempotent across re-runs — from the
386
+ // agent's own root, or the merge would idempotently check a file it is not going to write.
387
+ claudeMdContent: io.readFile(agentFile('CLAUDE.md')),
388
+ agentsMdContent: io.readFile(agentFile('AGENTS.md')),
389
+ reticleMdContent: io.readFile(agentFile(RETICLE_MD_PATH)),
390
+ cursorRuleContent: io.readFile(agentFile(CURSOR_RULE_PATH)),
391
+ claudeCommandContent: io.readFile(agentFile(CLAUDE_COMMAND_PATH)),
392
+ cursorCommandContent: io.readFile(agentFile(CURSOR_COMMAND_PATH)),
393
+ ...(agentRoot === undefined ? {} : { agentFileRoot: agentRoot }),
322
394
  options: {
323
395
  port: options.port,
324
396
  mcp: options.mcp,
@@ -402,26 +474,70 @@ export const FEEDBACK_HINT = 'Anything wrong, missing, or awkward — in this se
402
474
  'one thing that decides what gets fixed:\n' +
403
475
  ` ${CLI} feedback --agent --kind <bug|gap|ambiguity|feature_request|improvement> "what happened" (agents)\n` +
404
476
  ` ${CLI} feedback "what worked, what didn't" (humans)`;
405
- function restartHint(framework) {
477
+ function devServerRestart(framework) {
406
478
  if (framework === Framework.NEXT)
407
- return 'Restart `next dev`, then ask your agent: "List Reticle sessions".';
479
+ return 'Restart `next dev`';
408
480
  if (framework === Framework.VITE)
409
- return 'Restart `vite`, then ask your agent: "List Reticle sessions".';
481
+ return 'Restart `vite`';
410
482
  if (framework === Framework.ASTRO)
411
- return 'Restart `astro dev`, then ask your agent: "List Reticle sessions".';
483
+ return 'Restart `astro dev`';
412
484
  if (framework === Framework.SVELTEKIT)
413
- return 'Restart your dev server (`npm run dev`), then ask your agent: "List Reticle sessions".';
414
- return 'Reload your app on localhost, then ask your agent: "List Reticle sessions".';
485
+ return 'Restart your dev server (`npm run dev`)';
486
+ return 'Reload your app on localhost';
487
+ }
488
+ /**
489
+ * The last two lines a user reads, and the order matters.
490
+ *
491
+ * This used to say only "Restart <dev server>, then ask your agent: List Reticle sessions" — and
492
+ * asking the agent was the one thing that could not work yet. `init` registers the MCP server, but
493
+ * an agent client reads its tool list when it STARTS and never re-reads it, so the session that
494
+ * just ran `init` has no `reticle_*` tools however clean the install was. The user follows the
495
+ * instruction, the agent answers "unknown tool", and the obvious conclusion is that the install
496
+ * failed.
497
+ *
498
+ * So the reload is named FIRST, before the sentence that depends on it. Omitted entirely when this
499
+ * run did not register MCP (`--no-mcp`), where it would be advice about something we did not do.
500
+ */
501
+ function restartHint(framework, mcpRegistered) {
502
+ const dev = `${devServerRestart(framework)}.`;
503
+ // NAME THE COMMAND THAT PROVES IT, not one that merely asks.
504
+ //
505
+ // `init` writes files and stops; the install is not finished until an app carrying the SDK has
506
+ // actually dialled the daemon, and nothing here confirmed that. The field shape is unambiguous:
507
+ // people complete the agent half, never complete the app half, and keep a daemon running for
508
+ // weeks with nothing to drive — so this is the last instruction most of them read.
509
+ //
510
+ // "Ask your agent: List Reticle sessions" asks a question whose failure is a dead end. `reticle
511
+ // status` ANSWERS it: as of 2.7.0 it reports the session, or says why there is none — no app
512
+ // running, an app running that never dialled us, a tab that closed — with the fix for each.
513
+ const prove = 'Then run `npx @reticlehq/server status` — it confirms the app connected, or says exactly why ' +
514
+ 'it has not.';
515
+ if (!mcpRegistered)
516
+ return `${dev} ${prove}`;
517
+ return (`${dev}\n` +
518
+ 'Then restart your agent so it picks up the new MCP server — restart Claude Code, reload the ' +
519
+ 'window in Cursor, or hit Start in `.vscode/mcp.json` in VS Code.\n' +
520
+ 'The tools only appear after that: your agent read its server list before Reticle existed, ' +
521
+ 'and no slash command re-reads it (`/mcp` manages servers already loaded, so it cannot pick ' +
522
+ 'up a new one). This is once per machine — Reticle is registered globally, so every later ' +
523
+ 'project starts with the tools already there.\n' +
524
+ `${prove}\n` +
525
+ 'Once it shows a session, ask your agent to drive a flow — that is the install finished.');
415
526
  }
416
527
  const SKIPPED_DETAIL = 'skipped — the dependency install above failed, and wiring the app to a package that is not ' +
417
528
  'installed stops it booting. Run that install, then re-run `reticle init`.';
418
- function report(plan, dryRun, failed, skipped, degraded, io, projectDir) {
419
- io.print(dryRun ? 'reticle init (dry run — no files written)' : 'reticle init');
529
+ function report(plan, dryRun, failed, skipped, degraded, io, projectDir, agentRoot) {
530
+ io.print(dryRun ? 'reticle init (dry run, no files written)' : 'reticle init');
420
531
  // Every path below is printed RELATIVE, and until now nothing said what to. Reported from the
421
532
  // field as "[✓] Reticle config → .reticle.json" followed by the file not being there: the app was
422
533
  // in `frontend/`, init redirected into it, and the report's `.reticle.json` was true about a
423
534
  // directory the reader was not standing in. One line makes every path in the report unambiguous.
424
535
  io.print(` in ${projectDir}`);
536
+ // The agent files go where the AGENT is, which after a redirect is not where the app is. Said out
537
+ // loud because it changes where `/reticle` will exist, and a reader who assumes one directory for
538
+ // everything goes looking in the wrong one (#318).
539
+ if (agentRoot !== undefined)
540
+ io.print(` agent files in ${agentRoot} (where /reticle will exist)`);
425
541
  io.print('');
426
542
  let applied = 0;
427
543
  let manual = 0;
@@ -470,7 +586,7 @@ function report(plan, dryRun, failed, skipped, degraded, io, projectDir) {
470
586
  '"no browser session connected" until then. Everything else is already in place.');
471
587
  io.print('');
472
588
  }
473
- io.print(restartHint(plan.framework));
589
+ io.print(restartHint(plan.framework, wasMcpRegistered(resolvedStatus(plan, MCP_TARGET, failed, skipped))));
474
590
  return { ok: !connectPending, applied, manual };
475
591
  }
476
592
  /**
@@ -496,8 +612,8 @@ function report(plan, dryRun, failed, skipped, degraded, io, projectDir) {
496
612
  *
497
613
  * Reading node_modules answers the real question and costs one `exists` call per package.
498
614
  */
499
- function sdkPackagesPresent(framework, io) {
500
- const packages = frameworkPackages(framework);
615
+ function sdkPackagesPresent(framework, uiLibrary, io) {
616
+ const packages = frameworkPackages(framework, uiLibrary);
501
617
  return (packages.length > 0 &&
502
618
  packages.every((p) => io.exists(`${NODE_MODULES_DIR}/${p}/${PACKAGE_JSON}`)));
503
619
  }
@@ -558,7 +674,7 @@ function applyEffects(plan, io) {
558
674
  failed.add(s.target);
559
675
  // A failed install only blocks the wiring when the packages are genuinely ABSENT. See
560
676
  // sdkPackagesPresent: the guard protects "the import resolves", not "our subprocess exited 0".
561
- if (s.target === DEPS_TARGET && !sdkPackagesPresent(plan.framework, io))
677
+ if (s.target === DEPS_TARGET && !sdkPackagesPresent(plan.framework, plan.uiLibrary, io))
562
678
  installFailed = true;
563
679
  }
564
680
  }
@@ -620,7 +736,14 @@ function redirectToWorkspaceApp(options, io, pkgRaw) {
620
736
  }
621
737
  io.print(`No app in this directory — wiring ${target} instead.`);
622
738
  io.print('');
623
- return runInit({ ...options, cwd: join(options.cwd, target), redirected: true }, io.scoped(target));
739
+ return runInit({
740
+ ...options,
741
+ cwd: join(options.cwd, target),
742
+ redirected: true,
743
+ // Where the human is STANDING, kept across the redirect: their agent session runs here, so
744
+ // this is the only place a `/reticle` command file can be found by it.
745
+ agentRoot: options.agentRoot ?? options.cwd,
746
+ }, io.scoped(target));
624
747
  }
625
748
  export function runInit(options, io) {
626
749
  const result = runInitSteps(options, io);
@@ -655,8 +778,17 @@ function runInitSteps(options, io) {
655
778
  if (redirectedEarly !== null)
656
779
  return redirectedEarly;
657
780
  if (null === pkgRaw) {
658
- io.print('No package.json found here, and no app directory beneath it either. Run `reticle init` from ' +
659
- "your app's directory, or from a repo root that contains it.");
781
+ io.print(
782
+ // Two genuinely different situations used to share one sentence: a JS developer in the wrong
783
+ // directory, and a project that is not JavaScript at all. The second reads the old wording as
784
+ // a path problem and goes looking for a directory that cannot exist — reported from a
785
+ // Streamlit app, where the search continued into hunting for a browser bundle to inject by
786
+ // hand before the real answer surfaced.
787
+ noPackageJsonMessage((file) => io.exists(join(options.cwd, file))));
788
+ // The message says "add the snippet below". Print the snippet, or the message is the same
789
+ // broken promise in the other direction. `connectArg` carries the port; there is no projectId
790
+ // to bake, because a projectId is derived from the package.json that does not exist here.
791
+ io.print(staticPageSnippet(connectArgWithToken(options.port, undefined, readPairingToken())));
660
792
  // The onboarding funnel had NO instrumentation, so a setup that died here was indistinguishable
661
793
  // from someone who never ran the command — the two failure modes with the most different fixes.
662
794
  reportInitOutcome({ ok: false, reason: InitFailure.NO_PACKAGE_JSON });
@@ -670,16 +802,19 @@ function runInitSteps(options, io) {
670
802
  ? { failed: new Set(), skipped: new Set(), degraded: new Map() }
671
803
  : spanSync('init.apply', { steps: plan.steps.length }, () => applyEffects(plan, io));
672
804
  const { failed, skipped, degraded } = effects;
673
- const result = report(plan, options.dryRun, failed, skipped, degraded, io, options.cwd);
805
+ const result = report(plan, options.dryRun, failed, skipped, degraded, io, options.cwd, agentRootOf(options));
674
806
  // A dry run is a preview, not an outcome — reporting it would inflate both success and failure.
675
- if (!options.dryRun) {
676
- reportInitOutcome({
677
- ok: result.ok,
678
- ...(result.ok ? {} : { reason: classifyInitFailure(failed) }),
679
- stack: plan.framework,
680
- // The step's REAL final status, not the absence of a failure — see mcp-registered.
681
- mcpRegistered: wasMcpRegistered(resolvedStatus(plan, MCP_TARGET, failed, skipped)),
682
- });
683
- }
807
+ if (options.dryRun)
808
+ return result;
809
+ const outcome = {
810
+ ok: result.ok,
811
+ ...(result.ok ? {} : { reason: classifyInitFailure(failed) }),
812
+ stack: plan.framework,
813
+ // The step's REAL final status, not the absence of a failure — see mcp-registered.
814
+ mcpRegistered: wasMcpRegistered(resolvedStatus(plan, MCP_TARGET, failed, skipped)),
815
+ };
816
+ if (true === options.deferOutcome)
817
+ return { ...result, outcome };
818
+ reportInitOutcome(outcome);
684
819
  return result;
685
820
  }
@@ -24,4 +24,4 @@ export declare const CURSOR_COMMAND_PATH = ".cursor/commands/reticle.md";
24
24
  * are told apart.
25
25
  */
26
26
  export declare const SLASH_COMMAND_SIGNATURE = "description: Verify this app in the browser with Reticle";
27
- export declare const SLASH_COMMAND_BODY = "---\ndescription: Verify this app in the browser with Reticle \u2014 drive one real flow and report what happened.\n---\n\nVerify this running app with Reticle. Drive it; do not read the code and guess.\n\n## Pick ONE flow\n\nNot the whole app. Pick the single most important flow you can complete in a handful of steps \u2014 the\none a user would do first (sign in, search, add to cart, submit the form). If the user named a flow or\njust changed some code, use that one instead.\n\nState which flow you picked in one line before you start.\n\n## Drive it where the human can see\n\n1. `reticle_sessions` \u2014 find the connected tab. None? Tell the user to run their dev server and open\n the app, then stop. Reticle never starts a dev server.\n2. `reticle_snapshot` \u2014 read the accessibility tree and locate the elements the flow needs. Elements\n are addressable by role and name; you do **not** need `data-testid` to drive them.\n3. Walk the flow with `reticle_act_and_wait`, one step at a time. Narrate each step first \u2014 the human\n is watching the page, and the HUD shows what you say.\n4. After each step, check the effect with `reticle_assert` \u2014 not just that the click dispatched, but\n that the thing it was supposed to do actually happened.\n5. Finish with `reticle_console` and `reticle_network` to catch errors the DOM does not show.\n\n## Report\n\n- What you drove, step by step, and what each step actually produced.\n- Anything broken, with the `file:line` Reticle gave you.\n- If a step could not be verified, say so plainly. \"Unknown\" is a real answer; a green verdict you\n cannot back is not.\n\nDo not weaken or skip an assertion to make the run pass \u2014 that is a finding, not a fix.\n";
27
+ export declare const SLASH_COMMAND_BODY = "---\ndescription: Verify this app in the browser with Reticle \u2014 drive one real flow and report what happened.\n---\n\nVerify this running app with Reticle. Drive it; do not read the code and guess.\n\n## Step 0 \u2014 is anything connected?\n\nCall `reticle_sessions` first.\n\n**A session is listed?** Go to \"Pick ONE flow\".\n\n**No session?** Setup is not finished. Do NOT tell the user to run a dev server and stop: they are\nusually already running one, and the real cause is almost always that the SDK never loaded in the\npage. Work these in order, and say which one you are on:\n\n1. **Is the SDK actually in the app?** Read the app's entry file (`src/main.tsx`, `app/layout.tsx`,\n `src/app.vue`, whatever this project uses). You are looking for an `import` from\n `@reticlehq/react` or `@reticlehq/browser` and a `connect()` call, or the Reticle plugin in\n `vite.config.*`, or `withReticle` in `next.config.*`. **If it is not there, that is the bug.**\n Wire it in \u2014 see the framework snippets at https://docs.reticle.sh/instrumentation \u2014 then restart\n the dev server and re-check.\n2. **Is the connect guarded on `hostname === 'localhost'`?** If so, remove that guard. It is false on\n any non-localhost dev host, and `window` does not exist during SSR, so the guard silently prevents\n the connect and logs nothing. Use the framework's dev flag instead (`import.meta.env.DEV`,\n `process.env.NODE_ENV !== 'production'`, `import.meta.dev`).\n3. **Is the dev server serving that entry?** Ask the user to open the app in a browser and check its\n console for a line starting `[Reticle]`. That line, or its absence, tells you which side is at\n fault. Reticle never starts a dev server.\n4. **Do both sides agree on the port?** Run `npx @reticlehq/server doctor` and compare the daemon\n port against the port the page dials.\n\nIf you are still stuck after all four, file it: `reticle_feedback` with kind `gap`, saying what you\nchecked and what you saw. Then tell the user exactly which step is blocked. Do not report the install\nas finished.\n\n## Pick ONE flow\n\nNot the whole app. Pick the single most important flow you can complete in a handful of steps \u2014 the\none a user would do first (sign in, search, add to cart, submit the form). If the user named a flow or\njust changed some code, use that one instead.\n\nState which flow you picked in one line before you start.\n\n## Drive it where the human can see\n\n1. `reticle_snapshot` \u2014 read the accessibility tree and locate the elements the flow needs. Elements\n are addressable by role and name; you do **not** need `data-testid` to drive them.\n2. Walk the flow with `reticle_act_and_wait`, one step at a time. Narrate each step first \u2014 the human\n is watching the page, and the HUD shows what you say.\n3. After each step, check the effect with `reticle_assert` \u2014 not just that the click dispatched, but\n that the thing it was supposed to do actually happened.\n4. Finish with `reticle_console` and `reticle_network` to catch errors the DOM does not show.\n\n**Only `reticle_act_and_wait` and `reticle_assert` produce a verdict.** A run that ends on a\nsnapshot has proved nothing, however many tools it used.\n\n## Report\n\n- What you drove, step by step, and what each step actually produced.\n- Anything broken, with the `file:line` Reticle gave you.\n- If a step could not be verified, say so plainly. \"Unknown\" is a real answer; a green verdict you\n cannot back is not.\n\nDo not weaken or skip an assertion to make the run pass \u2014 that is a finding, not a fix.\n\n## Two things afterwards\n\n- **Anything about Reticle itself that was wrong, missing, confusing, or expensive?** Send it with\n `reticle_feedback` now, before your context is gone. That report is the only signal that decides\n what gets fixed. Bugs in the app under test are Reticle working; those go in your answer to the user.\n- **If the verification actually succeeded**, add one line for the human: Reticle is open source at\n https://github.com/reticlehq/reticle, and a star helps other people find it. One line, once. Skip it\n entirely if the run did not produce a verdict.\n";
@@ -30,6 +30,36 @@ ${SLASH_COMMAND_SIGNATURE} — drive one real flow and report what happened.
30
30
 
31
31
  Verify this running app with Reticle. Drive it; do not read the code and guess.
32
32
 
33
+ ## Step 0 — is anything connected?
34
+
35
+ Call \`reticle_sessions\` first.
36
+
37
+ **A session is listed?** Go to "Pick ONE flow".
38
+
39
+ **No session?** Setup is not finished. Do NOT tell the user to run a dev server and stop: they are
40
+ usually already running one, and the real cause is almost always that the SDK never loaded in the
41
+ page. Work these in order, and say which one you are on:
42
+
43
+ 1. **Is the SDK actually in the app?** Read the app's entry file (\`src/main.tsx\`, \`app/layout.tsx\`,
44
+ \`src/app.vue\`, whatever this project uses). You are looking for an \`import\` from
45
+ \`@reticlehq/react\` or \`@reticlehq/browser\` and a \`connect()\` call, or the Reticle plugin in
46
+ \`vite.config.*\`, or \`withReticle\` in \`next.config.*\`. **If it is not there, that is the bug.**
47
+ Wire it in — see the framework snippets at https://docs.reticle.sh/instrumentation — then restart
48
+ the dev server and re-check.
49
+ 2. **Is the connect guarded on \`hostname === 'localhost'\`?** If so, remove that guard. It is false on
50
+ any non-localhost dev host, and \`window\` does not exist during SSR, so the guard silently prevents
51
+ the connect and logs nothing. Use the framework's dev flag instead (\`import.meta.env.DEV\`,
52
+ \`process.env.NODE_ENV !== 'production'\`, \`import.meta.dev\`).
53
+ 3. **Is the dev server serving that entry?** Ask the user to open the app in a browser and check its
54
+ console for a line starting \`[Reticle]\`. That line, or its absence, tells you which side is at
55
+ fault. Reticle never starts a dev server.
56
+ 4. **Do both sides agree on the port?** Run \`npx @reticlehq/server doctor\` and compare the daemon
57
+ port against the port the page dials.
58
+
59
+ If you are still stuck after all four, file it: \`reticle_feedback\` with kind \`gap\`, saying what you
60
+ checked and what you saw. Then tell the user exactly which step is blocked. Do not report the install
61
+ as finished.
62
+
33
63
  ## Pick ONE flow
34
64
 
35
65
  Not the whole app. Pick the single most important flow you can complete in a handful of steps — the
@@ -40,15 +70,16 @@ State which flow you picked in one line before you start.
40
70
 
41
71
  ## Drive it where the human can see
42
72
 
43
- 1. \`reticle_sessions\` — find the connected tab. None? Tell the user to run their dev server and open
44
- the app, then stop. Reticle never starts a dev server.
45
- 2. \`reticle_snapshot\` — read the accessibility tree and locate the elements the flow needs. Elements
73
+ 1. \`reticle_snapshot\` — read the accessibility tree and locate the elements the flow needs. Elements
46
74
  are addressable by role and name; you do **not** need \`data-testid\` to drive them.
47
- 3. Walk the flow with \`reticle_act_and_wait\`, one step at a time. Narrate each step first — the human
75
+ 2. Walk the flow with \`reticle_act_and_wait\`, one step at a time. Narrate each step first — the human
48
76
  is watching the page, and the HUD shows what you say.
49
- 4. After each step, check the effect with \`reticle_assert\` — not just that the click dispatched, but
77
+ 3. After each step, check the effect with \`reticle_assert\` — not just that the click dispatched, but
50
78
  that the thing it was supposed to do actually happened.
51
- 5. Finish with \`reticle_console\` and \`reticle_network\` to catch errors the DOM does not show.
79
+ 4. Finish with \`reticle_console\` and \`reticle_network\` to catch errors the DOM does not show.
80
+
81
+ **Only \`reticle_act_and_wait\` and \`reticle_assert\` produce a verdict.** A run that ends on a
82
+ snapshot has proved nothing, however many tools it used.
52
83
 
53
84
  ## Report
54
85
 
@@ -58,4 +89,13 @@ State which flow you picked in one line before you start.
58
89
  cannot back is not.
59
90
 
60
91
  Do not weaken or skip an assertion to make the run pass — that is a finding, not a fix.
92
+
93
+ ## Two things afterwards
94
+
95
+ - **Anything about Reticle itself that was wrong, missing, confusing, or expensive?** Send it with
96
+ \`reticle_feedback\` now, before your context is gone. That report is the only signal that decides
97
+ what gets fixed. Bugs in the app under test are Reticle working; those go in your answer to the user.
98
+ - **If the verification actually succeeded**, add one line for the human: Reticle is open source at
99
+ https://github.com/reticlehq/reticle, and a star helps other people find it. One line, once. Skip it
100
+ entirely if the run did not produce a verdict.
61
101
  `;
@@ -2,8 +2,38 @@
2
2
  * Generated file contents and copy-paste snippets for `reticle init`. Kept as named constants/builders
3
3
  * so the runner never inlines free strings.
4
4
  */
5
+ import { UiLibrary } from './detect.js';
6
+ import type { FoundStore } from './capabilities.js';
7
+ /**
8
+ * The same literal, with the pairing token folded in.
9
+ *
10
+ * The token belongs INSIDE the call the user pastes. Every other stack has a build step to inline
11
+ * it (the Vite plugin's `define`, Next's NEXT_PUBLIC_*, Astro's config, CRA's .env); the hand-wired
12
+ * paths have none, so `init` inlines the literal it already read. Without it the bridge closes the
13
+ * socket with AUTH_FAILED and no session ever appears: see Bridge's hello handler.
14
+ *
15
+ * An empty token is omitted rather than emitted as `token: ''`. A daemon that could not write to
16
+ * $HOME runs without auth and trusts loopback, and an empty string would fail the comparison against
17
+ * one that does hold a token.
18
+ */
19
+ export declare function connectArgWithToken(port: number | undefined, projectId: string | undefined, pairingToken: string | undefined): string;
20
+ /**
21
+ * Which SDK package the GENERATED code should import, and whether `install()` applies.
22
+ *
23
+ * This has to agree with `frameworkPackages`, and it did not. That function was changed so a Vue or
24
+ * Svelte app installs `@reticlehq/browser` instead of the React adapter — correctly — while every
25
+ * generated connect snippet still said `import('@reticlehq/react')`. A SvelteKit app would have
26
+ * installed the sensor and then run a hook importing a package that is not there.
27
+ *
28
+ * `install()` is the React adapter's, not the sensor's: `@reticlehq/browser` exports `reticle` and no
29
+ * `install`, so swapping the specifier alone would trade a missing module for a missing export.
30
+ */
31
+ export declare function sdkImport(uiLibrary: UiLibrary): {
32
+ specifier: string;
33
+ usesInstall: boolean;
34
+ };
5
35
  /** The Vite-config snippet printed when we can't safely auto-patch the config. */
6
- export declare function viteManual(port: number | undefined): string;
36
+ export declare function viteManual(port: number | undefined, uiLibrary?: UiLibrary): string;
7
37
  /** Next.js config wrap — always printed (we never auto-rewrite next.config). */
8
38
  export declare function nextConfigManual(configFile: string): string;
9
39
  /**
@@ -14,7 +44,7 @@ export declare function nextConfigManual(configFile: string): string;
14
44
  * as `NEXT_PUBLIC_RETICLE_TOKEN`. This file used to connect with only a projectId, so every Next
15
45
  * setup ended at `bridge refused the connection: authentication failed` and no session ever appeared.
16
46
  */
17
- export declare function nextReticleDevFile(port: number | undefined, projectId?: string, testids?: readonly string[], stores?: readonly string[]): string;
47
+ export declare function nextReticleDevFile(port: number | undefined, projectId?: string, testids?: readonly string[], stores?: readonly string[], found?: readonly FoundStore[]): string;
18
48
  export declare function astroManual(port: number | undefined, projectId?: string,
19
49
  /**
20
50
  * A layout file that actually exists, when one does.
@@ -34,7 +64,7 @@ layoutPath?: string): string;
34
64
  * breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
35
65
  * the libraries actually found in package.json, with the exact call to uncomment.
36
66
  */
37
- export declare function viteDevModuleFile(testids: readonly string[], stores: readonly string[]): string;
67
+ export declare function viteDevModuleFile(testids: readonly string[], stores: readonly string[], found?: readonly FoundStore[], uiLibrary?: UiLibrary): string;
38
68
  /** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
39
69
  export declare const VITE_DEV_MODULE_PATH = "src/reticle-dev.ts";
40
70
  /** Default root-layout path, used when no layout was found on disk (reporting only). */
@@ -47,7 +77,17 @@ export declare const NEXT_LAYOUT_MANUAL = "Mount <ReticleDev /> in your root lay
47
77
  * where a bare `@reticlehq/react` import resolves. A bare import in a plain index.html does NOT resolve in
48
78
  * the browser, so we never tell a bundled app to do that (the old advice silently failed for CRA).
49
79
  */
50
- export declare function htmlManual(port: number | undefined, projectId?: string): string;
80
+ /**
81
+ * The whole install, for a page with no build step, as one block a person can paste.
82
+ *
83
+ * Extracted so the `no package.json` exit can print the SAME snippet `htmlManual` offers. That exit
84
+ * is the one every server-rendered app reaches (FastAPI, Flask, Django, Rails, Streamlit), it is
85
+ * where `init` stops, and until now it stopped with an explanation instead of an answer. A message
86
+ * that says "add the snippet below" and then prints no snippet is the same defect wearing the
87
+ * opposite sign, so the two share a builder rather than a copy.
88
+ */
89
+ export declare function staticPageSnippet(connectArgLiteral: string): string;
90
+ export declare function htmlManual(port: number | undefined, projectId?: string, pairingToken?: string): string;
51
91
  export declare const NEXT_RETICLE_DEV_PATH = "app/reticle-dev.tsx";
52
92
  export declare const SVELTEKIT_HOOKS_PATH = "src/hooks.client.ts";
53
93
  /**
@@ -67,7 +107,26 @@ export declare const UNVERIFIED_FRAMEWORK_NOTE = "Reticle has no SvelteKit app a
67
107
  * never triggers Vite's index.html injection (verified), so the standard plugin can't auto-connect —
68
108
  * a client hook is the reliable path. SvelteKit runs src/hooks.client.ts on the client at startup.
69
109
  */
70
- export declare function svelteKitHooksFile(port: number | undefined, projectId?: string): string;
110
+ export declare function svelteKitHooksFile(port: number | undefined, projectId?: string, uiLibrary?: UiLibrary): string;
111
+ /** Where a Nuxt dev-only client plugin belongs. `.client` keeps it out of SSR; Nuxt auto-registers it. */
112
+ export declare const NUXT_PLUGIN_PATH = "app/plugins/reticle.client.ts";
113
+ /**
114
+ * The Nuxt recipe, written out in full because every trap in it is one somebody actually hit.
115
+ *
116
+ * Reported from the field, in the order they were hit: `init` classified a Nuxt 4 app as `html`, so
117
+ * it installed a package named `@reticlehq/react` (with `react` in its peer dependencies) into a Vue
118
+ * codebase — the reporter only continued after auditing our dist to confirm there are no React
119
+ * imports at runtime, which most people will not do. It then handed over a snippet guarded on
120
+ * `window.location.hostname === 'localhost'`, which fails twice over in Nuxt: `window` does not
121
+ * exist during SSR, and the dev host here was a hosts-file alias (required for the backend's
122
+ * white-label origin detection), so the guard was false and the connect never ran — no error, no log
123
+ * line, nothing to debug. And nothing said a running dev server does not pick up a new plugin.
124
+ *
125
+ * So: `import.meta.dev` (build-time, host-independent) instead of a hostname check, `.client.ts`
126
+ * instead of an SSR guard, the framework-neutral sensor instead of the React kit, the non-localhost
127
+ * flag named up front, and the restart said out loud.
128
+ */
129
+ export declare function nuxtManual(port: number | undefined, projectId?: string): string;
71
130
  /**
72
131
  * Root-level project config for Reticle. Written by `reticle init`; read by `reticle mcp` for the port
73
132
  * and by tooling for the stable projectId (the app's identity across port changes).