> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser Automation (@browser)

> Drive a real local Chrome/Chromium over the DevTools Protocol — open a page, read it as text with numbered interactive elements, click, type, run JS, screenshot, and inspect console + network. The verification loop for web work. Keyless, zero new dependency.

The **`@browser`** tool drives a **real local Chrome/Chromium** browser so the agent can **see and interact with web pages** the way a person would: open a URL, read the rendered page as text, click and type into elements, run JavaScript, capture screenshots, and inspect the page's **console** and **network** activity.

It is the **verification loop** for web work — the agent builds or changes a frontend, then **actually looks at it** and debugs it from what the page logged and requested, instead of guessing from source.

<Info>
  `@browser` speaks the **Chrome DevTools Protocol (CDP)** directly over the websocket client ChatCLI already ships — **zero new dependency**, no driver, no downloaded runtime, no API key. It only needs a **locally installed Chromium-family browser** (Google Chrome, Chromium, Brave or Edge).
</Info>

***

## How it works

```text theme={"system"}
@browser open http://localhost:3000
        │
        ▼
launch a headless Chrome (first use) — reused for the whole session
        │
        ▼
attach over CDP (websocket) → navigate → wait for load
        │
        ▼
snapshot: title, URL, NUMBERED interactive elements, visible text
        │
        ▼
click [n] / type [n] "text" → snapshot again → console / network to debug
        │
        ▼
close (or it dies automatically when ChatCLI exits)
```

1. **Lazy launch** — the first `open` starts one browser; every later command reuses that **single stateful session**.
2. **CDP over websocket** — navigation, DOM reads, screenshots and event capture all flow over the DevTools websocket. No Selenium/Playwright, no external binary.
3. **Snapshot as text** — the page is rendered to model-friendly text with every interactive element **stamped and numbered** so the agent can address them precisely.
4. **Continuous capture** — console messages (errors included) and network responses are captured into bounded rings, so you can ask "what did the page log?" **after the fact**.
5. **Dies with the session** — a headless browser never outlives ChatCLI; it is closed on CLI shutdown (or explicitly with `close`).

***

## Subcommands

Invoke with a JSON envelope `{cmd, args}` or the flat argv form. The model calls `@browser` automatically in `/agent` and `/coder` when it needs to look at a page.

| Subcommand   | What it does                                                                         | Key args                                         |
| :----------- | :----------------------------------------------------------------------------------- | :----------------------------------------------- |
| `open`       | Navigate to a URL (launches the browser on first use) and return a **page snapshot** | `url` (req.)                                     |
| `snapshot`   | The current page as text: title, URL, numbered interactive elements, visible text    | `--max N` (cap text length)                      |
| `click`      | Click an element by its **`[n]` ref** from the last snapshot **or** a CSS selector   | `target` (req.)                                  |
| `type`       | Type into an input; `--submit` presses Enter / submits the form                      | `target` (req.), `text`, `--submit`              |
| `scroll`     | Move the viewport                                                                    | `down` · `up` · `top` · `bottom` · `--to target` |
| `eval`       | Run a JavaScript expression in the page and return its value                         | `javascript` (req.)                              |
| `screenshot` | Capture the viewport as **PNG**                                                      | `--file path` (optional)                         |
| `console`    | The captured console messages (errors included)                                      | `--tail N`                                       |
| `network`    | The captured network responses: method, status, URL                                  | `--tail N`                                       |
| `back`       | History back                                                                         | —                                                |
| `status`     | Whether a browser session is running and what page it is on                          | —                                                |
| `close`      | Close the browser session                                                            | —                                                |

***

## Refs vs. CSS selectors

Every `snapshot` stamps each interactive element with a `data-chatcli-ref` attribute and returns a **numbered listing**. `click` and `type` accept **either** that `[n]` ref number **or** a raw CSS selector:

```text theme={"system"}
Interactive elements:
  [1] link    "Docs"           → /docs
  [2] input   "Search"         (type=search)
  [3] button  "Sign in"

@browser click 3          # by ref (recommended — no guessing at selectors)
@browser click #login-btn # by CSS selector
```

<Note>
  Refs are stable **until the next navigation**. After you `open` a new URL or a `click` triggers navigation, take a fresh `snapshot` before addressing elements by `[n]` again.
</Note>

***

## A worked example

Build a page in `/coder`, then verify it end to end:

```text theme={"system"}
# 1) Open the app the coder just started
@browser open http://localhost:3000
  → Page: My App — Home
    Interactive elements:
      [1] link   "Products"
      [2] input  "Email"      (type=email)
      [3] button "Subscribe"

# 2) Fill the form and submit
@browser type 2 "user@example.com"
@browser click 3

# 3) See what happened
@browser snapshot
  → Page: My App — Thanks!
    "You're subscribed."

# 4) Something looked off? Debug from the page itself
@browser console --tail 20
  → [error] Uncaught TypeError: cannot read 'id' of undefined (app.js:42)

@browser network --tail 10
  → 500 POST http://localhost:3000/api/subscribe (Fetch)
```

The agent now knows the button worked in the UI but the backend returned **500**, and the exact console error — the full verify-and-fix loop without leaving ChatCLI.

***

## Environment variables

| Variable                   | Description                                                  | Default                                |
| :------------------------- | :----------------------------------------------------------- | :------------------------------------- |
| `CHATCLI_BROWSER_BIN`      | Path to the browser binary to use                            | auto-detect Chrome/Chromium/Brave/Edge |
| `CHATCLI_BROWSER_HEADLESS` | Run headless (`true`) or open a **visible window** (`false`) | `true`                                 |

```bash theme={"system"}
# Watch the agent drive a real window
export CHATCLI_BROWSER_HEADLESS=false

# Point at a specific browser
export CHATCLI_BROWSER_BIN="/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
```

***

## Security

Observation commands are **read-only** and skip the confirmation prompt — the agent can look freely: `open`, `snapshot`, `screenshot`, `console`, `network`, `scroll`, `back`, `status`, `close`.

Commands that **act** on a page — `click`, `type` and `eval` — can trigger real actions on remote sites, so they go through the standard **security gate** like any other mutating tool. See [Coder Mode Security](/features/coder-security).

<Warning>
  `@browser` drives a real browser signed into whatever profile it launches. Prefer the default throwaway session for untrusted pages, and treat `click`/`type`/`eval` on external sites as the side-effecting operations they are.
</Warning>

***

## Notes

* **One session per process**, launched lazily on the first `open` and reused across every call.
* Console and network are captured **continuously** into bounded rings — `console`/`network` read the recent history, they don't need to be "armed" first.
* `screenshot` writes a PNG (default under the temp dir, or `--file path`). Pair it with [`@view`](/features/vision-input) so the model can **look at the screenshot** it just took.
* The browser is **always** closed on ChatCLI shutdown; a stray headless Chrome never survives the session.

## Next steps

<CardGroup cols={2}>
  <Card title="Image Input (@view)" icon="eye" href="/features/vision-input">
    Attach a screenshot `@browser` captured so the model can see it with its own eyes.
  </Card>

  <Card title="Plugin @coder" icon="hammer" href="/features/coder-plugin">
    Read, write, patch and run — pair it with `@browser` to build then verify.
  </Card>

  <Card title="Agentic Plugins" icon="plug" href="/features/agentic-plugins">
    The full builtin tool catalog and how the agent uses them.
  </Card>

  <Card title="Web Tools" icon="globe" href="/features/web-tools">
    `@webfetch`, `@websearch` and the shared hardened HTTP client.
  </Card>
</CardGroup>
