cdp-skill 1.0.17 → 1.0.18

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.
Files changed (3) hide show
  1. package/EXAMPLES.md +1041 -0
  2. package/install.js +1 -1
  3. package/package.json +2 -2
package/EXAMPLES.md ADDED
@@ -0,0 +1,1041 @@
1
+ # CDP Skill — Examples
2
+
3
+ Worked examples and JSON code blocks for every step type. See SKILL.md for the compact reference.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ ### Open a Tab (Chrome auto-launches)
10
+ ```json
11
+ {"steps":[{"newTab":"https://google.com"}]}
12
+ ```
13
+
14
+ Non-default Chrome (rare):
15
+ ```json
16
+ {"steps":[{"newTab":{"url":"https://google.com","port":9333,"headless":true}}]}
17
+ ```
18
+
19
+ Separate open and navigate:
20
+ ```json
21
+ {"steps":[{"newTab":true},{"goto":"https://google.com"}]}
22
+ ```
23
+
24
+ Stdin pipe:
25
+ ```bash
26
+ echo '{"steps":[{"newTab":"https://google.com"}]}' | node scripts/cdp-skill.js
27
+ ```
28
+
29
+ ### Tab Lifecycle
30
+ ```json
31
+ {"steps":[{"newTab":"https://google.com"}]}
32
+ ```
33
+ Response: `{"tab": "t1", "steps": [{"action": "newTab", "status": "ok", ...}]}`
34
+
35
+ Subsequent calls:
36
+ ```json
37
+ {"tab":"t1","steps":[{"click":"#btn"}]}
38
+ ```
39
+ ```json
40
+ {"tab":"t1","steps":[{"snapshot":true}]}
41
+ ```
42
+
43
+ Optional timeout:
44
+ ```json
45
+ {"tab":"t1","timeout":60000,"steps":[{"goto":"https://slow-site.com"}]}
46
+ ```
47
+
48
+ Close when done:
49
+ ```json
50
+ {"steps":[{"closeTab":"t1"}]}
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Input / Output Schema
56
+
57
+ ### Full Output Example
58
+ ```json
59
+ {
60
+ "status": "ok",
61
+ "tab": "t1",
62
+ "navigated": true,
63
+ "context": {
64
+ "url": "https://example.com/page",
65
+ "title": "Page Title",
66
+ "scroll": {"y": 0, "percent": 0},
67
+ "viewport": {"width": 1189, "height": 739}
68
+ },
69
+ "screenshot": "/tmp/cdp-skill/t1.after.png",
70
+ "fullSnapshot": "/tmp/cdp-skill/t1.after.yaml",
71
+ "viewportSnapshot": "- heading \"Title\" [level=1]\n- button \"Submit\" [ref=f0s1e1]\n...",
72
+ "steps": [{"action": "goto", "status": "ok"}]
73
+ }
74
+ ```
75
+
76
+ ### Console Output
77
+ ```json
78
+ {
79
+ "console": {
80
+ "errors": 1,
81
+ "warnings": 2,
82
+ "messages": [{"level": "error", "text": "TypeError: x is undefined", "source": "app.js:142"}]
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Failure Diagnostics
88
+ ```json
89
+ {
90
+ "steps": [{
91
+ "action": "click",
92
+ "status": "error",
93
+ "params": {"text": "Nonexistent"},
94
+ "error": "Element not found",
95
+ "context": {
96
+ "url": "https://example.com/page",
97
+ "title": "Example Page",
98
+ "scrollPosition": {"x": 0, "y": 1200, "maxY": 5000, "percentY": 24},
99
+ "visibleButtons": [
100
+ {"text": "Submit", "selector": "#submit-btn", "ref": "f0s1e4"},
101
+ {"text": "Cancel", "selector": "button.cancel", "ref": "f0s1e5"}
102
+ ],
103
+ "visibleLinks": [{"text": "Home", "href": "..."}],
104
+ "visibleErrors": ["Please fill in all required fields"],
105
+ "nearMatches": [
106
+ {"text": "Submit Form", "selector": "button.submit-form", "ref": "f0s1e12", "score": 70},
107
+ {"text": "Submit Feedback", "selector": "#feedback-submit", "ref": "f0s1e15", "score": 50}
108
+ ]
109
+ }
110
+ }],
111
+ "errors": [{"step": 1, "action": "click", "error": "Element not found"}]
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Auto-Snapshot Diff
118
+
119
+ ### Navigation (URL changed)
120
+ ```json
121
+ {
122
+ "status": "ok",
123
+ "tab": "t1",
124
+ "navigated": true,
125
+ "context": {
126
+ "url": "https://example.com/new-page",
127
+ "title": "New Page Title",
128
+ "scroll": {"y": 0, "percent": 0},
129
+ "viewport": {"width": 1189, "height": 739}
130
+ },
131
+ "screenshot": "/tmp/cdp-skill/t1.after.png",
132
+ "fullSnapshot": "/tmp/cdp-skill/t1.after.yaml",
133
+ "viewportSnapshot": "- heading \"New Page\" [level=1]\n- button \"Submit\" [ref=f0s1e1]\n...",
134
+ "steps": [{"action": "click", "status": "ok"}]
135
+ }
136
+ ```
137
+
138
+ ### Same-Page Interaction (scroll, expand, toggle)
139
+ ```json
140
+ {
141
+ "status": "ok",
142
+ "tab": "t1",
143
+ "navigated": false,
144
+ "context": {
145
+ "url": "https://example.com/page",
146
+ "scroll": {"y": 2400, "percent": 65},
147
+ "activeElement": {"tag": "INPUT", "type": "text", "selector": "#search", "value": "", "editable": true, "box": {"x": 100, "y": 50, "width": 200, "height": 32}}
148
+ },
149
+ "screenshot": "/tmp/cdp-skill/t1.after.png",
150
+ "fullSnapshot": "/tmp/cdp-skill/t1.after.yaml",
151
+ "changes": {
152
+ "summary": "Clicked. 3 added (f0s1e120, f0s1e121, f0s1e122), 1 removed (f0s1e1).",
153
+ "added": ["- link \"New Link\" [ref=f0s1e120]"],
154
+ "removed": ["- link \"Old Link\" [ref=f0s1e1]"],
155
+ "changed": [{"ref": "f0s1e5", "field": "expanded", "from": false, "to": true}]
156
+ },
157
+ "steps": [{"action": "click", "status": "ok"}]
158
+ }
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Navigation
164
+
165
+ ### goto
166
+ ```json
167
+ {"goto": "https://google.com"}
168
+ {"goto": {"url": "https://google.com", "waitUntil": "networkidle"}}
169
+ ```
170
+
171
+ ### back / forward
172
+ ```json
173
+ {"back": true}
174
+ {"forward": true}
175
+ ```
176
+ Response:
177
+ ```json
178
+ {"url": "https://example.com/previous", "title": "Previous Page"}
179
+ ```
180
+ Or when no history:
181
+ ```json
182
+ {"noHistory": true}
183
+ ```
184
+
185
+ ### reload
186
+ ```json
187
+ {"reload": true}
188
+ {"reload": {"waitUntil": "networkidle"}}
189
+ ```
190
+
191
+ ### waitForNavigation
192
+ ```json
193
+ {"waitForNavigation": true}
194
+ {"waitForNavigation": {"timeout": 5000, "waitUntil": "networkidle"}}
195
+ ```
196
+
197
+ ### switchTab — Connect to Existing Tab
198
+ By alias:
199
+ ```json
200
+ {"steps":[{"switchTab":"t2"},{"snapshot":true}]}
201
+ ```
202
+
203
+ By URL regex:
204
+ ```json
205
+ {"steps":[{"switchTab":{"url":"github\\.com"}},{"snapshot":true}]}
206
+ ```
207
+
208
+ By targetId:
209
+ ```json
210
+ {"steps":[{"switchTab":{"targetId":"ABC123..."}},{"snapshot":true}]}
211
+ ```
212
+
213
+ ### New Tab Handling — click -> detect -> switchTab
214
+ When a click opens a new tab (e.g. `target="_blank"`), the response includes `newTabs`:
215
+ ```json
216
+ {
217
+ "steps": [{"action": "click", "status": "ok", "output": {
218
+ "method": "cdp",
219
+ "newTabs": [{"targetId": "DEF456", "url": "https://other.com", "title": "Other"}]
220
+ }}]
221
+ }
222
+ ```
223
+
224
+ Then connect to the new tab:
225
+ ```json
226
+ {"steps":[{"switchTab":{"url":"other\\.com"}},{"snapshot":true}]}
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Interaction
232
+
233
+ ### click — Basic
234
+ ```json
235
+ {"click": "#submit"}
236
+ {"click": {"ref": "f0s1e4"}}
237
+ {"click": {"x": 450, "y": 200}}
238
+ ```
239
+
240
+ ### click — By visible text
241
+ ```json
242
+ {"click": {"text": "Submit"}}
243
+ {"click": {"text": "Learn more", "exact": true}}
244
+ ```
245
+
246
+ ### click — Force (bypass actionability checks)
247
+ ```json
248
+ {"click": {"selector": "#submit", "force": true}}
249
+ {"click": {"ref": "f0s1e4", "force": true}}
250
+ ```
251
+
252
+ ### click — Multi-selector fallback
253
+ ```json
254
+ {"click": {"selectors": ["#submit", "button.primary", "[type=submit]"]}}
255
+ ```
256
+ Response: `{clicked: true, matchedSelector: "#submit"}`
257
+
258
+ ### click — Diagnostics (element covered)
259
+ ```json
260
+ {
261
+ "error": "Element is covered by another element",
262
+ "interceptedBy": {
263
+ "tagName": "div",
264
+ "id": "modal-overlay",
265
+ "className": "overlay active",
266
+ "textContent": "Loading..."
267
+ }
268
+ }
269
+ ```
270
+
271
+ ### fill — Targeted
272
+ ```json
273
+ {"fill": {"selector": "#email", "value": "user@example.com"}}
274
+ {"fill": {"ref": "f0s1e3", "value": "text"}}
275
+ {"fill": {"label": "Email address", "value": "test@example.com"}}
276
+ ```
277
+
278
+ ### fill — Currently focused element
279
+ ```json
280
+ {"fill": "search query"}
281
+ {"fill": {"value": "text", "clear": false}}
282
+ ```
283
+ Response:
284
+ ```json
285
+ {"filled": true, "tag": "INPUT", "type": "text", "selector": "#search", "valueBefore": "", "valueAfter": "search query", "mode": "focused"}
286
+ ```
287
+
288
+ ### fill — Batch (multiple fields)
289
+ ```json
290
+ {"fill": {"#firstName": "John", "#lastName": "Doe"}}
291
+ {"fill": {"fields": {"#firstName": "John"}, "react": true}}
292
+ ```
293
+ Response:
294
+ ```json
295
+ {"total": 2, "filled": 2, "failed": 0, "results": [{"selector": "#firstName", "status": "ok", "value": "John"}], "mode": "batch"}
296
+ ```
297
+
298
+ ### fill — Autocomplete pattern
299
+ ```json
300
+ {"steps": [
301
+ {"fill": {"selector": "#city", "value": "San Fra"}},
302
+ {"wait": ".autocomplete-dropdown"},
303
+ {"click": {"text": "San Francisco"}}
304
+ ]}
305
+ ```
306
+
307
+ ### press
308
+ ```json
309
+ {"press": "Enter"}
310
+ {"press": "Control+a"}
311
+ {"press": "Meta+Shift+Enter"}
312
+ ```
313
+
314
+ ### hover — Basic
315
+ ```json
316
+ {"hover": "#menu"}
317
+ {"hover": {"selector": "#tooltip", "duration": 500}}
318
+ ```
319
+
320
+ ### hover — With result capture
321
+ ```json
322
+ {"hover": {"selector": "#menu", "captureResult": true}}
323
+ ```
324
+ Response:
325
+ ```json
326
+ {
327
+ "hovered": true,
328
+ "capturedResult": {
329
+ "visibleElements": [
330
+ {"selector": ".tooltip", "text": "Click to edit", "visible": true},
331
+ {"selector": ".dropdown-menu", "text": "Menu items", "visible": true}
332
+ ]
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### drag
338
+ ```json
339
+ {"drag": {"source": "#draggable", "target": "#dropzone"}}
340
+ {"drag": {"source": {"ref": "f0s1e1"}, "target": {"ref": "f0s1e5"}}}
341
+ {"drag": {"source": {"ref": "f0s1e1", "offsetX": 20}, "target": {"ref": "f0s1e5", "offsetY": -10}}}
342
+ {"drag": {"source": {"x": 100, "y": 100}, "target": {"x": 300, "y": 200}}}
343
+ {"drag": {"source": "#item", "target": "#container", "steps": 20, "delay": 10}}
344
+ ```
345
+
346
+ ### drag — With method
347
+ ```json
348
+ {"drag": {"source": "#item", "target": "#zone", "method": "mouse"}}
349
+ {"drag": {"source": "#item", "target": "#zone", "method": "html5"}}
350
+ ```
351
+ Response:
352
+ ```json
353
+ {"dragged": true, "method": "mouse-events", "source": {"x": 100, "y": 100}, "target": {"x": 300, "y": 200}, "steps": 10}
354
+ ```
355
+
356
+ ### selectOption
357
+ ```json
358
+ {"selectOption": {"selector": "#country", "value": "US"}}
359
+ {"selectOption": {"selector": "#country", "label": "United States"}}
360
+ {"selectOption": {"selector": "#country", "index": 2}}
361
+ {"selectOption": {"selector": "#colors", "values": ["red", "blue"]}}
362
+ ```
363
+
364
+ ### selectText
365
+ ```json
366
+ {"selectText": "#input"}
367
+ {"selectText": {"selector": "#input", "start": 0, "end": 5}}
368
+ ```
369
+ Response:
370
+ ```json
371
+ {"selected": true, "selector": "#input"}
372
+ ```
373
+
374
+ ### upload
375
+ ```json
376
+ {"upload": "/path/to/file.pdf"}
377
+ {"upload": ["/path/to/a.txt", "/path/to/b.png"]}
378
+ {"upload": {"selector": "#file-input", "file": "/path/to/doc.pdf"}}
379
+ {"upload": {"selector": "#file-input", "files": ["/path/to/a.txt", "/path/to/b.png"]}}
380
+ {"upload": {"ref": "f0s1e3", "files": ["/path/to/photo.jpg"]}}
381
+ ```
382
+ Response:
383
+ ```json
384
+ {"uploaded": true, "files": ["/path/to/file.pdf"], "accept": ".pdf,.doc", "multiple": false, "target": "input[type=\"file\"]"}
385
+ ```
386
+
387
+ ### submit
388
+ ```json
389
+ {"submit": "form"}
390
+ {"submit": {"selector": "#login-form", "reportValidity": true}}
391
+ ```
392
+ Response:
393
+ ```json
394
+ {"submitted": true, "valid": true, "errors": []}
395
+ ```
396
+
397
+ ---
398
+
399
+ ## Query & Extraction
400
+
401
+ ### snapshot
402
+ ```json
403
+ {"snapshot": true}
404
+ {"snapshot": {"root": "#container", "maxElements": 500}}
405
+ {"snapshot": {"root": "role=main", "includeText": true}}
406
+ {"snapshot": {"includeFrames": true}}
407
+ {"snapshot": {"pierceShadow": true}}
408
+ {"snapshot": {"detail": "interactive"}}
409
+ {"snapshot": {"inlineLimit": 28000}}
410
+ {"snapshot": {"since": "f0s1"}}
411
+ ```
412
+
413
+ YAML output example:
414
+ ```yaml
415
+ - navigation:
416
+ - link "Home" [ref=f0s1e1]
417
+ - main:
418
+ - heading "Welcome" [level=1]
419
+ - textbox "Email" [required] [invalid] [name=email] [ref=f0s1e3]
420
+ - button "Submit" [ref=f0s1e4]
421
+ ```
422
+
423
+ ### Snapshot Caching (since)
424
+ ```json
425
+ {"snapshot": {"since": "f0s1"}}
426
+ ```
427
+
428
+ Unchanged:
429
+ ```json
430
+ {"unchanged": true, "snapshotId": "f0s1", "message": "Page unchanged since f0s1"}
431
+ ```
432
+
433
+ Changed:
434
+ ```json
435
+ {"snapshotId": "f0s2", "yaml": "- button \"Login\" [ref=f0s2e1]\n...", "refs": {}}
436
+ ```
437
+
438
+ ### Detail: summary
439
+ ```json
440
+ {"snapshot": {"detail": "summary"}}
441
+ ```
442
+ ```yaml
443
+ # Snapshot Summary
444
+ # Total elements: 1847
445
+ # Interactive elements: 67
446
+ # Viewport elements: 23
447
+
448
+ landmarks:
449
+ - role: main
450
+ interactiveCount: 47
451
+ children: [form, navigation, article]
452
+ ```
453
+
454
+ ### Large snapshot (auto-file)
455
+ ```json
456
+ {
457
+ "yaml": null,
458
+ "artifacts": {"snapshot": "/tmp/cdp-skill/t1.snapshot.yaml"},
459
+ "snapshotSize": 125000,
460
+ "truncatedInline": true
461
+ }
462
+ ```
463
+
464
+ ### snapshotSearch
465
+ ```json
466
+ {"snapshotSearch": {"text": "Submit"}}
467
+ {"snapshotSearch": {"text": "Submit", "role": "button"}}
468
+ {"snapshotSearch": {"pattern": "^Save.*draft$", "role": "button"}}
469
+ {"snapshotSearch": {"role": "button", "limit": 20}}
470
+ {"snapshotSearch": {"text": "Edit", "near": {"x": 500, "y": 300, "radius": 100}}}
471
+ ```
472
+ Response:
473
+ ```json
474
+ {
475
+ "matches": [
476
+ {"path": "main > form > button", "ref": "f0s1e47", "name": "Submit Form", "role": "button"},
477
+ {"path": "dialog > button", "ref": "f0s1e89", "name": "Submit Feedback", "role": "button"}
478
+ ],
479
+ "matchCount": 2,
480
+ "searchedElements": 1847
481
+ }
482
+ ```
483
+
484
+ ### query — By CSS
485
+ ```json
486
+ {"query": "h1"}
487
+ {"query": {"selector": "a", "limit": 5, "output": "href"}}
488
+ {"query": {"selector": "div", "output": ["text", "href"]}}
489
+ {"query": {"selector": "button", "output": {"attribute": "data-id"}}}
490
+ ```
491
+
492
+ ### query — By ARIA role
493
+ ```json
494
+ {"query": {"role": "button"}}
495
+ {"query": {"role": "button", "name": "Submit"}}
496
+ {"query": {"role": "heading", "level": 2}}
497
+ {"query": {"role": ["button", "link"]}}
498
+ ```
499
+
500
+ ### query — Count only
501
+ ```json
502
+ {"query": {"selector": "li.item", "count": true}}
503
+ ```
504
+ Response:
505
+ ```json
506
+ {"selector": "li.item", "total": 42}
507
+ ```
508
+
509
+ ### queryAll
510
+ ```json
511
+ {"queryAll": {"title": "h1", "links": "a", "buttons": {"role": "button"}}}
512
+ ```
513
+
514
+ ### get — Text extraction (default)
515
+ ```json
516
+ {"get": "#content"}
517
+ {"get": {"selector": "#content"}}
518
+ {"get": {"selector": "#content", "mode": "text"}}
519
+ ```
520
+ Response:
521
+ ```json
522
+ {"text": "Extracted content text", "mode": "text"}
523
+ ```
524
+
525
+ ### get — HTML extraction
526
+ ```json
527
+ {"get": {"selector": "#content", "mode": "html"}}
528
+ ```
529
+ Response:
530
+ ```json
531
+ {"html": "<div>...</div>", "tagName": "DIV", "length": 1245, "mode": "html"}
532
+ ```
533
+
534
+ ### get — Form value extraction
535
+ ```json
536
+ {"get": {"selector": "#form", "mode": "value"}}
537
+ ```
538
+ Response:
539
+ ```json
540
+ {
541
+ "selector": "#checkout-form",
542
+ "action": "/api/checkout",
543
+ "method": "POST",
544
+ "fields": [
545
+ {"name": "email", "type": "email", "value": "user@example.com", "label": "Email Address", "required": true, "valid": true}
546
+ ],
547
+ "valid": true,
548
+ "fieldCount": 3,
549
+ "mode": "value"
550
+ }
551
+ ```
552
+
553
+ ### get — Bounding box extraction
554
+ ```json
555
+ {"get": {"selector": "#element", "mode": "box"}}
556
+ {"get": {"ref": "f0s1e1", "mode": "box"}}
557
+ ```
558
+ Response:
559
+ ```json
560
+ {"x": 100, "y": 200, "width": 150, "height": 40, "center": {"x": 175, "y": 220}, "mode": "box"}
561
+ ```
562
+
563
+ ### get — Attributes extraction
564
+ ```json
565
+ {"get": {"selector": "#link", "mode": "attributes"}}
566
+ ```
567
+ Response:
568
+ ```json
569
+ {"attributes": {"href": "/page", "class": "link", "id": "main-link"}, "mode": "attributes"}
570
+ ```
571
+
572
+ ### get — Table extraction (auto-detected)
573
+ ```json
574
+ {"get": "table.results"}
575
+ {"get": {"selector": "#data-grid", "type": "table"}}
576
+ ```
577
+ Response:
578
+ ```json
579
+ {
580
+ "type": "table",
581
+ "headers": ["Name", "Email", "Status"],
582
+ "rows": [
583
+ ["John Doe", "john@example.com", "Active"],
584
+ ["Jane Smith", "jane@example.com", "Pending"]
585
+ ],
586
+ "rowCount": 2,
587
+ "mode": "text"
588
+ }
589
+ ```
590
+
591
+ ### get — List extraction (auto-detected)
592
+ ```json
593
+ {"get": "ul.items"}
594
+ {"get": {"selector": "#nav-links", "type": "list"}}
595
+ ```
596
+ Response:
597
+ ```json
598
+ {
599
+ "type": "list",
600
+ "items": ["Home", "About", "Contact"],
601
+ "itemCount": 3,
602
+ "mode": "text"
603
+ }
604
+ ```
605
+
606
+ ### getUrl
607
+ ```json
608
+ {"getUrl": true}
609
+ ```
610
+ Response:
611
+ ```json
612
+ {"url": "https://example.com/page"}
613
+ ```
614
+
615
+ ### getTitle
616
+ ```json
617
+ {"getTitle": true}
618
+ ```
619
+ Response:
620
+ ```json
621
+ {"title": "Page Title"}
622
+ ```
623
+
624
+ ### inspect
625
+ ```json
626
+ {"inspect": true}
627
+ {"inspect": {"selectors": [".item"], "limit": 3}}
628
+ ```
629
+ Response:
630
+ ```json
631
+ {
632
+ "title": "Page Title",
633
+ "url": "https://example.com",
634
+ "elements": {
635
+ "a": 12,
636
+ "button": 5,
637
+ "input": 3,
638
+ "textarea": 1,
639
+ "select": 0,
640
+ "h1": 1,
641
+ "h2": 3,
642
+ "h3": 0,
643
+ "img": 8,
644
+ "form": 1
645
+ }
646
+ }
647
+ ```
648
+
649
+ ### elementsAt — Single point
650
+ ```json
651
+ {"elementsAt": {"x": 600, "y": 200}}
652
+ ```
653
+ Response:
654
+ ```json
655
+ {
656
+ "ref": "f0s1e5",
657
+ "existing": false,
658
+ "tag": "BUTTON",
659
+ "selector": "#submit-btn",
660
+ "clickable": true,
661
+ "role": "button",
662
+ "name": "Submit",
663
+ "box": {"x": 580, "y": 190, "width": 100, "height": 40}
664
+ }
665
+ ```
666
+
667
+ ### elementsAt — Batch
668
+ ```json
669
+ {"elementsAt": [{"x": 100, "y": 200}, {"x": 300, "y": 400}, {"x": 500, "y": 150}]}
670
+ ```
671
+ Response:
672
+ ```json
673
+ {
674
+ "count": 3,
675
+ "elements": [
676
+ {"x": 100, "y": 200, "ref": "f0s1e1", "tag": "BUTTON", "selector": "#btn1", "clickable": true},
677
+ {"x": 300, "y": 400, "ref": "f0s1e2", "tag": "DIV", "selector": "div.card", "clickable": false},
678
+ {"x": 500, "y": 150, "error": "No element at this coordinate"}
679
+ ]
680
+ }
681
+ ```
682
+
683
+ ### elementsAt — Nearby search
684
+ ```json
685
+ {"elementsAt": {"x": 400, "y": 300, "radius": 100}}
686
+ {"elementsAt": {"x": 400, "y": 300, "radius": 75, "limit": 10}}
687
+ ```
688
+ Response:
689
+ ```json
690
+ {
691
+ "center": {"x": 400, "y": 300},
692
+ "radius": 100,
693
+ "count": 5,
694
+ "elements": [
695
+ {"ref": "f0s1e1", "tag": "BUTTON", "selector": "#nearby-btn", "clickable": true, "distance": 12},
696
+ {"ref": "f0s1e2", "tag": "SPAN", "selector": "span.label", "clickable": false, "distance": 28}
697
+ ]
698
+ }
699
+ ```
700
+
701
+ ---
702
+
703
+ ## Waiting & Polling
704
+
705
+ ### wait — Element
706
+ ```json
707
+ {"wait": "#content"}
708
+ {"wait": {"selector": "#loading", "hidden": true}}
709
+ {"wait": {"selector": ".item", "minCount": 10}}
710
+ ```
711
+
712
+ ### wait — Text
713
+ ```json
714
+ {"wait": {"text": "Welcome"}}
715
+ {"wait": {"text": "welcome", "caseSensitive": false}}
716
+ {"wait": {"textRegex": "Order #[A-Z0-9]+"}}
717
+ ```
718
+
719
+ ### wait — URL
720
+ ```json
721
+ {"wait": {"urlContains": "/success"}}
722
+ ```
723
+
724
+ ### sleep
725
+ ```json
726
+ {"sleep": 2000}
727
+ ```
728
+
729
+ ### poll
730
+ ```json
731
+ {"poll": "() => document.querySelector('.loaded') !== null"}
732
+ {"poll": "() => document.readyState === 'complete'"}
733
+ ```
734
+
735
+ Object form:
736
+ ```json
737
+ {"poll": {"fn": "() => !document.querySelector('.spinner') && document.querySelector('.results')?.children.length > 0", "interval": 100, "timeout": 10000}}
738
+ ```
739
+ Response:
740
+ ```json
741
+ {"resolved": true, "value": true, "elapsed": 2340}
742
+ ```
743
+
744
+ ---
745
+
746
+ ## Scripting
747
+
748
+ ### pageFunction
749
+ ```json
750
+ {"pageFunction": "() => document.title"}
751
+ {"pageFunction": "document.title"}
752
+ {"pageFunction": "(document) => [...document.querySelectorAll('.item')].map(i => ({text: i.textContent, href: i.href}))"}
753
+ ```
754
+
755
+ Object form:
756
+ ```json
757
+ {"pageFunction": {"fn": "(refs) => refs.size", "refs": true}}
758
+ {"pageFunction": {"fn": "() => document.querySelectorAll('button').length", "timeout": 5000}}
759
+ {"pageFunction": {"expression": "fetch('/api').then(r=>r.json())"}}
760
+ ```
761
+
762
+ ### Typed Return Values
763
+ - Numbers: `{type: "number", repr: "Infinity|NaN|-Infinity"}`
764
+ - Date: `{type: "Date", value: "ISO string", timestamp: N}`
765
+ - Map: `{type: "Map", size: N, entries: [...]}`
766
+ - Set: `{type: "Set", size: N, values: [...]}`
767
+ - Element: `{type: "Element", tagName, id, className, textContent, isConnected}`
768
+ - NodeList: `{type: "NodeList", length: N, items: [...]}`
769
+
770
+ ### assert
771
+ ```json
772
+ {"assert": {"url": {"contains": "/success"}}}
773
+ {"assert": {"url": {"equals": "https://example.com/done"}}}
774
+ {"assert": {"url": {"startsWith": "https://"}}}
775
+ {"assert": {"url": {"matches": "^https://.*\\.example\\.com"}}}
776
+ {"assert": {"text": "Welcome"}}
777
+ {"assert": {"selector": "h1", "text": "Title", "caseSensitive": false}}
778
+ ```
779
+ Response:
780
+ ```json
781
+ {
782
+ "passed": true,
783
+ "assertions": [
784
+ {"type": "url", "actual": "https://example.com/success", "expected": {"contains": "/success"}, "passed": true}
785
+ ]
786
+ }
787
+ ```
788
+
789
+ ---
790
+
791
+ ## Page Control
792
+
793
+ ### scroll
794
+ ```json
795
+ {"scroll": "top"}
796
+ {"scroll": "bottom"}
797
+ {"scroll": "up"}
798
+ {"scroll": "down"}
799
+ {"scroll": "#element"}
800
+ {"scroll": "f0s1e4"}
801
+ {"scroll": {"deltaY": 500}}
802
+ {"scroll": {"x": 0, "y": 1000}}
803
+ ```
804
+ Response:
805
+ ```json
806
+ {"scrollX": 0, "scrollY": 1000}
807
+ ```
808
+
809
+ ### frame — List frames
810
+ ```json
811
+ {"frame": {"list": true}}
812
+ ```
813
+
814
+ ### frame — Switch to frame
815
+ ```json
816
+ {"frame": "iframe#content"}
817
+ {"frame": 0}
818
+ {"frame": {"name": "myFrame"}}
819
+ ```
820
+
821
+ ### frame — Return to main frame
822
+ ```json
823
+ {"frame": "top"}
824
+ ```
825
+
826
+ ### frame — Workflow (switch -> interact -> return)
827
+ ```json
828
+ {"steps": [
829
+ {"frame": "iframe#editor"},
830
+ {"fill": {"selector": "#input", "value": "Hello"}},
831
+ {"click": "#save"},
832
+ {"frame": "top"}
833
+ ]}
834
+ ```
835
+
836
+ ### viewport
837
+ ```json
838
+ {"viewport": "iphone-14"}
839
+ {"viewport": "pixel-7"}
840
+ {"viewport": "macbook-pro-14"}
841
+ {"viewport": "desktop-hd"}
842
+ {"viewport": {"width": 1280, "height": 720}}
843
+ {"viewport": {"width": 375, "height": 667, "mobile": true, "hasTouch": true, "isLandscape": true}}
844
+ ```
845
+ Response:
846
+ ```json
847
+ {"width": 390, "height": 844, "deviceScaleFactor": 3}
848
+ ```
849
+
850
+ ---
851
+
852
+ ## Browser & Tabs
853
+
854
+ ### chromeStatus
855
+ ```json
856
+ {"steps":[{"chromeStatus":true}]}
857
+ {"steps":[{"chromeStatus":{"port":9333,"headless":true}}]}
858
+ {"steps":[{"chromeStatus":{"autoLaunch":false}}]}
859
+ ```
860
+ Response:
861
+ ```json
862
+ {
863
+ "status": "ok",
864
+ "chrome": {
865
+ "running": true,
866
+ "launched": true,
867
+ "version": "Chrome/120.0.6099.109",
868
+ "port": 9222,
869
+ "tabs": [{"targetId": "ABC123", "url": "about:blank", "title": ""}]
870
+ }
871
+ }
872
+ ```
873
+
874
+ ### listTabs
875
+ ```json
876
+ {"steps":[{"listTabs":true}]}
877
+ ```
878
+ Response:
879
+ ```json
880
+ {"count": 2, "tabs": [{"targetId": "ABC123", "url": "https://google.com", "title": "Google", "alias": "t1"}]}
881
+ ```
882
+
883
+ ### closeTab
884
+ ```json
885
+ {"closeTab": "t1"}
886
+ ```
887
+
888
+ ### cookies
889
+ ```json
890
+ {"cookies": {"get": true}}
891
+ {"cookies": {"get": ["https://other-domain.com"], "name": "session_id"}}
892
+ {"cookies": {"set": [{"name": "token", "value": "abc", "domain": "example.com", "expires": "7d"}]}}
893
+ {"cookies": {"delete": "session_id"}}
894
+ {"cookies": {"delete": "session_id", "domain": "example.com"}}
895
+ {"cookies": {"clear": true}}
896
+ {"cookies": {"clear": true, "domain": "example.com"}}
897
+ ```
898
+
899
+ ### console
900
+ ```json
901
+ {"console": true}
902
+ {"console": {"level": "error", "limit": 20, "stackTrace": true}}
903
+ {"console": {"type": "exception", "limit": 10}}
904
+ {"console": {"clear": true}}
905
+ ```
906
+ Response:
907
+ ```json
908
+ {
909
+ "total": 5,
910
+ "showing": 5,
911
+ "messages": [
912
+ {"level": "error", "text": "TypeError: x is undefined", "type": "console", "url": "app.js", "line": 42, "timestamp": 1700000000}
913
+ ]
914
+ }
915
+ ```
916
+
917
+ ### pdf
918
+ ```json
919
+ {"pdf": "report.pdf"}
920
+ {"pdf": {"path": "/absolute/path/report.pdf", "landscape": true, "printBackground": true}}
921
+ {"pdf": {"path": "element.pdf", "selector": "#chart"}}
922
+ ```
923
+
924
+ ---
925
+
926
+ ## Action Hooks
927
+
928
+ ### readyWhen
929
+ ```json
930
+ {"click": {"ref": "f0s1e5", "readyWhen": "() => !document.querySelector('.loading')"}}
931
+ {"fill": {"selector": "#email", "value": "test@test.com", "readyWhen": "() => document.querySelector('#email').offsetHeight > 0"}}
932
+ ```
933
+
934
+ ### settledWhen
935
+ ```json
936
+ {"click": {"ref": "f0s1e5", "settledWhen": "() => document.querySelector('.results')?.children.length > 0"}}
937
+ {"click": {"selector": "#nav-link", "settledWhen": "() => location.href.includes('/results')"}}
938
+ ```
939
+
940
+ ### observe
941
+ ```json
942
+ {"click": {"ref": "f0s1e5", "observe": "() => ({url: location.href, count: document.querySelectorAll('.item').length})"}}
943
+ ```
944
+
945
+ ### Combined hooks
946
+ ```json
947
+ {"click": {
948
+ "ref": "f0s1e5",
949
+ "readyWhen": "() => !document.querySelector('.loading')",
950
+ "settledWhen": "() => document.querySelectorAll('.result').length > 0",
951
+ "observe": "() => document.querySelectorAll('.result').length"
952
+ }}
953
+ ```
954
+
955
+ ---
956
+
957
+ ## Site Profiles
958
+
959
+ ### writeSiteProfile
960
+ ```json
961
+ {"writeSiteProfile": {"domain": "github.com", "content": "# github.com\nUpdated: 2025-01-15\n\n## Environment\n- React SPA\n..."}}
962
+ ```
963
+
964
+ ### readSiteProfile
965
+ ```json
966
+ {"readSiteProfile": "github.com"}
967
+ {"readSiteProfile": {"domain": "github.com"}}
968
+ ```
969
+ Response (found):
970
+ ```json
971
+ {"found": true, "domain": "github.com", "content": "# github.com\n..."}
972
+ ```
973
+ Response (not found):
974
+ ```json
975
+ {"found": false, "domain": "github.com"}
976
+ ```
977
+
978
+ ### Full Profile Template
979
+ ```markdown
980
+ # example.com
981
+ Updated: 2025-01-15 | Fingerprint: React 18, Next.js
982
+
983
+ ## Environment
984
+ - React 18.x, Next.js (SSR)
985
+ - SPA with pushState navigation
986
+ - Has <main> element: #__next > main
987
+
988
+ ## Quirks
989
+ - Turbo intercepts link clicks — use settledWhen with URL check
990
+ - File tree uses virtualization — only visible rows in DOM
991
+
992
+ ## Strategies
993
+ ### fill (React controlled inputs)
994
+ Use `react: true` option or settledWhen to verify input accepted.
995
+
996
+ ## Regions
997
+ - mainContent: `main, [role="main"]`
998
+ - navigation: `.nav-bar`
999
+
1000
+ ## Recipes
1001
+ ### Login
1002
+ {"steps": [
1003
+ {"fill": {"selector": "#username", "value": "{{user}}"}},
1004
+ {"fill": {"selector": "#password", "value": "{{pass}}"}},
1005
+ {"click": "#login"},
1006
+ {"wait": {"urlContains": "/dashboard"}}
1007
+ ]}
1008
+ ```
1009
+
1010
+ ---
1011
+
1012
+ ## Optional Steps
1013
+
1014
+ ```json
1015
+ {"click": "#maybe-exists", "optional": true}
1016
+ ```
1017
+ Response when element not found:
1018
+ ```json
1019
+ {"action": "click", "status": "skipped", "error": "Element not found"}
1020
+ ```
1021
+
1022
+ ---
1023
+
1024
+ ## Shell Tips
1025
+
1026
+ ### Heredoc (avoids quote escaping)
1027
+ ```bash
1028
+ node scripts/cdp-skill.js <<'EOF'
1029
+ {"steps":[{"pageFunction":"document.querySelectorAll('button').length"}]}
1030
+ EOF
1031
+ ```
1032
+
1033
+ ### Pipe from file
1034
+ ```bash
1035
+ cat steps.json | node scripts/cdp-skill.js
1036
+ ```
1037
+
1038
+ ### Debug logging
1039
+ ```bash
1040
+ node scripts/cdp-skill.js --debug '{"steps":[{"goto":"https://google.com"}]}'
1041
+ ```
package/install.js CHANGED
@@ -20,7 +20,7 @@ const targets = [
20
20
  const filesToCopy = [
21
21
  'SKILL.md',
22
22
  'EXAMPLES.md',
23
- 'src',
23
+ 'scripts',
24
24
  ];
25
25
 
26
26
  function ensureParentDir(targetPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-skill",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "Browser automation skill using Chrome DevTools Protocol for Claude Code and AI agents",
5
5
  "type": "module",
6
6
  "main": "scripts/index.js",
@@ -24,7 +24,7 @@
24
24
  "install.js",
25
25
  "uninstall.js",
26
26
  "SKILL.md",
27
- "scripts/",
27
+ "EXAMPLES.md",
28
28
  "scripts/"
29
29
  ],
30
30
  "repository": {