@reticlehq/server 2.3.0 → 2.5.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/SKILL.md +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +182 -93
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +26 -0
- package/dist/init/detect.js +78 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +58 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +20 -2
- package/dist/session/session.js +76 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
# Reticle
|
|
2
|
+
|
|
3
|
+
Start by detecting which mode to run:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Is Reticle already set up in this project?
|
|
7
|
+
cat .reticle.json 2>/dev/null || echo "NOT_FOUND"
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- **`.reticle.json` not found → run Setup (below)**
|
|
11
|
+
- **`.reticle.json` found → run Test (further below)**
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# SETUP MODE
|
|
16
|
+
|
|
17
|
+
> Run this once per project. Writes config files, installs the SDK, and validates the connection. After setup, every subsequent `/reticle` goes straight to Test mode.
|
|
18
|
+
|
|
19
|
+
## Step 1 — Run `init`. Ask the user nothing.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @reticlehq/server init
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That is the setup. It takes a few seconds and it does the whole job: detects the framework, package manager and UI library; registers the MCP server globally with whichever agents are installed; writes the agent verification rule **and the `/reticle` slash command**; installs the SDK pinned to the CLI's version; writes `.reticle.json`; wires the build config — the Vite plugin, or all three Next.js files (`next.config`, the root layout, and the `ReticleDev` component); and generates a **capabilities scaffold** pre-filled with the `data-testid` values it found and the state library it detected.
|
|
26
|
+
|
|
27
|
+
**Ask the user nothing. Not one question.** Not which framework, not which package manager, not which port, not which editor or MCP client, not whether they have `data-testid` attributes. Every one of those is answerable from the repository you are already sitting in, and `init` answers them itself — from `package.json`, the lockfile, the config files, and which agent CLIs exist on the machine. The people this is built for do not know the answers, and asking is how a two-minute setup became an hour.
|
|
28
|
+
|
|
29
|
+
If you genuinely cannot determine something, pick the sensible default, say which default you picked in one line, and keep going. A wrong default that gets corrected in ten seconds beats a question that blocks for ten minutes.
|
|
30
|
+
|
|
31
|
+
**Never ask about the port.** There are two different ports and conflating them is the single most common setup failure:
|
|
32
|
+
|
|
33
|
+
| | What it is | Who owns it |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| Dev-server port (3000, 5173, 4321, …) | where the app is served | the user's `npm run dev` — Reticle never touches it |
|
|
36
|
+
| Bridge port (**4400**) | the daemon ↔ SDK channel | Reticle, and it defaults correctly |
|
|
37
|
+
|
|
38
|
+
Reticle **attaches** to whatever is already running; it never starts or manages a dev server, so it does not need to know that port. Putting a dev-server port in `.reticle.json`'s `port` field makes the daemon fight the app for it. Leave `port` out unless you are running several apps at once.
|
|
39
|
+
|
|
40
|
+
**In a monorepo, run it at the repo root anyway.** If the root isn't the app, `init` finds the app under `apps/*` or `packages/*` and wires that instead. If it finds several, it lists them — pick the one the user has been working in (the one their recent edits touch) and say which you picked. Do not put the list to them as a question.
|
|
41
|
+
|
|
42
|
+
Then read the report. Each line is marked:
|
|
43
|
+
|
|
44
|
+
| Mark | Meaning | What you do |
|
|
45
|
+
| ---- | ------------------------ | --------------------------------------------------- |
|
|
46
|
+
| `✓` | applied | nothing |
|
|
47
|
+
| `·` | already wired | nothing |
|
|
48
|
+
| `–` | skipped by a flag | nothing |
|
|
49
|
+
| `⚠` | needs a human/agent edit | **only these** — the line carries the exact snippet |
|
|
50
|
+
|
|
51
|
+
**If every line is `✓`, `·` or `–`, skip to Step 4 and validate.** The manual sections below exist for the `⚠` lines only.
|
|
52
|
+
|
|
53
|
+
**`init` exits non-zero when a `⚠` lands on a step that makes the app CONNECT** (the Vite plugin, the `ReticleDev` component, the connect snippet). Nothing else applies that step, so the app will never dial the daemon and every tool will answer "no browser session connected" until you paste it in. A non-zero exit is therefore a to-do list for you, not a failed install — apply the snippet on that line, then validate. Other `⚠` lines (MCP registration, the agent rule) exit 0.
|
|
54
|
+
|
|
55
|
+
Useful flags: `--port N` (only when running several apps at once), `--no-install` (you'll run the package manager yourself), `--no-mcp` (skips the agent registration **and** the agent rule files and the `/reticle` command — all three only make sense once the tools are reachable), `--dry-run` (preview), `--app <dir>` (pick which app in a monorepo).
|
|
56
|
+
|
|
57
|
+
**What is proven:** Vite + React, Next.js, Remix and Astro each have an app in this repo and a CI gate that drives it — the first two in the `pnpm test:e2e` battery, Remix and Astro in `pnpm test:integration`. Plain HTML and bundled non-Vite apps (CRA, webpack, Parcel) are wired by hand and have **no** app and no gate: they may well work, nothing proves it. The SDK is framework-agnostic and will usually connect elsewhere — but on a Vue, Preact or Svelte app `init` prints an UNVERIFIED line saying which parts work (DOM, network, console, state) and which do not (component names, `file:line`). Repeat that to the user rather than reporting a clean install.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Manual fallback — Configure the MCP server
|
|
62
|
+
|
|
63
|
+
> **Only if `init` printed `⚠` for the MCP step**, or the user runs an agent `init` doesn't register (it handles Claude Code and Cursor automatically; the rest are below).
|
|
64
|
+
|
|
65
|
+
There is no single MCP config file all tools share. Each harness has its own file and schema — write only the one the user actually uses.
|
|
66
|
+
|
|
67
|
+
| Tool | File | Root key | Command format | `type` needed? |
|
|
68
|
+
| --- | --- | --- | --- | --- |
|
|
69
|
+
| Claude Code | `~/.claude.json` (user scope; prefer the `claude mcp add` CLI) | `mcpServers` | `"command"` + `"args"` split | no |
|
|
70
|
+
| OpenCode | `opencode.json` | `mcp` | `"command"` flat array | `"local"` required |
|
|
71
|
+
| Codex CLI | `.codex/config.toml` | `[mcp_servers.reticle]` | TOML `command` + `args` | no |
|
|
72
|
+
| Cursor | `~/.cursor/mcp.json` (global — what `reticle init` writes) | `mcpServers` | `"command"` + `"args"` split | no |
|
|
73
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` | `"command"` + `"args"` split | no |
|
|
74
|
+
| VS Code | `.vscode/mcp.json` | `"servers"` | `"command"` + `"args"` split | no |
|
|
75
|
+
| Zed | `~/.config/zed/settings.json` | `context_servers` | `"command"` + `"args"` split | no |
|
|
76
|
+
|
|
77
|
+
**Claude Code** (user-level, default)
|
|
78
|
+
|
|
79
|
+
Register once globally so Reticle is available in every project:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
claude mcp add reticle -s user -- npx @reticlehq/server mcp
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Confirm with `claude mcp list` — `reticle` should appear. **After adding, restart Claude Code** (or run `/mcp` to refresh) so it picks up the server.
|
|
86
|
+
|
|
87
|
+
**If the `claude` CLI is unavailable**, fall back to merging `"reticle"` into `mcpServers` in `~/.claude.json` — Claude Code's user-scope config, and the same file `claude mcp add` writes. It is a large stateful file, so merge one key; never rewrite it:
|
|
88
|
+
|
|
89
|
+
```jsonc
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"reticle": {
|
|
93
|
+
"command": "npx",
|
|
94
|
+
"args": ["@reticlehq/server", "mcp"],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Only write to `.mcp.json` (project root) if the user explicitly asks for project-level registration.
|
|
101
|
+
|
|
102
|
+
**OpenCode — `opencode.json`** (`type:"local"` required; command is one flat array, no `args`)
|
|
103
|
+
|
|
104
|
+
```jsonc
|
|
105
|
+
{
|
|
106
|
+
"mcp": {
|
|
107
|
+
"reticle": {
|
|
108
|
+
"type": "local",
|
|
109
|
+
"command": ["npx", "@reticlehq/server", "mcp"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Verify with `opencode mcp list`.
|
|
116
|
+
|
|
117
|
+
**Codex CLI — `.codex/config.toml`** (TOML, not JSON)
|
|
118
|
+
|
|
119
|
+
```toml
|
|
120
|
+
[mcp_servers.reticle]
|
|
121
|
+
command = "npx"
|
|
122
|
+
args = ["@reticlehq/server", "mcp"]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Cursor — `~/.cursor/mcp.json`** (same schema as Claude Code, different path. Global, not project-relative: `reticle init` manages this file, so editing a project-local `.cursor/mcp.json` edits something nothing else reads)
|
|
126
|
+
|
|
127
|
+
```jsonc
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"reticle": {
|
|
131
|
+
"command": "npx",
|
|
132
|
+
"args": ["@reticlehq/server", "mcp"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Windsurf — `~/.codeium/windsurf/mcp_config.json`** (global; create if missing)
|
|
139
|
+
|
|
140
|
+
```jsonc
|
|
141
|
+
{
|
|
142
|
+
"mcpServers": {
|
|
143
|
+
"reticle": {
|
|
144
|
+
"command": "npx",
|
|
145
|
+
"args": ["@reticlehq/server", "mcp"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**VS Code — `.vscode/mcp.json`** (`"servers"` not `"mcpServers"` — most common mistake)
|
|
152
|
+
|
|
153
|
+
```jsonc
|
|
154
|
+
{
|
|
155
|
+
"servers": {
|
|
156
|
+
"reticle": {
|
|
157
|
+
"command": "npx",
|
|
158
|
+
"args": ["@reticlehq/server", "mcp"],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
MCP tools only appear in Copilot **Agent mode**.
|
|
165
|
+
|
|
166
|
+
**Zed — `~/.config/zed/settings.json`** (`context_servers` not `mcpServers`)
|
|
167
|
+
|
|
168
|
+
```jsonc
|
|
169
|
+
{
|
|
170
|
+
"context_servers": {
|
|
171
|
+
"reticle": {
|
|
172
|
+
"command": "npx",
|
|
173
|
+
"args": ["@reticlehq/server", "mcp"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Step 1b — Stop hook (Claude Code only — skip unless asked)
|
|
182
|
+
|
|
183
|
+
**Do not add this hook by default.** Killing the daemon after every turn is the most common cause of the "Failed to reconnect to reticle: -32000" error: the daemon is stopped, Claude Code immediately reconnects, and the new daemon sometimes takes longer than expected to boot — the proxy times out and exits with code 1, which Claude Code reports as -32000.
|
|
184
|
+
|
|
185
|
+
Reticle doesn't need the hook. `reticle_session {action:"yield"}` (mandatory — see Rules) signals turn end in-band, and the server flips the panel to "waiting" automatically if the agent goes quiet.
|
|
186
|
+
|
|
187
|
+
Only add this if the user explicitly asks for the daemon to stop between turns:
|
|
188
|
+
|
|
189
|
+
```jsonc
|
|
190
|
+
{
|
|
191
|
+
"hooks": {
|
|
192
|
+
"Stop": [
|
|
193
|
+
{
|
|
194
|
+
"matcher": "",
|
|
195
|
+
"hooks": [{ "type": "command", "command": "npx @reticlehq/server stop --quiet" }],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Manual fallback — Install the SDK
|
|
205
|
+
|
|
206
|
+
> Only if `init` printed `⚠` for the install step (offline, a locked registry, an unusual package manager).
|
|
207
|
+
|
|
208
|
+
> **Mental model:** The user keeps running their dev server (`npm run dev`) themselves. Reticle embeds a tiny SDK in the app that connects to a local bridge daemon. The agent talks to the daemon over MCP — no Chromium is downloaded or needed for standard agent workflows. Playwright is only required if you explicitly use `--drive` mode.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npm install --save-dev @reticlehq/react @reticlehq/vite-plugin # swap npm for the project's package manager
|
|
212
|
+
# Next.js instead of Vite? npm install --save-dev @reticlehq/react @reticlehq/next
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Manual fallback — Wire up the SDK
|
|
218
|
+
|
|
219
|
+
> Only for the files `init` marked `⚠`. It auto-patches `vite.config.*` and all three Next.js files; it bails to `⚠` when a config's shape isn't one it recognises, and prints the snippet you need on that line.
|
|
220
|
+
|
|
221
|
+
**Vite + React**
|
|
222
|
+
|
|
223
|
+
Add the Reticle plugin to `vite.config.ts` — it auto-injects `reticle.connect()` in dev builds:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
// vite.config.ts
|
|
227
|
+
import { reticle } from '@reticlehq/vite-plugin';
|
|
228
|
+
|
|
229
|
+
export default defineConfig({
|
|
230
|
+
plugins: [react(), reticle()], // reticle() is dev-only, dropped from vite build
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Then describe your app's testable surface so the agent knows what to drive (fill in your real values):
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
// src/reticle-dev.ts — self-guards on import.meta.env.DEV, so it's a no-op in prod
|
|
238
|
+
import { registerCapabilities, registerStore } from '@reticlehq/react';
|
|
239
|
+
import { useApp } from './store'; // your zustand/Redux store
|
|
240
|
+
if (import.meta.env.DEV) {
|
|
241
|
+
// Register your store(s). This is the highest-value line in this file: it is what lets the agent
|
|
242
|
+
// check what the app BELIEVES, not just what it rendered — the class of bug a screenshot cannot see.
|
|
243
|
+
registerStore('app', useApp); // zustand or Redux: pass the store itself
|
|
244
|
+
registerCapabilities({
|
|
245
|
+
testids: [], // your data-testid values, e.g. ['login-btn', 'submit-form']
|
|
246
|
+
signals: [], // your reticle.signal() names, e.g. ['auth:login']
|
|
247
|
+
stores: ['app'],
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Registering the store is the step people skip, and it is the one that matters.** Pass the store itself (not `() => store.getState()`) — the store form wires `subscribe` too, so every mutation emits a state diff automatically; the getter form is read-only and silently produces empty diffs.
|
|
253
|
+
|
|
254
|
+
Which libraries work:
|
|
255
|
+
|
|
256
|
+
| Library | How |
|
|
257
|
+
| --- | --- |
|
|
258
|
+
| zustand, Redux, Redux Toolkit | `registerStore('app', store)` — no adapter needed |
|
|
259
|
+
| **TanStack Query** | `registerStore('queries', tanstackQueryStore(queryClient))` |
|
|
260
|
+
| Jotai | `registerStore('app', jotaiStore(getDefaultStore(), { cart, user }))` |
|
|
261
|
+
| XState / Valtio / MobX | `xstateStore(actor)` / `valtioStore(...)` / `mobxStore(...)` |
|
|
262
|
+
| Svelte stores | `registerStore('cart', svelteStore(cartStore))` |
|
|
263
|
+
| Pinia (Vue) | `registerStore('cart', piniaStore(useCartStore()))` |
|
|
264
|
+
| Recoil | `registerStore('app', recoilStore({ cart: cartAtom }, getSnapshot, subscribe))` — see the bridge in [docs/usage.md](docs/usage.md) |
|
|
265
|
+
| React Context / useState / useReducer | `useReticleStore('cart', cart)` from `@reticlehq/react/store` |
|
|
266
|
+
|
|
267
|
+
Register TanStack Query even if you register nothing else: a stale cache served as fresh fires **no network request**, so the network log shows silence and the DOM shows a plausible number — the cache is the only witness. Adapters come from `@reticlehq/browser`.
|
|
268
|
+
|
|
269
|
+
**You do not need to import this file.** `@reticlehq/vite-plugin` imports `src/reticle-dev.*` by convention, so `init` never has to edit the entry file you own. (Only if you are wiring by hand with `inject: false` do you add `if (import.meta.env.DEV) import('./reticle-dev');` to `src/main.tsx` yourself.)
|
|
270
|
+
|
|
271
|
+
To emit `reticle.signal()` from app code (components, stores), **never import `reticle` statically** — a top-level `import { reticle } from '@reticlehq/react'` drags the whole dev-only SDK into your production bundle. Funnel signals through a dev-guarded helper so `import.meta.env.DEV` dead-code-eliminates it in prod:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
// src/reticle.ts — import { signal } from './reticle' in your components
|
|
275
|
+
export function signal(name: string, data?: Record<string, unknown>): void {
|
|
276
|
+
if (!import.meta.env.DEV) return;
|
|
277
|
+
void import('@reticlehq/react').then(({ reticle }) => reticle.signal(name, data));
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Next.js (App Router)**
|
|
282
|
+
|
|
283
|
+
`init` creates this file, wraps `next.config`, and mounts the component for you — all three are `✓` on a normal `create-next-app`. Use this only to extend the generated file (adding `registerStore` / `registerCapabilities`), or if one of the three came back `⚠`.
|
|
284
|
+
|
|
285
|
+
Create it next to your root layout — `app/reticle-dev.tsx`, or `src/app/reticle-dev.tsx` in a `--src-dir` app:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
'use client';
|
|
289
|
+
import { useEffect } from 'react';
|
|
290
|
+
export function ReticleDev() {
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
if (process.env.NODE_ENV !== 'development') return;
|
|
293
|
+
void import('@reticlehq/react').then(
|
|
294
|
+
({ reticle, install, registerCapabilities, registerStore }) => {
|
|
295
|
+
install();
|
|
296
|
+
// The bridge requires a pairing token even on localhost. Vite gets it injected by the
|
|
297
|
+
// plugin; on Next it comes from withReticle(), which publishes it as NEXT_PUBLIC_RETICLE_TOKEN.
|
|
298
|
+
// Omit it and the browser logs "bridge refused the connection: authentication failed".
|
|
299
|
+
const token = process.env.NEXT_PUBLIC_RETICLE_TOKEN;
|
|
300
|
+
reticle.connect({ ...(token ? { token } : {}) });
|
|
301
|
+
registerStore('app', useApp); // your store — see the table above for non-zustand libraries
|
|
302
|
+
registerCapabilities({
|
|
303
|
+
testids: [], // your data-testid values
|
|
304
|
+
signals: [], // your reticle.signal() names
|
|
305
|
+
stores: ['app'],
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
}, []);
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Mount it in `app/layout.tsx` (dev-only):
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { ReticleDev } from './reticle-dev';
|
|
318
|
+
// inside <body>:
|
|
319
|
+
{
|
|
320
|
+
process.env.NODE_ENV === 'development' ? <ReticleDev /> : null;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Add `@reticlehq/next` → `withReticle` to `next.config.*`. It does two jobs: source mapping (`data-reticle-source`), and publishing the pairing token the connect above needs.
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
import { withReticle } from '@reticlehq/next';
|
|
328
|
+
export default withReticle(nextConfig);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
It configures **both** Turbopack and webpack, so it is correct on Next 16 (Turbopack by default) and on Next 15 and earlier. If `next dev` on Next 16 dies with _"This build is using Turbopack, with a webpack config and no turbopack config"_, you are on an old `@reticlehq/next` — upgrade it rather than dropping `withReticle`.
|
|
332
|
+
|
|
333
|
+
**Other frameworks** — call `reticle.connect()` and `install()` inside a dev guard. Vanilla / HTML: use a dynamic `import('@reticlehq/react')` inside `if (location.hostname === 'localhost')`.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Step 4 — Validate the connection
|
|
338
|
+
|
|
339
|
+
**The human can see this.** Reticle's HUD is on by default — a glow border round the page, an animated cursor, and a narration line for each action — and any browser Reticle launches itself is visible by default too. Neither needs a flag. Tell the user to keep the tab in view while you drive: watching the agent operate their own app IS the demo, and it is what makes the first two minutes land. (`--headless` hides the launched browser; CI does that automatically. `reticle.connect({ present: false })` turns the HUD off.)
|
|
340
|
+
|
|
341
|
+
`init` already wrote `.reticle.json`. Verify with:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
cat .reticle.json
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
If setting up manually, write `.reticle.json` to the project root (commit this — `reticle mcp` reads it to pick the right port):
|
|
348
|
+
|
|
349
|
+
```jsonc
|
|
350
|
+
{
|
|
351
|
+
"framework": "vite",
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`framework` is one of `vite`, `next`, `sveltekit`, `astro`, `cra`, `html`. **Leave `port` out** — it defaults to `4400` and everything just works for a single app.
|
|
356
|
+
|
|
357
|
+
> **`port` here is the Reticle _bridge_ port — NOT your dev server port.** The bridge is the daemon ↔ SDK channel (default `4400`); your dev server (e.g. 5173) is a separate thing Reticle never touches. Do **not** set `.reticle.json` `port` to your dev-server port — that collides the daemon with your app.
|
|
358
|
+
>
|
|
359
|
+
> Only set `port` when running **multiple apps at once**, so each gets its own bridge. When you do, set the **same** value in two places or the SDK and daemon never meet:
|
|
360
|
+
>
|
|
361
|
+
> ```jsonc
|
|
362
|
+
> // .reticle.json → reticle({ port: 4460 }) in vite.config.ts
|
|
363
|
+
> { "framework": "vite", "port": 4460 }
|
|
364
|
+
> ```
|
|
365
|
+
>
|
|
366
|
+
> Pick any free port that is **not** your dev-server port (4460, 4461, …).
|
|
367
|
+
|
|
368
|
+
Tell the user: **"Run `npm run dev` (your normal dev server) and open the app in your browser."**
|
|
369
|
+
|
|
370
|
+
Once they confirm the app is open, poll `reticle_sessions()` until your tab appears (the first live call already blocks for the session). You should see a session whose URL matches the app's localhost address.
|
|
371
|
+
|
|
372
|
+
### No session appeared? Work this checklist in order
|
|
373
|
+
|
|
374
|
+
Most no-connect cases are one of these. Fastest signal first:
|
|
375
|
+
|
|
376
|
+
1. **Read the browser console.** The SDK announces its own failures. If you see `[Reticle] could not reach the bridge at ws://localhost:<port> … Is the Reticle daemon running on that port?` — that's a **port mismatch or a dead daemon** (items 2–3), and the message tells you which port the app is dialing. No `[Reticle]` line at all → the SDK never loaded (item 4).
|
|
377
|
+
2. **Port match (the #1 manual-setup bug).** The app's bridge port MUST equal the daemon's. Check both:
|
|
378
|
+
- App side: `reticle({ port: N })` in `vite.config.ts` (or `connect({ url: 'ws://localhost:N/reticle' })`) — omitted ⇒ `4400`.
|
|
379
|
+
- Daemon side: `.reticle.json` `"port"` / `RETICLE_PORT` — omitted ⇒ `4400`. They must be the **same number**, and it must **not** be your dev-server port. Simplest fix: remove the port from both and let them default to `4400`.
|
|
380
|
+
3. **Is the daemon up on that port?** `npx @reticlehq/server status` lists running daemons + connected sessions. Nothing there ⇒ the agent hasn't launched it yet (restart the agent / `/mcp`), or it's on a different port than the app (item 2).
|
|
381
|
+
4. **Is the SDK actually wired + loaded in dev?** `reticle()` present in `vite.config.ts`; for the manual entry, `if (import.meta.env.DEV) import('./reticle-dev')` in `src/main.tsx`. After editing `vite.config.ts` you **must restart `vite`** (config changes need a fresh dev server), then **hard-reload the browser tab**. A production build won't connect — the SDK is dev-only by design.
|
|
382
|
+
5. **Still nothing?** See the full [Troubleshooting](#troubleshooting) section (stale `npx` cache, Stop-hook killing the daemon, `-32000`).
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Step 5 — Prove it on ONE flow, while the user watches
|
|
387
|
+
|
|
388
|
+
**Do not stop at "connected".** A connected session is not a result; the user has installed something and seen nothing happen. Drive one flow now, in front of them. This is the whole first impression.
|
|
389
|
+
|
|
390
|
+
**One flow. Not the app.** The person installing this has an existing project with dozens of flows. An agent that tries to instrument all of them spends ten minutes producing nothing to look at. Pick the single most important flow that completes in a handful of steps — the one a user would do first — say which one you picked in a line, and drive only that.
|
|
391
|
+
|
|
392
|
+
**You do not need to add `data-testid` anywhere to do this.** `reticle_snapshot` addresses elements by role and name, and it works on an app that has never heard of Reticle. Adding testids is an optimisation for flows you will replay often — it is not a prerequisite, and treating it as one is what turns a two-minute setup into an afternoon.
|
|
393
|
+
|
|
394
|
+
1. Tell the user: **"Keep the tab visible — you'll see this happen."** The HUD is on by default: a glow border, a moving cursor, and a narration line per step.
|
|
395
|
+
2. `reticle_snapshot` → find the elements the flow needs.
|
|
396
|
+
3. Walk it with `reticle_act_and_wait`, narrating each step before you take it.
|
|
397
|
+
4. `reticle_assert` after each step — that the effect happened, not just that the click dispatched.
|
|
398
|
+
5. `reticle_console` + `reticle_network` at the end, for what the DOM does not show.
|
|
399
|
+
|
|
400
|
+
Then report what you drove and what it produced, with `file:line` for anything broken.
|
|
401
|
+
|
|
402
|
+
**Only after that flow has run**, tell the user:
|
|
403
|
+
|
|
404
|
+
> "Reticle is set up, and you just watched it drive <flow>. Type `/reticle` any time to verify a flow after a change — `init` created that command in this project."
|
|
405
|
+
|
|
406
|
+
**Setup complete — stop here. Do not proceed to Test mode.**
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
# TEST MODE
|
|
413
|
+
|
|
414
|
+
> Runs automatically when `.reticle.json` exists. Connects to the running app, exercises flows, asserts outcomes, and reports what passed and what broke.
|
|
415
|
+
|
|
416
|
+
## Phase 1 — Connect
|
|
417
|
+
|
|
418
|
+
Just ran `reticle init` or started the dev server? Block until the app's SDK connects first, so your first real call doesn't lose the race with the WebSocket — just poll **`reticle_sessions()`** (readiness is server-internal now — the first live call blocks until the SDK connects) until your tab appears. Then, with `reticle_sessions()`, there are three possible states:
|
|
419
|
+
|
|
420
|
+
**A. One session → proceed.**
|
|
421
|
+
|
|
422
|
+
**B. No sessions:** Tell the user:
|
|
423
|
+
|
|
424
|
+
> "No app connected. Run your dev server (`npm run dev`) and open the app in your browser, then try `/reticle` again. Reticle never starts the dev server for you — that's your job." Stop here.
|
|
425
|
+
|
|
426
|
+
**C. Multiple sessions — ask:**
|
|
427
|
+
|
|
428
|
+
> "I see [N] sessions connected: [list sessionId + url]. Which should I test?"
|
|
429
|
+
|
|
430
|
+
Pin `sessionId` for every subsequent call.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Phase 2 — Orient
|
|
435
|
+
|
|
436
|
+
Call these in parallel:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
reticle_snapshot({ sessionId, mode: "interactive" })
|
|
440
|
+
reticle_run({ tool: "reticle_capabilities", sessionId }) // not advertised directly — reach it via reticle_run
|
|
441
|
+
reticle_network({ sessionId, limit: 10 })
|
|
442
|
+
reticle_console({ sessionId, limit: 20 })
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
> **There is one tool surface, and nothing to pick.** The verify loop is advertised directly (navigate / snapshot / query / act / act_and_wait / observe / network / console / wait_for / assert / state / inspect / sessions / session / feedback). Everything else the skill names — `reticle_capabilities`, `reticle_act_sequence`, the flow and record tools — is one hop away through two meta-tools: `reticle_run({ tool, args })` invokes any tool by name, `reticle_tools` lists them (pass `names:[…]` for full params). `reticle_run` takes `sessionId` at the top level and forwards it, so you do not have to nest it in `args`. Advertised counts, checked by a gate (`surface-sizes.test.ts`): `default` 17, `all` 48.
|
|
446
|
+
>
|
|
447
|
+
> `all` is a verification switch, not a mode to drive agents in: `RETICLE_ADVERTISE_ALL_TOOLS=1` advertises every tool **with its output schema**, at several times the per-turn cost. It is read by the DAEMON at startup, never by your client — exporting it while a daemon is already running changes nothing, so run `npx @reticlehq/server stop` first.
|
|
448
|
+
|
|
449
|
+
Build a mental model:
|
|
450
|
+
|
|
451
|
+
- **Route/screen:** where is the app right now?
|
|
452
|
+
- **Testids:** what interactive elements are registered?
|
|
453
|
+
- **Signals:** what domain events does the app emit?
|
|
454
|
+
- **Console state:** any errors already present before touching anything?
|
|
455
|
+
|
|
456
|
+
Pre-existing console errors → call them out immediately before testing.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Phase 3 — Decide what to test
|
|
461
|
+
|
|
462
|
+
Check what changed: `git diff HEAD --stat 2>/dev/null | head -20`
|
|
463
|
+
|
|
464
|
+
Then pick a mode:
|
|
465
|
+
|
|
466
|
+
| Context | Mode |
|
|
467
|
+
| ------------------------------------------ | ------------------------------------------------ |
|
|
468
|
+
| User says "test X" or names a flow | **Targeted** — focus on that feature |
|
|
469
|
+
| User says "everything" or "smoke test" | **Smoke** — exercise every registered testid |
|
|
470
|
+
| Recent git diff shows a specific component | **Targeted** — that component and adjacent flows |
|
|
471
|
+
| No clear signal | **Smoke** |
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Phase 4 — Run the tests
|
|
476
|
+
|
|
477
|
+
### Targeted
|
|
478
|
+
|
|
479
|
+
1. Navigate if needed: `reticle_navigate({ sessionId, url })`
|
|
480
|
+
2. Snapshot to confirm correct state
|
|
481
|
+
3. Act on controls using testids:
|
|
482
|
+
```
|
|
483
|
+
reticle_act({ sessionId, ref, action: "click" })
|
|
484
|
+
```
|
|
485
|
+
4. Assert — always use `since` from the act result:
|
|
486
|
+
```
|
|
487
|
+
reticle_assert({ sessionId, since, timeout_ms: 5000, predicate: { kind: "allOf", predicates: [
|
|
488
|
+
{ kind: "net", method: "POST", urlContains: "/api/...", status: 200 },
|
|
489
|
+
{ kind: "element", query: { role: "...", name: "..." }, state: "visible" },
|
|
490
|
+
{ kind: "signal", name: "..." },
|
|
491
|
+
{ kind: "console", level: "error", absent: true }
|
|
492
|
+
]}})
|
|
493
|
+
```
|
|
494
|
+
5. Record: ✅ pass / ❌ fail / ⚠️ partial
|
|
495
|
+
|
|
496
|
+
### Plan then batch — do NOT ping-pong act-by-act
|
|
497
|
+
|
|
498
|
+
The repeat loop is cheap (~175 tok); the expensive part is the FIRST drive of a surface you have not seen. Every extra round-trip pays the advertised tool surface again, so the way to make a first drive cheap is fewer, bigger hops — not smaller ones.
|
|
499
|
+
|
|
500
|
+
**Do this** — state the whole journey, then assert its consequence once:
|
|
501
|
+
|
|
502
|
+
```
|
|
503
|
+
reticle_run({ tool: "reticle_act_sequence", args: { sessionId, steps: [
|
|
504
|
+
{ ref: emailRef, action: "fill", args: { value: "a@b.com" } },
|
|
505
|
+
{ ref: passwordRef, action: "fill", args: { value: "hunter2" } },
|
|
506
|
+
{ ref: submitRef, action: "click" }
|
|
507
|
+
] }}) // act_sequence is not advertised directly — hence reticle_run
|
|
508
|
+
→ reticle_assert({ sessionId, since, predicate: { kind: "allOf", predicates: [
|
|
509
|
+
{ kind: "signal", name: "auth:granted" },
|
|
510
|
+
{ kind: "net", method: "POST", urlContains: "/api/login", status: 200 },
|
|
511
|
+
{ kind: "console", level: "error", absent: true }
|
|
512
|
+
]}})
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Not this** — act, look, act, look:
|
|
516
|
+
|
|
517
|
+
```
|
|
518
|
+
act(fill email) → snapshot → act(fill password) → snapshot → act(click) → snapshot → assert
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Both verify the same thing. The second costs several times more and gives the model more chances to wander off-plan mid-journey.
|
|
522
|
+
|
|
523
|
+
**Declare before you act.** Name the consequence you expect _before_ the action, in `until`/`mustHold` — never after seeing the result. An oracle written after the fact can be rationalized into agreeing with whatever happened; one written before cannot. `reticle_act_and_wait({ ref, action, until })` is the one-hop version of exactly this.
|
|
524
|
+
|
|
525
|
+
**Record the first drive.** If you had to explore to find a journey, save it (`reticle_flow_save`) so the exploration is paid once, ever — every later run replays it deterministically for ~175 tok with no LLM.
|
|
526
|
+
|
|
527
|
+
### Smoke
|
|
528
|
+
|
|
529
|
+
Walk every testid in `capabilities.testids`. For each one that is visible and interactable:
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
reticle_query({ sessionId, by: "testid", value: testid })
|
|
533
|
+
→ reticle_act({ sessionId, ref, action: "click" })
|
|
534
|
+
→ reticle_assert({ since, predicate: { kind: "console", level: "error", absent: true } })
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
Flag anything that throws a console error or triggers a `status >= 400` network call.
|
|
538
|
+
|
|
539
|
+
### Regression suite (record once, re-verify on every change)
|
|
540
|
+
|
|
541
|
+
For flows worth re-checking forever — the actual test suite — record them, then re-verify the whole set in ONE deterministic call (no LLM per flow, so it's ~hundreds of tokens, not a full re-drive):
|
|
542
|
+
|
|
543
|
+
1. Record + assert the business outcome (not just clicks):
|
|
544
|
+
```
|
|
545
|
+
reticle_record {action:"start"}({ recordingName: "ship-deploy" })
|
|
546
|
+
→ drive the flow with reticle_act
|
|
547
|
+
→ reticle_annotate({ flow: "ship-deploy", kind: "intent", text: "ship a deploy to production" })
|
|
548
|
+
→ reticle_annotate({ flow: "ship-deploy", kind: "success-state", signal: "deploy:shipped" })
|
|
549
|
+
→ reticle_record {action:"stop"}({ recordingName: "ship-deploy" }) → reticle_flow_save({ flowName: "ship-deploy" })
|
|
550
|
+
```
|
|
551
|
+
2. After any change, re-verify EVERY saved flow at once:
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
reticle_flow_verify({ sessionId })
|
|
555
|
+
→ { status: "pass"|"fail", passed, failed, failures: [{ flow, verdict, whatChanged, whereInSource, nextAction }] }
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
On a failure the envelope tells you exactly what changed, the `file:line`, and the fix (e.g. "rebind to 'new-deploy'") — act on `nextAction` directly. A single flow: `reticle_flow_replay({ flowName })`.
|
|
559
|
+
|
|
560
|
+
> **Reaching the flow tools.** Record/replay/verify/heal, screenshots, network-mock and `act_sequence` are not advertised directly — call `reticle_run({ tool: "reticle_flow_verify", sessionId })`, or `reticle_tools` first to list a tool's params.
|
|
561
|
+
|
|
562
|
+
### Read program truth in one call — instead of reconstructing it from the DOM
|
|
563
|
+
|
|
564
|
+
Reticle's edge over a DOM/screenshot tool is **efficiency and robustness**, not that it sees things nothing else can. A capable browser-automation agent _can_ reach app state by running JS in the page (e.g. walking the React fiber via `browser_evaluate`) — but that's fragile (breaks on minified prod builds, non-React stores, framework-internals changes) and costs ~10× the tool-calls/tokens/time. Reticle reads the same truth with **one typed call**, stably:
|
|
565
|
+
|
|
566
|
+
- **UI-vs-state desync** (the UI shows one value, the store holds another — e.g. a count that didn't refresh, or a value corrupted in the store but rendered in no view): `reticle_state({ sessionId, store, path })` returns it directly. A pure snapshot can't see it; an agent spelunking framework internals can, but at far higher cost — measured head-to-head, Reticle-MCP caught a never-rendered store tamper in **4 tool calls / 45s** where a Playwright-MCP agent needed **45 calls / ~9min** reverse-engineering the React fiber.
|
|
567
|
+
- **Present-but-unusable / off-theme controls**: `reticle_inspect` returns `occluded` (covered by an overlay), `styles.cursor`/`opacity`, `box` (0×0), and `theme.offTheme` (color off the design-token palette) — the "is it actually usable / on-brand?" read, in one call.
|
|
568
|
+
|
|
569
|
+
### Consume the human's bug reports (`reticle_session {action:"review"}`)
|
|
570
|
+
|
|
571
|
+
The dev can click **"Flag a bug"** in the running app, point at an element, and type what's wrong. Each flag becomes a **mark** you drain with `reticle_session {action:"review"}`:
|
|
572
|
+
|
|
573
|
+
```
|
|
574
|
+
reticle_session {action:"review"}({ sessionId })
|
|
575
|
+
→ { marks: [{ id: "m1", note: "this button is misaligned", label: "button \"Pay\"",
|
|
576
|
+
source: { file: "src/Checkout.tsx", line: 42 },
|
|
577
|
+
fix: "Open src/Checkout.tsx:42 and fix: this button is misaligned. Then reticle_session {action:"review"} { resolve: \"m1\" }" }],
|
|
578
|
+
pendingCount: 1 }
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Check it at the start of a session and whenever the human may have flagged something. Open the `source` file:line, apply the fix the `note` asks for, verify, then `reticle_session {action:"review"}({ resolve: "m1" })`. Reading never consumes a mark, so you can list → fix → verify → resolve.
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Phase 5 — Report
|
|
586
|
+
|
|
587
|
+
Always refer to the tool as **Reticle** in reports, summaries, and messages to the user.
|
|
588
|
+
|
|
589
|
+
```
|
|
590
|
+
## Reticle — <route or feature>
|
|
591
|
+
|
|
592
|
+
**Result: ✅ PASS / ❌ FAIL / ⚠️ PARTIAL**
|
|
593
|
+
|
|
594
|
+
| Flow | Result | Evidence |
|
|
595
|
+
|---|---|---|
|
|
596
|
+
| Login → dashboard | ✅ | POST /api/login 200, route /dashboard |
|
|
597
|
+
| Click "Deploy" | ❌ | POST /api/deploy 401 — missing auth header |
|
|
598
|
+
| Sidebar nav | ✅ | 4 items, no console errors |
|
|
599
|
+
|
|
600
|
+
**Console errors:** none / <list>
|
|
601
|
+
**Failed requests:** none / <list>
|
|
602
|
+
**Fix at:** src/lib/api.ts:65 ← from reticle_inspect on the failing element
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
If something failed, call `reticle_inspect({ sessionId, ref })` on the failing element to get the `file:line`, and include it in the report.
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Rules (always apply in Test mode)
|
|
610
|
+
|
|
611
|
+
- **Always close the session when you stop driving.** The human may be watching the browser, so the panel must reflect your real state — never leave it reading "live" when you've stopped. The moment you finish a turn or need the human, call `reticle_session {action:"yield"}({ mode: "waiting" })`, or `reticle_session {action:"yield"}({ mode: "ask", note: "<your question>" })` when you're blocked on them. Call `reticle_session {action:"end"}()` only when the whole task is done. The session revives automatically on your next action, so this is cheap and safe to do every time. (A server-side idle fallback flips the panel to "waiting" if you forget, but signal it yourself — it's immediate and it can say _why_.)
|
|
612
|
+
- Always pass `since` in `reticle_assert` — scopes to post-action events, prevents stale buffer fakes.
|
|
613
|
+
- Always assert `{ kind: "console", level: "error", absent: true }` — silent errors are the most common thing agents miss.
|
|
614
|
+
- Batch net + element + signal + console into one `allOf` — don't call `reticle_assert` four times.
|
|
615
|
+
- Never assert on pixels — use predicates, not `reticle_screenshot` (screenshots are for genuinely visual checks only).
|
|
616
|
+
- If the session disconnects mid-test (navigation creates a new session ID) — call `reticle_sessions()` again and continue.
|
|
617
|
+
- **If Reticle itself misbehaves, file it with `reticle_feedback` before you work around it.** Three things are worth reporting: a tool that returned something wrong or impossible (`kind: "bug"`), something you needed to observe and Reticle simply could not see (`kind: "gap"`), and a verification that ran but left you unable to tell pass from fail (`kind: "ambiguity"`). Write `text` as a root-cause analysis — what you called, what you expected, what you got — and put the call trail in `trace`. Then continue the task; do not retry the failing call just to re-report it. Report defects in **Reticle**, not bugs you found in the app under test: finding those is Reticle working, and they belong in your report to the user.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## Troubleshooting
|
|
622
|
+
|
|
623
|
+
### The `reticle_*` tools disappeared mid-session
|
|
624
|
+
|
|
625
|
+
The MCP proxy lost its stream to the daemon. It reconnects on its own and replays the handshake, so this should heal without anyone doing anything — the daemon stays up across the drop, and `npx @reticlehq/server status` will confirm it (`running:true`).
|
|
626
|
+
|
|
627
|
+
Read `~/.reticle/mcp-proxy.log` — it records every drop and reconnect with a reason (`sse_ended`, `sse_error`, `connect_error`) and the attempt number. It is the only place this is visible; the disconnect is silent from the agent's side.
|
|
628
|
+
|
|
629
|
+
If the tools stay gone, the proxy has stopped RETRYING (`reticle_mcp_proxy_dormant_after_budget` in that log) — it has not stopped serving. Just call a tool again: the next request wakes it and starts a daemon. It used to exit here, which is what made a human open `/mcp`; that no longer happens. If a call still fails after that, use the CLI for anything that doesn't need the tools: `npx @reticlehq/server status | doctor | open | drive`.
|
|
630
|
+
|
|
631
|
+
### Multiple projects / port conflicts
|
|
632
|
+
|
|
633
|
+
Each project should have its own port in `.reticle.json`. When `reticle mcp` starts, it reads `.reticle.json` in the current working directory and uses that project's port — so agents in different project directories automatically connect to different daemons.
|
|
634
|
+
|
|
635
|
+
If two projects share the same port, start the second on a different port:
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
npx @reticlehq/server stop --port 4400 # stop the other project's daemon if needed
|
|
639
|
+
# Then ensure .reticle.json in this project has a unique "port" field
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
Use `npx @reticlehq/server status` to see which daemons are running and which sessions are connected.
|
|
643
|
+
|
|
644
|
+
### No Chromium / Playwright needed for standard use
|
|
645
|
+
|
|
646
|
+
Reticle does NOT download Chromium for normal agent workflows. The browser SDK runs inside the user's own browser — the agent sees the DOM, network, console, and state through the WebSocket bridge. Playwright is only installed if you explicitly call `reticle serve --drive <url>` or `reticle verify`, which launch an autonomous browser for unattended automation.
|
|
647
|
+
|
|
648
|
+
To attach to a browser the user already has open (zero download, zero extra process):
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
# Start Chrome with remote debugging enabled (user does this once):
|
|
652
|
+
# Mac: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
|
|
653
|
+
# Then set RETICLE_CDP_URL when starting the daemon:
|
|
654
|
+
RETICLE_CDP_URL=http://localhost:9222 npx @reticlehq/server mcp
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
This connects Reticle to the existing Chrome — native clicks and screenshots work without Playwright.
|
|
658
|
+
|
|
659
|
+
### "Failed to reconnect to reticle: -32000"
|
|
660
|
+
|
|
661
|
+
This means the `reticle mcp` proxy process exited and Claude Code couldn't restart it cleanly. -32000 is the JSON-RPC code for a server-side error; here it means the proxy exited with code 1 before the MCP handshake completed.
|
|
662
|
+
|
|
663
|
+
**Check version first — stale npx cache is the most common silent culprit.** `npx` caches packages locally and may keep running an old version of `@reticlehq/server` even after a new one is published. An older daemon speaking a different protocol than the new proxy (or vice-versa) causes the proxy to exit immediately and Claude Code to report -32000. Always clear the cache and force-resolve the latest version before investigating anything else:
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
npx --yes @reticlehq/server@latest version # force-resolves latest and prints version
|
|
667
|
+
npx @reticlehq/server stop # stop any daemon running the old version
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
Then reload Claude Code (`/mcp`) so the new version is picked up on next connection.
|
|
671
|
+
|
|
672
|
+
**Second common cause: the Stop hook is killing the daemon between turns.** If `~/.claude/settings.json` has a Stop hook running `reticle stop --quiet`, remove it. The daemon must stay alive across turns — killing it forces a cold-boot spawn on every reconnect, and if that spawn takes longer than 10 seconds (cold npx cache, slow disk, first install), the proxy times out and exits with code 1. See Step 1b above.
|
|
673
|
+
|
|
674
|
+
**Fix (in order):**
|
|
675
|
+
|
|
676
|
+
1. **Force the latest version and clear the stale daemon:**
|
|
677
|
+
|
|
678
|
+
```bash
|
|
679
|
+
npx --yes @reticlehq/server@latest version
|
|
680
|
+
npx @reticlehq/server stop
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
Reload Claude Code. If -32000 is gone, done.
|
|
684
|
+
|
|
685
|
+
2. **Check for the Stop hook:** `cat ~/.claude/settings.json | grep reticle` If present, delete that hook entry, then repeat step 1.
|
|
686
|
+
|
|
687
|
+
3. **If -32000 persists**, the daemon may be crashing on startup. Check the log: `cat ~/.reticle/daemon-4400.log | tail -30` Look for `reticle_daemon_start_failed` or `reticle_mcp_daemon_unavailable`. If the port is taken by another process: `lsof -i :4400` to identify it, then kill it and retry.
|
|
688
|
+
|
|
689
|
+
4. **Confirm the MCP config is user-level** (not project-level) and has no pinned version:
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
claude mcp list # ~/.claude.json is large and stateful — ask the CLI, do not read it
|
|
693
|
+
# Should show: reticle → npx @reticlehq/server mcp (no --port, no pinned version)
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
If the project has a `.mcp.json` or `.claude/mcp.json` that overrides the user-level config with a pinned version in its args (`["@reticlehq/server@<pinned>", "mcp"]`), rename it out of the way and re-register:
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
claude mcp add reticle -s user -- npx @reticlehq/server mcp
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**Tell the user what you found** so they can confirm which fix applies.
|