@reticlehq/server 2.7.0 → 2.9.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.
- package/README.md +14 -5
- package/SKILL.md +130 -778
- package/dist/bridge/bridge.d.ts +23 -0
- package/dist/bridge/bridge.js +80 -2
- package/dist/bridge/bridge.test-harness.d.ts +5 -0
- package/dist/bridge/bridge.test-harness.js +6 -4
- package/dist/capsule/causal-summary.d.ts +36 -1
- package/dist/capsule/causal-summary.js +33 -7
- package/dist/capsule/divergence.js +7 -1
- package/dist/cli/chromium-hint.d.ts +33 -5
- package/dist/cli/chromium-hint.js +82 -12
- package/dist/cli/cli-doctor.js +65 -34
- package/dist/cli/cli-flow-commands.js +8 -8
- package/dist/cli/cli-kill.d.ts +58 -0
- package/dist/cli/cli-kill.js +162 -0
- package/dist/cli/cli-parse.d.ts +16 -1
- package/dist/cli/cli-parse.js +32 -9
- package/dist/cli/cli-port.d.ts +32 -0
- package/dist/cli/cli-port.js +82 -35
- package/dist/cli/cli-verify.d.ts +21 -0
- package/dist/cli/cli-verify.js +47 -3
- package/dist/cli/config-discovery.d.ts +35 -0
- package/dist/cli/config-discovery.js +163 -0
- package/dist/cli/doctor-daemon-line.js +4 -1
- package/dist/cli/doctor-project-line.d.ts +37 -0
- package/dist/cli/doctor-project-line.js +35 -0
- package/dist/cli/doctor-rows.d.ts +51 -0
- package/dist/cli/doctor-rows.js +54 -0
- package/dist/cli/doctor-sessions-line.d.ts +12 -0
- package/dist/cli/doctor-sessions-line.js +69 -0
- package/dist/cli/drive-attach.d.ts +99 -0
- package/dist/cli/drive-attach.js +203 -0
- package/dist/cli/drive-command.d.ts +13 -0
- package/dist/cli/drive-command.js +103 -0
- package/dist/cli/open-note.d.ts +20 -0
- package/dist/cli/open-note.js +37 -0
- package/dist/cli/port-holder.d.ts +10 -0
- package/dist/cli/port-holder.js +26 -2
- package/dist/cli/status-next-action.d.ts +37 -0
- package/dist/cli/status-next-action.js +61 -0
- package/dist/cli.js +119 -39
- package/dist/crawl/crawl-tools.js +12 -2
- package/dist/crawl/crawl.d.ts +13 -0
- package/dist/crawl/crawl.js +50 -2
- package/dist/events/contradictions.d.ts +46 -0
- package/dist/events/contradictions.js +35 -22
- package/dist/events/declared.d.ts +45 -0
- package/dist/events/declared.js +73 -0
- package/dist/events/echo-mismatch.d.ts +8 -2
- package/dist/events/echo-mismatch.js +33 -5
- package/dist/events/event-filters.d.ts +1 -1
- package/dist/events/event-filters.js +7 -4
- package/dist/events/predicate-eval.d.ts +17 -72
- package/dist/events/predicate-eval.js +117 -236
- package/dist/events/predicate-parse.js +31 -3
- package/dist/events/predicate-route.d.ts +32 -0
- package/dist/events/predicate-route.js +102 -0
- package/dist/events/predicate-schema.d.ts +99 -0
- package/dist/events/predicate-schema.js +309 -0
- package/dist/events/predicate.d.ts +8 -0
- package/dist/events/predicate.js +296 -22
- package/dist/flows/annotate-tools.js +3 -0
- package/dist/flows/annotate.js +18 -4
- package/dist/flows/coverage.d.ts +38 -5
- package/dist/flows/coverage.js +30 -4
- package/dist/flows/decision.d.ts +15 -0
- package/dist/flows/decision.js +1 -1
- package/dist/flows/flow-classify.js +1 -1
- package/dist/flows/flow-replay-run.js +8 -1
- package/dist/flows/flow-tools.js +21 -5
- package/dist/flows/heal-run.js +1 -1
- package/dist/flows/replay.js +3 -0
- package/dist/honesty/accepted-write.d.ts +3 -0
- package/dist/honesty/accepted-write.js +8 -3
- package/dist/honesty/already-true.js +15 -0
- package/dist/honesty/blind-spots.js +10 -2
- package/dist/honesty/honesty.d.ts +11 -0
- package/dist/honesty/honesty.js +1 -0
- package/dist/honesty/uncaptured-bodies.d.ts +4 -2
- package/dist/honesty/uncaptured-bodies.js +9 -2
- package/dist/honesty/unread-outcome.d.ts +8 -0
- package/dist/honesty/unread-outcome.js +36 -9
- package/dist/honesty/unsettled.d.ts +49 -0
- package/dist/honesty/unsettled.js +82 -0
- package/dist/honesty/verified.d.ts +36 -3
- package/dist/honesty/verified.js +54 -11
- package/dist/http-server.d.ts +19 -0
- package/dist/http-server.js +202 -4
- package/dist/impact/impact-recorder.d.ts +23 -0
- package/dist/impact/impact-recorder.js +76 -0
- package/dist/impact/impact-store.d.ts +47 -0
- package/dist/impact/impact-store.js +171 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +47 -10
- package/dist/init/agent-rules.d.ts +36 -0
- package/dist/init/agent-rules.js +132 -22
- package/dist/init/astro-patch.d.ts +2 -1
- package/dist/init/astro-patch.js +38 -6
- package/dist/init/capabilities.d.ts +22 -0
- package/dist/init/capabilities.js +76 -0
- package/dist/init/confirm.d.ts +114 -0
- package/dist/init/confirm.js +165 -0
- package/dist/init/connect-steps.d.ts +2 -0
- package/dist/init/connect-steps.js +50 -0
- package/dist/init/cra.js +3 -2
- package/dist/init/csp-check.d.ts +38 -0
- package/dist/init/csp-check.js +92 -0
- package/dist/init/csp-doctor.d.ts +25 -0
- package/dist/init/csp-doctor.js +49 -0
- package/dist/init/cursor.js +6 -9
- package/dist/init/detect.d.ts +8 -0
- package/dist/init/detect.js +33 -3
- package/dist/init/mcp-clients.d.ts +1 -0
- package/dist/init/mcp-clients.js +51 -7
- package/dist/init/mcp.d.ts +15 -0
- package/dist/init/mcp.js +19 -0
- package/dist/init/non-js-project.js +29 -5
- package/dist/init/plan-framework.d.ts +10 -0
- package/dist/init/plan-framework.js +78 -16
- package/dist/init/plan.d.ts +47 -6
- package/dist/init/plan.js +228 -69
- package/dist/init/run.d.ts +24 -2
- package/dist/init/run.js +157 -45
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +54 -6
- package/dist/init/snippets.d.ts +45 -5
- package/dist/init/snippets.js +194 -42
- package/dist/init/vite-config.js +53 -5
- package/dist/input/network-mock-tools.d.ts +19 -0
- package/dist/input/network-mock-tools.js +37 -5
- package/dist/input/scroll-find.d.ts +6 -0
- package/dist/input/scroll-find.js +17 -2
- package/dist/input/scroll-tools.js +4 -0
- package/dist/journal/attach-journal.d.ts +9 -1
- package/dist/journal/attach-journal.js +13 -2
- package/dist/journal/journal-query.d.ts +13 -0
- package/dist/journal/journal-query.js +28 -3
- package/dist/journal/workspace-gitignore.d.ts +10 -0
- package/dist/journal/workspace-gitignore.js +59 -0
- package/dist/license/license.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +35 -30
- package/dist/mcp/mcp-proxy.js +306 -104
- package/dist/mcp/mcp.d.ts +10 -1
- package/dist/mcp/mcp.js +70 -34
- package/dist/mcp/proxy-handshake.d.ts +82 -1
- package/dist/mcp/proxy-handshake.js +109 -2
- package/dist/mcp/proxy-lifecycle.d.ts +11 -2
- package/dist/mcp/proxy-lifecycle.js +12 -3
- package/dist/mcp/proxy-log.d.ts +63 -0
- package/dist/mcp/proxy-log.js +132 -0
- package/dist/mcp/server-instructions.d.ts +17 -0
- package/dist/mcp/server-instructions.js +56 -0
- package/dist/mcp/shared-params.d.ts +27 -0
- package/dist/mcp/shared-params.js +36 -0
- package/dist/pool/browser-pool.d.ts +22 -2
- package/dist/pool/browser-pool.js +44 -4
- package/dist/project/learned-routes.d.ts +24 -0
- package/dist/project/learned-routes.js +63 -0
- package/dist/project/project-store.d.ts +6 -0
- package/dist/project/project-store.js +25 -1
- package/dist/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- package/dist/session/connection-memory.d.ts +59 -0
- package/dist/session/connection-memory.js +108 -0
- package/dist/session/dev-command.d.ts +26 -0
- package/dist/session/dev-command.js +89 -0
- package/dist/session/dev-server-probe.d.ts +68 -0
- package/dist/session/dev-server-probe.js +102 -7
- package/dist/session/last-act.d.ts +26 -0
- package/dist/session/last-act.js +35 -1
- package/dist/session/live-control-tools.js +42 -5
- package/dist/session/no-session-diagnosis.d.ts +67 -2
- package/dist/session/no-session-diagnosis.js +236 -27
- package/dist/session/no-session-next-action.d.ts +46 -0
- package/dist/session/no-session-next-action.js +85 -0
- package/dist/session/no-session-watch.d.ts +40 -1
- package/dist/session/no-session-watch.js +174 -8
- package/dist/session/pending-commands.d.ts +12 -0
- package/dist/session/pending-commands.js +13 -1
- package/dist/session/ref-provenance.d.ts +54 -0
- package/dist/session/ref-provenance.js +61 -0
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +4 -0
- package/dist/session/session-manager.js +35 -5
- package/dist/session/session.d.ts +10 -1
- package/dist/session/session.js +146 -5
- package/dist/telemetry/app-instrumented.d.ts +8 -0
- package/dist/telemetry/app-instrumented.js +10 -0
- package/dist/telemetry/automation-hint.d.ts +17 -0
- package/dist/telemetry/automation-hint.js +71 -0
- package/dist/telemetry/bug-found.d.ts +15 -17
- package/dist/telemetry/bug-found.js +52 -21
- package/dist/telemetry/cli-telemetry.js +7 -1
- package/dist/telemetry/daemon-telemetry.js +24 -0
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/init-telemetry.js +9 -1
- package/dist/telemetry/install-source.d.ts +56 -0
- package/dist/telemetry/install-source.js +74 -0
- package/dist/telemetry/instrumentation-stall.d.ts +50 -0
- package/dist/telemetry/instrumentation-stall.js +90 -0
- package/dist/telemetry/run-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.d.ts +8 -0
- package/dist/telemetry/session-metrics.js +15 -0
- package/dist/telemetry/telemetry.d.ts +7 -1
- package/dist/telemetry/telemetry.js +26 -2
- package/dist/telemetry/tool-refused.d.ts +19 -0
- package/dist/telemetry/tool-refused.js +55 -0
- package/dist/telemetry/verification-of.js +46 -7
- package/dist/temp-dir.d.ts +27 -0
- package/dist/temp-dir.js +46 -0
- package/dist/tools/act-tools.js +209 -28
- package/dist/tools/assert-grade.d.ts +1 -1
- package/dist/tools/assert-grade.js +27 -5
- package/dist/tools/assert-verdict.js +49 -7
- package/dist/tools/browser-tools.js +10 -0
- package/dist/tools/contract-tools.js +4 -0
- package/dist/tools/dynamic-tools.js +16 -7
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +73 -9
- package/dist/tools/feedback-invite.js +1 -1
- package/dist/tools/invoke-tool.js +159 -26
- package/dist/tools/lease-hint.d.ts +30 -8
- package/dist/tools/lease-hint.js +65 -14
- package/dist/tools/lease-tools.d.ts +40 -1
- package/dist/tools/lease-tools.js +181 -83
- package/dist/tools/navigate-result.js +1 -1
- package/dist/tools/observe-tools.js +10 -4
- package/dist/tools/observed-nothing.js +1 -1
- package/dist/tools/react-grace.d.ts +52 -0
- package/dist/tools/react-grace.js +90 -0
- package/dist/tools/read-tools.js +67 -5
- package/dist/tools/resolve-target.d.ts +33 -0
- package/dist/tools/resolve-target.js +53 -0
- package/dist/tools/settle-in-flight.d.ts +94 -0
- package/dist/tools/settle-in-flight.js +146 -0
- package/dist/tools/snapshot-delta.d.ts +10 -0
- package/dist/tools/snapshot-delta.js +194 -9
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +60 -0
- package/dist/tools/tool-surface.js +86 -1
- package/dist/tools/tools.js +61 -8
- package/dist/tools/unadvertised-help.js +9 -6
- package/docs/README.md +55 -11
- package/docs/actions.mdx +120 -0
- package/docs/agent-cheatsheet.md +95 -29
- package/docs/architecture.md +52 -35
- package/docs/benchmarks.md +127 -75
- package/docs/best-practices.mdx +216 -0
- package/docs/capabilities.mdx +534 -0
- package/docs/cli/affected.mdx +53 -0
- package/docs/cli/capsules.mdx +70 -0
- package/docs/cli/cloud.mdx +153 -0
- package/docs/cli/doctor.mdx +109 -0
- package/docs/cli/drive.mdx +74 -0
- package/docs/cli/feedback.mdx +67 -0
- package/docs/cli/gate.mdx +64 -0
- package/docs/cli/help.mdx +94 -0
- package/docs/cli/hunt.mdx +73 -0
- package/docs/cli/identify.mdx +76 -0
- package/docs/cli/init.mdx +121 -0
- package/docs/cli/kill.mdx +91 -0
- package/docs/cli/license.mdx +48 -0
- package/docs/cli/mcp.mdx +61 -0
- package/docs/cli/open.mdx +74 -0
- package/docs/cli/restart.mdx +60 -0
- package/docs/cli/rollback.mdx +35 -0
- package/docs/cli/serve.mdx +69 -0
- package/docs/cli/status.mdx +88 -0
- package/docs/cli/stop.mdx +64 -0
- package/docs/cli/telemetry.mdx +57 -0
- package/docs/cli/update.mdx +52 -0
- package/docs/cli/verify.mdx +76 -0
- package/docs/cli/version.mdx +39 -0
- package/docs/cli/watch.mdx +62 -0
- package/docs/cli.mdx +241 -0
- package/docs/debugging.md +16 -10
- package/docs/deploy-checks.md +45 -12
- package/docs/desktop.mdx +384 -0
- package/docs/docs.json +364 -0
- package/docs/enterprise.md +22 -16
- package/docs/faq.mdx +102 -0
- package/docs/fixtures.md +13 -7
- package/docs/flows.md +29 -23
- package/docs/for-agents.md +71 -0
- package/docs/frameworks.mdx +238 -0
- package/docs/gate-plan.md +48 -42
- package/docs/gates.md +28 -22
- package/docs/getting-started.md +129 -77
- package/docs/human-control.md +24 -18
- package/docs/index.mdx +143 -0
- package/docs/install-agentic.mdx +224 -0
- package/docs/install-manual.mdx +196 -0
- package/docs/instrumentation.mdx +238 -0
- package/docs/integration-patterns.md +41 -35
- package/docs/local-registry.md +19 -13
- package/docs/multi-agent-testing.md +19 -13
- package/docs/packages/babel-plugin.mdx +79 -0
- package/docs/packages/browser.mdx +111 -0
- package/docs/packages/core.mdx +116 -0
- package/docs/packages/electron.mdx +85 -0
- package/docs/packages/eslint-plugin.mdx +86 -0
- package/docs/packages/next.mdx +89 -0
- package/docs/packages/react.mdx +91 -0
- package/docs/packages/server.mdx +98 -0
- package/docs/packages/tauri.mdx +63 -0
- package/docs/packages/test.mdx +119 -0
- package/docs/packages/vite-plugin.mdx +94 -0
- package/docs/packages.mdx +218 -0
- package/docs/platform-integration.md +35 -26
- package/docs/predicates.mdx +248 -0
- package/docs/quickstart.mdx +257 -0
- package/docs/recipes.mdx +282 -0
- package/docs/skill-file.mdx +107 -0
- package/docs/state-management.mdx +199 -0
- package/docs/style.css +14 -0
- package/docs/system-map.md +50 -33
- package/docs/telemetry-contract.md +106 -52
- package/docs/telemetry-events.mdx +475 -0
- package/docs/telemetry.md +63 -55
- package/docs/testing.md +44 -6
- package/docs/token-efficiency.md +37 -27
- package/docs/tools-act-and-wait.mdx +173 -0
- package/docs/tools-act-sequence.mdx +137 -0
- package/docs/tools-act.mdx +95 -0
- package/docs/tools-assert.mdx +129 -0
- package/docs/tools-console.mdx +63 -0
- package/docs/tools-inspect.mdx +88 -0
- package/docs/tools-navigate.mdx +99 -0
- package/docs/tools-network.mdx +119 -0
- package/docs/tools-observe.mdx +145 -0
- package/docs/tools-overview.mdx +160 -0
- package/docs/tools-query.mdx +126 -0
- package/docs/tools-session-and-feedback.mdx +112 -0
- package/docs/tools-sessions.mdx +94 -0
- package/docs/tools-snapshot.mdx +129 -0
- package/docs/tools-state.mdx +104 -0
- package/docs/tools-tools-and-run.mdx +156 -0
- package/docs/tools-wait-for.mdx +85 -0
- package/docs/troubleshooting.mdx +263 -0
- package/docs/usage.md +209 -165
- package/docs/vs-chrome-devtools-mcp.mdx +123 -0
- package/docs/vs-playwright-mcp.mdx +125 -0
- package/docs/vs-screenshots.mdx +137 -0
- package/docs/what-is-reticle.mdx +63 -0
- package/docs/why-reticle.mdx +109 -0
- package/package.json +5 -5
- package/docs/desktop-apps.md +0 -256
- package/docs/matrix/2.5.0/claude-code-machine.json +0 -19
- package/docs/matrix/2.5.0/codex-machine.json +0 -20
- package/docs/matrix/2.5.0/cursor-machine.json +0 -22
- package/docs/matrix/2.5.0/gemini-machine.json +0 -22
- package/docs/matrix/2.5.0/opencode-machine.json +0 -22
- package/docs/matrix/2.5.0/vscode-machine.json +0 -22
- package/docs/matrix/2.5.0/windsurf-machine.json +0 -22
- package/docs/matrix/MATRIX.md +0 -26
- package/docs/matrix/README.md +0 -59
- package/docs/mint.json +0 -61
package/docs/gates.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Gates
|
|
3
|
+
description: 'I changed some files. Which command do I run before I push?'
|
|
4
|
+
icon: shield-check
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
If you changed anything at all, run `pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:unit` (about two minutes). If you also touched the tool surface, install, desktop or Rust, section 1 below names the one extra command your change needs. CI runs everything regardless, so routing costs you a slower red, never a missed one.
|
|
2
8
|
|
|
3
9
|
> **One question this file answers:** _I changed some files. Which command do I run?_
|
|
4
10
|
>
|
|
@@ -15,19 +21,19 @@ Every gate below was executed end to end against `main` on **2026-08-12** (macOS
|
|
|
15
21
|
| `pnpm build` | ✅ | ~40s |
|
|
16
22
|
| `pnpm lint` (+ both guard self-tests) | ✅ | ~15s |
|
|
17
23
|
| `pnpm typecheck` | ✅ | ~10s |
|
|
18
|
-
| `pnpm test:unit` | ✅
|
|
24
|
+
| `pnpm test:unit` | ✅ 5,725 tests / 613 files across 8 packages | ~45s |
|
|
19
25
|
| `pnpm format:check` | ✅ | ~10s |
|
|
20
26
|
| `pnpm test:integration` | ✅ 12/12 | 17s |
|
|
21
|
-
| `pnpm test:e2e` | ✅
|
|
22
|
-
| `pnpm test:e2e:desktop` | ✅ 2/2 (Electron 20, Tauri
|
|
23
|
-
| `node apps/e2e/soak.mjs --self-check` | ✅ |
|
|
24
|
-
| `node apps/e2e/matrix.mjs --self-check` | ✅ |
|
|
27
|
+
| `pnpm test:e2e` | ✅ 33/33 specs + soak 60/60 answered | **490s** |
|
|
28
|
+
| `pnpm test:e2e:desktop` | ✅ 2/2 (Electron 20, Tauri 17) | 58s |
|
|
29
|
+
| `node apps/e2e/soak.mjs --self-check` | ✅ | `<1s` |
|
|
30
|
+
| `node apps/e2e/matrix.mjs --self-check` | ✅ | `<1s` |
|
|
25
31
|
| `pnpm matrix:compat --only cursor` | ✅ 4/4 | ~10s |
|
|
26
|
-
| `pnpm bench` | ✅ 10/10
|
|
32
|
+
| `pnpm bench` | ✅ 10/10 (**was failing before this sweep**, see below) | 279s |
|
|
27
33
|
|
|
28
|
-
**`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify
|
|
34
|
+
**`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify`, which correctly grades an assertion-free suite `unverifiable`. The product got more honest about false greens; the benchmark measuring it did not follow, so the whole run aborted at script 9 of 10 and `replay-determinism` never ran at all. Each flow now carries a success oracle. This is the failure mode `bench/` is most exposed to: **nothing in CI runs it**, so it can only rot silently.
|
|
29
35
|
|
|
30
|
-
`pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep
|
|
36
|
+
`pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep; they are CI-only or network-bound. They are green on `main` per the last CI run, which is a weaker claim than every row above, and is stated that way on purpose.
|
|
31
37
|
|
|
32
38
|
---
|
|
33
39
|
|
|
@@ -37,15 +43,15 @@ Find the row that matches what you changed. Run its commands. That is the whole
|
|
|
37
43
|
|
|
38
44
|
| You changed | Run | Cost |
|
|
39
45
|
| --- | --- | --- |
|
|
40
|
-
| **Anything at all** | `pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
|
|
46
|
+
| **Anything at all** | `pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
|
|
41
47
|
| The tool surface, the wire contract (`packages/core`), or an observer | ↑ **and** `pnpm test:e2e` | +~8 min |
|
|
42
|
-
| `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin
|
|
48
|
+
| `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin`, anything a user runs before their first session | ↑ **and** `pnpm gate:install` | +~15 min |
|
|
43
49
|
| `@reticlehq/electron`, `packages/tauri`, the IPC observer, desktop capture | ↑ **and** `pnpm test:e2e:desktop` | +~3 min |
|
|
44
|
-
| Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) |
|
|
50
|
+
| Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) | n/a |
|
|
45
51
|
| `packages/tauri` (Rust) | ↑ **and** `cd packages/tauri && cargo fmt --check && cargo clippy --all-targets -- -D warnings` | +~2 min |
|
|
46
52
|
| Docs, README, comments only | `pnpm format:check` | seconds |
|
|
47
53
|
|
|
48
|
-
**Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless
|
|
54
|
+
**Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless, so routing costs you a slower red, never a missed one.
|
|
49
55
|
|
|
50
56
|
---
|
|
51
57
|
|
|
@@ -59,16 +65,16 @@ Each gate exists because the ones above it are blind to something. That blindnes
|
|
|
59
65
|
| **Lint** | `pnpm lint` | style rules, plus the dependency-boundary and lossy-transform guards | anything not expressible as a rule | `verify` |
|
|
60
66
|
| **Typecheck** | `pnpm typecheck` | types agree across package boundaries | runtime behaviour | `verify` |
|
|
61
67
|
| **Unit** | `pnpm test:unit` | 4,315 tests, per-package, no browser | anything crossing a package boundary at runtime | `verify` |
|
|
62
|
-
| **Format** | `pnpm format:check` | Prettier |
|
|
68
|
+
| **Format** | `pnpm format:check` | Prettier | n/a | `verify` |
|
|
63
69
|
| **Integration** | `pnpm test:integration` | real headless Chromium: browser pool, crash isolation, framework adapters, `withReticle` | the MCP surface, the daemon | `e2e` |
|
|
64
|
-
| **Web e2e battery** | `pnpm test:e2e` |
|
|
70
|
+
| **Web e2e battery** | `pnpm test:e2e` | 32 specs against 3 booted servers and a real browser (the tool surface, the daemon lifecycle, transport faults, telemetry, trace shape), plus the soak | desktop runtimes; the install | `e2e` |
|
|
65
71
|
| **Desktop battery** | `pnpm test:e2e:desktop` | a real Electron main process and a **packaged** Tauri binary, driven headless | web-only paths | `desktop-e2e` |
|
|
66
|
-
| **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_
|
|
72
|
+
| **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_; see [`fixtures.md`](./fixtures.md) | `install-gate` |
|
|
67
73
|
| **Matrix records** | `pnpm matrix:validate` | every submitted client-compat record is well-formed | whether the client actually works | `matrix-records` |
|
|
68
74
|
| **Windows** | (CI only) | that the code runs at all on the majority platform | e2e; Windows is unit-only | `windows` |
|
|
69
75
|
| **Rust** | `cargo fmt/clippy/check` | `packages/tauri` compiles and lints on Linux, macOS, and cross-checks Windows | everything JS | `rust`, `rust-macos` |
|
|
70
76
|
|
|
71
|
-
**The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half
|
|
77
|
+
**The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half. A job missing from that list runs, reports, and is structurally incapable of blocking a merge.
|
|
72
78
|
|
|
73
79
|
### Guards that self-test
|
|
74
80
|
|
|
@@ -99,7 +105,7 @@ These are real and they work; they are not on the PR path, so they only run when
|
|
|
99
105
|
|
|
100
106
|
## 4. `bench/` is not a gate
|
|
101
107
|
|
|
102
|
-
`bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it
|
|
108
|
+
`bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it: it says which scripts are live and which are one-off studies kept as evidence for a published claim.
|
|
103
109
|
|
|
104
110
|
The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working regression gate for the replay numbers, and it is run by hand before a release.
|
|
105
111
|
|
|
@@ -109,9 +115,9 @@ The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working r
|
|
|
109
115
|
|
|
110
116
|
Sometimes it is. The specific failures worth recognising:
|
|
111
117
|
|
|
112
|
-
- **`EADDRINUSE` / "died during boot"
|
|
113
|
-
- **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9
|
|
114
|
-
- **A timing assertion
|
|
115
|
-
- **An `INCONCLUSIVE` verdict
|
|
118
|
+
- **`EADDRINUSE` / "died during boot".** A previous run left something on `:8787`, `:4310`, or `:3100`. `run-ci.sh` frees these on exit; if it was killed, free them by hand.
|
|
119
|
+
- **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9`.** This SIGKILLs the `reticle mcp` proxy too, because the proxy holds a _client_ socket on the bridge port. Always add `-sTCP:LISTEN`. This is the root cause of most "the MCP went down" reports.
|
|
120
|
+
- **A timing assertion.** If a test asserts `Date.now() - t < N`, that is a bug in the test, not a flake to re-run. Assert the bound (output size, a truncation flag), or use a generous per-test timeout. See [`harness-rules.md`](../apps/e2e/harness-rules.md).
|
|
121
|
+
- **An `INCONCLUSIVE` verdict.** The harness is telling you the transport did not stay up, so it is claiming nothing about the product. That is the harness working, not the product failing.
|
|
116
122
|
|
|
117
123
|
The four rules every tier obeys, and the incident behind each, are in [`apps/e2e/harness-rules.md`](../apps/e2e/harness-rules.md).
|
package/docs/getting-started.md
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Getting started
|
|
3
|
+
description: 'Zero to your agent verifying your real app, step by step, with working code for Vite, Next.js, React and plain HTML.'
|
|
4
|
+
icon: rocket
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**To get started with Reticle: run `npx @reticlehq/server init` in your project root, restart your dev server, restart your coding agent, then open your app in a browser and ask the agent to drive a flow.** Reticle is a verification layer that embeds a dev-only SDK in your running web app so an AI coding agent can prove a change works instead of guessing. It needs Node 20 or newer, an app you run locally, and an agent that speaks MCP.
|
|
8
|
+
|
|
9
|
+
> **Looking for the fast path?** [Quickstart](/quickstart) gets you to a real verdict in five minutes, and every response on it was captured live. [Agentic install](/install-agentic) and [Manual install](/install-manual) cover setup in detail, per agent and per framework.
|
|
10
|
+
>
|
|
11
|
+
> This page is the long-form walkthrough: the same ground, more slowly, plus per-framework wiring, multi-app setups and prerelease notes. Start here if you want the whole picture in one file rather than the fast route.
|
|
2
12
|
|
|
3
|
-
This walks you from zero to your agent verifying your app
|
|
13
|
+
This walks you from zero to your agent verifying your app, step by step, with real code for real frameworks. ~10 minutes.
|
|
4
14
|
|
|
5
15
|
- [What you're setting up](#what-youre-setting-up)
|
|
6
16
|
- [Prerequisites](#prerequisites)
|
|
7
|
-
- [Step 1
|
|
8
|
-
- [Step 2
|
|
17
|
+
- [Step 1: Connect your coding agent (MCP)](#step-1-connect-your-coding-agent-mcp)
|
|
18
|
+
- [Step 2: Embed the SDK in your app](#step-2-embed-the-sdk-in-your-app)
|
|
9
19
|
- [Vite + React](#vite--react)
|
|
10
20
|
- [Next.js](#nextjs)
|
|
11
21
|
- [Plain / other frameworks](#plain--other-frameworks)
|
|
12
|
-
- [Step 3
|
|
13
|
-
- [Step 4
|
|
14
|
-
- [Step 5
|
|
15
|
-
- [Step 6
|
|
22
|
+
- [Step 3: (React) component and source-file mapping](#step-3-react-component-and-source-file-mapping)
|
|
23
|
+
- [Step 4: Run it and verify the connection](#step-4-run-it-and-verify-the-connection)
|
|
24
|
+
- [Step 5: Your first verification](#step-5-your-first-verification)
|
|
25
|
+
- [Step 6: Make your app agent-legible](#step-6-make-your-app-agent-legible-optional-high-leverage)
|
|
16
26
|
- [Common setups at a glance](#common-setups-at-a-glance)
|
|
17
27
|
- [Troubleshooting](#troubleshooting)
|
|
18
28
|
|
|
@@ -22,58 +32,61 @@ This walks you from zero to your agent verifying your app — step by step, with
|
|
|
22
32
|
|
|
23
33
|
Three pieces, each tiny:
|
|
24
34
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
```mermaid
|
|
36
|
+
flowchart LR
|
|
37
|
+
A["coding agent<br/>(Claude Code)"]
|
|
38
|
+
S["reticle bridge + server<br/>npx @reticlehq/server"]
|
|
39
|
+
B["your app<br/>+ the Reticle SDK<br/>(dev only)"]
|
|
40
|
+
|
|
41
|
+
A <-- "MCP over stdio" --> S
|
|
42
|
+
S <-- "WebSocket<br/>localhost:4400" --> B
|
|
30
43
|
```
|
|
31
44
|
|
|
32
45
|
Three pieces, each from the package for its audience:
|
|
33
46
|
|
|
34
|
-
1. **The MCP server
|
|
35
|
-
2. **The SDK
|
|
36
|
-
3. **(Optional) React adapter + source-mapping
|
|
47
|
+
1. **The MCP server.** Your agent launches it with `npx @reticlehq/server mcp`; it hosts the tools _and_ the WebSocket bridge your app connects to. You don't run it by hand; the agent does.
|
|
48
|
+
2. **The SDK**: `import { reticle } from '@reticlehq/react'`, a few lines in your app's dev entry point.
|
|
49
|
+
3. **(Optional) React adapter + source-mapping**, so `reticle_inspect` can tell the agent which component/file to edit (also from `@reticlehq/react`).
|
|
37
50
|
|
|
38
51
|
Everything is **dev-only** and **localhost-only**. It's tree-shaken out of production builds.
|
|
39
52
|
|
|
40
53
|
## Prerequisites
|
|
41
54
|
|
|
42
|
-
- Node
|
|
55
|
+
- Node 20 or newer, and a package manager (npm/pnpm/yarn).
|
|
43
56
|
- A coding agent that speaks MCP: Claude Code, Cursor, Windsurf, Claude Desktop, etc.
|
|
44
57
|
- A web app you run locally in dev (any framework; React gets the richest features).
|
|
45
58
|
|
|
46
59
|
---
|
|
47
60
|
|
|
48
|
-
## Fastest path
|
|
61
|
+
## Fastest path: `reticle init`
|
|
49
62
|
|
|
50
63
|
From your project root:
|
|
51
64
|
|
|
52
65
|
```bash
|
|
53
|
-
npx @reticlehq/server init
|
|
66
|
+
RETICLE_INSTALL_SOURCE=docs_site npx @reticlehq/server init
|
|
54
67
|
```
|
|
55
68
|
|
|
56
69
|
It detects your framework, package manager, and React version, then:
|
|
57
70
|
|
|
58
|
-
- **registers the Reticle MCP server once, globally, for each agent you have installed**
|
|
59
|
-
- **writes a verification rule into your agent's instruction file**
|
|
71
|
+
- **registers the Reticle MCP server once, globally, for each agent you have installed** (Claude Code via `claude mcp add reticle -s user`, and/or Cursor via `~/.cursor/mcp.json`), so every project on this machine gets it; you never re-add it per project,
|
|
72
|
+
- **writes a verification rule into your agent's instruction file** (`CLAUDE.md`, `.cursor/rules/reticle.mdc`, or `AGENTS.md`), so the agent knows to verify a feature with Reticle _after building it_, not only when you remember to ask (idempotent; appended below anything you already have),
|
|
60
73
|
- installs the SDK kit (`@reticlehq/react`) and the right build plugin (`@reticlehq/vite-plugin` or `@reticlehq/next`) as dev dependencies,
|
|
61
|
-
- **Vite:** adds the `reticle()` plugin to your config
|
|
74
|
+
- **Vite:** adds the `reticle()` plugin to your config, which wires source mapping _and_ `reticle.connect()` for you, so there is nothing else to edit,
|
|
62
75
|
- **Next / other:** creates the dev component and prints the exact `withReticle` / mount / connect snippets to paste (it never half-edits a build config).
|
|
63
76
|
|
|
64
77
|
The bridge + MCP server is a single process that serves all your projects, so it's registered at **user scope**, not in a per-project `.mcp.json`. Only the SDK (the `reticle()` plugin / connect call) is added per project.
|
|
65
78
|
|
|
66
|
-
Re-running is safe (already-registered/already-patched steps are skipped). Preview without writing via `npx @reticlehq/server init --dry-run`. Flags: `--port N`, `--no-mcp`, `--no-install`, `--
|
|
79
|
+
Re-running is safe (already-registered/already-patched steps are skipped). Preview without writing via `npx @reticlehq/server init --dry-run`. Flags: `--dry-run`, `--port N`, `--no-mcp`, `--no-install`, `--app <dir>`.
|
|
67
80
|
|
|
68
|
-
Then restart your dev server and skip to [Step 4](#step-4
|
|
81
|
+
Then restart your dev server and skip to [Step 4](#step-4-run-it-and-verify-the-connection). The manual steps below explain what `init` sets up, if you prefer to wire it yourself.
|
|
69
82
|
|
|
70
83
|
---
|
|
71
84
|
|
|
72
|
-
## Step 1
|
|
85
|
+
## Step 1: Connect your coding agent (MCP), once
|
|
73
86
|
|
|
74
|
-
You don't start the server manually
|
|
87
|
+
You don't start the server manually; your agent starts it via MCP. Register Reticle **once, at the user (global) scope** so every project picks it up. There's nothing to add per project.
|
|
75
88
|
|
|
76
|
-
**Claude Code
|
|
89
|
+
**Claude Code**, one command:
|
|
77
90
|
|
|
78
91
|
```bash
|
|
79
92
|
claude mcp add reticle -s user -- npx @reticlehq/server mcp
|
|
@@ -81,7 +94,7 @@ claude mcp add reticle -s user -- npx @reticlehq/server mcp
|
|
|
81
94
|
|
|
82
95
|
(`reticle init` runs exactly this for you. `-s user` is what makes it global; drop it for a project-local registration instead.)
|
|
83
96
|
|
|
84
|
-
**Cursor
|
|
97
|
+
**Cursor**: add to your global `~/.cursor/mcp.json` (not per-project; `reticle init` writes this for you):
|
|
85
98
|
|
|
86
99
|
```jsonc
|
|
87
100
|
{
|
|
@@ -97,7 +110,7 @@ Other MCP clients (Windsurf, Claude Desktop, …) use the same `command`/`args`
|
|
|
97
110
|
|
|
98
111
|
---
|
|
99
112
|
|
|
100
|
-
## Step 2
|
|
113
|
+
## Step 2: Embed the SDK in your app
|
|
101
114
|
|
|
102
115
|
Install the SDK kit plus your framework's build plugin as dev dependencies (the kit re-exports the browser sensor, so one install gives both `reticle` and `install`):
|
|
103
116
|
|
|
@@ -110,7 +123,7 @@ Then call `reticle.connect()` once, in dev only. Where you put it depends on you
|
|
|
110
123
|
|
|
111
124
|
### Vite + React
|
|
112
125
|
|
|
113
|
-
**Recommended
|
|
126
|
+
**Recommended: the Vite plugin (one line, does everything).** Add `reticle()` to your `vite.config.ts`:
|
|
114
127
|
|
|
115
128
|
```ts
|
|
116
129
|
import { defineConfig } from 'vite';
|
|
@@ -118,18 +131,18 @@ import react from '@vitejs/plugin-react';
|
|
|
118
131
|
import { reticle } from '@reticlehq/vite-plugin';
|
|
119
132
|
|
|
120
133
|
export default defineConfig({
|
|
121
|
-
plugins: [
|
|
134
|
+
plugins: [reticle(), react()],
|
|
122
135
|
});
|
|
123
136
|
```
|
|
124
137
|
|
|
125
|
-
This injects `reticle.connect()` for you _and_ handles React 19 source mapping (Step 3)
|
|
138
|
+
This injects `reticle.connect()` for you _and_ handles React 19 source mapping (Step 3), so there's no entry-file edit and no separate Babel setup. `apply: 'serve'` means it's dropped from `vite build` entirely, so it can never reach production. (This is exactly what `reticle init` adds.)
|
|
126
139
|
|
|
127
140
|
<details>
|
|
128
141
|
<summary>Prefer to wire it by hand instead of the plugin?</summary>
|
|
129
142
|
|
|
130
143
|
In your entry file (`src/main.tsx`), call `connect()` in dev only:
|
|
131
144
|
|
|
132
|
-
```
|
|
145
|
+
```tsx
|
|
133
146
|
import { StrictMode } from 'react';
|
|
134
147
|
import { createRoot } from 'react-dom/client';
|
|
135
148
|
import { reticle, SESSION_AUTO } from '@reticlehq/react';
|
|
@@ -153,7 +166,7 @@ On React 19 you then also need the source-mapping Babel plugin from Step 3. The
|
|
|
153
166
|
|
|
154
167
|
### The pairing token (why some setups need one line more)
|
|
155
168
|
|
|
156
|
-
The daemon auto-generates a **pairing token** on first run and stores it at `~/.reticle/pairing-token` (owner-only, `0600`). The bridge requires it, so another app running on `http://localhost:<some-other-port>` can't quietly register or drive your session
|
|
169
|
+
The daemon auto-generates a **pairing token** on first run and stores it at `~/.reticle/pairing-token` (owner-only, `0600`). The bridge requires it, so another app running on `http://localhost:<some-other-port>` can't quietly register or drive your session. Only code that can read that file (your dev server, not a web page) can present it.
|
|
157
170
|
|
|
158
171
|
- **Vite plugin users:** nothing to do. The plugin reads the token server-side and injects it into `connect()` for you.
|
|
159
172
|
- **Next.js / hand-wired `connect()`:** your `connect()` runs in the browser and can't read the file, so pass the token in yourself. The simplest path is a shared secret: set `RETICLE_TOKEN` for the daemon (it uses that instead of auto-generating) and expose the same value to the client as `NEXT_PUBLIC_RETICLE_TOKEN`, then pass it to `connect({ token })` (see below). On a single-user machine you can also just read `~/.reticle/pairing-token` in your dev tooling and forward it the same way.
|
|
@@ -222,12 +235,12 @@ Or, with no build step, a script tag pointed at the bridge:
|
|
|
222
235
|
|
|
223
236
|
### Running multiple apps at once
|
|
224
237
|
|
|
225
|
-
It's common to have several apps open in dev
|
|
238
|
+
It's common to have several apps open in dev: a few Next.js and React projects, or multiple tabs of the same app. Reticle handles this cleanly **as long as each connection has a unique session id**, which is exactly what `SESSION_AUTO` gives you (a fresh id per tab). The examples above all use it, so you get this for free. When more than one app is connected, an Reticle tool call targets the focused / most recently active one automatically, or you can pass an explicit `sessionId` to target a specific app.
|
|
226
239
|
|
|
227
240
|
**Two separate projects, fully isolated.** If you want each repo to have its own independent Reticle bridge (separate sessions, separate `.reticle/` workspace), give each project its own port. Set the same port in both the MCP server config and the app's connection:
|
|
228
241
|
|
|
229
242
|
```jsonc
|
|
230
|
-
// project-b/.mcp.json
|
|
243
|
+
// project-b/.mcp.json: give this project its own bridge port
|
|
231
244
|
{
|
|
232
245
|
"mcpServers": {
|
|
233
246
|
"reticle": {
|
|
@@ -240,33 +253,33 @@ It's common to have several apps open in dev — a few Next.js and React project
|
|
|
240
253
|
```
|
|
241
254
|
|
|
242
255
|
```ts
|
|
243
|
-
// project-b's app
|
|
256
|
+
// project-b's app: dial the same port
|
|
244
257
|
reticle.connect({ session: SESSION_AUTO, url: 'ws://localhost:4401/reticle' });
|
|
245
258
|
```
|
|
246
259
|
|
|
247
|
-
**On the Vite plugin?** You don't have a hand-written `connect()` to edit
|
|
260
|
+
**On the Vite plugin?** You don't have a hand-written `connect()` to edit; the plugin injects it. Set the port on the plugin instead, and it bakes the matching URL in for you:
|
|
248
261
|
|
|
249
262
|
```ts
|
|
250
263
|
// project-b/vite.config.ts
|
|
251
|
-
plugins: [
|
|
264
|
+
plugins: [reticle({ port: 4401 }), react()],
|
|
252
265
|
```
|
|
253
266
|
|
|
254
|
-
Either way, the rule is the same: **the app's bridge port must equal the daemon's `RETICLE_PORT
|
|
267
|
+
Either way, the rule is the same: **the app's bridge port must equal the daemon's `RETICLE_PORT`**, and it's the Reticle bridge port, never your dev-server port. Project A stays on the default `4400`, project B on `4401`; they never touch each other. (A port that is already in use now fails fast with a clear error instead of hanging, so a misconfiguration is obvious.)
|
|
255
268
|
|
|
256
269
|
---
|
|
257
270
|
|
|
258
|
-
## Step 3
|
|
271
|
+
## Step 3: (React) component and source-file mapping
|
|
259
272
|
|
|
260
|
-
This is optional but high-value: it lets `reticle_inspect` map a DOM element back to the **React component and the source file:line
|
|
273
|
+
This is optional but high-value: it lets `reticle_inspect` map a DOM element back to the **React component and the source file:line**, so when the agent finds a problem, it knows which file to edit. (The React adapter ships in `@reticlehq/react`; nothing extra to install.)
|
|
261
274
|
|
|
262
275
|
```ts
|
|
263
276
|
import { install as installReticleReact } from '@reticlehq/react';
|
|
264
277
|
if (import.meta.env.DEV) installReticleReact(); // call before reticle.connect()
|
|
265
278
|
```
|
|
266
279
|
|
|
267
|
-
**React ≤ 18:** that's all
|
|
280
|
+
**React ≤ 18:** that's all; it uses React's dev `_debugSource`.
|
|
268
281
|
|
|
269
|
-
**React 19:** React removed `_debugSource`, so the source has to be stamped at build time. **If you added the `reticle()` Vite plugin in Step 2, this is already handled
|
|
282
|
+
**React 19:** React removed `_debugSource`, so the source has to be stamped at build time. **If you added the `reticle()` Vite plugin in Step 2, this is already handled, so skip ahead.** Otherwise add the Babel plugin (`@reticlehq/babel-plugin`) to stamp the source onto elements in dev:
|
|
270
283
|
|
|
271
284
|
```ts
|
|
272
285
|
// vite.config.ts
|
|
@@ -278,21 +291,24 @@ export default defineConfig({
|
|
|
278
291
|
});
|
|
279
292
|
```
|
|
280
293
|
|
|
281
|
-
> **Next.js:** verified on **Next.js 15 / React 19 (app router, SWC)**. For source-file mapping, use `@reticlehq/next` instead of the Babel plugin
|
|
294
|
+
> **Next.js:** verified on **Next.js 15 / React 19 (app router, SWC)**. For source-file mapping, use `@reticlehq/next` instead of the Babel plugin. It adds a **dev-only webpack pre-loader that keeps SWC** and stamps `data-reticle-source` so `reticle_inspect` returns `file:line` (e.g. `app/page.tsx:30`):
|
|
282
295
|
>
|
|
283
|
-
> ```
|
|
284
|
-
> // next.config.
|
|
285
|
-
> import
|
|
286
|
-
>
|
|
287
|
-
>
|
|
288
|
-
>
|
|
296
|
+
> ```ts
|
|
297
|
+
> // next.config.ts
|
|
298
|
+
> import { withReticle } from '@reticlehq/next';
|
|
299
|
+
> import type { NextConfig } from 'next';
|
|
300
|
+
>
|
|
301
|
+
> const nextConfig: NextConfig = {};
|
|
302
|
+
> export default withReticle(nextConfig); // no-op in production
|
|
289
303
|
> ```
|
|
290
304
|
>
|
|
305
|
+
> This is exactly what `reticle init` writes. `@reticlehq/next` is CommonJS, so a default import (`import reticleNext from '@reticlehq/next'` then `reticleNext.withReticle(...)`) and a CJS `const { withReticle } = require('@reticlehq/next')` both work too, if your config is `.mjs` or `.js`.
|
|
306
|
+
>
|
|
291
307
|
> Component identity works with or without it (Next's internal wrappers are filtered out so you see your components, e.g. just `Page`).
|
|
292
308
|
|
|
293
309
|
---
|
|
294
310
|
|
|
295
|
-
## Step 4
|
|
311
|
+
## Step 4: Run it and verify the connection
|
|
296
312
|
|
|
297
313
|
1. Start your app's dev server as usual (`npm run dev`).
|
|
298
314
|
2. Open it in the browser (the SDK connects when the page loads).
|
|
@@ -310,7 +326,7 @@ If the list is empty, see [Troubleshooting](#troubleshooting).
|
|
|
310
326
|
|
|
311
327
|
---
|
|
312
328
|
|
|
313
|
-
## Step 5
|
|
329
|
+
## Step 5: Your first verification
|
|
314
330
|
|
|
315
331
|
Now just talk to your agent in plain language. For example:
|
|
316
332
|
|
|
@@ -326,22 +342,22 @@ reticle_query({ by: "role", value: "button", name: "Refresh" }) // → ref e12
|
|
|
326
342
|
reticle_act({ ref: "e12", action: "click" }) // → { since: 920 }
|
|
327
343
|
|
|
328
344
|
// verifies the reaction
|
|
329
|
-
reticle_assert({ timeout_ms: 2000, predicate: { allOf: [
|
|
345
|
+
reticle_assert({ timeout_ms: 2000, predicate: { kind: "allOf", predicates: [
|
|
330
346
|
{ kind: "net", method: "GET", urlContains: "/api/dashboard", status: 200, since: 920 },
|
|
331
347
|
{ kind: "console", level: "error", absent: true }
|
|
332
348
|
]}})
|
|
333
349
|
// → { pass: true }
|
|
334
350
|
```
|
|
335
351
|
|
|
336
|
-
You get a real, evidence-backed answer
|
|
352
|
+
You get a real, evidence-backed answer. If it fails, the agent sees the reason (e.g. the call 404'd, or a `TypeError` in `Dashboard.tsx:88`) and can fix it and re-check.
|
|
337
353
|
|
|
338
354
|
That's the whole loop. From here, the [Usage Guide](usage.md) covers every tool, the full predicate DSL, and a dozen real situations (login, long lists, eventual consistency, file uploads, LLM calls, regressions, and more).
|
|
339
355
|
|
|
340
356
|
---
|
|
341
357
|
|
|
342
|
-
## Step 6
|
|
358
|
+
## Step 6: Make your app agent-legible (optional, high-leverage)
|
|
343
359
|
|
|
344
|
-
The basics above work with zero app changes. These four additions make the agent dramatically faster and let it verify things the DOM can't express
|
|
360
|
+
The basics above work with zero app changes. These four additions make the agent dramatically faster and let it verify things the DOM can't express; they're what turn Reticle from "usable" into "magic." All are dev-only.
|
|
345
361
|
|
|
346
362
|
**1. Stable `data-testid` on key elements.** Agents target testids more reliably than visible text (which changes with copy/i18n). Reticle matches testids _exactly_.
|
|
347
363
|
|
|
@@ -349,7 +365,7 @@ The basics above work with zero app changes. These four additions make the agent
|
|
|
349
365
|
<button data-testid="refresh">Refresh</button>
|
|
350
366
|
```
|
|
351
367
|
|
|
352
|
-
**2. `reticle.signal` for off-DOM facts.** When something matters but isn't visible
|
|
368
|
+
**2. `reticle.signal` for off-DOM facts.** When something matters but isn't visible (a save committed, a webhook arrived, an edit applied, an LLM caption finished), emit a signal the agent can assert on. This is the single highest-value instrumentation.
|
|
353
369
|
|
|
354
370
|
```ts
|
|
355
371
|
import { reticle } from '@reticlehq/react';
|
|
@@ -357,9 +373,9 @@ onSaved(() => reticle.signal('order:saved', { id, total }));
|
|
|
357
373
|
// agent: reticle_assert({ predicate: { kind: 'signal', name: 'order:saved', dataMatches: { id: '*' } } })
|
|
358
374
|
```
|
|
359
375
|
|
|
360
|
-
> **Recommended:** instead of importing `reticle` into components, inject a `createReticleEmitter()` emitter and pair each commit with `commitAndSignal(...)` so the mutation↔signal can't drift
|
|
376
|
+
> **Recommended:** instead of importing `reticle` into components, inject a `createReticleEmitter()` emitter and pair each commit with `commitAndSignal(...)` so the mutation↔signal can't drift. `reticle.signal` stays the primitive underneath. See [integration-patterns.md](integration-patterns.md).
|
|
361
377
|
|
|
362
|
-
**3. `registerStore` so the agent reads state directly.** No need to broadcast a signal for every fact
|
|
378
|
+
**3. `registerStore` so the agent reads state directly.** No need to broadcast a signal for every fact: expose the store and the agent reads it via `reticle_state`.
|
|
363
379
|
|
|
364
380
|
```ts
|
|
365
381
|
import { registerStore } from '@reticlehq/react';
|
|
@@ -379,11 +395,11 @@ registerCapabilities({
|
|
|
379
395
|
// agent: reticle_capabilities() → the whole testable surface
|
|
380
396
|
```
|
|
381
397
|
|
|
382
|
-
> **Multi-domain apps:** prefer `registerReticleDomain({ testids, signals, stores })` co-located in one `reticle.ts` per domain
|
|
398
|
+
> **Multi-domain apps:** prefer `registerReticleDomain({ testids, signals, stores })` co-located in one `reticle.ts` per domain. Each self-registers and `reticle_capabilities()` assembles the union, so there's no central map to forget. See [integration-patterns.md](integration-patterns.md).
|
|
383
399
|
|
|
384
400
|
> Watch the agent work: pass `present: true` to `reticle.connect()` for a glowing border, a cursor that flies to targets, and a HUD; the agent can call `reticle_session {action:"narrate"}({ text })` to show its intent. See [usage §16](usage.md#16-presenter-mode-narration--fake-clock-watch--control).
|
|
385
401
|
|
|
386
|
-
> **Hover-gated UI (tooltips, hover menus, pointer drag)?** Synthetic events can't trigger native `onMouseEnter`. Enable **real input** by launching your browser with `--remote-debugging-port=9222` and setting `RETICLE_CDP_URL` in the MCP server `env
|
|
402
|
+
> **Hover-gated UI (tooltips, hover menus, pointer drag)?** Synthetic events can't trigger native `onMouseEnter`. Enable **real input** by launching your browser with `--remote-debugging-port=9222` and setting `RETICLE_CDP_URL` in the MCP server `env`. Reticle then drives real pointer input and `reticle_act` reports `inputMode:"real"`. See [usage §18](usage.md#18-real-input-mode-native-hover--drag).
|
|
387
403
|
|
|
388
404
|
---
|
|
389
405
|
|
|
@@ -391,10 +407,10 @@ registerCapabilities({
|
|
|
391
407
|
|
|
392
408
|
Once the loop works, these turn ad-hoc runs into a maintained suite:
|
|
393
409
|
|
|
394
|
-
- **[Flows, recorder & self-healing](flows.md)
|
|
395
|
-
- **[Testing with `@reticlehq/test`](testing.md)**
|
|
396
|
-
- **[Human-in-the-loop control](human-control.md)
|
|
397
|
-
- **[Integration patterns](integration-patterns.md)**
|
|
410
|
+
- **[Flows, recorder & self-healing](flows.md)**: record a golden path once; Reticle saves it to a git-checked `.reticle/` flow anchored on testid+signal, replays it (with legible drift), and `reticle_flow_heal` repairs renamed anchors.
|
|
411
|
+
- **[Testing with `@reticlehq/test`](testing.md)** gives you declarative `reticleTest` specs you run headless / in CI; flows can _become_ the specs.
|
|
412
|
+
- **[Human-in-the-loop control](human-control.md)**: with `present: true`, pause / message / end the agent from the floating panel.
|
|
413
|
+
- **[Integration patterns](integration-patterns.md)** covers the recommended zero-prod-bundle emit adapter, store-layer signals, and incremental adoption.
|
|
398
414
|
|
|
399
415
|
---
|
|
400
416
|
|
|
@@ -404,22 +420,22 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
|
|
|
404
420
|
|
|
405
421
|
| Stack | SDK connect | Source mapping |
|
|
406
422
|
| --- | --- | --- |
|
|
407
|
-
| Vite + React (any) | `reticle()` plugin (auto)
|
|
423
|
+
| Vite + React (any) | `reticle()` plugin (auto), or `connect()` | `reticle()` plugin handles it (incl. React 19) |
|
|
408
424
|
| Next.js (app router) | `ReticleDev` client component in layout (dev) | `@reticlehq/next` (`withReticle`) → component + file:line |
|
|
409
425
|
| SvelteKit | `src/hooks.client.ts` (written by `reticle init`) | `reticle()` plugin stamps `.svelte` → file:line |
|
|
410
|
-
| Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none
|
|
426
|
+
| Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none (refs and testids only) |
|
|
411
427
|
|
|
412
428
|
### What Svelte support is, and what it is not
|
|
413
429
|
|
|
414
430
|
`reticle init` detects SvelteKit and writes both halves: a client hook that calls `connect()` (SvelteKit renders through `app.html`, so the plugin's HTML injection never fires) and `reticle()` in `vite.config`, which is what stamps `data-reticle-source`.
|
|
415
431
|
|
|
416
|
-
**You get** `file:line` on every element in a `.svelte` component, plus everything the framework-agnostic core already gave you
|
|
432
|
+
**You get** `file:line` on every element in a `.svelte` component, plus everything the framework-agnostic core already gave you (DOM, network, console, routing, storage, actions), and `svelteStore` for reading a Svelte store (see [usage](usage.md)).
|
|
417
433
|
|
|
418
434
|
**You do not get** component identity. `@reticlehq/react` walks the fiber tree to answer "which component rendered this element"; there is no Svelte equivalent, so snapshots carry the file and line but no component name. Stamping targets Svelte 5's compiler AST and also accepts Svelte 4's; `.svelte.ts` runes modules are code rather than markup and are not stamped.
|
|
419
435
|
|
|
420
|
-
**It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks
|
|
436
|
+
**It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks; `reticle init` says so out loud in its plan. React, Next.js, Remix and Astro each have an app and a gate. Treat SvelteKit as wired and plausible, not as supported.
|
|
421
437
|
|
|
422
|
-
**Vue is
|
|
438
|
+
**Vue is install-gated, drive-unverified.** `init` detects a Vue app (as a Vite app with Vue as the UI library) and the install gate scaffolds one from scratch, runs `init`, boots it and waits for a session, so the setup is proven. What is not proven is the drive: there is no Vue example app in CI. `piniaStore` reads a Pinia store, and everything the framework-neutral core provides works. What you do not get is a `source` field, because source stamping covers JSX and Svelte components and a `.vue` single-file component is neither. See [Frameworks](/frameworks) for the full status table.
|
|
423
439
|
|
|
424
440
|
---
|
|
425
441
|
|
|
@@ -427,13 +443,13 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
|
|
|
427
443
|
|
|
428
444
|
**`reticle_sessions` is empty / "no browser session connected"**
|
|
429
445
|
|
|
430
|
-
- Run **`reticle status
|
|
446
|
+
- Run **`reticle status`**. It shows whether the daemon is up and which tabs are connected (url, health, pending flagged bugs) at a glance. No connected sessions means the SDK isn't reaching the bridge.
|
|
431
447
|
- Is your app actually running and open in a browser tab?
|
|
432
448
|
- Is `reticle.connect()` running? (Check it's inside your dev guard and the guard is true.)
|
|
433
449
|
- Port mismatch? If you set `RETICLE_PORT`, pass the same URL to `reticle.connect({ url: 'ws://localhost:<port>/reticle' })`.
|
|
434
|
-
- Need to restart the daemon? **`reticle stop`** cleans it up
|
|
450
|
+
- Need to restart the daemon? **`reticle stop`** cleans it up, no `pkill` needed.
|
|
435
451
|
|
|
436
|
-
The errors Reticle returns to the agent now carry a `recovery` hint for this exact situation (and for multiple/unknown sessions, a throttled tab, a missing baseline)
|
|
452
|
+
The errors Reticle returns to the agent now carry a `recovery` hint for this exact situation (and for multiple/unknown sessions, a throttled tab, a missing baseline), so the agent knows the next move.
|
|
437
453
|
|
|
438
454
|
**The agent can't find an element**
|
|
439
455
|
|
|
@@ -452,13 +468,13 @@ The errors Reticle returns to the agent now carry a `recovery` hint for this exa
|
|
|
452
468
|
|
|
453
469
|
**Nothing should run in production**
|
|
454
470
|
|
|
455
|
-
- Keep `reticle.connect()` behind a dev guard (`import.meta.env.DEV` / `NODE_ENV`). The package is side-effect free and tree-shakes out when unused. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production` (so an SSR healthcheck or a prod bundle opened on localhost won't activate it)
|
|
471
|
+
- Keep `reticle.connect()` behind a dev guard (`import.meta.env.DEV` / `NODE_ENV`). The package is side-effect free and tree-shakes out when unused. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production` (so an SSR healthcheck or a prod bundle opened on localhost won't activate it). Pass `allowInProduction: true` only for a deliberate prod diagnostic.
|
|
456
472
|
|
|
457
473
|
## Installing alongside a Next.js or React prerelease
|
|
458
474
|
|
|
459
|
-
`@reticlehq/next` declares `peer next >=13`, and `@reticlehq/react` declares `peer react >=18`. If your app runs a **prerelease
|
|
475
|
+
`@reticlehq/next` declares `peer next >=13`, and `@reticlehq/react` declares `peer react >=18`. If your app runs a **prerelease**, such as a Next.js canary/preview (`16.3.0-preview.9`) or a React RC, npm will refuse the install with `ERESOLVE`.
|
|
460
476
|
|
|
461
|
-
That is npm's semver rule, not a Reticle restriction: a prerelease version satisfies a range only when some comparator shares its exact `major.minor.patch`. No floor-style range accepts it
|
|
477
|
+
That is npm's semver rule, not a Reticle restriction: a prerelease version satisfies a range only when some comparator shares its exact `major.minor.patch`. No floor-style range accepts it (verified, including `*`). Marking the peer optional does not help either, because npm still version-checks a peer that is present.
|
|
462
478
|
|
|
463
479
|
Install with either of these instead. Both are safe; the floor is a real minimum, not a maximum:
|
|
464
480
|
|
|
@@ -467,3 +483,39 @@ npm install @reticlehq/next --legacy-peer-deps
|
|
|
467
483
|
# or use pnpm, whose peer resolution does not hard-fail here
|
|
468
484
|
pnpm add @reticlehq/next
|
|
469
485
|
```
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Frequently asked questions
|
|
490
|
+
|
|
491
|
+
### What exactly does `reticle init` change in my project?
|
|
492
|
+
|
|
493
|
+
Four things, and none of them are mysterious: a `.reticle.json` project config, your build config (the `reticle()` Vite plugin, or `withReticle` in `next.config`), a dev-only capabilities file at `src/reticle-dev.ts` (or `app/reticle-dev.tsx` on Next.js), and your agent's rule files. It also registers the MCP server globally, which is a once-per-machine step rather than a per-project one. Run `npx @reticlehq/server init --dry-run` first to see the exact plan before anything is written.
|
|
494
|
+
|
|
495
|
+
### Do I have to re-register the MCP server for every project?
|
|
496
|
+
|
|
497
|
+
No. The bridge and MCP server are a single process serving all your projects, so `init` registers it at user scope. Every later project starts with the tools already there. Only the SDK wiring is per project.
|
|
498
|
+
|
|
499
|
+
### Why do the Reticle tools not appear after installing?
|
|
500
|
+
|
|
501
|
+
Because your agent read its MCP server list at startup, before Reticle existed, and no slash command re-reads it. Restart the agent process: quit and reopen Claude Code, reload the window in Cursor, or press Start in `.vscode/mcp.json` in VS Code. `/mcp` only manages servers that are already loaded, so it cannot discover a new one.
|
|
502
|
+
|
|
503
|
+
### Which Node version do I need?
|
|
504
|
+
|
|
505
|
+
Node 20 or newer.
|
|
506
|
+
|
|
507
|
+
### Do I need the React adapter?
|
|
508
|
+
|
|
509
|
+
No, it is optional enrichment and the core works without it. What it adds is component identity, meaning `reticle_inspect` can say which React component rendered an element. Combined with a build plugin that stamps source, that is what turns a DOM node into `src/components/Login.tsx:81`, which is the difference between an agent knowing something failed and knowing which file to open.
|
|
510
|
+
|
|
511
|
+
### Can I run several apps against Reticle at once?
|
|
512
|
+
|
|
513
|
+
Yes, as long as each connection has a unique session id, which `SESSION_AUTO` gives you automatically. Tool calls target the focused or most recently active app, or you can pass an explicit `sessionId`. If you want two projects fully isolated, give each its own bridge port and set the same port in both the MCP server config and the app's connection.
|
|
514
|
+
|
|
515
|
+
### Will Reticle end up in my production bundle?
|
|
516
|
+
|
|
517
|
+
No. Keep `connect()` behind a dev guard (`import.meta.env.DEV` or a `NODE_ENV` check); the packages are side-effect free and tree-shake out when unused. The Vite plugin uses `apply: 'serve'`, so it is dropped from `vite build` entirely. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production`.
|
|
518
|
+
|
|
519
|
+
### Why is my install failing with `ERESOLVE`?
|
|
520
|
+
|
|
521
|
+
You are almost certainly on a Next.js canary or a React RC. That is npm's semver rule for prereleases, not a Reticle restriction. Install with `--legacy-peer-deps`, or use pnpm, whose peer resolution does not hard-fail here. See [Installing alongside a Next.js or React prerelease](#installing-alongside-a-nextjs-or-react-prerelease) above.
|