@reticlehq/* changes in a real external app, publish the workspace to a local Verdaccio with bash scripts/local-registry.sh, then point that app’s .npmrc at http://localhost:4873/ as the default registry, not scoped to @reticlehq, for the reason in step 2. That is the whole procedure; the rest of this page is the detail.
For normal use, Reticle is on public npm. Just npm i -D @reticlehq/react @reticlehq/vite-plugin (see Getting Started). You only need this guide to test local, unpublished changes to the Reticle packages in a real external app before they ship.
Because the @reticlehq/* packages depend on each other via the workspace protocol, plain npm pack tarballs don’t resolve cleanly. The reliable way to exercise your in-progress changes in a real app is a tiny local registry (Verdaccio), the same path CI uses to validate a publish.
1. Publish @reticlehq/* to a local registry
From the Reticle repo:http://localhost:4873, creates a user/token, and publishes all @reticlehq/* packages there at the current workspace version:
For a browser app, install
@reticlehq/react plus the build plugin for your framework (@reticlehq/vite-plugin or @reticlehq/next); @reticlehq/server is what your agent runs. (Verified: an external npm i @reticlehq/react resolves its graph, including @reticlehq/core, and imports correctly.) Leave the registry running.
Note: pre-2.0 docs used a single @reticlehq/core umbrella package that re-exported everything; it’s been split into the audience-scoped packages above.
2. Point your app at the local registry
In your app’s project root, add an.npmrc pointing the default registry at Verdaccio. Verdaccio proxies npm for everything it does not hold, so the rest of your dependencies still resolve normally:
@reticlehq/core depends on open-verification, the protocol package, which is deliberately unscoped because the protocol is not ours to namespace. With @reticlehq:registry=… npm sends that one request to npmjs and the install dies:
reticle init then correctly reports ⚠ Install dependencies and skips wiring the build plugin, so the app is left un-instrumented by a registry mistake two steps earlier.
There is no scoped form that fixes this: npm’s per-registry setting is @scope:registry, and an unscoped package has no scope to key on. The default-registry line above is the only thing that works. Point it back at npm (npm config delete registry, or delete the .npmrc) when you are done testing.
3. Install + wire it up
Install the SDK kit plus the Vite build plugin (source mapping +connect() injection):
reticle.connect() (dev only) from @reticlehq/react, add the MCP server to your agent, and (React) install() the adapter from @reticlehq/react. For the fastest agent loop, also do Step 6: make your app agent-legible (testids, reticle.signal, registerStore, registerCapabilities) and the integration patterns (createReticleEmitter for zero prod-bundle cost).
Upgrading.Run the MCP server from the local registry too;scripts/local-registry.shresets Verdaccio and republishes at whatever version the workspace is on right now, so your app will not pick up a rebuild on its own. Pull it explicitly with@latest:
npx @reticlehq/server is the server:
Next.js specifics (verified on Next 15 / React 19)
next.config.mjs:
Real input for hover/drag (optional)
Synthetic events can’t trigger nativeonMouseEnter/pointer state (hover menus, tooltips, pointer drag). Enable real input so the server drives genuine pointer input and reticle_act reports inputMode:"real":
-
Easiest (
reticle drive): Reticle launches its own scriptable, headless-capable browser at your app URL (no flags to juggle): -
Or attach to your own browser: launch it with
--remote-debugging-port=9222, then point the MCP server at it viaenv:
inputMode. See usage §18.
Write replayable specs + git-checked flows
- Specs: with
@reticlehq/test, turn checks intoreticleTest("…", async t => { await t.act(...); await t.expectSignal(...) }). They are signal/testid-bound,reticle_clockfor determinism,t.expectInputModeReal()to skip-with-reason when real input isn’t active. Run them headless viareticle drive(the same path CI uses). - Flows: record a flow once and Reticle writes it to a git-checked
.reticle/flows/<name>.json(anchored on testid/signal);reticle_flow_replayre-resolves anchors at run time and reports legible drift with a nearest-match;reticle_verify { action: "heal" }proposes/applies the rebind. A fresh agent reads.reticle/contract.jsonto learn your testable surface without grepping source.
When you’re ready for real npm
The same packages publish to public npm unchanged:pnpm -r publish --access public after npm login. The Verdaccio run above is a faithful rehearsal of that.