Skip to content

Commit b0813ca

Browse files
authored
feat: add react devtools passthrough (#435)
* feat: add react devtools passthrough * docs: clarify react devtools passthrough * fix: preserve react devtools global flags * docs: add react devtools validation guidance * docs: simplify react devtools validation notes
1 parent 2062bff commit b0813ca

20 files changed

Lines changed: 425 additions & 11 deletions

File tree

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,17 @@ If you know Vercel's [agent-browser](https://github.com/vercel-labs/agent-browse
3737

3838
Use `agent-device` for on-device UI automation, screenshots/recordings, app logs, network inspection, and performance snapshots.
3939

40-
When the task needs the React component tree, props, state, hooks, or render profiling, pair it with the complementary [`agent-react-devtools`](https://github.com/callstackincubator/agent-react-devtools) project. The two tools solve different layers of the same debugging workflow.
40+
When the task needs the React Native component tree, props, state, hooks, or render profiling, use the bundled passthrough:
41+
42+
```bash
43+
agent-device react-devtools status
44+
agent-device react-devtools get tree --depth 3
45+
agent-device react-devtools profile start
46+
agent-device react-devtools profile stop
47+
agent-device react-devtools profile slow --limit 5
48+
```
49+
50+
`react-devtools` dynamically runs pinned `agent-react-devtools@0.4.0` commands 1:1, so `agent-device` covers both the device/app runtime layer and React component internals without making React DevTools part of the daemon.
4151

4252
## Command Flow
4353

@@ -77,6 +87,7 @@ For people:
7787
For agents:
7888

7989
- [agent-device skill](skills/agent-device/SKILL.md)
90+
- [react-devtools skill](skills/react-devtools/SKILL.md)
8091
- [dogfood skill](skills/dogfood/SKILL.md)
8192
- [agent-device skill on ClawHub](https://clawhub.ai/okwasniewski/agent-device)
8293

skills/agent-device/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,4 @@ Use this skill as a router with mandatory defaults. Read this file first. For no
7171
- Need desktop surfaces, menu bar behavior, or macOS-specific interaction rules: [references/macos-desktop.md](references/macos-desktop.md)
7272
- Need remote HTTP transport, `connect --remote-config`, or tenant leases on a remote macOS host: [references/remote-tenancy.md](references/remote-tenancy.md)
7373
This includes remote React Native runs where `agent-device` now prepares Metro locally and manages the local Metro companion tunnel automatically.
74-
- Need the React component tree, props, state, hooks, or render profiling: pair `agent-device` with the complementary [`agent-react-devtools`](https://github.com/callstackincubator/agent-react-devtools) project when available.
74+
- Need the React Native component tree, props, state, hooks, or render profiling: use `agent-device react-devtools ...` and the [react-devtools skill](../react-devtools/SKILL.md).

skills/agent-device/references/debugging.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
Open this file when the task turns into failure triage, logs, network inspection, permission prompts, setup trouble, or unstable session behavior.
66

7-
If the debugging task needs the React component tree, props, state, hooks, or render profiling, pair `agent-device` with the complementary [`agent-react-devtools`](https://github.com/callstackincubator/agent-react-devtools) project instead of trying to infer those internals from the accessibility tree or app logs alone.
7+
If the debugging task needs the React Native component tree, props, state, hooks, or render profiling, use `agent-device react-devtools ...` and the `skills/react-devtools` workflow instead of trying to infer those internals from the accessibility tree or app logs alone.
88

99
## Main commands to reach for first
1010

skills/agent-device/references/exploration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Open this file when the app or screen is already running and you need to discove
2020
- User asks what is visible on screen: `snapshot`
2121
- User asks for exact text from a known target: `get text`
2222
- User asks you to tap, type, or choose an element: `snapshot -i`, then act
23-
- User asks for the React component tree, props/state/hooks, or render profiling: pair `agent-device` with the complementary [`agent-react-devtools`](https://github.com/callstackincubator/agent-react-devtools) project
23+
- User asks for the React Native component tree, props/state/hooks, or render profiling: use `agent-device react-devtools ...` and the `skills/react-devtools` workflow
2424
- React Native dev or debug build shows warning/error UI: capture enough evidence to identify it, dismiss it if it is not the requested behavior, then continue the flow and report it in the summary
2525
- The on-screen keyboard is blocking the next step: `keyboard dismiss`; on iOS do this only while an app session is active, and use `keyboard status|get` only on Android
2626
- UI does not expose the answer: say so plainly; do not browse or force the app into a new state unless asked

skills/dogfood/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ agent-device --session {SESSION} close
166166
- Re-snapshot after any mutation (navigation, modal, list update, form submit).
167167
- Use `fill` for clear-then-type semantics; use `type` for incremental typing behavior checks.
168168
- Keep logs optional and targeted: enable/read app logs only when useful for diagnosis.
169-
- If the issue appears rooted in React internals rather than device/app runtime behavior, pair `agent-device` with the complementary [`agent-react-devtools`](https://github.com/callstackincubator/agent-react-devtools) project for component-tree or render-profiling inspection.
169+
- If the issue appears rooted in React Native internals rather than device/app runtime behavior, use `agent-device react-devtools ...` and the `skills/react-devtools` workflow for component-tree or render-profiling inspection.
170170
- Never read source code of the app under test; findings must come from observed runtime behavior.
171171
- Write each issue immediately to avoid losing evidence.
172172
- Never delete screenshots/videos/report artifacts during a session.

skills/react-devtools/SKILL.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
name: react-devtools
3+
description: Inspect and profile React Native component trees from agent-device. Use when debugging React Native props, state, hooks, render causes, slow components, excessive re-renders, or questions like why a component re-rendered.
4+
---
5+
6+
# react-devtools
7+
8+
Use this skill when the task needs React Native internals that are not visible in the accessibility tree: component hierarchy, props, state, hooks, render causes, or profiling data.
9+
10+
Run commands through `agent-device react-devtools`. The command dynamically runs pinned `agent-react-devtools@0.4.0` and passes arguments through 1:1.
11+
12+
The first run may download the pinned package from npm. `agent-device` global flags work before or after `react-devtools`; use `--` before downstream flags only when they intentionally share an `agent-device` global flag name.
13+
14+
## Default flow
15+
16+
1. Use `agent-device` to open the React Native app and verify the visible state when needed.
17+
2. Check `agent-device react-devtools status`.
18+
3. If no app is connected, start or wait for the devtools daemon, then reload or relaunch the app.
19+
4. Inspect with `get tree`, `find`, and `get component`.
20+
5. Profile only around the interaction being investigated.
21+
6. Verify the fix with the same command sequence and interaction.
22+
23+
For cross-platform validation with explicit `--device`, `--udid`, or `--serial` selectors, prefer an isolated `--state-dir` over separate named sessions. Named sessions enable bound-session locks during setup. Restart `agent-device react-devtools` between iOS and Android runs so `status`, `get tree`, and profiling clearly refer to the currently launched app.
24+
25+
## Main commands
26+
27+
```bash
28+
agent-device react-devtools status
29+
agent-device react-devtools wait --connected
30+
agent-device react-devtools get tree --depth 3
31+
agent-device react-devtools find <ComponentName>
32+
agent-device react-devtools get component @c5
33+
agent-device react-devtools profile start
34+
agent-device react-devtools profile stop
35+
agent-device react-devtools profile slow --limit 5
36+
agent-device react-devtools profile rerenders --limit 5
37+
```
38+
39+
## Decision rules
40+
41+
- Need current UI text, refs, screenshots, logs, network, or device metrics: use the `agent-device` skill.
42+
- Need props, state, hooks, component ownership, render causes, or React profiler data: use this skill.
43+
- Start component-tree reads with `get tree --depth 3` or `find <name>` to keep output bounded.
44+
- Labels like `@c5` reset when the app reloads or components remount. After reload, run `wait --connected` and inspect again.
45+
- Profiling only captures renders between `profile start` and `profile stop`.
46+
- On Android, set `adb reverse tcp:8097 tcp:8097` for React DevTools. If Metro is local, also set `adb reverse tcp:8081 tcp:8081`.
47+
48+
## References
49+
50+
| File | When to read |
51+
| --------------------------------------- | --------------------------------------------- |
52+
| [commands.md](references/commands.md) | Command reference and common inspection flows |
53+
| [profiling.md](references/profiling.md) | Render profiling workflow and interpretation |
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# React DevTools Commands
2+
3+
All commands are run through `agent-device react-devtools`.
4+
5+
## Connection
6+
7+
```bash
8+
agent-device react-devtools start
9+
agent-device react-devtools stop
10+
agent-device react-devtools status
11+
agent-device react-devtools wait --connected --timeout 30
12+
agent-device react-devtools wait --component <ComponentName> --timeout 30
13+
```
14+
15+
- `status` shows the daemon port, connected apps, component count, profiling state, uptime, and last connection event.
16+
- Most commands auto-start the daemon, but `start` is useful before launching or reloading the app.
17+
- React Native development builds connect to the daemon on port 8097. For Android emulators or physical devices, use `adb reverse tcp:8097 tcp:8097` if the app cannot reach the host. If the app also uses local Metro, set `adb reverse tcp:8081 tcp:8081`.
18+
19+
## Validation Notes
20+
21+
- When validating the same app across iOS and Android with explicit `--device`, `--udid`, or `--serial` selectors, prefer an isolated `--state-dir` over separate named sessions. A named `--session` enables bound-session lock behavior, so setup commands with explicit target selectors can be rejected.
22+
- Restart the React DevTools daemon between platforms so `status`, `get tree`, and profiling output belong to the currently launched app.
23+
- Verify the app is visibly loaded with `snapshot` before collecting React internals. Use `react-devtools` for component state and profiling, not for proving the device/app surface is open.
24+
25+
## Component Inspection
26+
27+
```bash
28+
agent-device react-devtools get tree --depth 3
29+
agent-device react-devtools get component @c5
30+
agent-device react-devtools find Button
31+
agent-device react-devtools find Button --exact
32+
agent-device react-devtools count
33+
agent-device react-devtools errors
34+
```
35+
36+
- `get tree` prints a component hierarchy with labels like `@c1`, `@c2`.
37+
- Use `--depth` on large apps. Start at `--depth 3` or `--depth 4`.
38+
- `get component` accepts a label or numeric React fiber id and shows props, state, and hooks.
39+
- `find` searches by display name. Use `--exact` when fuzzy results are noisy.
40+
- `errors` lists components with React-tracked warnings or errors.
41+
42+
## Profiling
43+
44+
```bash
45+
agent-device react-devtools profile start "interaction name"
46+
agent-device react-devtools profile stop
47+
agent-device react-devtools profile slow --limit 5
48+
agent-device react-devtools profile rerenders --limit 5
49+
agent-device react-devtools profile report @c5
50+
agent-device react-devtools profile timeline --limit 20
51+
agent-device react-devtools profile commit 3
52+
agent-device react-devtools profile export profile.json
53+
agent-device react-devtools profile diff before.json after.json --limit 10
54+
```
55+
56+
- `profile slow` ranks components by average render duration.
57+
- `profile rerenders` ranks components by render count.
58+
- `profile report @cN` shows render causes and changed props/state/hooks for one component.
59+
- `profile timeline` lists commits. Use `--limit` and `--offset` for long sessions.
60+
- `profile export` writes React DevTools Profiler JSON that can be diffed later.
61+
62+
## Common Flows
63+
64+
Inspect a component:
65+
66+
```bash
67+
agent-device react-devtools status
68+
agent-device react-devtools get tree --depth 3
69+
agent-device react-devtools find SearchScreen
70+
agent-device react-devtools get component @c12
71+
```
72+
73+
Profile a slow interaction:
74+
75+
```bash
76+
agent-device react-devtools profile start "slow search"
77+
# Trigger the interaction with agent-device or ask the user to perform it.
78+
agent-device react-devtools profile stop
79+
agent-device react-devtools profile slow --limit 5
80+
agent-device react-devtools profile rerenders --limit 5
81+
```
82+
83+
Verify a render fix:
84+
85+
```bash
86+
agent-device react-devtools profile start "after fix"
87+
# Repeat the same interaction.
88+
agent-device react-devtools profile stop
89+
agent-device react-devtools profile slow --limit 5
90+
agent-device react-devtools profile rerenders --limit 5
91+
```
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# React Native Profiling
2+
3+
Use this workflow when the user reports slow interactions, excessive re-renders, unstable props, or unclear render causes.
4+
5+
## Baseline
6+
7+
```bash
8+
agent-device react-devtools status
9+
agent-device react-devtools count
10+
agent-device react-devtools get tree --depth 3
11+
```
12+
13+
If the app is not connected, run:
14+
15+
```bash
16+
agent-device react-devtools start
17+
agent-device react-devtools wait --connected
18+
```
19+
20+
Then reload or relaunch the React Native app if needed.
21+
22+
## Capture One Interaction
23+
24+
```bash
25+
agent-device react-devtools profile start "short label"
26+
# Trigger exactly the interaction being investigated.
27+
agent-device react-devtools profile stop
28+
```
29+
30+
Keep the profiling window narrow. Extra navigation, warm-up work, or unrelated gestures make the report harder to interpret.
31+
32+
## Identify Suspects
33+
34+
```bash
35+
agent-device react-devtools profile slow --limit 5
36+
agent-device react-devtools profile rerenders --limit 5
37+
```
38+
39+
- A component with high average render time is a slow-render suspect.
40+
- A component with high render count is a re-render suspect.
41+
- A component can be both.
42+
43+
## Drill In
44+
45+
```bash
46+
agent-device react-devtools profile report @c12
47+
agent-device react-devtools get component @c12
48+
```
49+
50+
Use `profile report` to identify render causes and changed keys. Use `get component` to inspect current props, state, and hooks.
51+
52+
Common interpretations:
53+
54+
| Signal | Meaning | Typical follow-up |
55+
| ------------------------------------------ | ----------------------------------- | ---------------------------------------------- |
56+
| `props-changed` with function props | Parent may pass unstable callbacks | Check whether the parent can use `useCallback` |
57+
| `props-changed` with object or array props | Parent may pass unstable references | Check whether the parent can use `useMemo` |
58+
| `parent-rendered` with many child renders | Child has no bailout | Check whether `React.memo` is appropriate |
59+
| `state-changed` | Component state caused the render | Check whether the state update is necessary |
60+
| `hooks-changed` | Hook value or dependency changed | Inspect hook values and dependencies |
61+
62+
## Verify
63+
64+
After making a change, repeat the same interaction:
65+
66+
```bash
67+
agent-device react-devtools profile start "after fix"
68+
# Repeat the same interaction.
69+
agent-device react-devtools profile stop
70+
agent-device react-devtools profile slow --limit 5
71+
agent-device react-devtools profile rerenders --limit 5
72+
```
73+
74+
Compare render counts, average durations, changed keys, and commit counts against the baseline.

src/__tests__/cli-help.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ test('appstate --help prints command help and skips daemon dispatch', async () =
9595
assert.match(result.stdout, /Global flags:/);
9696
});
9797

98+
test('help react-devtools prints passthrough command help and skips daemon dispatch', async () => {
99+
const result = await runCliCapture(['help', 'react-devtools']);
100+
assert.equal(result.code, 0);
101+
assert.equal(result.daemonCalls, 0);
102+
assert.match(result.stdout, /Usage:\n agent-device react-devtools \[\.\.\.args\]/);
103+
assert.match(result.stdout, /React Native component trees/);
104+
});
105+
98106
test('help unknown command prints error plus global usage and skips daemon dispatch', async () => {
99107
const result = await runCliCapture(['help', 'not-a-command']);
100108
assert.equal(result.code, 1);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import fs from 'node:fs';
2+
import { test } from 'vitest';
3+
import assert from 'node:assert/strict';
4+
import {
5+
AGENT_REACT_DEVTOOLS_PACKAGE,
6+
buildReactDevtoolsNpmExecArgs,
7+
} from '../cli/commands/react-devtools.ts';
8+
9+
test('react-devtools passthrough pins agent-react-devtools package version', () => {
10+
assert.equal(AGENT_REACT_DEVTOOLS_PACKAGE, 'agent-react-devtools@0.4.0');
11+
assert.deepEqual(buildReactDevtoolsNpmExecArgs(['get', 'tree', '--depth', '3']), [
12+
'exec',
13+
'--yes',
14+
'--package',
15+
'agent-react-devtools@0.4.0',
16+
'--',
17+
'agent-react-devtools',
18+
'get',
19+
'tree',
20+
'--depth',
21+
'3',
22+
]);
23+
});
24+
25+
test('react-devtools docs mention the pinned package version', () => {
26+
const docs = ['README.md', 'website/docs/docs/commands.md', 'skills/react-devtools/SKILL.md'];
27+
28+
for (const file of docs) {
29+
assert.match(fs.readFileSync(file, 'utf8'), new RegExp(AGENT_REACT_DEVTOOLS_PACKAGE));
30+
}
31+
});

0 commit comments

Comments
 (0)