thinkbrowse-cli
Skill2 starsChanged 54 days agoMITView on GitHub
Control browsers via the ThinkBrowse CLI (the thinkbrowse / thinkrun command) — navigate pages, interact with elements, extract content, take screenshots. Use ONLY when the user explicitly names the thinkbrowse or thinkrun CLI, or asks to drive the browser from shell scripts / terminal commands. For general browse, scrape, or automation asks that don't name the CLI, prefer the web-browse skill. Do NOT use for simple URL fetching (use WebFetch tool instead).
- Reads credentials
- Installs packages
In this repository11 other files it gives its agents.
- before-afterSkill
- prove-itSkill
- thinkbrowse-cliSkill
- thinkbrowse-mcpSkill
- ux-auditSkill
- web-browseSkill
- before-afterSkill
- prove-itSkill
- thinkbrowse-mcpSkill
- ux-auditSkill
- web-browseSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is 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.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 154
All commands return JSON:
- Very specific: line 182
Use --tab <tabId> on every command to prevent tab contention:
≈ “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.
Files
ThinkBrowse CLI
Control real browsers from Claude Code using the thinkbrowse CLI. Two modes:
- Local mode — drives your actual Chrome via the ThinkBrowse extension (free, auth cookies available)
- Cloud mode — provisions isolated Playwright browsers on Fly.io (credits, headless, stealth)
Quick Start
# Local mode — list tabs, attach, navigate, observe
thinkbrowse tabs
thinkbrowse attach <tabId>
thinkbrowse navigate "https://example.com"
thinkbrowse snapshot # accessibility tree (best for AI)
thinkbrowse screenshot --output /tmp/page.png
# Cloud mode — create session, navigate, observe, clean up
thinkbrowse cloud start "https://example.com"
thinkbrowse snapshot
thinkbrowse screenshot --output /tmp/page.png
thinkbrowse cloud stop --allMode Detection
| Signal | Mode |
| Bridge running + extension connected | Local |
THINKBROWSE_API_KEY set | Cloud |
| Neither | Error |
Priority: --tab flag > THINKBROWSE_TAB_ID env > working-location.json > API key (cloud).
Top 5 Patterns
1. Navigate and Observe
thinkbrowse navigate "https://example.com"
sleep 2 # Wait for page load + CSRF tokens
thinkbrowse snapshot # Read structure before interacting
thinkbrowse screenshot --output /tmp/page.png
# Then use Read tool on /tmp/page.png to view it2. Click by Text Content (RECOMMENDED)
Direct CSS selectors often fail when elements lack unique selectors. Use evaluate:
thinkbrowse evaluate "[...document.querySelectorAll('button,a,[role=button]')].find(el=>el.textContent.trim()==='Sign In')?.click()"
sleep 13. Fill React Forms
# React inputs need keyboard events — use `type`, NOT `fill`
thinkbrowse click "input[type=email]"
thinkbrowse type "input[type=email]" "user@example.com"
thinkbrowse click "input[type=password]"
thinkbrowse type "input[type=password]" "password123"
thinkbrowse click "button[type=submit]"
sleep 34. List All Interactive Elements
thinkbrowse evaluate "[...document.querySelectorAll('button,[role=tab],[role=button]')].map(b=>({label:b.getAttribute('aria-label'),text:b.textContent.trim(),class:b.className.slice(0,50)})).filter(b=>b.text||b.label)"5. Wait Then Act
thinkbrowse navigate "https://app.example.com"
thinkbrowse wait "button[type=submit]" --timeout 10000
thinkbrowse click "button[type=submit]"For all 10+ patterns, see references/patterns.md.
Top 5 Anti-Patterns
1. Using text as a CSS selector
# BAD — "Timeline & Logs" is not a CSS selector
thinkbrowse click "Timeline & Logs" # ELEMENT_NOT_FOUND
# GOOD — find by text via evaluate
thinkbrowse evaluate "[...document.querySelectorAll('button')].find(b=>b.textContent.includes('Timeline'))?.click()"2. Using fill on React inputs
# BAD — fill uses native DOM, React won't detect the change
thinkbrowse fill "#search" "query text"
# GOOD — type uses keyboard events, triggers React onChange
thinkbrowse type "#search" "query text"3. Screenshotting immediately after attach
# BAD — screenshot fails with "fetch failed"
thinkbrowse attach <tabId>
thinkbrowse screenshot --output /tmp/page.png # ERROR
# GOOD — wait for connection
thinkbrowse attach <tabId>
sleep 2
thinkbrowse screenshot --output /tmp/page.png4. Clicking a link without waiting for navigation
# BAD — extract runs before new page loads
thinkbrowse click "a[href='/next-page']"
thinkbrowse extract ".content" # OLD page content!
# GOOD — wait for new page element
thinkbrowse click "a[href='/next-page']"
thinkbrowse wait "h1" --timeout 5000
thinkbrowse extract ".content"5. Not cleaning up sessions
# ALWAYS release/stop when done
thinkbrowse release # local mode
thinkbrowse cloud stop --all # cloud modeOutput Format
All commands return JSON:
{"success": true, "command": "navigate", "durationMs": 1234, "data": {...}}
{"success": false, "error": "...", "code": "ELEMENT_NOT_FOUND", "hint": "...", "retryable": true}Key gotchas:
thinkbrowse urlreturns{"data": "https://..."}— plain string, NOT nested objectthinkbrowse screenshot --output /tmp/x.pngreturns{"data": {"path": "/tmp/x.png"}}— use Read tool on that path to view itevaluateauto-wraps single expressions; multi-statement needs IIFE:(()=>{const x=5; return x*2})()evaluatedoes NOT await Promises — async operations return{}- Scroll has no effect on fixed-layout pages — screenshot after to verify
- Protected iframes (Stripe, OAuth) return
{"blocked": true, "handoffRequired": true}— stop automation
Timing Guidelines
| Action | Wait After |
navigate | sleep 2 (page load + CSRF) |
click | sleep 1 (state change) |
type / fill | No wait needed |
attach (new tab) | sleep 2 (connection) |
evaluate (click) | sleep 1 |
scroll | sleep 1 (rendering) |
Multi-Agent Coordination
Use --tab <tabId> on every command to prevent tab contention:
thinkbrowse navigate "https://site-a.com" --tab 12345
thinkbrowse navigate "https://site-b.com" --tab 67890Or: export THINKBROWSE_TAB_ID=12345
Setup & Diagnostics
thinkbrowse doctor # Check bridge, extension, config
thinkbrowse config show # Show current config
thinkbrowse config set apiKey <key> # Set API key for cloud mode
thinkbrowse agent-init [--name <name>] # Set agent identityReferences
- For complete command reference, see
references/command-reference.md - For all 10+ patterns and additional gotchas, see
references/patterns.md - For error codes and recovery playbooks, see
references/error-recovery.md
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
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.

