No description
  • Python 89%
  • PowerShell 5.8%
  • Shell 5.2%
Find a file
GeeFourteen 8463c6cd1a Fix README layout overflow and chart proportions
The three-column HTML install table overflowed its container on GitHub,
cutting off the machine-wide column behind a horizontal scrollbar; replace
it with a plain markdown table. Turn the decision chart left-to-right so it
is wide and short instead of several screens tall.

Also browser.py: add --window WxH (default 1440x900, was Chrome's 800x600
which made screenshots render mobile layouts) and let eval run
multi-statement JavaScript instead of only single expressions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-26 02:39:50 -06:00
.gitattributes Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
.gitignore Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
browser.py Fix README layout overflow and chart proportions 2026-10-26 02:39:50 -06:00
doctor.py Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
install.ps1 Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
install.sh Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
LICENSE Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
README.md Fix README layout overflow and chart proportions 2026-10-26 02:39:50 -06:00
requirements.txt Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
screen.py Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00
web.py Initial commit: browser, web and desktop control toolkit 2026-10-26 02:12:03 -06:00

🛠️ .claude/tools

Browser, web & desktop control for Claude Code — and any other agent

Give any AI session the ability to see a screen, drive a real browser, and talk to the web. Any project. Any site. Any app.


License Python Platform Deps


⚡ TL;DR

Four small scripts. Nothing tied to a particular repo. Drop them in, run the doctor, go.

python .claude/tools/doctor.py --install          # 1. install what's missing

python .claude/tools/web.py get https://site.com  # 2a. read the web (no deps, instant)
python .claude/tools/browser.py open https://site.com   # 2b. drive a real browser
python .claude/tools/screen.py shot               # 2c. look at the actual screen

Tip

The killer feature: browser.py keeps one browser open across commands. Sign in by hand once — in any project, in any session, every later command is still signed in.


Tool What it does Needs
🌐 web.py HTTP: pages as readable text, JSON, links, downloads, POST nothing (stdlib)
🧭 browser.py A real browser that stays open — click, fill, screenshot, run JS. Logins persist playwright
🖥️ screen.py The whole desktop — screenshot, click, type, keys, windows. Drives any app pillow · Windows
🩺 doctor.py Checks & installs everything above nothing (stdlib)

📦 Install

Where Windows macOS / Linux
Already here — just check & fix deps python doctor.py --install python3 doctor.py --install
Into a project .\install.ps1 -Target C:\code\app ./install.sh ~/code/app
Machine-wide → ~/.claude/tools .\install.ps1 -Global ./install.sh --global

Add -NoDeps/--no-deps to skip dependency install, -Force/--force to overwrite.

Requirements — Python 3.8+. web.py runs anywhere with nothing installed. browser.py works on Windows, macOS & Linux. screen.py is Windows-only.


🧭 Which tool?

flowchart LR
    A([What are you<br/>trying to reach?]) --> B{Web page<br/>or API?}
    B -->|an app, a game,<br/>an installer, Firefox| D["🖥️ screen.py"]
    B -->|yes| C{Needs JS, a login,<br/>or clicking?}
    C -->|no| E["🌐 web.py"]
    C -->|yes| F["🧭 browser.py"]

    style D fill:#7c3aed,stroke:#5b21b6,color:#fff
    style E fill:#059669,stroke:#047857,color:#fff
    style F fill:#2563eb,stroke:#1d4ed8,color:#fff

Start with web.py — it's instant and free. Reach for browser.py when the site needs JavaScript, a session, or clicking. Reach for screen.py when there's no DOM to talk to.


🌐 web.py — HTTP without a browser

python web.py get   https://example.com [--max 20000]   # HTML stripped to clean text
python web.py json  https://api.example.com/x --path data.items.0.name
python web.py links https://example.com --filter blog
python web.py dl    https://example.com/file.zip [out]
Full command reference
Command Does
get URL Fetch and strip HTML down to readable text
raw URL The body exactly as sent
json URL Pretty-print JSON; --path a.b.0 digs into it
links URL Every link, absolute; --filter sub narrows
head URL Status line + response headers
dl URL [out] Download to a file
post URL '{"a":1}' POST; --json (default) or --form

Flags on any command

Flag Does
-H "Name: value" Extra header (repeatable)
--ua "..." Override User-Agent
--timeout 30 Seconds
--cookies file.json Use a jar exported by browser.py cookies
--insecure Skip TLS verification
--max N Truncate output

HTTP errors print with their body instead of raising, so you can read what the server actually said.


🧭 browser.py — a real browser that stays open

The browser launches once and keeps running between commands.

python browser.py open  https://example.com
python browser.py fill  "#email" "me@example.com"
python browser.py click "text=Sign in"
python browser.py text  ".results"
python browser.py shot  out.png --full
Full command reference
Command Does
start [--headless] Optional — any command auto-starts the browser
open URL Navigate
text [SEL] Visible text of the page or one element
html [SEL] Markup
shot [out] [--full] Screenshot, optionally full-page
click "SEL" CSS, or text=Sign in
fill "SEL" "value" Type into a field
select "SEL" "value" Pick a dropdown option
upload "SEL" path Attach a file
press Enter · type "..." Raw keyboard
wait "SEL" [--timeout ms] Block until it appears
eval "js" Run JavaScript, get JSON back
links [--filter sub] Every link on the page
scroll N Screens, + down / − up
back · forward · reload Navigation
tabs · tab N · newtab [URL] · closetab Tab control
cookies [--out f.json] Export the session
pdf [out] Print to PDF (headless)
status · stop Inspect / shut down

🔐 Logins & profiles

Profiles live in ~/.claude/browser-profiles/NAME, shared by every project on the machine.

python browser.py --profile work     open https://intranet.example.com
python browser.py --profile personal open https://example.com

Tip

Run without --headless and the window is visible. Sign in by hand once and every later command — in this project or any other — is already signed in. Same answer for a captcha or an MFA prompt: clear that one step yourself, then hand control back.

🌍 Any Chromium browser

python browser.py start --channel chrome    # your installed Chrome
python browser.py start --channel msedge    # Edge
CLAUDE_BROWSER_EXE="C:\...\brave.exe" python browser.py start   # Brave / Vivaldi / Opera

With no --channel, Playwright's bundled Chromium is used.

Warning

Firefox and Safari have no CDP — drive them with screen.py instead.

A browser that's already open can't be attached to unless it was started with a debug port. To automate the Chrome window in front of you right now, use screen.py. Use a second --port 9333 to run a toolkit browser alongside your everyday one.


🖥️ screen.py — the whole desktop Windows

python screen.py shot              # look
python screen.py click 1120 289    # act on what you saw
python screen.py type "hello world"
Full command reference
Command Does
shot [out] [--region X,Y,W,H] [--full] Screenshot; --full spans all monitors
click X Y [right|middle|double] Click
move X Y · drag X1 Y1 X2 Y2 Move / drag
type "text" Type character by character
paste "long text" Via clipboard — instant and exact
key ctrl+l · enter · alt+tab Key combos
scroll -3 + up / − down
focus "window title" Raise a window
windows List visible windows
pos Cursor position, in both coordinate spaces

Important

Coordinates come from the screenshot you just looked at. shot downscales to ≤1600px wide and records the scale and origin, so an X,Y read off the image maps back to the real pixel — including on 4K displays and --region crops. Take a shot, read coordinates off it, click them.

paste beats type for anything long: it goes through the clipboard, so it can't drop characters.


🍳 Recipes

Scrape something behind a login
python browser.py start                       # visible window
python browser.py open https://site/login     # sign in by hand, once
python browser.py open https://site/reports
python browser.py text ".report-table"
Hand a browser session to the fast HTTP tool
python browser.py cookies --out c.json
python web.py get https://site/api/items --cookies c.json
Fill and submit a form
python browser.py open https://site/form
python browser.py fill "input[name=email]" "me@example.com"
python browser.py click "button[type=submit]"
python browser.py text body --max 500
Drive an app with no DOM (Firefox, a game, an installer)
python screen.py focus "Mozilla Firefox"
python screen.py shot                  # look at it
python screen.py key ctrl+l
python screen.py type "example.com"
python screen.py key enter
python screen.py shot                  # confirm
Pull a JSON API and dig into it
python web.py json https://api.github.com/repos/python/cpython --path stargazers_count

🤖 Tell Claude about it

Paste this into your project's CLAUDE.md so sessions reach for the toolkit on their own:

## Browser / web / desktop tools
`.claude/tools/` holds scripts for web and screen work. Check with
`python .claude/tools/doctor.py` (add `--install` to fix anything missing).
- `python .claude/tools/web.py get URL` — fetch a page as text; also `json`,
  `links`, `dl`, `post`. No dependencies; try this first.
- `python .claude/tools/browser.py open URL` — real browser that stays open
  between commands, so logins persist; `text`, `click`, `fill`, `shot`, `eval`.
- `python .claude/tools/screen.py shot` — screenshot the desktop, then
  `click X Y` / `type` / `key` using coordinates read off that screenshot.
Run any of them with no arguments for full usage.

🩹 Troubleshooting

Missing playwright / Missing Pillow


Run python doctor.py --install.

Browser did not open a CDP port


Something already uses the port. Pass --port 9333, or run python browser.py status to see what's running.

stop says it can't stop the browser


Close the window, or kill the process holding the port. stop closes the tabs → kills the recorded pid → falls back to whoever is listening on the port.

Clicks land in the wrong place


Take a fresh screen.py shot first. Coordinates are relative to the most recent screenshot — if the window moved or you cropped a region since, the mapping is stale.

A page is empty or differs from what the browser shows


It's JavaScript-rendered. Use browser.py rather than web.py.

Mangled characters in the console


Shouldn't happen — all tools force UTF-8 output, so CJK, emoji and accented text print correctly even in a cp1252 Windows console.


⚠️ Notes

  • Bot challenges are a stop sign, not a bug. Run the browser visibly, clear the captcha yourself, carry on — the session persists.
  • Profiles hold real cookies and logins. ~/.claude/browser-profiles/ is as sensitive as your browser profile. Don't commit it or copy it around. (.gitignore already blocks it, along with cookies.json.)
  • screen.py controls the actual mouse and keyboard. Whatever is focused receives the input — check with shot before typing.

MIT licensed — see LICENSE

Built for Claude Code, useful to any agent.