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
61 changes: 61 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Pages

on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true
Comment on lines +18 to +19

Choose a reason for hiding this comment

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

P1 Badge Split concurrency group by ref to avoid canceling deploys

Using a single concurrency.group: pages with cancel-in-progress: true means every PR build and every main push deployment share the same lock, so a new PR run can cancel an in-flight production deploy from push to main. Because this workflow is triggered by both pull_request and push, the current setting can intermittently prevent docs updates from reaching GitHub Pages when activity overlaps.

Useful? React with 👍 / 👎.


jobs:
build:
name: Build VitePress site
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
Comment on lines +26 to +27

Choose a reason for hiding this comment

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

P2 Badge Fetch full history when enabling VitePress lastUpdated

The workflow uses default checkout depth, but docs config enables lastUpdated, which depends on git history to compute per-page timestamps. With shallow checkout, VitePress can only see truncated history and will produce inaccurate "last updated" metadata for pages not changed in the latest commit; add fetch-depth: 0 to the checkout step so timestamps reflect actual file history.

Useful? React with 👍 / 👎.


- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm

- name: Setup Pages
uses: actions/configure-pages@v5

- name: Install dependencies
run: npm ci

- name: Build site
run: npm run docs:build

- name: Upload Pages artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist

deploy:
name: Deploy to GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Thumbs.db

coverage/
dist/
docs/.vitepress/cache/
docs/.vitepress/dist/
*.tgz

*.jsonl
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Agent-first CLI for inspecting React applications through a Playwright-managed b

It gives agents and engineers a structured command surface for React tree snapshots, node inspection, source reveal, deterministic browser interaction, and commit-oriented profiler analysis without opening the DevTools UI. The public CLI is engine-based: `auto` chooses between the current custom engine and a DevTools-aligned engine when capability checks allow it.

Project site: https://ring-wdr.github.io/react-devtool-cli/

## Recommended architecture

- `rdt` talks to Playwright directly through the Node API
Expand Down
42 changes: 42 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { defineConfig } from "vitepress";

const repoUrl = "https://github.com/Ring-wdr/react-devtool-cli";

export default defineConfig({
title: "react-devtool-cli",
description: "Playwright-native CLI for inspecting live React apps without opening the DevTools UI.",
base: "/react-devtool-cli/",
cleanUrls: true,
lastUpdated: true,
srcExclude: ["devtools-concept-mapping.md", "public-repo-strategy.md"],
themeConfig: {
nav: [
{ text: "Home", link: "/" },
{ text: "Workflows", link: "/workflows" },
{ text: "Architecture", link: "/architecture" },
{ text: "GitHub", link: repoUrl }
],
sidebar: [
{
text: "Guide",
items: [
{ text: "Why RDT", link: "/" },
{ text: "Workflows", link: "/workflows" },
{ text: "Architecture", link: "/architecture" }
]
}
],
socialLinks: [{ icon: "github", link: repoUrl }],
editLink: {
pattern: `${repoUrl}/edit/main/docs/:path`,
text: "Edit this page on GitHub"
},
search: {
provider: "local"
},
footer: {
message: "Playwright-native React inspection for agents and engineers.",
copyright: "MIT Licensed"
}
}
});
21 changes: 21 additions & 0 deletions docs/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
:root {
--vp-c-brand-1: #0f766e;
--vp-c-brand-2: #0b5f59;
--vp-c-brand-3: #134e4a;
--vp-c-brand-soft: rgba(15, 118, 110, 0.14);
--vp-c-sponsor: #c2410c;
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(120deg, #0f766e 10%, #c2410c 90%);
--vp-home-hero-image-background-image: radial-gradient(circle at top, rgba(15, 118, 110, 0.28), rgba(194, 65, 12, 0.2));
--vp-home-hero-image-filter: blur(64px);
}

.VPFeature {
border: 1px solid rgba(15, 23, 42, 0.08);
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.06);
}

.vp-doc table {
display: table;
width: 100%;
}
4 changes: 4 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import DefaultTheme from "vitepress/theme";
import "./custom.css";

export default DefaultTheme;
74 changes: 74 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Architecture

`react-devtool-cli` is intentionally not a browser extension wrapper. It is a CLI runtime that owns browser automation, React tree capture, and profiler export as one command surface.

## Runtime shape

| Layer | Responsibility |
| --- | --- |
| CLI | Parse commands, format output, and route calls to the session daemon |
| Session daemon | Own browser lifecycle, transport selection, and RPC between commands and the active page |
| Runtime script | Discover roots through the React global hook, collect snapshots, inspect nodes, and capture profiler data |

This split keeps user-facing commands stable while transport and runtime details evolve underneath.

## Session transports

| Transport | Meaning | Recommended use |
| --- | --- | --- |
| `open` | `rdt` launches the browser directly through Playwright | Default for local work |
| `connect` | `rdt` connects to an existing Playwright endpoint | Best remote path |
| `attach` | `rdt` attaches through Chromium CDP | Compatibility fallback when Playwright protocol is unavailable |

## Engine selection

`--engine auto` is the default because it chooses between the current custom engine and a DevTools-aligned path when capability checks allow it.

- `custom` forces the snapshot-diff engine
- `devtools` requests the DevTools-aligned path and falls back when capability checks fail
- `doctor` exposes `selectedEngine`, `recommendedEngine`, and capability hints so callers know what they are actually trusting

## Snapshot semantics

Snapshot-scoped node identity is a deliberate design choice, not a temporary workaround.

- `tree get` creates a snapshot and returns `snapshotId`
- follow-up node ids are only stable inside that snapshot
- commands fail loudly when a requested snapshot has expired from the cache

This makes the CLI more deterministic for agent workflows than a model that pretends node ids stay globally stable across live React updates.

## Inspect payload model

`node inspect` is closest to an inspected-element payload, but the CLI keeps it intentionally simpler than full DevTools frontend data.

- `ownerStack` is a lightweight owner chain
- `hooks` is a simplified serialized view of hook state
- `context` is a serialized view of current context dependencies
- `source` may be `null` when `_debugSource` is not present in the runtime
- `dom` is the first host descendant summary used for highlight and DOM-oriented follow-up

## Profiler model

Profiler support is commit-oriented.

- summaries focus on commit count, timestamps, roots, and live-tree size
- exported profiles use NDJSON or `jsonl.gz`
- ranked and flamegraph views provide CLI-oriented hotspot summaries
- duration support depends on what the current React runtime exposes

Important limitation: current profiler output does not prove exact rerender sets the way a full DevTools frontend with component-duration instrumentation might.

## Trust boundaries

Most structured responses expose the same three guardrails:

- `observationLevel`: what was directly observed
- `limitations`: what the payload does not prove
- `runtimeWarnings`: environmental or runtime conditions that can mislead follow-up analysis

The project is designed so agents can read those fields literally instead of inferring hidden guarantees.

## Documentation boundary

Public docs should explain the product, workflow, and architecture clearly. Maintainer handoff notes, raw validation logs, and internal investigation artifacts still belong in the repository, but they are not the public narrative for the GitHub Pages site.
67 changes: 67 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
layout: home

hero:
name: react-devtool-cli
text: Inspect live React apps without opening DevTools UI
tagline: A Playwright-native CLI for tree snapshots, node inspection, deterministic interaction, and commit-oriented profiler analysis.
actions:
- theme: brand
text: Start with Workflows
link: /workflows
- theme: alt
text: Read the Architecture
link: /architecture

features:
- title: Snapshot-aware tree inspection
details: Collect a tree once, keep the returned snapshot context, and inspect nodes deterministically instead of chasing unstable live ids.
- title: Playwright-managed sessions
details: Open local browser sessions directly, connect to Playwright endpoints remotely, or attach to Chromium over CDP when compatibility matters more than fidelity.
- title: Commit-oriented profiler analysis
details: Move from suspicion to evidence with doctor checks, built-in interaction helpers, profiler capture, and exportable NDJSON traces.
---

## Why this project exists

`react-devtool-cli` gives agents and engineers a structured command surface for investigating React applications from the terminal. It is built for workflows where opening the DevTools UI is either too manual, too hard to automate, or too indirect for repeatable debugging.

Unlike browser extensions or frontend-only DevTools flows, `rdt` owns the browser session through Playwright. That makes it useful for automated investigations, remote environments, and agent-driven debugging loops where commands need deterministic output and explicit trust boundaries.

## What you can do with it

- Open a browser session against a live app with `session open`, `session connect`, or `session attach`
- Capture snapshot-scoped trees with `tree get`
- Search, inspect, highlight, and source-map React nodes with explicit snapshot context
- Run built-in interactions before profiling, instead of stitching together ad hoc browser scripts
- Record and export commit-oriented profiler data for later comparison

## Recommended first run

```bash
npm install -g react-devtool-cli
rdt session open --url http://localhost:3000 --session demo
rdt tree get --session demo
rdt node search App --session demo --snapshot <snapshotId>
```

That sequence captures the core model of the tool:

1. Open a browser session that `rdt` manages.
2. Collect a tree and persist the returned `snapshotId`.
3. Reuse that snapshot for follow-up inspection so node lookups stay deterministic.

## What makes it different

| Area | `react-devtool-cli` bias |
| --- | --- |
| Runtime host | Playwright-managed browser session |
| Primary interface | Structured CLI output for agents and engineers |
| Tree stability model | Snapshot-scoped node ids |
| Profiler output | Commit-oriented summaries and NDJSON export |
| Trust boundary | Explicit `observationLevel`, `limitations`, and `runtimeWarnings` fields |

## Where to go next

- Use [Workflows](/workflows) to get productive quickly.
- Use [Architecture](/architecture) to understand engine choice, snapshot semantics, and profiler interpretation limits.
104 changes: 104 additions & 0 deletions docs/workflows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Workflows

This page is the fastest path from zero context to a useful `rdt` session.

## Install

```bash
npm install -g react-devtool-cli
```

`rdt` resolves Playwright from the local project first, then falls back through `playwright-core`, `RDT_PLAYWRIGHT_PATH`, and global installs.

## Choose the right session mode

| Command | Use when | Notes |
| --- | --- | --- |
| `rdt session open` | You want `rdt` to launch and own the browser locally | Recommended default path |
| `rdt session connect` | You already have a Playwright `wsEndpoint` | Remote-friendly, keeps Playwright fidelity |
| `rdt session attach` | You only have Chromium CDP access | Compatibility path, lower fidelity than Playwright protocol |

## Quick start

```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 node inspect <nodeId> --session demo --snapshot <snapshotId>
```

If you only remember one rule, remember this one: `tree get` starts the inspection cycle, and the returned `snapshotId` should follow later lookup commands.

## Snapshot-aware inspection

- `tree get` returns a `snapshotId`.
- Node ids are only guaranteed to be meaningful inside that snapshot.
- If you omit `--snapshot`, `rdt` falls back to the latest collected snapshot.
- If a requested snapshot has been evicted from the in-memory cache, the command fails with `snapshot-expired`.

Recommended flow:

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

Recovery flow:

```bash
rdt tree get --session demo
# save the new snapshotId
rdt node search App --session demo --snapshot <newSnapshotId>
```

## Run `doctor` before deeper investigation

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

Use it to confirm:

- React was detected
- which engine was selected
- whether built-in `interact` helpers are supported
- whether profiler and source-reveal capabilities are trustworthy for the current runtime

## Interact before profiling

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 type --session demo --selector 'input[name="query"]' --text hello
rdt interact wait --session demo --ms 500
```

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

```bash
rdt profiler start --session demo
rdt interact type --session demo --selector 'input[name="query"]' --text hello
rdt profiler stop --session demo
rdt profiler summary --session demo
rdt profiler ranked <commitId> --session demo --limit 10
rdt profiler export --session demo --compress
```

Read profiler output literally:

- `commitCount` tells you how many commits were captured
- `nodeCount` fields describe live tree size at commit time, not exact rerender counts
- `primaryUpdateCommitId` and `recommendedCommitIds` are better drill-down targets than blindly reading the first commit

## Close the loop cleanly

```bash
rdt session close --session demo
```

Treat sessions as explicit resources. Close them when the investigation is over so later runs start from a known state.
Loading
Loading