codex/skills/web-browser/SKILL.md
Use when tasks need real-browser web automation in Chrome/Chromium via CDP: open or navigate URLs, click/type/select in forms, run page JS, wait for selectors, scrape structured content, capture screenshots, validate UI flows, or run measured web-browser latency checks (`bench:eval`, `bench:all`) for perf regressions.
npx skillsauth add tkersey/dotfiles web-browserInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
codex/skills/web-browser/ (so ./tools/*.js resolves).await) and repo deps installed (notably puppeteer-core).CODEX_CHROME_PATH.# Start Chrome with CDP on :9222
./tools/start.js
# Safer (recommended on your laptop): avoid killing an existing Chrome session
./tools/start.js --no-kill
# Open a deterministic tab, then operate on it
./tools/nav.js https://example.com --new
./tools/eval.js 'document.title'
./tools/screenshot.js
Loop: take small steps → inspect state (eval.js / screenshot.js) → repeat.
Use this when optimizing or validating web-browser tool latency.
./tools/start.js --no-kill
./tools/nav.js https://example.com --new
cd tools
npm run -s bench:all -- --warmup 5 --samples 30 --screenshot-samples 20
For stricter CI-style gating, add p95 budgets:
cd tools
npm run -s bench:all -- \
--warmup 5 \
--samples 30 \
--screenshot-samples 20 \
--budget-nav-p95-ms 310 \
--budget-eval-p95-ms 220 \
--budget-screenshot-p95-ms 320 \
--budget-start-p95-ms 140
start.js: --port, --user-data-dir, --chrome-path, --profile, --no-killnav.js / eval.js / screenshot.js / pick.js: --port, --browser-urlCODEX_BROWSER_URL: CDP URL (used when no CLI --port/--browser-url)CODEX_BROWSER_PORT (alias: CODEX_CDP_PORT): CDP port for localhostCODEX_BROWSER_USER_DATA_DIR: Chrome user data dir (used by start.js)CODEX_CHROME_PATH (aliases: CHROME_PATH, PUPPETEER_EXECUTABLE_PATH): Chrome/Chromium executableAll tools operate on the “active tab”, defined as the last page returned by puppeteer.pages() (roughly: the most recently opened tab).
./tools/nav.js <url> --new when you want deterministic targeting.# See all options (safe; does not start/kill Chrome)
./tools/start.js --help
# Use a non-default port
./tools/start.js --port 9223 --no-kill
./tools/nav.js --port 9223 https://example.com --new
# Or configure once via env
export CODEX_BROWSER_URL=http://localhost:9223
./tools/eval.js 'document.title'
# Wait for a selector (polling with timeout; good for SPAs)
./tools/eval.js '(async () => { for (let i = 0; i < 50; i++) { const el = document.querySelector("button[type=submit]"); if (el) return true; await new Promise(r => setTimeout(r, 100)); } return false; })()'
# Screenshot current viewport
# Prints a PNG filepath in your system temp dir
./tools/screenshot.js
# Pick elements interactively
# Prints tag/id/class/text/html/parents for one element (or many via Cmd/Ctrl+click)
./tools/pick.js "Click the submit button"
Tip: use pick.js to inspect attributes/text, then craft a selector for eval.js.
# Scrape structured data (return an array of objects for readable output)
./tools/eval.js 'Array.from(document.querySelectorAll("a"), a => ({ href: a.href, text: a.textContent?.trim() }))'
./tools/start.js, log in once, then reuse the persisted profile in your user-data-dir (default: ~/.cache/scraping)../tools/start.js --profile when you truly need existing cookies/logins../tools/start.js --profile uses rsync -a --delete to copy your default Chrome profile into your user-data-dir (cookies/sessions/PII).~/.cache/scraping) as sensitive; avoid on shared machines and delete it when done if needed.✗ Failed to connect to Chrome via CDP: run ./tools/start.js (or set CODEX_BROWSER_URL/--port to match your Chrome).✗ No active tab found: open a page first (e.g., ./tools/nav.js https://example.com --new).9222 is busy: pick a new one (./tools/start.js --port 9223).domcontentloaded.CODEX_CHROME_PATH or pass --chrome-path../tools/start.js defaults to killing Chrome processes (use --no-kill to avoid this).When you need objective proof that the local $lift tooling is Zig-based:
bench_stats --help 2>&1 | sed -n '1,6p'
perf_report --help 2>&1 | sed -n '1,6p'
brew cat tkersey/tap/lift | rg -n 'depends_on \"zig\" => :build|build-exe|bench_stats.zig|perf_report.zig'
codex/skills/web-browser/tools/start.jscodex/skills/web-browser/tools/nav.jscodex/skills/web-browser/tools/eval.jscodex/skills/web-browser/tools/screenshot.jscodex/skills/web-browser/tools/pick.jscodex/skills/web-browser/tools/bench-eval.jscodex/skills/web-browser/tools/bench-all.jstesting
Use before local patching when bugs, regressions, malformed state, crashes, parser failures, migrations, cache drift, protocol problems, compatibility requests, tolerant readers, fallbacks, coercions, retries, catch-and-continue logic, or local workarounds may broaden accepted invalid state.
testing
Use for bug reports, PR/issue prose, reviewer comments, user diagnoses, generated summaries, memories, retrieved context, public tracker context, claimed root causes, proposed fixes, fake-minimal repro risk, or any investigation where natural-language context could anchor the implementation scope.
development
Use when non-trivial work needs Challenge Escalation, latent-intelligence activation, frame-market selection, doctrine operators, dominant-move selection, ablation/surface-tax judgment, reification, review comment law, negative capability, route receipts, or proof-bearing refusal to mutate.
development
Apply Algebra-Driven Design. Use for ADD, denotational design, combinator models, law-driven architecture, domain algebra, property tests, codebase modeling, event sourcing, workflow design, or agentic skill design. If the canonical bundle is unavailable, use this wrapper as the minimal ADD kernel and report the missing bundle path.