# cssprobe-cli Runtime CSS probe — inspect layout, scroll, colors, backgrounds, fonts and more in a live browser. Built for both humans and AI agents. [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.5.0-brightgreen.svg)](https://nodejs.org/) [![npm version](https://img.shields.io/npm/v/cssprobe-cli.svg)](https://www.npmjs.com/package/cssprobe-cli) [![npm downloads](https://img.shields.io/npm/dm/cssprobe-cli.svg)](https://www.npmjs.com/package/cssprobe-cli) [![GitHub stars](https://img.shields.io/github/stars/mack-peng/cssprobe-cli?style=social)](https://github.com/mack-peng/cssprobe-cli/stargazers) > Static + runtime CSS diagnosis for AI agents — pair with [cssgraph](https://github.com/mack-peng/cssgraph) (the CSS knowledge graph) for the full picture. --- ## Why cssprobe-cli? When an AI agent needs to debug CSS — why is this element overflowing, what's the actual rendered layout, is this container scrollable — it can't see the browser. It guesses from static code, missing runtime-only issues like overflow, scroll chains, and containing block hijacks. **cssprobe-cli gives the agent eyes.** It opens a real browser, collects computed styles and metrics via injected JavaScript, analyzes them with a confidence model, and returns structured findings — all in one command. The agent asks a question about any selector and gets back the DOM tree, ASCII layout diagram, overflow warnings, and scroll diagnosis without leaving the terminal. --- ## Installation ### For Humans Copy and paste this prompt to your LLM agent (Claude Code, Cursor, Codex, etc.): ``` Install and configure cssprobe-cli by following the instructions here: https://raw.githubusercontent.com/mack-peng/cssprobe-cli/main/docs/guide/installation.md ``` Or read the [Installation Guide](docs/guide/installation.md), but seriously, let an agent do it. Humans fat-finger configs. ### For LLM Agents Fetch the installation guide and follow it: ```bash curl -s https://raw.githubusercontent.com/mack-peng/cssprobe-cli/main/docs/guide/installation.md ``` Or install directly: ```bash npm install -g cssprobe-cli cssprobe-cli skill-install ``` --- ## How It Works ``` ┌───────────────────────────────────────────────────────────┐ │ AI Agent │ │ │ │ "Is .sidebar overflowing?" │ │ calls: cssprobe-cli inspect .sidebar │ │ │ │ └─────────────────────────────┬─────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────┐ │ cssprobe-cli daemon │ │ │ │ collector (IIFE) → analyzer (pure) → renderer (md/json) │ │ │ │ │ ▼ │ │ Playwright browser (Chromium) │ │ getBoundingClientRect · getComputedStyle · DOM walk │ └───────────────────────────────────────────────────────────┘ ``` 1. **Session** — `open` spawns a daemon process managing a browser. Commands connect via Unix socket. 2. **Collector** — Injected as IIFE into the page. Walks the DOM, reads computed styles, declared values, and element metrics. 3. **Analyzer** — Pure Node.js functions. Classifies findings with confidence levels (DEFINITE / INDEFINITE / UNVERIFIABLE). 4. **Renderer** — Outputs Markdown (default) or JSON. Includes ASCII layout diagram, DOM tree, and findings. --- ## Quick Start ### 1. Inspect a Page ```bash # Open browser (non-blocking, starts daemon session) cssprobe-cli open https://getbootstrap.com/docs/5.3/examples/checkout # Inspect with CSS selector cssprobe-cli inspect .container # Show ASCII layout diagram cssprobe-cli layout .container # Show only issues/warnings cssprobe-cli findings .container # Close browser when done cssprobe-cli close ``` ### 2. Login-Protected Pages ```bash # Open browser in headed mode cssprobe-cli open https://mysite.com --headed # User manually logs in... # Save session state for future use cssprobe-cli state-save --name mysite # Reopen with saved state (no login needed) cssprobe-cli open https://mysite.com --state state.json --headed ``` ### 3. JSON Output ```bash cssprobe-cli inspect body --json cssprobe-cli inspect body --json | jq '.findings[] | {id, confidence, message}' ``` ### 4. Use as an MCP Server (AI clients) cssprobe-cli ships an MCP server (stdio), so AI clients call it as a native tool — no shell needed: ```bash cssprobe-cli mcp-install # auto-detect and configure installed clients cssprobe-cli mcp-install --target=claude,cursor,codex cssprobe-cli mcp-install --local # write project-scoped config ``` Manual configuration for any MCP client: ```json { "mcpServers": { "cssprobe-cli": { "command": "cssprobe-cli", "args": ["mcp"] } } } ``` Exposes 10 tools: `cssprobe_open`, `cssprobe_inspect`, `cssprobe_tree`, `cssprobe_layout`, `cssprobe_findings`, `cssprobe_eval`, `cssprobe_screenshot`, `cssprobe_inject_css`, `cssprobe_close`, `cssprobe_status`. Remove with `cssprobe-cli mcp-uninstall`. --- ## Commands ### Session Management | Command | Description | |---------|-------------| | `open [url]` | Open browser in session mode (non-blocking) | | `open [url] --headed` | Show browser window | | `open [url] --state ` | Open with saved cookies + localStorage | | `open [url] --viewport 1280x720` | Custom viewport size | | `open [url] --expect-url ` | Fail unless the final URL matches the pattern | | `open [url] --fail-on-login` | Fail fast when redirected to a login URL | | `close` | Close browser session | | `close --all` | Close all sessions across all workspaces | | `status` | Show session status (URL, title, frame count) | | `frames` | List frames: index, name, URL, main marker | | `doctor` | Diagnose browser install vs Playwright expected revision | ### CSS Inspection | Command | Description | |---------|-------------| | `inspect ` | Full CSS diagnosis (computed values, declared values, findings) | | `inspect --layout` | Include ASCII layout diagram | | `inspect --brief` | Compact: tree sketch + warnings/errors only | | `inspect --json` | Structured JSON output | | `tree ` | DOM tree structure | | `layout ` | ASCII layout diagram | | `findings ` | Only issues/warnings/errors | Inspection commands accept `--frame ` to target an iframe; the default is the main frame. ### CSS Injection | Command | Description | |---------|-------------| | `inject-css ` | Inject CSS into current page | ### Browser | Command | Description | |---------|-------------| | `resize ` | Resize browser viewport | | `eval ` | Evaluate JavaScript (browser context); DOM nodes are auto-serialized; promises are awaited, `undefined` fields become `null` | | `eval @file` / `eval -` | Read the expression from a file or stdin | | `playwright ` | Execute Playwright API (Node.js context) | | `screenshot` | Take screenshot (saves to ~/.cssprobe-cli/screenshots/, --out to override) | ### Interaction | Command | Description | |---------|-------------| | `click ` | Real mouse click (auto-scroll + actionability wait; works with custom React components) | | `hover ` | Real mouse hover | | `type ` | Fill text (replaces value); `--press Enter` to submit; `--sequential` for real key events | | `wait-for ` | Wait for selector state (`--state visible\|hidden\|attached\|detached`, `--timeout ms`) | | `wait-for --js ""` | Wait until a JS predicate is truthy | | `wait-for --network-idle` | Wait until the page is network-idle | | `sample ""` | Sample an expression over time (`--interval`, `--duration`, `--key`, `--frame`); returns timeline + summary | | `wait-stable ""` | Wait until an expression stops changing (`--tolerance`, `--window`, `--timeout`) | | `run ` | Run a multi-step flow script | `click` / `hover` support `--nth N` (1-based, default first match), `--force` (skip actionability checks) and `--timeout ms`. Without `--nth`, multiple matches fail (strict) to prevent wrong-target clicks. ### State | Command | Description | |---------|-------------| | `state-import ` | Import cookies from Netscape format | | `state-save` | Save current browser session state (cookies + localStorage) | | `state-check` | Validate a state file: cookie count, expiry, target domain coverage (`--state`, `--url`) | ### Configuration | Command | Description | |---------|-------------| | `config-show` | Show current config | | `config-set ` | Set config value | | `config-list` | List all profiles | | `config-use ` | Switch active profile | | `config-new ` | Create new profile | | `config-path` | Show config file path | --- ## Multi-step Flows The interaction commands drive a page step by step; `run` turns a sequence into a versionable script: ```bash cssprobe-cli open https://example.com/editor cssprobe-cli wait-for .editor-canvas cssprobe-cli click ".layout-switch" --nth 1 cssprobe-cli type ".block-title input" "Hello" --press Enter cssprobe-cli inspect .block-grid --json cssprobe-cli close ``` For repeatable flows, write a script and run it with one command. The script runs inside the daemon session (full Playwright `page` access): ```js // flow.js export default async ({ page, context, frames, newPage, click, waitFor, type, inspect, screenshot, expect, log }) => { await page.goto('https://example.com/editor', { waitUntil: 'domcontentloaded' }); await waitFor('.editor-canvas', { state: 'visible', timeout: 10000 }); await click('.layout-switch', { nth: 1 }); await waitFor({ js: "() => document.body.dataset.layout === 'two-col'" }); await type('.block-title input', 'Hello', { press: 'Enter', sequential: true, clear: true }); await waitFor({ networkIdle: true }); const { findings } = await inspect('.block-grid', { format: 'json' }); log('findings:', findings.length); await screenshot({ path: './after-layout-switch.png' }); return { findings: findings.length }; }; ``` ```bash cssprobe-cli run ./flow.js # --timeout ms for the whole script (default 300000, 0 = unlimited) ``` Scripts must `default-export` an async function receiving the API object: `page`, `context`, `frames()`, `newPage()`, `click`, `hover`, `type`, `waitFor`, `inspect`, `screenshot`, `expect`, `log`. Uncaught page errors are collected into the result envelope as `pageErrors`. `screenshot` relative paths resolve against the script's directory; logs (`log()` and `console.*`) are collected and returned with the result. On failure the command exits `1`, prints the collected logs, and keeps the session alive. > Scripts are executed by the local daemon with full Node privileges — only run trusted scripts. --- ## Scenario Suites (Regression) Run repeatable regression cases from a JSON suite: each case opens a fresh browser, executes steps, asserts on saved values, and writes artifacts + an optional JUnit report. ```bash cssprobe-cli scenario run suites/ticket-26571394.json \ --state ~/.cssprobe-cli/states/preprod.json \ --artifacts out/ticket-26571394 \ --junit out/junit.xml ``` ```json { "name": "ticket 26571394 preprod", "defaults": { "viewport": { "width": 1280, "height": 800 } }, "cases": [ { "name": "refresh keeps the hash section aligned", "url": "https://example.com/#listen-to-tcmi", "steps": [ { "do": "sample", "expression": "({ hash: location.hash, top: Math.round(document.querySelector('#listen-to-tcmi')?.getBoundingClientRect().top ?? -1) })", "interval": 250, "duration": 12000, "key": "top", "tailFrom": 9500, "save": "timeline" }, { "do": "assert", "label": "hash retained", "on": "timeline", "path": "summary.last.value.hash", "eq": "#listen-to-tcmi" }, { "do": "assert", "label": "anchor stable", "on": "timeline", "path": "summary.tail.min", "min": 70 }, { "do": "assert", "label": "anchor stable", "on": "timeline", "path": "summary.tail.max", "max": 90 } ] }, { "name": "deployment fingerprint", "url": "https://example.com/", "steps": [ { "do": "waitFor", "js": "() => !!window.DEBUG?.NavigatorStore", "timeout": 20000 }, { "do": "assert", "globals": { "DEBUG.NavigatorStore.setHashRestoreProtected": "function" }, "absent": ["DEBUG.NavigatorStore.navigateToSectionId"] } ] } ] } ``` Steps: `goto`, `reload`, `eval`, `sample`, `waitStable`, `click`, `waitFor`, `screenshot`, `assert`. `sample` and `eval` save values with `save: ""`; assertions read them with `on: ""` + `path`, or check live `state` (`url`, `title`, `frames`), `url`, `globals`, `absent`. `sample` supports `key`, `tailFrom`, `ignoreErrors`; every run writes `summary.json`, per-case `case.json`, sample series and failure screenshots. ## Confidence Model Every finding carries a confidence level: | Level | Meaning | |-------|---------| | **DEFINITE** | Based on computed values (facts from getComputedStyle) or accessible declared values | | **INDEFINITE** | Declared value uses `%` — resolves at runtime | | **UNVERIFIABLE** | Declared value missing or from blocked cross-origin stylesheet | The report header shows: `confidence: DEFINITE 8 | INDEFINITE 0 | UNVERIFIABLE 1` --- ## Complementary Tool: cssgraph cssprobe-cli is a **runtime** CSS probe — it opens a real browser and reads what is actually rendered. For **static** questions about the source code — where is `.btn-primary` defined, what cascades over it, which components reference it, what would change if I edited it — use [cssgraph](https://github.com/mack-peng/cssgraph), a local SQLite knowledge graph of every className, property, variable, and at-rule in your stylesheets. The two tools complement each other: | | cssgraph (static) | cssprobe-cli (runtime) | |---|---|---| | Analyzes | source code (CSS/SCSS/Less + JSX/TSX + templates) | live browser (computed styles + DOM) | | Answers | where a class is defined, cascade, impact, unused CSS | actual rendered layout, overflow, scroll, height chains | | When | before/without a browser | after static analysis, to verify at runtime | | Usage | `cssgraph init` + `explore`/`rule`/`impact` | `open` + `inspect`/`layout`/`findings` | **Recommended workflow for a style problem** — go static first, then verify at runtime: ```bash # 1. Static: find the definitions, cascade, and blast radius (cssgraph) cssgraph explore .btn-primary cssgraph cascade .btn-primary # 2. Runtime: confirm what actually renders (cssprobe-cli) cssprobe-cli open https://example.com cssprobe-cli inspect .btn-primary cssprobe-cli findings .btn-primary ``` Install: `npm install -g cssgraph` (requires Node.js >= 22.5.0). It exposes 12 MCP tools (`cssgraph_explore`, `cssgraph_rule`, `cssgraph_impact`, …) plus CLI commands. See [npm](https://www.npmjs.com/package/cssgraph) / [GitHub](https://github.com/mack-peng/cssgraph). --- ## Configuration ```bash # Set values cssprobe-cli config-set browser chromium cssprobe-cli config-set depth 8 cssprobe-cli config-set viewport 375x812 # Profile management cssprobe-cli config-new staging cssprobe-cli -p staging config-set browser firefox cssprobe-cli config-use staging ``` Priority: CLI flags > Environment variables > Config file ``` --browser CSSPROBE_BROWSER --depth CSSPROBE_DEPTH --headed CSSPROBE_HEADED --viewport CSSPROBE_VIEWPORT -p, --profile CSSPROBE_PROFILE ``` Config file: `~/.cssprobe-clirc` --- ## Supported Platforms | Platform | Architectures | |----------|---------------| | macOS | x64, arm64 | | Linux | x64, arm64 | | Windows | x64, arm64 | --- ## Development ```bash npm install npm run build # tsc + esbuild collector + daemon entry + generate help.json npm test # Run unit tests (58 tests) npx tsc --noEmit # Type check only ``` ## If cssprobe-cli is useful A ⭐ star helps other developers discover it — and tells us to keep shipping. --- ## License MIT