reticle
Install, instrument and verify this running web app from the inside (DOM, network, routing, console and framework state) instead of screenshots or guessing. Drives one real flow end to end and returns a verdict with the file:line to fix. Use when the user asks to set up or install Reticle, when a user-facing change needs proving before you call it done, when a test passes but the UI is broken, or when the user types /reticle.
git clone --depth 1 https://github.com/reticlehq/reticle /tmp/reticle && cp -r /tmp/reticle/plugin ~/.claude/skills/reticleSKILL.md
# Reticle
Reticle embeds a dev-only SDK in the running app and exposes it to you as `reticle_*` MCP tools. You look, act, observe, and assert against the real app. No screenshots.
## Setup runs to the end without you
**The plugin already registered the MCP server, so this whole file runs with no client restart and no human input at all.** Do not stop, and do not ask the user anything, until their app is running with Reticle inside it and a session is connected. Everything between here and that point is yours to do: run `init`, fix the wiring it could not, start the dev server, open the browser.
Questions you must not ask, because the repo you are sitting in already answers them: which framework, which package manager, which port, whether to start the dev server, whether to open the browser, whether to carry on. Decide, act, and say what you did in one line. A setup turn that ends in a question ends with nothing verified, and that is the most common way this fails in the field.
There are exactly two places you may stop, and neither is a question about a preference:
1. **No recognisable dev script in `package.json`.** Say so; do not invent one.
2. **Your host asks the human to approve a command.** That prompt belongs to the host. Never bypass or suppress it, and take a refusal as the answer.
## Run this branch first
```bash
cat .reticle.json 2>/dev/null || echo NOT_FOUND
```
- `NOT_FOUND` → **ONBOARD**, then **VERIFY**. Do not stop between them.
- File exists → **VERIFY**.
Either way you are finished only when `reticle_act_and_wait` or `reticle_assert` has produced a verdict. Config files are not an install, and a listed session is not a result.
---
# ONBOARD
Four steps, no pauses. Say which step you are on in one line each; do not narrate more than that.
## 1. Wire it
```bash
RETICLE_INSTALL_SOURCE=plugin npx @reticlehq/server@latest init
```
`@latest` is deliberate: `npx` caches, and a stale cached CLI is the most common silent setup failure. In a monorepo run it at the root anyway. With one app under `apps/*` or `packages/*` it wires that app silently. With several it wires nothing, lists them, and names the flag: re-run with `--app <dir>` for the one the user is working in, the one their request named, or the one whose `package.json` has the dev script. **Pick it yourself and re-run.** Asking which app they meant is the most likely place this whole install stops, and the answer is almost always in the request you were already given.
Read the report rather than trusting the exit code: `✓` applied, `·` already wired, `–` skipped, `ℹ` done but incomplete in a way that matters, `⚠` needs your edit. **`⚠` and `ℹ` both need you**, and each line carries the exact snippet. A non-zero exit is a to-do list, not a failure. Fix every one before step 2. Per-framework wiring: `curl https://docs.reticle.sh/frameworks.md`.
Two rules that fail silently if broken:
- **Never guard the connect on `window.location.hostname === 'localhost'`.** False on every non-localhost dev host, and `window` does not exist during SSR. Use the framework's dev flag plus a client-only boundary.
- **A config change needs a dev server restart.** A plugin added to a `vite.config.ts` the running server already read is not in the bundle.
**If the user gave you a license key** (now or at any later point), see [License key](#license-key) below and do it before moving on.
## 2. Serve the app
If something is already listening on the app's port, **use it**. Do not start a second one, and do not tell the user to start what they are already running. If the dev server was up before step 1, restart it, or nothing you do next finds an instrumented page.
If nothing is listening: read the project's own dev script out of `package.json` (`dev`, `start`, whatever this project calls it), run it in the **background**, and say in one line that it is running and how to stop it. Never guess a command that is not in `package.json`, and never kill anything.
## 3. Open the app yourself
Do not ask the user to open a browser. Open it:
```bash
npx @reticlehq/server open <the url the dev server is serving>
```
That reuses an already-connected tab or opens a new one, and waits for the page to register. Then confirm:
```
reticle_sessions()
```
**A listed session whose URL matches the app is the gate. Nothing below is meaningful without one, and you may not report setup complete without one.**
Empty list? Read `next_action` first. It names which of the four cases this is and, where there is one, the literal command and port taken from this project's own scripts. Then `why`, which is the same thing in prose. In order: is the SDK imported and called in the app entry, is the dev server serving that entry, is the connect guarded on `hostname === 'localhost'`, do both sides agree on the bridge port (**4400**, never the dev-server port). Full checklist: `curl https://docs.reticle.sh/troubleshooting.md`.
Headless environment with no browser to open? Take a tab Reticle owns instead:
```
reticle_run({ tool: "reticle_lease", args: { action: "acquire", url: "<the same url>" } })
```
## 4. Drive one flow, and save it
Go straight into **VERIFY** below. Pick the single most important flow that finishes in a handful of steps, the one a user would do first (sign in, search, add to cart, submit the form). Say which one you picked in a line, and drive only that. Not the whole app.
Tell the user to keep the tab visible: the HUD is on by default (glow border, animated cursor, narration per step), and watching you drive their own app is the demo.
**Then save it**, which is what makes every future check one call instead of a repeat of this one:
```
reticle_run({ tool: "reticle_record", args: { action: "start", recordingName: "<flow>" } })
... the drive ...
reticle_run({ tool: "reticle_record", args: { action: "stop", recordingName: "<flow>" } })
reticle_run({ tool: "reticle_flow_save", args: { flowName: "<flow>" } })
```
`reticle_flow_saTest-driven development for behaviour a unit test cannot reach, by writing the expectation against the running app before writing the code. Declare the consequence first, watch it fail, implement, watch it pass. Use when building a user-facing feature, when the user asks for TDD on UI or full-stack work, when a unit test cannot express the outcome that matters, or when you want a red-green loop that runs against the real app instead of mocks.
Sweep a whole running web app for what is broken, without writing a script or knowing the codebase. Clicks every reachable control and reports dead buttons, console errors, failed requests, and places where the API and the screen disagree. Use on an unfamiliar codebase, before a release, after a big merge or dependency bump, when the user asks for a smoke test or a health check, or when someone says "just check everything still works".
Find out why something in a running web app does not work, when the console is empty and the code looks correct. Reads the click, the request, the store and the console together and returns the file:line to open. Use when a button does nothing, a form will not submit, data will not load, a page renders blank or stale, a modal will not close, or the user says "it's broken" and the code review says it is fine.
Check that the UI you actually rendered uses the design system, by reading computed styles in the running app against the project's design tokens. Catches hardcoded hex colors, off-palette backgrounds, invisible or unusable controls, and animations that never ran. Use after building or restyling a component, when a design review is wanted, when a UI looks slightly off but nobody can say why, or when a design system exists and nothing checks whether the code follows it.
Drive and verify an Electron or Tauri desktop app from the inside, including the main-process and Rust IPC calls a browser tool cannot see. Use when a desktop app needs testing, when a feature works in the browser but not in the packaged app, when an IPC or invoke call needs proving, when a desktop screenshot or visual diff is wanted, or when you need a headless run of a desktop UI in CI.
Find out why the tests pass but the app is broken. Catches false greens: a green suite over a feature that does not work, a mocked API standing in for a real one, an assertion that holds no matter what the app does, a click handler wired to nothing. Use when the suite is green and the user says it is broken, when a test never fails, when coverage looks fine but bugs still ship, or before trusting a passing run you did not watch.
Pick up the bugs a human flagged by pointing at them in the running app, each arriving with the element, the note they typed, and the source file and line. Use when the user says they marked or flagged something, when starting a session on an app someone has been clicking through, when a designer or PM has left feedback in the UI, or when the user describes a problem as "that button there" without saying which file.