A plugin for Claude Code and Codex that turns your coding agent into a React Native development partner — one that reads your running app's component tree, store state, and navigation over the Chrome DevTools Protocol, taps real UI on iOS and Android, and records the evidence that the feature actually works.
# 1. Install (Claude Code — Codex below)
/plugin marketplace add Lykhoyda/rn-dev-agent
/plugin install rn-dev-agent@rn-dev-agent
/reload-plugins
# 2. Onboard your app — checks the toolchain, writes the project config
/rn-dev-agent:setup
# 3. Ask for a feature. Get it back verified on the simulator.
/rn-dev-agent:rn-feature-dev add a shopping cart with badge, item list, and checkout flow
The agent explores your codebase, designs the change, implements it — then connects to the running app, navigates to the screen, checks the component tree and store state, taps through the flow, and saves the walk as a replayable test. You get working code and the proof.
Codex users: replace /rn-dev-agent:<name> with $rn-dev-agent:<name>. Install steps →
They are good at writing React Native code and bad at knowing whether it runs. This plugin closes that loop — and the numbers below come from real features built on a real Expo app, not synthetic benchmarks.
| Verified, not claimed | After implementing, the agent connects over CDP, walks the screen, reads the component tree and store, exercises the interaction, and screenshots the result — before it says "done" |
| 210× faster replays | A 3-step wizard that took ~14 min as an interactive walk replays in ~4 s as a saved action. Average session time across the measured features dropped from ~12 min to ~4 min once actions existed |
| Flows that repair themselves | When a testID drifts, the saved action fuzzy-matches the live snapshot, patches its own YAML, and retries. Cosmetic drift is absorbed; genuinely broken product logic is surfaced, never auto-fixed |
| Minutes, not sessions | Simple features land in 3–5 min, complex multi-step flows in 11–25 min. Zero crashes and zero manual interventions across all 35 measured features |
| iOS and Android, one contract | In-tree XCTest and UiAutomator runners give real taps, typing, scrolling, and screenshots — shipped as prebuilt artifacts so first use skips the cold build |
| Both hosts, full parity | Claude Code: 17 slash commands + 11 skills + 6 agents. Codex: 28 native skills (17 workflow + 11 domain). The same 81 MCP tools on both |
Full benchmarks and methodology →
Yes, if you build React Native or Expo apps with a coding agent, you run an iOS Simulator or Android Emulator locally, and you are tired of reviewing changes that were never actually opened. It pays off most on apps with real navigation, real state, and flows you re-walk every session.
Not yet, if you work on web-only React, have no local simulator or emulator, or need an agent pointed at production or store-signed builds — that last one is deliberately out of scope, see Security.
/rn-dev-agent:rn-feature-dev "<what you want>"
│
├─ 1 Discover understand the feature, plan the work
├─ 2 Explore parallel agents map screens, store, navigation, conventions
├─ 3 Question clarify edge cases, error states, data flow
├─ 4 Architect design the change against your existing patterns
├─ 5 Implement store, components, navigation, testIDs
├─ 5.5 VERIFY ◀── CDP health · component tree · store state · interaction · screenshot
├─ 6 Review parallel agents check correctness and RN conventions
└─ 7 PROOF ◀── rehearse off camera, persist the action, record a clean replay
│
working code + video + screenshots + PR body
Phase 5.5 is the one that makes the difference: nothing is reported as working until it has been observed working. Phase 7 rehearses first, so discovery fumbling never ends up in the recording.
The 8-phase pipeline in detail →
Claude spelling shown. Codex has native parity for every row — use $rn-dev-agent:<name>.
| Command | Purpose |
|---|---|
/rn-dev-agent:rn-feature-dev <desc> |
Full 8-phase feature pipeline |
/rn-dev-agent:test-feature <desc> |
Test an already-implemented feature; auto-replays a matching saved action |
/rn-dev-agent:qa-pr <PR> |
Device-test a GitHub PR on iOS simulator, Android emulator, and/or physical device |
/rn-dev-agent:debug-screen |
Diagnose and fix a broken screen — parallel evidence from CDP + native logs + component tree |
/rn-dev-agent:proof-capture <desc> |
Rehearsal-gated video + screenshots + generated PR body |
/rn-dev-agent:observe |
Local web UI to watch the agent live — tool-call timeline, device mirror, route/store/component-tree panels, browser-triggered action and E2E replays (guide) |
/rn-dev-agent:setup |
Onboard a project — Claude manages CLAUDE.md, Codex manages AGENTS.md; every project write is previewed |
The other 10 commands — actions, regression, build, diagnostics
Actions & regression:
| Command | Purpose |
|---|---|
/rn-dev-agent:list-learned-actions |
List persisted actions, flows, and feedback memories |
/rn-dev-agent:run-action <name> |
Replay a saved action with auto-repair and structured run records |
/rn-dev-agent:lock-e2e <name> |
Promote a verified action into a frozen, locked e2e regression test (strict no-repair run required) |
Build & session:
| Command | Purpose |
|---|---|
/rn-dev-agent:run-workflow <journey> |
Establish the proven operating sequence before a real device journey |
/rn-dev-agent:build-and-test <desc> |
Build the app (local or EAS), install on device, then test |
Diagnostics:
| Command | Purpose |
|---|---|
/rn-dev-agent:doctor |
Strictly read-only multi-axis plugin/MCP/schema/task/environment diagnosis; recommends but never executes recovery |
/rn-dev-agent:check-env |
Quick environment-readiness check |
/rn-dev-agent:nav-graph |
Extract and inspect the app navigation graph |
/rn-dev-agent:check-vercel-rules |
Report drift between bundled best-practice rules and upstream |
/rn-dev-agent:send-feedback |
Open a GitHub issue with sanitized environment context |
Codex exposes exactly these 17 workflow skills plus 11 domain skills; install-time
source-command-* migration is deliberately disabled.
The plugin also keeps a gitignored per-project troubleshooting memory
(.rn-agent/local/troubleshooting.md) — auto-captured failures and config notes, read at session start.
An action is a saved Maestro flow the agent emits when verification passes — not something you author. Each task then splits in two: replayable actions for the predictable parts (login, navigation, multi-step setup) and live discovery for the part that is actually new. Actions run as prologues to reach a known state before fresh interactive work.
| What | A saved, parameterised flow with a metadata header and ${KEY} placeholders |
| Where | .rn-agent/actions/<name>.yaml — the plugin's home in your project is .rn-agent/ |
| Create one | Run /rn-dev-agent:test-feature <description>; the verified walk is saved automatically |
| Run one | /rn-dev-agent:run-action <name> — the agent also picks actions itself when it needs a known state |
| Self-repair | If a testID changes, cdp_repair_action fuzzy-matches against the live snapshot, patches the YAML, and retries. Small UI drift is absorbed; broken product logic is surfaced, never auto-fixed |
| Lock it in | /rn-dev-agent:lock-e2e freezes a passing action into .rn-agent/e2e/ — locked tests run strict (no repair) via cdp_run_e2e_suite |
| Why it works | Pure scripts don't adapt; a pure LLM re-derives everything every session. Every successful verification adds an action, every drift gets quietly absorbed, every truly broken flow escalates |
Everything the commands above are built from — expand what you need. The architecture and review phases also apply a bundled set of React Native and React best-practice rules.
81 MCP tools across six families
| Family | What it's for | Examples |
|---|---|---|
| Session | Fence one worktree, Metro, app, device, runner, Observe UI, and proof run | rn_session |
| CDP | React internals via Chrome DevTools Protocol | cdp_status, cdp_component_tree, cdp_store_state, cdp_evaluate, cdp_native_errors, cdp_navigate, collect_logs |
| Device | Native interaction with the simulator/emulator | device_find, device_press, device_fill, device_screenshot, device_pick_date, device_batch |
| Actions | Record / replay / self-repair persistent flows, including fail-stop login | cdp_login_prologue, cdp_run_action, cdp_repair_action, cdp_record_test_save_as_action, cdp_lock_e2e_test, cdp_run_e2e_suite |
| Testing | E2E replay and PR-ready proof | proof_step, cross_platform_verify, maestro_run, maestro_test_all (cdp_auto_login is legacy per-call recovery, not a failed-login fallback or PR proof) |
| Macro-Asserts | State-assertive replays — internal state, not pixels | expect_redux, expect_route, expect_visible_by_testid, expect_text |
The committed tool surface is asserted in CI against a golden registry
(packages/rn-dev-agent-core/test/fixtures/tool-registry.json), so tool additions and removals
can't silently drift. Full tool reference →
Reliability baked into the tool layer
- Self-healing taps — a stale
@refis re-bound by identity (testID/label/role, unique match only). Opt out withRN_SELF_HEAL=0. A dispatched tap is never replayed because its effect is uncertain: on iOS an unchanged tap stays a success carryingmeta.noUiChange, and on Android a tap whose effect cannot be observed fails (INTERACTION_EFFECT_UNVERIFIED,mutation: possible) rather than reporting success. - Quiescence bypass (iOS) — XCTest's private idle-wait is disabled by default so apps with Reanimated or looping animations can't hang queries. Opt out with
RN_QUIESCENCE_BYPASS=0. - Engine pinning — setup installs attested maestro-runner
1.1.24in the versioned pin-cache (floor>= 1.1.24) and verifies its checksum fail-closed; replay and/doctorrefuse missing, older, or unattested engines. - Degraded-runtime detection — when taps succeed but the app doesn't respond, results carry a "simulator likely wedged, reboot it" hint instead of a misleading "element not found."
Six specialized agents
Each runs a focused protocol: tester, PR QA, debugger, code explorer, architect, reviewer.
Note:
rn-tester,rn-pr-qa, andrn-debuggerneed MCP tools, which don't propagate to spawned subagents — use/rn-dev-agent:test-feature,/rn-dev-agent:qa-pr, and/rn-dev-agent:debug-screen, which run the protocols inline (GH #31).
Architecture
Claude Code / Codex
├── Host workflows + shared domain knowledge
│ Claude: commands/agents · Codex: explicit workflow/domain skills
│
├── Fenced session authority ── worktree → Metro → app → device
│
├── MCP Server (CDP Bridge) ─── WebSocket → bound Metro → Hermes CDP
│ Tools: component tree, store state, profiling, network,
│ interaction, recording, self-healing replay
│
└── Device interaction
├── iOS → in-tree rn-fast-runner (XCTest /command HTTP)
└── Android → in-tree rn-android-runner (UiAutomator instrumentation)
│ │
iOS Simulator Android Emulator
Device lifecycle (boot / install / launch): xcrun simctl + adb
E2E test execution: maestro-runner 1.1.24 (pin-cache only)
/plugin marketplace add Lykhoyda/rn-dev-agent
/plugin install rn-dev-agent@rn-dev-agent
/reload-plugins
Local checkout: claude --plugin-dir /path/to/rn-dev-agent (the root .claude-plugin/marketplace.json
resolves the plugin package from packages/claude-plugin/).
codex plugin marketplace add Lykhoyda/rn-dev-agent
codex plugin add rn-dev-agent@rn-dev-agentLocal checkout: register the package directory /path/to/rn-dev-agent/packages/codex-plugin — not
the repository root. The Codex package is self-contained (bundled MCP runtime, native runner
sources, helpers, and runner manifest) and loads the same cdp MCP server from its .mcp.json.
Codex does not load Claude Code hooks — No plugin hooks is expected. Codex 0.145.0 is the
live-refresh floor; older hosts are restart-only. An external CLI or manual plugin change always
requires exiting and relaunching Codex.
cd /path/to/your-rn-app
Claude: /rn-dev-agent:setup
Codex: $rn-dev-agent:setup
Claude setup manages CLAUDE.md; Codex setup manages an idempotent sentinel-bounded AGENTS.md
block, runs strictly read-only package/recovery diagnostics first, and previews every later
project write for consent. Full setup guide →
What setup checks, and what it fixes for you
Claude hooks and normal runtime use can perform the automatic handling below; Codex setup keeps recovery diagnosis read-only and prints the exact commands for you to confirm and run.
| Check | Required | Automatic handling |
|---|---|---|
| Node.js ≥ 24 | Yes | No |
| CDP bridge deps | Yes | Yes |
| rn-fast-runner (iOS) | iOS targets only | Prebuilt artifact on releases; one-time xcodebuild build-for-testing fallback |
| rn-android-runner (Android) | Android targets only | Prebuilt artifact on releases; Gradle build fallback on first use |
| maestro-runner | Yes | Yes (pin-cache engine >= 1.1.24, attested 1.1.24 checksum-verified) |
| iOS Simulator / Android Emulator | One platform | No |
| Session-bound Metro | Yes | Project integration starts or validates it through literal pnpm ios / pnpm android |
| CDP connection | Yes | rn_session owns the binding; cdp_status is passive and cdp_connect pins the exact target |
| ffmpeg | Optional (proof videos; required for strict iOS proof) | Yes |
| idb + idb-companion | Optional (smooth observe-UI mirroring) | Yes |
Claude automation failures and Codex missing prerequisites are reported with step-by-step manual instructions.
Prebuilt runners: on a released version, the device runners install from a verified prebuilt
artifact (SHA-256-checked local cache, then the GitHub Release asset for your exact plugin
version), so the first device_snapshot action=open skips the cold build. Resolution is fail-open
— offline, a checksum mismatch, or a dev checkout falls back transparently to the on-machine build
(the host's doctor workflow reports which one you got). Force local builds with
RN_RUNNER_BUILD=local.
Most apps need zero setup — the plugin reads the React fiber tree directly via Metro's CDP endpoint. Redux is auto-detected.
Zustand stores — one line in your app entry (details):
if (__DEV__) {
global.__ZUSTAND_STORES__ = { auth: useAuthStore, cart: useCartStore };
}testIDs — add to interactive elements for reliable queries:
<Pressable testID="checkout-button" onPress={handleCheckout}>
<Text testID="cart-badge">{itemCount}</Text>
</Pressable>35 features completed on the public test app.
| Complexity | Time | Crashes | Manual interventions |
|---|---|---|---|
| Simple (search, toggle, store) | 3–5 min | 0 | 0 |
| Medium (forms, charts, lists) | 5–10 min | 0 | 0 |
| Complex (3-step wizard, onboarding) | 11–25 min | 0 | 0 |
Libraries verified end-to-end: react-hook-form, zod, @tanstack/react-query, @gorhom/bottom-sheet, @shopify/flash-list, zustand, react-native-svg, expo-notifications, react-native-reanimated, react-native-gesture-handler, expo-haptics
The cdp_evaluate tool runs arbitrary JavaScript in your app's Hermes runtime with full access to
the component tree, store state, AsyncStorage, and any in-memory secrets. This is intentional —
runtime introspection is what makes the plugin useful — but it means only run this plugin against
apps where you trust the agent's prompts.
- Local dev environments only. Do not point the plugin at production builds, store-signed apps, or any app holding real user data.
- Treat the agent like a developer with shell access to your laptop. Any prompt that reaches
cdp_evaluate(directly or through another tool) can read or mutate your app's runtime state. - Use the fenced session as CDP authority.
rn_sessionbinds the intended worktree, Metro, app, and device;cdp_targetsmay explain ambient Hermes processes but never authorizes selecting one. See Parallel session authority.
The plugin makes no attempt to sandbox cdp_evaluate. If you need that, gate tool access through
your agent's permission prompts rather than trusting the tool layer.
What the observability UI and the local evidence store record
The observability UI (/rn-dev-agent:observe)
binds to 127.0.0.1 only and rejects cross-origin requests via Host-header + Sec-Fetch-Site
checks. It is read-only except for two deliberate, CSRF-token-gated endpoints that trigger action
and locked-E2E replays. Tool arguments are deep-redacted fail-closed before reaching the stream and
typed fill text is never streamed verbatim (see the
security posture for
what is redacted and what stays visible), and the recorder keeps only a small bounded in-memory
ring buffer — the event stream itself never touches disk (action RunRecords and locked-E2E run
output follow the session state directory: fenced sessions use their session-private runtime state,
while an unfenced process uses .rn-agent/state/ for actions and .rn-agent/state/e2e-runs/ for
E2E history; .rn-agent/e2e/ contains the locked test definitions).
Meaningful tool failures and immediate successful retries also feed a separate, local-only evidence
store at ~/.claude/rn-agent/experience/patterns.jsonl. Records are sanitized before writing,
deduplicated, capped at 500 patterns, and retained for 14 days; ordinary successful calls are never
stored. Runner failures may also retain up to five sanitized diagnostics bundles alongside that
store, each capped at 200 typed lifecycle events and 256 KB for reviewed feedback or an explicit
collect_logs export. From an installed plugin package, inspect the read-only trend report with
node <plugin-package>/rn-dev-agent-core/dist/experience-trends.js --since <previous-report-ISO-timestamp>
(omit --since for the last 24 hours). The command never updates the evidence store or uploads data.
| Problem | Solution |
|---|---|
| "Metro not found" | Inspect rn_session(action="status"), then use literal pnpm ios or pnpm android through the confirmed project integration |
| "No Hermes target" | Open the bound app, inspect passive cdp_status, then use cdp_connect to pin the exact signed target |
| CDP rejected (1006) | Close React Native DevTools, Flipper, or Chrome DevTools |
| Zustand store error | Add global.__ZUSTAND_STORES__ (setup) |
| Plugin not detected (Claude) | /plugin install rn-dev-agent@rn-dev-agent then /reload-plugins |
| Subagent says "MCP tools unavailable" | Never spawn rn-tester/rn-pr-qa/rn-debugger via the Task tool — use /rn-dev-agent:test-feature, /rn-dev-agent:qa-pr, or /rn-dev-agent:debug-screen instead (GH #31) |
More: host recovery, device runners, wedged simulators
| Problem | Solution |
|---|---|
| Plugin not detected (Codex) | Inspect with codex plugin list --json and /mcp verbose; user-confirm codex plugin add rn-dev-agent@rn-dev-agent --json, then relaunch after external changes |
| Codex tools fail after upgrade | /mcp verbose inspects only. Relaunch Codex for external/manual changes or legacy hosts; never kill another host's bridge |
| Blank white screen after many reloads | NativeWind stylesheet corruption after 5+ cdp_reload cycles — kill and restart Metro, relaunch the app |
device_scroll times out on Reanimated screens |
A waitForIdle round-trip can deadlock against Reanimated worklets; scroll routes through the in-tree runner's HID synthesis instead. Ensure the runner is healthy via the device session |
Legacy AgentDeviceRunner re-appears on iOS |
Stale ~/.agent-device/daemon.json respawns the upstream runner. The plugin terminates stale processes at session-open (opt out: RN_DEVICE_KILL_LEGACY=0); manual cleanup: pkill -f AgentDeviceRunner && rm -f ~/.agent-device/daemon.{json,lock} |
| iOS "rn-fast-runner did not become ready" | The runner self-build timed out or failed. In a source checkout, pre-build once: cd packages/rn-fast-runner/RnFastRunner && xcodebuild build-for-testing -project RnFastRunner.xcodeproj -scheme RnFastRunner -destination "platform=iOS Simulator,id=<UDID>" -derivedDataPath ../build/DerivedData |
device_fill reports TEXT_ENTRY_UNVERIFIED |
A fill attempt ran, but stable exact read-back could not prove the requested value. Check meta.mutation: retry from a fresh snapshot only when it is none; for observed or possible, read and rebind the field before correcting it so a blind retry cannot double-type |
| Need an intentional coordinate tap | Use device_press({x, y}) (or a batch press step with x/y). With a visible iOS keyboard, raw coordinates are geometry-unknown: the keyboard is proven hidden before the one tap. Prefer fresh refs for normal UI controls |
| Native logs include another device/app | Reopen the exact device session. collect_logs pins Android to that session's adb serial and iOS to that simulator plus the current target-app PID; it fails closed when exact scope cannot be resolved. When the probe runs and proves the app is not running, the stream stays pinned to that simulator and reports scopes.native_ios.process = app-not-running-device-scoped so a crash trail is still captured |
| Want XCTest's stock idle-waits back | Kill the running runner (pkill -f RnFastRunnerUITests), set RN_QUIESCENCE_BYPASS=0, reopen the device session, and inspect the next device result's meta.quiescenceBypass |
Seeing meta.reResolved / meta.noUiChange |
Stale-ref healing at work; meta.noUiChange means the iOS tap was dispatched but changed nothing. Disable ref healing with RN_SELF_HEAL=0 (retryIfNoChange is a deprecated no-op — taps are never replayed) |
Enable auto-update in the host plugin manager, or update manually:
Claude: /plugin update rn-dev-agent@rn-dev-agent
/reload-plugins
Codex: codex plugin marketplace upgrade rn-dev-agent
codex plugin add rn-dev-agent@rn-dev-agent --json
# relaunch after this external mutation
Release notes: GitHub Releases · core changelog
Development — building from source
This is a Yarn workspace monorepo:
| Package | What it is |
|---|---|
packages/rn-dev-agent-core |
The MCP server (CDP bridge, device control, actions, testing) — all TypeScript source and tests |
packages/claude-plugin |
Claude Code plugin package — manifest, commands, agents, skills, hooks, MCP registration |
packages/codex-plugin |
Codex plugin package — self-contained with bundled runtime |
packages/shared-agent-knowledge |
Source of truth both host packages are generated from |
packages/rn-fast-runner |
In-tree iOS XCTest device runner |
packages/rn-android-runner |
In-tree Android UiAutomator device runner |
apps/docs-site |
Astro Starlight docs → lykhoyda.github.io/rn-dev-agent |
git clone https://github.com/Lykhoyda/rn-dev-agent.git
cd rn-dev-agent
corepack enable
corepack yarn install --immutable
corepack yarn build:host-runtimes # builds core + generates both host packagesRun locally: claude --plugin-dir /path/to/rn-dev-agent (Claude Code) or register
packages/codex-plugin (Codex).
corepack yarn test # complete unit-test suite
corepack yarn lint # oxlint
corepack yarn format:check # oxfmtVersioning uses changesets; every tool-surface change
must update the golden registry (node scripts/update-tool-registry.mjs).
Read the docs · Star the repo · Report a bug or run /rn-dev-agent:send-feedback
Free · open source · MIT
