Skill v1.0.3
Automated scan100/1002 files
version: "1.0.3" name: chrome-devtools description: Use when the user asks to "take a screenshot of a website", "navigate to a URL", "fill a form in the browser", "interact with Chrome", or when a chrome automation task is needed. user-invocable: true
Chrome DevTools CLI
A CLI that talks directly to your running Chrome via the DevTools Protocol.
Prerequisites
Chrome must have remote debugging enabled:
- Open Chrome
- Go to
chrome://inspect/#remote-debugging - Enable the remote debugging server
The CLI auto-connects — no URL needed. A daemon is spawned on first invocation and reused across commands (5-minute idle timeout).
⚠️ Critical: How Page Targeting Works
Targets are NOT arbitrary strings. You cannot use --target main, --target page1, or any made-up name.
Targets are friendly word-pair names (like warm-squid, pink-hen) that the CLI derives from Chrome's internal target IDs. You get them from command output — never invent them.
Names are stable for the lifetime of a tab — navigating within the same tab keeps the same target name; closing and reopening the tab gives a new name.
The Correct Workflow
Step 1: Run `list-pages` to see what's open and get target names
chrome-devtools list-pages
Output:
[0] (warm-squid) Your Repositories — https://github.com/aeroxy[1] (pink-hen) Gmail — https://mail.google.com[2] (hazy-vole) Example — https://example.com
Step 2: Use the friendly name from the output in subsequent commands
chrome-devtools --target warm-squid navigate https://example.comchrome-devtools --target pink-hen screenshot --output screenshot.png
Alternative: Use `--page <index>` for numeric indexing (0-based)
chrome-devtools --page 0 navigate https://example.com
If both `--target` and `--page` are omitted, the command runs on page 0 (leftmost tab). This is fine for single-tab workflows but should generally be avoided — always pin to a known page.
Core Capabilities
- Navigation:
navigate,navigate --back,navigate --forward,navigate --reload - Page management:
list-pages,new-page,close-page,select-page - Extraction:
screenshot,snapshot(accessibility tree),evaluate(JavaScript),read-page(page content as markdown),run-script(run local JS file),adapter(run site adapter) - Interaction:
click,fill,type-text,press-key,hover,click-at - Emulation:
emulate(viewport, mobile, geolocation, URL blocking) - Inspection:
console(logs),network(requests),sw-logs(extension service workers) - Third-party tools:
list-3p-tools,execute-3p-tool(tools exposed bywindow.__dtmcp) - Synchronization:
wait-for(wait for text on page) - Daemon control:
kill-daemon
Standard Patterns
Pattern 1: Navigate and Interact
navigate and new-page print the target name at the end — capture it to pin subsequent commands.
# 1. List pages to find targetchrome-devtools list-pages# 2. Navigate (target name shown at end of output)chrome-devtools --target warm-squid navigate https://example.com# stdout: Navigated to https://example.com# stderr: [navigated to: https://example.com]# stderr: [target:warm-squid]# 3. Pin all subsequent commands to this pagechrome-devtools --target warm-squid screenshot --output page.pngchrome-devtools --target warm-squid evaluate "document.title"# 4. Open a new tab — capture the NEW target name from outputchrome-devtools new-page https://github.com# stdout: Opened: https://github.com# stderr: [target:icy-goat] ← new tab, new targetchrome-devtools --target icy-goat snapshot
Note: The [navigated to: ...] and [target:...] lines go to stderr, not stdout. The stdout contains only the main command output ("Navigated to …", "Opened: …").
Pattern 2: Emulation (Viewport & Geolocation)
Overrides are per-tab: each page keeps its own viewport/geolocation/URL-blocks, persisting across navigation within that tab and isolated from other tabs (until cleared, the tab closes, or the daemon exits). emulate with no flags shows the active tab's state.
# Set viewport and geolocationchrome-devtools --target warm-squid emulate --viewport 1920x1080 --geolocation 40.71,-74.00# Emulate mobile devicechrome-devtools --target warm-squid emulate --viewport 375x812 --mobile --device-scale-factor 3# Navigate with emulation (emulation applied before URL loads)chrome-devtools --target warm-squid navigate https://example.com --viewport 375x812 --mobile# Open new tab with emulationchrome-devtools new-page https://example.com --viewport 375x812# Show current overrideschrome-devtools --target warm-squid emulate# Output: No emulation overrides active. (or lists current blocks/viewport/etc.)# Clear emulation overrideschrome-devtools --target warm-squid emulate --clear-all # clears everythingchrome-devtools --target warm-squid emulate --clear-viewport # clears viewport onlychrome-devtools --target warm-squid emulate --clear-geolocation
Pattern 3: URL Blocking (Network Debugging)
Block URL patterns using simple * wildcards: *.png (all PNG files), cdn.example.com/* (a domain path), *analytics* (any URL containing "analytics"). Patterns persist in the daemon until cleared.
Scope: blocking applies to subresources the page loads (images, scripts, fetch/XHR, stylesheets, CDN, trackers). It does not block the top-level navigation document itself — e.g.--block-url "*example.com*"thennavigate https://example.comstill loads the page, but anyexample.comsubresources are blocked. This is a ChromeNetwork.setBlockedURLslimitation, not a CLI bug.
# Add block patternschrome-devtools --target warm-squid emulate --block-url "*.png"chrome-devtools --target warm-squid emulate --block-url "*.ico" --block-url "*.svg"# Block while navigating (inline)chrome-devtools --target warm-squid --block-url "*.png" navigate https://example.com# Show current blockschrome-devtools --target warm-squid emulate# Output: Blocked URLs:# *.png# *.ico# *.svg# Remove a specific pattern from the blocklistchrome-devtools --target warm-squid emulate --unblock-url "*.png"# (Note: --unblock-url REMOVES that pattern from the blocklist. There is no separate "allowlist".)# Clear all blockschrome-devtools --target warm-squid emulate --clear-blockschrome-devtools --target warm-squid emulate --clear-all
Pattern 4: Form Interaction
Two ways to fill inputs — choose based on what the site expects:
fillsets the value directly viaelement.value = .... Fast, no key events. Works for text inputs, textareas,<select>, checkboxes, radio buttons. Often breaks React/Vue apps because these frameworks rely on real input events.type-textdispatches individual keyboard events. Slower but triggers all theinput,compositionstart/end, etc. events that frameworks listen for. Use this when `fill` seems to "not work" on interactive frameworks.
# Click an element by CSS selectorchrome-devtools --target warm-squid click "button.submit"# Click at viewport-relative coordinates (0,0 is top-left of the visible viewport)chrome-devtools --target warm-squid click-at 100 200# Fill a text input (fast, no key events)chrome-devtools --target warm-squid fill "input.search" "search query"# Type text one char at a time (use for React/Vue/form-validation sites)chrome-devtools --target warm-squid type-text "search query" --submit-key Enter# Press a key or key combo. Examples: Enter, Tab, Escape, ArrowDown,# Control+A, Meta+C, Shift+Tab, Backspace, Space.chrome-devtools --target warm-squid press-key Enterchrome-devtools --target warm-squid press-key Control+A# Hover over an elementchrome-devtools --target warm-squid hover ".menu-item"# Wait for text to appear (default timeout: 30 seconds = 30000 ms)chrome-devtools --target warm-squid wait-for "Results" --timeout 10000
Pattern 5: Console & Network Inspection
The daemon maintains a persistent session for the current page that continuously collects network and console events across commands.
console and network return accumulated events and clear the buffer (drain). A second call immediately after returns empty unless new events arrived. Use this for inspecting what happened; use --duration for live monitoring.
# Navigate (generates network + console events)chrome-devtools --target warm-squid navigate https://example.com# Drain accumulated network requests (instant, non-blocking)chrome-devtools --target warm-squid network# Drain console messageschrome-devtools --target warm-squid console# Filter by console type: log, warning, error, info, debug, exceptionchrome-devtools --target warm-squid console --type error --type warning# Filter network by resource type (case-sensitive): Document, Script, Stylesheet,# Image, Font, XHR, Fetch, Manifest, Media, WebSocket, Otherchrome-devtools --target warm-squid network --type Fetch --type XHR# Live monitoring: blocks for N milliseconds, collecting events during that windowchrome-devtools --target warm-squid console --duration 5000chrome-devtools --target warm-squid network --duration 3000# Drain instantly (default): returns whatever has accumulated so farchrome-devtools --target warm-squid console --duration 0
Valid `--type` values for `network`: Document, Script, Stylesheet, Image, Media, Font, WebSocket, Manifest, XHR, Fetch, Other.
Pattern 6: JavaScript Evaluation
evaluate runs a JavaScript expression and returns the result. It automatically awaits promises and serializes the return value (objects become JSON, primitives come back as plain text).
# Get the page titlechrome-devtools --target warm-squid evaluate "document.title"# Await a promise automaticallychrome-devtools --target warm-squid evaluate "fetch('/api/user').then(r => r.json())"# Get a value as JSON (forces JSON serialization)chrome-devtools --target warm-squid --json evaluate "performance.navigation"# Handle a JS dialog (alert, confirm, prompt). Without --dialog-action, eval hangs.chrome-devtools --target warm-squid evaluate "alert('hi')" --dialog-action acceptchrome-devtools --target warm-squid evaluate "confirm('sure?')" --dialog-action dismisschrome-devtools --target warm-squid evaluate "prompt('name')" --dialog-action "my-answer"# Valid --dialog-action values: "accept", "dismiss", or any prompt-text string.# Save JS output to a filechrome-devtools --target warm-squid evaluate "JSON.stringify(performance.timing)" -o /tmp/perf.json
Avoid `evaluate` for DOM traversal. Use snapshot to read page structure and click/fill to interact.
Pattern 7: Output Formats
All commands default to human-readable text output. Use --json or --toon (compact, LLM-friendly) for structured output.
chrome-devtools list-pages # human-readable table (default)chrome-devtools list-pages --json # JSONchrome-devtools list-pages --toon # TOON (compact)chrome-devtools --target warm-squid snapshot --toonchrome-devtools --target warm-squid network --toon --type Fetch
--json and --toon are mutually exclusive.
Pattern 8: Snapshot (Accessibility Tree)
Use snapshot instead of evaluate document.querySelector(...) for understanding page structure.
chrome-devtools --target warm-squid snapshotchrome-devtools --target warm-squid snapshot --output /tmp/ax-tree.txtchrome-devtools --target warm-squid snapshot --toon # compact output
Pattern 9: Screenshots
# Default: viewport-only PNGchrome-devtools --target warm-squid screenshot --output page.png# Full scrollable page (can be very tall)chrome-devtools --target warm-squid screenshot --full-page --output full-page.png# Save to a specific pathchrome-devtools --target warm-squid screenshot --output /tmp/whatever.jpg
Pattern 10: Extension Service Worker Logs
Browser-level command — no --target needed.
# Collect logs from ALL extension service workers (2s window)chrome-devtools sw-logs --duration 2000# Filter by extension ID (from sw-logs output)chrome-devtools sw-logs --duration 2000 --extension-id abcdef123456
Pattern 11: Third-party Developer Tools
For pages that expose tools via window.__dtmcp.
chrome-devtools --target warm-squid list-3p-toolschrome-devtools --target warm-squid execute-3p-tool "<tool-name>" '<json-params>'
Pattern 12: Reading Page Content as Markdown
Extract the main article content of a page as clean markdown. Uses Readability to identify the article body and converts it to LLM-friendly markdown with metadata (title, byline, excerpt, URL). Non-article pages (SPAs, dashboards) fall back to converting the full page.
# Read the current page as markdown (title prepended as H1)chrome-devtools --target warm-squid read-page# Save to a filechrome-devtools --target warm-squid read-page --output /tmp/article.md# JSON output includes metadata fields (title, byline, excerpt, site_name, url)chrome-devtools --target warm-squid read-page --json
When to use `read-page` vs `snapshot`:
read-page— you want the page's textual content as readable markdown (articles, docs, wiki pages). Best for summarization, extraction, or feeding content to an LLM.snapshot— you need the full accessibility tree with element IDs, roles, and interactive elements. Best for understanding page structure and finding elements to click/fill.
Pattern 13: Local JS Scripting (run-script)
Evaluate a local JavaScript file inside the page context. Dynamic arguments can be passed as raw positional values at the end of the command or via -a/--arg keys, and are automatically typed and injected into the execution context as ctx.args. Supports comment-based @url auto-navigation.
See the dedicated Custom Scripting Guide for full documentation on script creation, argument parsing, and auto-navigation.
# Run a script with trailing positional arguments (auto-navigates if @url is present)chrome-devtools --target warm-squid run-script skill/chrome-devtools/examples/search_hn.js -- "Rust"
Pattern 14: Custom Domain-Aware Adapters (adapter)
Run site-specific adapter actions. If the browser is not currently on a matching domain (as defined by @domain comments in the JSDoc header), the CLI auto-navigates to that domain first.
See the dedicated Custom Scripting Guide for full documentation on custom adapters, domain protection, and argument parsing.
# Run an adapter function with positional args (auto-navigates if target domain is mismatch)chrome-devtools --target warm-squid adapter skill/chrome-devtools/examples/hn_adapter.js search -- "Rust"
Pattern 15: Memory Leak Debugging (Heap Snapshots)
Take two heap snapshots around a suspected leak, diff them per class, then drill into individual node IDs. compare-heapsnapshots and inspect-heapsnapshot-node are fully offline (they parse local files — no Chrome connection needed).
# 1. Take a baseline snapshotchrome-devtools --target warm-squid take-heapsnapshot --output /tmp/base.heapsnapshot# 2. Perform the leaky action in the page (click, navigate, etc.), then take a second snapshotchrome-devtools --target warm-squid take-heapsnapshot --output /tmp/current.heapsnapshot# 3. Diff: one row per class with added/removed counts and sizes, sorted by size deltachrome-devtools compare-heapsnapshots --base /tmp/base.heapsnapshot --current /tmp/current.heapsnapshot# idx,className,addedCount,removedCount,countDelta,addedSize,removedSize,sizeDelta# 0,Detached HTMLDivElement,120,0,120,46080,0,46080# ...# 4. Detail for one summary row (use the idx column): per-node-id adds/removeschrome-devtools compare-heapsnapshots --base /tmp/base.heapsnapshot --current /tmp/current.heapsnapshot --class-index 0# 5. Inspect a specific node ID from the detail outputchrome-devtools inspect-heapsnapshot-node --file-path /tmp/current.heapsnapshot --node-id 12345
⚠️ Both snapshots must come from the same Chrome session. The diff matches nodes by V8 heap object ID, which is only stable within a single browser session. Comparing snapshots taken across a Chrome restart (or from different profiles/machines) produces a meaningless result where nearly everything is reported as both added and removed — the CLI prints a warning on stderr when it detects this.
Complete Command Reference
Navigation
chrome-devtools list-pageschrome-devtools navigate <url> [--viewport WxH] [--mobile] [--device-scale-factor N] [--geolocation lat,lon]chrome-devtools navigate <url> --extra-headers '{"Authorization":"Bearer ..."}'chrome-devtools navigate --backchrome-devtools navigate --forwardchrome-devtools navigate --reloadchrome-devtools new-page <url> [--viewport WxH] [--mobile]chrome-devtools close-page [index_or_target_name]chrome-devtools select-page [index_or_target_name]
Inspection
chrome-devtools --target <name> screenshot [--output <path>] [--full-page]chrome-devtools --target <name> snapshotchrome-devtools --target <name> read-page [--output <path>]chrome-devtools --target <name> evaluate "<js-expression>" [--dialog-action accept|dismiss|text]chrome-devtools --target <name> network [--duration <ms>] [--type <resource>]chrome-devtools --target <name> console [--duration <ms>] [--type <level>]chrome-devtools sw-logs [--duration <ms>] [--extension-id <id>]
Memory (heap snapshots)
chrome-devtools --target <name> take-heapsnapshot --output <path.heapsnapshot>chrome-devtools compare-heapsnapshots --base <path> --current <path> [--class-index N] # offlinechrome-devtools inspect-heapsnapshot-node --file-path <path> --node-id <id> # offline
Interaction
chrome-devtools --target <name> click "<css-selector>"chrome-devtools --target <name> click-at <x> <y>chrome-devtools --target <name> fill "<css-selector>" "<value>"chrome-devtools --target <name> type-text "<text>" [--submit-key <key>]chrome-devtools --target <name> press-key <key>chrome-devtools --target <name> hover "<css-selector>"chrome-devtools --target <name> wait-for "<text>" [--timeout <ms>] # default 30000
Emulation
chrome-devtools --target <name> emulate [--viewport WxH] [--mobile] [--geolocation lat,lon]chrome-devtools --target <name> emulate # show current statechrome-devtools --target <name> emulate --block-url "<pattern>" [--block-url ...]chrome-devtools --target <name> emulate --unblock-url "<pattern>" # remove from blocklistchrome-devtools --target <name> emulate --clear-blockschrome-devtools --target <name> emulate --clear-viewportchrome-devtools --target <name> emulate --clear-geolocationchrome-devtools --target <name> emulate --clear-all # clears everything
Third-party Tools
chrome-devtools --target <name> list-3p-toolschrome-devtools --target <name> execute-3p-tool <name> '<json-params>'
Custom Scripting & Adapters
chrome-devtools --target <name> run-script <file-path> [--arg key=value] [--output <path>] [--track-navigation]chrome-devtools --target <name> adapter <file-path> <function-name> [--arg key=value] [--output <path>] [--track-navigation]
Daemon
chrome-devtools kill-daemon # refuses when run non-interactively (agents) unless --forcechrome-devtools kill-daemon --force # kills unconditionally
Failure Handling: "Failed to connect to Chrome" / a command hangs
Chrome's remote-debugging connection requires a one-time human approval dialog in Chrome. If a command hangs or fails with a connection/timeout error, the most likely cause is that this dialog is open and waiting for the human — not a bug you can fix by retrying.
If a command hangs for a long time or errors with "Failed to connect to Chrome" or "Timed out ... connecting to Chrome":
- Retry at most once (the human may have already approved it just now).
- If it fails again, STOP. Do not keep retrying — the human is very likely
away from the keyboard and no amount of retrying will approve the dialog for them.
- Tell the user directly that Chrome is waiting for them to approve the
remote-debugging connection dialog, and wait for their response.
Never run `kill-daemon` as a way to "fix" a connection problem. It does not help — it destroys the daemon's already-approved connection (if one exists) and guarantees the next attempt needs a fresh human approval, making things worse. For this reason, kill-daemon refuses to run non-interactively without --force. As an agent, only pass --force if the user has explicitly asked you to kill the daemon for some other reason (e.g. it's stuck on unrelated JS execution) — never as a reaction to a connection failure.
Critical Gotchas
✗ WRONG: Using invented target names
chrome-devtools --target main navigate https://example.com # ❌ WRONGchrome-devtools --target page1 screenshot # ❌ WRONGchrome-devtools --target "my-page" evaluate "..." # ❌ WRONG
✓ CORRECT: Get target from command output
chrome-devtools list-pages # ✓ First: list pages# [0] (warm-squid) Example — https://example.comchrome-devtools --target warm-squid navigate https://github.com # ✓ Use the name from output
✗ WRONG: Running commands without first finding a target
chrome-devtools screenshot --output page.png # ❌ WRONG: unpredictable page
✓ CORRECT: Always identify the page first
chrome-devtools list-pages # ✓ Firstchrome-devtools --target warm-squid screenshot --output page.png # ✓ Pinned to known page
✗ WRONG: Using evaluate for DOM traversal or interaction
chrome-devtools --target warm-squid evaluate "document.querySelector('...').click()" # ❌ WRONG
✓ CORRECT: Use snapshot for structure, click/fill for interaction
chrome-devtools --target warm-squid snapshot # ✓ Read structurechrome-devtools --target warm-squid click "button.submit" # ✓ Interact
✗ WRONG: Expecting fill to update React/Vue forms
chrome-devtools --target warm-squid fill "input" "value" # ❌ value set, but framework state unchanged
✓ CORRECT: Use type-text for stateful frameworks
chrome-devtools --target warm-squid type-text "value" # ✓ real key events → framework state updates
✗ WRONG: Expecting console/network to remember events after drain
chrome-devtools --target warm-squid console# [error] foo# [warning] barchrome-devtools --target warm-squid console# No console messages collected. ← the buffer was drained on the first call
✓ CORRECT: Each drain is a fresh window
Run console / network right after the action that produces events, OR use --duration to collect for a window of time.
✗ WRONG: Retrying in a loop or running kill-daemon on connection failure
chrome-devtools list-pages # hangs / fails: "Failed to connect to Chrome"chrome-devtools kill-daemon --force # ❌ WRONG: destroys the approved connection, makes it worsechrome-devtools list-pages # retry again, and again... ❌ WRONG: human is likely away
✓ CORRECT: Retry once, then stop and ask the human
chrome-devtools list-pages # hangs / fails: "Failed to connect to Chrome"chrome-devtools list-pages # ✓ one retry, in case the human just approved it# still failing → STOP retrying, tell the user Chrome needs approval, and wait
Output Format Summary
| Flag | Description | |
|---|---|---|
| (none) | Human-readable text (default) | |
--json | Pretty-printed JSON | |
--toon | TOON — compact tabular format (fewer tokens for LLM agents) |
--json and --toon are mutually exclusive.
Stdout vs Stderr
- stdout contains the primary command output (table, text, JSON, TOON).
- stderr contains informational lines (
[target:...],[navigated to: ...]) and errors.
When parsing command output programmatically, read only stdout to get the main result.