Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ Published package notes:
## Commands

```bash
rdt doctor --session demo
rdt session open --url http://localhost:3000 --browser chromium --engine auto --session demo
rdt session connect --ws-endpoint ws://127.0.0.1:3000/ --target-url localhost:3000 --session remote
rdt session attach --cdp-url http://127.0.0.1:9222 --target-url localhost:3000 --session cdp
rdt session doctor --session demo
rdt tree get --session demo
rdt tree stats --session demo --top 5
rdt interact type --session demo --selector 'input[name="query"]' --text hello
rdt node search App --session demo --snapshot <snapshotId>
rdt node search App --session demo --snapshot <snapshotId> --structured
rdt node inspect <nodeId> --session demo --snapshot <snapshotId>
rdt profiler start --session demo
rdt profiler stop --session demo
Expand All @@ -126,6 +128,7 @@ rdt profiler export --session demo --compress
## Snapshot Semantics

- `tree get` returns a `snapshotId`.
- `tree stats` returns the same `snapshotId` plus summary fields such as `rootCount`, `nodeCount`, `rootSummaries`, and `topLevelComponents`.
- Node IDs are only meaningful within that snapshot.
- The runtime currently keeps up to `5` snapshots in memory per session.
- Agent-friendly recommended flow:
Expand All @@ -139,6 +142,7 @@ rdt profiler export --session demo --compress
## Doctor

- `rdt session doctor --session <name>` reports runtime readiness and trust boundaries before a deeper investigation.
- `rdt doctor --session <name>` is a first-class alias for the same command.
- It also reports `enginePreference`, `selectedEngine`, `recommendedEngine`, `availableEngines`, and DevTools capability hints so agents know whether they are on a custom fallback or a DevTools-aligned path.
- `sourceCapability` is reported separately from engine selection.
- `_debugSource` is treated as an optional legacy source-mapping capability, not an engine-selection gate.
Expand All @@ -154,12 +158,15 @@ rdt profiler export --session demo --compress

- Use built-in `interact` commands before reaching for external Playwright helper scripts.
- Current supported actions:
- `rdt interact click --session <name> --selector <css>`
- `rdt interact click --session <name> --selector <css> [--delivery auto|playwright|dom]`
- `rdt interact type --session <name> --selector <css> --text <value>`
- `rdt interact press --session <name> --key <name> [--selector <css>]`
- `rdt interact wait --session <name> --ms <n>`
- These commands execute through the same Playwright session that owns the current `rdt` browser page.
- They target the first matching selector only and return structured action metadata plus trust-boundary fields.
- `interact click` defaults to `--delivery auto`.
- In `auto`, profiler-active clicks fall back to DOM dispatch and report `requestedDelivery`, `effectiveDelivery`, `profilerActive`, and `fallbackApplied`.
- Use `--delivery playwright` to force Playwright pointer input, or `--delivery dom` to force DOM dispatch.
- `click`, `type`, and `press` confirm that the action was dispatched. They do not guarantee that the page or React tree has fully settled afterward.
- When profiling or triggering large rerenders, follow `interact` with an explicit verification step such as `interact wait`, `tree get`, `node inspect`, or a profiler read command.

Expand All @@ -168,10 +175,10 @@ Example deterministic flow:
```bash
rdt tree get --session demo
# => save snapshotId from output
rdt node search App --session demo --snapshot <snapshotId>
rdt node search App --session demo --snapshot <snapshotId> --structured
rdt node inspect <nodeId> --session demo --snapshot <snapshotId>
rdt node highlight <nodeId> --session demo --snapshot <snapshotId>
rdt source reveal <nodeId> --session demo --snapshot <snapshotId>
rdt source reveal <nodeId> --session demo --snapshot <snapshotId> --structured
```

Snapshot recovery:
Expand Down Expand Up @@ -269,7 +276,11 @@ Use `node pick` when the agent knows the visible element but not the component n
- `hooks` is a simplified serialized view of hook state from the inspected fiber.
- `context` is a serialized view of current context dependencies for the inspected node.
- `source` projects `_debugSource` when available; `null` is expected in many dev builds.
- `source reveal --structured` returns `status`, `available`, `mode`, `reason`, and `source` so automation can distinguish unavailable source data from a successful source payload.
- `source reveal` without `--structured` preserves the raw legacy behavior and may return literal `null`.
- `dom` is the first host element summary used for CLI highlight and DOM-oriented inspection.
- `node search --structured` wraps search results in `{ items, query, snapshotId, matchCount, runtimeWarnings }`.
- When `node search --structured` returns `matchCount: 0`, `runtimeWarnings` explains that the component may be absent from the current snapshot rather than absent from the codebase.
- Profiler summary fields are commit-oriented CLI metrics, not the full DevTools profiler session schema.
- `profiler summary` and exported summaries explicitly report:
- `measurementMode: "actual-duration" | "structural-only" | "mixed"`
Expand Down
6 changes: 5 additions & 1 deletion docs/devtools-concept-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ It is a maintenance aid for agents and contributors. It is not a commitment to r
| `__REACT_DEVTOOLS_GLOBAL_HOOK__` shim | `src/runtime-script.js` | DevTools global hook | `rdt` uses the hook for renderer/root discovery, but does not speak the official frontend protocol. |
| `state.roots` + `rootId` | `src/runtime-script.js` | Fiber roots / renderer root registry | `rdt` assigns CLI-friendly `root-*` ids per discovered root. |
| `collectTree()` snapshot | `src/runtime-script.js` | Inspected tree payload | `rdt` serializes a tree for CLI use instead of streaming updates to a frontend. |
| `tree stats` | `src/runtime-script.js` + `src/cli.js` | No direct public equivalent | Snapshot summary view for large trees; returns metadata without the full node dump. |
| `snapshotId` | `src/runtime-script.js` | No direct public equivalent | Intentional divergence. This is a CLI-specific stability layer for follow-up commands. |
| `node id` like `n68` | `src/runtime-script.js` | Element/fiber identity in inspector payloads | Snapshot-scoped only. Not intended to be globally stable across commits. |
| `node inspect` payload | `src/runtime-script.js` | Inspected element details | Closest conceptual match to DevTools inspected element data. |
| `ownerStack` | `src/runtime-script.js` | Owner chain / component stack | Serialized as lightweight `{id, displayName}` records. |
| `hooks` | `src/runtime-script.js` | Hook inspection data | Derived from `memoizedState`; intentionally simpler than full DevTools hook typing. |
| `context` | `src/runtime-script.js` | Context dependencies / inspected context values | Derived from fiber dependencies, serialized for CLI use. |
| `source reveal` | `src/server.js` + `src/runtime-script.js` | View source / inspect source location | Depends on `_debugSource`; may legitimately return `null`. |
| `source reveal` | `src/server.js` + `src/runtime-script.js` | View source / inspect source location | Raw mode may legitimately return `null`; structured mode reports source availability explicitly. |
| `profiler` summary/export | `src/runtime-script.js` + `src/cli.js` | Profiler commit data | `rdt` keeps commit-oriented summaries and NDJSON export rather than full DevTools frontend state. |
| `profiler compare` | `src/cli.js` | No direct public equivalent | CLI-side comparison of stored profiler artifacts or exported NDJSON files. |
| `interact click|type|press|wait` | `src/server.js` | No direct public equivalent | Playwright-backed deterministic interaction helpers for agent workflows. |
| `interact click --delivery` | `src/server.js` | No direct public equivalent | CLI-specific interaction contract for choosing Playwright pointer input vs DOM dispatch. |
| `session doctor` | `src/server.js` + `src/runtime-script.js` | No direct public equivalent | CLI-specific preflight that reports trust boundaries, runtime readiness, Playwright resolution diagnostics, and helper import targets. |

## Intentional Divergences
Expand Down Expand Up @@ -72,6 +74,8 @@ It is a maintenance aid for agents and contributors. It is not a commitment to r
- `hooks`: serialized hook state view, intentionally simplified.
- `context`: serialized current context dependency values.
- `source`: `_debugSource` projection when available; `null` is expected in many builds.
- `source reveal --structured`: wraps source lookup in `{ status, available, mode, reason, source }`.
- `mode`: source-capability mode, not engine mode. Current values describe whether `_debugSource` is available, removed by newer React runtimes, or stripped by the build.
- `dom`: first host element descendant summary used for CLI-oriented highlight/reveal behavior.

### Profiler payload
Expand Down
18 changes: 14 additions & 4 deletions docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ npm install -g react-devtool-cli
```bash
rdt session open --url http://localhost:3000 --browser chromium --engine auto --session demo
rdt tree get --session demo
rdt node search App --session demo --snapshot <snapshotId>
rdt tree stats --session demo --top 5
rdt node search App --session demo --snapshot <snapshotId> --structured
rdt node inspect <nodeId> --session demo --snapshot <snapshotId>
```

Expand All @@ -40,11 +41,16 @@ Recommended flow:

```bash
rdt tree get --session demo
rdt node search App --session demo --snapshot <snapshotId>
rdt tree stats --session demo --top 5
rdt node search App --session demo --snapshot <snapshotId> --structured
rdt node highlight <nodeId> --session demo --snapshot <snapshotId>
rdt source reveal <nodeId> --session demo --snapshot <snapshotId>
rdt source reveal <nodeId> --session demo --snapshot <snapshotId> --structured
```

- `tree stats` is the lightweight summary path when `tree get --format json` is too heavy.
- `node search --structured` keeps the default array-returning behavior opt-in while adding `matchCount` and `runtimeWarnings`.
- `source reveal --structured` returns availability metadata instead of only raw `null`.

Recovery flow:

```bash
Expand All @@ -56,6 +62,7 @@ rdt node search App --session demo --snapshot <newSnapshotId>
## Run `doctor` before deeper investigation

```bash
rdt doctor --session demo
rdt session doctor --session demo
```

Expand All @@ -71,11 +78,14 @@ Use it to confirm:
Built-in interactions keep the investigation inside the same session instead of forcing separate helper scripts.

```bash
rdt interact click --session demo --selector 'button.save'
rdt interact click --session demo --selector 'button.save' --delivery auto
rdt interact type --session demo --selector 'input[name="query"]' --text hello
rdt interact wait --session demo --ms 500
```

- `interact click --delivery auto` uses Playwright pointer input by default.
- When the profiler is active, `auto` may fall back to DOM dispatch and reports the applied delivery in the response payload.

After interaction, verify the app settled by collecting a fresh tree or reading profiler output instead of assuming the UI state changed correctly.

## Profile a real update
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-devtool-cli",
"version": "0.1.35-rc.1",
"version": "0.1.35-rc.2",
"description": "Agent-first CLI for React component tree inspection, snapshot-aware node debugging, and profiler analysis through a Playwright-managed browser session.",
"license": "MIT",
"type": "module",
Expand Down
46 changes: 39 additions & 7 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ function collectSnapshotPayload(options) {
return options.snapshot ? { snapshotId: String(options.snapshot) } : {};
}

function normalizeStructuredFlag(options) {
return Boolean(options.structured);
}

function resolveCommitId(positionals, options, message) {
const commitId = positionals[0] ? String(positionals[0]) : (options.commit ? String(options.commit) : null);
ensure(commitId, message, { code: "missing-commit-id" });
Expand Down Expand Up @@ -278,9 +282,14 @@ async function handleSessionCommand(command, options) {
}

async function handleTreeCommand(command, options) {
ensure(command === "get", "Only `rdt tree get` is supported.", { code: "unsupported-command" });
ensure(command === "get" || command === "stats", "Only `rdt tree get` and `rdt tree stats` are supported.", {
code: "unsupported-command",
});
ensure(options.session, "Missing required option --session", { code: "missing-session" });
const response = await requestSession(options.session, "tree.get");
const action = command === "stats" ? "tree.stats" : "tree.get";
const response = await requestSession(options.session, action, {
top: options.top ? Number(options.top) : undefined,

Choose a reason for hiding this comment

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

P2 Badge Respect explicit --top 0 in tree stats requests

The payload builder drops --top 0 because the truthy check treats 0 as absent, so rdt tree stats --top 0 sends top: undefined and the runtime falls back to the default (10) instead of honoring the caller’s explicit limit handling. This produces unexpectedly larger summaries and diverges from runtime-side clamping behavior.

Useful? React with 👍 / 👎.

});
writeStdout(response.result, resolveFormat(options));
}

Expand All @@ -304,6 +313,7 @@ async function handleNodeCommand(command, positionals, options) {
ensure(query, "Missing query for `rdt node search`.", { code: "missing-query" });
const response = await requestSession(options.session, "node.search", {
query,
structured: normalizeStructuredFlag(options),
...collectSnapshotPayload(options),
});
writeStdout(response.result, resolveFormat(options));
Expand Down Expand Up @@ -339,6 +349,7 @@ async function handleInteractCommand(command, options) {
ensure(options.selector, "Missing required option --selector for `rdt interact click`.", { code: "missing-selector" });
const response = await requestSession(options.session, "interact.click", {
selector: String(options.selector),
delivery: options.delivery ? String(options.delivery) : undefined,
timeoutMs: options.timeoutMs ?? undefined,
});
writeStdout(response.result, resolveFormat(options));
Expand Down Expand Up @@ -732,11 +743,30 @@ async function handleSourceCommand(command, positionals, options) {
ensure(nodeId, "Missing node id for `rdt source reveal`.", { code: "missing-node-id" });
const response = await requestSession(options.session, "source.reveal", {
nodeId,
structured: normalizeStructuredFlag(options),
...collectSnapshotPayload(options),
});
writeStdout(response.result, resolveFormat(options));
}

export function normalizeCliPositionals(positionals) {
const [resource, command, ...rest] = positionals;

if (resource === "doctor") {
return {
resource: "session",
command: "doctor",
rest: command ? [command, ...rest] : [],
};
}

return {
resource,
command,
rest,
};
}

function printHelp() {
process.stdout.write(`react-devtool-cli

Expand All @@ -746,21 +776,23 @@ Recommended flow:
3. Use rdt session attach only for Chromium CDP compatibility
4. Use rdt tree/node/profiler commands for structured output
5. For agent workflows, capture snapshotId from tree get and pass it to later node/source commands
6. Use rdt doctor before profiling if helper scripts or Playwright resolution look suspicious
6. Use rdt doctor (alias: rdt session doctor) before profiling if helper scripts or Playwright resolution look suspicious

Usage:
rdt doctor --session <name> [--format json|yaml|pretty]
rdt session open --url <url> [--browser chromium|firefox|webkit] [--engine auto|custom|devtools] [--channel <name>] [--device <name>] [--storage-state <path>] [--user-data-dir <path>] [--timeout <ms>] [--headless=false] [--session <name>]
rdt session connect --ws-endpoint <url> [--browser chromium|firefox|webkit] [--engine auto|custom|devtools] [--target-url <substring>] [--timeout <ms>] [--session <name>]
rdt session attach --cdp-url <url> [--engine auto|custom|devtools] [--target-url <substring>] [--timeout <ms>] [--session <name>]
rdt session status --session <name>
rdt session doctor --session <name> [--format json|yaml|pretty]
rdt session close --session <name>
rdt tree get --session <name> [--format json|yaml|pretty]
rdt tree stats --session <name> [--top <n>] [--format json|yaml|pretty]
rdt node inspect <id> --session <name> [--snapshot <id>] [--commit <id>]
rdt node search <query> --session <name> [--snapshot <id>]
rdt node search <query> --session <name> [--snapshot <id>] [--structured]
rdt node highlight <id> --session <name> [--snapshot <id>]
rdt node pick --session <name> [--timeout-ms 30000]
rdt interact click --session <name> --selector <css> [--timeout-ms <ms>]
rdt interact click --session <name> --selector <css> [--delivery auto|playwright|dom] [--timeout-ms <ms>]
rdt interact type --session <name> --selector <css> --text <value> [--timeout-ms <ms>]
rdt interact press --session <name> --key <name> [--selector <css>] [--timeout-ms <ms>]
rdt interact wait --session <name> --ms <n>
Expand All @@ -773,7 +805,7 @@ Usage:
rdt profiler flamegraph <id> --session <name> [--format json|pretty]
rdt profiler compare --session <name> --left <profileId|file> --right <profileId|file> [--format json|yaml|pretty]
rdt profiler export --session <name> [--output file.jsonl] [--compress]
rdt source reveal <id> --session <name> [--snapshot <id>]
rdt source reveal <id> --session <name> [--snapshot <id>] [--structured]

Snapshot behavior:
- tree get returns snapshotId
Expand All @@ -795,7 +827,7 @@ export async function runCli(argv) {
return;
}

const [resource, command, ...rest] = positionals;
const { resource, command, rest } = normalizeCliPositionals(positionals);

try {
switch (resource) {
Expand Down
Loading
Loading