Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions .vera/skills/vera-browser/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
name: vera-browser
description: "Render pages with headless Chromium: screenshot, mobile/responsive layout review, viewport sweep, post-JS DOM, eval JS. Use for any visual/design/frontend QA task — see how a page actually looks rather than read its source. Triggers on: screenshot, render, mobile layout, responsive, viewport, breakpoint, design review, visual review, frontend QA, what does X look like, see the page, hydrated DOM, SPA, headless browser."
---

# vera-browser

A headless-Chromium CLI on `PATH`. Use it whenever the task needs to *see* or *render* a page rather than just read its source.

## When to use

- Screenshotting a URL at a specific viewport (mobile / desktop / responsive sweep)
- Reviewing mobile layout, responsive breakpoints, design changes
- Visually verifying a frontend fix landed correctly
- Extracting the *post-JS-execution* DOM of a SPA (React, Vue, Next, etc.)
- Probing page state with `document.title`, computed styles, element counts, etc.

## When NOT to use

- Just need raw HTML / readable content → `web_fetch` is faster and lighter.
- Pure CSS reasoning from source → read the stylesheet directly.
- Anything that needs interactive auth, form submission, or multi-step flows — **deferred**, not yet supported.

## Subcommands

```
vera-browser screenshot <url> [--viewport <preset>] [--out <path>] [--wait-for <sel>] [--no-full-page] [--timeout <ms>]
vera-browser dom <url> [--viewport <preset>] [--wait-for <sel>] [--timeout <ms>]
vera-browser eval <url> --script <js> [--viewport <preset>] [--wait-for <sel>] [--timeout <ms>]
```

Run `vera-browser --help` for the full reference.

## Viewport presets

Default: `desktop-1280`. Mobile presets set `isMobile`, `hasTouch`, and a real iOS/Android UA.

| preset | css size | notes |
|-----------------|-------------|--------------------|
| `iphone-se` | 375 × 667 | small modern phone |
| `iphone-14-pro` | 393 × 852 | mid modern phone |
| `pixel-7` | 412 × 915 | large android |
| `desktop-1280` | 1280 × 800 | laptop default |
| `desktop-1920` | 1920 × 1080 | full HD desktop |

PNG dimensions will be `css × DPR` (e.g. iphone-se → 750 × 1334 @ 2× DPR). That's correct.

## The read-back pattern (most common flow)

`vera-browser` prints the screenshot path on stdout. Pipe it straight into `read` to actually *see* the rendered page:

```bash
vera-browser screenshot https://example.com --viewport iphone-se --out /tmp/shot.png
# stdout: /tmp/shot.png
```

Then:

```
read /tmp/shot.png
```

The PNG comes back as an image attachment in your context. Reason about what you see, then act.

## Pattern: mobile-layout audit

Loop the four most-useful viewports, read each shot, then write up findings:

```bash
URL=https://agent-vera.com
for vp in iphone-se iphone-14-pro pixel-7 desktop-1280; do
vera-browser screenshot "$URL" --viewport $vp --out /tmp/audit-$vp.png
done
```

Then `read /tmp/audit-iphone-se.png`, `read /tmp/audit-iphone-14-pro.png`, etc., and produce a per-viewport finding list. **Never caveat with "I can't actually render the page"** — you can. Use this pattern.

## Pattern: SPA post-hydration DOM

For React/Vue/Next sites, `web_fetch` only sees the empty shell. Use `dom` to get the hydrated tree:

```bash
vera-browser dom https://react.dev --wait-for main --timeout 20000
```

The HTML printed to stdout is what the user actually sees in their browser, not the SSR skeleton.

## Pattern: probing page state

For one-off questions ("what's the page title?", "how many product cards rendered?"):

```bash
vera-browser eval https://shop.example.com --script "document.querySelectorAll('.product-card').length"
# stdout: 24
vera-browser eval https://example.com --script "document.title"
# stdout: "Example Domain"
```

The result is JSON-stringified. Strings come back quoted, numbers/booleans/objects as-is.

## Local dev servers

Anything reachable from inside the agent container works, including `localhost`:

```bash
# In another shell tab / background process
python3 -m http.server 8000 &
vera-browser screenshot http://localhost:8000 --out /tmp/local.png
```

## Wait strategies

- **Default** (no `--wait-for`): navigates with `networkidle`. Best for static-ish pages.
- **`--wait-for <css-selector>`**: navigates with `domcontentloaded`, then waits for the selector. **Use this for SPAs** — many never go fully `networkidle` and the default wait will burn the timeout.

## Defaults & gotchas

- **Timeout:** 30 s per op. Override with `--timeout <ms>`. Unreachable hosts fail fast (`net::ERR_NAME_NOT_RESOLVED`, exit 1).
- **Screenshots are full-page by default.** Use `--no-full-page` if you only want the fold (above-the-scroll viewport area).
- **Default output path:** `/tmp/vera-browser/<ts>-<slug>.png`. Always read it back from the printed stdout path — don't guess.
- **Eval is subject to page CSP.** Strict CSP pages may block `new Function()`. Falls back gracefully — you'll see an error from the page, not a hang.
- **`PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright`** is set in the image — you don't need to export it.
- **Exit codes:** 0 success, 1 runtime error (timeout, navigation failure, CSP block, etc.), 2 CLI usage error. Always check.

## Anti-patterns

- ❌ Captioning a screenshot you didn't actually `read`. Take it, read it, *then* describe what's in it.
- ❌ Using `eval` for things `dom` already gives you (selectors, text content of large regions).
- ❌ Screenshotting one viewport when the task says "mobile audit" — always sweep at least 3 mobile presets.
- ❌ Reaching for `web_fetch` first on a known SPA. Use `dom` directly.
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ COPY .vera /opt/vera/.vera
COPY --from=compile /build/agents /opt/vera/agents
COPY scripts /opt/vera/scripts

# Headless browser: Playwright + Chromium. Installed as root, into a shared
# world-readable path (/opt/ms-playwright) so the non-root `vera` user can use
# it after the USER switch below. `playwright install --with-deps chromium`
# fetches the browser AND apt-installs the required system libs (libnss3,
# libatk*, fonts, etc.) in one shot. Pinned for reproducible builds. Adds
# ~400–500 MB to the image.
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright
RUN echo '{"name":"vera-scripts","private":true,"type":"module"}' \
> /opt/vera/scripts/package.json \
&& cd /opt/vera/scripts \
&& npm install --no-audit --no-fund playwright@1.59.1 \
&& npx playwright install --with-deps chromium \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& chmod +x /opt/vera/scripts/vera-browser.ts \
&& ln -s /opt/vera/scripts/vera-browser.ts /usr/local/bin/vera-browser

# Binaries + package.json (some deps resolve it at runtime via getPackageDir())
COPY --from=compile /build/vera-server /usr/local/bin/vera-server
COPY --from=compile /build/vera-headless /usr/local/bin/vera-headless
Expand Down
Loading
Loading