agentleFS
LetermeFlorentLetermeFlorent/claude-skills/chrome-bidi/SKILL.md

chrome-bidi

Skill0 starsChanged 10 days agoMITView on GitHub

Use when a page must be opened, read, clicked, filled, screenshotted, printed or measured, checked at phone and desktop widths (/chrome-bidi), compared with its image files, or when a leftover Chrome or chromedriver must be closed.

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Never stop a chrome.exe or chromedriver.exe whose command line lacks --remote-debugging-port=<your port> or --port=<your port + 1000>: it …

    line 26
  2. Distinctive:

    Check free RAM, CPU and disk before a launch.

    line 31
  3. Very specific:

    Read the result of shot rather than guessing from the DOM.

    line 44
  4. Very specific:

    Check, replacing P and P1000 (empty output is the only good one):

    line 46
  5. Very specific:

    Read each shot and the flagged image lines: blurry (file narrower than box times dpr), heavy (over twice the needed width), not loaded.

    line 62

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Chrome over WebDriver BiDi

The scripts start Chrome with a debugging port and a throwaway profile, a chromedriver of the same version on port + 1000 (kept in bin/, downloaded from Chrome for Testing when the build differs), and talk BiDi over a websocket. Call every script as python -I "<skill dir>/scripts/<name>.py" with forward slashes. Stdlib only. A failure prints chrome-bidi: <Type>: <message> and a by hand: line, exit 1.

Scripts: launch.py (start, prints the port), bidi.py (one action on a running Chrome), measure_images.py (box of every displayed image per width, JSON), visual_test.py (the /chrome-bidi run).

Rules

These hold for every task, whatever the page.

  1. Pick the mode yourself and never ask about it. --visible when a person has to see or act in the window: a sign-in, a code or phone check, the user saying they want to watch. Headless for everything else (reading, checks, screenshots, /chrome-bidi).
  2. One port per task, never shared. Profile <temp>/claude-skills/chrome-<port>, downloads in chrome-<port>-downloads, session in chrome-<port>.session. Never the user's profile.
  3. Never stop a chrome.exe or chromedriver.exe whose command line lacks --remote-debugging-port=<your port> or --port=<your port + 1000>: it belongs to the user or another session. Never move, hide, resize or close a window you did not start.
  4. Native input only (input.performActions, input.setFiles, WebDriver element calls). JavaScript reads the page, it never clicks, types or scrolls.
  5. Check free RAM, CPU and disk before a launch. One Chrome at a time per session.
  6. Close with bidi.py close --port P before the task ends, then check nothing is left.

Procedure

Three steps, from launch to reading the result.

  1. python -I "$S/launch.py" --url "http://127.0.0.1:3000" with S=<skill dir>/scripts. Port picked in 9300-9899 when --port is missing; reuse a fixed port only to get back a signed-in profile. Options: --size 1280x720 (content area), --profile <dir>, --prompt dismiss|accept|ignore (unanswered alert, dismiss default), --visible, --desktop, --login.
  2. python -I "$S/bidi.py" <action> [value] --port P [--tab n] [--frame <url part>] [--timeout s]. Each call reconnects to the same session. One call at a time per port.
  3. Read the result of shot rather than guessing from the DOM.
  4. python -I "$S/bidi.py" close --port P.
  5. Check, replacing P and P1000 (empty output is the only good one):

    powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='chrome.exe' or Name='chromedriver.exe'\" | ? { \$_.CommandLine -match 'remote-debugging-port=P(?!\d)|--port=P1000(?!\d)' } | Select ProcessId,Name"

/chrome-bidi

/chrome-bidi <url or local folder> [widths].

  1. python -I "$S/visual_test.py" "<target>" 390,1440. Widths default to 390,1440, height 720 (--height), --visible only if the user wrote it. A local folder or file is served on 127.0.0.1 during the run.
  2. Output goes to <project>/.ia/chrome-bidi/<date-time>/ (shot-<width>.png, images.json), .ia/ added to .git/info/exclude. Project is the git root of the target, or of the current folder for a url; with none, <temp>/claude-skills/chrome-bidi/.
  3. Read each shot and the flagged image lines: blurry (file narrower than box times dpr), heavy (over twice the needed width), not loaded.
  4. Answer in a few lines: what breaks at which width, images to resize, the folder. The script already closed Chrome; run step 5 with the port it printed.

Image measures alone

python -I "$S/measure_images.py" --url "<url or folder>" --widths 390,1440 --out "<file.json>"

Per image and width: width, dpr, src, kind (img or background), box (content box for <img>, padding box for a background, CSS px), natural, fit, selector. Empty boxes are skipped; lazy images are made eager and awaited up to 5 s.

Sign-in a human has to type

Google and Microsoft refuse a driven browser: launch.py --port <p> --login --visible --url <page>, no remote control. The user signs in and closes the window; start again on the same port without --login, the profile keeps the session.

Window mode (--visible)

Chrome opens maximized on the virtual desktop of the host window when the VirtualDesktop PowerShell module is there and that desktop can be found, else on the desktop shown now. Nothing to ask. --desktop <n> forces one. Never call viewport or mobile in this mode.

Actions

Two-part values use |. A selector starting with / or ( is xpath, else CSS.

  • Tabs: nav <url> (reuses the tab on the same host, else a blank one, else a new one; prints the index for --tab), newtab [url], tabs, activate, closetab (refuses the last), url, back, forward, reload, close.
  • Reading: wait <selector> (polls up to --timeout, 10) or wait <n>, find <css>, xpath <query>, text [selector], html [n chars], eval <expression> (read only), console [s] (messages logged after the call), network [s] [all] (reloads, failures only without all), cookies, clearcookies.
  • Input: click <css>, clickxpath <xpath>, clickxy x,y, hover x,y, scroll [delta][@x,y], type [text] (stdin when empty, for a password), key <name>[ xN] (enter tab escape up down left right backspace delete ctrl shift, combos like ctrl+a), upload <selector>|<path>, prompt accept|dismiss[|text], rejectcookies, fill <selector>|<text> (stdin when no text), select <selector>|<option text>. fill and select are WebDriver element calls, top page only.
  • Capture: shot <png>, fullshot <png>, elementshot <selector>|<png>, pdf <file>.
  • Emulation, then reload: viewport WxH, mobile (390x844, ratio 3, Android UA), desktop, geo lat,lon|off (page origin only), locale <fr-FR|off>, timezone <Europe/Paris|off>.

Traps

Each of these has cost a run before.

  • Git Bash rewrites an argument starting with /: prefix with MSYS_NO_PATHCONV=1 or write (//button)[1].
  • shot keeps 720 px of height at most; a taller viewport is scaled down and the factor printed. Multiply coordinates read on the image by it before clickxy or hover.
  • --prompt dismiss closes an alert before prompt can answer: launch with --prompt ignore.
  • click and fill take the first match; a hidden duplicate gives "element not interactable". Narrow the selector, or clickxy then type.
  • A native <select> menu and the Windows "Save as" dialog live outside the page: use select; downloads land in chrome-<port>-downloads.
  • In a covered window document.visibilityState is hidden and keys get lost; fill still works.
  • One nav per page. reload restarts loaders and can replay an expired sign-in: use wait <selector> or shot to see a change.
  • The profile refuses geolocation, notifications, camera, microphone and third-party cookies.
  • The WebDriver HTTP API of the session stays open (id in chrome-<port>.session, endpoints under http://127.0.0.1:<port+1000>/session/<id>/). Use it only for a missing action, never DELETE the session: close does it.
  • Web chat app: take the "open the app anyway" link, the "open in the browser tab" one can loop. Ticket app: a scroll placed on the conversation panel moves it.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.