Repository navigation
Home
Chrome DevTools Protocol in your terminal. Designed for AI agents and developers who want direct browser control without framework overhead.
Current release: v0.22.0 (release notes, changelog). See What's New in 0.22.0 below.
| Page | Description |
|---|---|
| Getting Started | Install, first session, basic commands |
| Commands | Complete command reference |
| For AI Agents | Discovery, JSON envelope, exit codes, recovery hints |
| Recipes | Common workflows and automation patterns |
| Quick Reference | Fast lookup: commands, filters, exit codes, quick fixes |
| Architecture | How bdg works: daemon, IPC, session lifecycle |
| Troubleshooting | Common errors and how to fix them |
0.22.0 fixes three false verdicts found by a red-team round of agents. Full notes: GitHub release v0.22.0.
-
A redirect to an app or script URL fails the navigation. When a server redirects to
myapp://…,javascript:orview-source:,bdg <url>,page navigate,page reload,dom clickon a link anddom submitexit 80 withCould not load <url> (redirected to <target>): net::ERR_ABORTEDinstead of✓ Navigatedon the old page. -
dom fillchecks the value again after 2 s. A value the page puts back (its old value, or a blank) exits 90 like an immediate mismatch. A value the page formats later passes withPage shows: <value>(data.pageValue). A fill now takes about 2 s;--no-waitskips the late check. -
No "nothing observed" after a click that changed a control or a same-origin iframe. The verdict for a click with no seen effect now reads
nothing observed within about half a second … later results are not watchedand points tobdg dom query,bdg peekorbdg dom screenshot;--jsoneffect: "none"is unchanged.
0.21.0 removes a class of false successes and adds logpoints, an accessibility audit and request timing phases. Full notes: GitHub release v0.21.0.
-
bdg debug: logpoints.bdg debug log app.js:8 total discountlogs values at a source line without pausing the page; hits appear in action reports andbdg debug hits. Works on source-mapped bundles (src/cart.ts:7). See Commands. -
bdg dom audit a11y: image alt text, control names, heading order, landmarks, duplicate ids and broken ARIA references, from the computed accessibility tree. Part of the defaultbdg dom auditrun. -
Timing phases in
network details: queued, dns, connect, tls, send, wait, download, and which one dominated (timingBreakdownin--json). bdg attach --dialog accept|dismiss.
Changed behaviour (check scripts that read exit codes):
-
A load that ends on Chrome's error page exits 80:
bdg <url>,page navigate,page reload,dom wait --load(Could not load <url>: net::…).attachto such a tab warns and exits 0; a certificate warning page exits 0 with⚠. -
An action whose navigation fails exits 80 (
dom click,submit,press-key, …):⚠ … (navigation failed: net::…),--jsonnavigationFailed: { url, errorText }. A page that answers but never finishes is a⚠ still loading, exit 0. -
dom fillexits 90 when the page doesn't keep the value (valueMismatch); a number in a formatted field matches (8→$8.00). -
bdg evalon a page paused in the debugger exits 85 and says how to resume (bdg cdp Debugger.resume), instead of 102. -
bdg attachdismisses JavaScript dialogs by default (bdg <url>still accepts). -
network list --sort durationand--preset slowinclude a request still pending, ranked by its elapsed time (PND ≥25.0s;--jsonpending,elapsedMs). -
HAR
timings.blockedandtimeinclude Chrome's queue time.
The 1.0 renames from 0.20.0 below are unchanged: old spellings still work with a warning.
0.20.0 is the last 0.x release. It gives a set of commands and flags their 1.0 names. Every old spelling still works, prints one Warning: line on stderr (warnings in the --json envelope) naming the new one, and is hidden from help; in 1.0 the old spellings stop working (exit 81). Full notes: GitHub release v0.20.0.
| Old | Use instead |
|---|---|
bdg tail |
bdg peek --follow |
bdg details network <id> / details console <n>
|
bdg network details <id> / bdg console details <n>
|
bdg network document |
bdg network headers |
bdg dom eval |
bdg eval |
bdg dom pressKey |
bdg dom press-key |
bdg network getCookies |
bdg network cookies |
bdg cleanup --aggressive, cleanup -f
|
bdg cleanup --force |
bdg <url> -t/--timeout, bdg attach --timeout
|
--stop-after <seconds> |
bdg <url> -a/--all |
--capture-all |
bdg console -H/--history |
bdg console --page all |
bdg dom audit --level |
bdg dom audit --wcag |
bdg dom get --nth |
bdg dom get --index |
bdg stop --kill-chrome |
bdg stop |
envelope warning (string) |
warnings (array; both written in 0.x) |
Also new: console --follow --ndjson (one JSON record per line), the page URL in follow-mode separators, backendNodeId in dom query --json and dom get --raw --json. --compact and cleanup --remove-output are removed (exit 81).
0.19.0 makes interrupting a slow start reliable, fixes HAR body sizes, and separates what an action changed from what the page changes by itself. Full notes: GitHub release v0.19.0.
- Ctrl-C during the start returns in well under a second at any moment, even while the page hasn't answered, and leaves no session.
-
Also changing:(alsoChanging) names regions the page kept updating on its own (tickers, polled badges); they no longer fillNew text:or mark an action "still changing". -
Shown:lists a<dialog>a click opens;New text:after a navigation skips footer headings. -
dom waittimeouts on a loading page name the pending requests (pending,pendingCount). - Performance recipes (LCP, CLS, long tasks, CPU profile) in the Commands reference.
Behaviour changes (no breaking changes):
- A start you interrupt (Ctrl-C, SIGTERM,
bdg stop) force-kills its Chrome 500 ms afterBrowser.close. Profile data Chrome hadn't written may be lost; with a real--user-data-dir, Chrome may show its restore prompt next time. A normalbdg stopis unchanged. - HAR
response.bodySizeno longer includes the headers (0 for 304 / cache hits, -1 when unknown).
0.18.0 attaches to the Chrome you already have open, makes action results more honest, and ends every command cleanly on Ctrl-C. Full notes: GitHub release v0.18.0.
-
bdg attach [tab]: list the tabs of your running Chrome, or attach to one without reloading it (enablechrome://inspect/#remote-debugging). Earlier console messages and exceptions are replayed. -
network list --filter url:<text>: filter by part of the URL (!url:negates,*matches anything);domain:<host>:<port>now compares the port. -
Honest action results:
Form submitted,Button changed,Activated,Expanded,DOM changed (N nodes),Focus moved to; repeated requests share one row; tracker errors are hidden. -
Ctrl-C: every interrupted command prints an
Interruptedenvelope (--json) or one line;dom evalstops its script on the page. -
statusandpeekshow a page that is still loading and what it waits on.
Breaking changes:
- Bare
bdg --jsonandbdg <group> --json(bdg page --json, ...) exit 81 with an error envelope instead of printing help. Usebdg --help --jsonfor the help document. - Help JSON
optionsis an array of{flags, description, required, takesValue, optional}everywhere (the compact root help used a map). Read it as an array. - A page-code failure during an action (
dom click,fill,submit,pressKey,scroll) exits 91, not 110 (pressKeyon a page that replaced built-ins exits 90). Match 91 and 90. -
--chrome-ws-url: a declined "Allow remote debugging?" prompt exits 82, an unanswered one 102 after 30 s. Handle both. - A
--filterwith an odd number of"exits 81. Write"url:a b", noturl:"a b". -
dom screenshot --followexits 130 / 143 on Ctrl-C / SIGTERM. -
dom evalno longer leaves top-levelconst,letorclasson the page. Usewindow.x = ...to share state between evals.
0.17.0 gives agents tabs, saved logins and event capture, and masks credentials by default. Full notes: GitHub release v0.17.0.
-
Tabs and popups:
bdg page tabs|switch|close; actions list the tabs and popups they opened. -
Auth state:
bdg state save|loadandbdg <url> --state <file>keep cookies and storage across sessions. -
bdg cdp --collect/--until/--out/--listen/--eventsread CDP events (tracing, heap snapshots,Fetch.requestPaused). -
Dialogs:
--dialog accept|dismissand--prompt-texton DOM actions;--dialogand--stateat start. -
network list --page/--sort,details network --body/--no-body/--body-max(JSON body cut at 20000 characters). -
Masked by default:
details network,network headersandnetwork documentshow credentials as••••; page URLs loseuser:pass@and show tokens as••••(--include-sensitiveonstatus,sessions,page info,page tabs,network list,peek); HTML bodies in HAR and details mask secret form values. -
Actions:
Submit blockedin click/key results, console errors an action caused,dom filltypes like a user, secret fields recognised by label. -
bdg cleanupremoves atomic-write leftovers.
0.16.0 is about trust in what bdg reports: a busy, slow or starved page is reported as it is, Ctrl-C always leaves a clean session, downloads stay out of your ~/Downloads, and the default HAR no longer leaks tokens from responses. Every change was reproduced before it was fixed. Full notes: GitHub release v0.16.0.
Behaviour changes:
-
A page whose JavaScript is blocked or starved is no longer reported settled. After
dom click/pressKey, time the page's main thread could not run (a long task, or timers a starved renderer runs late) no longer counts as a quiet gap, so such a page ispending.domChanging. Clicks on idle pages are as fast as before. -
Downloads go to the session directory (
<session dir>/downloads/, created 0700) instead of~/Downloads, also fromtarget=_blanklinks andwindow.open(). Actions report them (Download: report.txt → … (completed, 15 B), JSONdownloads), and so dostatusandpeek. With--chrome-ws-urlthe browser is yours, so downloads go to that Chrome's own folder.
Security:
- The default HAR redacts response bodies and WebSocket messages too (JSON, forms, socket.io/SockJS packets, SSE, NDJSON, base64), and a JWT under any name. Only credential values are replaced, so every other byte stays (64-bit numbers included).
- More credential names are redacted and masked:
passphrase,csrf,auth_code,code_verifier, camelCase names likeuserPin, and more.
Fixes:
-
Ctrl-C on
bdg <url>leaves no session behind and no Chrome, and exits 130 (143 for SIGTERM) once the session is gone;bdg stopduring a slow start ends it at once. Ctrl-C ondom screenshotrestores the session's viewport, pixel ratio and touch emulation before the next page command runs, and prints the error envelope with--json. -
bdg cdpsends methods missing from the bundled protocol to Chrome, with a warning. Typos still exit 81 (--send-anywayoverrides).--describeshows redirects, enums and types, with example values that work as typed. Errors say why a method is missing. - A static page on a loaded machine is no longer reported
busy: a starved renderer is told apart from a long task. Pages changing every 100–150 ms are reported changing. - Request bodies count toward the 100 MB body budget (
requestBodyNotCapturedwhen evicted); a response body Chrome refused to return says why (bodyNotCaptured). - Element screenshots have
captureMode: "element"in JSON. A slow Chrome start is no longer reported as a port conflict, and a failed screenshot reports the real error.
0.15.0 hardens what 0.14.0 started, with fixes from a dependency audit, each reproduced in a real browser before it was fixed. Full notes: GitHub release v0.15.0.
Breaking change:
-
network harwrites a sanitized HAR by default. Credential headers and cookie values become[redacted](names stay), and so do sensitive fields in request bodies and sensitive URL parameters (?code=,?access_token=).--include-sensitivewrites the full HAR. HAR files are 0600.
Security:
- Session directories are created 0700 and checked before use. A symlink, another user's directory, or one others can write to is refused with exit 103. Existing
~/.bdgdirectories are accepted and tightened. A symlinkeddaemon.logis refused.
Fixes:
- Every new profile used to download about 45 MB of Chrome ML models, because bdg's
--disable-featuresreplaced chrome-launcher's. There is now one merged flag.HEADLESS=1no longer forces headless. - Headless sessions keep their client hints (
navigator.userAgentData.brands) and look like a regular Chrome. -
dom eval --jsonbounds arrays (100 elements,count/omitted) and large objects.--fullreturns every entry. - "Did you mean: form or frames?"; the "No Chrome found" error suggests
CHROME_PATH.
0.14.0 is about what bdg costs an agent and how far you can trust it. A deep audit found where output was unbounded, where bdg quietly stopped recording, and where commands disagreed about the same element; all of it is fixed. Full notes: GitHub release v0.14.0.
Breaking changes:
-
dom a11y tree --jsonis a flat, boundednodeslist withdepth,count,omittedandskipped(data.rootandchildIdsare gone); 50 nodes by default,--limit/--depth. -
--jsonlists default to 100 rows fordom queryanddom a11y query;count/omittedsay how many matched,--limit 0lists all. -
Long values are cut:
dom get --raw,dom eval(20,000 chars), console text inconsole/peek.--fullreturns them whole; JSON marks a cut value withtruncatedFrom. -
--jsonis one line when piped (indented in a terminal).
Fixes:
- Network capture keeps the newest requests at its 10,000 cap (it used to drop every new one) and keeps at most 100 MB of response bodies;
network list,peekandstatussay what was dropped. -
:has-text,:text-isanddom wait --textread shadow DOM likedom queryshows it;dom layoutsees clipping through a<slot>;dom clicksays when an element is collapsed. -
dom inspectno longer waits 1 s for hints on every call on CSS-heavy pages. - A slow Chrome start is no longer reported as "Port N is already in use".
-
bdg --help --json --fulldocuments--headlessand--all; the agent skill matches the CLI.
0.13.0 is about trust in bdg's answers. Seven cloud test agents and a retest by eight fresh agents on real sites looked for places where bdg gave a wrong answer with exit 0. Full notes: GitHub release v0.13.0.
Breaking changes:
-
bdg cdpprints text without--json: lists,--describeand results are readable text. Scripts that parse its output need--json. -
bdg --help --jsonis compact: 29 KB instead of 173 KB.bdg <command> --help --jsondescribes one command in full;--fullprints the previous tree. -
dom queryindexes the first 1000 matches and lists 50 (--json: 1000) with the total;--limit <n>lists more (0 = all). -
dom audit contrastcounts infailingonly text it can measure exactly; text over images and gradients is counted asuncertain. -
Follow modes stop with exit 83 when their session ends, exit 130/143 on Ctrl-C/SIGTERM, and print NDJSON with
--json. -
Chrome gets a window on macOS by default (unless over SSH or
CIis set). Agents running unattended on a Mac should pass--headless. -
New exit codes: 107 (
PAGE_CRASHED), and 90 when the page broke an action's script by replacing a built-in.
Fixes:
-
dom form --jsonno longer contains passwords; an index from an earlier document exits 87 instead of acting on another page's element. - Pages that replace built-ins (
querySelectorAll,JSON.stringify, MooTools): read commands run in bdg's isolated world; actions say what the page replaced. - Web components read through their shadow roots, with slotted content in place;
dom getadds aText:line when the accessible name differs from the visible text. - A crashed page is reported by
status,peek,consoleandnetwork list;bdg page reloadbrings it back. -
bdg sessionslists ended sessions with why and when;bdg stopreturns once the daemon has exited; no Chrome is left behind after a second start or a lostchrome.pid. -
consolekeeps the newest 10000 messages (not the first) and says how many were dropped. -
bdg install-skillkeeps an edited skill asSKILL.md.bak.
0.12.0 comes from a deep review of dom inspect: agents new to bdg described, debugged and compared layouts on many real sites, and a second round checked the fixes. Full notes: GitHub release v0.12.0.
-
bdg dom audit: page-wide checks without screenshots ordom eval: text below WCAG AA/AAA (weakest first), what makes the page scroll sideways, cut-off text, upscaled or distorted images, fixed/sticky layers and running animations -
bdg css search <text>: finds a text in every stylesheet of the page, cross-origin ones included, with the rule and itsfile:line(bdg css search -- --brand) -
--mobile(bdg <url> --mobile,bdg page emulate --mobile): a phone with a 390x844 viewport at pixel ratio 3, touch and an Android Chrome user agent -
dom inspectaccuracy:- text, font and contrast come from the text a user sees;
- contrast composites translucent ancestors like the browser and marks approximate values with
≈; -
--whyanswers every shorthand and inherited property and prints the winning rule as written; - fewer false hints; "covered by" names the overlay.
-
More:
- element screenshots include shadows, outlines and descenders, and
--padding <px>adds page around the element; -
dom hover --offcloses hover menus; -
dom fillfills fields inside web components; -
peek --follow --interval <ms>;bdg tailis deprecated in favour ofpeek --follow.
- element screenshots include shadows, outlines and descenders, and
-
bdg install-skill: installs the bdg agent skill for Claude Code (~/.claude/skills/bdg) and agents that read~/.agents/skills(Codex, Gemini CLI, ...)
0.10.0 lets an agent see what an element looks like, and why, without a screenshot. Four agents new to bdg worked through styling tasks in three rounds (a broken dashboard, Bootstrap's button colors, Tailwind's responsive heading, MDN's search box). None of them took a screenshot. Full notes: GitHub release v0.10.0.
-
bdg dom inspect <selector>: an element's styles grouped like Figma Dev Mode, in about 60–100 tokens:- box and layout, including the parent's layout and the distances to its edges;
- text, with the font Chrome rendered and WCAG contrast;
- fills, borders, effects and pseudo-elements;
- a child tree.
-
Hints: declarations that have no effect, with the reason, the fix and the file and line (
justify-content: space-between has no effect: display is block → use display: flex or grid on this element · in .topbar (app.css:4)) -
--rules/--why <property>: which CSS rule sets each value and what it beats. The output includes specificity, media conditions and layers,var()values substituted with where each custom property is set, and a warning when a transition is still running. -
bdg page emulate --viewport 900x700(or--color-scheme light,--reset): change the viewport or color scheme mid-session -
Checking a page against a Figma design with the Figma MCP server and
dom inspect --json --rules -
More:
-
bdg cdpexits 81 for bad parameters and 83 for a missing node, instead of 110; - a selector that tries to cross into a shadow root says which selector works;
-
dom inspect --props '--*'lists custom properties; - Preact handlers in
dom listeners.
-
Most of 0.9.0 comes from watching agents with no prior knowledge of bdg work through real sites. Full notes: GitHub release v0.9.0.
-
bdg dom layout: position, viewport visibility, what covers an element and how far to scroll, without a screenshot -
bdg dom wait: wait for an element to appear, become visible, contain text or go away (or for--load) instead ofsleeploops -
Actions say what changed:
Page: navigated to …,New text: "…",⚠ … (no visible effect observed …),valueMismatch/movedToafterdom fill, and the requests the action started -
Named sessions:
--session <name>/BDG_SESSION,bdg sessions,bdg cleanup --session <name> --purge -
Start options:
--viewport 1280x800,--color-scheme dark,--chrome-ws-url 9222(attach by port) -
Selectors: scoped text filters (
li:has-text("Write report") .toggle), filters inside:has(), labels act as their control -
More:
bdg page info,dom listenersresolves React props and jQuery handlers,dom framesorigins and nesting,dom a11y query --limit,dom get --index/--full, warnings for pages still loading,dom formlists dialog forms first
Breaking changes (the JSON envelope itself is unchanged):
- Session start prints 4-7 lines instead of the ~60-line command list
-
dom evalprints strings raw in human output (My Page, not"My Page") -
dom formJSON:readyToSubmitis false for an untouched form, choice groups count once, hidden fields are listed (hidden: true), forms are ordered dialog-first and indices follow that order -
dom submit:networkRequestscounts the reportable requests; the humanNavigation: yesrow became aPage:line -
dom layoutJSON:scrollByis the minimal scroll for a partly visible element -
dom get:--indexreplaces--nth(kept as an alias); without an argument it readsbody - Session names are lower-cased and must start with a letter or digit
| bdg | Puppeteer/Playwright | Chrome DevTools MCP | |
|---|---|---|---|
| CDP coverage | All 675 methods (59 domains) | Via CDPSession API | Curated tool set |
| Self-documenting |
--list, --search, --describe, --help --json
|
External docs | Fixed descriptions |
| Discovery | Search methods by keyword | Know what you need | Browse tool list |
| Interface | CLI, immediate | Write code first | MCP server + client |
| Token efficient | Semantic a11y output, short previews | Raw HTML | Verbose responses |
| Unix philosophy | Pipes, jq, composable | Programmatic only | Protocol-based |
# Install (Node.js 22.12+)
npm install -g browser-debugger-cli
# Start session
bdg example.com
# High-level helpers
bdg dom query "a" # Find elements, indexed [0], [1], ...
bdg dom layout "a" # Where they are and whether they are visible
bdg dom click 'a:has-text("More")' # Click by visible text
bdg page navigate example.com/about # Move the page and wait for load
bdg dom wait ".result" --visible # Wait for content shown later
bdg eval "document.title" # Run JavaScript in the page
bdg network list --preset errors # Failed requests
bdg console # Errors and warnings
# Raw CDP: discover and run any method
bdg cdp --search cookie
bdg cdp Network.getCookies
# Stop
bdg stop- Full CDP Access: All 675 protocol methods (59 domains), not a subset
-
Self-Documenting: Discover capabilities via
--list,--search,--describeandbdg --help --json - Page Interaction: navigate, click, hover, fill (selects, file uploads), keys, scroll, submit, forms; results report navigations, new messages and triggered requests
-
Layout Without Screenshots:
dom layoutreports position, visibility, covering elements and the scroll needed -
Waiting:
dom waitfor elements, text, removal or page load -
Smart Selectors:
:has-text(),:text-is(),:visible, also scoped (li:has-text("x") .toggle); open shadow roots and same-origin iframes are searched automatically - Debugging: event listeners per element (React props, jQuery handlers), iframe evaluation (cross-origin too), console and network streaming, HAR export
- Parallel Sessions: named sessions, each with its own daemon, Chrome and port
-
Agent-Friendly: One JSON envelope for every
--jsonoutput, semantic exit codes, recovery suggestions - Unix Native: Pipes, jq, shell composition
- macOS and Linux (native)
- Windows via WSL
- Docker (see
docs/DOCKER.mdin the repository) - Browsers: Chrome, Chromium or Microsoft Edge (
CHROME_PATH); not Firefox or Safari - PowerShell/Git Bash (not yet supported)