Skip to content

feat(react): /react-doctor umbrella skill + in-house browser core + debug-agent debug job#853

Open
aidenybai wants to merge 10 commits into
mainfrom
feat/react-skill
Open

feat(react): /react-doctor umbrella skill + in-house browser core + debug-agent debug job#853
aidenybai wants to merge 10 commits into
mainfrom
feat/react-skill

Conversation

@aidenybai

@aidenybai aidenybai commented Jun 17, 2026

Copy link
Copy Markdown
Member

Based on main. The diff is the skill umbrella + in-house browser core + the debug job, with the React DevTools profiler harness living inside @react-doctor/browser (no separate perf-agent package).

Grows React Doctor from a code scanner into one skill, /react-doctor, that helps the agent write better React, checks its own changes in the background, and can open a real browser to profile, debug, and review UI. Built to follow doc.md.

What's here

  • /react-doctor umbrella skill — the existing scanner-only skill grows into one skill that routes across jobs. Ten always-on React baseline rules plus prose routing across doctor (static scan + 0–100 score, runs in the background), perf (React render + V8 CPU profiling), debug (reproduce in a real browser), design (measured UI review), and rule explain/configure. The command, package name, and brand stay React Doctor; the browser jobs only run when asked.
  • @react-doctor/browser — a thin playwright-core wrapper that attaches to a running Chrome over CDP (logins/cookies come along), launching its own persistent, detached Chrome only as a fallback so the page survives across CLI invocations. playwright-core is an optional dependency and loaded lazily, so playwright never bundles into the CLI hot path. It also houses the React DevTools profiler harness in src/react-profiler.
  • react-doctor browser commands:
    • open / snapshot (accessibility tree) / screenshot
    • eval — runs the expression with the Playwright page in scope, so an agent acts on what snapshot showed using Playwright's own selectors (page.locator("text=Login").click(), page.getByRole(...)), with page.evaluate(() => …) for raw DOM. No bespoke ref scheme.
    • console (console + uncaught errors on load) / network (request waterfall, failures flagged)
    • audit — axe-core accessibility
    • perf — long-animation-frame jank with per-script attribution, plus LCP/CLS. With a URL it measures a fresh load; with no URL it measures the live page without reloading, so jank from a preceding eval interaction is captured.
    • profile — one recording, two lenses: a React render profile (slowest commits, hottest components by self time, unnecessary re-render counts) and a Chrome DevTools CPU profile (V8 sampler over CDP, JS functions ranked by self time), returned as JSON for the agent — no files written. Pass --interaction '<playwright expr>' to record what it triggers; both lenses cover the post-load + interaction window.
    • report — captures console, network, performance, and accessibility in a single page load (one navigation, not four), ~2.6x faster than running the focused commands separately.
    • Shared --cdp, --no-launch, and a one-shot --viewport WxH emulation.
    • browser open injects the React DevTools profiler as window.__REACT_PERF__; browser profile is the one-shot record + analysis, or browser eval can start() / stop() it manually and pull the raw DevTools export.
  • react-doctor debug + @react-doctor/debug — a local NDJSON logging server (react-doctor debug serve, the default subcommand) the debug job posts runtime evidence to. Handles lock reuse, dedup, and daemon/--json modes so the server outlives the spawning CLI process and the agent just gets back the endpoint.
  • react-doctor mcp + @react-doctor/mcp — a Model Context Protocol server over stdio (the bin fast-paths mcp) that exposes the doctor scan and the browser/debug jobs as MCP tools, so any MCP-capable agent can call doctor_scan, the browser_* tools (including browser_profile), and the debug_* log server directly. Debug binds are loopback-only with a minted-endpoint allowlist.
  • debug job = debug-agent — the debug playbook runs the runtime-evidence loop: hypothesize → instrument with NDJSON logs (posted to the server above) → reproduce in the live browser → fix only with log proof → verify → clean up.
  • Playbooksreferences/{debug,design,performance,explain}.md, followed per job.

Build / checks

  • build, typecheck, lint, format green for @react-doctor/core, @react-doctor/browser, @react-doctor/debug, and react-doctor.
  • @react-doctor/browser, @react-doctor/debug, install-react-doctor, install-agent-hooks, parse-viewport, and strip-unknown-cli-flags suites pass; full react-doctor suite green.
  • The skill ships to dist/skills/react-doctor.
  • Merged up to current main; repo-wide typecheck and lint are green.

Deliberately deferred (follow-ups)

  • Ambient task-end static-scan hook (on by default, conservative threshold)
  • Publishing story for @react-doctor/browser and @react-doctor/debug (workspace deps today)
  • product-thinking pass for the browser / debug CLI surface

Note

Medium Risk
Large new surface area (local Chrome launch, CDP attach, debug HTTP server, MCP stdio) with thoughtful guards, but agents can drive live browsers and post runtime logs—operational and prompt-injection considerations beyond the existing static scan.

Overview
Expands React Doctor from a static scanner into a /react-doctor umbrella skill (v1.6) with ten always-on React writing rules, job routing (doctor / perf / debug / design), and new playbooks for browser-backed perf, debug-agent, design, and rule explain flows.

Adds @react-doctor/browser: CDP attach (optional persistent Chrome launch), BrowserSession for open/eval/snapshot/screenshot, axe audits, console/network capture, LoAF/LCP/CLS perf, combined profile (React DevTools export analysis + V8 CPU self-time), and one-load report. Ships a document-start React profiler inject and CLI react-doctor browser subcommands with --cdp, --no-launch, and --viewport.

Adds @react-doctor/debug and react-doctor debug serve (daemon/--json, project-scoped lock reuse) for NDJSON runtime logs, plus react-doctor mcp via @react-doctor/mcp (doctor_scan, browser_*, debug_* tools; loopback-only debug bind and minted-endpoint allowlist). The bin fast-paths mcp like the LSP entry; build copies the profiler inject asset into the published CLI bundle; playwright-core is optional and lazy-loaded.

Reviewed by Cursor Bugbot for commit c4e5658. Bugbot is set up for automated code reviews on this repo. Configure here.

@pkg-pr-new

pkg-pr-new Bot commented Jun 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/eslint-plugin-react-doctor@853
npm i https://pkg.pr.new/oxlint-plugin-react-doctor@853
npm i https://pkg.pr.new/react-doctor@853

commit: c4e5658

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

React Doctor skipped this pull request — it changed no React files.

Reviewed by React Doctor for commit c4e5658.

cursor[bot]

This comment was marked as resolved.

@aidenybai aidenybai changed the base branch from main to cursor/perf-agent-devtools-setup-d63d June 17, 2026 20:49
@aidenybai aidenybai changed the title feat: /react-doctor skill umbrella + in-house browser core (debug/design/grab) feat(react): /react umbrella skill + in-house browser core + debug-agent debug job Jun 17, 2026
cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

Comment thread packages/browser/src/connect.ts Outdated

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread packages/browser/src/perf-observer.ts Outdated
Comment thread packages/browser/src/session.ts Outdated
Comment thread packages/react-doctor/src/cli/commands/browser.ts Outdated
@aidenybai

Copy link
Copy Markdown
Member Author

/rde parity

@react-doctor-evals

react-doctor-evals Bot commented Jun 18, 2026

Copy link
Copy Markdown

❌ Parity failed — trace d4ed45083ac35e225cd9a55783f6c52b. Check server logs for that trace ID.

@aidenybai

Copy link
Copy Markdown
Member Author

/rde parity

Comment thread packages/browser/src/perf-observer.ts
Comment thread packages/react-doctor/src/cli/commands/debug.ts
@aidenybai aidenybai changed the title feat(react): /react umbrella skill + in-house browser core + debug-agent debug job feat(react): /react-doctor umbrella skill + in-house browser core + debug-agent debug job Jun 18, 2026
Comment thread packages/debug/src/server.ts Outdated
Comment thread packages/react-doctor/src/cli/commands/debug.ts
Comment thread packages/browser/src/session.ts Outdated
@aidenybai aidenybai changed the base branch from cursor/perf-agent-devtools-setup-d63d to main June 19, 2026 03:03
Comment thread packages/debug/src/server.ts Fixed
Comment thread packages/debug/src/server.ts Fixed
… debug/perf runtime jobs

Adds the @react-doctor/browser package (CDP attach-and-launch over playwright-core:
open, eval, snapshot, screenshot, axe-core audit, console/network capture, LoAF-based
perf with per-script attribution, single-load report, viewport emulation) and the
React DevTools profiler harness injected as a document-start init script
(window.__REACT_PERF__), the @react-doctor/debug NDJSON logging server for the debug
job (per-project lock/log scoping, session dedup, daemon/json modes), the `browser`
and `debug serve` CLI commands, and the expanded /react-doctor skill (perf, debug,
design references) installed to both skills/ and .agents/skills/.
Adds the internal @react-doctor/mcp package and surfaces it as a `react-doctor
mcp` subcommand (bundled into the CLI and bin-fast-pathed like experimental-lsp,
since the stdio transport owns stdin/stdout). It exposes the skill's jobs as MCP
tools over stdio: doctor_scan (diagnose() score + diagnostics), the browser
tools (open with React profiler, eval, snapshot, screenshot, audit, console,
network, perf, report — each attaching a fresh CDP session per call), and the
debug log server (debug_serve / debug_read_logs / debug_clear_logs). Tools reuse
the existing @react-doctor/api and @react-doctor/browser/debug surfaces; failures
return isError results instead of throwing. Records one cli.invoked{command:mcp}
metric on start.
…on table

The five read-only page tools (browser_audit/console/network/perf/report) shared
the same url+connection+viewport input schema, read-only annotations, and
runTool/withSession lifecycle. Register them through a small registerPageTool
table so each is just its name, description, and a session action — no behavior
change (verified by the tool-registration tests).
- security: validate the model-supplied debug endpoint is a loopback /ingest/<id>
  URL before debug_read_logs/debug_clear_logs fetch it, and bound those fetches
  with a timeout — closes a prompt-injection SSRF (incl. a destructive DELETE).
- browser: BrowserSession.setViewport now holds its CDP session and clears the
  device-metrics override + detaches on dispose, so an emulated viewport can't
  linger on the persistent Chrome instead of relying on disconnect semantics.
- privacy: scrub the user's home directory out of tool error messages (Chrome /
  profile / CDP paths) before they go back to the model.
- docs: browser_audit/browser_report descriptions now state they reload the
  current page when no URL is given (use browser_perf to measure post-interaction).
Replace the CLI's inline { width; height } shape with @react-doctor/browser's
Viewport (type-only import, erased at build, so the lazy playwright-core boundary
is unaffected). Removes a duplicated type per AGENTS.md DRY.
Comment thread packages/debug/src/server.ts
…s review)

- debug_serve rejects non-loopback hosts (loopback-only bind for the
  agent-driven path; CLI --host keeps its on-device flexibility)
- debug_read_logs/clear_logs accept only endpoints debug_serve minted,
  replacing the loopback/path check with a stronger SSRF allowlist
- debug server appendLog rejects JSON array bodies
Move the playwright-core and axe-core value imports behind dynamic
import() inside @react-doctor/browser (load-playwright.ts + runAxe), so
the barrel has no heavy static deps and consumers can import it
statically. This dissolves the duplicated loadBrowser/isModuleNotFoundError
dance in both the CLI and MCP (the missing-playwright hint now lives in
one place) and lets parseViewport + MAX_VIEWPORT_PX live once in the
browser package, reused by the CLI's Commander wrapper and the MCP tool.

Bundle boundary preserved: axe-core splits into lazy chunks and
playwright-core stays an external dynamic import — neither lands in the
main cli.js startup path.
Record one `browser profile` pass that returns both a React render
profile (slowest commits, hottest components by self time, unnecessary
re-render counts) and a V8 CPU profile (functions ranked by self time,
via the DevTools sampler over CDP) as JSON for agents to consume — no
files written. Navigation runs before the profiler attaches so it binds
to the final document's renderer; both lenses cover the post-load +
interaction window. Surfaced through `BrowserSession.profile()`, the
`browser_profile` MCP tool, and the `react-doctor browser profile` CLI
command.
Comment thread packages/react-doctor/src/cli/utils/strip-unknown-cli-flags.ts
The pre-parse strip omitted --interaction, so its Playwright expression
leaked in as a positional and `browser profile --interaction` failed with
too-many-arguments. Add it to BROWSER_FLAG_SPEC with a regression test.
Comment thread .agents/skills/react-doctor/references/debug.md
Remove two constant comments that just restated NAVIGATION_TIMEOUT_MS /
LAUNCH_READY_TIMEOUT_MS, and a CLI comment that duplicated the
openWithReactProfiler explanation already on the method it calls.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c4e5658. Configure here.

} finally {
await cdpSession.send("Profiler.disable").catch(() => {});
await cdpSession.detach().catch(() => {});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profiler not stopped on failure

Medium Severity

In profile, when --interaction (or browser_profile’s interaction) throws, the finally block only calls CDP Profiler.disable and never Profiler.stop, and it never calls __REACT_PERF__.stop(). On the persistent Chrome session, a failed profile can leave the React and V8 profilers mid-recording and skew later browser profile runs until the page is reloaded.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c4e5658. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants