"Empty your mind, be formless, shapeless — like water." — Bruce Lee
Turn any webpage into a CLI. Agent-friendly.
Water uses Chrome DevTools Protocol (CDP) to give you structured, JSON-native access to any webpage — no API needed. Every command outputs NDJSON, every interaction is a CLI verb, and every page becomes programmable.
water open https://github.com/trending
water dom --semantic
# {"tag":"h1","name":"Trending","role":"heading"}
# {"tag":"a","name":"anthropics/claude-code","href":"/anthropics/claude-code"}
water eval "document.title"
# {"result":"Trending repositories on GitHub today"}
water ax query --role link --name "*claude*"
# {"id":"42","role":"link","name":"anthropics/claude-code","url":"/anthropics/claude-code"}
water screenshot -o trending.pnggo install github.com/anthropics/water/cmd/water@latestgit clone https://github.com/anthropics/water.git
cd water
go build -o water ./cmd/water/Requires: Go 1.22+ and Chrome/Chromium installed.
# Open a page (launches headless Chrome, persists session)
water open https://example.com
# Read the page
water eval "document.title" # Execute JavaScript
water text --selector "h1" # Extract text
water dom --semantic # Semantic elements as NDJSON
water ax tree # Accessibility tree (Agent-friendly)
# Interact
water click --role button --name "Login"
water type --name "Email" --value "user@example.com"
# Capture
water screenshot -o page.png
water screenshot --full -o full.png
# Monitor network
water network listen # NDJSON stream of all requests
water network listen | jq 'select(.status >= 400)'
# Navigate
water open https://another-page.com # Reuses existing session
# Done
water close # Kill Chrome, clean up| Command | Description |
|---|---|
open <url> |
Open a page (launches Chrome if needed) |
close |
Close the browser session |
eval <expr> |
Execute JavaScript, return JSON |
text |
Extract text content |
dom |
Get DOM structure (NDJSON) |
dom --semantic |
Semantic elements only |
ax tree |
Full accessibility tree |
ax query |
Query by ARIA role/name |
click |
Click an element |
type |
Type into an input |
screenshot |
Take a screenshot |
network listen |
Stream network events |
targets |
List browser targets |
water dom --semantic # NDJSON (default, Agent-friendly)
water dom --semantic -f table # Human-readable table
water dom --semantic -f csv # CSV
water text -f raw # Raw text, no JSON wrapperWater outputs NDJSON by default — one JSON object per line. AI agents can parse it directly:
import subprocess, json
result = subprocess.run(["water", "eval", "document.title"], capture_output=True, text=True)
data = json.loads(result.stdout)
print(data["result"]) # "Example Domain"CSS selectors break when pages redesign. The accessibility tree is semantic and stable:
# Fragile (breaks on redesign)
water dom --selector "div.repo-list > li:nth-child(2) > h3 > a"
# Stable (survives redesign)
water ax query --role link --name "Trending"water CLI ─── raw CDP WebSocket ─── Chrome/Chromium
│
53 CDP domains available │
No chromedp target lifecycle │
Target persists across commands │
Water uses a raw CDP WebSocket client (not chromedp's context lifecycle) so browser targets persist across commands. Each water invocation connects, executes, and disconnects cleanly without closing the tab.
| Variable | Description |
|---|---|
CHROME_PATH |
Path to Chrome/Chromium binary |
MIT