@agent-scope/cli
v1.25.3
Published
CLI for Scope — capture, replay, and generate tests from the command line
Readme
@agent-scope/cli
CLI for Scope — capture, replay, and analyze React components from the command line.
Table of Contents
Installation
npm install -g @agent-scope/cli
# or use it locally in a project
npm install --save-dev @agent-scope/cli
npx scope --helpRequirements: Node.js 18+, Playwright browsers (installed automatically on first run).
Quick Start
# 1. First setup in your React project
# Auto-detects Tailwind tokens + CSS entry file, scaffolds config, then runs the first sync
scope init --yes
# 2. Repeatable refresh after code, token, or .scope.tsx scenario changes
scope sync
# 3. Validate your setup and inspect refreshed outputs
scope doctor --json
scope manifest list --format json
scope site serve --dir .reactscope/siteThe everyday model is two commands: scope init for first setup and scope sync for repeatable refresh. Advanced commands remain available as debug/reference entry points when you need a narrower operation: scope manifest * for structural questions, scope render * for focused screenshots, scope tokens * for token inspection/compliance, scope site * for gallery operations, scope doctor for diagnostics, and scope instrument * for live browser instrumentation.
Commands Reference
scope init
Scaffold a Scope project for the first time — creates reactscope.config.json, reactscope.tokens.json, and the .reactscope/ output directory. By default, init finishes by running the first scope sync, so scope init --yes is the non-interactive first-setup command for agents and bootstrap scripts.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -y, --yes | boolean | false | Accept all detected defaults without prompting |
| --no-sync | boolean | false | Skip the initial sync after scaffolding |
| --force | boolean | false | Overwrite existing reactscope.config.json |
Auto-detection: Scope detects your framework (Next.js, Vite, Create React App, etc.), TypeScript support, likely component file patterns, Tailwind design tokens, and your CSS entry file (src/index.css, src/styles.css, etc.) before prompting. If no token sources are detected, Scope creates an empty token stub and warns that token compliance is not meaningful until real tokens are added.
After setup, run scope sync whenever source, token, or .scope.tsx scenario files change. Sync refreshes the manifest, changed/stale renders, token compliance, static site, .reactscope/run-summary.json, and .reactscope/sync-state.json in one repeatable pipeline. Warnings are non-fatal by default; pass scope sync --strict-warnings to treat warnings as failures.
Example:
scope init
scope init --yes # non-interactive, accept defaults, then run the first sync
scope init --no-sync # scaffold only; run `scope sync` later
scope init --force # overwrite existing config
scope sync # repeatable refresh
scope sync --strict-warningsInteractive output:
🚀 scope init — project configuration
Press Enter to accept the detected value shown in brackets.
Detected framework: Next.js
Component include patterns (comma-separated) [src/**/*.tsx]:
Component exclude patterns (comma-separated) [**/*.test.tsx,**/*.stories.tsx]:
Token file location [reactscope.tokens.json]:
Output directory [.reactscope/]:
✅ Scope project initialised!
Created files:
/home/user/project/reactscope.config.json
/home/user/project/reactscope.tokens.json
/home/user/project/.reactscope/
Running initial sync...
Generated manifest with 12 component(s).
Refreshed 12 stale render component(s).
Built site at .reactscope/site.
Next steps: run `scope sync` after code, token, or scenario changes.Files written:
| Path | Description |
|------|-------------|
| reactscope.config.json | Project configuration |
| reactscope.tokens.json | Design tokens stub |
| .reactscope/ | Output directory |
| .gitignore | .reactscope/ entry added |
scope doctor
Validate your Scope project configuration. Checks config validity, token file, globalCSS paths, manifest freshness, target-project dependencies, and whether Playwright browser rendering is ready.
scope doctor --json
scope doctor --print-fix-commandsWarnings are non-fatal; errors exit non-zero. The Playwright check reports browser rendering as ready/not-ready in human-facing output, with remediation details and fix commands in JSON output when rendering is not ready.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --json | boolean | false | Emit structured JSON (for CI integration) |
| --print-fix-commands | boolean | false | Print deduplicated remediation commands for failing checks |
Example output:
Scope Doctor
────────────────────────────────────────
[✓] config reactscope.config.json valid
[✓] tokens Token file valid
[!] globalCSS No globalCSS configured — Tailwind styles won't apply to renders
[✓] manifest Manifest present and up to date
[✓] dependencies node_modules present
[✓] playwright Playwright package available
────────────────────────────────────────
1 warning(s) — everything works but could be betterExits 0 when clean (warnings are non-fatal), 1 on any error.
If dependencies fails, run your package manager install command in the target project root (for Bun projects: bun install), then rerun scope doctor --json.
If playwright fails, copy the package-manager-aware command from scope doctor --print-fix-commands or the JSON fixCommands array, then rerun scope doctor --json before retrying scope sync, scope render component, scope render all, scope site build, or scope instrument .... Scope detects Bun, pnpm, Yarn, or npm from lockfiles/package metadata and reports browser rendering as ready/not-ready without requiring agents to inspect cache paths or exact browser revisions.
Use scope doctor --print-fix-commands to emit copy/paste-ready remediation commands for the current project.
scope capture
Capture a React component tree from a live URL and output raw JSON.
scope capture <url>| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -o, --output <path> | string | stdout | Write JSON to file instead of stdout |
| --pretty | boolean | false | Pretty-print JSON output |
| --timeout <ms> | number | 10000 | Max wait time for React to mount |
| --wait <ms> | number | 0 | Additional wait after page load before capture |
Example:
scope capture http://localhost:5173
scope capture http://localhost:5173 --pretty -o capture.json
scope capture http://localhost:5173 --timeout 30000Output: PageReport JSON containing the full component tree, hooks, console entries, errors, and suspense boundaries.
scope tree
Display the React component tree from a live URL as an ASCII tree in the terminal.
scope tree <url>| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --depth <n> | number | unlimited | Max depth to display |
| --show-props | boolean | false | Include prop names next to components |
| --show-hooks | boolean | false | Show hook counts per component |
| --timeout <ms> | number | 10000 | Max wait time for React to mount |
| --wait <ms> | number | 0 | Additional wait after page load |
Example:
scope tree http://localhost:5173
scope tree http://localhost:5173 --depth 3 --show-propsOutput:
Button
├── div
│ ├── span
│ └── svg
└── FocusRingscope report
Capture and display a human-readable summary of a React app.
scope report <url>| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --json | boolean | false | Output as structured JSON instead of human-readable text |
| --timeout <ms> | number | 10000 | Max wait time for React to mount |
| --wait <ms> | number | 0 | Additional wait after page load |
Example:
scope report http://localhost:5173
scope report http://localhost:5173 --jsonTTY output:
Scope Report for http://localhost:5173
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Components: 24 total (18 function, 4 memo, 2 forwardRef)
Max depth: 6
Hooks: 42 total (12 useState, 8 useEffect, ...)
Error boundaries: 2
Suspense boundaries: 1 (1 resolved)
Console entries: 3 (1 warn, 2 error)
Capture time: 45msscope manifest
Manage the component manifest — a catalogue of all React components in your project with their metadata.
The manifest is stored at .reactscope/manifest.json by default.
scope manifest generate
Scan source files and generate the component manifest.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --root <path> | string | cwd | Project root directory |
| --output <path> | string | .reactscope/manifest.json | Output path |
| --include <globs> | string | from config | Comma-separated glob patterns to include |
| --exclude <globs> | string | from config | Comma-separated glob patterns to exclude |
scope manifest generate
scope manifest generate --root /path/to/project --output manifest.json
scope manifest generate --include "src/**/*.tsx" --exclude "**/*.stories.tsx"Files written: .reactscope/manifest.json
scope manifest list
List all components in the manifest.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --format <fmt> | json\|table | auto (TTY→table, pipe→json) | Output format |
| --filter <glob> | string | — | Filter by component name glob |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
scope manifest list
scope manifest list --filter "Button*"
scope manifest list --format json | jq '.[] | select(.complexityClass == "complex")'Table output (TTY):
NAME FILE COMPLEXITY HOOKS CONTEXTS
Button src/Button.tsx simple 2 0
SearchPage src/SearchPage.tsx complex 5 2JSON output (pipe):
[
{
"name": "Button",
"file": "src/Button.tsx",
"complexityClass": "simple",
"hookCount": 2,
"contextCount": 0
}
]scope manifest get <name>
Get full details of a single component by name.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --format <fmt> | json\|table | auto | Output format |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
scope manifest get Button
scope manifest get Button --format jsonTTY output:
Component: Button
File: src/Button.tsx
Export: named
Display Name: Button
Complexity: simple
Memoized: true
Forwarded Ref: false
HOC Wrappers: none
Hooks: useState, useCallback
Contexts: none
Composes: Icon
Composed By: ButtonGroup
Side Effects: none
Props (3):
variant: "primary" | "secondary" — optional [default: "primary"]
size: "sm" | "md" | "lg" — optional [default: "md"]
disabled: boolean — optional [default: false]scope manifest query
Query components by attributes.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --context <name> | string | — | Find components consuming a context |
| --hook <name> | string | — | Find components using a specific hook |
| --complexity <class> | simple\|complex | — | Filter by complexity class |
| --side-effects | boolean | false | Find components with any side effects |
| --has-fetch | boolean | false | Find components with fetch calls |
| --format <fmt> | json\|table | auto | Output format |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
scope manifest query --hook useState --complexity complex
scope manifest query --context AuthContext
scope manifest query --has-fetch --format jsonscope render
Render components to PNG screenshots or JSON using esbuild bundling and Playwright.
scope render <component> (or scope render component <component>)
Render a single component.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --props <json> | string | {} | Inline props JSON |
| --viewport <WxH> | string | 375x812 | Viewport size |
| --theme <name> | string | — | Theme name from the token system |
| -o, --output <path> | string | .reactscope/renders/<component>.png | Write PNG to file |
| --format <fmt> | png\|json | auto (TTY→png, pipe→json) | Output format |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
scope render component Button
scope render component Button --props '{"variant":"primary","size":"lg"}'
scope render component Button --viewport 1280x800 --format json
scope render component Button -o screenshots/button.pngJSON output:
{
"component": "Button",
"props": { "variant": "primary" },
"width": 120,
"height": 48,
"renderTimeMs": 145,
"screenshot": "iVBORw0KGgoAAAANSUhEUgAA...",
"computedStyles": {}
}Files written (TTY mode):
.reactscope/renders/<component>.png.reactscope/renders/<component>.json
scope render matrix <component>
Render a component across a matrix of prop combinations (Cartesian product).
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --axes <spec> | string | — | Axis definitions, e.g. 'variant:primary,secondary size:sm,md,lg' |
| --contexts <ids> | string | — | Composition context IDs, comma-separated |
| --stress <ids> | string | — | Stress preset IDs, comma-separated |
| --sprite <path> | string | .reactscope/renders/<component>-matrix.png | Write sprite sheet |
| --format <fmt> | json\|png\|html\|csv | auto | Output format |
| --concurrency <n> | number | 8 | Max parallel renders |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
scope render matrix Button --axes 'variant:primary,secondary size:sm,md,lg'
scope render matrix Button --axes 'variant:primary,secondary' --format html > matrix.html
scope render matrix Button --axes 'variant:primary,secondary size:sm,md' --format csvCSV output:
component,variant,size,renderTimeMs,width,height
Button,primary,sm,145,100,36
Button,primary,md,148,120,48
Button,secondary,sm,142,100,36scope render all
Render all components in the manifest.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --concurrency <n> | number | 4 | Max parallel renders |
| --output-dir <dir> | string | .reactscope/renders | Output directory |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
| --format <fmt> | json\|png | png | Output format |
scope render all
scope render all --concurrency 8 --output-dir ./screenshotsIf a component's .scope.tsx file defines 2 or more scenarios, render all automatically runs a matrix render and merges the cells into the component's JSON output. No separate scope render matrix step needed.
After all renders complete, .reactscope/compliance-styles.json is written with aggregated computed styles across every component.
Progress output (stderr):
Rendering 128 components (concurrency: 4)…
Rendering 128/128 Button [===================> ] 95%Files written per component:
| Path | Description |
|------|-------------|
| .reactscope/renders/<component>.png | Screenshot |
| .reactscope/renders/<component>.json | Structured render data |
| .reactscope/renders/<component>.error.json | Error details (if failed) |
scope tokens
Manage and validate design tokens from reactscope.tokens.json.
Token file resolution order:
--fileflagtokens.fileinreactscope.config.jsonreactscope.tokens.json(default)
scope tokens get <path>
Resolve a token path to its computed value.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|text | auto | Output format |
scope tokens get color.semantic.success
# Output: #22C55E
scope tokens get color.semantic.success --format jsonJSON output:
{
"path": "color.semantic.success",
"value": "{color.green.500}",
"resolvedValue": "#22C55E",
"type": "color"
}scope tokens list [category]
List tokens, optionally filtered by category or type.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --type <type> | string | — | Filter by token type (color, dimension, fontFamily, etc.) |
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|table | auto | Output format |
scope tokens list
scope tokens list color
scope tokens list --type color --format jsonTable output:
PATH VALUE RESOLVED TYPE
color.primary {color.blue.500} #3B82F6 color
color.semantic.success {color.green.500} #22C55E color
spacing.xs 0.25rem 0.25rem dimensionscope tokens search <value>
Find which tokens match a computed value. Supports fuzzy color matching.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --type <type> | string | — | Restrict search to a token type |
| --fuzzy | boolean | false | Return nearest match even if no exact match |
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|table | auto | Output format |
scope tokens search "#22C55E"
scope tokens search "#22C55E" --fuzzy
scope tokens search "0.25rem" --type dimensionscope tokens resolve <path>
Show the full resolution chain for a token (useful for debugging alias chains).
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|text | auto | Output format |
scope tokens resolve color.semantic.successText output:
color.semantic.success → {color.green.500}
{color.green.500} → #22C55Escope tokens validate
Validate the token file for errors (circular references, missing references, type mismatches).
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|text | auto | Output format |
scope tokens validate
scope tokens validate --file custom-tokens.jsonSuccess output:
✓ Token file is valid: reactscope.tokens.jsonError output:
✗ Token file has 2 error(s): reactscope.tokens.json
CIRCULAR_REFERENCE [color.a]: Token references itself via chain
MISSING_REFERENCE [color.b]: Referenced token "color.missing" not foundscope tokens compliance
Check token compliance scores — how many computed CSS values in rendered components map back to design tokens.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|table | auto | Output format |
| --threshold <n> | number | 0.90 | Fail if compliance is below this value (0–1) |
scope tokens impact
Analyze the impact of token changes — which components will be affected if a token value changes.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --token <path> | string | — | Token path to analyze impact for |
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|table | auto | Output format |
scope tokens preview
Preview the visual effect of token changes before committing them.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --set <path=value> | string | — | Token override, e.g. color.primary=#FF0000 |
| --file <path> | string | — | Override token file path |
| --format <fmt> | json\|html | auto | Output format |
scope tokens export
Export tokens in various formats for consumption by other tools.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --format <fmt> | css\|tailwind\|json\|scss | css | Export format |
| --file <path> | string | — | Override token file path |
| -o, --output <path> | string | stdout | Write to file |
scope tokens export --format css -o tokens.css
scope tokens export --format tailwind -o tailwind-tokens.jsscope instrument
Structured runtime instrumentation for analyzing React component behavior during interactions.
scope instrument renders <component>
Trace re-render causality chains during an interaction sequence.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --interaction <json> | string | [] | Interaction sequence JSON |
| --json | boolean | false | Force JSON output |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
Interaction steps are JSON arrays with action, target, and optional fields:
scope instrument renders SearchPage \
--interaction '[{"action":"click","target":"button"},{"action":"type","target":"input","text":"hello"}]'Supported actions: click, type, wait, hover, blur, focus, scroll
JSON output:
{
"component": "SearchPage",
"interaction": [{"action": "click", "target": "button"}],
"summary": {
"totalRenders": 12,
"uniqueComponents": 5,
"wastedRenders": 2,
"interactionDurationMs": 450
},
"renders": [
{
"component": "SearchPage",
"renderIndex": 0,
"trigger": "state_change",
"propsChanged": false,
"stateChanged": true,
"contextChanged": false,
"memoized": false,
"wasted": false,
"chain": [{"component": "SearchPage", "trigger": "state_change"}],
"cascade": {
"totalRendersTriggered": 8,
"uniqueComponents": 3,
"unchangedPropRenders": 2
}
}
],
"flags": [
{
"id": "RENDER_CASCADE",
"severity": "warning",
"component": "SearchPage",
"detail": "State change in SearchPage triggered 8 downstream re-renders"
}
]
}Heuristic flags: WASTED_RENDER, RENDER_CASCADE
scope instrument hooks <component>
Profile per-hook-instance data for a component.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --props <json> | string | {} | Inline props JSON |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
| --format <fmt> | json\|text | json | Output format |
| --show-flags | boolean | false | Show heuristic flags only |
scope instrument hooks Button
scope instrument hooks Button --props '{"variant":"primary"}' --show-flagsJSON output:
{
"component": "Button",
"components": [
{
"name": "Button",
"source": {"file": "src/Button.tsx", "line": 42},
"hooks": [
{
"index": 0,
"type": "useState",
"currentValue": true,
"updateCount": 0
},
{
"index": 1,
"type": "useCallback",
"dependencyValues": ["variant"],
"recomputeCount": 0,
"cacheHitRate": 0
}
],
"flags": ["MEMO_INEFFECTIVE"]
}
],
"flags": ["MEMO_INEFFECTIVE"]
}Heuristic flags: WASTED_RENDER, MEMO_INEFFECTIVE, EFFECT_EVERY_RENDER, MISSING_CLEANUP, STALE_CLOSURE, STATE_UPDATE_LOOP
scope instrument profile <component>
Capture a full interaction-scoped performance profile (JS timing, layout, paint, layout shifts).
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --interaction <json> | string | [] | Interaction steps |
| --props <json> | string | {} | Inline props JSON |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
| --format <fmt> | json\|text | json | Output format |
| --show-flags | boolean | false | Show heuristic flags only |
scope instrument profile Button
scope instrument profile SearchPage \
--interaction '[{"action":"type","target":"input","text":"hello"}]'JSON output:
{
"component": "Button",
"totalRenders": 5,
"uniqueComponents": 3,
"wastedRenders": 1,
"timing": {
"js": 45,
"layout": 12,
"paint": 8
},
"layoutShifts": {
"count": 0,
"cumulativeScore": 0
},
"flags": [],
"interaction": []
}Heuristic flags: WASTED_RENDER, MEMO_INEFFECTIVE, EFFECT_EVERY_RENDER, MISSING_CLEANUP, HIGH_RENDER_COUNT, LAYOUT_SHIFT_DETECTED, SLOW_INTERACTION
scope instrument tree <component>
Output a structured instrumentation tree of a rendered component, annotated with render counts, timing, and context usage.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| --sort-by <field> | renderCount\|depth | depth | Sort nodes by field |
| --limit <n> | number | — | Limit to first N nodes (depth-first) |
| --uses-context <name> | string | — | Filter to components using a specific context |
| --provider-depth | boolean | false | Annotate with context-provider nesting depth |
| --wasted-renders | boolean | false | Filter to components with wasted renders |
| --format <fmt> | json\|tree | auto (TTY→tree, pipe→json) | Output format |
| --manifest <path> | string | .reactscope/manifest.json | Path to manifest |
scope instrument tree SearchPage
scope instrument tree SearchPage --sort-by renderCount --limit 20
scope instrument tree SearchPage --wasted-renders
scope instrument tree SearchPage --format json | jq '.children[]'TTY (tree) output:
SearchPage
├── div
│ ├── SearchInput [renders:1 45.32ms]
│ │ ├── input [renders:1]
│ │ └── ClearButton [memo] [renders:1]
│ └── ResultsList [ctx:ListContext] [renders:2 12.01ms]
│ └── ResultItem [consumer] [renders:3]JSON output:
{
"component": "SearchPage",
"type": "function",
"renderCount": 1,
"lastRenderDuration": 45.32,
"memoized": false,
"memoSkipped": 0,
"props": {"query": ""},
"propsChanged": false,
"state": {"searchTerm": ""},
"stateChanged": false,
"contexts": [],
"contextChanged": false,
"depth": 0,
"children": []
}scope report baseline / diff / pr-comment
Sub-commands for capturing and comparing baseline snapshots (visual regression workflow).
scope report baseline
Capture a complete baseline snapshot (manifest + renders + compliance data).
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -o, --output <dir> | string | .reactscope/baseline | Output directory |
| --components <glob> | string | — | Only baseline matching components |
| --manifest <path> | string | — | Use an existing manifest instead of regenerating |
| --viewport <WxH> | string | 375x812 | Viewport size |
scope report baseline
scope report baseline -o ./baselines/main
scope report baseline --components "Button,TextField"Files written:
.reactscope/baseline/
├── manifest.json # Full component manifest
├── compliance.json # Token compliance batch report
└── renders/
├── Button.json # Structured render output
├── Button.png # Screenshot
├── Button.error.json # Error details (if failed)
└── ...scope report diff
Compare current renders against a baseline snapshot and report regressions.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -b, --baseline <dir> | string | .reactscope/baseline | Baseline directory |
| --threshold <px> | number | 10 | Pixel dimension threshold for regression |
| --format <fmt> | json\|text\|html | auto | Output format |
scope report diff
scope report diff --baseline ./baselines/main --threshold 5
scope report diff --format html > regression-report.htmlText output:
Visual Regression Report
========================
Regressions detected: 3
Button
Expected: 120×48px
Actual: 125×50px
Δ: +5px width, +2px height
SearchPage
Expected: 800×600px
Actual: 805×595px
Δ: +5px width, -5px heightscope report pr-comment
Generate a Markdown-formatted GitHub PR comment with regression findings.
scope report pr-comment
scope report pr-comment --baseline ./baselines/main | gh pr comment --body-file -Output: Markdown suitable for posting as a GitHub PR comment.
scope ci
Run a complete non-interactive CI pipeline: manifest generation → rendering → compliance checks → visual regression comparison.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -b, --baseline <dir> | string | — | Baseline directory for regression comparison |
| --checks <list> | string | all | Comma-separated checks: compliance,a11y,console-errors,visual-regression |
| --threshold <n> | number | 0.90 | Compliance pass threshold (0–1) |
| --viewport <WxH> | string | 375x812 | Viewport size |
| --json | boolean | false | Emit structured JSON to stdout |
| -o, --output <path> | string | — | Write CI result JSON to file |
scope ci
scope ci --baseline .reactscope/baseline --threshold 0.95
scope ci --checks compliance,a11y --json
scope ci --json -o ci-result.jsonTTY output:
Scope CI Report
================================================
Components: 128 total 124 rendered 4 failed
[pass] Compliance 94.2% >= threshold 90.0%
[FAIL] Visual regression detected in 2 component(s): Button, TextField
[pass] No console errors detected
[FAIL] 4 component(s) failed to render
================================================
CI failed in 23.4s (exit code 4)JSON output (--json):
{
"ranAt": "2026-03-11T15:30:45.123Z",
"passed": false,
"exitCode": 4,
"checks": [
{
"check": "compliance",
"passed": true,
"message": "Compliance 94.2% >= threshold 90.0%",
"value": 0.942,
"threshold": 0.9
},
{
"check": "visual-regression",
"passed": false,
"message": "Visual regression detected in 2 component(s): Button, TextField"
}
],
"components": {
"total": 128,
"rendered": 124,
"failed": 4
},
"complianceScore": 0.942,
"complianceThreshold": 0.9,
"baselineCompared": true,
"wallClockMs": 23456
}Exit codes:
| Code | Meaning |
|------|---------|
| 0 | All checks passed |
| 1 | Compliance below threshold |
| 2 | Accessibility violations found |
| 3 | Console errors during render |
| 4 | Visual regression detected |
| 5 | Component render failures |
Pipeline steps:
- Generate manifest (scan for components)
- Render all components (4 parallel by default)
- Run token-compliance checks
- Compare against baseline (if
--baselineprovided) - Exit with appropriate code
scope site
Build and serve a static HTML component gallery from .reactscope/ output.
scope site build
Build the static HTML gallery site.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -i, --input <path> | string | .reactscope | Path to .reactscope input directory |
| -o, --output <path> | string | .reactscope/site | Output directory |
| --base-path <path> | string | / | Base URL path prefix for subdirectory deployment |
| --compliance <path> | string | — | Path to compliance batch report JSON |
| --title <text> | string | Scope — Component Gallery | Site title |
scope site build
scope site build -o ./docs/components
scope site build --base-path /docs/ --title "Acme Component Library"
scope site build --compliance .reactscope/compliance.jsonFiles written:
.reactscope/site/
├── index.html # Gallery homepage with component grid + search
├── dashboard.html # Analytics dashboard
├── button.html # Per-component detail pages (slugified name)
├── search-page.html
└── ...scope site serve
Serve the built site locally for preview.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| -p, --port <number> | number | 3000 | Port to listen on |
| -d, --dir <path> | string | .reactscope/site | Directory to serve |
scope site serve
scope site serve --port 8080
scope site serve --dir ./docs/components --port 8080Output:
Scope site running at http://localhost:3000
Serving /home/user/project/.reactscope/site
Press Ctrl+C to stop.Configuration
reactscope.config.json
Full configuration schema:
{
"components": {
"include": ["src/**/*.tsx"],
"exclude": ["**/*.test.tsx", "**/*.stories.tsx"],
"wrappers": {
"providers": [],
"globalCSS": []
}
},
"render": {
"viewport": {
"default": { "width": 375, "height": 812 }
},
"theme": "light",
"warmBrowser": false
},
"tokens": {
"file": "reactscope.tokens.json",
"compliance": {
"threshold": 0.9
}
},
"output": {
"dir": ".reactscope/",
"sprites": {
"format": "png",
"cellPadding": 8,
"labelAxes": true
},
"json": {
"pretty": false
}
},
"ci": {
"complianceThreshold": 0.9,
"failOnA11yViolations": true,
"failOnConsoleErrors": false,
"baselinePath": ".reactscope/baseline"
}
}reactscope.tokens.json
Design token file format:
{
"$schema": "https://raw.githubusercontent.com/FlatFilers/Scope/main/packages/tokens/schema.json",
"tokens": {
"color": {
"primary": { "value": "#3B82F6", "$type": "color" },
"semantic": {
"success": { "value": "{color.green.500}", "$type": "color" }
}
},
"spacing": {
"xs": { "value": "0.25rem", "$type": "dimension" }
}
}
}Tokens can reference other tokens using {path.to.token} syntax.
Architecture
The CLI is built with Commander.js and organized as a tree of command modules. The program.ts entry point builds the command tree at import time without executing it, keeping it testable in isolation.
Directory structure
src/
├── cli.ts # Entry point — creates and runs the program
├── program.ts # Builds the Commander command tree
├── browser.ts # Playwright-based browser capture
├── component-bundler.ts # esbuild IIFE bundling of components
├── manifest-commands.ts # manifest list/get/query/generate
├── manifest-formatter.ts # Table/JSON formatting for manifest output
├── render-commands.ts # render component/matrix/all
├── render-formatter.ts # Table/JSON/HTML/CSV formatting for render output
├── report-formatter.ts # Report summary formatting
├── tree-formatter.ts # ASCII tree formatting
├── site-commands.ts # site build/serve
├── tailwind-css.ts # Tailwind v4 CSS compilation
├── init/
│ ├── index.ts # scope init — project scaffolding
│ └── detect.ts # Framework/config auto-detection
├── tokens/
│ └── commands.ts # tokens get/list/search/resolve/validate/...
├── instrument/
│ ├── renders.ts # scope instrument renders
│ ├── hooks.ts # scope instrument hooks
│ ├── profile.ts # scope instrument profile
│ └── tree.ts # scope instrument tree
├── report/
│ ├── baseline.ts # scope report baseline
│ ├── diff.ts # scope report diff
│ └── pr-comment.ts # scope report pr-comment
└── ci/
└── commands.ts # scope ci pipelineKey patterns
- TTY detection — All formatters check
process.stdout.isTTYto switch between human-readable (table/tree) and machine-readable (JSON) output. - Lazy singletons —
BrowserPoolis created on demand and reused within a single command invocation. - Error wrapping —
safeRender()wraps individual component renders so one failure does not abort batch operations. - Concurrent execution — Render commands use configurable worker pools (
--concurrency) for parallel rendering. - Configuration resolution — Manifest and token file paths are resolved through a fallback chain: CLI flag → config file → default path.
- Dual export — The package exposes both ESM and CommonJS builds; the
scopebinary points to the CJS entrypoint for maximum compatibility.
