---
name: web-browse
description: "Browse the web programmatically — create cloud browser sessions, navigate pages, interact with elements, extract content. Use when: visit or open a URL, check a webpage, interact with a browser, verify something works, take screenshots of a page, scrape or extract content, automate a browser task. Do NOT use for structured UX audits with scoring and fix reports — use the ux-audit skill for that."
compatibility: Requires the `thinkrun` CLI (cloud mode needs an API key; local mode needs the ThinkRun browser extension + native host).
metadata:
  version: 1.0.0
  tags: [browsing, automation, scraping, testing]
---

# Web Browse

Browse the web using the `thinkrun` CLI. Supports two modes:

- **Cloud mode**: Spins up an isolated browser session in the cloud — no login, no local setup, good for autonomous/headless work.
- **Local mode**: Controls your actual browser via the ThinkRun extension + native host — needed when a site requires your real login cookies.

## When to Use Which

| Scenario | Mode | Why |
|----------|------|-----|
| Site needs your login cookies | **Local** | Uses your real, already-signed-in browser |
| Headless autonomous scraping | **Cloud** | No browser needed on your machine, stealth mode available |
| Anti-detection needed | **Cloud** | `--stealth` flag, no `navigator.webdriver` |
| Interactive development against your own app | **Local** | Direct DOM access, real DevTools if you need them |

## Setup

### Cloud Mode

```bash
thinkrun config set-key <your-api-key>
thinkrun cloud start                 # provisions a session; waits for it to be ready
```

No browser extension or native host needed — cloud mode only needs the CLI and an API key.

Config lives at `~/.config/thinkrun/config.json`. Run `thinkrun doctor` any time to check endpoint, auth, and session status.

### Local Mode

1. Install the ThinkRun browser extension and run `thinkrun setup` to register the native host.
2. `thinkrun doctor` confirms the native host and extension are both connected.
3. `thinkrun tabs` lists your open tabs; `thinkrun attach <tabId>` binds the CLI to one of them.

---

## Dedicated Window (Local Mode — Avoid Tab Clashing)

When more than one agent or session might be running, work in a dedicated window so you don't hijack a tab someone else is using:

```bash
# Open a fresh window and auto-attach to it
thinkrun new-window "https://example.com"

# Confirm the attach worked
thinkrun tabs
```

Never reuse a tab another agent is actively controlling. If a `TAB_OWNED_BY_OTHER_SESSION` error appears, that tab already belongs to another session — run `thinkrun session debug --json` to see who owns it, then open a new window and attach to that one instead.

---

## Quick Start — Local Mode

```bash
thinkrun doctor
thinkrun tabs
thinkrun attach <tabId>
thinkrun navigate "https://example.com"
sleep 2
thinkrun snapshot                                 # accessibility tree
thinkrun evaluate "document.body.innerText.slice(0,2000)"
thinkrun click "button.submit"
thinkrun fill "input[type=email]" "user@example.com"
thinkrun press Enter
thinkrun screenshot --output "/tmp/page.png"      # then: view /tmp/page.png however your agent harness supports
thinkrun release                                  # always clean up
```

## Quick Start — Cloud Mode

```bash
thinkrun cloud start                              # ~60-90s cold start
thinkrun navigate "https://example.com"
thinkrun snapshot
thinkrun fill "#email" "user@example.com"
thinkrun click "button[type=submit]"
thinkrun wait-for-text "Dashboard" --timeout 10000
thinkrun extract "article" --format json
thinkrun console
thinkrun network
thinkrun screenshot --output "/tmp/page.png"
thinkrun cloud stop                               # always clean up!
```

---

## Known Gotchas

- **`fill`/`type` may not trigger React `onChange`** — use the native setter trick for React-controlled inputs:
  ```js
  thinkrun evaluate "
  const el = document.querySelector('input[type=email]');
  const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
  setter.call(el, 'value'); el.dispatchEvent(new Event('input', {bubbles:true}));
  "
  ```
- **Local screenshot** — saves to disk with `--output`. View the image however your agent harness supports (e.g. Claude Code's Read tool).
- **Cloud screenshot** — pass `--output` to save to disk, or `--json` to get it inline.
- **`thinkrun url` returns a plain string** — `{"data": "http://..."}` not `{"data": {"url": "..."}}`.
- **Scroll syntax** — `thinkrun scroll --down 500` for either mode.
- **Never use a generic `button` selector when multiple buttons exist** — use `button[type=submit]`, or click by visible text via `evaluate`. If the text comes from anywhere other than a literal you typed (e.g. a variable), JSON-encode it before embedding it in the script so quotes/backslashes can't break out:
  ```bash
  label_json=$(printf '%s' "$LABEL" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
  thinkrun evaluate "[...document.querySelectorAll('button,a,[role=button]')].find(el=>el.textContent.trim()===${label_json})?.click()"
  ```
- **CSRF-protected forms** — use `sleep 2` after navigation before filling, so any CSRF token fetch completes first.
- **Local tab attachment** — after `thinkrun attach <tabId>`, all commands route to that tab. `TAB_NOT_OWNED` means this session never attached to a tab (run `attach`); `TAB_OWNED_BY_OTHER_SESSION` means another session already owns that tab (run `thinkrun session debug --json`, then attach to a different one).
- **Never type a real password for the user** — sign-in is a human-only step; wait for it to complete, then continue.

---

## Retry Behavior

**Cloud**: retried automatically with exponential backoff on 502/503/504.
**Local**: `EXTENSION_NOT_CONNECTED`, `EXTENSION_DISCONNECTED`, and `NATIVE_HOST_UNREACHABLE` are treated as transient bridge-reconnect states and retried automatically — don't treat them as fatal.

Fatal errors (stop immediately, don't retry): `SESSION_CLOSED`, `TAB_NOT_FOUND`, `TAB_NOT_OWNED`, `TAB_OWNED_BY_OTHER_SESSION`, `EVALUATE_FAILED`, `INVALID_API_KEY`.

---

## Reference Links

- Full command reference: run `thinkrun --help` or see the `@thinkrun/cli` README.
- Health check / diagnostics: `thinkrun doctor`
