|
| 1 | +# React Doctor: one skill that makes agents good at React |
| 2 | + |
| 3 | +Today React Doctor is a code scanner. The plan is to grow it into a single skill, `/react`, that does more than scan: it helps a coding agent write better React, quietly checks its own changes in the background, and can open a real browser to measure speed, reproduce bugs, and review design. People pick it up one developer at a time because the local skill is free and useful on its own, and the way it spreads to a whole team is by getting added to continuous integration (CI). |
| 4 | + |
| 5 | +## What it is |
| 6 | + |
| 7 | +It is the skill we already ship, grown, rather than a new wrapper around a pile of separate tools. The cleanup pass it does today becomes one option among several, and [React Grab](https://github.com/aidenybai/react-grab) and the browser piece from [Expect](https://github.com/millionco/expect) get folded in so they stop being separate installs. That consolidation is deliberate. With Expect, having a few separate tools left people unsure what to install and when, and that confusion is a big part of what hurt adoption. |
| 8 | + |
| 9 | +Installation stays the same. You install it with `npx react-doctor` and call it as `/react`. The package name and the brand stay React Doctor — so we keep the GitHub stars, the install command people already know, and the [react.doctor](https://www.react.doctor) domain — but the command itself is the shorter `/react`, since that is what you reach for while writing React. |
| 10 | + |
| 11 | +## What it does |
| 12 | + |
| 13 | +There are really two kinds of work it handles. Most of it runs instantly off the code itself, and the parts that need a browser only spin one up when you actually ask for them. |
| 14 | + |
| 15 | +- **Finding and fixing problems** (`doctor`, or `debug`, `audit`, `review`): it reads your code for the score and the 502 rules, profiles speed when you ask (`perf`), or opens a browser to reproduce a bug (`debug`). |
| 16 | +- **Building and improving the user interface, or UI** (`design`, or `dev`): it writes or refines UI and reviews it against a screenshot, and `grab` lets you point at an element on screen to get its file, line, and component tree. |
| 17 | + |
| 18 | +When something does need a browser, it connects to the Chrome you already have open over the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) (CDP), and only downloads its own copy of Chrome if nothing is running. That one detail solves the biggest headache Expect had, because logins and cookies work on their own when it is your real session. |
| 19 | + |
| 20 | +The browser part is our own, slimmed down from Expect. It keeps the page open between commands, runs scripts in a sandbox, and carries the accessibility check, the speed trace, the page snapshot, and the network log. We left out the things we do not need, like the video recording, the terminal UI, and the test runner. Since it runs from the command line and the open page holds the state, there is no separate server to keep alive. |
| 21 | + |
| 22 | +## How it picks what to do |
| 23 | + |
| 24 | +We let the model choose which job to run by reading the skill, the same way it already routes to other skills, rather than matching keywords. So `/react` on its own lets it decide, naming a job like `/react perf` forces that one, and when the request is genuinely unclear the skill just asks. |
| 25 | + |
| 26 | +For the common cases it follows a rough guide: |
| 27 | + |
| 28 | +| You say or change | Job | |
| 29 | +| ------------------------------------------------------- | ------------------- | |
| 30 | +| "review", "before commit", "clean up", or changed files | doctor (reads code) | |
| 31 | +| "slow", "laggy", "re-rendering" | perf | |
| 32 | +| "broken", "crashes", "not working" | debug (browser) | |
| 33 | +| "looks off", "polish", a pasted element or screenshot | design | |
| 34 | +| unclear | ask | |
| 35 | + |
| 36 | +We keep this judgment with the model instead of a fixed keyword list, because keyword matching only gets worse as models get better at reading what you actually meant. |
| 37 | + |
| 38 | +## Where the instructions live |
| 39 | + |
| 40 | +The skill itself stays small, in the same spirit as the [writing-guidelines](.agents/skills/writing-guidelines/SKILL.md) skill. The `SKILL.md` holds only the ten always-on rules and the short routing guide, and each job pulls its full instructions fresh from a web address when it runs: |
| 41 | + |
| 42 | +```bash |
| 43 | +curl --fail --silent --show-error \ |
| 44 | + https://www.react.doctor/prompts/debug.md |
| 45 | +``` |
| 46 | + |
| 47 | +The per-rule fix recipes load the same way, for example `https://www.react.doctor/prompts/rules/react-builtins/exhaustive-deps.md`. Editing one of these files updates every user on their next run, with no reinstall, and the `/react` triage flow already works exactly like this. |
| 48 | + |
| 49 | +Because react-doctor is open source, these instruction files live in the repo and download straight from GitHub (`raw.githubusercontent.com`), the same approach writing-guidelines uses, and the `react.doctor/prompts` links are just a stable alias for those same files. A copy ships inside the package as a fallback for when the download fails or the network is blocked. |
| 50 | + |
| 51 | +The reason for the split is that the ten rules belong inside the skill, since they apply to every edit, while the longer job instructions belong online so we can change them without shipping a new version. |
| 52 | + |
| 53 | +## Running in the background |
| 54 | + |
| 55 | +By default the skill works without anyone calling it. When the agent finishes a task it looks only at the changed lines, and it stays quiet unless it finds one to three issues it is genuinely confident about, mentioning each one once. |
| 56 | + |
| 57 | +The browser jobs (perf, debug, design review) never start on their own. They only run when you or the agent asks, because they are slower and need a live page, and firing them on every change would just be noise. Noise is what gets a skill turned off. |
| 58 | + |
| 59 | +## The always-on React rules |
| 60 | + |
| 61 | +The skill ships with ten rules that shape how the agent writes React on every edit. We keep the list at ten on purpose, since the real value is in the browser jobs rather than a long rulebook: |
| 62 | + |
| 63 | +1. Derive state, do not duplicate it. |
| 64 | +2. Skip effects for values you can compute while rendering and for handling events. |
| 65 | +3. Compose components instead of piling on boolean props. |
| 66 | +4. Lift state only as far as it needs to go. |
| 67 | +5. Keep one source of truth. |
| 68 | +6. Render without side effects. |
| 69 | +7. Use stable keys, never the array index. |
| 70 | +8. Fetch independent data in parallel. |
| 71 | +9. Skip manual memoization and let the compiler handle it. |
| 72 | +10. Handle the loading, error, and empty states. |
| 73 | + |
| 74 | +## The debug job |
| 75 | + |
| 76 | +The debug job is [debug-agent](https://github.com/millionco/debug-agent), the runtime-evidence debugging skill, wired into the browser part. Rather than guess a fix from the code, the agent writes down a few hypotheses, drops small [NDJSON](https://github.com/ndjson/ndjson-spec) logs into the suspect spots, reproduces the bug, and only fixes once the logs prove the cause — then verifies against the logs and removes the instrumentation. A short-lived local server (`npx debug-agent`) collects the logs; the browser part drives the reproduction and reads what actually rendered. We reuse debug-agent here instead of writing our own loop because it already solves the hard part — making the agent fix with evidence, not vibes — and it is our own tool, so the two stay in step. |
| 77 | + |
| 78 | +## The design job |
| 79 | + |
| 80 | +We write the design job ourselves, treating [React UI](../brain/react-ui/SKILL.md), [ui.sh](https://ui.sh), [Impeccable](../brain/impeccable/SKILL.md), and the [design reference](../brain/design-reference.md) as source material rather than dependencies. The important part is that a review opens the page, takes a screenshot, and reports things it can actually measure, like contrast ratio, line length, spacing, and tap-target size, instead of offering opinions. Those measured findings are the part a smarter model cannot match just by reading code, whereas plain taste advice is. The `dev` name runs the live loop, where you point at an element and improve it as you build. |
| 81 | + |
| 82 | +## CI: where it goes team-wide |
| 83 | + |
| 84 | +In CI the first version stays code-only: the scan, the score, a pinned pull request comment, and inline suggestions. The browser jobs stay on your machine, and profiling in CI comes later, since it needs the app booted and a stable baseline, and it is really the "prove the impact to my manager" feature. |
| 85 | + |
| 86 | +The local skill earns the habit one developer at a time, and CI is how that becomes the whole team's default on every pull request. Both are free. The path from one to the other is the prompt already in the installer that offers to add the GitHub Action, shown once per repo after the local skill has shown its worth. How we make money is a separate decision, not a paywall on CI. |
| 87 | + |
| 88 | +## What we measure |
| 89 | + |
| 90 | +Three numbers carry the story: |
| 91 | + |
| 92 | +- **Habit**: how often the agent accepts and lands a fix the background check surfaced, plus weekly active repos. |
| 93 | +- **Conversion**: the share of local users who turn on the CI check, which tells us whether personal use spreads to the whole team. |
| 94 | +- **Retention**: whether teams keep the CI check on over time, which is the one that matters most. |
| 95 | + |
| 96 | +Alongside that, a short "very sad, mid, not sad" survey runs in the command line for a sample of users. |
| 97 | + |
| 98 | +## What to build, in order |
| 99 | + |
| 100 | +We build all of this and land it as one pull request, rather than merging anything piecemeal first. [Pull request #811](https://github.com/millionco/react-doctor/pull/811) is the starting point for the profiler, not something we merge on its own. The order we build it in: |
| 101 | + |
| 102 | +1. Take the profiler from pull request #811 as the starting point and rebuild it as the perf job. It is the template for every browser job. |
| 103 | +2. Prove the browser part works: connect to a running Chrome, keep the page open across commands, run sandboxed scripts, and inject the [React DevTools](https://react.dev/learn/react-developer-tools) hooks. Everything else depends on this, so it goes first. |
| 104 | +3. Expand the `/react` skill, with the ten rules plus the routing guide, under 200 lines. |
| 105 | +4. Turn on the background check, code-only and on by default. |
| 106 | +5. Add grab on top of the browser part, and wire the debug job to the [debug-agent](https://github.com/millionco/debug-agent) loop driving that same browser. |
| 107 | +6. Write the design job with measured review. |
| 108 | +7. Deprecate React Grab, leaving a shim so current users do not break. |
| 109 | + |
| 110 | +## What could go wrong |
| 111 | + |
| 112 | +- **The browser part**: if connecting to Chrome, keeping the page open, and injecting the DevTools hooks do not compose, the whole browser story is shaky, so we test it before building on it. |
| 113 | +- **Background noise**: on by default with a loose threshold is how you get uninstalled. We ship it nearly silent and loosen it only once the fix-acceptance data says it is safe. |
| 114 | +- **Conversion**: if people love the local skill but never turn it on in CI, it stays a personal tool and never reaches the team. Better to learn that early and cheaply. |
| 115 | +- **Design**: if measured review turns out no better than the model's own taste, we cut design from the first version. |
0 commit comments