unchainedsky-cli

August 16, 2026 · View on GitHub

Browser automation CLI over local Chrome CDP — DDM-first methodology for LLM agents.

Install

# With uv (recommended)
uv tool install unchainedsky-cli[agent]

# Or pip
pip install unchainedsky-cli[agent]

# Or brew
brew install --HEAD unchainedsky/tap/unchainedsky-cli

The [agent] extra includes the Anthropic SDK for the interactive Claude agent. Omit it if you only need the CLI tools.

Why Local Chrome

Some browser tasks depend on identity that is already assembled in a Chrome profile: cookies, login state, extensions, and account selection. For example, an agent can open the AWS console with the profile you already use, read the current budget status, and prepare the next step. A caller should stop for your confirmation before changing a budget, permission, subscription, or other consequential setting.

Use an isolated profile for ordinary browsing. Use --use-profile only when a task genuinely needs an existing session, and treat that profile as access to the accounts it contains. Automate only sites and accounts you are permitted to use, respect site controls and terms, and keep secrets out of prompts, logs, and screenshots.

Quick Start

# Launch Chrome and browse
unchained launch https://news.ycombinator.com

# Navigate returns page layout + intel probe automatically
unchained navigate https://example.com

# Search page text
unchained ddm --text --find "price"

# Element details at coordinates
unchained ddm --at 694,584

# Start the interactive Claude agent
export ANTHROPIC_API_KEY=sk-ant-...
unchained agent "find the cheapest flight to NYC"

Launch Options

# Sandboxed profile (default — clean, isolated)
unchained launch

# Existing Chrome profile (with cookies, logins, extensions)
unchained --port 9333 launch --use-profile --profile "Profile 3" https://x.com

# Headless Chrome (applies the same compatibility overrides as --stealth)
unchained launch --headless https://example.com

# Compatibility overrides for sites that misclassify automated Chrome
unchained launch --stealth https://example.com

# Multiple profiles on different ports
unchained --port 9222 launch --profile work
unchained --port 9333 launch --use-profile --profile "Profile 3"
unchained --port 9444 launch --use-profile --profile "Profile 8"

Commands

Browser Lifecycle

CommandDescription
launch [url]Launch Chrome with CDP (--profile, --use-profile, --stealth, --headless)
statusCheck if Chrome is alive
killKill Chrome on this port

--stealth adjusts browser-exposed properties for compatibility. It does not make automation invisible or grant permission to bypass a site's controls.

CommandDescription
navigate <url>Navigate (returns inline DDM layout + Intel probe)
click --x X --y YClick at pixel coordinates from DDM
click --selector CSSClick by CSS selector
type <text>Type into focused element
press_enterPress Enter key
key <key> [--modifiers N]Press any key (Escape, Tab, ArrowDown, etc.)
scroll [--direction DIR]Scroll page (up/down/left/right, default: 500px)
submit_form [--selector]Submit a form
set_file --selector CSS --files PATH...Set files on file input

Page Intelligence

CommandDescription
ddmDOM Density Map — text layout with interactive elements
ddm --textExtract page text (innerText)
ddm --text --find "keyword"Search page text, show nearby elements
ddm --at 694,584Element details at pixel coordinates
ddm --sparseRLE-compressed output (~100-400 tokens)
ddm --interactiveInteractive elements only (smallest output)
ddm --llm-2passLLM-optimized layout (default for navigate)
ddm --tabsList open tabs
ddm --formsDetect forms as callable contracts
intel --probeFingerprint page + rank 8 extraction strategies
intel --extractFull extraction pipeline (auto-selects strategy)
intel --storesList JS data stores (YouTube, Next.js, Nuxt, etc.)
intel --shape <global>Map JS global variable structure
intel --find-paths <global> <pattern>Search paths in JS globals

Data & Tabs

CommandDescription
js <expression>Evaluate JavaScript
js_frame <frame> <expr>Evaluate JS in a specific iframe
screenshot [--output FILE]Save screenshot (last resort — use DDM first)
tabsList open tabs
create_tab [url]Open a new tab
close_tab <tab_id>Close a tab
cookies get [--urls URL...]Get cookies
cookies set <json>Inject cookies
framesList iframes
cdp <method> [params]Send raw CDP command
wait [--strategy dom|network|both]Wait for page load
alias set <name> <tab_id>Set a tab alias
alias listList tab aliases

Agent

CommandDescription
agent [task]Interactive Claude browser agent
agent --model opus "task"Use a specific model (sonnet/opus/haiku)

DDM-First Methodology

Every browsing task follows this pipeline:

  1. ORIENTnavigate and click return DDM layout inline. Read it. Don't call ddm separately.
  2. IDENTIFYddm --at x,y on elements you need details about.
  3. CLASSIFY — Check the Intel probe in navigate output. If js_global > 50%, use intel --stores. If host_attrs > 50%, use intel --extract.
  4. ACT — Click coordinates from DDM, type text, or run JS.
  5. VERIFY — Check the layout changed after actions.
  6. EXTRACT — Use ddm --text, intel --extract, or js based on page type.

Token Comparison

MethodTokensInfo
Screenshot (base64 PNG)~2,100Visual only, no selectors
ddm --llm-2pass~500Layout + interactive elements
ddm --sparse~100-400RLE-compressed, varies by complexity
intel --probe~50Fingerprint + strategy ranking

Global Options

--port PORT    Chrome remote debugging port (default: 9222, env: UNCHAINED_PORT)
--tab TAB_ID   Target tab ID, alias, or 'auto' (default: auto)
--json         Output raw JSON

Environment Variables

VariableDefaultDescription
UNCHAINED_PORT9222Chrome remote debugging port
UNCHAINED_DATA_DIR~/.unchainedBase directory for Chrome profiles
UNCHAINED_CHROME_BINChrome/Chromium binary override
UNCHAINED_DDM_BINDDM binary path override
UNCHAINED_INTEL_BINIntel binary path override
ANTHROPIC_API_KEYRequired for agent command

Build Binaries

DDM and Intel can be compiled to native binaries (protects proprietary algorithms):

pip install nuitka ordered-set
python build_binaries.py --install

Binaries are installed to ~/.unchained/bin/. The CLI automatically uses them when available, falling back to the Python engine otherwise.

Requirements

  • Python 3.10+
  • Google Chrome, Chromium, or Microsoft Edge