@agent-scope/cli 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1176 @@
1
+ # @agent-scope/cli
2
+
3
+ CLI for Scope — capture, replay, and analyze React components from the command line.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [Commands Reference](#commands-reference)
10
+ - [scope init](#scope-init)
11
+ - [scope capture](#scope-capture)
12
+ - [scope tree](#scope-tree)
13
+ - [scope report](#scope-report)
14
+ - [scope manifest](#scope-manifest)
15
+ - [scope render](#scope-render)
16
+ - [scope tokens](#scope-tokens)
17
+ - [scope instrument](#scope-instrument)
18
+ - [scope report baseline / diff / pr-comment](#scope-report-baseline--diff--pr-comment)
19
+ - [scope ci](#scope-ci)
20
+ - [scope site](#scope-site)
21
+ - [Configuration](#configuration)
22
+ - [Architecture](#architecture)
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install -g @agent-scope/cli
30
+ # or use it locally in a project
31
+ npm install --save-dev @agent-scope/cli
32
+ npx scope --help
33
+ ```
34
+
35
+ **Requirements:** Node.js 18+, Playwright browsers (installed automatically on first run).
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ```bash
42
+ # 1. Scaffold configuration in your React project
43
+ scope init
44
+
45
+ # 2. Scan your components and build the manifest
46
+ scope manifest generate
47
+
48
+ # 3. Render all components to screenshots
49
+ scope render all
50
+
51
+ # 4. Build a static component gallery site
52
+ scope site build
53
+
54
+ # 5. Preview it
55
+ scope site serve
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Commands Reference
61
+
62
+ ### `scope init`
63
+
64
+ Scaffold a Scope project — creates `reactscope.config.json`, an empty `reactscope.tokens.json`, and the `.reactscope/` output directory.
65
+
66
+ | Flag | Type | Default | Description |
67
+ |------|------|---------|-------------|
68
+ | `-y, --yes` | boolean | `false` | Accept all detected defaults without prompting |
69
+ | `--force` | boolean | `false` | Overwrite existing `reactscope.config.json` |
70
+
71
+ **Auto-detection:** Scope detects your framework (Next.js, Vite, Create React App, etc.), TypeScript support, and likely component file patterns before prompting.
72
+
73
+ **Example:**
74
+
75
+ ```bash
76
+ scope init
77
+ scope init --yes # non-interactive, accept all defaults
78
+ scope init --force # overwrite existing config
79
+ ```
80
+
81
+ **Interactive output:**
82
+
83
+ ```
84
+ 🚀 scope init — project configuration
85
+ Press Enter to accept the detected value shown in brackets.
86
+
87
+ Detected framework: Next.js
88
+
89
+ Component include patterns (comma-separated) [src/**/*.tsx]:
90
+ Component exclude patterns (comma-separated) [**/*.test.tsx,**/*.stories.tsx]:
91
+ Token file location [reactscope.tokens.json]:
92
+ Output directory [.reactscope/]:
93
+
94
+ ✅ Scope project initialised!
95
+
96
+ Created files:
97
+ /home/user/project/reactscope.config.json
98
+ /home/user/project/reactscope.tokens.json
99
+ /home/user/project/.reactscope/
100
+
101
+ Next steps: run `scope manifest` to scan your components.
102
+ ```
103
+
104
+ **Files written:**
105
+
106
+ | Path | Description |
107
+ |------|-------------|
108
+ | `reactscope.config.json` | Project configuration |
109
+ | `reactscope.tokens.json` | Design tokens stub |
110
+ | `.reactscope/` | Output directory |
111
+ | `.gitignore` | `.reactscope/` entry added |
112
+
113
+ ---
114
+
115
+ ### `scope capture`
116
+
117
+ Capture a React component tree from a live URL and output raw JSON.
118
+
119
+ ```bash
120
+ scope capture <url>
121
+ ```
122
+
123
+ | Flag | Type | Default | Description |
124
+ |------|------|---------|-------------|
125
+ | `-o, --output <path>` | string | stdout | Write JSON to file instead of stdout |
126
+ | `--pretty` | boolean | `false` | Pretty-print JSON output |
127
+ | `--timeout <ms>` | number | `10000` | Max wait time for React to mount |
128
+ | `--wait <ms>` | number | `0` | Additional wait after page load before capture |
129
+
130
+ **Example:**
131
+
132
+ ```bash
133
+ scope capture http://localhost:5173
134
+ scope capture http://localhost:5173 --pretty -o capture.json
135
+ scope capture http://localhost:5173 --timeout 30000
136
+ ```
137
+
138
+ **Output:** `PageReport` JSON containing the full component tree, hooks, console entries, errors, and suspense boundaries.
139
+
140
+ ---
141
+
142
+ ### `scope tree`
143
+
144
+ Display the React component tree from a live URL as an ASCII tree in the terminal.
145
+
146
+ ```bash
147
+ scope tree <url>
148
+ ```
149
+
150
+ | Flag | Type | Default | Description |
151
+ |------|------|---------|-------------|
152
+ | `--depth <n>` | number | unlimited | Max depth to display |
153
+ | `--show-props` | boolean | `false` | Include prop names next to components |
154
+ | `--show-hooks` | boolean | `false` | Show hook counts per component |
155
+ | `--timeout <ms>` | number | `10000` | Max wait time for React to mount |
156
+ | `--wait <ms>` | number | `0` | Additional wait after page load |
157
+
158
+ **Example:**
159
+
160
+ ```bash
161
+ scope tree http://localhost:5173
162
+ scope tree http://localhost:5173 --depth 3 --show-props
163
+ ```
164
+
165
+ **Output:**
166
+
167
+ ```
168
+ Button
169
+ ├── div
170
+ │ ├── span
171
+ │ └── svg
172
+ └── FocusRing
173
+ ```
174
+
175
+ ---
176
+
177
+ ### `scope report`
178
+
179
+ Capture and display a human-readable summary of a React app.
180
+
181
+ ```bash
182
+ scope report <url>
183
+ ```
184
+
185
+ | Flag | Type | Default | Description |
186
+ |------|------|---------|-------------|
187
+ | `--json` | boolean | `false` | Output as structured JSON instead of human-readable text |
188
+ | `--timeout <ms>` | number | `10000` | Max wait time for React to mount |
189
+ | `--wait <ms>` | number | `0` | Additional wait after page load |
190
+
191
+ **Example:**
192
+
193
+ ```bash
194
+ scope report http://localhost:5173
195
+ scope report http://localhost:5173 --json
196
+ ```
197
+
198
+ **TTY output:**
199
+
200
+ ```
201
+ Scope Report for http://localhost:5173
202
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
203
+ Components: 24 total (18 function, 4 memo, 2 forwardRef)
204
+ Max depth: 6
205
+ Hooks: 42 total (12 useState, 8 useEffect, ...)
206
+ Error boundaries: 2
207
+ Suspense boundaries: 1 (1 resolved)
208
+ Console entries: 3 (1 warn, 2 error)
209
+ Capture time: 45ms
210
+ ```
211
+
212
+ ---
213
+
214
+ ### `scope manifest`
215
+
216
+ Manage the component manifest — a catalogue of all React components in your project with their metadata.
217
+
218
+ The manifest is stored at `.reactscope/manifest.json` by default.
219
+
220
+ #### `scope manifest generate`
221
+
222
+ Scan source files and generate the component manifest.
223
+
224
+ | Flag | Type | Default | Description |
225
+ |------|------|---------|-------------|
226
+ | `--root <path>` | string | cwd | Project root directory |
227
+ | `--output <path>` | string | `.reactscope/manifest.json` | Output path |
228
+ | `--include <globs>` | string | from config | Comma-separated glob patterns to include |
229
+ | `--exclude <globs>` | string | from config | Comma-separated glob patterns to exclude |
230
+
231
+ ```bash
232
+ scope manifest generate
233
+ scope manifest generate --root /path/to/project --output manifest.json
234
+ scope manifest generate --include "src/**/*.tsx" --exclude "**/*.stories.tsx"
235
+ ```
236
+
237
+ **Files written:** `.reactscope/manifest.json`
238
+
239
+ #### `scope manifest list`
240
+
241
+ List all components in the manifest.
242
+
243
+ | Flag | Type | Default | Description |
244
+ |------|------|---------|-------------|
245
+ | `--format <fmt>` | `json\|table` | auto (TTY→table, pipe→json) | Output format |
246
+ | `--filter <glob>` | string | — | Filter by component name glob |
247
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
248
+
249
+ ```bash
250
+ scope manifest list
251
+ scope manifest list --filter "Button*"
252
+ scope manifest list --format json | jq '.[] | select(.complexityClass == "complex")'
253
+ ```
254
+
255
+ **Table output (TTY):**
256
+
257
+ ```
258
+ NAME FILE COMPLEXITY HOOKS CONTEXTS
259
+ Button src/Button.tsx simple 2 0
260
+ SearchPage src/SearchPage.tsx complex 5 2
261
+ ```
262
+
263
+ **JSON output (pipe):**
264
+
265
+ ```json
266
+ [
267
+ {
268
+ "name": "Button",
269
+ "file": "src/Button.tsx",
270
+ "complexityClass": "simple",
271
+ "hookCount": 2,
272
+ "contextCount": 0
273
+ }
274
+ ]
275
+ ```
276
+
277
+ #### `scope manifest get <name>`
278
+
279
+ Get full details of a single component by name.
280
+
281
+ | Flag | Type | Default | Description |
282
+ |------|------|---------|-------------|
283
+ | `--format <fmt>` | `json\|table` | auto | Output format |
284
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
285
+
286
+ ```bash
287
+ scope manifest get Button
288
+ scope manifest get Button --format json
289
+ ```
290
+
291
+ **TTY output:**
292
+
293
+ ```
294
+ Component: Button
295
+ File: src/Button.tsx
296
+ Export: named
297
+ Display Name: Button
298
+ Complexity: simple
299
+ Memoized: true
300
+ Forwarded Ref: false
301
+ HOC Wrappers: none
302
+ Hooks: useState, useCallback
303
+ Contexts: none
304
+ Composes: Icon
305
+ Composed By: ButtonGroup
306
+ Side Effects: none
307
+
308
+ Props (3):
309
+ variant: "primary" | "secondary" — optional [default: "primary"]
310
+ size: "sm" | "md" | "lg" — optional [default: "md"]
311
+ disabled: boolean — optional [default: false]
312
+ ```
313
+
314
+ #### `scope manifest query`
315
+
316
+ Query components by attributes.
317
+
318
+ | Flag | Type | Default | Description |
319
+ |------|------|---------|-------------|
320
+ | `--context <name>` | string | — | Find components consuming a context |
321
+ | `--hook <name>` | string | — | Find components using a specific hook |
322
+ | `--complexity <class>` | `simple\|complex` | — | Filter by complexity class |
323
+ | `--side-effects` | boolean | `false` | Find components with any side effects |
324
+ | `--has-fetch` | boolean | `false` | Find components with fetch calls |
325
+ | `--format <fmt>` | `json\|table` | auto | Output format |
326
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
327
+
328
+ ```bash
329
+ scope manifest query --hook useState --complexity complex
330
+ scope manifest query --context AuthContext
331
+ scope manifest query --has-fetch --format json
332
+ ```
333
+
334
+ ---
335
+
336
+ ### `scope render`
337
+
338
+ Render components to PNG screenshots or JSON using esbuild bundling and Playwright.
339
+
340
+ #### `scope render <component>` (or `scope render component <component>`)
341
+
342
+ Render a single component.
343
+
344
+ | Flag | Type | Default | Description |
345
+ |------|------|---------|-------------|
346
+ | `--props <json>` | string | `{}` | Inline props JSON |
347
+ | `--viewport <WxH>` | string | `375x812` | Viewport size |
348
+ | `--theme <name>` | string | — | Theme name from the token system |
349
+ | `-o, --output <path>` | string | `.reactscope/renders/<component>.png` | Write PNG to file |
350
+ | `--format <fmt>` | `png\|json` | auto (TTY→png, pipe→json) | Output format |
351
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
352
+
353
+ ```bash
354
+ scope render Button
355
+ scope render Button --props '{"variant":"primary","size":"lg"}'
356
+ scope render Button --viewport 1280x800 --format json
357
+ scope render Button -o screenshots/button.png
358
+ ```
359
+
360
+ **JSON output:**
361
+
362
+ ```json
363
+ {
364
+ "component": "Button",
365
+ "props": { "variant": "primary" },
366
+ "width": 120,
367
+ "height": 48,
368
+ "renderTimeMs": 145,
369
+ "screenshot": "iVBORw0KGgoAAAANSUhEUgAA...",
370
+ "computedStyles": {}
371
+ }
372
+ ```
373
+
374
+ **Files written (TTY mode):**
375
+ - `.reactscope/renders/<component>.png`
376
+ - `.reactscope/renders/<component>.json`
377
+
378
+ #### `scope render matrix <component>`
379
+
380
+ Render a component across a matrix of prop combinations (Cartesian product).
381
+
382
+ | Flag | Type | Default | Description |
383
+ |------|------|---------|-------------|
384
+ | `--axes <spec>` | string | — | Axis definitions, e.g. `'variant:primary,secondary size:sm,md,lg'` |
385
+ | `--contexts <ids>` | string | — | Composition context IDs, comma-separated |
386
+ | `--stress <ids>` | string | — | Stress preset IDs, comma-separated |
387
+ | `--sprite <path>` | string | `.reactscope/renders/<component>-matrix.png` | Write sprite sheet |
388
+ | `--format <fmt>` | `json\|png\|html\|csv` | auto | Output format |
389
+ | `--concurrency <n>` | number | `8` | Max parallel renders |
390
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
391
+
392
+ ```bash
393
+ scope render matrix Button --axes 'variant:primary,secondary size:sm,md,lg'
394
+ scope render matrix Button --axes 'variant:primary,secondary' --format html > matrix.html
395
+ scope render matrix Button --axes 'variant:primary,secondary size:sm,md' --format csv
396
+ ```
397
+
398
+ **CSV output:**
399
+
400
+ ```
401
+ component,variant,size,renderTimeMs,width,height
402
+ Button,primary,sm,145,100,36
403
+ Button,primary,md,148,120,48
404
+ Button,secondary,sm,142,100,36
405
+ ```
406
+
407
+ #### `scope render all`
408
+
409
+ Render all components in the manifest.
410
+
411
+ | Flag | Type | Default | Description |
412
+ |------|------|---------|-------------|
413
+ | `--concurrency <n>` | number | `4` | Max parallel renders |
414
+ | `--output-dir <dir>` | string | `.reactscope/renders` | Output directory |
415
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
416
+ | `--format <fmt>` | `json\|png` | `png` | Output format |
417
+
418
+ ```bash
419
+ scope render all
420
+ scope render all --concurrency 8 --output-dir ./screenshots
421
+ ```
422
+
423
+ **Progress output (stderr):**
424
+
425
+ ```
426
+ Rendering 128 components (concurrency: 4)…
427
+ Rendering 128/128 Button [===================> ] 95%
428
+ ```
429
+
430
+ **Files written per component:**
431
+
432
+ | Path | Description |
433
+ |------|-------------|
434
+ | `.reactscope/renders/<component>.png` | Screenshot |
435
+ | `.reactscope/renders/<component>.json` | Structured render data |
436
+ | `.reactscope/renders/<component>.error.json` | Error details (if failed) |
437
+
438
+ ---
439
+
440
+ ### `scope tokens`
441
+
442
+ Manage and validate design tokens from `reactscope.tokens.json`.
443
+
444
+ **Token file resolution order:**
445
+ 1. `--file` flag
446
+ 2. `tokens.file` in `reactscope.config.json`
447
+ 3. `reactscope.tokens.json` (default)
448
+
449
+ #### `scope tokens get <path>`
450
+
451
+ Resolve a token path to its computed value.
452
+
453
+ | Flag | Type | Default | Description |
454
+ |------|------|---------|-------------|
455
+ | `--file <path>` | string | — | Override token file path |
456
+ | `--format <fmt>` | `json\|text` | auto | Output format |
457
+
458
+ ```bash
459
+ scope tokens get color.semantic.success
460
+ # Output: #22C55E
461
+
462
+ scope tokens get color.semantic.success --format json
463
+ ```
464
+
465
+ **JSON output:**
466
+
467
+ ```json
468
+ {
469
+ "path": "color.semantic.success",
470
+ "value": "{color.green.500}",
471
+ "resolvedValue": "#22C55E",
472
+ "type": "color"
473
+ }
474
+ ```
475
+
476
+ #### `scope tokens list [category]`
477
+
478
+ List tokens, optionally filtered by category or type.
479
+
480
+ | Flag | Type | Default | Description |
481
+ |------|------|---------|-------------|
482
+ | `--type <type>` | string | — | Filter by token type (`color`, `dimension`, `fontFamily`, etc.) |
483
+ | `--file <path>` | string | — | Override token file path |
484
+ | `--format <fmt>` | `json\|table` | auto | Output format |
485
+
486
+ ```bash
487
+ scope tokens list
488
+ scope tokens list color
489
+ scope tokens list --type color --format json
490
+ ```
491
+
492
+ **Table output:**
493
+
494
+ ```
495
+ PATH VALUE RESOLVED TYPE
496
+ color.primary {color.blue.500} #3B82F6 color
497
+ color.semantic.success {color.green.500} #22C55E color
498
+ spacing.xs 0.25rem 0.25rem dimension
499
+ ```
500
+
501
+ #### `scope tokens search <value>`
502
+
503
+ Find which tokens match a computed value. Supports fuzzy color matching.
504
+
505
+ | Flag | Type | Default | Description |
506
+ |------|------|---------|-------------|
507
+ | `--type <type>` | string | — | Restrict search to a token type |
508
+ | `--fuzzy` | boolean | `false` | Return nearest match even if no exact match |
509
+ | `--file <path>` | string | — | Override token file path |
510
+ | `--format <fmt>` | `json\|table` | auto | Output format |
511
+
512
+ ```bash
513
+ scope tokens search "#22C55E"
514
+ scope tokens search "#22C55E" --fuzzy
515
+ scope tokens search "0.25rem" --type dimension
516
+ ```
517
+
518
+ #### `scope tokens resolve <path>`
519
+
520
+ Show the full resolution chain for a token (useful for debugging alias chains).
521
+
522
+ | Flag | Type | Default | Description |
523
+ |------|------|---------|-------------|
524
+ | `--file <path>` | string | — | Override token file path |
525
+ | `--format <fmt>` | `json\|text` | auto | Output format |
526
+
527
+ ```bash
528
+ scope tokens resolve color.semantic.success
529
+ ```
530
+
531
+ **Text output:**
532
+
533
+ ```
534
+ color.semantic.success → {color.green.500}
535
+ {color.green.500} → #22C55E
536
+ ```
537
+
538
+ #### `scope tokens validate`
539
+
540
+ Validate the token file for errors (circular references, missing references, type mismatches).
541
+
542
+ | Flag | Type | Default | Description |
543
+ |------|------|---------|-------------|
544
+ | `--file <path>` | string | — | Override token file path |
545
+ | `--format <fmt>` | `json\|text` | auto | Output format |
546
+
547
+ ```bash
548
+ scope tokens validate
549
+ scope tokens validate --file custom-tokens.json
550
+ ```
551
+
552
+ **Success output:**
553
+
554
+ ```
555
+ ✓ Token file is valid: reactscope.tokens.json
556
+ ```
557
+
558
+ **Error output:**
559
+
560
+ ```
561
+ ✗ Token file has 2 error(s): reactscope.tokens.json
562
+
563
+ CIRCULAR_REFERENCE [color.a]: Token references itself via chain
564
+ MISSING_REFERENCE [color.b]: Referenced token "color.missing" not found
565
+ ```
566
+
567
+ #### `scope tokens compliance`
568
+
569
+ Check token compliance scores — how many computed CSS values in rendered components map back to design tokens.
570
+
571
+ | Flag | Type | Default | Description |
572
+ |------|------|---------|-------------|
573
+ | `--file <path>` | string | — | Override token file path |
574
+ | `--format <fmt>` | `json\|table` | auto | Output format |
575
+ | `--threshold <n>` | number | `0.90` | Fail if compliance is below this value (0–1) |
576
+
577
+ #### `scope tokens impact`
578
+
579
+ Analyze the impact of token changes — which components will be affected if a token value changes.
580
+
581
+ | Flag | Type | Default | Description |
582
+ |------|------|---------|-------------|
583
+ | `--token <path>` | string | — | Token path to analyze impact for |
584
+ | `--file <path>` | string | — | Override token file path |
585
+ | `--format <fmt>` | `json\|table` | auto | Output format |
586
+
587
+ #### `scope tokens preview`
588
+
589
+ Preview the visual effect of token changes before committing them.
590
+
591
+ | Flag | Type | Default | Description |
592
+ |------|------|---------|-------------|
593
+ | `--set <path=value>` | string | — | Token override, e.g. `color.primary=#FF0000` |
594
+ | `--file <path>` | string | — | Override token file path |
595
+ | `--format <fmt>` | `json\|html` | auto | Output format |
596
+
597
+ #### `scope tokens export`
598
+
599
+ Export tokens in various formats for consumption by other tools.
600
+
601
+ | Flag | Type | Default | Description |
602
+ |------|------|---------|-------------|
603
+ | `--format <fmt>` | `css\|tailwind\|json\|scss` | `css` | Export format |
604
+ | `--file <path>` | string | — | Override token file path |
605
+ | `-o, --output <path>` | string | stdout | Write to file |
606
+
607
+ ```bash
608
+ scope tokens export --format css -o tokens.css
609
+ scope tokens export --format tailwind -o tailwind-tokens.js
610
+ ```
611
+
612
+ ---
613
+
614
+ ### `scope instrument`
615
+
616
+ Structured runtime instrumentation for analyzing React component behavior during interactions.
617
+
618
+ #### `scope instrument renders <component>`
619
+
620
+ Trace re-render causality chains during an interaction sequence.
621
+
622
+ | Flag | Type | Default | Description |
623
+ |------|------|---------|-------------|
624
+ | `--interaction <json>` | string | `[]` | Interaction sequence JSON |
625
+ | `--json` | boolean | `false` | Force JSON output |
626
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
627
+
628
+ **Interaction steps** are JSON arrays with `action`, `target`, and optional fields:
629
+
630
+ ```bash
631
+ scope instrument renders SearchPage \
632
+ --interaction '[{"action":"click","target":"button"},{"action":"type","target":"input","text":"hello"}]'
633
+ ```
634
+
635
+ **Supported actions:** `click`, `type`, `wait`, `hover`, `blur`, `focus`, `scroll`
636
+
637
+ **JSON output:**
638
+
639
+ ```json
640
+ {
641
+ "component": "SearchPage",
642
+ "interaction": [{"action": "click", "target": "button"}],
643
+ "summary": {
644
+ "totalRenders": 12,
645
+ "uniqueComponents": 5,
646
+ "wastedRenders": 2,
647
+ "interactionDurationMs": 450
648
+ },
649
+ "renders": [
650
+ {
651
+ "component": "SearchPage",
652
+ "renderIndex": 0,
653
+ "trigger": "state_change",
654
+ "propsChanged": false,
655
+ "stateChanged": true,
656
+ "contextChanged": false,
657
+ "memoized": false,
658
+ "wasted": false,
659
+ "chain": [{"component": "SearchPage", "trigger": "state_change"}],
660
+ "cascade": {
661
+ "totalRendersTriggered": 8,
662
+ "uniqueComponents": 3,
663
+ "unchangedPropRenders": 2
664
+ }
665
+ }
666
+ ],
667
+ "flags": [
668
+ {
669
+ "id": "RENDER_CASCADE",
670
+ "severity": "warning",
671
+ "component": "SearchPage",
672
+ "detail": "State change in SearchPage triggered 8 downstream re-renders"
673
+ }
674
+ ]
675
+ }
676
+ ```
677
+
678
+ **Heuristic flags:** `WASTED_RENDER`, `RENDER_CASCADE`
679
+
680
+ #### `scope instrument hooks <component>`
681
+
682
+ Profile per-hook-instance data for a component.
683
+
684
+ | Flag | Type | Default | Description |
685
+ |------|------|---------|-------------|
686
+ | `--props <json>` | string | `{}` | Inline props JSON |
687
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
688
+ | `--format <fmt>` | `json\|text` | `json` | Output format |
689
+ | `--show-flags` | boolean | `false` | Show heuristic flags only |
690
+
691
+ ```bash
692
+ scope instrument hooks Button
693
+ scope instrument hooks Button --props '{"variant":"primary"}' --show-flags
694
+ ```
695
+
696
+ **JSON output:**
697
+
698
+ ```json
699
+ {
700
+ "component": "Button",
701
+ "components": [
702
+ {
703
+ "name": "Button",
704
+ "source": {"file": "src/Button.tsx", "line": 42},
705
+ "hooks": [
706
+ {
707
+ "index": 0,
708
+ "type": "useState",
709
+ "currentValue": true,
710
+ "updateCount": 0
711
+ },
712
+ {
713
+ "index": 1,
714
+ "type": "useCallback",
715
+ "dependencyValues": ["variant"],
716
+ "recomputeCount": 0,
717
+ "cacheHitRate": 0
718
+ }
719
+ ],
720
+ "flags": ["MEMO_INEFFECTIVE"]
721
+ }
722
+ ],
723
+ "flags": ["MEMO_INEFFECTIVE"]
724
+ }
725
+ ```
726
+
727
+ **Heuristic flags:** `WASTED_RENDER`, `MEMO_INEFFECTIVE`, `EFFECT_EVERY_RENDER`, `MISSING_CLEANUP`, `STALE_CLOSURE`, `STATE_UPDATE_LOOP`
728
+
729
+ #### `scope instrument profile <component>`
730
+
731
+ Capture a full interaction-scoped performance profile (JS timing, layout, paint, layout shifts).
732
+
733
+ | Flag | Type | Default | Description |
734
+ |------|------|---------|-------------|
735
+ | `--interaction <json>` | string | `[]` | Interaction steps |
736
+ | `--props <json>` | string | `{}` | Inline props JSON |
737
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
738
+ | `--format <fmt>` | `json\|text` | `json` | Output format |
739
+ | `--show-flags` | boolean | `false` | Show heuristic flags only |
740
+
741
+ ```bash
742
+ scope instrument profile Button
743
+ scope instrument profile SearchPage \
744
+ --interaction '[{"action":"type","target":"input","text":"hello"}]'
745
+ ```
746
+
747
+ **JSON output:**
748
+
749
+ ```json
750
+ {
751
+ "component": "Button",
752
+ "totalRenders": 5,
753
+ "uniqueComponents": 3,
754
+ "wastedRenders": 1,
755
+ "timing": {
756
+ "js": 45,
757
+ "layout": 12,
758
+ "paint": 8
759
+ },
760
+ "layoutShifts": {
761
+ "count": 0,
762
+ "cumulativeScore": 0
763
+ },
764
+ "flags": [],
765
+ "interaction": []
766
+ }
767
+ ```
768
+
769
+ **Heuristic flags:** `WASTED_RENDER`, `MEMO_INEFFECTIVE`, `EFFECT_EVERY_RENDER`, `MISSING_CLEANUP`, `HIGH_RENDER_COUNT`, `LAYOUT_SHIFT_DETECTED`, `SLOW_INTERACTION`
770
+
771
+ #### `scope instrument tree <component>`
772
+
773
+ Output a structured instrumentation tree of a rendered component, annotated with render counts, timing, and context usage.
774
+
775
+ | Flag | Type | Default | Description |
776
+ |------|------|---------|-------------|
777
+ | `--sort-by <field>` | `renderCount\|depth` | `depth` | Sort nodes by field |
778
+ | `--limit <n>` | number | — | Limit to first N nodes (depth-first) |
779
+ | `--uses-context <name>` | string | — | Filter to components using a specific context |
780
+ | `--provider-depth` | boolean | `false` | Annotate with context-provider nesting depth |
781
+ | `--wasted-renders` | boolean | `false` | Filter to components with wasted renders |
782
+ | `--format <fmt>` | `json\|tree` | auto (TTY→tree, pipe→json) | Output format |
783
+ | `--manifest <path>` | string | `.reactscope/manifest.json` | Path to manifest |
784
+
785
+ ```bash
786
+ scope instrument tree SearchPage
787
+ scope instrument tree SearchPage --sort-by renderCount --limit 20
788
+ scope instrument tree SearchPage --wasted-renders
789
+ scope instrument tree SearchPage --format json | jq '.children[]'
790
+ ```
791
+
792
+ **TTY (tree) output:**
793
+
794
+ ```
795
+ SearchPage
796
+ ├── div
797
+ │ ├── SearchInput [renders:1 45.32ms]
798
+ │ │ ├── input [renders:1]
799
+ │ │ └── ClearButton [memo] [renders:1]
800
+ │ └── ResultsList [ctx:ListContext] [renders:2 12.01ms]
801
+ │ └── ResultItem [consumer] [renders:3]
802
+ ```
803
+
804
+ **JSON output:**
805
+
806
+ ```json
807
+ {
808
+ "component": "SearchPage",
809
+ "type": "function",
810
+ "renderCount": 1,
811
+ "lastRenderDuration": 45.32,
812
+ "memoized": false,
813
+ "memoSkipped": 0,
814
+ "props": {"query": ""},
815
+ "propsChanged": false,
816
+ "state": {"searchTerm": ""},
817
+ "stateChanged": false,
818
+ "contexts": [],
819
+ "contextChanged": false,
820
+ "depth": 0,
821
+ "children": []
822
+ }
823
+ ```
824
+
825
+ ---
826
+
827
+ ### `scope report baseline / diff / pr-comment`
828
+
829
+ Sub-commands for capturing and comparing baseline snapshots (visual regression workflow).
830
+
831
+ #### `scope report baseline`
832
+
833
+ Capture a complete baseline snapshot (manifest + renders + compliance data).
834
+
835
+ | Flag | Type | Default | Description |
836
+ |------|------|---------|-------------|
837
+ | `-o, --output <dir>` | string | `.reactscope/baseline` | Output directory |
838
+ | `--components <glob>` | string | — | Only baseline matching components |
839
+ | `--manifest <path>` | string | — | Use an existing manifest instead of regenerating |
840
+ | `--viewport <WxH>` | string | `375x812` | Viewport size |
841
+
842
+ ```bash
843
+ scope report baseline
844
+ scope report baseline -o ./baselines/main
845
+ scope report baseline --components "Button,TextField"
846
+ ```
847
+
848
+ **Files written:**
849
+
850
+ ```
851
+ .reactscope/baseline/
852
+ ├── manifest.json # Full component manifest
853
+ ├── compliance.json # Token compliance batch report
854
+ └── renders/
855
+ ├── Button.json # Structured render output
856
+ ├── Button.png # Screenshot
857
+ ├── Button.error.json # Error details (if failed)
858
+ └── ...
859
+ ```
860
+
861
+ #### `scope report diff`
862
+
863
+ Compare current renders against a baseline snapshot and report regressions.
864
+
865
+ | Flag | Type | Default | Description |
866
+ |------|------|---------|-------------|
867
+ | `-b, --baseline <dir>` | string | `.reactscope/baseline` | Baseline directory |
868
+ | `--threshold <px>` | number | `10` | Pixel dimension threshold for regression |
869
+ | `--format <fmt>` | `json\|text\|html` | auto | Output format |
870
+
871
+ ```bash
872
+ scope report diff
873
+ scope report diff --baseline ./baselines/main --threshold 5
874
+ scope report diff --format html > regression-report.html
875
+ ```
876
+
877
+ **Text output:**
878
+
879
+ ```
880
+ Visual Regression Report
881
+ ========================
882
+
883
+ Regressions detected: 3
884
+
885
+ Button
886
+ Expected: 120×48px
887
+ Actual: 125×50px
888
+ Δ: +5px width, +2px height
889
+
890
+ SearchPage
891
+ Expected: 800×600px
892
+ Actual: 805×595px
893
+ Δ: +5px width, -5px height
894
+ ```
895
+
896
+ #### `scope report pr-comment`
897
+
898
+ Generate a Markdown-formatted GitHub PR comment with regression findings.
899
+
900
+ ```bash
901
+ scope report pr-comment
902
+ scope report pr-comment --baseline ./baselines/main | gh pr comment --body-file -
903
+ ```
904
+
905
+ **Output:** Markdown suitable for posting as a GitHub PR comment.
906
+
907
+ ---
908
+
909
+ ### `scope ci`
910
+
911
+ Run a complete non-interactive CI pipeline: manifest generation → rendering → compliance checks → visual regression comparison.
912
+
913
+ | Flag | Type | Default | Description |
914
+ |------|------|---------|-------------|
915
+ | `-b, --baseline <dir>` | string | — | Baseline directory for regression comparison |
916
+ | `--checks <list>` | string | all | Comma-separated checks: `compliance,a11y,console-errors,visual-regression` |
917
+ | `--threshold <n>` | number | `0.90` | Compliance pass threshold (0–1) |
918
+ | `--viewport <WxH>` | string | `375x812` | Viewport size |
919
+ | `--json` | boolean | `false` | Emit structured JSON to stdout |
920
+ | `-o, --output <path>` | string | — | Write CI result JSON to file |
921
+
922
+ ```bash
923
+ scope ci
924
+ scope ci --baseline .reactscope/baseline --threshold 0.95
925
+ scope ci --checks compliance,a11y --json
926
+ scope ci --json -o ci-result.json
927
+ ```
928
+
929
+ **TTY output:**
930
+
931
+ ```
932
+ Scope CI Report
933
+ ================================================
934
+ Components: 128 total 124 rendered 4 failed
935
+
936
+ [pass] Compliance 94.2% >= threshold 90.0%
937
+ [FAIL] Visual regression detected in 2 component(s): Button, TextField
938
+ [pass] No console errors detected
939
+ [FAIL] 4 component(s) failed to render
940
+
941
+ ================================================
942
+ CI failed in 23.4s (exit code 4)
943
+ ```
944
+
945
+ **JSON output (`--json`):**
946
+
947
+ ```json
948
+ {
949
+ "ranAt": "2026-03-11T15:30:45.123Z",
950
+ "passed": false,
951
+ "exitCode": 4,
952
+ "checks": [
953
+ {
954
+ "check": "compliance",
955
+ "passed": true,
956
+ "message": "Compliance 94.2% >= threshold 90.0%",
957
+ "value": 0.942,
958
+ "threshold": 0.9
959
+ },
960
+ {
961
+ "check": "visual-regression",
962
+ "passed": false,
963
+ "message": "Visual regression detected in 2 component(s): Button, TextField"
964
+ }
965
+ ],
966
+ "components": {
967
+ "total": 128,
968
+ "rendered": 124,
969
+ "failed": 4
970
+ },
971
+ "complianceScore": 0.942,
972
+ "complianceThreshold": 0.9,
973
+ "baselineCompared": true,
974
+ "wallClockMs": 23456
975
+ }
976
+ ```
977
+
978
+ **Exit codes:**
979
+
980
+ | Code | Meaning |
981
+ |------|---------|
982
+ | `0` | All checks passed |
983
+ | `1` | Compliance below threshold |
984
+ | `2` | Accessibility violations found |
985
+ | `3` | Console errors during render |
986
+ | `4` | Visual regression detected |
987
+ | `5` | Component render failures |
988
+
989
+ **Pipeline steps:**
990
+ 1. Generate manifest (scan for components)
991
+ 2. Render all components (4 parallel by default)
992
+ 3. Run token-compliance checks
993
+ 4. Compare against baseline (if `--baseline` provided)
994
+ 5. Exit with appropriate code
995
+
996
+ ---
997
+
998
+ ### `scope site`
999
+
1000
+ Build and serve a static HTML component gallery from `.reactscope/` output.
1001
+
1002
+ #### `scope site build`
1003
+
1004
+ Build the static HTML gallery site.
1005
+
1006
+ | Flag | Type | Default | Description |
1007
+ |------|------|---------|-------------|
1008
+ | `-i, --input <path>` | string | `.reactscope` | Path to `.reactscope` input directory |
1009
+ | `-o, --output <path>` | string | `.reactscope/site` | Output directory |
1010
+ | `--base-path <path>` | string | `/` | Base URL path prefix for subdirectory deployment |
1011
+ | `--compliance <path>` | string | — | Path to compliance batch report JSON |
1012
+ | `--title <text>` | string | `Scope — Component Gallery` | Site title |
1013
+
1014
+ ```bash
1015
+ scope site build
1016
+ scope site build -o ./docs/components
1017
+ scope site build --base-path /docs/ --title "Acme Component Library"
1018
+ scope site build --compliance .reactscope/compliance.json
1019
+ ```
1020
+
1021
+ **Files written:**
1022
+
1023
+ ```
1024
+ .reactscope/site/
1025
+ ├── index.html # Gallery homepage with component grid + search
1026
+ ├── dashboard.html # Analytics dashboard
1027
+ ├── button.html # Per-component detail pages (slugified name)
1028
+ ├── search-page.html
1029
+ └── ...
1030
+ ```
1031
+
1032
+ #### `scope site serve`
1033
+
1034
+ Serve the built site locally for preview.
1035
+
1036
+ | Flag | Type | Default | Description |
1037
+ |------|------|---------|-------------|
1038
+ | `-p, --port <number>` | number | `3000` | Port to listen on |
1039
+ | `-d, --dir <path>` | string | `.reactscope/site` | Directory to serve |
1040
+
1041
+ ```bash
1042
+ scope site serve
1043
+ scope site serve --port 8080
1044
+ scope site serve --dir ./docs/components --port 8080
1045
+ ```
1046
+
1047
+ **Output:**
1048
+
1049
+ ```
1050
+ Scope site running at http://localhost:3000
1051
+ Serving /home/user/project/.reactscope/site
1052
+ Press Ctrl+C to stop.
1053
+ ```
1054
+
1055
+ ---
1056
+
1057
+ ## Configuration
1058
+
1059
+ ### `reactscope.config.json`
1060
+
1061
+ Full configuration schema:
1062
+
1063
+ ```json
1064
+ {
1065
+ "components": {
1066
+ "include": ["src/**/*.tsx"],
1067
+ "exclude": ["**/*.test.tsx", "**/*.stories.tsx"],
1068
+ "wrappers": {
1069
+ "providers": [],
1070
+ "globalCSS": []
1071
+ }
1072
+ },
1073
+ "render": {
1074
+ "viewport": {
1075
+ "default": { "width": 375, "height": 812 }
1076
+ },
1077
+ "theme": "light",
1078
+ "warmBrowser": false
1079
+ },
1080
+ "tokens": {
1081
+ "file": "reactscope.tokens.json",
1082
+ "compliance": {
1083
+ "threshold": 0.9
1084
+ }
1085
+ },
1086
+ "output": {
1087
+ "dir": ".reactscope/",
1088
+ "sprites": {
1089
+ "format": "png",
1090
+ "cellPadding": 8,
1091
+ "labelAxes": true
1092
+ },
1093
+ "json": {
1094
+ "pretty": false
1095
+ }
1096
+ },
1097
+ "ci": {
1098
+ "complianceThreshold": 0.9,
1099
+ "failOnA11yViolations": true,
1100
+ "failOnConsoleErrors": false,
1101
+ "baselinePath": ".reactscope/baseline"
1102
+ }
1103
+ }
1104
+ ```
1105
+
1106
+ ### `reactscope.tokens.json`
1107
+
1108
+ Design token file format:
1109
+
1110
+ ```json
1111
+ {
1112
+ "$schema": "https://raw.githubusercontent.com/FlatFilers/Scope/main/packages/tokens/schema.json",
1113
+ "tokens": {
1114
+ "color": {
1115
+ "primary": { "value": "#3B82F6", "$type": "color" },
1116
+ "semantic": {
1117
+ "success": { "value": "{color.green.500}", "$type": "color" }
1118
+ }
1119
+ },
1120
+ "spacing": {
1121
+ "xs": { "value": "0.25rem", "$type": "dimension" }
1122
+ }
1123
+ }
1124
+ }
1125
+ ```
1126
+
1127
+ Tokens can reference other tokens using `{path.to.token}` syntax.
1128
+
1129
+ ---
1130
+
1131
+ ## Architecture
1132
+
1133
+ The CLI is built with [Commander.js](https://github.com/tj/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.
1134
+
1135
+ ### Directory structure
1136
+
1137
+ ```
1138
+ src/
1139
+ ├── cli.ts # Entry point — creates and runs the program
1140
+ ├── program.ts # Builds the Commander command tree
1141
+ ├── browser.ts # Playwright-based browser capture
1142
+ ├── component-bundler.ts # esbuild IIFE bundling of components
1143
+ ├── manifest-commands.ts # manifest list/get/query/generate
1144
+ ├── manifest-formatter.ts # Table/JSON formatting for manifest output
1145
+ ├── render-commands.ts # render component/matrix/all
1146
+ ├── render-formatter.ts # Table/JSON/HTML/CSV formatting for render output
1147
+ ├── report-formatter.ts # Report summary formatting
1148
+ ├── tree-formatter.ts # ASCII tree formatting
1149
+ ├── site-commands.ts # site build/serve
1150
+ ├── tailwind-css.ts # Tailwind v4 CSS compilation
1151
+ ├── init/
1152
+ │ ├── index.ts # scope init — project scaffolding
1153
+ │ └── detect.ts # Framework/config auto-detection
1154
+ ├── tokens/
1155
+ │ └── commands.ts # tokens get/list/search/resolve/validate/...
1156
+ ├── instrument/
1157
+ │ ├── renders.ts # scope instrument renders
1158
+ │ ├── hooks.ts # scope instrument hooks
1159
+ │ ├── profile.ts # scope instrument profile
1160
+ │ └── tree.ts # scope instrument tree
1161
+ ├── report/
1162
+ │ ├── baseline.ts # scope report baseline
1163
+ │ ├── diff.ts # scope report diff
1164
+ │ └── pr-comment.ts # scope report pr-comment
1165
+ └── ci/
1166
+ └── commands.ts # scope ci pipeline
1167
+ ```
1168
+
1169
+ ### Key patterns
1170
+
1171
+ - **TTY detection** — All formatters check `process.stdout.isTTY` to switch between human-readable (table/tree) and machine-readable (JSON) output.
1172
+ - **Lazy singletons** — `BrowserPool` is created on demand and reused within a single command invocation.
1173
+ - **Error wrapping** — `safeRender()` wraps individual component renders so one failure does not abort batch operations.
1174
+ - **Concurrent execution** — Render commands use configurable worker pools (`--concurrency`) for parallel rendering.
1175
+ - **Configuration resolution** — Manifest and token file paths are resolved through a fallback chain: CLI flag → config file → default path.
1176
+ - **Dual export** — The package exposes both ESM and CommonJS builds; the `scope` binary points to the CJS entrypoint for maximum compatibility.