Skip to content
Kumak edited this page Oct 22, 2026 · 18 revisions

Browser Debugger CLI

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.

Quick Navigation

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

What's New in 0.22.0

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: or view-source:, bdg <url>, page navigate, page reload, dom click on a link and dom submit exit 80 with Could not load <url> (redirected to <target>): net::ERR_ABORTED instead of ✓ Navigated on the old page.
  • dom fill checks 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 with Page shows: <value> (data.pageValue). A fill now takes about 2 s; --no-wait skips 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 watched and points to bdg dom query, bdg peek or bdg dom screenshot; --json effect: "none" is unchanged.

What's New in 0.21.0

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 discount logs values at a source line without pausing the page; hits appear in action reports and bdg 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 default bdg dom audit run.
  • Timing phases in network details: queued, dns, connect, tls, send, wait, download, and which one dominated (timingBreakdown in --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::…). attach to 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::…), --json navigationFailed: { url, errorText }. A page that answers but never finishes is a ⚠ still loading, exit 0.
  • dom fill exits 90 when the page doesn't keep the value (valueMismatch); a number in a formatted field matches (8 → $8.00).
  • bdg eval on a page paused in the debugger exits 85 and says how to resume (bdg cdp Debugger.resume), instead of 102.
  • bdg attach dismisses JavaScript dialogs by default (bdg <url> still accepts).
  • network list --sort duration and --preset slow include a request still pending, ranked by its elapsed time (PND ≥25.0s; --json pending, elapsedMs).
  • HAR timings.blocked and time include Chrome's queue time.

The 1.0 renames from 0.20.0 below are unchanged: old spellings still work with a warning.

What's New in 0.20.0

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).

What's New in 0.19.0

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 fill New text: or mark an action "still changing".
  • Shown: lists a <dialog> a click opens; New text: after a navigation skips footer headings.
  • dom wait timeouts 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 after Browser.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 normal bdg stop is unchanged.
  • HAR response.bodySize no longer includes the headers (0 for 304 / cache hits, -1 when unknown).

What's New in 0.18.0

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 (enable chrome://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 Interrupted envelope (--json) or one line; dom eval stops its script on the page.
  • status and peek show a page that is still loading and what it waits on.

Breaking changes:

  • Bare bdg --json and bdg <group> --json (bdg page --json, ...) exit 81 with an error envelope instead of printing help. Use bdg --help --json for the help document.
  • Help JSON options is 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 (pressKey on 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 --filter with an odd number of " exits 81. Write "url:a b", not url:"a b".
  • dom screenshot --follow exits 130 / 143 on Ctrl-C / SIGTERM.
  • dom eval no longer leaves top-level const, let or class on the page. Use window.x = ... to share state between evals.

What's New in 0.17.0

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|load and bdg <url> --state <file> keep cookies and storage across sessions.
  • bdg cdp --collect/--until/--out/--listen/--events read CDP events (tracing, heap snapshots, Fetch.requestPaused).
  • Dialogs: --dialog accept|dismiss and --prompt-text on DOM actions; --dialog and --state at start.
  • network list --page/--sort, details network --body/--no-body/--body-max (JSON body cut at 20000 characters).
  • Masked by default: details network, network headers and network document show credentials as ••••; page URLs lose user:pass@ and show tokens as •••• (--include-sensitive on status, sessions, page info, page tabs, network list, peek); HTML bodies in HAR and details mask secret form values.
  • Actions: Submit blocked in click/key results, console errors an action caused, dom fill types like a user, secret fields recognised by label.
  • bdg cleanup removes atomic-write leftovers.

What's New in 0.16.0

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 is pending.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 from target=_blank links and window.open(). Actions report them (Download: report.txt → … (completed, 15 B), JSON downloads), and so do status and peek. With --chrome-ws-url the 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 like userPin, 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 stop during a slow start ends it at once. Ctrl-C on dom screenshot restores the session's viewport, pixel ratio and touch emulation before the next page command runs, and prints the error envelope with --json.
  • bdg cdp sends methods missing from the bundled protocol to Chrome, with a warning. Typos still exit 81 (--send-anyway overrides). --describe shows 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 (requestBodyNotCaptured when 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.

What's New in 0.15.0

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 har writes 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-sensitive writes 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 ~/.bdg directories are accepted and tightened. A symlinked daemon.log is refused.

Fixes:

  • Every new profile used to download about 45 MB of Chrome ML models, because bdg's --disable-features replaced chrome-launcher's. There is now one merged flag. HEADLESS=1 no longer forces headless.
  • Headless sessions keep their client hints (navigator.userAgentData.brands) and look like a regular Chrome.
  • dom eval --json bounds arrays (100 elements, count/omitted) and large objects. --full returns every entry.
  • "Did you mean: form or frames?"; the "No Chrome found" error suggests CHROME_PATH.

What's New in 0.14.0

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 --json is a flat, bounded nodes list with depth, count, omitted and skipped (data.root and childIds are gone); 50 nodes by default, --limit / --depth.
  • --json lists default to 100 rows for dom query and dom a11y query; count / omitted say how many matched, --limit 0 lists all.
  • Long values are cut: dom get --raw, dom eval (20,000 chars), console text in console / peek. --full returns them whole; JSON marks a cut value with truncatedFrom.
  • --json is 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, peek and status say what was dropped.
  • :has-text, :text-is and dom wait --text read shadow DOM like dom query shows it; dom layout sees clipping through a <slot>; dom click says when an element is collapsed.
  • dom inspect no 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 --full documents --headless and --all; the agent skill matches the CLI.

What's New in 0.13.0

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 cdp prints text without --json: lists, --describe and results are readable text. Scripts that parse its output need --json.
  • bdg --help --json is compact: 29 KB instead of 173 KB. bdg <command> --help --json describes one command in full; --full prints the previous tree.
  • dom query indexes the first 1000 matches and lists 50 (--json: 1000) with the total; --limit <n> lists more (0 = all).
  • dom audit contrast counts in failing only text it can measure exactly; text over images and gradients is counted as uncertain.
  • 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 CI is 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 --json no 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 get adds a Text: line when the accessible name differs from the visible text.
  • A crashed page is reported by status, peek, console and network list; bdg page reload brings it back.
  • bdg sessions lists ended sessions with why and when; bdg stop returns once the daemon has exited; no Chrome is left behind after a second start or a lost chrome.pid.
  • console keeps the newest 10000 messages (not the first) and says how many were dropped.
  • bdg install-skill keeps an edited skill as SKILL.md.bak.

What's New in 0.12.0

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 or dom 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 its file: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 inspect accuracy:
    • text, font and contrast come from the text a user sees;
    • contrast composites translucent ancestors like the browser and marks approximate values with ≈;
    • --why answers 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 --off closes hover menus;
    • dom fill fills fields inside web components;
    • peek --follow --interval <ms>; bdg tail is deprecated in favour of peek --follow.

What's New in 0.11.0

  • bdg install-skill: installs the bdg agent skill for Claude Code (~/.claude/skills/bdg) and agents that read ~/.agents/skills (Codex, Gemini CLI, ...)

What's New in 0.10.0

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 cdp exits 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.

What's New in 0.9.0

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 of sleep loops
  • Actions say what changed: Page: navigated to …, New text: "…", ⚠ … (no visible effect observed …), valueMismatch / movedTo after dom 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 listeners resolves React props and jQuery handlers, dom frames origins and nesting, dom a11y query --limit, dom get --index / --full, warnings for pages still loading, dom form lists 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 eval prints strings raw in human output (My Page, not "My Page")
  • dom form JSON: readyToSubmit is 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: networkRequests counts the reportable requests; the human Navigation: yes row became a Page: line
  • dom layout JSON: scrollBy is the minimal scroll for a partly visible element
  • dom get: --index replaces --nth (kept as an alias); without an argument it reads body
  • Session names are lower-cased and must start with a letter or digit

Why bdg?

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

5-Minute Quick Start

# 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

Key Features

  • Full CDP Access: All 675 protocol methods (59 domains), not a subset
  • Self-Documenting: Discover capabilities via --list, --search, --describe and bdg --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 layout reports position, visibility, covering elements and the scroll needed
  • Waiting: dom wait for 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 --json output, semantic exit codes, recovery suggestions
  • Unix Native: Pipes, jq, shell composition

Platform Support

  • macOS and Linux (native)
  • Windows via WSL
  • Docker (see docs/DOCKER.md in the repository)
  • Browsers: Chrome, Chromium or Microsoft Edge (CHROME_PATH); not Firefox or Safari
  • PowerShell/Git Bash (not yet supported)

Links